From 919af0757efdcd085e8a27935e93752e9b45cc8e Mon Sep 17 00:00:00 2001 From: bbraun Date: Sat, 3 Dec 2011 01:31:42 +0000 Subject: [PATCH] Initial commit. --- ChangeLog | 3 + LICENSE | 8 +++ Makefile | 7 ++ README | 3 + cli/Makefile.inc | 14 ++++ cli/main.c | 162 ++++++++++++++++++++++++++++++++++++++++++++ lib/Makefile.inc | 18 +++++ lib/checksum.c | 30 ++++++++ lib/err.c | 11 +++ lib/locate_drvr.c | 98 +++++++++++++++++++++++++++ lib/macrompatcher.h | 65 ++++++++++++++++++ lib/romdisk.c | 29 ++++++++ lib/romdrv.h | 86 +++++++++++++++++++++++ 13 files changed, 534 insertions(+) create mode 100644 ChangeLog create mode 100644 LICENSE create mode 100644 Makefile create mode 100644 README create mode 100644 cli/Makefile.inc create mode 100644 cli/main.c create mode 100644 lib/Makefile.inc create mode 100644 lib/checksum.c create mode 100644 lib/err.c create mode 100644 lib/locate_drvr.c create mode 100644 lib/macrompatcher.h create mode 100644 lib/romdisk.c create mode 100644 lib/romdrv.h diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 0000000..b4cc529 --- /dev/null +++ b/ChangeLog @@ -0,0 +1,3 @@ +devel + 2011-12-02 Rob Braun bbraun@synack.net + * Initial creation. Includes ROMdisk driver installation, checksum calculation & update. diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..63ae746 --- /dev/null +++ b/LICENSE @@ -0,0 +1,8 @@ +The MIT License (MIT) +Copyright (c) 2011 Rob Braun + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..48926e4 --- /dev/null +++ b/Makefile @@ -0,0 +1,7 @@ +CFLAGS := -Wall -Werror "-Ilib/" + +dir_names := lib cli +all: $(dir_names:%=%_all) +clean: $(dir_names:%=%_clean) + +include $(dir_names:%=%/Makefile.inc) diff --git a/README b/README new file mode 100644 index 0000000..42c93ee --- /dev/null +++ b/README @@ -0,0 +1,3 @@ +This is a tool and associated library intended to apply known ROM modifications to 68k Macintosh ROMs. + +It currently knows about Mac II, IIx, SE/30 ROMs as "24bit" ROMs, and IIsi/IIci and higher ROMs, although it is most tested with machines that have a ROM SIMM slot, such as the Mac II, IIx, SE/30, IIsi, IIci, Quadra 700, etc. diff --git a/cli/Makefile.inc b/cli/Makefile.inc new file mode 100644 index 0000000..f8bac28 --- /dev/null +++ b/cli/Makefile.inc @@ -0,0 +1,14 @@ +ROMPATCHER_SRCS := main.c +ROMPATCHER_SRCS := $(patsubst %, cli/%, $(ROMPATCHER_SRCS)) + +ROMPATCHER_EXE := cli/macrompatcher + +cli_all: $(ROMPATCHER_EXE) +cli_clean: + rm -f $(ROMPATCHER_SRCS:%.c=%.o) + rm -f $(ROMPATCHER_EXE) + +$(ROMPATCHER_EXE): $(ROMPATCHER_SRCS:%.c=%.o) $(ROMLIB_A) + $(CC) $(CFLAGS) -o $@ $< $(LDFLAGS) $(ROMLIB_A) + +cli/%o: cli/%.c diff --git a/cli/main.c b/cli/main.c new file mode 100644 index 0000000..75dee07 --- /dev/null +++ b/cli/main.c @@ -0,0 +1,162 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "macrompatcher.h" + +#define MINROMSIZE 262144 /* 256k is the smallest we know how to use */ +#define MAXROMSIZE 2097152 /* 2MB is the largest we know what to do with */ + +void usage(char *progname) { + fprintf(stderr, "Usage: %s [OPTION]\n", progname); + fprintf(stderr, "Options:\n"); + fprintf(stderr, " -f, --infile=FILE Specify the ROM file to operate on\n"); + fprintf(stderr, " -o, --outfile=FILE Specify the filename to write the modified ROM to\n"); + fprintf(stderr, " -c, --checksum Compute and print the checksum\n"); + fprintf(stderr, " -r, --romdiskdrvr Install the ROMdisk driver\n"); + return; +} + +int main(int argc, char *argv[]) { + char c; + int loptind; + int print_checksum = 0; + int apply_romdisk = 0; + char *romname = NULL; + int romfd; + char *outname = NULL; + RomErr err = 0; + struct option o[] = { + {"checksum", 0, 0, 'c'}, + {"infile", 1, 0, 'f'}, + {"outfile", 1, 0, 'o'}, + {"romdiskdrvr", 0, 0, 'r'}, + {"help", 0, 0, 'h'}, + {0, 0, 0, 0} + }; + RomCtx *rom = NULL; + struct stat sb; + + if(argc < 2) { + usage(argv[0]); + exit(1); + } + + while( (c = getopt_long(argc, argv, "chf:o:r", o, &loptind)) != -1 ) { + switch(c) { + case 'c': print_checksum = 1; break; + case 'f': romname = optarg; break; + case 'o': outname = optarg; break; + case 'r': apply_romdisk = 1; break; + case 'h': + default: + usage(argv[0]); + exit(1); + }; + } + + if(romname == NULL) { + fprintf(stderr, "No ROM file specified.\n"); + usage(argv[0]); + exit(1); + } + + romfd = open(romname, O_RDONLY); + if(romfd < 0) { + fprintf(stderr, "Could not open ROM %s: %d %s\n", romname, errno, strerror(errno)); + exit(1); + } + + rom = calloc(1, sizeof(RomCtx)); + if(!rom) { + fprintf(stderr, "Could not allocate memory for ROM context\n"); + exit(1); + } + + if(fstat(romfd, &sb) < 0) { + fprintf(stderr, "Could not fstat ROM file descriptor\n"); + exit(1); + } + + if(sb.st_size < MINROMSIZE) { + fprintf(stderr, "ROM is too small. %d is smaller than minimum ROM size of %d\n", (int)sb.st_size, MINROMSIZE); + exit(1); + } + + if(sb.st_size > MAXROMSIZE) { + fprintf(stderr, "ROM is too big. %d is bigger than maximum ROM size of %d\n", (int)sb.st_size, MAXROMSIZE); + exit(1); + } + + rom->data = calloc(1, sb.st_size); + if(!rom->data) { + fprintf(stderr, "Could not allocate RAM for the ROM image\n"); + exit(1); + } + + rom->datasize = rom->filesize = sb.st_size; + + if(sb.st_size < (512*1024)) { + rom->type = e24bit; + }else{ + rom->type = e32bit; + } + + errno = 0; + if(read(romfd, rom->data, rom->datasize) < rom->datasize) { + fprintf(stderr, "Could not read ROM image into RAM: %d %s\n", errno, strerror(errno)); + exit(1); + } + + close(romfd); + + if(apply_romdisk) { + err = InstallRomdiskDrvr(rom); + if(err != eSuccess) { + fprintf(stderr, "Error installing ROMdisk driver: %s\n", GetROMErrString(err)); + exit(1); + } + } + + if(print_checksum) { + uint32_t checksum = 0; + err = GetChecksum(rom, &checksum); + if(err != eSuccess) { + fprintf(stderr, "Error computing checksum: %s\n", GetROMErrString(err)); + } else { + printf("Checksum: %#x\n", checksum); + } + } + + if(outname) { + do { + RomErr err = UpdateChecksum(rom); + if(err != eSuccess) { + fprintf(stderr, "Error updating checksum: %s\n", GetROMErrString(err)); + break; + } + + int outfd = open(outname, O_WRONLY | O_TRUNC | O_CREAT, 0644); + if(outfd < 0) { + fprintf(stderr, "Error opening output file %s: %d %s\n", outname, errno, strerror(errno)); + break; + } + + if(write(outfd, rom->data, rom->datasize) < rom->datasize) { + fprintf(stderr, "Error writing to output file %s: %d %s\n", outname, errno, strerror(errno)); + } + close(outfd); + }while(0); + } + + if(rom) { + free(rom->data); + free(rom); + } + exit(0); +} diff --git a/lib/Makefile.inc b/lib/Makefile.inc new file mode 100644 index 0000000..f08d6dd --- /dev/null +++ b/lib/Makefile.inc @@ -0,0 +1,18 @@ +ROMLIB_SRCS := checksum.c err.c locate_drvr.c romdisk.c +ROMLIB_A := lib/libmacrom.a + +ROMLIB_SRCS := $(patsubst %, lib/%, $(ROMLIB_SRCS)) + +lib_all: $(ROMLIB_A) +lib_clean: + rm -f $(ROMLIB_SRCS:%.c=%.o) + rm -f $(ROMLIB_A) + +$(ROMLIB_A): $(ROMLIB_SRCS:%.c=%.o) + $(AR) cvr $@ $+ +# $(RANLIB) $@ + +lib/%.o: lib/%.c + +lib/%.c: lib/macrompatcher.h +lib/romdisk.c: lib/macrompatcher.h lib/romdrv.h diff --git a/lib/checksum.c b/lib/checksum.c new file mode 100644 index 0000000..d607b6b --- /dev/null +++ b/lib/checksum.c @@ -0,0 +1,30 @@ +#include +#include +#include "macrompatcher.h" + +RomErr GetChecksum(RomCtx *rom, uint32_t *checksum) { + uint8_t *tmpptr = NULL; + size_t i; + + if(!rom || !rom->data || !checksum) return eParmErr; + + tmpptr = rom->data+4; + + *checksum = 0; + for(i = 0; i < rom->filesize; i++) { + *checksum += tmpptr[i++] << 8; + *checksum += tmpptr[i]; + } + + return eSuccess; +} + +RomErr UpdateChecksum(RomCtx *rom) { + uint32_t checksum; + RomErr ret; + ret = GetChecksum(rom, &checksum); + if(ret != eSuccess) return ret; + + *(uint32_t*)(rom->data) = htonl(checksum); + return eSuccess; +} diff --git a/lib/err.c b/lib/err.c new file mode 100644 index 0000000..1f4c309 --- /dev/null +++ b/lib/err.c @@ -0,0 +1,11 @@ +#include "macrompatcher.h" + +const char *GetROMErrString(RomErr err) { + switch(err) { + case eSuccess: return "Success!"; + case eNotSupp: return "Modification not supported for this ROM"; + case eNotFound: return "Couldn't find where to apply the modification"; + case eParmErr: return "Parameter error"; + }; + return "Unknown error"; +} diff --git a/lib/locate_drvr.c b/lib/locate_drvr.c new file mode 100644 index 0000000..aa8dd62 --- /dev/null +++ b/lib/locate_drvr.c @@ -0,0 +1,98 @@ +#include +#include +#include "macrompatcher.h" + +RomErr GetDRVROffset(RomCtx *rom, uint16_t drvrid, uint32_t *offset) { + uint8_t *tmpptr; + RomErr ret = eNotFound; + + if(!rom || !rom->data || !offset) return eParmErr; + + // 0x1A stores the 4 byte location of the resources + tmpptr = rom->data + 0x1A; + + uint32_t resourcesoffset = *(uint32_t*)tmpptr; + resourcesoffset = ntohl(resourcesoffset); + + if(resourcesoffset > rom->datasize) return eNotFound; + + tmpptr = rom->data + resourcesoffset; + + if(rom->type == e32bit) { + uint32_t indirect = *(uint32_t*)tmpptr; + indirect = ntohl(indirect); + tmpptr = rom->data + indirect; + do { + uint32_t nextoff; + uint32_t data; + uint32_t type; + uint16_t resid; + data = *(uint32_t*)(tmpptr+12); + data = ntohl(data); + type = *(uint32_t*)(tmpptr+16); + type = ntohl(type); + resid = *(uint16_t*)(tmpptr+20); + resid = ntohs(resid); + if(type == 0x44525652 && resid == drvrid) { + *offset = data; + ret = eSuccess; + break; + } + + nextoff = *(uint32_t*)(tmpptr+8); + nextoff = ntohl(nextoff); + if(nextoff > rom->datasize) break; + tmpptr = rom->data + nextoff; + }while(1); + } else if(rom->type == e24bit) { + uint16_t typelistoff = *(uint16_t*)(tmpptr+28); + typelistoff = ntohs(typelistoff); + tmpptr += 4; + tmpptr += typelistoff; + + uint16_t numresourcetypes = *(uint16_t*)(tmpptr);; + numresourcetypes = ntohs(numresourcetypes); + tmpptr += 2; + + uint16_t drvroff = 0; + uint16_t numdrvrs = 0; + int i; + for(i = 0; i < 11; i++) { + uint32_t resourceid; + uint16_t numresources; + uint16_t listoffset; + resourceid = *(uint32_t*)(tmpptr + (i*8)); + resourceid = ntohl(resourceid); + if(resourceid != 0x44525652) continue; /* DRVR 4char const */ + + numresources = *(uint16_t*)(tmpptr + (i*8) + 4); + listoffset = *(uint16_t*)(tmpptr + (i*8) + 6); + numresources = ntohs(numresources); + listoffset = ntohs(listoffset); + + numdrvrs = numresources; + drvroff = listoffset; + break; + } + + if(!drvroff) return eNotFound; + + tmpptr = rom->data + resourcesoffset + typelistoff + drvroff + 4; + for(i = 0; i <= numdrvrs; i++) { + uint16_t resid; + resid = *(uint16_t*)(tmpptr + (i*12)); + resid = ntohs(resid); + if(resid == drvrid) { + uint32_t attroff; + attroff = *(uint32_t*)(tmpptr + (i*12) + 4); + attroff = ntohl(attroff); + attroff &= 0x00FFFFFF; + *offset = attroff; + ret = eSuccess; + break; + } + } + } + + return ret; +} diff --git a/lib/macrompatcher.h b/lib/macrompatcher.h new file mode 100644 index 0000000..98b01df --- /dev/null +++ b/lib/macrompatcher.h @@ -0,0 +1,65 @@ +#ifndef __MACROMPATCHER_H__ +#define __MACROMPATCHER_H__ 1 +#include + +/* Known types of ROM images. */ +enum RomType { + eUnknown = 0, + + /* 24bit ROMs are Mac II, IIx, SE/30 ROMs that are normally 256KB */ + e24bit = 1, + + /* 32bit ROMs are IIsi, IIci, and higher that are normally + * 512KB or larger. + */ + e32bit = 2 +}; +typedef enum RomType RomType; + +struct romctx { + /* pointer to the in-memory instance of the ROM, ready to be modified. + * This pointer can change, if something needs to increase the size + * of the ROM, it can be realloc'd to be larger. + */ + uint8_t *data; + + /* Size of the memory pointed to by data above. If the ROM image grows + * via realloc, this needs to be updated. + */ + uint32_t datasize; + + /* The original filesize that was read in from disk. If the ROM image + * size hasn't changed in the modification proces, this will == datasize + */ + uint32_t filesize; + + /* Represents what type the ROM is. Some ROMs are structured + * differently, so modification routines will need to key off this + * to determine how and if they can perform their modification. + */ + RomType type; +}; +typedef struct romctx RomCtx; + +/* Standard error types the ROM modification routines can return */ +enum RomErr { + eSuccess = 0, + + /* The modification is not supported for the given ROM */ + eNotSupp = 1, + + /* Couldn't find where to apply the patch in the given ROM */ + eNotFound = 2, + + /* Error with one of the parameters */ + eParmErr = 3 +}; +typedef enum RomErr RomErr; + +RomErr GetChecksum(RomCtx *rom, uint32_t *checksum); +RomErr UpdateChecksum(RomCtx *rom); +const char *GetROMErrString(RomErr err); +RomErr GetDRVROffset(RomCtx *rom, uint16_t drvrid, uint32_t *offset); +RomErr InstallRomdiskDrvr(RomCtx *rom); + +#endif /* __MACROMPATCHER_H__ */ diff --git a/lib/romdisk.c b/lib/romdisk.c new file mode 100644 index 0000000..48f8108 --- /dev/null +++ b/lib/romdisk.c @@ -0,0 +1,29 @@ +#include +#include + +#include "macrompatcher.h" +#include "romdrv.h" + +/* romdrv.h defines a ROM disk driver that will present a disk image starting + * at 0x00880000 (512kb in to the ROM) and is 512kb in size, as a bootable + * drive. + * This function installs the driver in place of the .Sony floppy driver, + * DRVR resource ID 4. + */ + +RomErr InstallRomdiskDrvr(RomCtx *rom) { + RomErr err; + uint32_t sonyoffset; + uint8_t *tmpptr; + + if(!rom || !rom->data) return eParmErr; + + err = GetDRVROffset(rom, 4, &sonyoffset); + if(err != eSuccess) return err; + + tmpptr = rom->data + sonyoffset; + + memcpy(tmpptr, romdrv, sizeof(romdrv)); + + return eSuccess; +} diff --git a/lib/romdrv.h b/lib/romdrv.h new file mode 100644 index 0000000..02ce7f8 --- /dev/null +++ b/lib/romdrv.h @@ -0,0 +1,86 @@ +unsigned char romdrv[] = { + 79,0,0,0,0,0,0,0,0,50,0,54,0,58,0,62,0,66,5,46,83,111,110,121,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,112,0,96,14,112,1,96,34,112, + 2,96,30,112,3,96,26,112,4,47,12,63,0,47,9,47,8,78,186,0,64,78,186,4,192, + 79,239,0,10,40,95,78,117,72,231,0,200,63,0,47,9,47,8,78,186,0,38,78,186, + 4,166,79,239,0,10,76,223,19,0,176,124,0,1,103,14,8,40,0,1,0,6,102,4,47, + 56,8,252,78,117,112,0,78,117,65,250,255,162,209,252,0,0,133,84,32,8,160, + 85,193,140,78,117,78,117,34,124,0,0,3,8,114,1,32,105,0,2,96,14,178,104, + 0,6,110,6,50,40,0,6,82,65,32,80,32,8,102,238,48,1,78,117,78,86,0,0,66, + 128,48,46,0,10,72,64,48,46,0,8,32,110,0,12,160,78,78,94,78,117,78,86, + 255,252,47,10,45,124,0,0,1,52,255,252,36,124,0,0,8,238,32,10,103,8,74, + 146,103,4,32,82,78,144,32,110,255,252,32,80,32,104,0,12,49,124,0,1,0, + 10,36,95,78,94,78,117,78,86,0,0,72,231,24,48,38,46,0,12,40,60,0,0,1,66, + 38,124,0,0,1,52,112,30,167,30,36,72,66,82,21,124,255,128,0,2,21,124,0, + 8,0,3,66,42,0,4,66,42,0,5,66,170,0,6,53,124,0,1,0,10,78,186,255,76,53, + 64,0,12,32,67,53,104,0,24,0,14,66,106,0,16,53,124,4,0,0,18,66,106,0,20, + 112,16,167,34,34,67,35,72,0,20,32,67,32,104,0,20,160,41,32,67,32,104, + 0,20,38,144,32,83,48,170,0,12,32,83,33,124,0,136,0,0,0,2,32,83,33,124, + 0,8,0,0,0,6,32,83,66,104,0,10,112,14,167,30,34,83,35,72,0,12,32,83,32, + 104,0,12,66,144,32,83,32,104,0,12,49,124,0,1,0,4,65,250,255,20,34,83, + 34,105,0,12,35,72,0,6,32,83,32,104,0,12,49,124,0,1,0,10,32,83,32,104, + 0,12,66,104,0,12,32,67,66,168,0,16,72,106,0,6,63,42,0,12,63,42,0,14,78, + 186,254,196,32,83,32,104,0,12,160,51,32,110,0,8,66,104,0,16,32,68,66, + 80,112,0,80,79,76,223,12,24,78,94,78,117,78,86,255,252,72,231,31,48,36, + 110,0,8,46,46,0,12,45,124,0,0,1,66,255,252,122,0,124,0,48,42,0,44,2,64, + 0,15,103,10,83,64,103,14,85,64,103,16,96,24,32,71,42,40,0,16,96,16,42, + 42,0,46,96,10,32,71,42,40,0,16,218,170,0,46,74,133,108,4,124,206,96,120, + 12,133,0,8,0,0,111,4,124,206,96,108,32,5,208,170,0,36,12,128,0,8,0,0, + 111,4,124,206,96,90,48,42,0,6,2,64,0,255,85,64,103,6,83,64,103,72,96, + 72,48,42,0,44,2,64,0,64,102,62,38,124,0,136,0,0,215,197,38,42,0,32,40, + 42,0,36,96,6,32,67,82,131,16,155,32,4,83,132,74,128,102,242,37,106,0, + 36,0,40,32,5,208,170,0,36,32,71,33,64,0,16,32,71,37,104,0,16,0,46,96, + 2,124,213,53,70,0,16,32,110,255,252,48,134,48,42,0,6,2,64,2,0,103,4,0, + 70,0,1,48,6,76,223,12,248,78,94,78,117,78,86,0,0,47,11,34,110,0,8,38, + 124,0,0,1,66,36,60,0,0,1,52,114,239,12,105,0,1,0,26,102,6,112,1,96,0, + 0,206,12,105,0,5,0,26,102,6,114,0,96,0,0,170,12,105,0,6,0,26,102,6,114, + 0,96,0,0,156,12,105,0,9,0,26,102,6,114,0,96,0,0,142,12,105,0,8,0,26,102, + 6,114,0,96,0,0,128,12,105,0,65,0,26,102,14,32,66,32,80,49,124,0,1,0,10, + 114,0,96,104,12,105,0,7,0,26,102,4,114,239,96,92,12,105,0,21,0,26,102, + 12,65,236,128,0,35,72,0,28,114,0,96,72,12,105,0,22,0,26,102,12,65,236, + 129,2,35,72,0,28,114,0,96,52,12,105,0,23,0,26,102,16,51,124,0,1,0,28, + 51,124,0,6,0,30,114,0,96,28,12,105,0,24,0,26,102,20,66,105,0,28,51,124, + 0,4,0,30,66,105,0,32,66,105,0,34,114,0,51,65,0,16,54,129,48,41,0,6,2, + 64,2,0,103,4,0,65,0,1,48,1,38,95,78,94,78,117,78,86,0,0,72,231,28,48, + 36,110,0,8,40,60,0,0,1,52,118,238,42,60,0,0,1,66,48,42,0,26,93,64,103, + 14,85,64,103,68,85,64,103,0,0,138,96,0,0,162,74,106,0,28,111,48,53,124, + 0,1,0,28,66,106,0,30,66,106,0,32,53,124,0,4,0,34,66,106,0,36,66,106,0, + 38,66,106,0,40,66,106,0,42,66,106,0,44,53,124,0,64,0,46,96,108,118,206, + 96,104,38,74,71,235,0,28,66,83,23,124,255,128,0,2,23,124,0,8,0,3,66,43, + 0,4,66,43,0,5,66,171,0,6,55,124,0,1,0,10,32,68,32,80,55,80,0,12,32,110, + 0,12,55,104,0,24,0,14,66,107,0,16,55,124,4,0,0,18,66,107,0,20,118,0,96, + 30,53,124,0,255,0,28,53,124,0,255,0,30,53,124,0,255,0,32,53,124,0,255, + 0,34,118,0,96,2,118,238,53,67,0,16,32,69,48,131,48,42,0,6,2,64,2,0,103, + 4,0,67,0,1,48,67,32,8,76,223,12,56,78,94,78,117,78,86,255,252,45,124, + 0,0,1,66,255,252,32,110,255,252,66,80,112,0,78,94,78,117,78,86,0,0,72, + 231,0,48,38,110,0,8,36,110,0,12,48,46,0,16,12,64,0,4,98,80,208,64,48, + 59,0,6,78,251,0,2,0,10,0,22,0,34,0,46,0,58,47,10,47,11,78,186,251,208, + 80,79,96,50,47,10,47,11,78,186,252,210,80,79,96,38,47,10,47,11,78,186, + 253,176,80,79,96,26,47,10,47,11,78,186,254,156,80,79,96,14,47,10,47,11, + 78,186,255,132,80,79,96,2,112,1,76,223,12,0,78,94,78,117,127,255,255, + 248,129,0,1,4,129,0,113,2,129,0,137,1,129,0,137,1,129,0,137,1,129,0,137, + 1,129,0,137,1,129,0,113,1,129,0,1,1,128,255,254,1,128,0,0,1,128,0,0,1, + 128,0,0,1,128,0,0,1,128,0,0,1,128,0,0,1,128,0,0,1,135,255,255,225,136, + 0,0,17,136,0,0,17,136,0,0,17,136,0,0,17,136,0,0,17,136,0,0,17,136,0,0, + 17,136,0,0,17,136,0,0,17,136,0,0,17,136,0,0,17,136,0,0,17,127,255,255, + 254,127,255,255,248,255,255,255,252,255,255,255,254,255,255,255,255,255, + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,127, + 255,255,254,0,0,127,255,255,248,129,0,1,4,129,0,113,2,129,0,137,1,129, + 0,137,1,129,0,137,1,129,0,137,1,129,0,137,1,129,0,113,1,129,0,1,1,128, + 255,254,1,128,0,0,1,128,0,0,1,128,0,0,1,128,0,0,1,128,0,0,1,128,0,0,1, + 128,0,0,1,135,255,255,225,136,0,0,17,136,0,0,17,136,0,0,17,136,0,0,17, + 136,0,0,17,136,0,0,17,136,0,0,17,136,0,0,17,136,0,0,17,136,0,0,17,136, + 0,0,17,136,0,0,17,127,255,255,254,127,255,255,248,255,255,255,252,255, + 255,255,254,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,127,255,255,254,0,0 +};