Skip to content
This repository has been archived by the owner. It is now read-only.
Permalink
master
Switch branches/tags
Go to file
 
 
Cannot retrieve contributors at this time
/*
makebin - turn a .ihx file into a binary image or GameBoy format binaryimage
Copyright (c) 2000 Michael Hope
Copyright (c) 2010 Borut Razem
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#if defined(_WIN32)
#include <fcntl.h>
#include <io.h>
#else
#include <unistd.h>
#endif
typedef unsigned char BYTE;
#define FILL_BYTE 0xff
int
getnibble (FILE *fin)
{
int ret;
int c = getc (fin);
if (feof (fin) || ferror (fin))
{
fprintf (stderr, "error: unexpected end of file.\n");
exit (6);
}
ret = c - '0';
if (ret > 9)
{
ret -= 'A' - '9' - 1;
}
if (ret > 0xf)
{
ret -= 'a' - 'A';
}
if (ret < 0 || ret > 0xf)
{
fprintf (stderr, "error: character %02x.\n", ret);
exit (7);
}
return ret;
}
int
getbyte (FILE *fin, int *sum)
{
int b = (getnibble (fin) << 4) | getnibble (fin);
*sum += b;
return b;
}
void
usage (void)
{
fprintf (stderr,
"makebin: convert a Intel IHX file to binary or GameBoy format binary.\n"
"Usage: makebin [options] [<in_file> [<out_file>]]\n"
"Options:\n"
" -p pack mode: the binary file size will be truncated to the last occupied byte\n"
" -s romsize size of the binary file (default: 32768)\n"
" -Z genarate GameBoy format binary file\n"
"GameBoy format options (applicable only with -Z option):\n"
" -yo n number of rom banks (default: 2)\n"
" -ya n number of ram banks (default: 0)\n"
" -yt n MBC type (default: no MBC)\n"
" -yn name cartridge name (default: none)\n"
"Arguments:\n"
" <in_file> optional IHX input file, '-' means stdin. (default: stdin)\n"
" <out_file> optional output file, '-' means stdout. (default: stdout)\n");
}
#define CART_NAME_LEN 16
struct gb_opt_s
{
char cart_name[CART_NAME_LEN]; /* cartridge name buffer */
BYTE mbc_type; /* MBC type (default: no MBC) */
short nb_rom_banks; /* Number of rom banks (default: 2) */
BYTE nb_ram_banks; /* Number of ram banks (default: 0) */
};
void
gb_postproc (BYTE * rom, int size, int *real_size, struct gb_opt_s *o)
{
int i, chk;
/*
* 0134-0142: Title of the game in UPPER CASE ASCII. If it
* is less than 16 characters then the
* remaining bytes are filled with 00's.
*/
/* capitalize cartridge name */
for (i = 0; i < CART_NAME_LEN; ++i)
{
rom[0x134 + i] = toupper (o->cart_name[i]);
}
/*
* 0147: Cartridge type:
* 0-ROM ONLY 12-ROM+MBC3+RAM
* 1-ROM+MBC1 13-ROM+MBC3+RAM+BATT
* 2-ROM+MBC1+RAM 19-ROM+MBC5
* 3-ROM+MBC1+RAM+BATT 1A-ROM+MBC5+RAM
* 5-ROM+MBC2 1B-ROM+MBC5+RAM+BATT
* 6-ROM+MBC2+BATTERY 1C-ROM+MBC5+RUMBLE
* 8-ROM+RAM 1D-ROM+MBC5+RUMBLE+SRAM
* 9-ROM+RAM+BATTERY 1E-ROM+MBC5+RUMBLE+SRAM+BATT
* B-ROM+MMM01 1F-Pocket Camera
* C-ROM+MMM01+SRAM FD-Bandai TAMA5
* D-ROM+MMM01+SRAM+BATT FE - Hudson HuC-3
* F-ROM+MBC3+TIMER+BATT FF - Hudson HuC-1
* 10-ROM+MBC3+TIMER+RAM+BATT
* 11-ROM+MBC3
*/
rom[0x147] = o->mbc_type;
/*
* 0148 ROM size:
* 0 - 256Kbit = 32KByte = 2 banks
* 1 - 512Kbit = 64KByte = 4 banks
* 2 - 1Mbit = 128KByte = 8 banks
* 3 - 2Mbit = 256KByte = 16 banks
* 4 - 4Mbit = 512KByte = 32 banks
* 5 - 8Mbit = 1MByte = 64 banks
* 6 - 16Mbit = 2MByte = 128 banks
* $52 - 9Mbit = 1.1MByte = 72 banks
* $53 - 10Mbit = 1.2MByte = 80 banks
* $54 - 12Mbit = 1.5MByte = 96 banks
*/
switch (o->nb_rom_banks)
{
case 2:
rom[0x148] = 0;
break;
case 4:
rom[0x148] = 1;
break;
case 8:
rom[0x148] = 2;
break;
case 16:
rom[0x148] = 3;
break;
case 32:
rom[0x148] = 4;
break;
case 64:
rom[0x148] = 5;
break;
case 128:
rom[0x148] = 6;
break;
case 256:
rom[0x148] = 7;
break;
case 512:
rom[0x148] = 8;
break;
default:
fprintf (stderr, "warning: unsupported number of ROM banks (%d)\n", o->nb_rom_banks);
rom[0x148] = 0;
break;
}
/*
* 0149 RAM size:
* 0 - None
* 1 - 16kBit = 2kB = 1 bank
* 2 - 64kBit = 8kB = 1 bank
* 3 - 256kBit = 32kB = 4 banks
* 4 - 1MBit =128kB =16 banks
*/
switch (o->nb_ram_banks)
{
case 0:
rom[0x149] = 0;
break;
case 1:
rom[0x149] = 2;
break;
case 4:
rom[0x149] = 3;
break;
case 16:
rom[0x149] = 4;
break;
default:
fprintf (stderr, "warning: unsupported number of RAM banks (%d)\n", o->nb_ram_banks);
rom[0x149] = 0;
break;
}
/* Update complement checksum */
chk = 0;
for (i = 0x134; i < 0x14d; ++i)
chk += rom[i];
rom[0x014d] = (unsigned char) (0xe7 - (chk & 0xff));
/* Update checksum */
chk = 0;
rom[0x14e] = 0;
rom[0x14f] = 0;
for (i = 0; i < size; ++i)
chk += rom[i];
rom[0x14e] = (unsigned char) ((chk >> 8) & 0xff);
rom[0x14f] = (unsigned char) (chk & 0xff);
if (*real_size < 0x150)
*real_size = 0x150;
}
int
read_ihx (FILE *fin, BYTE *rom, int size, int *real_size)
{
int record_type;
do
{
int nbytes;
int addr;
int checksum, sum = 0;
if (getc (fin) != ':')
{
fprintf (stderr, "error: invalid IHX line.\n");
return 0;
}
nbytes = getbyte (fin, &sum);
addr = getbyte (fin, &sum) << 8 | getbyte (fin, &sum);
record_type = getbyte (fin, &sum);
if (record_type > 1)
{
fprintf (stderr, "error: unsupported record type: %02x.\n", record_type);
return 0;
}
if (addr + nbytes > size)
{
fprintf (stderr, "error: size of the buffer is too small.\n");
return 0;
}
while (nbytes--)
{
if (addr < size)
rom[addr++] = getbyte (fin, &sum);
}
if (addr > *real_size)
*real_size = addr;
checksum = getbyte (fin, &sum);
if (0 != (sum & 0xff))
{
fprintf (stderr, "error: bad checksum: %02x.\n", checksum);
return 0;
}
while (isspace (sum = getc (fin))) /* skip all kind of speces */
;
ungetc (sum, fin);
}
while (1 != record_type); /* EOF record */
return 1;
}
int
main (int argc, char **argv)
{
int size = 32768, pack = 0, real_size = 0;
BYTE *rom;
FILE *fin, *fout;
int ret;
int gb = 0;
struct gb_opt_s gb_opt = { "", 0, 2, 0 };
#if defined(_WIN32)
setmode (fileno (stdout), O_BINARY);
#endif
while (*++argv && '-' == argv[0][0] && '\0' != argv[0][1])
{
switch (argv[0][1])
{
case 's':
if (!*++argv)
{
usage ();
return 1;
}
size = atoi (*argv);
break;
case 'h':
usage ();
return 0;
case 'p':
pack = 1;
break;
case 'Z':
/* generate GameBoy binary file */
gb = 1;
break;
case 'y':
/* GameBoy options:
* -yo Number of rom banks (default: 2)
* -ya Number of ram banks (default: 0)
* -yt MBC type (default: no MBC)
* -yn Name of program (default: name of output file)
*/
switch (argv[0][2])
{
case 'o':
if (!*++argv)
{
usage ();
return 1;
}
gb_opt.nb_rom_banks = atoi (*argv);
break;
case 'a':
if (!++argv)
{
usage ();
return 1;
}
gb_opt.nb_ram_banks = atoi (*argv);
break;
case 't':
if (!*++argv)
{
usage ();
return 1;
}
gb_opt.mbc_type = atoi (*argv);
break;
case 'n':
if (!*++argv)
{
usage ();
return 1;
}
strncpy (gb_opt.cart_name, *argv, CART_NAME_LEN);
break;
default:
usage ();
return 1;
}
break;
default:
usage ();
return 1;
}
}
fin = stdin;
fout = stdout;
if (*argv)
{
if ('-' != argv[0][0] || '\0' != argv[0][1])
{
if (NULL == (fin = fopen (*argv, "r")))
{
fprintf (stderr, "error: can't open %s: ", *argv);
perror(NULL);
return 1;
}
}
++argv;
}
if (NULL != argv[0] && NULL != argv[1])
{
usage ();
return 1;
}
if (gb && size != 32768)
{
fprintf (stderr, "error: only length of 32768 bytes supported for GameBoy binary.\n");
return 1;
}
rom = malloc (size);
if (rom == NULL)
{
fclose (fin);
fprintf (stderr, "error: couldn't allocate room for the image.\n");
return 1;
}
memset (rom, FILL_BYTE, size);
ret = read_ihx (fin, rom, size, &real_size);
fclose (fin);
if (ret)
{
if (gb)
gb_postproc (rom, size, &real_size, &gb_opt);
if (*argv)
{
if ('-' != argv[0][0] || '\0' != argv[0][1])
{
if (NULL == (fout = fopen (*argv, "wb")))
{
fprintf (stderr, "error: can't create %s: ", *argv);
perror(NULL);
return 1;
}
}
}
fwrite (rom, 1, (pack ? real_size : size), fout);
fclose (fout);
return 0;
}
else
return 1;
}