Skip to content

Instantly share code, notes, and snippets.

@karmic64
Created January 16, 2022 04:04
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save karmic64/29f40fc055358d08b094c74223b0859c to your computer and use it in GitHub Desktop.
Save karmic64/29f40fc055358d08b094c74223b0859c to your computer and use it in GitHub Desktop.
Megapanel (MegaDrive) graphics ripper
/***
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