Created
January 16, 2022 04:04
-
-
Save karmic64/29f40fc055358d08b094c74223b0859c to your computer and use it in GitHub Desktop.
Megapanel (MegaDrive) graphics ripper
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/*** | |
Megapanel (MegaDrive) ripper written by karmic, jan 14-15, 2022 | |
requires libpng (use the option "-lpng" on command line when compiling) | |
rom must be in the working directory and called "Megapanel (J) [c][!].bin" | |
***/ | |
#include <stdlib.h> | |
#include <stdio.h> | |
#include <stdint.h> | |
#include <string.h> | |
#include <errno.h> | |
#include <sys/stat.h> | |
#include <png.h> | |
uint16_t inline get16(uint8_t *p) { return (p[0]<<8) | p[1]; } | |
uint32_t inline get32(uint8_t *p) { return (p[0]<<24) | (p[1]<<16) | (p[2]<<8) | p[3]; } | |
size_t decomp(uint8_t *dest, uint8_t *src) | |
{ | |
uint8_t buf[0x1000]; | |
memset(buf,0,0xfee); | |
size_t bi = 0xfee; | |
uint8_t *sp = src; | |
uint8_t *dp = dest; | |
size_t dsize = get16(sp); | |
sp += 2; | |
uint8_t *de = dp+dsize; | |
/***********/ | |
int bb = 0; | |
uint8_t bf = *sp++; | |
while (dp<de) | |
{ | |
if (bf & (1<<bb)) | |
{ /* regular byte */ | |
uint8_t b = *sp++; | |
*dp++ = b; | |
buf[bi] = b; | |
bi = (bi+1)&0xfff; | |
} | |
else | |
{ /* lz block */ | |
uint16_t bsi = *sp++; | |
bsi |= ((*sp) & 0xf0) << 4; | |
uint8_t size = ((*sp++) & 0x0f) + 3; | |
while (size--) | |
{ | |
uint8_t b = buf[bsi]; | |
*dp++ = b; | |
buf[bi] = b; | |
bi = (bi+1)&0xfff; | |
bsi = (bsi+1)&0xfff; | |
if (dp>=de) return dsize; | |
} | |
} | |
bb = (bb+1)&7; | |
if (!bb) | |
bf = *sp++; | |
} | |
return dsize; | |
} | |
png_byte image[8*4*8][8*4*8 / 2]; | |
png_bytep rows[8*4*8]; | |
int main() | |
{ | |
int status; | |
struct stat st; | |
uint8_t *rom = NULL; | |
status = stat("Megapanel (J) [c][!].bin",&st); | |
if (status) | |
{ | |
printf("stat: %s\n",strerror(errno)); | |
goto fail; | |
} | |
size_t romsize = st.st_size; | |
FILE *f = fopen("Megapanel (J) [c][!].bin","rb"); | |
if (!f) | |
{ | |
printf("fopen: %s\n",strerror(errno)); | |
goto fail; | |
} | |
rom = malloc(romsize); | |
if (!rom) | |
{ | |
printf("malloc: %s\n",strerror(errno)); | |
goto fail; | |
} | |
size_t readsize = fread(rom, 1, romsize, f); | |
fclose(f); | |
if (readsize != romsize) | |
{ | |
printf("read error: %s\n",strerror(errno)); | |
goto fail; | |
} | |
for (int i = 0; i < sizeof(rows)/sizeof(*rows); i++) | |
{ | |
rows[i] = image[i]; | |
} | |
uint8_t *isp = &rom[0x629a]; | |
for (int i = 0; i < 0x310/0x10; i++) | |
{ | |
uint32_t tileindex = get32(isp+0); | |
uint32_t metamapindex = get32(isp+4); | |
uint32_t tilemapindex = get32(isp+8); | |
uint32_t palindex = get32(isp+12); | |
isp += 0x10; | |
/* palette */ | |
png_color palette[0x10]; | |
{ | |
uint8_t *p = &rom[palindex]; | |
for (int i = 0; i < 0x10; i++) | |
{ | |
uint8_t b0 = *p++; | |
uint8_t b1 = *p++; | |
png_byte b = b0&0x0f; | |
png_byte g = b1>>4; | |
png_byte r = b1&0x0f; | |
r |= r<<4; | |
g |= g<<4; | |
b |= b<<4; | |
palette[i].red = r; | |
palette[i].green = g; | |
palette[i].blue = b; | |
} | |
} | |
/* metatile map */ | |
uint16_t metamap[8][8]; | |
uint16_t maxmeta = 0; | |
{ | |
uint8_t b[8*8*2]; | |
decomp(b,&rom[metamapindex]); | |
uint16_t *mm = (uint16_t*)metamap; | |
for (int i = 0; i < 8*8; i++) | |
{ | |
uint16_t mt = get16(b+(i*2)); | |
mm[i] = mt; | |
if (mt > maxmeta) maxmeta = mt; | |
} | |
} | |
/* metatiles */ | |
uint16_t *metatiles = malloc((maxmeta+1)*4*4*sizeof(*metatiles)); | |
uint16_t maxtile = 0; | |
{ | |
uint8_t *b = malloc((maxmeta+1)*4*4*2); | |
decomp(b,&rom[tilemapindex]); | |
for (int i = 0; i <= maxmeta; i++) | |
{ | |
uint16_t *mt = &metatiles[i*4*4]; | |
for (int j = 0; j < 4*4; j++) | |
{ | |
uint16_t t = get16(b+(((i*4*4)+j)*2)); | |
mt[j] = t; | |
t &= 0x7ff; | |
if (t > maxtile) maxtile = t; | |
} | |
} | |
free(b); | |
} | |
/* tiles */ | |
png_byte *tiles = malloc((maxtile+1)*0x20); | |
decomp(tiles,&rom[tileindex]); | |
/******* build image **********/ | |
/* get width and height in metatiles */ | |
int width = 8; | |
int height = 8; | |
do | |
{ | |
uint16_t top = metamap[0][width-1]; | |
for (int i = 1; i < 8; i++) | |
{ | |
if (top != metamap[i][width-1]) goto width_end; | |
} | |
} while (--width); | |
width_end: | |
do | |
{ | |
uint16_t left = metamap[height-1][0]; | |
for (int i = 1; i < 8; i++) | |
{ | |
if (left != metamap[height-1][i]) goto height_end; | |
} | |
} while (--height); | |
height_end: | |
/* actually make image data */ | |
for (int mapy = 0; mapy < height; mapy++) | |
{ | |
for (int mapx = 0; mapx < width; mapx++) | |
{ | |
for (int ty = 0; ty < 4; ty++) | |
{ | |
for (int tx = 0; tx < 4; tx++) | |
{ | |
uint16_t metaid = metamap[mapy][mapx]; | |
uint16_t t = metatiles[(metaid*4*4)+(ty*4)+(tx)]; | |
uint16_t tileid = t & 0x7ff; | |
uint16_t tileflags = t & 0xf800; | |
uint8_t *tile = &tiles[tileid*0x20]; | |
for (int tr = 0; tr < 8; tr++) | |
{ | |
uint8_t *tilerow = &tile[(tr ^ (tileflags&0x1000 ? 7 : 0))*4]; | |
uint8_t *imgrow = &image[(mapy*8*4)+(ty*8)+tr][(mapx*4*4)+(tx*4)]; | |
if (tileflags & 0x800) | |
{ | |
uint8_t b[4]; | |
for (int i = 0; i < 4; i++) | |
b[i] = (tilerow[i^3] << 4) | (tilerow[i^3] >> 4); | |
memcpy(imgrow,b,4); | |
} | |
else | |
{ | |
memcpy(imgrow,tilerow,4); | |
} | |
} | |
} | |
} | |
} | |
} | |
char fnam[32]; | |
sprintf(fnam,"img%03i.png",i); | |
printf("writing %s: ",fnam); | |
f = fopen(fnam,"wb"); | |
if (!f) | |
{ | |
printf("fopen: %s\n", strerror(errno)); | |
goto png_fail; | |
} | |
png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING,NULL,NULL,NULL); | |
if (!png_ptr) | |
{ | |
puts("png struct init error"); | |
goto png_fail; | |
} | |
png_infop info_ptr = png_create_info_struct(png_ptr); | |
if (!info_ptr) | |
{ | |
png_destroy_write_struct(&png_ptr,NULL); | |
puts("png info init error"); | |
goto png_fail; | |
} | |
if (setjmp(png_jmpbuf(png_ptr))) | |
{ | |
png_destroy_write_struct(&png_ptr,&info_ptr); | |
goto png_fail; | |
} | |
png_init_io(png_ptr,f); | |
png_set_IHDR(png_ptr,info_ptr, width*4*8,height*4*8, 4, PNG_COLOR_TYPE_PALETTE, | |
PNG_INTERLACE_NONE,PNG_COMPRESSION_TYPE_DEFAULT,PNG_FILTER_TYPE_DEFAULT); | |
png_set_PLTE(png_ptr,info_ptr, palette, 0x10); | |
png_set_rows(png_ptr,info_ptr, rows); | |
png_write_png(png_ptr,info_ptr,0,NULL); | |
png_destroy_write_struct(&png_ptr,&info_ptr); | |
puts("ok"); | |
png_fail: | |
fclose(f); | |
free(metatiles); | |
free(tiles); | |
} | |
fail: | |
free(rom); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment