Update this shit.

This commit is contained in:
Marisa the Magician 2022-01-01 20:50:20 +01:00
commit cc65edd617
8 changed files with 786 additions and 21 deletions

6
.gitignore vendored Normal file
View file

@ -0,0 +1,6 @@
*
!.gitignore
!*.c
!*.h
COPYING
README.md

View file

@ -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

View file

@ -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.

79
endoomview.c Normal file
View file

@ -0,0 +1,79 @@
#include <stdio.h>
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 <file>\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;
}

92
fuzz.c
View file

@ -8,6 +8,10 @@
#include <SDL2/SDL_ttf.h>
#include <time.h>
#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; y<fh1; y++ )
@ -44,11 +79,11 @@ void mklayer1( void )
float rg;
for ( int x=0; x<fw1; x++ )
{
rg = 2.f*fabsf(fract(rn(x,y)+t*1.3526f)-0.5f);
fl1[x+y*fw1].r = 0.71f*rg;
fl1[x+y*fw1].g = 0.67f*rg;
fl1[x+y*fw1].b = 0.95f*rg;
fl1[x+y*fw1].a = 1.f*rg;
rg = 2.f*fabsf(fract(rn(x,y)+t*speed[0])-0.5f);
fl1[x+y*fw1].r = layers[0].r*rg;
fl1[x+y*fw1].g = layers[0].g*rg;
fl1[x+y*fw1].b = layers[0].b*rg;
fl1[x+y*fw1].a = layers[0].a*rg;
}
}
}
@ -60,11 +95,11 @@ void mklayer2( void )
float rg;
for ( int x=0; x<fw2; x++ )
{
rg = 2.f*fabsf(fract(rn(x,y)+t*0.7843f)-0.5f);
fl2[x+y*fw2].r = 0.66f*rg;
fl2[x+y*fw2].g = 0.84f*rg;
fl2[x+y*fw2].b = 0.73f*rg;
fl2[x+y*fw2].a = 1.f*rg;
rg = 2.f*fabsf(fract(rn(x,y)+t*speed[1])-0.5f);
fl2[x+y*fw2].r = layers[1].r*rg;
fl2[x+y*fw2].g = layers[1].g*rg;
fl2[x+y*fw2].b = layers[1].b*rg;
fl2[x+y*fw2].a = layers[1].a*rg;
}
}
}
@ -76,15 +111,20 @@ void mklayer3( void )
float rg;
for ( int x=0; x<fw3; x++ )
{
rg = 2.f*fabsf(fract(rn(x,y)+t*0.3725f)-0.5f);
fl3[x+y*fw3].r = 0.95f*rg;
fl3[x+y*fw3].g = 0.73f*rg;
fl3[x+y*fw3].b = 0.81f*rg;
fl3[x+y*fw3].a = 1.f*rg;
rg = 2.f*fabsf(fract(rn(x,y)+t*speed[2])-0.5f);
fl3[x+y*fw3].r = layers[2].r*rg;
fl3[x+y*fw3].g = layers[2].g*rg;
fl3[x+y*fw3].b = layers[2].b*rg;
fl3[x+y*fw3].a = layers[2].a*rg;
}
}
}
float clamp( float a, float l, float h )
{
return (a>h)?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;

178
pngread.c Normal file
View file

@ -0,0 +1,178 @@
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <zlib.h>
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;
}

275
startuptest.c Normal file
View file

@ -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 <stdio.h>
#include <errno.h>
#include <string.h>
#include <SDL2/SDL.h>
#include <SDL2/SDL_mixer.h>
#include <time.h>
#include <math.h>
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)<<i;
startup[j+1] |= ((bits>>6)&1)<<i;
startup[j+2] |= ((bits>>5)&1)<<i;
startup[j+3] |= ((bits>>4)&1)<<i;
startup[j+4] |= ((bits>>3)&1)<<i;
startup[j+5] |= ((bits>>2)&1)<<i;
startup[j+6] |= ((bits>>1)&1)<<i;
startup[j+7] |= (bits&1)<<i;
}
fclose(f);
// read notch
memset(&notch[0],0,ST_NOTCH_WIDTH*ST_NOTCH_HEIGHT);
f = fopen(n,"rb");
if ( !f )
{
fprintf(stderr,"Could not open notch file: %s\n",strerror(errno));
return 1;
}
for ( int i=0; i<(ST_NOTCH_WIDTH*ST_NOTCH_HEIGHT); i+=2 )
{
Uint8 bits;
fread(&bits,1,1,f);
notch[i] = (bits>>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 <startup path> <notch path> [music path] [tick path]\n"
" startuptest -n <startup path> <notch path> <netnotch path> [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; i<ST_NOTCH_HEIGHT; i++ )
memcpy((Uint8*)st->pixels+(ST_PROGRESS_Y+i)*640+ST_PROGRESS_X+ST_NOTCH_WIDTH*ncnt,&notch[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; i<ST_NETNOTCH_HEIGHT; i++ )
memcpy((Uint8*)st->pixels+(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;
}

170
umodextract.c Normal file
View file

@ -0,0 +1,170 @@
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <sys/stat.h>
#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<l; i++ ) crc = (crc<<8)^crctable[(crc>>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 <umod file>\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<ndir; i++ )
{
int32_t flen = readindex(f);
dir[i].fname = malloc(flen);
fread(dir[i].fname,1,flen,f);
fread(&(dir[i].ofs),1,4,f);
fread(&(dir[i].len),1,4,f);
fread(&(dir[i].flags),1,4,f);
//printf("fname: %.*s (%d)\nofs: &%u\nlen: %u\nflags: %08X\n",
// flen,dir[i].fname,flen,dir[i].ofs,dir[i].len,dir[i].flags);
long saved = ftell(f);
fseek(f,dir[i].ofs,SEEK_SET);
int isini = 0;
if ( (isini=!!strstr(dir[i].fname,"Manifest.ini")) || !!strstr(dir[i].fname,"Manifest.int") )
{
printf("==== BEGIN Manifest.in%c ====\n\n",isini?'i':'t');
uint8_t *str = malloc(dir[i].len);
fread(str,1,dir[i].len,f);
fwrite(str,1,dir[i].len,stdout);
free(str);
printf("\n==== END Manifest.in%c ====\n",isini?'i':'t');
}
else
{
char *rpath = dir[i].fname;
if ( !strchr(rpath,'\\') )
{
FILE *fout = fopen(rpath,"wb");
void *dat = malloc(dir[i].len);
fread(dat,1,dir[i].len,f);
fwrite(dat,1,dir[i].len,fout);
free(dat);
fclose(fout);
}
else
{
FILE *fout;
// subdivide folders
char *nxt = 0;
//int i = 0;
do
{
nxt = strchr(rpath,'\\');
if ( nxt )
{
*nxt = '\0';
//printf("path%d: %s\n",i,rpath);
mkdir(rpath,0755);
*nxt = '/';
rpath = nxt+1;
}
else
{
// the file itself
//printf("fout: %s\n",dir[i].fname);
printf("==== EXTRACTING %s (%u bytes) ====\n",dir[i].fname,dir[i].len);
fout = fopen(dir[i].fname,"wb");
void *dat = malloc(dir[i].len);
fread(dat,1,dir[i].len,f);
fwrite(dat,1,dir[i].len,fout);
free(dat);
fclose(fout);
}
//i++;
} while ( nxt );
}
}
fseek(f,saved,SEEK_SET);
}
for ( int32_t i=0; i<ndir; i++ ) free(dir[i].fname);
free(dir);
fclose(f);
return 0;
}