-
-
Save karmic64/7c567d74c7d75e51602dba8490ed34a1 to your computer and use it in GitHub Desktop.
Hydlide (NES) map 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
/*** | |
hydlide (nes) map ripper | |
coded by karmic, jan 27 2022 | |
requires libpng (compile with "-lpng") | |
rom must be in the working directory and called "Hydlide (U) [!].nes" | |
***/ | |
#include <stdlib.h> | |
#include <stdio.h> | |
#include <string.h> | |
#include <stdint.h> | |
#include <setjmp.h> | |
#include <png.h> | |
/*** | |
map writing subroutine is at $d4bb | |
map is written to a buffer row by row | |
screen pointers are at $ec00-$ec45 | |
attribute writing subroutine is at $dc8d | |
attributes are written row by row, in steps of two columns | |
tile->attribute table is at $dfc4 | |
the player starts on screen 0, the first 25 screens are the game map, arranged row by row, then the underground parts | |
***/ | |
uint16_t inline get16(uint8_t *p) { return p[0] | (p[1]<<8); } | |
#define SCREENSIZE (0x16) | |
uint8_t screenbuf[SCREENSIZE][SCREENSIZE]; | |
png_color inline getcolor(uint32_t c) | |
{ | |
png_color co; | |
co.red = (c>>16)&0xff; | |
co.green = (c>>8)&0xff; | |
co.blue = (c>>0)&0xff; | |
return co; | |
} | |
const uint32_t palette[] = { 0xFF666666, 0xFF002A88, 0xFF1412A7, 0xFF3B00A4, 0xFF5C007E, 0xFF6E0040, 0xFF6C0600, 0xFF561D00, 0xFF333500, 0xFF0B4800, 0xFF005200, 0xFF004F08, 0xFF00404D, 0xFF000000, 0xFF000000, 0xFF000000, 0xFFADADAD, 0xFF155FD9, 0xFF4240FF, 0xFF7527FE, 0xFFA01ACC, 0xFFB71E7B, 0xFFB53120, 0xFF994E00, 0xFF6B6D00, 0xFF388700, 0xFF0C9300, 0xFF008F32, 0xFF007C8D, 0xFF000000, 0xFF000000, 0xFF000000, 0xFFFFFEFF, 0xFF64B0FF, 0xFF9290FF, 0xFFC676FF, 0xFFF36AFF, 0xFFFE6ECC, 0xFFFE8170, 0xFFEA9E22, 0xFFBCBE00, 0xFF88D800, 0xFF5CE430, 0xFF45E082, 0xFF48CDDE, 0xFF4F4F4F, 0xFF000000, 0xFF000000, 0xFFFFFEFF, 0xFFC0DFFF, 0xFFD3D2FF, 0xFFE8C8FF, 0xFFFBC2FF, 0xFFFEC4EA, 0xFFFECCC5, 0xFFF7D8A5, 0xFFE4E594, 0xFFCFEF96, 0xFFBDF4AB, 0xFFB3F3CC, 0xFFB5EBF2, 0xFFB8B8B8, 0xFF000000, 0xFF000000 }; | |
#define BGCOLOR (0x0f) | |
const uint8_t gamepal[] = {0x2a,0x18,0x1a, 0x14,0x28,0x30, 0x10,0x00,0x12, 0x00,0x10,0x2a}; | |
#define PNGPALSIZE (4*3 + 1) | |
png_color pngpal[PNGPALSIZE]; | |
#define BMPSIZE (SCREENSIZE*8) | |
png_byte bmp[BMPSIZE][BMPSIZE]; | |
png_bytep bmprows[BMPSIZE]; | |
uint8_t prg[0x8000]; | |
uint8_t chr[0x2000]; | |
int main() | |
{ | |
FILE *f = fopen("Hydlide (U) [!].nes","rb"); | |
if (!f) | |
{ | |
perror("rom open error"); | |
return EXIT_FAILURE; | |
} | |
fseek(f,0x10,SEEK_SET); | |
fread(prg,1,sizeof(prg),f); | |
fread(chr,1,sizeof(chr),f); | |
fclose(f); | |
pngpal[0] = getcolor(palette[BGCOLOR]); | |
for (int i = 0; i < sizeof(gamepal); i++) | |
pngpal[i+1] = getcolor(palette[gamepal[i]]); | |
for (int i = 0; i < BMPSIZE; i++) | |
bmprows[i] = bmp[i]; | |
for (int screen = 0; screen < (0x46/2); screen++) | |
{ | |
/* screen */ | |
memset(screenbuf,0x40,sizeof(screenbuf)); | |
uint8_t *p = prg+get16(prg+0x6c00+(screen*2))-0x8000; | |
uint8_t *sp = (uint8_t*)screenbuf; | |
unsigned repcnt = 0; | |
unsigned tile; | |
while (sp < (uint8_t*)screenbuf+sizeof(screenbuf)) | |
{ | |
if (!repcnt) | |
{ | |
unsigned b = *p++; | |
repcnt = b & 0x0f; | |
if (!repcnt) repcnt = 0x10; | |
tile = b >> 4; | |
} | |
if (tile < 9) | |
*sp = tile; | |
else if (tile < 0x0d) | |
{ | |
unsigned t = (tile-9)*4 + 9; | |
sp[0] = t; | |
sp[1] = t+1; | |
sp[0 + SCREENSIZE] = t+2; | |
sp[1 + SCREENSIZE] = t+3; | |
} | |
else if (tile < 0x0e) | |
{ | |
sp[0] = 0x19; | |
sp[1] = 0x1a; | |
sp[2] = 0x1b; | |
sp[3] = 0x1c; | |
sp[SCREENSIZE + 0] = 0x1d; | |
sp[SCREENSIZE + 1] = 0x1e; | |
sp[SCREENSIZE + 2] = 0x1f; | |
sp[SCREENSIZE + 3] = 0x20; | |
sp[SCREENSIZE*2 + 0] = 0x21; | |
sp[SCREENSIZE*2 + 1] = 0x22; | |
sp[SCREENSIZE*2 + 2] = 0x23; | |
sp[SCREENSIZE*2 + 3] = 0x24; | |
sp[SCREENSIZE*3 + 0] = 0x25; | |
sp[SCREENSIZE*3 + 1] = 0x26; | |
sp[SCREENSIZE*3 + 2] = 0x27; | |
sp[SCREENSIZE*3 + 3] = 0x28; | |
} | |
--repcnt; | |
sp++; | |
} | |
/* image */ | |
memset(bmp,0,sizeof(bmp)); | |
for (int ytile = 0; ytile < SCREENSIZE; ytile++) | |
{ | |
for (int xtile = 0; xtile < SCREENSIZE; xtile++) | |
{ | |
unsigned t = screenbuf[ytile][xtile]; | |
unsigned attr = prg[0x5fc4 + t]; | |
uint8_t *td = &chr[0x1000+(t*0x10)]; | |
for (int yf = 0; yf < 8; yf++) | |
{ | |
for (int xf = 0; xf < 8; xf++) | |
{ | |
unsigned x = (xtile*8)+xf; | |
unsigned y = (ytile*8)+yf; | |
unsigned tm = (0x80)>>xf; | |
unsigned tc = (td[yf] & tm ? 1 : 0) | (td[yf+8] & tm ? 2 : 0); | |
unsigned c = !tc ? 0 : (attr*3)+(tc-1)+1; | |
bmp[y][x] = c; | |
} | |
} | |
} | |
} | |
/* output png */ | |
char fnam[16]; | |
sprintf(fnam, "%02i.png",screen); | |
void pngerr(png_structp p, const char *e) | |
{ | |
fprintf(stderr,"%s libpng error: %s\n",fnam,e); | |
} | |
void pngwarn(png_structp p, const char *e) | |
{ | |
fprintf(stderr,"%s libpng warning: %s\n",fnam,e); | |
} | |
FILE *f = fopen(fnam,"wb"); | |
if (!f) | |
{ | |
fprintf(stderr, "%s open error: %s\n", fnam,strerror(errno)); | |
continue; | |
} | |
png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING,NULL,pngerr,pngwarn); | |
if (!png_ptr) | |
{ | |
fprintf(stderr,"%s png init error\n", fnam); | |
fclose(f); | |
continue; | |
} | |
png_infop info_ptr = png_create_info_struct(png_ptr); | |
if (!info_ptr) | |
{ | |
fprintf(stderr,"%s png info init error\n", fnam); | |
png_destroy_write_struct(&png_ptr,NULL); | |
fclose(f); | |
continue; | |
} | |
if (setjmp(png_jmpbuf(png_ptr))) | |
{ | |
} | |
else | |
{ | |
png_init_io(png_ptr,f); | |
png_set_IHDR(png_ptr,info_ptr, BMPSIZE,BMPSIZE, 4,PNG_COLOR_TYPE_PALETTE, | |
PNG_INTERLACE_NONE,PNG_COMPRESSION_TYPE_DEFAULT,PNG_FILTER_TYPE_DEFAULT); | |
png_set_PLTE(png_ptr,info_ptr,pngpal,PNGPALSIZE); | |
png_write_info(png_ptr,info_ptr); | |
png_set_packing(png_ptr); | |
png_write_image(png_ptr,bmprows); | |
png_write_end(png_ptr,info_ptr); | |
fprintf(stderr, "exported %s\n",fnam); | |
} | |
png_destroy_write_struct(&png_ptr,&info_ptr); | |
fclose(f); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment