diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7055202 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +* +!.gitignore +!*.c +!*.h +COPYING +README.md diff --git a/COPYING b/COPYING index 9797c07..4190e64 100644 --- a/COPYING +++ b/COPYING @@ -1,4 +1,4 @@ -Copyright (c) 2020 Marisa Kirisame, UnSX Team +Copyright (c) 2020-2022 Marisa Kirisame, UnSX Team Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index 947fddc..1e502cb 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,9 @@ Random single-file programs I've written in my spare time for small tasks. * **ddsinfo:** Shows contents of a DDS header. * **dood:** Reads an ENDOOM lump and mirrors every word "down the middle". * **dtexdupes:** Small tool I've used once or twice to clean up my doom projects of duplicate textures. +* **endoomview:** Renders ENDOOM lumps onto a terminal. Requires an Unicode terminal with 256-color support (not xterm, basically). * **fmod\_playbank (formerly fuck\_fmod):** Tool for playback of .fsb files. +* **fuzrip:** Tool for awkwardly extracting audio from Creation Engine .fuz voice files. * **fuzz:** A fancy blocky noise filter using proto-AliceGL designs. * **glfuzz:** OpenGL version of the filter. * **iwad64ex:** A small, failed experiment for ripping the Hexen 64 IWAD. @@ -23,13 +25,16 @@ Random single-file programs I've written in my spare time for small tasks. * **osnorm:** Experiment for generating object-space normals from an .obj model. * **pcxpalex:** Extracts the palette from PCX images. * **pframes:** Short utility for automating long FrameIndex lists for MODELDEF. +* **pngread:** Unfinished program for debugging PNG chunks. * **schange:** Program used along with mkwall to update the wallpaper on screen geometry changes. * **skse_cosave:** Experiment for dumping information in SKSE co-saves. * **soapstone:** Random soapstone messages from all 3 dark souls games. Messages can be generated in bulk. +* **startuptest:** Tool for showing Hexen-style startup screens (something GZDoom can't do on Linux yet). * **totty:** Sends text from stdin to tty1. Used to send certain commands when remoting into a Raspberry Pi. * **u95/u083/u086extract:** Programs for extracting data from Unreal alpha packages. This and other Unreal tools might be shifted to another repo. * **udmfvis:** dmvis clone in C for UDMF maps. No external dependencies. * **ufontext:** companion to mkfont, for extracting UE fonts. Currently does not yet extract the textures themselves. +* **umodextract:** .umod archive extractor (sort of). * **umxunpack:** Extractor for music in UE archives, with support for Unreal 227's UMX files containing vorbis audio. * **unrundeleter:** WIP program to unset the bDeleteMe flag on stuff in UE1 maps. Yes, some mappers are so hellbent on preventing modification that they delete all brushes after baking the geometry. * **usndextract:** Extracts sounds from UE archives. diff --git a/endoomview.c b/endoomview.c new file mode 100644 index 0000000..d7c4484 --- /dev/null +++ b/endoomview.c @@ -0,0 +1,79 @@ +#include + +int cgatolinux( int col ) +{ + switch ( col ) + { + case 0: + return 0; + case 1: + return 4; + case 2: + return 2; + case 3: + return 6; + case 4: + return 1; + case 5: + return 5; + case 6: + return 3; + case 7: + return 7; + } + return 0; +} + +char cp437[256][8] = +{ + // note: treat 0x00 and 0xFF as spaces, at least for the sake of printing + // _0 _1 _2 _3 _4 _5 _6 _7 _8 _9 _A _B _C _D _E _F + " " , "\u263A", "\u263B", "\u2665", "\u2666", "\u2663", "\u2660", "\u2022", "\u25CB", "\u25CB", "\u25D9", "\u2642", "\u2640", "\u266A", "\u266B", "\u263C", // 0_ + "\u25BA", "\u25C4", "\u2195", "\u203C", "\u00B6", "\u00A7", "\u25AC", "\u21A8", "\u2191", "\u2193", "\u2192", "\u2190", "\u221F", "\u2194", "\u25B2", "\u25BC", // 1_ + " " , "!" , "\"" , "#" , "$" , "%" , "&" , "'" , "(" , ")" , "*" , "+" , "," , "-" , "ยท" , "/" , // 2_ + "0" , "1" , "2" , "3" , "4" , "5" , "6" , "7" , "8" , "9" , ":" , ";" , "<" , "=" , ">" , "?" , // 3_ + "@" , "A" , "B" , "C" , "D" , "E" , "F" , "G" , "H" , "I" , "J" , "K" , "L" , "M" , "N" , "O" , // 4_ + "P" , "Q" , "R" , "S" , "T" , "U" , "V" , "W" , "X" , "Y" , "Z" , "[" , "\\" , "]" , "^" , "_" , // 5_ + "`" , "a" , "b" , "c" , "d" , "e" , "f" , "g" , "h" , "i" , "j" , "k" , "l" , "m" , "n" , "o" , // 6_ + "p" , "q" , "r" , "s" , "t" , "u" , "v" , "w" , "x" , "y" , "z" , "{" , "|" , "}" , "~" , "\u2302", // 7_ + "\u00C7", "\u00FC", "\u00E9", "\u00E2", "\u00E4", "\u00E0", "\u00E5", "\u00E7", "\u00EA", "\u00EB", "\u00E8", "\u00EF", "\u00EE", "\u00EC", "\u00C4", "\u00C5", // 8_ + "\u00C9", "\u00E6", "\u00C6", "\u00F4", "\u00F6", "\u00F2", "\u00FB", "\u00F9", "\u00FF", "\u00D6", "\u00DC", "\u00A2", "\u00A3", "\u00A5", "\u20A7", "\u0192", // 9_ + "\u00E1", "\u00ED", "\u00F3", "\u00FA", "\u00F1", "\u00D1", "\u00AA", "\u00BA", "\u00BF", "\u2310", "\u00AC", "\u00BD", "\u00BC", "\u00A1", "\u00AB", "\u00BB", // A_ + "\u2591", "\u2592", "\u2593", "\u2502", "\u2524", "\u2561", "\u2562", "\u2556", "\u2555", "\u2563", "\u2551", "\u2557", "\u255D", "\u255C", "\u255B", "\u2510", // B_ + "\u2514", "\u2534", "\u252C", "\u251C", "\u2500", "\u253C", "\u255E", "\u255F", "\u255A", "\u2554", "\u2569", "\u2566", "\u2560", "\u2550", "\u256C", "\u2567", // C_ + "\u2568", "\u2564", "\u2565", "\u2559", "\u2558", "\u2552", "\u2553", "\u256B", "\u256A", "\u2518", "\u250C", "\u2588", "\u2584", "\u258C", "\u2590", "\u2580", // D_ + "\u03B1", "\u00DF", "\u0393", "\u03C0", "\u03A3", "\u03C3", "\u00B5", "\u03C4", "\u03A6", "\u0398", "\u03A9", "\u03B4", "\u221E", "\u03C6", "\u03B5", "\u2229", // E_ + "\u2261", "\u00B1", "\u2265", "\u2264", "\u2320", "\u2321", "\u00F7", "\u2248", "\u00B0", "\u2219", "\u00B7", "\u221A", "\u207F", "\u00B2", "\u25A0", " " , // F_ +}; + +int main( int argc, char **argv ) +{ + if ( argc < 2 ) + { + fprintf(stderr,"usage: endoomview \n"); + return 1; + } + FILE *f = fopen(argv[1],"rb"); + fseek(f,0,SEEK_END); + long sz = ftell(f); + if ( sz != 4000 ) + { + fprintf(stderr,"4000 bytes expected, got %ld.\n",sz); + fclose(f); + return 2; + } + fseek(f,0,SEEK_SET); + for ( int i=0; i<2000; i++ ) + { + int chr = fgetc(f); + int col = fgetc(f); + int fg = 30+cgatolinux(col&0x07); + int bold = !!(col&0x08); + int bg = 40+cgatolinux((col&0x70)>>4); + int blink = !!(col&0x80); + printf("\033[%d;%d;%d;%dm%s",bold?1:22,blink?5:25,fg,bg,cp437[chr]); + if ( !((i+1)%80) ) printf("\033[0m\n"); + } + fclose(f); + return 0; +} diff --git a/fuzz.c b/fuzz.c index ab69f1d..667ee3e 100644 --- a/fuzz.c +++ b/fuzz.c @@ -8,6 +8,10 @@ #include #include +#ifndef FUZZ_FONT_PATH +#error please define FUZZ_FONT_PATH with the absolute path to a bitmap font +#endif + typedef struct { float r,g,b,a; @@ -37,6 +41,37 @@ SDL_Surface *ws, *fz; rgbfpx_t *fl1, *fl2, *fl3; const int fw1 = 320, fw2 = 160, fw3 = 80, fh1 = 240, fh2 = 120, fh3 = 60; +rgbfpx_t layers[3] = +{ + // Void + {0.71f,0.67f,0.95f,1.f}, + {0.66f,0.84f,0.73f,1.f}, + {0.95f,0.73f,0.81f,1.f} + // SWWM GZ + //{0.91f,0.87f,1.95f,1.f}, + //{0.66f,1.84f,0.73f,1.f}, + //{1.35f,0.73f,1.21f,1.f} + // RED ONI + //{0.61f,0.77f,0.85f,1.f}, + //{0.86f,0.64f,0.63f,1.f}, + //{1.25f,0.33f,0.41f,1.f} + // RED-EYED RAMPAGE + //{1.81f,0.97f,0.75f,1.f}, + //{0.36f,0.64f,0.93f,1.f}, + //{1.25f,0.33f,0.41f,1.f} + // RED STAR OF INNOCENCE + //{1.31f,0.87f,1.25f,1.f}, + //{0.76f,0.64f,0.63f,1.f}, + //{1.25f,1.13f,0.21f,1.f} + +}; +float speed[3] = +{ + 1.3526f, + 0.7843f, + 0.3725f +}; + void mklayer1( void ) { for ( int y=0; yh)?h:(a>l)?a:l; +} + void mergedown( void ) { rgbfpx_t twofivefive = {255.f,255.f,255.f,255.f}; @@ -113,10 +153,10 @@ void mergedown( void ) :"=m"(merged) :"m"(fl1[x1+y1*fw1]),"m"(fl2[x2+y2*fw2]), "m"(fl3[x3+y3*fw3]),"m"(twofivefive)); - stripe[x].r = merged.r; - stripe[x].g = merged.g; - stripe[x].b = merged.b; - stripe[x].a = merged.a; + stripe[x].r = clamp(merged.r,0,255); + stripe[x].g = clamp(merged.g,0,255); + stripe[x].b = clamp(merged.b,0,255); + stripe[x].a = clamp(merged.a,0,255); } } } @@ -154,7 +194,19 @@ int main( void ) fl2 = malloc(sizeof(rgbfpx_t)*fw2*fh2); fl3 = malloc(sizeof(rgbfpx_t)*fw3*fh3); TTF_Init(); - TTF_Font *fon = TTF_OpenFont("/usr/share/fonts/misc/unifont.bdf",16); + TTF_Font *fon = TTF_OpenFont(FUZZ_FONT_PATH,16); + if ( !fon ) + { + fprintf(stderr,"Could not open font '%s'.\n",FUZZ_FONT_PATH); + TTF_Quit(); + free(fl3); + free(fl2); + free(fl1); + SDL_FreeSurface(fz); + SDL_DestroyWindow(w); + SDL_Quit(); + return 1; + } SDL_Event e; SDL_Color fpscol = {160,0,0,255}; int active = 1; diff --git a/pngread.c b/pngread.c new file mode 100644 index 0000000..3b90365 --- /dev/null +++ b/pngread.c @@ -0,0 +1,178 @@ +#include +#include +#include +#include +#include + +uint32_t endianswap( uint32_t n ) +{ + // if we're in a big endian system, we don't need this + uint16_t testme = 0x1234; + if ( *(uint8_t*)(&testme) == 0x12 ) return n; + uint32_t on; + for ( int i=0; i<4; i++ ) + *(((uint8_t*)(&on))+i) = *(((uint8_t*)(&n))+(3-i)); + return on; +} + +typedef struct +{ + uint8_t highbitcheck; + char sig[3]; + char crlf[2]; + char dosstop; + char lf; +} pnghead_t; + +typedef struct +{ + uint32_t length; + char type[4]; + uint8_t *data; + uint32_t crc; +} pngchunk_t; + +typedef struct +{ + uint32_t width, height; + uint8_t depth, type, compression, filter, interlace; +} ihdr_t; + +typedef struct +{ + uint32_t xppu, yppu; + uint8_t unit; +} phys_t; + +int exitval = 0; +FILE *f = 0; +int nchunk = 0; +pnghead_t hdr = {0,0,0,0,0}; +pngchunk_t chk = {0,0,0,0}; // make sure this is null at the very start +uint32_t curcrc = 0; +int validcrc = 0; +uint8_t *tdat = 0; // accumulated data for all IDAT chunks +uint32_t tdatsiz = 0; // size of it + +void readIHDR( ihdr_t* ihdr ) +{ + printf(" width: %u\n height: %u\n depth: %u\n type: %u\n compression: %u\n filter: %u\n interlace: %u\n", + endianswap(ihdr->width),endianswap(ihdr->height),ihdr->depth,ihdr->type,ihdr->compression,ihdr->filter,ihdr->interlace); +} + +// read up a stream chunk +void readIDAT( uint8_t* dat, uint32_t len ) +{ + if ( tdat ) tdat = realloc(tdat,tdatsiz+len); + else tdat = malloc(len); + memcpy(tdat+tdatsiz,dat,len); + tdatsiz += len; +} + +// process the whole thing +void processIDAT( void ) +{ +} + +void readpHYs( phys_t* phys ) +{ + printf(" xppu: %u\n yppu: %u\n unit: %u\n",endianswap(phys->xppu),endianswap(phys->yppu),phys->unit); +} + +void readtEXt( uint8_t* data, uint32_t len ) +{ + uint8_t *key = data; + uint8_t *val = data+strlen(data)+1; + uint32_t vallen = len-(val-key); + printf(" '%s' :: '%.*s'\n",key,vallen,val); +} + +void readchunkdata( pngchunk_t* chk ) +{ + if ( !strncmp(chk->type,"IHDR",4) ) + readIHDR((ihdr_t*)chk->data); + else if ( !strncmp(chk->type,"pHYs",4) ) + readpHYs((phys_t*)chk->data); + else if ( !strncmp(chk->type,"tEXt",4) ) + readtEXt(chk->data,chk->length); +} + + +int main( int argc, char **argv ) +{ + if ( argc < 2 ) + { + printf("No file supplied.\n"); + exitval = 0; + goto endmii; + } + f = fopen(argv[1],"rb"); + if ( !f ) + { + printf("Failed to open file.\n"); + exitval = 1; + goto endmii; + } + fread(&hdr,1,sizeof(hdr),f); + if ( hdr.highbitcheck != 0x89 ) + { + printf("High bit check failed.\n"); + exitval = 2; + goto endmii; + } + if ( strncmp(hdr.sig,"PNG",3) ) + { + printf("PNG signature check failed.\n"); + exitval = 4; + goto endmii; + } + if ( strncmp(hdr.crlf,"\r\n",2) ) + { + printf("CRLF check failed.\n"); + exitval = 8; + goto endmii; + } + if ( hdr.dosstop != '\032' ) + { + printf("EOF check failed.\n"); + exitval = 16; + goto endmii; + } + if ( hdr.lf != '\n' ) + { + printf("LF check failed.\n"); + exitval = 32; + goto endmii; + } +readchunk: + fread(&chk,1,8,f); // read size and type first + chk.length = endianswap(chk.length); // swap from BE + if ( (nchunk == 0) && strncmp(chk.type,"IHDR",4) ) + { + printf("First chunk is not IHDR.\n"); + exitval = 64; + goto endmii; + } + // (re)allocate data + if ( chk.data ) chk.data = realloc(chk.data,chk.length); + else chk.data = malloc(chk.length); + fread(chk.data,1,chk.length,f); // read data + fread(&(chk.crc),1,4,f); // read CRC + chk.crc = endianswap(chk.crc); + curcrc = crc32(0,chk.type,4); + if ( chk.length ) curcrc = crc32(curcrc,chk.data,chk.length); + validcrc = (curcrc == chk.crc); + printf("%.4s chunk of length %u (CRC32: %08X, %s).\n",chk.type,chk.length,chk.crc,validcrc?"OK":"FAILED"); + if ( !validcrc ) printf(" CRC32 ACTUAL: %08X\n",curcrc); + else readchunkdata(&chk); + nchunk++; + if ( strncmp(chk.type,"IEND",4) ) goto readchunk; + long cpos = ftell(f); + fseek(f,0,SEEK_END); + long epos = ftell(f); + if ( epos > cpos ) printf("%lu bytes of extra data after IEND.\n",epos-cpos); +endmii: + if ( f ) fclose(f); + if ( chk.data ) free(chk.data); + return exitval; +} diff --git a/startuptest.c b/startuptest.c new file mode 100644 index 0000000..f869b55 --- /dev/null +++ b/startuptest.c @@ -0,0 +1,275 @@ +/* + startuptest.c : Previews Hexen STARTUP screens for GZDoom mods. + Requires SDL2 and SDL2_mixer. + (C)2021 Marisa Kirisame, UnSX Team. + Released under the MIT License. +*/ + +#define _POSIX_C_SOURCE 199309L +#include +#include +#include +#include +#include +#include +#include + +SDL_Window *w = 0; +SDL_Surface *ws = 0, *st = 0; + +Mix_Music *startmus = 0; +Mix_Chunk *ticksnd = 0, *dripsnd = 0; + +#define ST_MAX_NOTCHES 32 +#define ST_NOTCH_WIDTH 16 +#define ST_NOTCH_HEIGHT 23 +#define ST_PROGRESS_X 64 +#define ST_PROGRESS_Y 441 + +#define ST_NETPROGRESS_X 288 +#define ST_NETPROGRESS_Y 32 +#define ST_NETNOTCH_WIDTH 4 // 8 in vanilla hexen +#define ST_NETNOTCH_HEIGHT 16 +#define ST_MAX_NETNOTCHES 8 + +SDL_Color pal[16], pals[16] = {{0,0,0,0xFF}}; +Uint8 startup[640*480], notch[ST_NOTCH_WIDTH*ST_NOTCH_HEIGHT], netnotch[ST_NETNOTCH_WIDTH*ST_NETNOTCH_HEIGHT]; +int donet = 0; + +int ReadFiles( const char *s, const char *n, const char *nn ) +{ + FILE *f = fopen(s,"rb"); + if ( !f ) + { + fprintf(stderr,"Could not open startup file: %s\n",strerror(errno)); + return 1; + } + // read palette + for ( int i=0; i<16; i++ ) + { + fread(&pal[i],1,3,f); + pal[i].r <<= 2; + pal[i].g <<= 2; + pal[i].b <<= 2; + pal[i].a = 0xFF; + } + // read image + memset(&startup[0],0,640*480); + for ( int i=0; i<4; i++ ) for ( int j=0; j<(640*480); j+=8 ) + { + Uint8 bits; + fread(&bits,1,1,f); + startup[j] |= ((bits>>7)&1)<>6)&1)<>5)&1)<>4)&1)<>3)&1)<>2)&1)<>1)&1)<>4)&0xF; + notch[i+1] = bits&0xF; + } + fclose(f); + // read netnotch + memset(&netnotch[0],0,ST_NETNOTCH_WIDTH*ST_NETNOTCH_HEIGHT); + if ( !donet ) return 0; + f = fopen(nn,"rb"); + if ( !f ) + { + fprintf(stderr,"Could not open netnotch file: %s\n",strerror(errno)); + return 1; + } + for ( int i=0; i<(ST_NETNOTCH_WIDTH*ST_NETNOTCH_HEIGHT); i+=2 ) + { + Uint8 bits; + fread(&bits,1,1,f); + netnotch[i] = (bits>>4)&0xF; + netnotch[i+1] = bits&0xF; + } + fclose(f); + return 0; +} + +enum EStartupState +{ + STATE_FADEIN, + STATE_NOTCH, + STATE_NETNOTCH, + STATE_WAIT +}; + +double gametime; + +#define NANOS_SEC 1000000000L + +long ticker( void ) +{ + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC_RAW,&ts); + return ts.tv_nsec+ts.tv_sec*NANOS_SEC; +} + +int main( int argc, char **argv ) +{ + if ( (argc > 1) && !strcmp(argv[1],"-n") ) donet = 1; + if ( argc < 3+donet*2 ) + { + fprintf(stderr, "usage: startuptest [music path] [tick path]\n" + " startuptest -n [music path] [tick path] [drip path]\n"); + return 1; + } + if ( ReadFiles(argv[1+donet],argv[2+donet],donet?argv[4]:"") ) + return 1; + SDL_Init(SDL_INIT_VIDEO|SDL_INIT_AUDIO|SDL_INIT_TIMER|SDL_INIT_EVENTS); + w = SDL_CreateWindow("STARTUP TEST",SDL_WINDOWPOS_UNDEFINED,SDL_WINDOWPOS_UNDEFINED,640,480,SDL_WINDOW_SHOWN); + ws = SDL_GetWindowSurface(w); + st = SDL_CreateRGBSurface(0,640,480,8,0,0,0,0); + SDL_SetSurfaceBlendMode(st,SDL_BLENDMODE_NONE); + SDL_SetPaletteColors(st->format->palette,&pals[0],0,16); + Mix_Init(MIX_INIT_FLAC|MIX_INIT_MOD|MIX_INIT_MP3|MIX_INIT_OGG); + Mix_OpenAudio(48000,MIX_DEFAULT_FORMAT,2,4096); + if ( argc > 3+donet*2 ) startmus = Mix_LoadMUS(argv[3+donet*2]); + if ( argc > 4+donet*2 ) ticksnd = Mix_LoadWAV(argv[4+donet*2]); + if ( donet && (argc > 7) ) dripsnd = Mix_LoadWAV(argv[7]); + SDL_Event e; + int active = 1; + long tick, tock; + if ( startmus ) Mix_PlayMusic(startmus,-1); + // first blit + SDL_LockSurface(st); + memcpy(st->pixels,&startup[0],640*480); + SDL_UnlockSurface(st); + int state = STATE_FADEIN; + int fadestep = 0; + int ncnt = 0; + while ( active ) + { + tick = ticker(); + while ( SDL_PollEvent(&e) ) if ( (e.type == SDL_QUIT) + || ((e.type == SDL_KEYDOWN) + && (e.key.keysym.sym == SDLK_ESCAPE)) ) active = 0; + switch ( state ) + { + case STATE_FADEIN: + switch ( fadestep ) + { + case 0: + if ( gametime > .125 ) + { + fadestep = 1; + gametime = 0.; + } + break; + case 1: + ncnt++; + for ( int i=0; i<16; i++ ) + { + pals[i].r = (pal[i].r*ncnt)/8; + pals[i].g = (pal[i].g*ncnt)/8; + pals[i].b = (pal[i].b*ncnt)/8; + } + SDL_SetPaletteColors(st->format->palette,&pals[0],0,16); + fadestep = 0; + gametime = 0.; + if ( ncnt >= 8 ) + { + ncnt = 0; + state = STATE_NOTCH; + gametime = -1.; + } + break; + } + break; + case STATE_NOTCH: + switch ( fadestep ) + { + case 0: + if ( gametime > 1. ) + { + fadestep++; + gametime = (float)ncnt/(ST_MAX_NOTCHES); + gametime = powf(gametime,.25)*.9; + } + break; + case 1: + if ( ticksnd ) Mix_PlayChannel(0,ticksnd,0); + SDL_LockSurface(st); + // draw notch + for ( int i=0; ipixels+(ST_PROGRESS_Y+i)*640+ST_PROGRESS_X+ST_NOTCH_WIDTH*ncnt,¬ch[0]+ST_NOTCH_WIDTH*i,ST_NOTCH_WIDTH); + SDL_UnlockSurface(st); + ncnt++; + if ( ncnt >= ST_MAX_NOTCHES ) fadestep = 2; + else fadestep = 0; + break; + case 2: + if ( donet ) state = STATE_NETNOTCH; + else state = STATE_WAIT; + fadestep = 0; + gametime = 0.; + ncnt = 0; + break; + } + break; + case STATE_NETNOTCH: + switch ( fadestep ) + { + case 0: + if ( gametime > 1. ) + { + fadestep++; + gametime = 0.; + } + break; + case 1: + if ( dripsnd ) Mix_PlayChannel(0,dripsnd,0); + SDL_LockSurface(st); + // draw netnotch + for ( int i=0; ipixels+(ST_NETPROGRESS_Y+i)*640+ST_NETPROGRESS_X+ST_NETNOTCH_WIDTH*ncnt,&netnotch[0]+ST_NETNOTCH_WIDTH*i,ST_NETNOTCH_WIDTH); + SDL_UnlockSurface(st); + ncnt++; + if ( ncnt >= ST_MAX_NETNOTCHES ) fadestep = 2; + else fadestep = 0; + break; + case 2: + state = STATE_WAIT; + fadestep = 0; + gametime = 0.; + break; + } + break; + case STATE_WAIT: + // wow it's literally nothing + break; + } + SDL_BlitSurface(st,0,ws,0); + SDL_UpdateWindowSurface(w); + tock = ticker(); + gametime += (float)(tock-tick)/NANOS_SEC; + } + if ( startmus ) Mix_HaltMusic(); + if ( dripsnd ) Mix_FreeChunk(dripsnd); + if ( ticksnd ) Mix_FreeChunk(ticksnd); + if ( startmus ) Mix_FreeMusic(startmus); + Mix_Quit(); + SDL_FreeSurface(st); + SDL_DestroyWindow(w); + SDL_Quit(); + return 0; +} diff --git a/umodextract.c b/umodextract.c new file mode 100644 index 0000000..c5e0278 --- /dev/null +++ b/umodextract.c @@ -0,0 +1,170 @@ +#include +#include +#include +#include +#include + +#define UMOD_MAGIC 0x9FE3C5A3 + +typedef struct +{ + uint32_t sig, dir, size, ver, crc; +} umodfoot_t; + +typedef struct +{ + char *fname; + uint32_t ofs, len, flags; +} umoddir_t; + +// CRC has to be done in this very specific way or shit will go wrong +// I totally did not take this from leaked Unreal Engine source code + +uint32_t crctable[256]; + +void crcinit( void ) +{ + for( uint32_t i=0; i<256; i++ ) for ( uint32_t c=(i<<24), j=8; j; j-- ) + crctable[i] = c = (c&0x80000000)?((c<<1)^0x04C11DB7):(c<<1); +} + +uint32_t crc( const void *s, size_t l, uint32_t crc ) +{ + uint8_t *d = (uint8_t*)s; + crc = ~crc; + for ( size_t i=0; i>24)^d[i]]; + return ~crc; +} + +// fuck you tim sweeney (or whoever else is responsible for this crime) +int32_t readindex( FILE *f ) +{ + uint8_t byte[5] = {0}; + fread(&byte[0],1,1,f); + if ( !byte[0] ) return 0; + if ( byte[0]&0x40 ) + { + for ( int i=1; i<5; i++ ) + { + fread(&byte[i],1,1,f); + if ( !(byte[i]&0x80) ) break; + } + } + int32_t tf = byte[0]&0x3f; + tf |= (int32_t)(byte[1]&0x7f)<<6; + tf |= (int32_t)(byte[2]&0x7f)<<13; + tf |= (int32_t)(byte[3]&0x7f)<<20; + tf |= (int32_t)(byte[4]&0x7f)<<27; + if ( byte[0]&0x80 ) tf *= -1; + return tf; +} + +int main( int argc, char **argv ) +{ + if ( argc < 2 ) + { + fprintf(stderr,"usage: umodextract \n"); + return 1; + } + FILE *f = fopen(argv[1],"rb"); + fseek(f,-20,SEEK_END); + umodfoot_t feet; + fread(&feet,1,20,f); + if ( feet.sig != UMOD_MAGIC ) + { + fclose(f); + return 2; + } + //printf("sig: %08X\ndir: &%u\nsize: %u bytes\nver: %u\ncrc: %08X\n", + // feet.sig,feet.dir,feet.size,feet.ver,feet.crc); + // CRC verification + void *dat = malloc(feet.size-20); + fseek(f,0,SEEK_SET); + fread(dat,1,feet.size-20,f); + crcinit(); + uint32_t thiscrc = crc(dat,feet.size-20,0); + free(dat); + if ( thiscrc != feet.crc ) + { + fprintf(stderr,"CRC mismatch! %08X != %08X\n",thiscrc,feet.crc); + fclose(f); + return 4; + } + fseek(f,feet.dir,SEEK_SET); + int32_t ndir = readindex(f); + //printf("ndir: %d\n",ndir); + umoddir_t *dir = calloc(ndir,sizeof(umoddir_t)); + for ( int32_t i=0; i