/*
* Uzebox(tm) Packrom utility
* Copyright (C) 2008 Alec Bourque
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*
* Uzebox is a reserved trade mark
*/
/* This tool converts standard .HEX files into .UZE file. UZE files are
* binary version of HEX files and includes a 512 bytes header. They
* are intended to be simpler to load by the Uzebox bootloader.
*
* Revisions:
* ----------
* 1.3: 4/9/2012 - Fixed parse_hex_nibble bug (s >= 'a' && s <= 'f')
*/
#include
#include
#include
#include
#include
#include
#define HEADER_VERSION 1
#define VERSION_MAJOR 1
#define VERSION_MINOR 3
#define MAX_PROG_SIZE 61440 //65536-4096
#define HEADER_SIZE 512
#define MARKER_SIZE 6
#if defined (_MSC_VER) && _MSC_VER >= 1400
// don't whine about sprintf and fopen.
// could switch to sprintf_s but that's not standard.
#pragma warning(disable:4996)
#endif
// 644 Overview: http://www.atmel.com/dyn/resources/prod_documents/doc2593.pdf
// AVR8 insn set: http://www.atmel.com/dyn/resources/prod_documents/doc0856.pdf
typedef uint8_t u8;
typedef int8_t s8;
typedef uint16_t u16;
typedef int16_t s16;
typedef uint32_t u32;
#pragma pack( 1 )
typedef struct{
/*Header fields*/
u8 marker[MARKER_SIZE]; //'UZEBOX'
u8 version; //header version
u8 target; //AVR target (ATmega644=0, ATmega1284=1)
u32 progSize; //program memory size in bytes
u16 year;
u8 name[32];
u8 author[32];
u8 icon[16*16];
u32 crc32;
u8 mouse;
u8 description[64];
u8 reserved[114];
}RomHeader;
union ROM{
u8 progmem[MAX_PROG_SIZE+HEADER_SIZE];
RomHeader header;
};
#pragma pack()
ROM rom;
/* crc_tab[] -- this crcTable is being build by chksum_crc32GenTab().
* so make sure, you call it before using the other
* functions!
*/
u32 crc_tab[256];
/* chksum_crc() -- to a given block, this one calculates the
* crc32-checksum until the length is
* reached. the crc32-checksum will be
* the result.
*/
u32 chksum_crc32 (unsigned char *block, unsigned int length)
{
unsigned long crc;
unsigned long i;
crc = 0xFFFFFFFF;
for (i = 0; i < length; i++)
{
crc = ((crc >> 8) & 0x00FFFFFF) ^ crc_tab[(crc ^ *block++) & 0xFF];
}
return (crc ^ 0xFFFFFFFF);
}
/* chksum_crc32gentab() -- to a global crc_tab[256], this one will
* calculate the crcTable for crc32-checksums.
* it is generated to the polynom [..]
*/
void chksum_crc32gentab ()
{
unsigned long crc, poly;
int i, j;
poly = 0xEDB88320L;
for (i = 0; i < 256; i++)
{
crc = i;
for (j = 8; j > 0; j--)
{
if (crc & 1)
{
crc = (crc >> 1) ^ poly;
}
else
{
crc >>= 1;
}
}
crc_tab[i] = crc;
}
}
//copy strings without end of lines special characters
static void strcpy2(char* dest, char* src, int maxsize)
{
u8 c;
int i=0;
while(i= '0' && s <= '9')
return s - '0';
else if (s >= 'A' && s <= 'F')
return s - 'A' + 10;
else if (s >= 'a' && s <= 'f')
return s - 'a' + 10;
else
return -1;
}
static int parse_hex_byte(const char *s)
{
return (parse_hex_nibble(s[0])<<4) | parse_hex_nibble(s[1]);
}
static int parse_hex_word(const char *s)
{
return (parse_hex_nibble(s[0])<<12) | (parse_hex_nibble(s[1]) << 8) |
(parse_hex_nibble(s[2])<<4) | parse_hex_nibble(s[3]);
}
bool load_hex(const char *in_filename)
{
//http://en.wikipedia.org/wiki/.hex
//(I've added the spaces for clarity, they don't exist in the real files)
//:10 65B0 00 661F771F881F991F1A9469F760957095 59
//:10 65C0 00 809590959B01AC01BD01CF010895F894 91
//:02 65D0 00 FFCF FB
//:02 65D2 00 0100 C6
//:00 0000 01 FF [EOF marker]
//First field is the byte count. Second field is the 16-bit address. Third field is the record type;
//00 is data, 01 is EOF. For record type zero, next "wide" field is the actual data, followed by a
//checksum.
u16 progmemLast=0;
char line[128];
int lineNumber = 1;
bool warned = false;
FILE *in_file = fopen(in_filename,"r");
if (!in_file) return false;
// Set entire memory out first in case new image is shorter than last one (0xff == NOP)
memset(rom.progmem+HEADER_SIZE, 0xff , MAX_PROG_SIZE);
while (fgets(line, sizeof(line), in_file) && line[0]==':')
{
int bytes = parse_hex_byte(line+1);
int addr = parse_hex_word(line+3);
int recordType = parse_hex_byte(line+7);
if (recordType == 0)
{
char *lp = line + 9;
while (bytes > 0)
{
// Check if it went past the 60KB
if(addr < MAX_PROG_SIZE)
{
rom.progmem[addr+HEADER_SIZE] = parse_hex_byte(lp);
addr ++;
if (addr > progmemLast){
progmemLast = addr;
}
}
else if(!warned)
{
fprintf(stderr, "\n\t***Warning***: The hex file has instructions after\n "
"\tthe 60KB mark, which are being ignored and is, probably, incompatible with the\n"
"\tbootloader. Note: This might not be a problem if your hex is a dump from the\n "
"\tchip's flash.\n\n");
warned = true;
}
lp += 2;
bytes -= 1;
}
}
else if (recordType == 1)
{
break;
}
else
fprintf(stderr,"ignoring unknown record type %d in line %d of %s\n",recordType,lineNumber,in_filename);
++lineNumber;
}
rom.header.progSize=progmemLast;
fclose(in_file);
return true;
}
int main(int argc,char **argv)
{
if (argc!=4)
{
fprintf(stderr,"%s ver %i.%i -- Packs a HEX file to binary and adds a header.\n",argv[0],VERSION_MAJOR,VERSION_MINOR);
fprintf(stderr,"usage: %s \n",argv[0]);
fprintf(stderr,"example: %s halloween.hex halloween.uze gameinfo.properties\n",argv[0]);
return 1;
}
fprintf(stderr,"\tPacking file: %s\n",argv[1]);
chksum_crc32gentab();
//parse the games properties file
FILE *file = fopen ( argv[3], "r" );
if ( file )
{
char line [128];
while ( fgets ( line, sizeof(line), file ) != NULL ) /* read a line */
{
if(!strncmp(line,"#",1)){
//ignore comment line
}else if(!strncmp(line,"name=",5)){
strcpy2((char*)rom.header.name,line+5,sizeof(rom.header.name));
fprintf(stderr,"\tGame Name: %s\n", rom.header.name);
}else if(!strncmp(line,"desc=",5)){
strcpy2((char*)rom.header.description,line+5,sizeof(rom.header.description));
fprintf(stderr,"\tDescription: %s\n", rom.header.description);
}else if(!strncmp(line,"author=",7)){
strcpy2((char*)rom.header.author,line+7,sizeof(rom.header.author));
fprintf(stderr,"\tAuthor: %s\n", rom.header.author);
}else if(!strncmp(line,"year=",5)){
rom.header.year=(u16) strtoul(line+5,NULL,10);
fprintf(stderr,"\tYear: %i\n", rom.header.year);
}
}
fclose (file);
}
else
{
fprintf(stderr,"Can't read properties file: %s \n",argv[3]);
return 1;
}
if(!load_hex(argv[1])){
fprintf(stderr,"Could not process HEX file.\n");
return 1;
}
memcpy(rom.header.marker,"UZEBOX",MARKER_SIZE);
rom.header.version=HEADER_VERSION;
rom.header.target=0;
rom.header.crc32=chksum_crc32(rom.progmem+HEADER_SIZE, rom.header.progSize);
fprintf(stderr,"\tCRC32: 0x%lx\n", (long unsigned int) rom.header.crc32);
fprintf(stderr,"\tProgram size: %li \n",
(long unsigned int) rom.header.progSize);
//write the output file
FILE *out_file = fopen(argv[2],"wb");
if(!fwrite(&rom.progmem,rom.header.progSize+HEADER_SIZE,1,out_file)){
fprintf(stderr,"Could not process output file.\n");
return 1;
}
fclose(out_file);
//while (1)
//{
// if ('n' == getchar())
// break;
//}
return 0;
}