# HG changeset patch # User Matti Hamalainen # Date 1351901991 -7200 # Node ID 59244a7ae37f422bd3616bbb8a81c0068aba0810 # Parent a0160ffdf7e5b902b6ecc363a44d88317076a03c Move c64 utilities to the engine lib, as we benefit from a common framework. diff -r a0160ffdf7e5 -r 59244a7ae37f Makefile --- a/Makefile Sat Nov 03 01:54:00 2012 +0200 +++ b/Makefile Sat Nov 03 02:19:51 2012 +0200 @@ -3,9 +3,13 @@ # SDL_CFLAGS=`sdl-config --cflags` SDL_LDFLAGS=`sdl-config --static-libs` + TREMOR_CFLAGS=-I/usr/local/lib/ TREMOR_LDFLAGS=/usr/local/lib/libvorbisidec.a /usr/lib/i386-linux-gnu/libogg.a +LIBPNG_CFLAGS=`pkg-config --cflags libpng` -DHAVE_LIBPNG +LIBPNG_CFLAGS=`pkg-config --libs libpng` + RANLIB=ranlib DMLIB = ./ diff -r a0160ffdf7e5 -r 59244a7ae37f Makefile.gen --- a/Makefile.gen Sat Nov 03 01:54:00 2012 +0200 +++ b/Makefile.gen Sat Nov 03 02:19:51 2012 +0200 @@ -189,11 +189,14 @@ ### Dependancies ifeq ($(DM_BUILD_TOOLS),yes) +DMLIB_OBJS += lib64gfx.o ifeq ($(DM_USE_STDIO),yes) +BINARIES+= objlink data2inc ifeq ($(SUP_MODLOAD),yes) BINARIES+= viewmod mod2wav testpl +ifeq ($(DM_GFX_BLITS),yes) +BINARIES+= view64 ifeq ($(DM_GFX_BM_TEXT),yes) -ifeq ($(DM_GFX_BLITS),yes) ifeq ($(DM_GFX_MISC),yes) BINARIES+= ppl endif @@ -366,6 +369,24 @@ @echo " LINK $+" @$(CC) -o $@ $(filter %.o %.a,$+) $(DM_LDFLAGS) $(SDL_LDFLAGS) + +$(BINPATH)objlink$(EXEEXT): $(OBJPATH)objlink.o $(DMLIB_A) + @echo " LINK $+" + @$(CC) -o $@ $(filter %.o %.a,$+) $(DM_LDFLAGS) + +$(BINPATH)data2inc$(EXEEXT): $(OBJPATH)data2inc.o $(DMLIB_A) + @echo " LINK $+" + @$(CC) -o $@ $(filter %.o %.a,$+) $(DM_LDFLAGS) + +$(BINPATH)gfxconv$(EXEEXT): $(OBJPATH)gfxconv.o $(DMLIB_A) + @echo " LINK $+" + @$(CC) -o $@ $(filter %.o %.a,$+) $(DM_LDFLAGS) $(LIBPNG_CFLAGS) $(LIBPNG_LDFLAGS) + +$(BINPATH)view64$(EXEEXT): $(OBJPATH)view64.o $(DMLIB_A) + @echo " LINK $+" + @$(CC) -o $@ $(filter %.o %.a,$+) $(DM_LDFLAGS) $(SDL_LDFLAGS) + + ### ### Editor targets ### diff -r a0160ffdf7e5 -r 59244a7ae37f data2inc.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/data2inc.c Sat Nov 03 02:19:51 2012 +0200 @@ -0,0 +1,404 @@ +/* + * data2inc - Convert binary data to "C"-source or XA-compatible include file + * Programmed and designed by Matti 'ccr' Hamalainen + * (C) Copyright 2003,2009-2012 Tecnic Software productions (TNSP) + * + * Please read file 'COPYING' for information on license and distribution. + */ +#include +#include "dmlib.h" +#include "dmargs.h" +#include "dmmutex.h" + +#define RA_LINEBUF (16) + +enum +{ + FMT_AUTO = 0, + FMT_C, + FMT_ASM +}; + +char *optInFilename = NULL, + *optOutFilename = NULL, + *optObjName = "default_object", + *optDataType = NULL, + *optAddLine = NULL; + +int optIndentation = -1; +int optFormat = FMT_AUTO; +BOOL optHexMode = FALSE, + optQuiet = FALSE, + optExtraData = FALSE, + optFormatting = TRUE; + + +void (*writeHeader) (FILE *, char *) = NULL; +void (*writeDecl) (FILE *, size_t, char *) = NULL; +void (*writeData) (FILE *, Uint8 *, size_t) = NULL; +void (*writeFooter) (FILE *, size_t, char *) = NULL; + + +static DMOptArg optList[] = +{ + { 0, '?', "help", "Show this help", OPT_NONE }, + { 4, 'A', "format-asm", "Output in XA-compatible asm", OPT_NONE }, + { 5, 'C', "format-c", "Output in ANSI C", OPT_NONE }, + { 1, 'n', "name", "Set object name", OPT_ARGREQ }, + { 2, 't', "type", "Set datatype (unsigned char/byte)", OPT_ARGREQ }, + { 3, 'a', "add-line", "Add this line to start of file", OPT_ARGREQ }, + { 6, 'x', "hexadecimal", "Use hexadecimal output", OPT_NONE }, + { 7, 'q', "quiet", "Do not add comments", OPT_NONE }, + { 8, 'f', "no-formatting", "Disable additional output formatting", OPT_NONE }, + { 9, 'i', "indentation", "Set indentation (negative value = tab)", OPT_ARGREQ }, + { 10, 'e', "extra-data", "Add object end labels and size in asm output", OPT_NONE }, +}; + +static const int optListN = sizeof(optList) / sizeof(optList[0]); + + +void argShowHelp() +{ + dmPrintBanner(stdout, dmProgName, + "[options] [sourcefile] [destfile]"); + + dmArgsPrintHelp(stdout, optList, optListN); + + printf( + "\n" + "To convert a data file to a C structure using 'Uint8' as type:\n" + "$ data2inc -C -n variable_name -t Uint8 input.bin output.h\n" + "\n" + ); +} + + +BOOL argHandleOpt(const int optN, char *optArg, char *currArg) +{ + switch (optN) + { + case 0: + argShowHelp(); + exit(0); + break; + + case 1: + optObjName = optArg; + break; + + case 2: + optDataType = optArg; + break; + + case 3: + optAddLine = optArg; + break; + + case 4: + optFormat = FMT_ASM; + break; + case 5: + optFormat = FMT_C; + break; + case 6: + optHexMode = TRUE; + break; + case 7: + optQuiet = TRUE; + break; + case 8: + optFormatting = FALSE; + break; + + case 9: + optIndentation = atoi(optArg); + break; + + case 10: + optExtraData = TRUE; + break; + + default: + dmError("Unknown option '%s'.\n", currArg); + return FALSE; + } + + return TRUE; +} + + +BOOL argHandleFile(char * currArg) +{ + if (!optInFilename) + optInFilename = currArg; + else + if (!optOutFilename) + optOutFilename = currArg; + else + dmError("Source and destination filenames already specified, extraneous argument '%s'.\n", currArg); + + return TRUE; +} + + +/* Assembler include data output functions + */ +void writeHeader_ASM(FILE * f, char *name) +{ + if (name) + fprintf(f, "; '%s'", name); + else + fprintf(f, "; Generated"); + fprintf(f, " by %s v%s\n", + dmProgName, dmProgVersion); +} + +void writeDecl_ASM(FILE * f, size_t len, char *name) +{ + if (optExtraData) + fprintf(f, "%s_size = %u\n", name, len); + fprintf(f, "%s:\n", name); +} + +void writeData_ASM(FILE * f, Uint8 * buf, size_t len) +{ + size_t i; + + fprintf(f, "%s ", optDataType); + for (i = 0; i < len; i++) + { + if (optFormatting) + { + if (optHexMode) + fprintf(f, "$%.2x", buf[i]); + else + fprintf(f, "%3d", buf[i]); + } + else + { + if (optHexMode) + fprintf(f, "$%x", buf[i]); + else + fprintf(f, "%d", buf[i]); + } + + if (i < (len - 1)) + fprintf(f, ","); + } +} + +void writeFooter_ASM(FILE * f, size_t len, char *name) +{ + (void) len; + if (optExtraData) + fprintf(f, "%s_end: \n", name); + else + fprintf(f, "\n"); +} + + +/* ANSI-C include data output functions + */ +void writeHeader_C(FILE * f, char *name) +{ + if (name) + fprintf(f, "/* '%s' generated", name); + else + fprintf(f, "/* Generated"); + + fprintf(f, " by %s v%s\n" " */\n", + dmProgName, dmProgVersion); +} + +void writeDecl_C(FILE * f, size_t len, char *name) +{ + fprintf(f, "const %s %s[%u] = {\n", + optDataType, name, len); + + printf("extern const %s %s[%u];\n", + optDataType, name, len); +} + +void writeData_C(FILE * f, Uint8 * buf, size_t len) +{ + size_t i; + + for (i = 0; i < len; i++) + { + if (optFormatting) + { + if (optHexMode) + fprintf(f, "0x%.2x", buf[i]); + else + fprintf(f, "%3d", buf[i]); + } + else + { + if (optHexMode) + fprintf(f, "0x%x", buf[i]); + else + fprintf(f, "%d", buf[i]); + } + + fprintf(f, ","); + } +} + +void writeFooter_C(FILE * f, size_t len, char *name) +{ + (void) len; + (void) name; + fprintf(f, "};\n"); +} + + +off_t dmGetFileSize(FILE *f) +{ + off_t len, pos = ftell(f); + fseeko(f, 0, SEEK_END); + len = ftell(f); + fseek(f, pos, SEEK_SET); + return len; +} + + +int main(int argc, char *argv[]) +{ + FILE *sfile = NULL, *dfile = NULL; + off_t inSize; + Uint8 inBuf[RA_LINEBUF]; + int tmpRes; + + /* Initialize */ + dmInitProg("data2inc", "Data to include converter", "0.6", NULL, NULL); + dmVerbosity = 0; + + /* Parse arguments */ + if (!dmArgsProcess(argc, argv, optList, optListN, + argHandleOpt, argHandleFile, TRUE)) + exit(1); + + /* Determine output type, if not specified */ + if (optFormat == FMT_AUTO) + { + char *dext; + + if (optOutFilename == NULL) + { + dmError("Output format not specified and no output filename given (try --help)\n"); + exit(1); + } + + /* Check filename extension */ + dext = strrchr(optOutFilename, '.'); + if (dext) + { + dext++; + if (!strcasecmp(dext, "c") || !strcasecmp(dext, "h") || + !strcasecmp(dext, "cc") || !strcasecmp(dext, "cpp") || + !strcasecmp(dext, "hpp") || !strcasecmp(dext, "c++")) + optFormat = FMT_C; + else + optFormat = FMT_ASM; + } else + optFormat = FMT_ASM; + } + + /* Set functions */ + switch (optFormat) + { + case FMT_ASM: + if (!optDataType) + optDataType = ".byte"; + + writeHeader = writeHeader_ASM; + writeDecl = writeDecl_ASM; + writeData = writeData_ASM; + writeFooter = writeFooter_ASM; + break; + + case FMT_C: + if (!optDataType) + optDataType = "unsigned char"; + + writeHeader = writeHeader_C; + writeDecl = writeDecl_C; + writeData = writeData_C; + writeFooter = writeFooter_C; + break; + + case FMT_AUTO: + default: + dmError("Internal error, FMT_AUTO at output function init.\n"); + exit(2); + } + + /* Open the files */ + if (optInFilename == NULL) + sfile = stdin; + else + if ((sfile = fopen(optInFilename, "rb")) == NULL) + { + tmpRes = errno; + dmError("Error opening input file '%s'. (%s)\n", + optInFilename, strerror(tmpRes)); + exit(3); + } + + if (optOutFilename == NULL) + dfile = stdout; + else + if ((dfile = fopen(optOutFilename, "wa")) == NULL) + { + tmpRes = errno; + dmError("Error creating output file '%s'. (%s)\n", + optOutFilename, strerror(tmpRes)); + exit(4); + } + + /* Get sourcefile size */ + inSize = dmGetFileSize(sfile); + + /* Output header */ + if (!optQuiet) + writeHeader(dfile, optOutFilename); + + if (optAddLine) + fprintf(dfile, "%s\n", optAddLine); + + /* Output declaration */ + writeDecl(dfile, inSize, optObjName); + + /* Output data */ + while (!feof(sfile)) + { + tmpRes = fread(inBuf, sizeof(Uint8), RA_LINEBUF, sfile); + if (tmpRes > 0) + { + if (optIndentation < 0) + fprintf(dfile, "\t"); + else + if (optIndentation > 0) + { + int i; + for (i = 0; i < optIndentation; i++) + fputs(" ", dfile); + } + + writeData(dfile, inBuf, tmpRes); + + fprintf(dfile, "\n"); + } + } + + + /* Output footer */ + writeFooter(dfile, inSize, optObjName); + + /* Exit */ + fclose(sfile); + fclose(dfile); + + exit(0); + return 0; +} diff -r a0160ffdf7e5 -r 59244a7ae37f gfxconv.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gfxconv.c Sat Nov 03 02:19:51 2012 +0200 @@ -0,0 +1,1630 @@ +/* + * gfxconv - Convert various graphics formats + * Programmed and designed by Matti 'ccr' Hamalainen + * (C) Copyright 2012 Tecnic Software productions (TNSP) + * + * Please read file 'COPYING' for information on license and distribution. + */ +#include +#include "dmlib.h" +#include "dmargs.h" +#include "dmfile.h" +#include "dmmutex.h" +#include "lib64gfx.h" + +//#define UNFINISHED 1 + +#ifdef HAVE_LIBPNG +#include +#endif + +enum +{ + INFMT_AUTO = 0, + INFMT_CHAR, + INFMT_SPRITE, + INFMT_BITMAP, + INFMT_IMAGE, +}; + +enum +{ + OUTFMT_ASCII, + OUTFMT_ANSI, + OUTFMT_PNG, + OUTFMT_PPM, + OUTFMT_PCX, + OUTFMT_ARAW, + +#ifdef UNFINISHED + OUTFMT_SPRITE, + OUTFMT_CHAR, +#endif + + OUTFMT_LAST +}; + +char * outFormatList[OUTFMT_LAST] = +{ + "ascii", + "ansi", + "png", + "ppm", + "pcx", + "araw", +#ifdef UNFINISHED + "spr", + "char", +#endif +}; + +static const int noutFormatList = sizeof(outFormatList) / sizeof(outFormatList[0]); + + +#define ASC_NBITS 8 +#define ASC_NCOLORS 4 +static const char dmASCIIPalette[ASC_NCOLORS] = ".:X#"; + + +char *optInFilename = NULL, + *optOutFilename = NULL; +int optInFormat = INFMT_AUTO, + optOutFormat = OUTFMT_ASCII, + optItemCount = -1, + optScale = 2, + optPlanedWidth = 1, + optBPP = 4; +int optInSkip = 0; +BOOL optInMulticolor = FALSE, + optSequential = FALSE, + optPaletted = FALSE; +int optColors[C64_MAX_COLORS]; + + +static DMOptArg optList[] = +{ + { 0, '?', "help", "Show this help", OPT_NONE }, + { 3, 'o', "output", "Output filename", OPT_ARGREQ }, + { 1, 'i', "informat", "Set input format ([s]prite, [c]har, [b]itmap)", OPT_ARGREQ }, + { 2, 'm', "multicolor", "Input is multicolor", OPT_NONE }, + { 4, 's', "skip", "Skip bytes in input", OPT_ARGREQ }, + { 5, 'f', "format", "Output format (see list below)", OPT_ARGREQ }, + { 8, 'q', "sequential", "Output sequential files (image output only)", OPT_NONE }, + { 6, 'c', "colormap", "Color mappings (see below for information)", OPT_ARGREQ }, + { 7, 'n', "numitems", "How many 'items' to view (default: all)", OPT_ARGREQ }, + { 9, 'S', "scale", "Scale output by x (image output only)", OPT_ARGREQ }, +#ifdef UNFINISHED + {10, 'b', "bformat", "Force input bitmap format (see below)", OPT_ARGREQ }, +#endif + {11, 'w', "width", "Item width (number of items per row, min 1)", OPT_ARGREQ }, + {12, 'P', "paletted", "Use indexed/paletted output (png, pcx output only)", OPT_NONE }, + {13, 'b', "bpp", "Bits per pixel (certain image output formats)", OPT_ARGREQ }, +}; + +static const int optListN = sizeof(optList) / sizeof(optList[0]); + + +void argShowHelp() +{ + int i; + + dmPrintBanner(stdout, dmProgName, "[options] "); + dmArgsPrintHelp(stdout, optList, optListN); + + printf("\nAvailable output formats: "); + for (i = 0; i < noutFormatList; i++) + { + printf("%s", outFormatList[i]); + if (i < noutFormatList - 1) + printf(", "); + else + printf("\n"); + } + +#ifdef UNFINISHED + printf("\nAvailable bitmap formats:\n"); + for (i = 0; i < ndmC64ImageFormats; i++) + { + DM64ImageFormat *fmt = &dmC64ImageFormats[i]; + printf("%3d | %-5s | %-15s | %s\n", + i, fmt->extension, + dmC64ImageTypeNames[fmt->type], + fmt->name); + } +#endif + + printf( + "\n" + "Color map definitions are used for ANSI, PCX, PPM and PNG output, to declare what\n" + "output colors of the C64 palette are used for each single color/multi color\n" + "bit-combination. For example, if the input is multi color sprite or char,\n" + "you can define colors like: -c 0,8,3,15 .. for single color: -c 0,1\n" + "The numbers are palette indexes, and the order is for bit(pair)-values\n" + "00, 01, 10, 11 (multi color) and 0, 1 (single color). NOTICE! 255 is the\n" + "special color that can be used for transparency.\n" + ); +} + + +BOOL argHandleOpt(const int optN, char *optArg, char *currArg) +{ + switch (optN) + { + case 0: + argShowHelp(); + exit(0); + break; + + case 1: + switch (tolower(optArg[0])) + { + case 's': + optInFormat = INFMT_SPRITE; + break; + case 'c': + optInFormat = INFMT_CHAR; + break; + default: + dmError("Invalid input format '%s'.\n", optArg); + return FALSE; + } + break; + + case 2: + optInMulticolor = TRUE; + break; + + case 3: + optOutFilename = optArg; + break; + + case 4: + if (!dmGetIntVal(optArg, &optInSkip)) + { + dmError("Invalid skip value argument '%s'.\n", optArg); + return FALSE; + } + break; + + case 5: + { + int i, format = -1; + for (i = 0; i < noutFormatList; i++) + if (strcasecmp(optArg, outFormatList[i]) == 0) + { + format = i; + break; + } + + if (format < 0) + { + dmError("Invalid output format '%s'.\n", optArg); + return FALSE; + } + + optOutFormat = format; + } + break; + + case 6: + { + int index = 0, tmp; + char *s, *p = optArg; + + while (index < C64_MAX_COLORS && *p != 0 && (s = strchr(p, ':')) != NULL) + { + *s = 0; + if (sscanf(p, "%d", &tmp) == 1) + optColors[index++] = tmp; + p = s + 1; + } + + if (*p && index < C64_MAX_COLORS) + { + if (sscanf(p, "%d", &tmp) == 1) + optColors[index++] = tmp; + } + + dmMsg(1, "Set color table: "); + for (tmp = 0; tmp < index; tmp++) + { + dmPrint(1, "[%d:%d]%s", + tmp, optColors[tmp], + (tmp < index - 1) ? ", " : ""); + } + dmPrint(1, "\n"); + } + break; + + case 7: + if (sscanf(optArg, "%d", &optItemCount) != 1) + { + dmError("Invalid count value argument '%s'.\n", optArg); + return FALSE; + } + break; + + case 8: + optSequential = TRUE; + break; + + case 9: + { + int tmp = atoi(optArg); + if (tmp < 1 || tmp > 50) + { + dmError("Invalid scale value '%s'.\n", optArg); + return FALSE; + } + optScale = tmp; + } + break; + + case 11: + { + int tmp = atoi(optArg); + if (tmp < 1 || tmp > 512) + { + dmError("Invalid width value '%s'.\n", optArg); + return FALSE; + } + optPlanedWidth = tmp; + } + break; + + case 12: + optPaletted = TRUE; + break; + + default: + dmError("Unknown option '%s'.\n", currArg); + return FALSE; + } + + return TRUE; +} + + +BOOL argHandleFile(char *currArg) +{ + if (!optInFilename) + optInFilename = currArg; + else + { + dmError("Source filename already specified, extraneous argument '%s'.\n", + currArg); + return FALSE; + } + + return TRUE; +} + + +void dmPrintByte(FILE *out, int byte, int format, BOOL multicolor) +{ + int i; + + if (multicolor) + { + for (i = ASC_NBITS; i; i -= 2) + { + int val = (byte & (3ULL << (i - 2))) >> (i - 2); + char ch; + switch (format) + { + case OUTFMT_ASCII: + ch = dmASCIIPalette[val]; + fprintf(out, "%c%c", ch, ch); + break; + case OUTFMT_ANSI: + fprintf(out, "%c[0;%d;%dm##%c[0m", + 0x1b, + 1, + 31 + optColors[val], + 0x1b); + break; + } + } + } + else + { + for (i = ASC_NBITS; i; i--) + { + int val = (byte & (1ULL << (i - 1))) >> (i - 1); + char ch; + switch (format) + { + case OUTFMT_ASCII: + ch = val ? '#' : '.'; + fputc(ch, out); + break; + case OUTFMT_ANSI: + fprintf(out, "%c[0;%d;%dm %c[0m", + 0x1b, + 1, + 31 + optColors[val], + 0x1b); + break; + } + } + } +} + + +void dmDumpCharASCII(FILE *outFile, const uint8_t *buf, int *offs, int format, BOOL multicolor) +{ + int yc; + + for (yc = 0; yc < C64_CHR_HEIGHT; yc++) + { + fprintf(outFile, "%04x : ", *offs); + dmPrintByte(outFile, buf[yc], format, multicolor); + fprintf(outFile, "\n"); + (*offs)++; + } +} + + +void dmDumpSpriteASCII(FILE *outFile, const uint8_t *buf, int *offs, int format, BOOL multicolor) +{ + int bufOffs, xc, yc; + + for (bufOffs = yc = 0; yc < C64_SPR_HEIGHT; yc++) + { + fprintf(outFile, "%04x : ", *offs); + for (xc = 0; xc < C64_SPR_WIDTH; xc++) + { + dmPrintByte(outFile, buf[bufOffs], format, multicolor); + fprintf(outFile, " "); + bufOffs++; + (*offs)++; + } + fprintf(outFile, "\n"); + } + (*offs)++; +} + + +int dmWriteImageData(DMImage *img, void *cbdata, BOOL (*writeRowCB)(void *, uint8_t *, size_t), int scale, int format) +{ + int x, y, yscale, xscale, res = 0, rowSize, rowWidth; + uint8_t *row = NULL; + + // Allocate memory for row buffer + rowWidth = img->width * scale; + rowSize = rowWidth * dmImageGetBytesPerPixel(format); + + if ((row = dmMalloc(rowSize + 16)) == NULL) + { + res = -16; + goto done; + } + + // Generate the image + for (y = 0; y < img->height; y++) + { + uint8_t *ptr = row, + *ptr1 = row, + *ptr2 = ptr1 + rowWidth, + *ptr3 = ptr2 + rowWidth; + + for (x = 0; x < img->width; x++) + { + uint8_t c = img->data[(y * img->pitch) + x], qr, qg, qb, qa; + switch (format) + { + case DM_IFMT_PALETTE: + for (xscale = 0; xscale < scale; xscale++) + *ptr++ = c; + break; + + case DM_IFMT_RGBA: + qr = img->pal[c].r; + qg = img->pal[c].g; + qb = img->pal[c].b; + qa = (c == img->ctrans) ? 0 : 255; + + for (xscale = 0; xscale < scale; xscale++) + { + *ptr++ = qr; + *ptr++ = qg; + *ptr++ = qb; + *ptr++ = qa; + } + break; + + case DM_IFMT_RGB: + qr = img->pal[c].r; + qg = img->pal[c].g; + qb = img->pal[c].b; + + for (xscale = 0; xscale < scale; xscale++) + { + *ptr++ = qr; + *ptr++ = qg; + *ptr++ = qb; + } + break; + + case DM_IFMT_RGB_PLANE: + qr = img->pal[c].r; + qg = img->pal[c].g; + qb = img->pal[c].b; + + for (xscale = 0; xscale < scale; xscale++) + { + *ptr1++ = qr; + *ptr2++ = qg; + *ptr3++ = qb; + } + break; + } + } + + for (yscale = 0; yscale < scale; yscale++) + { + if (!writeRowCB(cbdata, row, rowSize)) + { + res = -32; + goto done; + } + } + } + +done: + dmFree(row); + return res; +} + + +#define DMCOL(x) (((x) >> 4) & 0xf) + +int dmWriteIFFMasterRAWPalette(const char *filename, DMImage *img, int ncolors) +{ + FILE *fp; + int i; + + if ((fp = fopen(filename, "w")) == NULL) + { + dmError("IFFMasterRAW: Could not open file '%s' for writing.\n", filename); + return -15; + } + + for (i = 0; i < ncolors; i++) + { + int color; + if (i < img->ncolors) + { + color = (DMCOL(img->pal[i].r) << 8) | + (DMCOL(img->pal[i].g) << 4) | + (DMCOL(img->pal[i].b)); + } + else + color = 0; + + fprintf(fp, "\tdc.w $%04X\n", color); + } + + return 0; +} + + +typedef struct +{ + int bpp; + DMImage *img; + FILE *fp; +} DMRawData; + + +static BOOL dmWriteIFFMasterRAWRow(void *cbdata, uint8_t *row, size_t len) +{ + DMRawData *raw = (DMRawData *) cbdata; + size_t i; + + for (i = 0; i < len; i++) + { + } + + return fwrite(row, sizeof(uint8_t), len, raw->fp) == len; +} + + +int dmWriteIFFMasterRAWImageFILE(FILE *fp, DMImage *img, int scale, int bpp) +{ + DMRawData raw; + + raw.fp = fp; + raw.img = img; + raw.bpp = bpp; + + return dmWriteImageData(img, (void *) &raw, dmWriteIFFMasterRAWRow, scale, DM_IFMT_PALETTE); +} + +int dmWriteIFFMasterRAWImage(const char *filename, DMImage *img, int scale, int bpp) +{ + FILE *fp; + int res; + + if ((fp = fopen(filename, "wb")) == NULL) + { + dmError("IFFMasterRAW: Could not open file '%s' for writing.\n", filename); + return -15; + } + + res = dmWriteIFFMasterRAWImageFILE(fp, img, scale, bpp); + + fclose(fp); + return res; +} + + +static BOOL dmWritePPMRow(void *cbdata, uint8_t *row, size_t len) +{ + return fwrite(row, sizeof(uint8_t), len, (FILE *) cbdata) == len; +} + + +int dmWritePPMImageFILE(FILE *fp, DMImage *img, int scale) +{ + // Write PPM header + fprintf(fp, + "P6\n%d %d\n255\n", + img->width * scale, img->height * scale); + + // Write image data + return dmWriteImageData(img, (void *) fp, dmWritePPMRow, scale, DM_IFMT_RGB); +} + + +int dmWritePPMImage(const char *filename, DMImage *img, int scale) +{ + FILE *fp; + int res; + + // Create output file + if ((fp = fopen(filename, "wb")) == NULL) + { + dmError("PPM: could not open file '%s' for writing.\n", filename); + return -15; + } + + res = dmWritePPMImageFILE(fp, img, scale); + + fclose(fp); + return res; +} + + +#ifdef HAVE_LIBPNG +static BOOL dmWritePNGRow(void *cbdata, uint8_t *row, size_t len) +{ + png_structp png_ptr = cbdata; + (void) len; + + if (setjmp(png_jmpbuf(png_ptr))) + return FALSE; + + png_write_row(png_ptr, row); + + return TRUE; +} + + +int dmWritePNGImageFILE(FILE *fp, DMImage *img, int scale, int format) +{ + png_structp png_ptr = NULL; + png_infop info_ptr = NULL; + png_colorp palette = NULL; + int fmt; + + // Create PNG structures + png_ptr = png_create_write_struct( + PNG_LIBPNG_VER_STRING, + NULL, NULL, NULL); + + if (png_ptr == NULL) + { + dmError("PNG: png_create_write_struct() failed.\n"); + goto error; + } + + info_ptr = png_create_info_struct(png_ptr); + if (info_ptr == NULL) + { + dmError("PNG: png_create_info_struct(%p) failed.\n", png_ptr); + goto error; + } + + if (setjmp(png_jmpbuf(png_ptr))) + { + dmError("PNG: Error during image writing..\n"); + goto error; + } + + png_init_io(png_ptr, fp); + + // Write PNG header info + switch (format) + { + case DM_IFMT_PALETTE: fmt = PNG_COLOR_TYPE_PALETTE; break; + case DM_IFMT_RGB : fmt = PNG_COLOR_TYPE_RGB; break; + case DM_IFMT_RGBA : fmt = PNG_COLOR_TYPE_RGB_ALPHA; break; + default: + dmError("PNG: Internal error, unsupported image format %d.\n", format); + goto error; + } + + png_set_IHDR(png_ptr, info_ptr, + img->width * scale, + img->height * scale, + 8, /* bits per component */ + fmt, + PNG_INTERLACE_NONE, + PNG_COMPRESSION_TYPE_DEFAULT, + PNG_FILTER_TYPE_DEFAULT); + + // Palette + if (format == DM_IFMT_PALETTE) + { + int i; + + palette = png_malloc(png_ptr, PNG_MAX_PALETTE_LENGTH * sizeof(png_color)); + if (palette == NULL) + { + dmError("PNG: Could not allocate palette structure."); + goto error; + } + + memset(palette, 0, PNG_MAX_PALETTE_LENGTH * sizeof(png_color)); + + for (i = 0; i < img->ncolors; i++) + { + palette[i].red = img->pal[i].r; + palette[i].green = img->pal[i].g; + palette[i].blue = img->pal[i].b; + } + + png_set_PLTE(png_ptr, info_ptr, palette, PNG_MAX_PALETTE_LENGTH); + } + +// png_set_gAMA(png_ptr, info_ptr, 2.2); + + png_write_info(png_ptr, info_ptr); + + + // Write compressed image data + dmWriteImageData(img, (void *) png_ptr, dmWritePNGRow, scale, format); + + // Write footer + png_write_end(png_ptr, NULL); + + png_free(png_ptr, palette); + palette = NULL; + + // Deallocate shit + if (png_ptr && info_ptr) + { + png_destroy_write_struct(&png_ptr, &info_ptr); + } + + return 0; + +error: + png_free(png_ptr, palette); + palette = NULL; + + if (png_ptr && info_ptr) + { + png_destroy_write_struct(&png_ptr, &info_ptr); + } + return -15; +} + + +int dmWritePNGImage(const char *filename, DMImage *img, int scale, int format) +{ + int res; + FILE *fp; + + if ((fp = fopen(filename, "wb")) == NULL) + { + dmError("PNG: could not open file '%s' for writing.\n", filename); + return -15; + } + + res = dmWritePNGImageFILE(fp, img, scale, format); + + fclose(fp); + return res; +} +#endif + + +typedef struct +{ + uint8_t r,g,b; +} DMPCXColor; + + +typedef struct +{ + uint8_t manufacturer, + version, + encoding, + bpp; + uint16_t xmin, ymin, xmax, ymax; + uint16_t hres, vres; + DMPCXColor colormap[16]; + uint8_t reserved; + uint8_t nplanes; + uint16_t bpl; + uint16_t palinfo; + uint8_t filler[58]; +} DMPCXHeader; + +typedef struct +{ + DMPCXHeader *header; + uint8_t *buf; + size_t bufLen, bufOffs; + int format; + FILE *fp; +} DMPCXData; + + +static inline uint8_t dmPCXGetByte(uint8_t *row, const size_t len, const size_t soffs) +{ + return (soffs < len) ? row[soffs] : 0; +} + +static BOOL dmPCXFlush(DMPCXData *pcx) +{ + BOOL ret = fwrite(pcx->buf, sizeof(uint8_t), pcx->bufOffs, pcx->fp) == pcx->bufOffs; + pcx->bufOffs = 0; + return ret; +} + +static inline BOOL dmPCXPutByte(DMPCXData *pcx, const uint8_t val) +{ + if (pcx->bufOffs < pcx->bufLen) + { + pcx->buf[pcx->bufOffs++] = val; + return TRUE; + } + else + return dmPCXFlush(pcx); +} + +BOOL dmWritePCXRow(void *cbdata, uint8_t *row, size_t len) +{ + DMPCXData *pcx = (DMPCXData *) cbdata; + int plane; + size_t soffs = 0; + + for (plane = 0; plane < pcx->header->nplanes; plane++) + { + uint8_t data = dmPCXGetByte(row, len, soffs++), + count = 1; + + pcx->bufOffs = 0; + + while (soffs < pcx->header->bpl) + { + if (data == dmPCXGetByte(row, len, soffs) && count < 63) + { + count++; + soffs++; + } + else + { + if (count == 1 && (data & 0xC0) != 0xC0) + { + if (!dmPCXPutByte(pcx, data)) + return FALSE; + } + else + { + if (!dmPCXPutByte(pcx, 0xC0 | count) || + !dmPCXPutByte(pcx, data)) + return FALSE; + } + + data = dmPCXGetByte(row, len, soffs++); + count = 1; + } + } + + if (count > 1) + { + if (!dmPCXPutByte(pcx, 0xC0 | count) || + !dmPCXPutByte(pcx, data)) + return FALSE; + } + + if (!dmPCXFlush(pcx)) + return FALSE; + } + + return TRUE; +} + + +int dmWritePCXImage(const char *filename, DMImage *img, int scale, BOOL paletted) +{ + DMPCXData pcx; + DMPCXHeader hdr; + int res; + + // Create output file + pcx.buf = NULL; + pcx.format = paletted ? DM_IFMT_PALETTE : DM_IFMT_RGB_PLANE; + pcx.header = &hdr; + if ((pcx.fp = fopen(filename, "wb")) == NULL) + { + dmError("PCX: Could not open file '%s' for writing.\n", filename); + res = -15; + goto error; + } + + // Create PCX header + memset(&hdr, 0, sizeof(hdr)); + if (paletted) + { + int i; + for (i = 0; i < (img->ncolors > 16 ? 16 : img->ncolors); i++) + { + hdr.colormap[i].r = img->pal[i].r; + hdr.colormap[i].g = img->pal[i].g; + hdr.colormap[i].b = img->pal[i].b; + } + } + hdr.manufacturer = 10; + hdr.version = 5; + hdr.encoding = 1; + hdr.bpp = 8; + hdr.hres = img->width * scale; + hdr.vres = img->height * scale; + hdr.xmin = hdr.ymin = 0; + hdr.xmax = hdr.hres - 1; + hdr.ymax = hdr.vres - 1; + hdr.nplanes = dmImageGetBytesPerPixel(pcx.format); + hdr.bpl = (((img->width * scale) / 2) + 1) * 2; + hdr.palinfo = 1; + + dmMsg(1, "PCX: paletted=%d, nplanes=%d, bpp=%d, bpl=%d\n", + paletted, hdr.nplanes, hdr.bpp, hdr.bpl); + + pcx.bufLen = hdr.bpl * 4; + if ((pcx.buf = dmMalloc(pcx.bufLen)) == NULL) + { + dmError("PCX: Could not allocate %d bytes for RLE compression buffer.\n", + pcx.bufLen); + res = -11; + goto error; + } + + // Write PCX header + if (!dm_fwrite_byte(pcx.fp, hdr.manufacturer) || + !dm_fwrite_byte(pcx.fp, hdr.version) || + !dm_fwrite_byte(pcx.fp, hdr.encoding) || + !dm_fwrite_byte(pcx.fp, hdr.bpp)) + { + dmError("PCX: Could not write basic header data.\n"); + res = -10; + goto error; + } + + if (!dm_fwrite_le16(pcx.fp, hdr.xmin) || + !dm_fwrite_le16(pcx.fp, hdr.ymin) || + !dm_fwrite_le16(pcx.fp, hdr.xmax) || + !dm_fwrite_le16(pcx.fp, hdr.ymax) || + !dm_fwrite_le16(pcx.fp, hdr.hres) || + !dm_fwrite_le16(pcx.fp, hdr.vres)) + { + dmError("PCX: Could not write image dimensions.\n"); + res = -9; + goto error; + } + + if (!dm_fwrite_str(pcx.fp, (uint8_t *) &hdr.colormap, sizeof(hdr.colormap))) + { + dmError("PCX: Could not write colormap.\n"); + res = -8; + goto error; + } + + if (!dm_fwrite_byte(pcx.fp, hdr.reserved) || + !dm_fwrite_byte(pcx.fp, hdr.nplanes) || + !dm_fwrite_le16(pcx.fp, hdr.bpl) || + !dm_fwrite_le16(pcx.fp, hdr.palinfo) || + !dm_fwrite_str(pcx.fp, (uint8_t *) &hdr.filler, sizeof(hdr.filler))) + { + dmError("PCX: Could not write header remainder.\n"); + res = -7; + goto error; + } + + // Write image data + res = dmWriteImageData(img, (void *) &pcx, dmWritePCXRow, scale, pcx.format); + + // Write VGA palette + if (paletted) + { + int i; + dm_fwrite_byte(pcx.fp, 0x0C); + + for (i = 0; i < img->ncolors; i++) + { + dm_fwrite_byte(pcx.fp, img->pal[i].r); + dm_fwrite_byte(pcx.fp, img->pal[i].g); + dm_fwrite_byte(pcx.fp, img->pal[i].b); + } + + // Pad the palette, if necessary + for (; i < 256; i++) + { + dm_fwrite_byte(pcx.fp, 0); + dm_fwrite_byte(pcx.fp, 0); + dm_fwrite_byte(pcx.fp, 0); + } + } + +error: + if (pcx.fp != NULL) + fclose(pcx.fp); + + dmFree(pcx.buf); + + return res; +} + + +static BOOL dmPCXDecodeRLERow(FILE *fp, uint8_t *buf, const size_t bufLen) +{ + size_t offs = 0; + do + { + int count; + uint8_t data; + + if (!dm_fread_byte(fp, &data)) + return FALSE; + + if ((data & 0xC0) == 0xC0) + { + count = data & 0x3F; + if (!dm_fread_byte(fp, &data)) + return FALSE; + } + else + count = 1; + + while (count-- && offs < bufLen) + buf[offs++] = data; + + } while (offs < bufLen); + + return TRUE; +} + + +int dmReadPCXImageFILE(FILE *fp, DMImage **pimg) +{ + DMImage *img; + DMPCXData pcx; + DMPCXHeader hdr; + BOOL paletted; + int res = 0, yc, xc; + uint8_t *dp; + + pcx.buf = NULL; + + // Read PCX header + if (!dm_fread_byte(fp, &hdr.manufacturer) || + !dm_fread_byte(fp, &hdr.version) || + !dm_fread_byte(fp, &hdr.encoding) || + !dm_fread_byte(fp, &hdr.bpp)) + { + dmError("PCX: Could not read basic header data.\n"); + res = -9; + } + + if (hdr.manufacturer != 10 || + hdr.version != 5 || + hdr.encoding != 1 || + hdr.bpp != 8) + { + dmError("PCX: Not a PCX file, or unsupported variant.\n"); + res = -11; + goto error; + } + + if (!dm_fread_le16(fp, &hdr.xmin) || + !dm_fread_le16(fp, &hdr.ymin) || + !dm_fread_le16(fp, &hdr.xmax) || + !dm_fread_le16(fp, &hdr.ymax) || + !dm_fread_le16(fp, &hdr.hres) || + !dm_fread_le16(fp, &hdr.vres)) + { + dmError("PCX: Could not read image dimensions.\n"); + res = -8; + goto error; + } + + if (!dm_fread_str(fp, (uint8_t *) &hdr.colormap, sizeof(hdr.colormap))) + { + dmError("PCX: Could not read colormap.\n"); + res = -7; + goto error; + } + + if (!dm_fread_byte(fp, &hdr.reserved) || + !dm_fread_byte(fp, &hdr.nplanes) || + !dm_fread_le16(fp, &hdr.bpl) || + !dm_fread_le16(fp, &hdr.palinfo) || + !dm_fread_str(fp, (uint8_t *) &hdr.filler, sizeof(hdr.filler))) + { + dmError("PCX: Could not read header remainder.\n"); + res = -6; + goto error; + } + + if (hdr.nplanes != 3 && hdr.nplanes != 1) + { + dmError("PCX: Unsupported number of bitplanes %d.\n", hdr.nplanes); + res = -4; + goto error; + } + + // Allocate image + if ((*pimg = img = dmImageAlloc(hdr.xmax - hdr.xmin + 1, hdr.ymax - hdr.ymin + 1)) == NULL) + { + dmError("PCX: Could not allocate image structure.\n"); + res = -5; + goto error; + } + + paletted = hdr.nplanes == 1; + pcx.bufLen = hdr.nplanes * hdr.bpl; + if ((pcx.buf = dmMalloc(pcx.bufLen)) == NULL) + { + dmError("PCX: Could not allocate RLE buffer.\n"); + res = -3; + goto error; + } + + // Read image data + dp = img->data; + for (yc = 0; yc < img->height; yc++) + { + // Decode row of RLE'd data + if (!dmPCXDecodeRLERow(fp, pcx.buf, pcx.bufLen)) + { + dmError("PCX: Error decoding RLE data.\n"); + res = -100; + goto error; + } + + // Decode bitplanes + switch (hdr.nplanes) + { + case 1: + memcpy(dp, pcx.buf, img->width); + break; + + case 3: + { + uint8_t *dptr = dp, + *sptr1 = pcx.buf, + *sptr2 = sptr1 + hdr.bpl, + *sptr3 = sptr2 + hdr.bpl; + + for (xc = 0; xc < img->width; xc++) + { + *dptr++ = *sptr1++; + *dptr++ = *sptr2++; + *dptr++ = *sptr3++; + } + } + break; + } + + dp += img->pitch; + } + + // Read VGA palette + if (paletted) + { + int i; + uint8_t tmpb; + + if (!dm_fread_byte(fp, &tmpb) || tmpb != 0x0C) + goto error; + + for (i = 0; i < img->ncolors; i++) + { + if (!dm_fread_byte(fp, &tmpb)) + goto error; + img->pal[i].r = tmpb; + + if (!dm_fread_byte(fp, &tmpb)) + goto error; + img->pal[i].g = tmpb; + + if (!dm_fread_byte(fp, &tmpb)) + goto error; + img->pal[i].b = tmpb; + } + } + +error: + dmFree(pcx.buf); + return res; +} + + +int dmReadPCXImage(const char *filename, DMImage **pimg) +{ + FILE *fp; + int res; + + if ((fp = fopen(filename, "rb")) == NULL) + { + dmError("PCX: Could not open file '%s' for reading.\n", filename); + return -15; + } + + res = dmReadPCXImageFILE(fp, pimg); + + fclose(fp); + return res; +} + + +int fmtProbePNGImageFILE(FILE *fp) +{ + uint8_t buf[6]; +// if (!dm_fread_str(fp, + return DM_PROBE_SCORE_FALSE; +} + + +int fmtProbePCXImageFILE(FILE *fp) +{ + DMPCXHeader hdr; + + if (!dm_fread_byte(fp, &hdr.manufacturer) || + !dm_fread_byte(fp, &hdr.version) || + !dm_fread_byte(fp, &hdr.encoding) || + !dm_fread_byte(fp, &hdr.bpp)) + return DM_PROBE_SCORE_FALSE; + + if (hdr.manufacturer == 10 && + hdr.version == 5 && + hdr.encoding == 1 && + hdr.bpp == 8) + return DM_PROBE_SCORE_GOOD; + + return DM_PROBE_SCORE_FALSE; +} + + +#ifdef UNFINISHED +int dmConvertBMP2(DMImage *screen, const DM64Image *img) +{ + int yc; + uint8_t *dp = screen->data; + + for (yc = 0; yc < screen->height; yc++) + { + uint8_t *d = dp; + const int y = yc / 8, yb = yc & 7; + const int scroffsy = y * C64_SCR_CH_WIDTH; + const int bmoffsy = y * C64_SCR_WIDTH; + int xc; + + for (xc = 0; xc < screen->width / 2; xc++) + { + const int x = xc / 4; + const int scroffs = scroffsy + x; + const int b = img->bitmap[0][bmoffsy + (x * 8) + yb]; + const int v = 6 - ((xc * 2) & 6); + uint8_t c; + + switch ((b >> v) & 3) + { + case 0: c = img->bgcolor; break; + case 1: c = img->screen[0][scroffs] >> 4; break; + case 2: c = img->screen[0][scroffs] & 15; break; + case 3: c = img->color[0][scroffs] & 15; break; + } + + *d++ = c; + *d++ = c; + } + + dp += screen->pitch; + } + + return 0; +} +#endif + + +int dmWriteImage(char *filename, DMImage *image, int format, BOOL paletted, int scale, int bpp) +{ + switch (format) + { +#ifdef HAVE_LIBPNG + case OUTFMT_PNG: + return dmWritePNGImage(filename, image, scale, paletted ? DM_IFMT_PALETTE : DM_IFMT_RGBA); +#endif + + case OUTFMT_PPM: + return dmWritePPMImage(filename, image, scale); + + case OUTFMT_PCX: + return dmWritePCXImage(filename, image, scale, paletted); + + case OUTFMT_ARAW: + { + int res; + char *palFilename = dm_strdup_printf("%s.pal", filename); + res = dmWriteIFFMasterRAWPalette(palFilename, image, 1 << bpp); + dmFree(palFilename); + if (res != 0) + return res; + + return dmWriteIFFMasterRAWImage(filename, image, scale, bpp); + } + + default: + return FALSE; + } +} + + +int dmDumpSpritesAndChars(FILE *inFile) +{ + int dataOffs, itemCount, outWidth, outWidthPX, outHeight; + size_t bufSize; + uint8_t *bufData; + + switch (optInFormat) + { + case INFMT_CHAR: + bufSize = C64_CHR_SIZE; + outWidth = C64_CHR_WIDTH; + outWidthPX = C64_CHR_WIDTH_PX; + outHeight = C64_CHR_HEIGHT; + break; + case INFMT_SPRITE: + bufSize = C64_SPR_SIZE; + outWidth = C64_SPR_WIDTH; + outWidthPX = C64_SPR_WIDTH_PX; + outHeight = C64_SPR_HEIGHT; + break; + default: + dmError("Invalid input format %d, internal error.\n", optInFormat); + return -1; + } + + if ((bufData = dmMalloc(bufSize)) == NULL) + { + dmError("Could not allocate temporary buffer of %d bytes.\n", bufSize); + return -2; + } + + + dataOffs = optInSkip; + itemCount = 0; + + if (optOutFormat == OUTFMT_ANSI || optOutFormat == OUTFMT_ASCII) + { + BOOL error = FALSE; + FILE *outFile; + + if (optOutFilename == NULL) + outFile = stdout; + else + if ((outFile = fopen(optOutFilename, "w")) == NULL) + { + int res = errno; + dmError("Error opening output file '%s'. (%s)\n", + optOutFilename, strerror(res)); + goto error; + } + + while (!feof(inFile) && !error && (optItemCount < 0 || itemCount < optItemCount)) + { + memset(bufData, 0, bufSize); + + if (fread(bufData, 1, bufSize, inFile) != bufSize) + { + dmError("Could not read full bufferful (%d bytes) of data at 0x%x.\n", + bufSize, dataOffs); + error = TRUE; + } + + fprintf(outFile, "---- : -------------- #%d\n", itemCount); + + switch (optInFormat) + { + case INFMT_CHAR: + dmDumpCharASCII(outFile, bufData, &dataOffs, optOutFormat, optInMulticolor); + break; + case INFMT_SPRITE: + dmDumpSpriteASCII(outFile, bufData, &dataOffs, optOutFormat, optInMulticolor); + break; + } + itemCount++; + } + + fclose(outFile); + } + else + if (optOutFormat == OUTFMT_PNG || optOutFormat == OUTFMT_PPM || optOutFormat == OUTFMT_PCX) + { + DMImage *outImage = NULL; + char *outFilename = NULL; + int outX = 0, outY = 0, err; + +#ifndef HAVE_LIBPNG + if (optOutFormat == OUTFMT_PNG) + { + dmError("PNG output format support not compiled in, sorry.\n"); + goto error; + } +#endif + + if (optSequential) + { + if (optOutFilename == NULL) + { + dmError("Sequential image output requires filename template.\n"); + goto error; + } + + outImage = dmImageAlloc(outWidthPX, outHeight); + dmMsg(1, "Outputting sequence of %d images @ %d x %d -> %d x %d.\n", + optItemCount, + outImage->width, outImage->height, + outImage->width * optScale, outImage->height * optScale); + } + else + { + int outIWidth, outIHeight; + if (optItemCount <= 0) + { + dmError("Single-image output requires count to be set (-n).\n"); + goto error; + } + + outIWidth = optPlanedWidth; + outIHeight = (optItemCount / optPlanedWidth); + if (optItemCount % optPlanedWidth) + outIHeight++; + + outImage = dmImageAlloc(outWidthPX * outIWidth, outIHeight * outHeight); + dmMsg(1, "Outputting image %d x %d -> %d x %d.\n", + outImage->width, outImage->height, + outImage->width * optScale, outImage->height * optScale); + } + + outImage->constpal = TRUE; + outImage->pal = dmC64Palette; + outImage->ncolors = C64_NCOLORS; + outImage->ctrans = 255; + + while (!feof(inFile) && (optItemCount < 0 || itemCount < optItemCount)) + { + memset(bufData, 0, bufSize); + + if (fread(bufData, 1, bufSize, inFile) != bufSize) + { + dmError("Could not read full bufferful (%d bytes) of data at 0x%x.\n", + bufSize, dataOffs); + break; + } + + if ((err = dmC64ConvertCSData(outImage, outX * outWidthPX, outY * outHeight, + bufData, outWidth, outHeight, optInMulticolor, optColors)) != 0) + { + dmError("Internal error in conversion of raw data to bitmap: %d.\n", err); + break; + } + + if (optSequential) + { + outFilename = dm_strdup_printf("%s%04d.%s", optOutFilename, itemCount, outFormatList[optOutFormat]); + if (outFilename == NULL) + { + dmError("Could not allocate memory for filename template?\n"); + goto error; + } + + dmWriteImage(outFilename, outImage, optOutFormat, optPaletted, optScale, optBPP); + dmFree(outFilename); + } + else + { + if (++outX >= optPlanedWidth) + { + outX = 0; + outY++; + } + } + + itemCount++; + } + + if (!optSequential) + { + dmWriteImage(optOutFilename, outImage, optOutFormat, optPaletted, optScale, optBPP); + } + + dmImageFree(outImage); + } + + dmFree(bufData); + return 0; + +error: + dmFree(bufData); + return -1; +} + + +int main(int argc, char *argv[]) +{ + FILE *inFile; + int i, optInImageFormat; + + // Default colors + for (i = 0; i < C64_MAX_COLORS; i++) + optColors[i] = i + 1; + + // Initialize and parse commandline + dmInitProg("gfxconv", "Simple c64 graphics converter", "0.4", NULL, NULL); + + if (!dmArgsProcess(argc, argv, optList, optListN, + argHandleOpt, argHandleFile, TRUE)) + exit(1); + + // Determine input format, if not specified' + if (optInFormat == INFMT_AUTO && optInFilename != NULL) + { + char *dext = strrchr(optInFilename, '.'); + if (dext) + { + dext++; + if (!strcasecmp(dext, "fnt") || !strcasecmp(dext, "chr")) + optInFormat = INFMT_CHAR; + else if (!strcasecmp(dext, "spr")) + optInFormat = INFMT_SPRITE; + else if (!strcasecmp(dext, "png") || !strcasecmp(dext, "pcx")) + optInFormat = INFMT_IMAGE; + } + } + + if (optInFilename == NULL) + { + if (optInFormat == INFMT_AUTO) + { + dmError("Standard input cannot be used without specifying input format.\n"); + exit(3); + } + inFile = stdin; + } + else + if ((inFile = fopen(optInFilename, "rb")) == NULL) + { + int res = errno; + dmError("Error opening input file '%s'. (%s)\n", + optInFilename, strerror(res)); + exit(3); + } + + if (optInFormat == INFMT_AUTO) + { + // Skip, if needed + if (fseek(inFile, optInSkip, SEEK_SET) != 0) + { + int res = errno; + dmError("Could not seek to file position %d (0x%x): %s\n", + optInSkip, optInSkip, strerror(res)); + exit(3); + } +#if 0 + if (optInFormat == INFMT_AUTO) + { + int ret = dmC64ProbeGeneric + } +#endif + + if (optInFormat == INFMT_AUTO || optInFormat == INFMT_IMAGE) + { + if (fmtProbePNGImageFILE(inFile)) + { + optInFormat = INFMT_IMAGE; + optInImageFormat = OUTFMT_PNG; + } + else + if (fmtProbePCXImageFILE(inFile)) + { + optInFormat = INFMT_IMAGE; + optInImageFormat = OUTFMT_PCX; + } + else + if (optInFormat == INFMT_IMAGE) + { + dmError("Unsupported image input format.\n"); + exit(4); + } + } + } + + if (optInFormat == INFMT_AUTO) + { + dmError("No input format specified, and could not be determined automatically.\n"); + exit(1); + } + + // Skip, if needed + if (fseek(inFile, optInSkip, SEEK_SET) != 0) + { + int res = errno; + dmError("Could not seek to file position %d (0x%x): %s\n", + optInSkip, optInSkip, strerror(res)); + exit(3); + } + + switch (optInFormat) + { + case INFMT_SPRITE: + case INFMT_CHAR: + dmDumpSpritesAndChars(inFile); + break; + + case INFMT_BITMAP: + case INFMT_IMAGE: + { + DMImage *img; + int res; + + if (optOutFilename == NULL) + { + dmError("Output filename not set, required for image formats.\n"); + exit(3); + } + + // Read input + switch (optInImageFormat) + { + case OUTFMT_PCX: + res = dmReadPCXImageFILE(inFile, &img); + break; + case OUTFMT_PNG: +// res = dmReadPNGImageFILE(inFile, &img); + break; + } + + switch (optOutFormat) + { + case OUTFMT_PCX: + case OUTFMT_PPM: + case OUTFMT_PNG: + case OUTFMT_ARAW: + res = dmWriteImage(optOutFilename, img, optOutFormat, optPaletted, optScale, optBPP); + break; + } + } + break; + } + + fclose(inFile); + + exit(0); + return 0; +} diff -r a0160ffdf7e5 -r 59244a7ae37f lib64gfx.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib64gfx.c Sat Nov 03 02:19:51 2012 +0200 @@ -0,0 +1,727 @@ +/* + * Functions for reading and converting various restricted + * C64/etc and/or indexed/paletted graphics formats. + * Programmed and designed by Matti 'ccr' Hamalainen + * (C) Copyright 2012 Tecnic Software productions (TNSP) + * + * Please read file 'COPYING' for information on license and distribution. + */ +#include "lib64gfx.h" +#include + +const char *dmC64ImageTypeNames[DM_C64IFMT_LAST_TYPE] = +{ + "hires", + "multicolor", + "hires interlace", + "mc interlace", + "hires fli", + "mc fli", +}; + + +// Based on Pepto's palette, stolen from VICE +DMColor dmC64Palette[C64_NCOLORS] = +{ + { 0x00, 0x00, 0x00, 0 }, + { 0xFF, 0xFF, 0xFF, 0 }, + { 0x68, 0x37, 0x2B, 0 }, + { 0x70, 0xA4, 0xB2, 0 }, + { 0x6F, 0x3D, 0x86, 0 }, + { 0x58, 0x8D, 0x43, 0 }, + { 0x35, 0x28, 0x79, 0 }, + { 0xB8, 0xC7, 0x6F, 0 }, + { 0x6F, 0x4F, 0x25, 0 }, + { 0x43, 0x39, 0x00, 0 }, + { 0x9A, 0x67, 0x59, 0 }, + { 0x44, 0x44, 0x44, 0 }, + { 0x6C, 0x6C, 0x6C, 0 }, + { 0x9A, 0xD2, 0x84, 0 }, + { 0x6C, 0x5E, 0xB5, 0 }, + { 0x95, 0x95, 0x95, 0 }, +}; + + +const size_t dmC64DefaultSizes[DT_LAST] = +{ + C64_SCR_COLOR_SIZE, + C64_SCR_BITMAP_SIZE, + C64_SCR_SCREEN_SIZE, + 1, + C64_SCR_EXTRADATA, +}; + + + +DMImage * dmImageAlloc(int width, int height) +{ + DMImage *img = dmCalloc(1, sizeof(DMImage)); + if (img == NULL) + return NULL; + + img->width = img->pitch = width; + img->height = height; + img->data = dmMalloc(width * height * sizeof(uint8_t)); + if (img->data == NULL) + { + dmFree(img); + return NULL; + } + + return img; +} + + +void dmImageFree(DMImage *img) +{ + if (img != NULL) + { + if (!img->constpal) + { + dmFree(img->pal); + } + dmFree(img->data); + dmFree(img); + } +} + + +int dmImageGetBytesPerPixel(int format) +{ + switch (format) + { + case DM_IFMT_PALETTE : return 1; + case DM_IFMT_RGB : return 3; + case DM_IFMT_RGBA : return 4; + case DM_IFMT_RGB_PLANE : return 3; + default: return 0; + } +} + + +int dmC64ConvertCSData(DMImage *img, + int xoffs, int yoffs, const uint8_t *buf, + int width, int height, BOOL multicolor, int *colors) +{ + int yc, widthpx = width * 8; + uint8_t *dp; + + if (img == NULL) + return -1; + if (xoffs < 0 || yoffs < 0) + return -2; + if (xoffs > img->width - widthpx || + yoffs > img->height - height) + return -3; + + dp = img->data + (yoffs * img->pitch) + xoffs; + + if (multicolor) + { + for (yc = 0; yc < height; yc++) + { + const int offs = yc * width; + int xc; + uint8_t *d = dp; + + for (xc = 0; xc < widthpx / 2; xc++) + { + const int b = buf[offs + (xc / 4)]; + const int v = 6 - ((xc * 2) & 6); + const uint8_t c = colors[(b >> v) & 3]; + + *d++ = c; + *d++ = c; + } + + dp += img->pitch; + } + } + else + { + for (yc = 0; yc < height; yc++) + { + const int offs = yc * width; + int xc; + uint8_t *d = dp; + + for (xc = 0; xc < widthpx; xc++) + { + const int b = buf[offs + (xc / 8)]; + const int v = 7 - (xc & 7); + const uint8_t c = colors[(b >> v) & 1]; + + *d++ = c; + } + + dp += img->pitch; + } + } + + return 0; +} + + +static int fmtProbeDrazPaint(const uint8_t *buf, const size_t len) +{ + if (len == 10051 && buf[0] == 0x00 && buf[1] == 0x58) + return DM_PROBE_SCORE_GOOD; + + return DM_PROBE_SCORE_FALSE; +} + + +static int fmtProbeDrazPaint20Packed(const uint8_t *buf, const size_t len) +{ + const char *ident = (const char *) buf + 2; + if (len > 22 && buf[0] == 0x00 && buf[1] == 0x58 && + strncmp(ident, "DRAZPAINT ", 10) == 0 && + ident[11] == '.' && ( + (ident[10] == '1' && ident[12] == '4') || + (ident[10] == '2' && ident[12] == '0') + )) + return DM_PROBE_SCORE_MAX; + + return DM_PROBE_SCORE_FALSE; +} + + +static int fmtDecodeDrazPaintPacked(DMC64Image *img, const uint8_t *buf, const size_t len, const DMC64ImageFormat *fmt) +{ + int res; + uint8_t rleMarker; + uint8_t *mem, *dst, *dstEnd; + const uint8_t *src, *srcEnd; + + if ((mem = dmMalloc(C64_RAM_SIZE)) == NULL) + return -1; + + rleMarker = *(buf + 0x0d); + src = buf + 0x0e; + srcEnd = buf + len; + dst = mem; + dstEnd = mem + C64_RAM_SIZE; + + while (src <= srcEnd && dst <= dstEnd) + { + int c = *src++; + if (c == rleMarker && src + 2 <= srcEnd) + { + int cnt = *src++; + c = *src++; + while (cnt-- && dst <= dstEnd) + *dst++ = c; + } + else + *dst++ = c; + } + + res = dmC64DecodeGenericBMP(img, mem, dst - mem + 1, fmt); + + dmFree(mem); + + return res; +} + + +static int fmtProbeDrazLace10(const uint8_t *buf, const size_t len) +{ + if (len == 18242 && buf[0] == 0x00 && buf[1] == 0x58) + return DM_PROBE_SCORE_GOOD; + return DM_PROBE_SCORE_FALSE; +} + + +static int fmtProbeDrazLace10Packed(const uint8_t *buf, const size_t len) +{ + const char *ident = (const char *) buf + 2; + if (len > 22 && buf[0] == 0x00 && buf[1] == 0x58 && + strncmp(ident, "DRAZLACE! 1.0", 13) == 0) + return DM_PROBE_SCORE_MAX; + + return DM_PROBE_SCORE_FALSE; +} + + +static BOOL fmtDrazLaceSetLaceType(DMC64Image *img, const struct _DMDecodeOp *op, const uint8_t *buf, const size_t len) +{ + img->laceType = buf[op->offs] ? DM_C64ILACE_RES : DM_C64ILACE_COLOR; + img->laceBank2 = 0; + return TRUE; +} + + +#define AMICA_DM_PROBE_SIZE 1024 +static int fmtProbeAmicaPaintPacked(const uint8_t *buf, const size_t len) +{ + int i, n; + if (len < AMICA_DM_PROBE_SIZE || buf[0] != 0x00 || buf[1] != 0x40) + return DM_PROBE_SCORE_FALSE; + + for (n = 0, i = 2; i < AMICA_DM_PROBE_SIZE; i++) + if (buf[i] == 0xC2) n++; + + if (n > 5) + return DM_PROBE_SCORE_GOOD; + if (n > 3) + return DM_PROBE_SCORE_AVG; + + return DM_PROBE_SCORE_MAYBE; +} + + +static int fmtDecodeAmicaPaintPacked(DMC64Image *img, const uint8_t *buf, const size_t len, const DMC64ImageFormat *fmt) +{ + int res; + uint8_t *mem, *dst, *dstEnd; + const uint8_t *src, *srcEnd; + + if ((mem = dmMalloc(C64_RAM_SIZE)) == NULL) + return -1; + + src = buf; + srcEnd = buf + len; + dst = mem; + dstEnd = mem + C64_RAM_SIZE; + + while (src <= srcEnd && dst <= dstEnd) + { + int c = *src++; + if (c == 0xC2 && src + 2 <= srcEnd) + { + int cnt = *src++; + c = *src++; + while (cnt-- && dst <= dstEnd) + *dst++ = c; + } + else + *dst++ = c; + } + + res = dmC64DecodeGenericBMP(img, mem, dst - mem + 1, fmt); + + dmFree(mem); + + return res; +} + + +static int fmtProbeKoalaPaint(const uint8_t *buf, const size_t len) +{ + if (len == 10003 && buf[0] == 0x00 && buf[1] == 0x60) + return DM_PROBE_SCORE_AVG; + return DM_PROBE_SCORE_FALSE; +} + + +static int fmtProbeTruePaint(const uint8_t *buf, const size_t len) +{ + if (len == 19434 && buf[0] == 0x00 && buf[1] == 0x9c) + return DM_PROBE_SCORE_GOOD; + return DM_PROBE_SCORE_FALSE; +} + + +static BOOL fmtTruePaintSetLaceType(DMC64Image *img, const struct _DMDecodeOp *op, const uint8_t *buf, const size_t len) +{ + img->laceType = DM_C64ILACE_RES; + img->laceBank2 = 1; + return TRUE; +} + + +DMC64ImageFormat dmC64ImageFormats[] = +{ + { + DM_C64IFMT_MC, ".drp", "DrazPaint 2.0 (packed)", + fmtProbeDrazPaint20Packed, fmtDecodeDrazPaintPacked, NULL, + 4, + { + { DT_COLOR_RAM, 0x0000, 0, 0, NULL }, + { DT_BITMAP, 0x0800, 0, 0, NULL }, + { DT_SCREEN_RAM, 0x0400, 0, 0, NULL }, + { DT_BGCOLOR, 0x2740, 0, 0, NULL }, + } + }, + + { + DM_C64IFMT_MC_ILACE, ".dlp", "DrazLace 1.0 (packed)", + fmtProbeDrazLace10Packed, fmtDecodeDrazPaintPacked, NULL, + 6, + { + { DT_COLOR_RAM, 0x0000, 0, 0, NULL }, + { DT_BITMAP, 0x0800, 0, 0, NULL }, + { DT_SCREEN_RAM, 0x0400, 0, 0, NULL }, + { DT_BGCOLOR, 0x2740, 0, 0, NULL }, + { DT_BITMAP, 0x2800, 1, 0, NULL }, + { DT_FUNCTION, 0x2742, 0, 1, fmtDrazLaceSetLaceType }, + } + }, + + { + DM_C64IFMT_MC, ".drp", "DrazPaint (unpacked)", + fmtProbeDrazPaint, NULL, NULL, + 4, + { + { DT_COLOR_RAM, 0x0000, 0, 0, NULL }, + { DT_BITMAP, 0x0800, 0, 0, NULL }, + { DT_SCREEN_RAM, 0x0400, 0, 0, NULL }, + { DT_BGCOLOR, 0x2740, 0, 0, NULL }, + } + }, + + { + DM_C64IFMT_MC_ILACE, ".drl", "DrazLace 1.0 (unpacked)", + fmtProbeDrazLace10, NULL, NULL, + 6, + { + { DT_COLOR_RAM, 0x0000, 0, 0, NULL }, + { DT_BITMAP, 0x0800, 0, 0, NULL }, + { DT_SCREEN_RAM, 0x0400, 0, 0, NULL }, + { DT_BGCOLOR, 0x2740, 0, 0, NULL }, + { DT_BITMAP, 0x2800, 1, 0, NULL }, + { DT_FUNCTION, 0x2742, 0, 1, fmtDrazLaceSetLaceType }, + } + }, + + { + DM_C64IFMT_MC_ILACE, ".mci", "Truepaint (unpacked)", + fmtProbeTruePaint, NULL, NULL, + 6, + { + { DT_SCREEN_RAM, 0x0000, 0, 0, NULL }, + { DT_BGCOLOR, 0x03e8, 0, 0, NULL }, + { DT_BITMAP, 0x0400, 0, 0, NULL }, + { DT_BITMAP, 0x2400, 1, 0, NULL }, + { DT_SCREEN_RAM, 0x4400, 1, 0, NULL }, + { DT_COLOR_RAM, 0x4800, 0, 0, NULL }, + { DT_FUNCTION, 0x0000, 0, 0, fmtTruePaintSetLaceType }, + } + }, + + { + DM_C64IFMT_MC, ".kla", "Koala Paint (unpacked)", + fmtProbeKoalaPaint, NULL, NULL, + 4, + { + { DT_COLOR_RAM, 0x2328, 0, 0, NULL }, + { DT_BITMAP, 0x0000, 0, 0, NULL }, + { DT_SCREEN_RAM, 0x1f40, 0, 0, NULL }, + { DT_BGCOLOR, 0x2710, 0, 0, NULL }, + } + }, + + { + DM_C64IFMT_MC, ".ami", "Amica Paint (packed)", + fmtProbeAmicaPaintPacked, fmtDecodeAmicaPaintPacked, NULL, + 4, + { + { DT_COLOR_RAM, 0x2328, 0, 0, NULL }, + { DT_BITMAP, 0x0000, 0, 0, NULL }, + { DT_SCREEN_RAM, 0x1f40, 0, 0, NULL }, + { DT_BGCOLOR, 0x2710, 0, 0, NULL }, + } + }, + +}; + +const int ndmC64ImageFormats = sizeof(dmC64ImageFormats) / sizeof(dmC64ImageFormats[0]); + + +int dmC64ProbeGeneric(const uint8_t *buf, const size_t len, + DMC64ImageFormat **pfmt) +{ + int i, scoreMax = 0, scoreIndex = -1; + + for (i = 0; i < ndmC64ImageFormats; i++) + { + DMC64ImageFormat *fmt = &dmC64ImageFormats[i]; + int score = fmt->probe(buf, len); + if (score > scoreMax) + { + scoreMax = score; + scoreIndex = i; + } + } + + if (scoreIndex >= 0) + { + *pfmt = &dmC64ImageFormats[scoreIndex]; + return scoreMax; + } + else + return 0; +} + + +int dmC64DecodeGenericBMP(DMC64Image *img, const uint8_t *buf, + const size_t len, const DMC64ImageFormat *fmt) +{ + int i; + + memset(img, 0, sizeof(*img)); + img->type = fmt->type; + + for (i = 0; i < fmt->ndecodeOps; i++) + { + const DMDecodeOp *op = &fmt->decodeOps[i]; + const uint8_t *src; + size_t size; + + if (op->bank < 0 || op->bank >= C64_SCR_MAX_BANK) + { + dmError("Invalid bank %d definition in generic decode operator %d @ #%d.\n", + op->bank, op->type, i); + return -1; + } + + if (op->type < 0 || op->type >= DT_LAST) + { + dmError("Invalid decode operator type %d @ #%d.\n", + op->type, i); + return -1; + } + + size = (op->size == 0) ? dmC64DefaultSizes[op->type] : op->size; + + if (op->offs + size > len) + { + dmError("Decode out of bounds, op #%d type=%d, offs=%d ($%04x), bank=%d, size=%d ($%04x) @ %d ($%04x\n", + i, op->type, op->offs, op->offs, op->bank, size, size, len, len); + return -2; + } + + src = buf + op->offs; + + switch (op->type) + { + case DT_COLOR_RAM: memcpy(img->color[op->bank], src, size); break; + case DT_BITMAP: memcpy(img->bitmap[op->bank], src, size); break; + case DT_SCREEN_RAM: memcpy(img->screen[op->bank], src, size); break; + case DT_BGCOLOR: img->bgcolor = *src; break; + case DT_EXTRADATA: memcpy(img->extradata, src, size); break; + case DT_FUNCTION: + if (op->function == NULL) + { + dmError("Decode op is a function, but function ptr is NULL: op #%d, offs=%d ($%04x), bank=%d, size=%d ($%04x) @ %d ($%04x\n", + i, op->offs, op->offs, op->bank, size, size, len, len); + return -6; + } + if (!op->function(img, op, buf, len)) + { + dmError("Decode op custom function failed: op #%d, offs=%d ($%04x), bank=%d, size=%d ($%04x) @ %d ($%04x\n", + i, op->offs, op->offs, op->bank, size, size, len, len); + return -5; + } + break; + } + } + + return 0; +} + + +static int dmC64ConvertHiResBMP(DMImage *screen, const DMC64Image *img) +{ + int yc; + uint8_t *dp = screen->data; + + for (yc = 0; yc < C64_SCR_HEIGHT; yc++) + { + uint8_t *d = dp; + const int y = yc / 8, yb = yc & 7; + const int scroffsy = y * C64_SCR_CH_WIDTH; + const int bmoffsy = y * C64_SCR_WIDTH; + int xc; + + for (xc = 0; xc < C64_SCR_WIDTH; xc++) + { + const int x = xc / 8; + const int scroffs = scroffsy + x; + const int b = img->bitmap[0][bmoffsy + (x * 8) + yb]; + const int v = 7 - (xc & 7); + uint8_t c; + + if ((b >> v) & 1) + c = img->screen[0][scroffs] & 15; + else + c = img->screen[0][scroffs] >> 4; + + *d++ = c; + } + + dp += screen->pitch; + } + + return 0; +} + + +static int dmC64ConvertMultiColorBMP(DMImage *screen, const DMC64Image *img) +{ + int yc; + uint8_t *dp = screen->data; + + for (yc = 0; yc < C64_SCR_HEIGHT; yc++) + { + uint8_t *d = dp; + const int y = yc / 8, yb = yc & 7; + const int scroffsy = y * C64_SCR_CH_WIDTH; + const int bmoffsy = y * C64_SCR_WIDTH; + int xc; + + for (xc = 0; xc < C64_SCR_WIDTH / 2; xc++) + { + const int x = xc / 4; + const int scroffs = scroffsy + x; + const int b = img->bitmap[0][bmoffsy + (x * 8) + yb]; + const int v = 6 - ((xc * 2) & 6); + uint8_t c; + + switch ((b >> v) & 3) + { + case 0: c = img->bgcolor; break; + case 1: c = img->screen[0][scroffs] >> 4; break; + case 2: c = img->screen[0][scroffs] & 15; break; + case 3: c = img->color[0][scroffs] & 15; break; + } + + *d++ = c; + *d++ = c; + } + + dp += screen->pitch; + } + + return 0; +} + + +static int dmC64ConvertLaceMultiColorBMP(DMImage *screen, const DMC64Image *img) +{ + int yc; + uint8_t *dp = screen->data; + + for (yc = 0; yc < C64_SCR_HEIGHT; yc++) + { + uint8_t *d = dp; + const int y = yc / 8, yb = yc & 7; + const int scroffsy = y * C64_SCR_CH_WIDTH; + const int bmoffsy = y * C64_SCR_WIDTH; + int xc; + + for (xc = 0; xc < C64_SCR_WIDTH / 2; xc++) + { + const int x = xc / 4; + const int scroffs = scroffsy + x; + const int bmoffs = bmoffsy + (x * 8) + yb; + const int v = 6 - ((xc * 2) & 6); + const int b1 = (img->bitmap[0][bmoffs] >> v) & 3; + const int b2 = (img->bitmap[1][bmoffs] >> v) & 3; + uint8_t c1, c2; + + switch (b1) + { + case 0: c1 = img->bgcolor; break; + case 1: c1 = img->screen[0][scroffs] >> 4; break; + case 2: c1 = img->screen[0][scroffs] & 15; break; + case 3: c1 = img->color[0][scroffs] & 15; break; + } + + switch (b2) + { + case 0: c2 = img->bgcolor; break; + case 1: c2 = img->screen[img->laceBank2][scroffs] >> 4; break; + case 2: c2 = img->screen[img->laceBank2][scroffs] & 15; break; + case 3: c2 = img->color[img->laceBank2][scroffs] & 15; break; + } + + *d++ = c1; + *d++ = c2; + } + + dp += screen->pitch; + } + + return 0; +} + + +int dmC64ConvertGenericBMP2Image(DMImage *dst, const DMC64Image *src) +{ + switch (src->type) + { + case DM_C64IFMT_HIRES: + return dmC64ConvertHiResBMP(dst, src); + + case DM_C64IFMT_MC: + return dmC64ConvertMultiColorBMP(dst, src); + + case DM_C64IFMT_MC_ILACE: + return dmC64ConvertLaceMultiColorBMP(dst, src); + + default: + return -1; + } +} + + +#define BUF_SIZE_INITIAL (16*1024) +#define BUF_SIZE_GROW (2*1024) + +int dmReadDataFile(const char *filename, uint8_t **pbuf, size_t *pbufSize) +{ + FILE *f; + uint8_t *dataBuf = NULL, *dataPtr; + size_t bufSize, readSize, dataSize; + + if (filename == NULL) + f = stdin; + else + if ((f = fopen(filename, "rb")) == NULL) + { + int err = errno; + dmError("Could not open input file '%s': %d, %s\n", + filename, err, strerror(err)); + return -1; + } + + readSize = bufSize = BUF_SIZE_INITIAL; + if ((dataBuf = dmMalloc(bufSize)) == NULL) + { + fclose(f); + dmError("Error allocating memory for data, %d bytes.\n", bufSize); + return -4; + } + + dataPtr = dataBuf; + dataSize = 0; + + while (!feof(f) && !ferror(f)) + { + size_t read = fread(dataPtr, 1, readSize, f); + + dataSize += read; + dataPtr += read; + + if (read == readSize && !feof(f)) + { + readSize = BUF_SIZE_GROW; + bufSize += BUF_SIZE_GROW; + if ((dataBuf = dmRealloc(dataBuf, bufSize)) == NULL) + { + dmError("Error reallocating memory for data, %d bytes.\n", bufSize); + return -4; + } + } + else + break; + } + + fclose(f); + + *pbuf = dataBuf; + *pbufSize = dataSize; + + return 0; +} diff -r a0160ffdf7e5 -r 59244a7ae37f lib64gfx.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib64gfx.h Sat Nov 03 02:19:51 2012 +0200 @@ -0,0 +1,184 @@ +/* + * Functions for reading and manipulating some C64/etc graphics formats + * Programmed and designed by Matti 'ccr' Hamalainen + * (C) Copyright 2012 Tecnic Software productions (TNSP) + * + * Please read file 'COPYING' for information on license and distribution. + */ +#ifndef LIB64GFX_H +#define LIB64GFX_H 1 + +#include "dmlib.h" + +// Bitmap constants +#define C64_SCR_WIDTH 320 +#define C64_SCR_HEIGHT 200 +#define C64_SCR_CH_WIDTH (C64_SCR_WIDTH/8) +#define C64_SCR_CH_HEIGHT (C64_SCR_HEIGHT/8) +#define C64_SCR_COLOR_SIZE (C64_SCR_CH_WIDTH * C64_SCR_CH_HEIGHT) +#define C64_SCR_SCREEN_SIZE (C64_SCR_CH_WIDTH * C64_SCR_CH_HEIGHT) +#define C64_SCR_BITMAP_SIZE (C64_SCR_WIDTH * C64_SCR_HEIGHT/8) +#define C64_SCR_EXTRADATA 1024 +#define C64_SCR_MAX_BANK 8 + +// C64 video screen pixel aspect ratio on PAL +#define C64_SCR_PAR_XY (0.9365f) + +// Sprite constants +#define C64_SPR_WIDTH 3 // bytes +#define C64_SPR_HEIGHT 21 // lines +#define C64_SPR_WIDTH_PX (8 * C64_SPR_WIDTH) +#define C64_SPR_SIZE ((C64_SPR_WIDTH * C64_SPR_HEIGHT) + 1) + +// Character constants +#define C64_CHR_WIDTH 1 // bytes +#define C64_CHR_HEIGHT 8 // lines +#define C64_CHR_WIDTH_PX (8 * C64_CHR_WIDTH) +#define C64_CHR_SIZE (C64_CHR_WIDTH * C64_CHR_HEIGHT) + +// Etc. +#define C64_RAM_SIZE (64*1024) +#define C64_NCOLORS 16 +#define C64_MAX_COLORS 16 +#define C64_VIDBANK_SIZE (16*1024) +#define C64_MAX_SPRITES (C64_VIDBANK_SIZE / C64_SPR_SIZE) +#define C64_MAX_CHARS 256 + +// Probe scores +#define DM_PROBE_SCORE_MAX 1000 +#define DM_PROBE_SCORE_GOOD 750 +#define DM_PROBE_SCORE_AVG 500 +#define DM_PROBE_SCORE_MAYBE 250 +#define DM_PROBE_SCORE_FALSE 0 + +enum +{ + DM_IFMT_PALETTE, + DM_IFMT_RGB, + DM_IFMT_RGBA, + DM_IFMT_RGB_PLANE, +}; + + +// RGBx color struct +typedef struct +{ + uint8_t r, g, b, x; +} DMColor; + + +// Bitmapped image struct (can be one of types specified by DM_IFMT_*) +typedef struct +{ + int width, height, pitch; + BOOL constpal; + int ncolors, ctrans; + DMColor *pal; + uint8_t *data; +} DMImage; + + +// Different supported C64 bitmap "modes" +enum +{ + DM_C64IFMT_HIRES, + DM_C64IFMT_MC, + DM_C64IFMT_HIRES_ILACE, + DM_C64IFMT_MC_ILACE, + DM_C64IFMT_HIRES_FLI, + DM_C64IFMT_MC_FLI, + + DM_C64IFMT_LAST_TYPE +}; + + +enum +{ + DM_C64ILACE_COLOR, + DM_C64ILACE_RES, +}; + +typedef struct +{ + BOOL multicolor, xexpand, yexpand; + int color, xc, yc; + uint8_t data[C64_SPR_HEIGHT][C64_SPR_WIDTH]; +} DMC64Sprite; + + +typedef struct +{ + int type, // Image type (DM_C64IFMT_*) + laceType, // Interlace type (DM_C64ILACE_*) + fliType, // FLI type (if FLI used) + fliLines, // FLI on every # line + laceBank2; + + uint8_t color[C64_SCR_MAX_BANK][C64_SCR_COLOR_SIZE], + bitmap[C64_SCR_MAX_BANK][C64_SCR_BITMAP_SIZE], + screen[C64_SCR_MAX_BANK][C64_SCR_SCREEN_SIZE], + extradata[C64_SCR_EXTRADATA], + d020, bgcolor, d022, d023, d024; + + uint8_t charset[C64_MAX_CHARS][C64_CHR_HEIGHT * C64_CHR_WIDTH]; + DMC64Sprite sprites[C64_MAX_SPRITES]; +} DMC64Image; + + +enum +{ + DT_COLOR_RAM, + DT_BITMAP, + DT_SCREEN_RAM, + DT_BGCOLOR, + DT_EXTRADATA, + + DT_FUNCTION, + + DT_LAST, +}; + + +typedef struct _DMDecodeOp +{ + int type; + size_t offs; + int bank; + size_t size; + BOOL (*function)(DMC64Image *img, const struct _DMDecodeOp *op, const uint8_t *buf, const size_t len); +} DMDecodeOp; + + +typedef struct _DMC64ImageFormat +{ + int type; + char *extension; + char *name; + int (*probe)(const uint8_t *buf, const size_t len); + int (*decode)(DMC64Image *img, const uint8_t *buf, const size_t len, const struct _DMC64ImageFormat *fmt); + int (*convert)(DMImage *, DMC64Image *); + + int ndecodeOps; + DMDecodeOp decodeOps[16]; +} DMC64ImageFormat; + + +extern const size_t dmC64DefaultSizes[DT_LAST]; +extern DMColor dmC64Palette[C64_NCOLORS]; +extern DMC64ImageFormat dmC64ImageFormats[]; +extern const int ndmC64ImageFormats; +extern const char * dmC64ImageTypeNames[]; + + +DMImage * dmImageAlloc(int width, int height); +void dmImageFree(DMImage *img); +int dmImageGetBytesPerPixel(int format); + + +int dmC64ConvertCSData(DMImage *img, int xoffs, int yoffs, const uint8_t *inBuf, int width, int height, BOOL multicolor, int *colors); +int dmC64ProbeGeneric(const uint8_t *buf, const size_t len, DMC64ImageFormat **fmt); +int dmC64DecodeGenericBMP(DMC64Image *img, const uint8_t *buf, const size_t len, const DMC64ImageFormat *fmt); +int dmC64ConvertGenericBMP2Image(DMImage *screen, const DMC64Image *img); +int dmReadDataFile(const char *filename, uint8_t **buf, size_t *size); + +#endif // LIB64GFX_H diff -r a0160ffdf7e5 -r 59244a7ae37f objlink.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/objlink.c Sat Nov 03 02:19:51 2012 +0200 @@ -0,0 +1,899 @@ +/* + * objlink - Link files (RAW and PRG) into one PRG object + * Programmed and designed by Matti 'ccr' Hamalainen + * (C) Copyright 2002-2012 Tecnic Software productions (TNSP) + * + * Please read file 'COPYING' for information on license and distribution. + */ +#include +#include "dmlib.h" +#include "dmargs.h" +#include "dmfile.h" +#include "dmmutex.h" + +#define MAX_FILENAMES (128) +#define MAX_MEMBLOCKS (128) + + +/* Typedefs + */ +typedef struct +{ + ssize_t start, end; // Start and end address + int type; // Type + char *name; // Name of the block + ssize_t size; + int placement; +} DMMemBlock; + +typedef struct +{ + char *name; // Description of memory model + char *desc; + ssize_t size; // Total addressable memory size + ssize_t nmemBlocks; // Defined memory areas + DMMemBlock memBlocks[MAX_MEMBLOCKS]; +} DMMemModel; + +typedef struct +{ + char *filename; + int type; + int placement; + ssize_t addr; +} DMSourceFile; + +// Source file type +enum +{ + STYPE_RAW = 1, + STYPE_PRG, + STYPE_PRGA +}; + +// How to determine block placement / address +enum +{ + PLACE_STATIC = 1, // Already known + PLACE_ARGUMENT, // Commandline argument + PLACE_FILE, // From file +}; + +enum +{ + FMT_GENERIC = 1, + FMT_PLAIN, + FMT_DECIMAL +}; + +enum +{ + MTYPE_NONE = 0, + MTYPE_ROM, // Hard ROM + MTYPE_ROM_WT, // Write to RAM through ROM + MTYPE_IO, // I/O lines + MTYPE_RES // RESERVED +}; + + +/* Memory models + */ +const DMMemModel memoryModels[] = { + { "C64 unrestricted", "$01 = $34", (64*1024), 0, { + { 0, 0, 0, NULL, 0, 0 } + }}, + + { "C64 normal (IO+Basic+Kernal)", "$01 = $37", (64*1024), 3, { + { 0xA000, 0xBFFF, MTYPE_ROM_WT, "Basic ROM", 0, PLACE_STATIC }, + { 0xD000, 0xDFFF, MTYPE_IO, "I/O", 0, PLACE_STATIC }, + { 0xE000, 0xFFFF, MTYPE_ROM_WT, "Kernal ROM", 0, PLACE_STATIC }, + }}, + + { "C64 modified (IO+Kernal)", "$01 = $36", (64*1024), 2, { + { 0xD000, 0xDFFF, MTYPE_IO, "I/O", 0, PLACE_STATIC }, + { 0xE000, 0xFFFF, MTYPE_ROM_WT, "Kernal ROM", 0, PLACE_STATIC }, + }}, + + { "C64 modified (IO only)", "$01 = $35", (64*1024), 1, { + { 0xD000, 0xDFFF, MTYPE_IO, "I/O", 0, PLACE_STATIC }, + }}, + + { "C64 modified (Char+Kernal+Basic)", "$01 = $33", (64*1024), 3, { + { 0xA000, 0xBFFF, MTYPE_ROM_WT, "Basic ROM", 0, PLACE_STATIC }, + { 0xD000, 0xDFFF, MTYPE_ROM, "Char ROM", 0, PLACE_STATIC }, + { 0xE000, 0xFFFF, MTYPE_ROM_WT, "Kernal ROM", 0, PLACE_STATIC }, + }}, + +/* + { "C64 normal", "$01 = $37", (64*1024), 0, { + { 0x0000, 0x0000, MTYPE_RAM, "" }, + }}, +*/ +}; + +static const int nmemoryModels = sizeof(memoryModels) / sizeof(memoryModels[0]); + + +/* Global variables + */ +int nsrcFiles = 0; // Number of source files +DMSourceFile srcFiles[MAX_FILENAMES]; // Source file names + +int nmemBlocks = 0; +DMMemBlock memBlocks[MAX_FILENAMES]; + +char *optLinkFileName = NULL; +int optLinkFileFormat = FMT_GENERIC; + +BOOL optDescribe = FALSE, + optAllowOverlap = FALSE; + +Uint32 optInitValue = 0; +int optInitValueType = 1; + +int optMemModel = 0; +const DMMemModel *memModel = NULL; +Uint8 *memory = NULL; + +char *optDestName = NULL; + + +/* Arguments + */ +static DMOptArg optList[] = { + { 0, '?', "help", "Show this help", OPT_NONE }, + { 1, 'r', "input-raw", "RAW input: -r :", OPT_ARGREQ }, + { 2, 'p', "input-prg", "PRG input: -p [:]", OPT_ARGREQ }, + { 12, 's', "section", "Reserved section: -s - or :", OPT_ARGREQ }, + { 5, 'o', "output", "Specify output file, -o ", OPT_ARGREQ }, + { 6, 'O', "overlap", "Allow overlapping memory areas", OPT_NONE }, + { 7, 'm', "model", "Set memory model", OPT_ARGREQ }, + { 8, 'l', "link-file", "Output addresses and labels into file", OPT_ARGREQ }, + { 9, 'f', "format", "Format of link-file: (g)eneric, (p)lain, (d)ecimal", OPT_ARGREQ }, + { 10, 'i', "initvalue", "Initialize memory with: -i :[bwd]", OPT_ARGREQ }, + { 11, 'd', "describe", "Output ASCII memory map description", OPT_NONE }, +}; + +static const int optListN = sizeof(optList) / sizeof(optList[0]); + + +void argShowHelp() +{ + int i; + + dmPrintBanner(stdout, dmProgName, "[options]"); + dmArgsPrintHelp(stdout, optList, optListN); + + printf( + "\n" + "Each numeric argument can be prefixed with $ or 0x for hexadecimal values.\n" + "NOTICE! -p filename: will ignore load address and use instead!\n" + "\n" + "Available memory models:\n"); + + for (i = 0; i < nmemoryModels; i++) + { + const DMMemModel *m = &memoryModels[i]; + printf(" %d = %-40s [%s] (%d kB)\n", + i, m->name, m->desc, m->size / 1024); + } +} + + +off_t dmGetFileSize(FILE *f) +{ + off_t len, pos = ftell(f); + fseeko(f, 0, SEEK_END); + len = ftell(f); + fseek(f, pos, SEEK_SET); + return len; +} + + +/* Memory block handling + */ +void reserveMemBlock(ssize_t startAddr, ssize_t endAddr, const char *blockName, int blockType) +{ + if (startAddr > endAddr) + { + dmError("ERROR! Block '%s' has startAddr=$%.4x > endAddr=$%.4x!\n", + blockName, startAddr, endAddr); + exit(4); + } + + if (nmemBlocks < MAX_FILENAMES) + { + memBlocks[nmemBlocks].start = startAddr; + memBlocks[nmemBlocks].end = endAddr; + memBlocks[nmemBlocks].size = (endAddr - startAddr + 1); + memBlocks[nmemBlocks].name = dm_strdup(blockName); + memBlocks[nmemBlocks].type = blockType; + nmemBlocks++; + } + else + { + dmError("Maximum number of memBlock definitions (%d) exceeded!\n", + MAX_FILENAMES); + exit(4); + } +} + + +int compareMemBlock(const void *cva, const void *cvb) +{ + const DMMemBlock *a = cva, *b = cvb; + return a->start - b->start; +} + +BOOL argHandleOpt(const int optN, char *optArg, char *currArg) +{ + char *p, *s; + ssize_t tmpi; + + switch (optN) { + case 0: + argShowHelp(); + exit(0); + break; + + case 1: + // Add RAW + if ((p = strrchr(optArg, ':')) != NULL) + { + *p = 0; + if (!dmGetIntVal(p + 1, &tmpi)) + { + dmError("Invalid RAW address '%s' specified for '%s'.\n", + p + 1, optArg); + return FALSE; + } + } + else + { + dmError("No RAW loading address specified for '%s'.\n", optArg); + return FALSE; + } + srcFiles[nsrcFiles].filename = optArg; + srcFiles[nsrcFiles].type = STYPE_RAW; + srcFiles[nsrcFiles].addr = tmpi; + nsrcFiles++; + break; + + case 2: + // Add PRG + if ((p = strrchr(optArg, ':')) != NULL) + { + *p = 0; + if (!dmGetIntVal(p + 1, &tmpi)) + { + dmError("Invalid PRG address '%s' specified for '%s'.\n", + p + 1, optArg); + return FALSE; + } + srcFiles[nsrcFiles].addr = tmpi; + srcFiles[nsrcFiles].type = STYPE_PRGA; + } + else + { + srcFiles[nsrcFiles].type = STYPE_PRG; + } + + srcFiles[nsrcFiles].filename = optArg; + nsrcFiles++; + break; + + case 5: + // Set output file name + optDestName = optArg; + break; + + case 6: + // Allow overlapping segments + optAllowOverlap = TRUE; + dmError("Warning, allowing overlapping data.\n"); + break; + + case 7: + // Set memory model + optMemModel = atoi(optArg); + if (optMemModel < 0 || optMemModel >= nmemoryModels) + { + dmError("Invalid memory model number %i!\n", optMemModel); + return FALSE; + } + break; + + case 8: + // Linker file + optLinkFileName = optArg; + break; + + case 9: + // Linker file format + switch (tolower(optArg[0])) + { + case 'g': + optLinkFileFormat = FMT_GENERIC; + break; + case 'p': + optLinkFileFormat = FMT_PLAIN; + break; + case 'd': + optLinkFileFormat = FMT_DECIMAL; + break; + + default: + dmError("Invalid/unknown linker file format '%s'!\n", + optArg); + return FALSE; + } + break; + + case 10: + // Initialization value + optInitValueType = 1; + if ((p = strrchr(optArg, ':')) != NULL) + { + *p = 0; + switch (tolower(p[1])) + { + case 'b': optInitValueType = 1; break; + case 'w': optInitValueType = 2; break; + case 'd': optInitValueType = 4; break; + default: + dmError("Invalid init value type '%c' specified for '%s'.\n", + p[1], optArg); + return FALSE; + } + } + if (!dmGetIntVal(optArg, &tmpi)) + { + dmError("Invalid initvalue '%s'.\n", optArg); + return FALSE; + } + optInitValue = tmpi; + break; + + case 11: + // Set describe mode + optDescribe = TRUE; + break; + + case 12: + { + ssize_t sectStart, sectEnd, sectLen; + char sectMode; + + // Define reserved section + // Create a copy of the argument + if ((s = dm_strdup(optArg)) == NULL) + { + dmError("Could not allocate temporary string!\n"); + exit(128); + } + + // Get start address + if ((p = strchr(s, '-')) == NULL && + (p = strchr(s, ':')) == NULL) + { + dmFree(s); + dmError("Section definition '%s' invalid.\n", optArg); + return FALSE; + } + sectMode = *p; + *p = 0; + + // Get value + if (!dmGetIntVal(s, §Start)) + { + dmError("Section start address '%s' in '%s' invalid.\n", s, optArg); + dmFree(s); + return FALSE; + } + + // Get end address or length + if (!dmGetIntVal(p + 1, &tmpi)) + { + dmError("Section %s '%s' in '%s' invalid.\n", + sectMode == '-' ? "end address" : "length", + p, optArg); + dmFree(s); + return FALSE; + } + + dmFree(s); + + if (sectMode == ':') + { + sectEnd = sectStart + tmpi; + sectLen = tmpi; + } + else + { + if (tmpi < sectStart) + { + dmError("Section start address > end address in '%s'.\n", + optArg); + return FALSE; + } + sectLen = tmpi - sectStart + 1; + sectEnd = tmpi; + } + + // Allocate memory block + dmMsg(1, "Reserve $%.4x - $%.4x ($%x, %d bytes) as 'Clear'\n", + sectStart, sectEnd, sectLen, sectLen); + + reserveMemBlock(sectStart, sectEnd, "Clear", MTYPE_RES); + } + break; + + default: + dmError("Unknown argument '%s'.\n", currArg); + return FALSE; + } + + return TRUE; +} + + +int dmLoadPRG(const char *fname, BOOL forceAddr, int destAddr) +{ + FILE *f; + ssize_t a, b, s; + + // Open the input file + if ((f = fopen(fname, "rb")) == NULL) + { + dmError("Error opening input file '%s' (%s).\n", + fname, strerror(errno)); + return 1; + } + + // Get filesize + if ((s = dmGetFileSize(f) - 2) < 0) + { + dmError("Error getting file size for '%s'.\n", fname); + return 6; + } + + // Get loading address + if ((a = fgetc(f)) < 0) + { + dmError("Error reading input file '%s' (%s).\n", + fname, strerror(errno)); + return 2; + } + + if ((b = fgetc(f)) < 0) + { + dmError("Error reading input file '%s' (%s).\n", + fname, strerror(errno)); + return 3; + } + + // Show information + if (forceAddr) + a = destAddr; + else + a = a + (b * 0x100); + + dmPrint(1, "* Loading '%s', %s at $%.4x-$%.4x", + fname, forceAddr ? "PRGA" : "PRG", a, (a + s - 1)); + + if (a + s >= memModel->size) + { + dmPrint(1, " .. Does not fit into the memory!\n"); + return 5; + } + + // Load data + if (fread(&memory[a], s, 1, f) < 1) + { + dmPrint(1, " .. Error: %s.\n", + strerror(errno)); + return 4; + } + + dmPrint(1, " .. OK\n"); + + // Add info to list + reserveMemBlock(a, (a + s + 1), fname, MTYPE_RES); + + return 0; +} + + +int dmLoadRAW(const char *fname, int destAddr) +{ + FILE *f; + ssize_t s; + + // Open the input file + if ((f = fopen(fname, "rb")) == NULL) + { + dmError("Error opening input file '%s' (%s).\n", + fname, strerror(errno)); + return 1; + } + + // Get filesize + s = dmGetFileSize(f); + if (s < 0) + { + dmError("Error getting file size for '%s'.\n", fname); + return 6; + } + + // Show information + dmPrint(1, "* Loading '%s', RAW at $%.4x-$%.4x", + fname, destAddr, (destAddr + s - 1)); + + if (destAddr + s >= memModel->size) + { + dmPrint(1, " .. Does not fit into the memory!\n"); + return 5; + } + + // Load data + if (fread(&memory[destAddr], s, 1, f) < 1) + { + dmPrint(1, " .. Error: %s.\n", + strerror(errno)); + return 4; + } + + dmPrint(1, " .. OK\n"); + + // Add info to list + reserveMemBlock(destAddr, (destAddr + s + 1), fname, MTYPE_RES); + + return 0; +} + + +int outputLinkData(FILE *dfile, const char *blockName, const int blockStart, const int blockEnd) +{ + char *tmpStr, *s, *t; + int blockSize; + + blockSize = (blockEnd - blockStart + 1); + + // Create label name from filename + tmpStr = dm_strdup(blockName); + if (tmpStr == NULL) + { + dmError("Could not allocate memory for string '%s'!\n", + blockName); + return -1; + } + + if ((t = strrchr(tmpStr, '/'))) + s = (t + 1); + else if ((t = strrchr(tmpStr, '\\'))) + s = (t + 1); + else + s = tmpStr; + + if ((t = strrchr(s, '.'))) + *t = 0; + + for (t = s; *t; t++) + { + if (!isalnum(*t)) + *t = '_'; + } + + // Print the label line + switch (optLinkFileFormat) + { + case FMT_PLAIN: + fprintf(dfile, "%s = $%.4x\n", tmpStr, blockStart); + break; + + case FMT_DECIMAL: + fprintf(dfile, "%s = %d\n", tmpStr, blockStart); + break; + + case FMT_GENERIC: + default: + fprintf(dfile, "; %s ($%.4x - $%.4x, %d/$%x bytes)\n", + blockName, blockStart, blockEnd, blockSize, blockSize); + fprintf(dfile, "%s = $%.4x\n", s, blockStart); + break; + } + + dmFree(tmpStr); + return 0; +} + + +/* Print out an ASCII presentation of memory map + */ +void memPrintLine(FILE *f) +{ + fprintf(f, " +------------------------------------------+\n"); +} + +void memPrintEmpty(FILE *f, ssize_t n) +{ + ssize_t i; + for (i = 0; i < n; i++) + fprintf(f, " | |\n"); +} + +void dmDescribeMemory(FILE *f) +{ + int i; + DMMemBlock *prev = NULL; + + memPrintLine(f); + + for (i = 0; i < nmemBlocks; i++) + { + DMMemBlock *curr = &memBlocks[i]; + char desc[512], *s; + ssize_t siz, kz; + + // Check for empty, unreserved areas + if (prev != NULL && prev->start - 1 > curr->end + 1) + { + siz = (prev->start - 1) - (curr->end + 1); + kz = siz / (1024 * 2); + + if (kz > 1) memPrintEmpty(f, kz); + + snprintf(desc, sizeof(desc), "EMPTY (%d)", siz); + fprintf(f, "$%.4x - $%.4x | %-40s |\n", curr->end + 1, prev->start - 1, desc); + + if (kz > 1) memPrintEmpty(f, kz); + memPrintLine(f); + } + prev = curr; + + // Print current block + switch (curr->type) + { + case MTYPE_NONE: s = "N/A (NC)"; break; + case MTYPE_ROM: s = "ROM"; break; + case MTYPE_ROM_WT: s = "ROM/WT"; break; + case MTYPE_IO: s = "I/O"; break; + case MTYPE_RES: s = "RSVD"; break; + default: s = "????"; break; + } + + siz = curr->end - curr->start + 1; + kz = siz / (1024 * 2); + + if (kz > 1) memPrintEmpty(f, kz); + snprintf(desc, sizeof(desc), "%s (%s, %d)", curr->name, s, siz); + fprintf(f, "$%.4x - $%.4x | %-40s |\n", curr->start, curr->end, desc); + if (kz > 1) memPrintEmpty(f, kz); + memPrintLine(f); + + } + + fprintf(f, + "\n" + "NC = Not Connected\n" + "RSVD = Reserved\n" + "ROM/WT = RAM under 'write-through' ROM\n" + "\n" + ); +} + + +/* + * The main program + */ +int main(int argc, char *argv[]) +{ + FILE *dfile = NULL; + BOOL hasOverlaps; + int i, j; + ssize_t startAddr, endAddr, dataSize, totalSize; + + dmInitProg("objlink", "Simple file-linker", "0.80", NULL, NULL); + dmVerbosity = 1; + + // Parse arguments + if (!dmArgsProcess(argc, argv, optList, optListN, + argHandleOpt, NULL, TRUE)) + exit(1); + + if (nsrcFiles < 1) + { + dmError("Nothing to do. (try --help)\n"); + exit(0); + } + + // Allocate memory + memModel = &memoryModels[optMemModel]; + dmMsg(1, "Using memory model #%d '%s', %d bytes.\n", + optMemModel, memModel->name, memModel->size); + + memory = (Uint8 *) dmMalloc(memModel->size + 32); + if (memory == NULL) + { + dmError("Could not allocate memory.\n"); + exit(2); + } + + // Initialize memory + dmMsg(1, "Initializing memory with "); + + if (optInitValueType == 1 || optInitValue <= 0xff) + { + dmPrint(1, "BYTE 0x%.2x\n", optInitValue); + memset(memory, optInitValue, memModel->size); + } + else + if (optInitValueType == 2 || optInitValue <= 0xffff) + { + uint16_t *mp = (uint16_t *) memory; + dmPrint(1, "WORD 0x%.4x\n", optInitValue); + for (i = memModel->size / sizeof(*mp); i; i--) + { + *mp++ = optInitValue; + } + } + else + { + Uint32 *mp = (Uint32 *) memory; + dmPrint(1, "DWORD 0x%.8x\n", optInitValue); + for (i = memModel->size / sizeof(*mp); i; i--) + { + *mp++ = optInitValue; + } + } + + // Load the datafiles + for (i = 0; i < nsrcFiles; i++) + switch (srcFiles[i].type) + { + case STYPE_RAW: + dmLoadRAW(srcFiles[i].filename, srcFiles[i].addr); + break; + + case STYPE_PRG: + dmLoadPRG(srcFiles[i].filename, FALSE, 0); + break; + + case STYPE_PRGA: + dmLoadPRG(srcFiles[i].filename, TRUE, srcFiles[i].addr); + break; + } + + // Add memory model blocks + dmMsg(1, "Applying memory model restrictions...\n"); + for (i = 0; i < memModel->nmemBlocks; i++) + { + reserveMemBlock( + memModel->memBlocks[i].start, + memModel->memBlocks[i].end, + memModel->memBlocks[i].name, + memModel->memBlocks[i].type); + } + + // Sort the blocks + qsort(memBlocks, nmemBlocks, sizeof(DMMemBlock), compareMemBlock); + + // Check for overlapping conflicts + hasOverlaps = FALSE; + for (i = 0; i < nmemBlocks; i++) + for (j = 0; j < nmemBlocks; j++) + if (j != i && memBlocks[i].type == MTYPE_RES) + { + DMMemBlock *mbi = &memBlocks[i], + *mbj = &memBlocks[j]; + + // Check for per-file conflicts + if ((mbj->start >= mbi->start && mbj->start <= mbi->end) || + (mbj->end >= mbi->start && mbj->end <= mbi->end)) + { + dmPrint(1, "* '%s' and '%s' overlap ($%.4x-$%.4x vs $%.4x-$%.4x)\n", + mbi->name, mbj->name, mbi->start, + mbi->end, mbj->start, mbj->end); + hasOverlaps = TRUE; + } + } + + if (!optAllowOverlap && hasOverlaps) + { + dmError("Error occured, overlaps not allowed.\n"); + exit(5); + } + + // Find out start and end-addresses + startAddr = memModel->size; + totalSize = endAddr = 0; + for (i = 0; i < nmemBlocks; i++) + { + DMMemBlock *mbi = &memBlocks[i]; + if (mbi->type == MTYPE_RES) + { + if (mbi->start < startAddr) + startAddr = mbi->start; + + if (mbi->end > endAddr) + endAddr = mbi->end; + + totalSize += (mbi->end - mbi->start + 1); + } + } + + if (startAddr >= memModel->size || endAddr < startAddr) + { + dmError("Invalid saveblock addresses (start=$%.4x, end=$%.4x)!\n", startAddr, endAddr); + exit(8); + } + + // Output linkfile + if (optLinkFileName) + { + dmMsg(1, "Writing linkfile to '%s'\n", optLinkFileName); + if ((dfile = fopen(optLinkFileName, "wb")) == NULL) + { + dmError("Error creating file '%s' (%s).\n", optLinkFileName, strerror(errno)); + exit(1); + } + + switch (optLinkFileFormat) + { + case FMT_GENERIC: + default: + fprintf(dfile, "; Definitions generated by %s v%s\n", + dmProgName, dmProgVersion); + break; + } + + for (i = 0; i < nmemBlocks; i++) + { + DMMemBlock *mbi = &memBlocks[i]; + outputLinkData(dfile, mbi->name, mbi->start, mbi->end); + } + + fclose(dfile); + } + + // Show some information + dataSize = endAddr - startAddr + 1; + dmMsg(1, "Total of %d/$%x bytes unused(?) areas.\n", + dataSize - totalSize, dataSize - totalSize); + + dmMsg(1, "Writing $%.4x - $%.4x (%d/$%x bytes) ", + startAddr, endAddr, dataSize, dataSize); + + + // Open the destination file + if (optDestName == NULL) + { + dfile = stdout; + dmPrint(1, "...\n"); + } + else if ((dfile = fopen(optDestName, "wb")) == NULL) + { + dmError("Error creating output file '%s' (%s).\n", optDestName, strerror(errno)); + exit(1); + } + else + dmPrint(1, "to '%s'\n", optDestName); + + // Save loading address + dm_fwrite_le16(dfile, startAddr); + + // Save the data + if (fwrite(&memory[startAddr], dataSize, 1, dfile) < 1) + { + dmError("Error writing to file (%s)\n", strerror(errno)); + } + + fclose(dfile); + + // Describe + if (optDescribe) + dmDescribeMemory(stdout); + + exit(0); + return 0; +} diff -r a0160ffdf7e5 -r 59244a7ae37f view64.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/view64.c Sat Nov 03 02:19:51 2012 +0200 @@ -0,0 +1,348 @@ +/* + * view64 - Display some C64 etc graphics formats via libSDL + * Programmed and designed by Matti 'ccr' Hamalainen + * (C) Copyright 2012 Tecnic Software productions (TNSP) + * + * Please read file 'COPYING' for information on license and distribution. + */ +#include "dmlib.h" +#include "dmargs.h" +#include "dmfile.h" +#include "lib64gfx.h" +#include + + +char * optFilename = NULL; +int optVFlags = SDL_SWSURFACE | SDL_HWPALETTE; +int optScrWidth, optScrHeight; +int optForcedFormat = -1; + + +static DMOptArg optList[] = +{ + { 0, '?', "help", "Show this help", OPT_NONE }, + { 1, 'v', "verbose", "Be more verbose", OPT_NONE }, + { 2, 0, "fs", "Fullscreen", OPT_NONE }, + { 3, 'S', "scale", "Scale image by factor (1-10)", OPT_ARGREQ }, + { 4, 'f', "format", "Force input format (see list below)", OPT_ARGREQ }, +}; + +const int optListN = sizeof(optList) / sizeof(optList[0]); + + +void dmSetScaleFactor(float factor) +{ + optScrWidth = (int) ((float) C64_SCR_WIDTH * factor * C64_SCR_PAR_XY); + optScrHeight = (int) ((float) C64_SCR_HEIGHT * factor); +} + + +void argShowHelp() +{ + int i; + + dmPrintBanner(stdout, dmProgName, "[options] "); + dmArgsPrintHelp(stdout, optList, optListN); + + printf("\nAvailable bitmap formats:\n"); + for (i = 0; i < ndmC64ImageFormats; i++) + { + DMC64ImageFormat *fmt = &dmC64ImageFormats[i]; + printf("%3d | %-5s | %-15s | %s\n", + i, fmt->extension, + dmC64ImageTypeNames[fmt->type], + fmt->name); + } +} + + +BOOL argHandleOpt(const int optN, char *optArg, char *currArg) +{ + switch (optN) + { + case 0: + argShowHelp(); + exit(0); + break; + + case 1: + dmVerbosity++; + break; + + case 2: + optVFlags |= SDL_FULLSCREEN; + break; + + case 3: + { + float factor; + if (sscanf(optArg, "%f", &factor) == 1) + { + if (factor < 1 || factor >= 10) + { + dmError("Invalid scale factor %1.0f, see help for valid values.\n", factor); + return FALSE; + } + + dmSetScaleFactor(factor); + } + else + { + dmError("Invalid scale factor '%s'.\n", optArg); + return FALSE; + } + } + break; + + case 4: + { + int i; + if (sscanf(optArg, "%d", &i) == 1) + { + if (i < 0 || i >= ndmC64ImageFormats) + { + dmError("Invalid image format index %d, see help for valid values.\n", i); + return FALSE; + } + optForcedFormat = i; + } + else + { + dmError("Invalid image format argument '%s'.\n", optArg); + return FALSE; + } + } + break; + + default: + dmError("Unknown option '%s'.\n", currArg); + return FALSE; + } + + return TRUE; +} + + +BOOL argHandleFile(char *filename) +{ + if (optFilename == NULL) + { + optFilename = dm_strdup(filename); + return TRUE; + } + else + { + dmError("Too many filenames specified ('%s')\n", filename); + return FALSE; + } +} + + +BOOL dmInitializeVideo(SDL_Surface **screen) +{ + *screen = SDL_SetVideoMode(optScrWidth, optScrHeight, 8, optVFlags | SDL_RESIZABLE); + if (*screen == NULL) + { + dmError("Can't SDL_SetVideoMode(): %s\n", SDL_GetError()); + return FALSE; + } + return TRUE; +} + + +int main(int argc, char *argv[]) +{ + SDL_Surface *screen = NULL, *surf = NULL; + DMImage bmap; + BOOL initSDL = FALSE, exitFlag, needRedraw; + DMC64ImageFormat *fmt; + DMC64Image image; + char *windowTitle; + Uint8 *dataBuf = NULL; + size_t dataSize; + int i, ret; + + dmSetScaleFactor(2.0); + + dmInitProg("view64", "Display some C64 bitmap graphics formats", "0.2", NULL, NULL); + + /* Parse arguments */ + if (!dmArgsProcess(argc, argv, optList, optListN, + argHandleOpt, argHandleFile, FALSE)) + exit(1); + + + if (optFilename == NULL) + { + dmError("No input file specified, perhaps you need some --help\n"); + goto error_exit; + } + + if (dmReadDataFile(optFilename, &dataBuf, &dataSize) != 0) + goto error_exit; + + // Probe for format + if (optForcedFormat >= 0) + { + fmt = &dmC64ImageFormats[optForcedFormat]; + dmMsg(0,"Forced %s format image, type %d, %s\n", fmt->name, fmt->type, fmt->extension); + + if (fmt->decode != NULL) + ret = fmt->decode(&image, dataBuf + 2, dataSize - 2, fmt); + else + ret = dmC64DecodeGenericBMP(&image, dataBuf + 2, dataSize - 2, fmt); + + if (ret < 0) + { + dmError("Error decoding image format.\n"); + return -1; + } + } + else + { + BOOL found = FALSE; + for (i = 0; i < ndmC64ImageFormats; i++) + { + fmt = &dmC64ImageFormats[i]; + ret = fmt->probe(dataBuf, dataSize); + if (ret > 0) + { + dmMsg(0,"Probed %s format image, type %d, %s\n", fmt->name, fmt->type, fmt->extension); + if (fmt->decode != NULL) + ret = fmt->decode(&image, dataBuf + 2, dataSize - 2, fmt); + else + ret = dmC64DecodeGenericBMP(&image, dataBuf + 2, dataSize - 2, fmt); + + if (ret < 0) + { + dmError("Error decoding image format.\n"); + return -1; + } + + found = TRUE; + break; + } + else + if (ret < 0) + { + dmError("Error in probing.\n"); + } + } + if (!found) + { + dmError("Probing could not find any matching image format. Perhaps try forcing a format via -f\n"); + return -2; + } + } + + // Initialize libSDL + if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER) != 0) + { + dmError("Could not initialize SDL: %s\n", SDL_GetError()); + goto error_exit; + } + initSDL = TRUE; + + + // Open window/set video mode + screen = SDL_SetVideoMode(optScrWidth, optScrHeight, 8, optVFlags | SDL_RESIZABLE); + if (screen == NULL) + { + dmError("Can't SDL_SetVideoMode(): %s\n", SDL_GetError()); + goto error_exit; + } + + // Create surface (we are lazy and ugly) + surf = SDL_CreateRGBSurface(SDL_SWSURFACE, C64_SCR_WIDTH, C64_SCR_HEIGHT, 8, 0, 0, 0, 0); + SDL_SetColors(surf, (SDL_Color *)dmC64Palette, 0, C64_NCOLORS); + SDL_SetColors(screen, (SDL_Color *)dmC64Palette, 0, C64_NCOLORS); + + // Convert bitmap (this is a bit ugly and lazy here) + bmap.data = surf->pixels; + bmap.pitch = surf->pitch; + bmap.width = surf->w; + bmap.height = surf->h; + bmap.constpal = TRUE; + + if (fmt->convert != NULL) + ret = fmt->convert(&bmap, &image); + else + ret = dmC64ConvertGenericBMP2Image(&bmap, &image); + + + // Set window title and caption + windowTitle = dm_strdup_printf("%s - %s", dmProgName, optFilename); + SDL_WM_SetCaption(windowTitle, dmProgName); + dmFree(windowTitle); + + + // Start main loop + needRedraw = TRUE; + exitFlag = FALSE; + while (!exitFlag) + { + SDL_Event event; + while (SDL_PollEvent(&event)) + switch (event.type) + { + case SDL_KEYDOWN: + switch (event.key.keysym.sym) + { + case SDLK_ESCAPE: exitFlag = TRUE; break; + + default: + break; + } + + needRedraw = TRUE; + break; + + case SDL_VIDEORESIZE: + optScrWidth = event.resize.w; + optScrHeight = event.resize.h; + + if (!dmInitializeVideo(&screen)) + goto error_exit; + + needRedraw = TRUE; + break; + + case SDL_VIDEOEXPOSE: + needRedraw = TRUE; + break; + + case SDL_QUIT: + exit(0); + } + + if (needRedraw) + { + if (SDL_MUSTLOCK(screen) != 0 && SDL_LockSurface(screen) != 0) + { + dmError("Can't lock surface.\n"); + goto error_exit; + } + + dmScaledBlitSurface8to8(surf, 0, 0, screen->w, screen->h, screen); + SDL_SetColors(screen, (SDL_Color *)dmC64Palette, 0, C64_NCOLORS); + + if (SDL_MUSTLOCK(screen) != 0) + SDL_UnlockSurface(screen); + + SDL_Flip(screen); + needRedraw = FALSE; + } + + SDL_Delay(100); + } + + +error_exit: + if (screen) + SDL_FreeSurface(screen); + + if (initSDL) + SDL_Quit(); + + return 0; +}