Update this shit.
This commit is contained in:
parent
8d0c3e84f2
commit
cc65edd617
8 changed files with 786 additions and 21 deletions
6
.gitignore
vendored
Normal file
6
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
*
|
||||
!.gitignore
|
||||
!*.c
|
||||
!*.h
|
||||
COPYING
|
||||
README.md
|
||||
2
COPYING
2
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
|
||||
|
|
|
|||
|
|
@ -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
79
endoomview.c
Normal 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
92
fuzz.c
|
|
@ -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
178
pngread.c
Normal 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
275
startuptest.c
Normal 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(¬ch[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,¬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; 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
170
umodextract.c
Normal 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;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue