From 55010c8b48290a31b2c68aa7b4fb56ab71365bf5 Mon Sep 17 00:00:00 2001 From: Marisa Kirisame Date: Tue, 19 Nov 2019 17:28:33 +0100 Subject: [PATCH] Wow I haven't updated this in a long time. --- README.md | 22 +- cube2enviro.c | 220 ++++++++++++ ddsinfo.c | 4 + dood.c | 44 ++- mazestuff.c | 522 +++++++++++++++++++++++++++++ pcxpalex.c | 25 ++ pframes.c | 2 +- u083extract.c | 889 +++++++++++++++++++++++++++++++++++++++++++++++++ u086extract.c | 404 ++++++++++++++++++++++ u95extract.c | 430 ++++++++++++++++++++++++ ufontext.c | 33 +- unrundeleter.c | 361 ++++++++++++++++++++ usndextract.c | 420 +++++++++++++++++++++++ utxextract.c | 581 ++++++++++++++++++++++++++++++++ wavrip.c | 46 +++ 15 files changed, 3980 insertions(+), 23 deletions(-) create mode 100644 cube2enviro.c create mode 100644 mazestuff.c create mode 100644 pcxpalex.c create mode 100644 u083extract.c create mode 100644 u086extract.c create mode 100644 u95extract.c create mode 100644 unrundeleter.c create mode 100644 usndextract.c create mode 100644 utxextract.c create mode 100644 wavrip.c diff --git a/README.md b/README.md index a132a56..b046aa1 100644 --- a/README.md +++ b/README.md @@ -2,13 +2,22 @@ Random single-file programs I've written in my spare time for small tasks. * bleep: I got bored and decided to write a pc speaker music program. * ckmextract: Extracts ESP and BSA from Skyrim steam workshop mod archives. +* cube2enviro: A simple GL 4.4 program. Loads a cubemap and draws a flattened + hemisphere environment map that can be used in Unreal. * 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. * fmod\_playbank (formerly fuck\_fmod): Tool for playback of .fsb 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. * lutconv: A program for converting various "3D" LUT textures to actual 3D LUTs - in DDS volume maps. Successor to mkvolume. Used for MariENB. + in DDS volume maps. Successor to mkvolume. Used for MariENB. Plus two + additional tools for "deconverting" volume maps, and one for smoothing them + out to reduce potential banding. +* mazestuff: A dungeon generator for roguelikes. This was made as part of a + commission for a friend, hence the very detailed comments. * memrd/memsk/memwr: Quick 'n dirty tools for memory manipulation on running programs. * mkfont: A tool I use to convert UE fonts exported with UTPT into fonts for @@ -22,6 +31,7 @@ Random single-file programs I've written in my spare time for small tasks. * mkwall: A program I use on a daily basis to set my wallpaper on every Linux machine. * osnorm: Experiment for generating object-space normals from an .obj model. +* pcxpalex.c: Extracts the palette from PCX images. * pframes: Short utility for automating long FrameIndex lists for MODELDEF. * schange: Program used along with mkwall to update the wallpaper on screen geometry changes. @@ -30,11 +40,21 @@ Random single-file programs I've written in my spare time for small tasks. can be generated in bulk. * 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. * 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. +* utxextract: Extracts textures from UE archives. * vc2sdl: Passes the contents of the VC4 framebuffer to a SDL window. Was used for video playback experiments on a Raspberry Pi with a SPI LCD. +* wavrip: Cheap WAV file extractor that naively searchs for RIFF headers. * withhands: Talk like W.D. Gaster. * zfs-rootfs.patch: The original patch for archzfs to support my specific rootfs dataset mountpoint quirks. diff --git a/cube2enviro.c b/cube2enviro.c new file mode 100644 index 0000000..f00ebd8 --- /dev/null +++ b/cube2enviro.c @@ -0,0 +1,220 @@ +#include +#include +#include +#include + +const char *vsrc = +"#version 430\n" +"layout(location=0) in vec3 vPosition;\n" +"layout(location=1) in vec2 vCoord;\n" +"out vec2 fCoord;\n" +"void main()\n" +"{\n" +"\tgl_Position.xyz = vPosition;\n" +"\tgl_Position.w = 1.0;\n" +"\tfCoord = vCoord;\n" +"}\n"; +const char *fsrc = +"#version 430\n" +"in vec2 fCoord;\n" +"layout(location=0) out vec4 FragColor;\n" +"layout(binding=0) uniform samplerCube Texture;\n" +"void main()\n" +"{\n" +"\tvec2 p = fCoord-0.5;\n" +"\tfloat pitch = length(p)*3.14159265;\n" +"\tfloat roll = atan(p.y,p.x);\n" +"\tvec3 ccoord = vec3(sin(pitch)*cos(roll),-cos(pitch),sin(pitch)*sin(roll));\n" +"\tvec4 res = texture(Texture,-ccoord);\n" +"\tFragColor = res;\n" +"}\n"; + +GLint prog; +GLuint vao, vbuf, tex; + +int tex_load( const char *basename ) +{ + const char f[6] = {'b','f','l','r','d','t'}; + const int glf[6] = + { + GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, + GL_TEXTURE_CUBE_MAP_POSITIVE_Z, + GL_TEXTURE_CUBE_MAP_NEGATIVE_X, + GL_TEXTURE_CUBE_MAP_POSITIVE_X, + GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, + GL_TEXTURE_CUBE_MAP_POSITIVE_Y, + }; + char filename[256]; + glGenTextures(1,&tex); + glBindTexture(GL_TEXTURE_CUBE_MAP,tex); + glTexParameteri(GL_TEXTURE_CUBE_MAP,GL_TEXTURE_MIN_FILTER,GL_LINEAR); + glTexParameteri(GL_TEXTURE_CUBE_MAP,GL_TEXTURE_MAG_FILTER,GL_LINEAR); + glTexParameteri(GL_TEXTURE_CUBE_MAP,GL_TEXTURE_WRAP_S,GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_CUBE_MAP,GL_TEXTURE_WRAP_T,GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_CUBE_MAP,GL_TEXTURE_WRAP_R,GL_CLAMP_TO_EDGE); + for ( int i=0; i<6; i++ ) + { + snprintf(filename,256,"%s_%c.png",basename,f[i]); + SDL_Surface *tx = IMG_Load(filename); + if ( !tx ) + { + fprintf(stderr,"could not load %s: %s\n",filename,SDL_GetError()); + return -1; + } + SDL_Surface *txconv = SDL_ConvertSurfaceFormat(tx,SDL_PIXELFORMAT_RGBA32,0); + glTexImage2D(glf[i],0,GL_RGBA,txconv->w,txconv->h,0,GL_RGBA,GL_UNSIGNED_BYTE,txconv->pixels); + SDL_FreeSurface(txconv); + SDL_FreeSurface(tx); + } + return 0; +} + +void init_render() +{ + glClearColor(0.5f,0.5f,0.5f,1.f); + glClearDepth(1.f); + glUseProgram(prog); + glBindBuffer(GL_ARRAY_BUFFER,vbuf); + glEnableVertexAttribArray(0); + glEnableVertexAttribArray(1); + glBindTexture(GL_TEXTURE_CUBE_MAP,tex); + glActiveTexture(GL_TEXTURE0); +} + +void draw_frame() +{ + glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); + glVertexAttribPointer(0,3,GL_FLOAT,GL_FALSE,sizeof(float)*5, + (char*)(0)); + glVertexAttribPointer(1,2,GL_FLOAT,GL_FALSE,sizeof(float)*5, + (char*)(sizeof(float)*3)); + glDrawArrays(GL_TRIANGLE_STRIP,0,4); +} + +void scr_load() +{ + glGenVertexArrays(1,&vao); + glBindVertexArray(vao); + glGenBuffers(1,&vbuf); + glBindBuffer(GL_ARRAY_BUFFER,vbuf); + float vboe[20] = + { + -1, -1, 0, 0., 0., + -1, 1, 0, 0., 1., + 1, -1, 0, 1., 0., + 1, 1, 0, 1., 1., + }; + glBufferData(GL_ARRAY_BUFFER,sizeof(float)*20,&vboe[0], + GL_STATIC_DRAW); +} + +GLint compile_shader( GLenum type, const char *src ) +{ + GLint hnd, len, suc; + char *log; + hnd = glCreateShader(type); + len = strlen(src); + glShaderSource(hnd,1,&src,&len); + glCompileShader(hnd); + glGetShaderiv(hnd,GL_COMPILE_STATUS,&suc); + if ( !suc ) + { + glGetShaderiv(hnd,GL_INFO_LOG_LENGTH,&len); + log = malloc(len); + glGetShaderInfoLog(hnd,len,&suc,log); + fprintf(stderr,"Shader compile error:\n%s\n",log); + free(log); + return -1; + } + return hnd; +} + +GLint link_shader( GLint geom, GLint vert, GLint frag ) +{ + GLint hnd, suc, len; + char *log; + hnd = glCreateProgram(); + if ( geom != -1 ) glAttachShader(hnd,geom); + if ( vert != -1 ) glAttachShader(hnd,vert); + if ( frag != -1 ) glAttachShader(hnd,frag); + glLinkProgram(hnd); + glGetProgramiv(hnd,GL_LINK_STATUS,&suc); + if ( !suc ) + { + glGetShaderiv(hnd,GL_INFO_LOG_LENGTH,&len); + log = malloc(len); + glGetShaderInfoLog(hnd,len,&suc,log); + fprintf(stderr,"Shader link error:\n%s\n",log); + free(log); + return -1; + } + return hnd; +} + +int prog_load() +{ + GLint vert, frag; + if ( (frag=compile_shader(GL_FRAGMENT_SHADER,fsrc)) == -1 ) return -1; + if ( (vert=compile_shader(GL_VERTEX_SHADER,vsrc)) == -1 ) return -1; + if ( (prog=link_shader(-1,vert,frag)) == -1 ) return -1; + glDeleteShader(frag); + glDeleteShader(vert); + return 0; +} + +int main( int argc, char **argv ) +{ + int retcode = 0; + if ( argc < 2 ) + { + fprintf(stderr,"usage: cube2enviro [size]\n"); + return 0; + } + int sz = 256; + if ( argc > 2 ) sscanf(argv[2],"%u",&sz); + SDL_Init(SDL_INIT_VIDEO|SDL_INIT_EVENTS); + IMG_Init(IMG_INIT_PNG); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION,4); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION,3); + SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES,4); + SDL_Window *win = SDL_CreateWindow("cube2enviro", + SDL_WINDOWPOS_UNDEFINED,SDL_WINDOWPOS_UNDEFINED,sz,sz, + SDL_WINDOW_OPENGL|SDL_WINDOW_SHOWN); + SDL_GLContext *ctx = SDL_GL_CreateContext(win); + SDL_GL_SetSwapInterval(1); + if ( tex_load(argv[1]) == -1 ) + { + retcode = 1; + goto bail; + } + if ( prog_load() == -1 ) + { + retcode = 2; + goto bail; + } + scr_load(); + init_render(); + SDL_Event e; + int active = 1; + float frame = 0.f; + long tick, tock; + while ( active ) + { + while ( SDL_PollEvent(&e) ) + { + if ( (e.type == SDL_QUIT) || (((e.type == SDL_KEYDOWN) + || (e.type == SDL_KEYUP)) + && (e.key.keysym.sym == SDLK_ESCAPE)) ) + active = 0; + } + draw_frame(); + SDL_GL_SwapWindow(win); + } +bail: + SDL_GL_DeleteContext(ctx); + SDL_DestroyWindow(win); + IMG_Quit(); + SDL_Quit(); + return retcode; +} + diff --git a/ddsinfo.c b/ddsinfo.c index 5866d5a..f0d6b84 100644 --- a/ddsinfo.c +++ b/ddsinfo.c @@ -266,6 +266,10 @@ int main( int argc, char **argv ) if ( head.caps[1]&DDSCAPS2_CUBEMAP_POSITIVEZ ) printf(" POSITIVEZ"); if ( head.caps[1]&DDSCAPS2_CUBEMAP_NEGATIVEZ ) printf(" NEGATIVEZ"); if ( head.caps[1]&DDSCAPS2_VOLUME ) printf(" VOLUME"); + if ( !strncmp(head.pf_fourcc,"DX10",4) ) + { + printf("\ndxgi format: %d",head10.dxgiformat); + } printf("\n"); return 0; } diff --git a/dood.c b/dood.c index d581a99..7608d54 100644 --- a/dood.c +++ b/dood.c @@ -1,30 +1,56 @@ #include +#include +#include -unsigned short endoom[80*25]; - -int val( unsigned short ch ) +int nspace( unsigned short ch ) { return ((ch>0x2F)&&(ch<0x3A))||((ch>0x40)&&(ch<0x5B)) ||((ch>0x60)&&(ch<0x7B)); } -int main( void ) +int main( int argc, char **argv ) { - fread(endoom,sizeof(short),80*25,stdin); + if ( argc < 3 ) + { + fprintf(stderr,"usage: dood \n"); + return 0; + } + unsigned short endoom[2000]; + FILE *fin = fopen(argv[1],"rb"); + if ( !fin ) + { + fprintf(stderr,"failed to open input: %s.\n",strerror(errno)); + return 1; + } + int nch = 0; + if ( (nch = fread(endoom,sizeof(short),2000,fin)) < 2000 ) + { + fprintf(stderr,"short read, expected 2000 chars got %d.\n",nch); + fclose(fin); + return 2; + } + fclose(fin); int c = 0; do { - if ( val(endoom[c]&0xFF) ) + if ( nspace(endoom[c]&0xFF) ) { int s = c; - while ( (c++ < 80*25 ) && val(endoom[c]&0xFF) ); + while ( (c++ < 2000 ) && nspace(endoom[c]&0xFF) ); int e = c; for ( int i=0; i<(e-s)/2; i++ ) endoom[e-i-1] = endoom[s+i]; } else c++; } - while ( c < 80*25 ); - fwrite(endoom,sizeof(short),80*25,stdout); + while ( c < 2000 ); + FILE *fout = fopen(argv[2],"wb"); + if ( !fout ) + { + fprintf(stderr,"failed to open output: %s.\n",strerror(errno)); + return 4; + } + fwrite(endoom,sizeof(short),2000,fout); + close(fout); return 0; } diff --git a/mazestuff.c b/mazestuff.c new file mode 100644 index 0000000..a491713 --- /dev/null +++ b/mazestuff.c @@ -0,0 +1,522 @@ +/* + A C implementation of some dungeon generator, or at least an attempt. + Based mainly on http://journal.stuffwithstuff.com/2014/12/21/rooms-and-mazes + (C)2019 Marisa Kirisame, UnSX Team. + Released under the LGPLv3. +*/ +#include +#include +#include +#include +#include + +// map dimensions +// should be odd, otherwise you'll get some "padding" on the bottom and right +#define MAP_WIDTH 32 +#define MAP_HEIGHT 32 +// dimensions of the display window +#define WIN_WIDTH (MAP_WIDTH*16) +#define WIN_HEIGHT (MAP_HEIGHT*16) +// size of each cell in pixels when rendered +#define CW (WIN_WIDTH/MAP_WIDTH) +#define CH (WIN_HEIGHT/MAP_HEIGHT) + +// delay after each "draw map" operation +// helps visualize the generation better +#define GENDELAY 10 + +// room generator parameters +// sizes must be odd +// more tries means more dense room placement +// I wonder what the probability of there being only one room could be +#define ROOM_MIN 3 +#define ROOM_MAX 7 +#define ROOM_TRIES 100 + +// tile types +#define TILE_WALL 0 +#define TILE_AIR 1 +#define TILE_DOOR 2 + +// probability of mazes being more squiggly +#define WINDING_PCT 0.35 +// probability of additional doors into a region appearing +#define EXTDOOR_PCT 0.15 +// probability of doors connecting back to the same region +#define SELFDOOR_PCT 0.25 + +// the map itself +uint8_t map[MAP_WIDTH][MAP_HEIGHT] = {{TILE_WALL}}; +// what region each cell is part of +// 0 = no region +int region[MAP_WIDTH][MAP_HEIGHT] = {{0}}; +// secondary region, for grouping rooms +int sregion[MAP_WIDTH][MAP_HEIGHT] = {{0}}; + +SDL_Surface *ws; +SDL_Window *w; + +int active = 1; + +// a 2d vector +typedef struct +{ + int x, y; +} vec2_t; + +// a room +typedef struct +{ + int x1, y1, x2, y2; +} room_t; + +// a connection between two regions +typedef struct +{ + int x, y, r[2]; +} conn_t; + +// do these rooms touch each other? +int room_intersect( room_t* a, room_t* b ) +{ + return ((a->x1 <= b->x2) && (a->x2 >= b->x1) + && (a->y1 <= b->y2) && (a->y2 >= b->y1)); +} + +vec2_t dirs[8] = +{ + { 0,-1}, // north + { 0, 1}, // south + {-1, 0}, // west + { 1, 0}, // east + {-1,-1}, // northwest + { 1,-1}, // northeast + {-1, 1}, // southwest + { 1, 1}, // southeast +}; + +int current_region = 0; +int current_sregion = 0; + +// RNG stuff +double FRandom( double a, double b ) +{ + return a + (rand()/(RAND_MAX/(b-a))); +} +int Random( int a, int b ) +{ + return FRandom(a,b); +} + +// draw the map surface onto the window surface +// black for walls, white for air +void drawmap( void ) +{ + uint32_t *px = ws->pixels; + uint32_t pal[3] = + { + SDL_MapRGB(ws->format,16,16,16), + SDL_MapRGB(ws->format,192,192,192), + SDL_MapRGB(ws->format,16,255,16), + }; + for ( int y=0; yy1; jy2; j++ ) for ( int i=r->x1; ix2; i++ ) + carve(i,j,TILE_AIR); + // add other stuff here if you want +} + +// place rooms +void add_rooms() +{ + int rw, rh, rx, ry; + room_t *rooms = malloc(0); + int nrooms = 0; + for ( int i=0; i= MAP_WIDTH) || (y < 0) || (y >= MAP_HEIGHT) ) + return 0; + x = sx+dirs[dir].x*2; + y = sy+dirs[dir].y*2; + return (map[x][y] == TILE_WALL); +} + +// make a maze +void grow_maze( int sx, int sy ) +{ + int lastdir = -1; + current_region++; + carve(sx,sy,TILE_AIR); + vec2_t *cells = malloc(sizeof(vec2_t)); + int ncells = 1; + cells[0].x = sx; + cells[0].y = sy; + while ( ncells > 0 ) + { + vec2_t *ccell = &cells[ncells-1]; + int carvedirs[4] = {0}; + int cancarve = 0; + for ( int i=0; i<4; i++ ) + { + if ( can_carve(ccell->x,ccell->y,i) ) + { + carvedirs[i] = 1; + cancarve = 1; + } + } + if ( cancarve ) + { + int dir; + if ( (lastdir != -1) && carvedirs[lastdir] + && (FRandom(0,1) > WINDING_PCT) ) + dir = lastdir; + else + { + // cheap-arse random picking + int wdir[4]; + int ndirs = 0; + int j = 0; + for ( int i=0; i<4; i++ ) + { + if ( !carvedirs[i] ) continue; + wdir[j++] = i; + ndirs++; + } + // shuffle + for ( int i=0; ix+dirs[dir].x; + int y = ccell->y+dirs[dir].y; + carve(x,y,TILE_AIR); + x += dirs[dir].x; + y += dirs[dir].y; + carve(x,y,TILE_AIR); + ncells++; + cells = realloc(cells,sizeof(vec2_t)*ncells); + cells[ncells-1].x = x; + cells[ncells-1].y = y; + lastdir = dir; + } + else + { + ncells--; + cells = realloc(cells,sizeof(vec2_t)*ncells); + lastdir = -1; + } + } + free(cells); +} + +// this generates doors +// it's kind of convoluted +// but not as much as the original implementation lol +void connect_regions() +{ + int creg[MAP_WIDTH][MAP_HEIGHT][5] = {{{0}}}; + for ( int j=1; j SELFDOOR_PCT) ) + continue; + nconn++; + conn = realloc(conn,nconn*sizeof(conn_t)); + memcpy(conn+nconn-1,&cconn,sizeof(conn_t)); + } + // iterate through all regions and place doors + // make sure there is at least ONE door between two regions + // but allow a random chance for more + for ( int i=1; i EXTDOOR_PCT ) continue; + // open an extra door + carve(conn[connl[j]].x,conn[connl[j]].y,TILE_DOOR); + } + free(connl); + } + // clean up any overlapping doors + // must be done on a second pass + for ( int i=0; i 0 ); +} + +// get rid of groups of rooms that are "isolated" from the larger part of the +// dungeon +int spread_sregion( int x, int y ) +{ + // non-recursive flood fill using the same queue system as the maze + // generator + int ssz = 1; + int ncells = 1; + vec2_t *cells = malloc(sizeof(vec2_t)); + sregion[x][y] = current_sregion; + cells[0].x = x; + cells[0].y = y; + while ( ncells > 0 ) + { + x = cells[ncells-1].x; + y = cells[ncells-1].y; + int dir = -1; + for ( int i=0; i<4; i++ ) + { + if ( (map[x+dirs[i].x][y+dirs[i].y] == TILE_WALL) + || sregion[x+dirs[i].x][y+dirs[i].y] ) + continue; + dir = i; + break; + } + if ( dir != -1 ) + { + ncells++; + cells = realloc(cells,sizeof(vec2_t)*ncells); + sregion[x+dirs[dir].x][y+dirs[dir].y] = current_sregion; + cells[ncells-1].x = x+dirs[dir].x; + cells[ncells-1].y = y+dirs[dir].y; + ssz++; + } + else + { + ncells--; + cells = realloc(cells,sizeof(vec2_t)*ncells); + } + } + free(cells); + return ssz; +} + +void clean_isolated() +{ + int *ssizes = malloc(0); + for ( int j=1; j ssizes[largest] ) largest = i; + for ( int j=1; j 1 ) sscanf(argv[1],"%u",&seed); + else seed = time(0); + srand(seed); + printf("seed: %u\n",seed); + SDL_Init(SDL_INIT_VIDEO|SDL_INIT_TIMER|SDL_INIT_EVENTS); + w = SDL_CreateWindow("Dungeon",SDL_WINDOWPOS_UNDEFINED, + SDL_WINDOWPOS_UNDEFINED,WIN_WIDTH,WIN_HEIGHT, + SDL_WINDOW_SHOWN); + ws = SDL_GetWindowSurface(w); + dungeon_make(); + while ( active ) drawmap(); + return 0; +} diff --git a/pcxpalex.c b/pcxpalex.c new file mode 100644 index 0000000..34ef9f7 --- /dev/null +++ b/pcxpalex.c @@ -0,0 +1,25 @@ +#include +#include + +int main( int argc, char **argv ) +{ + FILE *fin = fopen(argv[1],"rb"); + uint8_t head[4]; + fread(&head[0],4,1,fin); + if ( (head[0] != 0x0A) || (head[2] != 0x01) || (head[3] != 0x08) ) + { + fclose(fin); + return 1; + } + uint8_t pal[768]; + fseek(fin,-769,SEEK_END); + if ( fgetc(fin) != 0x0C ) + { + fclose(fin); + return 2; + } + fread(&pal[0],768,1,fin); + fclose(fin); + fwrite(&pal[0],768,1,stdout); + return 0; +} diff --git a/pframes.c b/pframes.c index d40b2cb..670eeeb 100644 --- a/pframes.c +++ b/pframes.c @@ -19,7 +19,7 @@ int main( int argc, char **argv ) printf("\tFrameIndex %.4s %c 0 %d\n",s,f,fi); fi+=sk; f++; - if ( f > 'Z' ) + if ( f > ']' ) { sp++; if ( sp == 2 ) diff --git a/u083extract.c b/u083extract.c new file mode 100644 index 0000000..54bdca3 --- /dev/null +++ b/u083extract.c @@ -0,0 +1,889 @@ +/* + extract text, meshes, textures, palettes and sounds from 0.82~0.84 + packages. + TODO: extract animseq and meshmap data for meshes +*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define NAMES_MARK "[REFERENCED NAMES]" +#define IMPORT_MARK "[IMPORTED RESOURCES]" +#define EXPORT_MARK "[EXPORTED RESOURCES]" +#define DATA_MARK "[EXPORTED RESOURCE DATA]" +#define HEADER_MARK "[EXPORTED RESOURCE HEADERS]" +#define TABLE_MARK "[EXPORTED RESOURCE TABLE]" +#define TRAILER_MARK "[SUMMARY]" +#define RES_FILE_TAG "Unreal Resource\x1A" + +#define RES_FILE_VERSION_083 (0x0009) +#define RES_FILE_VERSION_084 (0x000A) + +enum EResourceType +{ + RES_None = 0, // No resource. + RES_Buffer = 1, // A large binary object holding misc saveable data. + RES_Array = 3, // An array of resources. + RES_TextBuffer = 4, // A text buffer. + RES_Texture = 5, // Texture or compound texture. + RES_Font = 6, // Font for use in game. + RES_Palette = 7, // A palette. + RES_Script = 9, // Script. + RES_Class = 10, // An actor class. + RES_ActorList = 11, // An array of actors. + RES_Sound = 12, // Sound effect. + RES_Mesh = 14, // Animated mesh. + RES_Vectors = 16, // 32-bit floating point vector list. + RES_BspNodes = 17, // Bsp node list. + RES_BspSurfs = 18, // Bsp polygon list. + RES_LightMesh = 19, // Bsp polygon lighting mesh. + RES_Polys = 20, // Editor polygon list. + RES_Model = 21, // Model or level map. + RES_Level = 22, // A game level. + RES_Camera = 25, // A rendering camera on this machine. + RES_Player = 28, // A remote player logged into the local server. + RES_VertPool = 29, // A vertex pool corresponding to a Bsp and FPoints/FVectors table. + RES_Ambient = 30, // An ambient sound definition. + RES_TransBuffer = 31, // Transaction tracking buffer. + RES_MeshMap = 32, // MeshMap. + RES_Bounds = 33, // Bounding structure. + RES_Terrain = 34, // Terrain. + RES_Enum = 35, // Enumeration (array of FName's). +}; + +const char typenames[36][32] = +{ + "None", "Buffer", "Type2", "Array", "TextBuffer", "Texture", "Font", + "Palette", "Type8", "Script", "Class", "ActorList", "Sound", "Type13", + "Mesh", "Type15", "Vectors", "BspNodes", "BspSurfs", "LightMesh", + "Polys", "Model", "Level", "Type23", "Type24", "Camera", "Type26", + "Type27", "Player", "VertPool", "Ambient", "TransBuffer", "MeshMap", + "Bounds", "Terrain", "Enum" +}; + +typedef struct +{ + uint32_t version, nexports, nimports, nnames, onames, oimports, + oexports, oheaders, lheaders; + uint8_t pad[16]; + char deltafname[80]; + char tag[32]; +} resourcefiletrailer_t; + +typedef struct +{ + char name[32]; + uint32_t flags; +} nameentry_t; + +typedef struct +{ + char name[32]; + uint32_t version, type, size; +} resnamefileentry_t; + +typedef struct +{ + uint32_t lead; // unused leading data + uint32_t type; // should be equal to the export's type + char name[32]; // descriptive name of class + uint32_t dataptr; // empty, set at runtime by engine + uint32_t index, version, filenum, flags, dataofs, datasize, crc, unused; +} uresource_t; + +typedef struct +{ + uresource_t res; + int32_t num, max; +} udatabase_t; + +typedef struct +{ + udatabase_t db; +} ubuffer_t; + +typedef struct +{ + udatabase_t db; +} uarray_t; + +typedef struct +{ + udatabase_t db; + int32_t pos; +} utextbuffer_t; + +typedef struct +{ + uint8_t r, g, b, x; +} color_t; + +typedef struct +{ + uresource_t res; + uint32_t class, palette, microtexture, fireparams; + uint16_t familyname, unusedname; + float paldiffusionc, diffusec, specularc, frictionc; + uint32_t footstepsound, hitsound, polyflags, notile, lockcount, + cameracaps, pad3, pad4, pad5, pad6, pad7, pad8; + int32_t usize, vsize, datasize, colorbytes; + color_t mipzero; + uint32_t mipofs[8]; + uint8_t ubits, vbits, unused1, unused2, pad[32]; +} utexture083_t; + +typedef struct +{ + uresource_t res; + uint32_t class, palette, microtexture, fireparams; + uint16_t familyname, unusedname; + uint32_t pad1; + float diffusec, specularc, frictionc; + uint32_t footstepsound, hitsound, polyflags, untiled, lockcount, + cameracaps, pad3, pad4, pad5, pad6, pad7, pad8; + int32_t usize, vsize, datasize, colorbytes; + color_t mipzero; + uint32_t mipofs[8]; + uint8_t ubits, vbits, pad9, pad10, pad[32]; +} utexture084_t; + +typedef struct +{ + int32_t startu, startv, usize, vsize; +} fontcharacter_t; + +typedef struct +{ + udatabase_t db; + uint32_t texture; +} ufont_t; + +typedef struct +{ + udatabase_t db; +} upalette_t; + +typedef struct +{ + uresource_t res; + uint32_t class; + int32_t length, numstacktree, codeoffset, pad[5]; +} uscript_t; + +/*typedef struct +{ + udatabase_t db; + uint32_t parentclass, scripttext, script, intrinsic, actorfunc, + scripttextcrc, parenttextcrc, pad1, pad2, pad3; + actor_t defaultactor; // no way I'm defining this fucking thing + int32_t length, numstacktree, codeoffset, pad[5]; +} uclass_t;*/ + +typedef struct +{ + udatabase_t db; + uint32_t locktype, trans, staticactors, dynamicactors, collidingactors, + activeactors, unusedactors, justdeletedactors; +} uactorlist_t; + +typedef struct +{ + uresource_t res; + int32_t datasize; + int16_t familyname; + int32_t soundid; + uint8_t pad[256]; +} usound_t; + +typedef struct +{ + float x, y, z; + uint32_t flags; +} vector_t; + +typedef struct +{ + int32_t pitch, yaw, roll; +} rotator_t; + +typedef struct +{ + vector_t min, max; +} boundingrect_t; + +typedef struct +{ + boundingrect_t rect; + vector_t sphere; +} boundingvolume_t; + +typedef struct +{ + uint16_t numpolys; + uint16_t numverts; + /* everything below is unused */ + uint16_t bogusrot; + uint16_t bogusframe; + uint32_t bogusnorm[3]; + uint32_t fixscale; + uint32_t unused[3]; + uint8_t padding[12]; +} __attribute__((packed)) dataheader_t; +typedef struct +{ + uint16_t vertices[3]; + uint8_t type; + uint8_t color; /* unused */ + uint8_t uv[3][2]; + uint8_t texnum; + uint8_t flags; /* unused */ +} __attribute__((packed)) datapoly_t; +typedef struct +{ + uint16_t numframes; + uint16_t framesize; +} __attribute__((packed)) aniheader_t; + +typedef struct +{ + int16_t seqname; + int32_t startframe, numframes, rate; +} animseq_t; + +typedef struct +{ + int32_t numverttris, trilistoffset; +} vertlink_t; + +typedef struct +{ + uresource_t res; + vector_t origin; + rotator_t rotorigin; + boundingvolume_t bound; + int32_t ntris, mtris, nverts, mverts, nvertlinks, mvertlinks, + nanimframes, manimframes, nanimseqs, manimseqs; +} umesh083_t; + +typedef struct +{ + uresource_t res; + vector_t origin; + rotator_t rotorigin; + boundingrect_t bound; + int32_t ntris, mtris, nverts, mverts, nvertlinks, mvertlinks, + nanimframes, manimframes, nanimseqs, manimseqs; +} umesh084_t; + +typedef struct +{ + udatabase_t db; +} uvectors_t; + +typedef struct +{ + vector_t plane; + uint64_t zonemask; + int32_t nodeflags, ivertpool, isurf, iback, ifront, iplane, ibound; + uint8_t izone[2], numvertices, pad1; + int32_t idynamic[2]; +} bspnode_t; + +typedef struct +{ + int32_t izoneactor, unused1; + uint32_t unused2; + uint64_t connectivity, visibility, unused3; +} zoneproperties083_t; + +typedef struct +{ + int32_t izoneactor, unused; + uint64_t connectivity, visibility; +} zoneproperties084_t; + +typedef struct +{ + udatabase_t db; + uint32_t numzones, numuniqueplanes, unused1, unused2; + zoneproperties083_t zones[64]; +} ubspnodes083_t; + +typedef struct +{ + udatabase_t db; + uint32_t numzones, numuniqueplanes, unused1, unused2; + zoneproperties084_t zones[64]; +} ubspnodes084_t; + +typedef struct +{ + uint32_t texture, brush, polyflags; + int32_t pbase, vnormal, vtextureu, vtexturev, ilightmesh, ibrushpoly; + uint8_t panu, panuv; + int32_t iactor; + int16_t laststarty, lastendy; +} bspsurf_t; + +typedef struct +{ + udatabase_t db; +} ubspsurfs_t; + +typedef struct +{ + int32_t dataoffset, textureustart, texturevstart, meshusize, meshvsize, + meshspacing, numstaticlights, numdynamiclights, ilightactor[16]; + uint8_t meshshift, meshubits, meshvbits, unused; +} lightmeshindex_t; + +typedef struct +{ + uresource_t res; + int32_t numindices, numdatabytes; +} ulightmesh_t; + +typedef struct +{ + vector_t base, normal, textureu, texturev, vertex[16]; + uint32_t polyflags, brush, texture; + int16_t groupname, itemname; + int32_t numvertices, ilink, ibrushpoly; + uint8_t panu, panv, izone[2]; +} poly_t; + +typedef struct +{ + udatabase_t db; +} upolys_t; + +typedef struct +{ + vector_t scale; + float sheerrate; + int32_t sheeraxis; +} scale_t; + +typedef struct +{ + uresource_t res; + uint32_t vectors, points, bspnodes, bspsurfs, vertpool, polys, + lightmesh, bounds, terrain; + vector_t location; + rotator_t rotation; + vector_t prepivot; + scale_t scaling; + uint32_t csgoper, locktype, color, polyflags, modelflags; + vector_t postpivot; + scale_t postscale, tempscale; + boundingvolume_t bound[2]; + uint8_t pad[32]; +} umodel_t; + +typedef struct +{ + uresource_t res; + uint32_t levelstate; + uint32_t beginplay, model, actorlist, playerlist, brusharray, misc, + textblocks[16]; +} ulevel_t; + +/*typedef struct +{ +} ucamera_t; + +typedef struct +{ +} uplayer_t;*/ + +typedef struct +{ + int32_t pvertex, iside; +} vertpool_t; + +typedef struct +{ + udatabase_t db; + int32_t numsharedsides; +} uvertpool_t; + +/*typedef struct +{ +} uambient_t; + +typedef struct +{ +} utransbuffer_t;*/ + +typedef struct +{ + uresource_t res; + uint32_t mesh, maxtextures, andflags, orflags; + vector_t scale; +} umeshmap_t; + +typedef struct +{ + udatabase_t db; +} ubounds_t; + +typedef struct +{ + int32_t gridx0, gridx1, gridy0, gridy1; +} terrainindex_t; + +typedef struct +{ + udatabase_t db; + uint32_t layerflags[8], heightmaps[8], tilemaps[8], tilerefs[128]; +} uterrain_t; + +typedef struct +{ + udatabase_t db; +} uenum_t; + +typedef struct +{ + uint32_t hdrsize, recsize; // only the essentials needed +} resourcetype_t; + +// global stuff +FILE *f; +resourcefiletrailer_t tail; +nameentry_t *names; +resnamefileentry_t *imports; +resnamefileentry_t *exports; +char *headers; +char **eheaders; + +// initialized at launch +resourcetype_t types[36] = {{0}}; + +void initialize_types( void ) +{ + // RES_None + types[RES_Buffer].hdrsize = sizeof(ubuffer_t); + types[RES_Buffer].recsize = sizeof(char); + types[RES_Array].hdrsize = sizeof(uarray_t); + types[RES_Array].recsize = sizeof(uint32_t); + types[RES_TextBuffer].hdrsize = sizeof(utextbuffer_t); + types[RES_TextBuffer].recsize = sizeof(char); + if ( tail.version == RES_FILE_VERSION_083 ) + types[RES_Texture].hdrsize = sizeof(utexture083_t); + else types[RES_Texture].hdrsize = sizeof(utexture084_t); + types[RES_Texture].recsize = 0; + types[RES_Font].hdrsize = sizeof(ufont_t); + types[RES_Font].recsize = sizeof(fontcharacter_t); + types[RES_Palette].hdrsize = sizeof(upalette_t); + types[RES_Palette].recsize = sizeof(color_t); + types[RES_Script].hdrsize = sizeof(uscript_t); + types[RES_Script].recsize = 1; + // guessed, can't be bothered to declare AActor + if ( tail.version == RES_FILE_VERSION_083 ) + types[RES_Class].hdrsize = 1168; + else types[RES_Class].hdrsize = 1272; + types[RES_Class].recsize = 0; + types[RES_ActorList].hdrsize = sizeof(uactorlist_t); + types[RES_ActorList].recsize = 0; // will have to guess + types[RES_Sound].hdrsize = sizeof(usound_t); + types[RES_Sound].recsize = 1; + if ( tail.version == RES_FILE_VERSION_083 ) + types[RES_Mesh].hdrsize = sizeof(umesh083_t); + else types[RES_Mesh].hdrsize = sizeof(umesh084_t); + types[RES_Mesh].recsize = 0; + types[RES_Vectors].hdrsize = sizeof(uvectors_t); + types[RES_Vectors].recsize = sizeof(vector_t); + if ( tail.version == RES_FILE_VERSION_083 ) + types[RES_BspNodes].hdrsize = sizeof(ubspnodes083_t); + else types[RES_BspNodes].hdrsize = sizeof(ubspnodes084_t); + types[RES_BspNodes].recsize = sizeof(bspnode_t); + types[RES_BspSurfs].hdrsize = sizeof(ubspsurfs_t); + types[RES_BspSurfs].recsize = sizeof(bspsurf_t); + types[RES_LightMesh].hdrsize = sizeof(ulightmesh_t); + types[RES_LightMesh].recsize = 0; + types[RES_Polys].hdrsize = sizeof(upolys_t); + types[RES_Polys].recsize = sizeof(poly_t); + types[RES_Model].hdrsize = sizeof(umodel_t); + types[RES_Model].recsize = 0; + types[RES_Level].hdrsize = sizeof(ulevel_t); + if ( tail.version == RES_FILE_VERSION_084 ) + types[RES_Level].hdrsize += 4; // dunno why + types[RES_Level].recsize = 0; + // RES_Camera + // RES_Player + types[RES_VertPool].hdrsize = sizeof(uvertpool_t); + types[RES_VertPool].recsize = sizeof(vertpool_t); + // RES_Ambient + // RES_TransBuffer + types[RES_MeshMap].hdrsize = sizeof(umeshmap_t); + types[RES_MeshMap].recsize = 0; + types[RES_Bounds].hdrsize = sizeof(ubounds_t); + types[RES_Bounds].recsize = sizeof(boundingrect_t); + types[RES_Terrain].hdrsize = sizeof(uterrain_t); + types[RES_Terrain].recsize = sizeof(terrainindex_t); + types[RES_Enum].hdrsize = sizeof(uenum_t); + types[RES_Enum].recsize = sizeof(uint32_t); +} + +void extract_txt( uint32_t i, utextbuffer_t *txt ) +{ + mkdir("Classes",0755); + fseek(f,txt->db.res.dataofs,SEEK_SET); + char *rbuf = malloc(txt->db.res.datasize); + fread(rbuf,1,txt->db.res.datasize-1,f); + char fname[256]; + snprintf(fname,256,"Classes/%.32s.tcx",exports[i].name); + FILE *out = fopen(fname,"wb"); + fwrite(rbuf,1,txt->db.res.datasize-1,out); + fclose(out); + free(rbuf); +} + +void extract_snd( uint32_t i, usound_t *snd ) +{ + mkdir("Sounds",0755); + fseek(f,snd->res.dataofs,SEEK_SET); + char sig[4]; + fread(sig,4,1,f); + if ( !strncmp(sig,"CSNX",4) ) fseek(f,248,SEEK_CUR); + else fseek(f,-4,SEEK_CUR); + char *rbuf = malloc(snd->res.datasize); + fread(rbuf,1,snd->res.datasize-1,f); + char fname[256]; + snprintf(fname,256,"Sounds/%.32s.wav",exports[i].name); + FILE *out = fopen(fname,"wb"); + fwrite(rbuf,1,snd->res.datasize-1,out); + fclose(out); + free(rbuf); +} + + +int writepng( const char *filename, unsigned char *fdata, int fw, int fh, + png_color *fpal, int fpalsiz ) +{ + if ( !filename ) return 0; + png_structp pngp; + png_infop infp; + FILE *pf; + if ( !(pf = fopen(filename,"wb")) ) return 0; + pngp = png_create_write_struct(PNG_LIBPNG_VER_STRING,0,0,0); + if ( !pngp ) + { + fclose(pf); + return 0; + } + infp = png_create_info_struct(pngp); + if ( !infp ) + { + fclose(pf); + png_destroy_write_struct(&pngp,0); + return 0; + } + if ( setjmp(png_jmpbuf(pngp)) ) + { + png_destroy_write_struct(&pngp,&infp); + fclose(pf); + return 0; + } + png_init_io(pngp,pf); + if ( fpal ) + { + png_set_IHDR(pngp,infp,fw,fh,8,PNG_COLOR_TYPE_PALETTE, + PNG_INTERLACE_NONE,PNG_COMPRESSION_TYPE_DEFAULT, + PNG_FILTER_TYPE_DEFAULT); + png_set_PLTE(pngp,infp,fpal,fpalsiz); + unsigned char t = 0; + png_set_tRNS(pngp,infp,&t,1,0); + png_write_info(pngp,infp); + for ( int i=0; idb.res.dataofs,SEEK_SET); + fread(cpal,sizeof(color_t),256,f); + for ( int i=0; i<256; i++ ) + { + fpal[i].red = cpal[i].r; + fpal[i].green = cpal[i].g; + fpal[i].blue = cpal[i].b; + } + for ( int y=0; y<128; y++ ) for ( int x=0; x<128; x++ ) + { + dat[x+y*128] = (x/8)+(y/8)*16; + } + char fname[256]; + snprintf(fname,256,"Palettes/%.32s.png",exports[i].name); + writepng(fname,dat,128,128,fpal,256); +} + +void extract_tex083( uint32_t i, utexture083_t *tex ) +{ + mkdir("Textures",0755); + uint8_t *pxdata = malloc(tex->res.datasize); + fseek(f,tex->res.dataofs+tex->mipofs[0],SEEK_SET); + fread(pxdata,1,tex->res.datasize,f); + png_color fpal[256]; + if ( tex->palette ) + { + upalette_t *pal = (upalette_t*)eheaders[tex->palette-1]; + color_t cpal[256]; + fseek(f,pal->db.res.dataofs,SEEK_SET); + fread(cpal,sizeof(color_t),256,f); + for ( int i=0; i<256; i++ ) + { + fpal[i].red = cpal[i].r; + fpal[i].green = cpal[i].g; + fpal[i].blue = cpal[i].b; + } + } + else + { + // fallback + for ( int i=0; i<256; i++ ) + { + fpal[i].red = i; + fpal[i].green = i; + fpal[i].blue = i; + } + } + char fname[256]; + snprintf(fname,256,"Textures/%.32s.png",exports[i].name); + writepng(fname,pxdata,tex->usize,tex->vsize,&fpal[0],256); + free(pxdata); +} + +void extract_tex084( uint32_t i, utexture084_t *tex ) +{ + mkdir("Textures",0755); + uint8_t *pxdata = malloc(tex->res.datasize); + fseek(f,tex->res.dataofs+tex->mipofs[0],SEEK_SET); + fread(pxdata,1,tex->res.datasize,f); + png_color fpal[256]; + if ( tex->palette ) + { + upalette_t *pal = (upalette_t*)eheaders[tex->palette-1]; + color_t cpal[256]; + fseek(f,pal->db.res.dataofs,SEEK_SET); + fread(cpal,sizeof(color_t),256,f); + for ( int i=0; i<256; i++ ) + { + fpal[i].red = cpal[i].r; + fpal[i].green = cpal[i].g; + fpal[i].blue = cpal[i].b; + } + } + else + { + // fallback + for ( int i=0; i<256; i++ ) + { + fpal[i].red = i; + fpal[i].green = i; + fpal[i].blue = i; + } + } + char fname[256]; + snprintf(fname,256,"Textures/%.32s.png",exports[i].name); + writepng(fname,pxdata,tex->usize,tex->vsize,&fpal[0],256); + free(pxdata); +} + +void extract_msh083( uint32_t i, umesh083_t *msh ) +{ + mkdir("Models",0755); + char fname[256]; + FILE *out; + // write datafile + snprintf(fname,256,"Models/%.32s_d.3d",exports[i].name); + out = fopen(fname,"wb"); + dataheader_t dhead = + { + .numpolys = msh->mtris, + .numverts = msh->mverts + }; + fwrite(&dhead,sizeof(dataheader_t),1,out); + fseek(f,msh->res.dataofs,SEEK_SET); + datapoly_t *dpoly = calloc(sizeof(datapoly_t),dhead.numpolys); + fread(dpoly,sizeof(datapoly_t),dhead.numpolys,f); + fwrite(dpoly,sizeof(datapoly_t),dhead.numpolys,out); + free(dpoly); + fclose(out); + // write anivfile + snprintf(fname,256,"Models/%.32s_a.3d",exports[i].name); + out = fopen(fname,"wb"); + aniheader_t ahead = + { + .numframes = msh->manimframes, + .framesize = msh->mverts*4 + }; + fwrite(&ahead,sizeof(aniheader_t),1,out); + uint32_t *uverts = calloc(sizeof(uint32_t),dhead.numverts + *ahead.numframes); + fread(uverts,sizeof(uint32_t),dhead.numverts*ahead.numframes,f); + fwrite(uverts,sizeof(uint32_t),dhead.numverts*ahead.numframes,out); + free(uverts); + fclose(out); +} + +void extract_msh084( uint32_t i, umesh084_t *msh ) +{ + mkdir("Models",0755); + char fname[256]; + FILE *out; + // write datafile + snprintf(fname,256,"Models/%.32s_d.3d",exports[i].name); + out = fopen(fname,"wb"); + dataheader_t dhead = + { + .numpolys = msh->mtris, + .numverts = msh->mverts + }; + fwrite(&dhead,sizeof(dataheader_t),1,out); + fseek(f,msh->res.dataofs,SEEK_SET); + datapoly_t *dpoly = calloc(sizeof(datapoly_t),dhead.numpolys); + fread(dpoly,sizeof(datapoly_t),dhead.numpolys,f); + fwrite(dpoly,sizeof(datapoly_t),dhead.numpolys,out); + free(dpoly); + fclose(out); + // write anivfile + snprintf(fname,256,"Models/%.32s_a.3d",exports[i].name); + out = fopen(fname,"wb"); + aniheader_t ahead = + { + .numframes = msh->manimframes, + .framesize = msh->mverts*4 + }; + fwrite(&ahead,sizeof(aniheader_t),1,out); + uint32_t *uverts = calloc(sizeof(uint32_t),dhead.numverts + *ahead.numframes); + fread(uverts,sizeof(uint32_t),dhead.numverts*ahead.numframes,f); + fwrite(uverts,sizeof(uint32_t),dhead.numverts*ahead.numframes,out); + free(uverts); + fclose(out); +} + +int main( int argc, char **argv ) +{ + if ( argc < 2 ) + { + fprintf(stderr,"usage: u083extract \n"); + return 0; + } + f = fopen(argv[1],"rb"); + if ( !f ) + { + fprintf(stderr,"Cannot open %s: %s\n",argv[1],strerror(errno)); + return 1; + } + fseek(f,-sizeof(resourcefiletrailer_t),SEEK_END); + fread(&tail,sizeof(resourcefiletrailer_t),1,f); + if ( strncmp(tail.tag,RES_FILE_TAG,32) ) + { + fprintf(stderr,"Not a valid Unreal Resource file.\n"); + fclose(f); + return 1; + } + if ( (tail.version != RES_FILE_VERSION_083) + && (tail.version != RES_FILE_VERSION_084) ) + { + fprintf(stderr,"Unreal Resource version %04x not supported.\n", + tail.version); + fclose(f); + return 1; + } + printf("%s - %u names, %u imports, %u exports, %u bytes of headers\n", + argv[1],tail.nnames,tail.nimports,tail.nexports,tail.lheaders); + names = calloc(tail.nnames,sizeof(nameentry_t)); + imports = calloc(tail.nimports,sizeof(resnamefileentry_t)); + exports = calloc(tail.nexports,sizeof(resnamefileentry_t)); + headers = calloc(tail.lheaders,1); + fseek(f,tail.onames,SEEK_SET); + fread(names,sizeof(nameentry_t),tail.nnames,f); + fseek(f,tail.oimports,SEEK_SET); + fread(imports,sizeof(resnamefileentry_t),tail.nimports,f); + fseek(f,tail.oexports,SEEK_SET); + fread(exports,sizeof(resnamefileentry_t),tail.nexports,f); + fseek(f,tail.oheaders,SEEK_SET); + fread(headers,1,tail.lheaders,f); + initialize_types(); + eheaders = calloc(tail.nexports,sizeof(char*)); + char *ptr = headers, *prev = 0; + for ( int i=0; i +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define RES_FILE_TAG "Unrealfile\x1A" + +typedef struct +{ + char tag[32]; + uint32_t version, nobjects, nnames, onames, oobjects, ndeps; +} unrealheader_t; + +typedef struct +{ + char name[32]; + uint32_t flags; +} unrealname_t; + +typedef struct +{ + int16_t name, pkg, class; + uint32_t flags, headerofs, crc, dataofs, headersiz, datasiz; +} unrealobject_t; + +FILE *f; +unrealheader_t head; +unrealname_t *names; +unrealobject_t *objects; + +void readstr( char *dest, int max, FILE *f ) +{ + int cnt = 0; + while ( cnt < max ) + { + fread(&dest[cnt],1,1,f); + if ( !dest[cnt] ) return; + cnt++; + } +} + +void debug_printheader( uint32_t i ) +{ + uint8_t *header = malloc(objects[i].headersiz); + fseek(f,objects[i].headerofs,SEEK_SET); + fread(header,objects[i].headersiz,1,f); + int j = 0; + printf("%.32s\'%.32s\' header (%u):\n",names[objects[i].class-1].name, + names[objects[i].name-1].name,objects[i].headersiz); + for ( j=0; j= 27 ) fofs = 34; + else if ( head.version >= 25 ) fofs = 0; + fseek(f,objects[i].headerofs+fofs,SEEK_SET); + int16_t family; + fread(&family,2,1,f); + fseek(f,objects[i].dataofs,SEEK_SET); + char sig[4]; + fread(sig,4,1,f); + if ( !strncmp(sig,"CSNX",4) ) fseek(f,248,SEEK_CUR); + else fseek(f,-4,SEEK_CUR); + char *rbuf = malloc(objects[i].datasiz); + fread(rbuf,1,objects[i].datasiz-1,f); + char fname[256]; + if ( family > 0 ) snprintf(fname,256,"Sounds/%.32s.%.32s.wav", + names[family-1].name,names[objects[i].name-1].name); + else snprintf(fname,256,"Sounds/%.32s.wav", + names[objects[i].name-1].name); + FILE *out = fopen(fname,"wb"); + fwrite(rbuf,1,objects[i].datasiz-1,out); + fclose(out); + free(rbuf); +} + +int writepng( const char *filename, unsigned char *fdata, int fw, int fh, + png_color *fpal, int fpalsiz ) +{ + if ( !filename ) return 0; + png_structp pngp; + png_infop infp; + FILE *pf; + if ( !(pf = fopen(filename,"wb")) ) return 0; + pngp = png_create_write_struct(PNG_LIBPNG_VER_STRING,0,0,0); + if ( !pngp ) + { + fclose(pf); + return 0; + } + infp = png_create_info_struct(pngp); + if ( !infp ) + { + fclose(pf); + png_destroy_write_struct(&pngp,0); + return 0; + } + if ( setjmp(png_jmpbuf(pngp)) ) + { + png_destroy_write_struct(&pngp,&infp); + fclose(pf); + return 0; + } + png_init_io(pngp,pf); + if ( fpal ) + { + png_set_IHDR(pngp,infp,fw,fh,8,PNG_COLOR_TYPE_PALETTE, + PNG_INTERLACE_NONE,PNG_COMPRESSION_TYPE_DEFAULT, + PNG_FILTER_TYPE_DEFAULT); + png_set_PLTE(pngp,infp,fpal,fpalsiz); + unsigned char t = 0; + png_set_tRNS(pngp,infp,&t,1,0); + png_write_info(pngp,infp); + for ( int i=0; i= 27 ) oofs += 8; + else if ( head.version >= 25 ) oofs += 2; + int32_t pal; + fseek(f,objects[i].headerofs+oofs,SEEK_SET); + fread(&pal,4,1,f); + uint32_t w, h; + if ( head.version >= 27 ) oofs += 21; + else if ( head.version >= 25 ) oofs += 8; + fseek(f,objects[i].headerofs+oofs+38,SEEK_SET); + fread(&w,4,1,f); + fread(&h,4,1,f); + uint32_t mipofs; + fseek(f,objects[i].headerofs+oofs+66,SEEK_SET); + fread(&mipofs,4,1,f); + if ( mipofs == 0xffffffff ) mipofs = 0; // why + uint8_t *pxdata = malloc(w*h); + fseek(f,objects[i].dataofs+mipofs,SEEK_SET); + fread(pxdata,1,w*h,f); + png_color fpal[256]; + if ( pal ) + { + color_t cpal[256]; + fseek(f,objects[pal-1].dataofs,SEEK_SET); + fread(cpal,sizeof(color_t),256,f); + for ( int i=0; i<256; i++ ) + { + fpal[i].red = cpal[i].r; + fpal[i].green = cpal[i].g; + fpal[i].blue = cpal[i].b; + } + } + else + { + // fallback + for ( int i=0; i<256; i++ ) + { + fpal[i].red = i; + fpal[i].green = i; + fpal[i].blue = i; + } + } + char fname[256]; + snprintf(fname,256,"Textures/%.32s.png",names[objects[i].name-1].name); + writepng(fname,pxdata,w,h,&fpal[0],256); + free(pxdata); +} + +typedef struct +{ + uint16_t numpolys; + uint16_t numverts; + /* everything below is unused */ + uint16_t bogusrot; + uint16_t bogusframe; + uint32_t bogusnorm[3]; + uint32_t fixscale; + uint32_t unused[3]; + uint8_t padding[12]; +} __attribute__((packed)) dataheader_t; +typedef struct +{ + uint16_t vertices[3]; + uint8_t type; + uint8_t color; /* unused */ + uint8_t uv[3][2]; + uint8_t texnum; + uint8_t flags; /* unused */ +} __attribute__((packed)) datapoly_t; +typedef struct +{ + uint16_t numframes; + uint16_t framesize; +} __attribute__((packed)) aniheader_t; + +void extract_msh( uint32_t i ) +{ + mkdir("Models",0755); + int dofs = 16; + int oofs = 57; + if ( head.version >= 27 ) + { + oofs += 20; + dofs += 8; + } + else if ( head.version >= 25 ) + { + dofs += 2; + oofs += 2; + } + int32_t verts, tris, seqs; + uint32_t nverts, nframes, tverts, npolys; + fseek(f,objects[i].headerofs+oofs,SEEK_SET); + fread(&verts,4,1,f); + fread(&tris,4,1,f); + fread(&seqs,4,1,f); + fseek(f,objects[i].headerofs+oofs+32,SEEK_SET); + fread(&nverts,4,1,f); + fread(&nframes,4,1,f); + fseek(f,objects[verts-1].headerofs+dofs,SEEK_SET); + fread(&tverts,4,1,f); + fseek(f,objects[tris-1].headerofs+dofs,SEEK_SET); + fread(&npolys,4,1,f); + char fname[256]; + FILE *out; + // write datafile + snprintf(fname,256,"Models/%.32s_d.3d",names[objects[i].name-1].name); + out = fopen(fname,"wb"); + dataheader_t dhead = + { + .numpolys = npolys, + .numverts = nverts, + }; + fwrite(&dhead,sizeof(dataheader_t),1,out); + fseek(f,objects[tris-1].dataofs,SEEK_SET); + datapoly_t *dpoly = calloc(sizeof(datapoly_t),dhead.numpolys); + fread(dpoly,sizeof(datapoly_t),dhead.numpolys,f); + fwrite(dpoly,sizeof(datapoly_t),dhead.numpolys,out); + free(dpoly); + fclose(out); + // write anivfile + snprintf(fname,256,"Models/%.32s_a.3d",names[objects[i].name-1].name); + out = fopen(fname,"wb"); + aniheader_t ahead = + { + .numframes = nframes, + .framesize = nverts*4, + }; + fwrite(&ahead,sizeof(aniheader_t),1,out); + fseek(f,objects[verts-1].dataofs,SEEK_SET); + uint32_t *uverts = calloc(sizeof(uint32_t),dhead.numverts + *ahead.numframes); + fread(uverts,sizeof(uint32_t),dhead.numverts*ahead.numframes,f); + fwrite(uverts,sizeof(uint32_t),dhead.numverts*ahead.numframes,out); + free(uverts); + fclose(out); +} + +int main( int argc, char **argv ) +{ + if ( argc < 2 ) + { + fprintf(stderr,"usage: u086extract \n"); + return 0; + } + f = fopen(argv[1],"rb"); + if ( !f ) + { + fprintf(stderr,"Cannot open %s: %s\n",argv[1],strerror(errno)); + return 1; + } + fread(&head,sizeof(unrealheader_t),1,f); + if ( strncmp(head.tag,RES_FILE_TAG,32) ) + { + fprintf(stderr,"Not a valid Unreal Resource file.\n"); + fclose(f); + return 1; + } + printf("%s - version %u, %u names (&%x), %u objects (&%x)," + " %u dependencies\n",argv[1],head.version,head.nnames, + head.onames,head.nobjects,head.oobjects,head.ndeps); + names = calloc(head.nnames,sizeof(unrealname_t)); + fseek(f,head.onames,SEEK_SET); + for ( int i=0; i= 25 ) fread(&objects[i].pkg,2,1,f); + fread(&objects[i].class,2,1,f); + fread(&objects[i].flags,4,2,f); + if ( objects[i].headerofs != 0 ) fread(&objects[i].crc,4,4,f); + if ( !strcmp(names[objects[i].class-1].name,"Class") ) + fseek(f,14,SEEK_CUR); // haven't figured this out yet + } + /*for ( int i=0; i +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define NAMES_MARK "[REFERENCED NAMES]" +#define IMPORT_MARK "[IMPORTED RESOURCES]" +#define EXPORT_MARK "[EXPORTED RESOURCES]" +#define DATA_MARK "[EXPORTED RESOURCE DATA]" +#define HEADER_MARK "[EXPORTED RESOURCE HEADERS]" +#define TABLE_MARK "[EXPORTED RESOURCE TABLE]" +#define TRAILER_MARK "[SUMMARY]" +#define RES_FILE_TAG "Unreal Resource\0" + +enum EResourceType +{ + RES_None = 0, // No resource. + RES_Buffer = 1, // A large binary object holding misc saveable data. + RES_Array = 3, // An array of resources. + RES_TextBuffer = 4, // A text buffer. + RES_Texture = 5, // Texture or compound texture. + RES_Font = 6, // Font for use in game. + RES_Palette = 7, // A palette. + RES_Script = 9, // Script. + RES_Class = 10, // An actor class. + RES_ActorList = 11, // An array of actors. + RES_Sound = 12, // Sound effect. + RES_Mesh = 14, // Animated mesh. + RES_Vectors = 16, // 32-bit floating point vector list. + RES_BspNodes = 17, // Bsp node list. + RES_BspSurfs = 18, // Bsp polygon list. + RES_LightMesh = 19, // Bsp polygon lighting mesh. + RES_Polys = 20, // Editor polygon list. + RES_Model = 21, // Model or level map. + RES_Level = 22, // A game level. + RES_Camera = 25, // A rendering camera on this machine. + RES_Player = 28, // A remote player logged into the local server. + RES_VertPool = 29, // A vertex pool corresponding to a Bsp and FPoints/FVectors table. + RES_Ambient = 30, // An ambient sound definition. + RES_TransBuffer = 31, // Transaction tracking buffer. + RES_MeshMap = 32, // MeshMap. + RES_Bounds = 33, // Bounding structure. + RES_Terrain = 34, // Terrain. + RES_Enum = 35, // Enumeration (array of FName's). +}; + +typedef struct +{ + uint16_t version, nexports, nimports, nnames; + uint32_t onames, oimports, oheaders, oexports; + uint8_t pad[32]; + char tag[16]; +} __attribute__((packed)) resourcefiletrailer_t; + +typedef struct +{ + char name[16]; + uint16_t unused; +} __attribute__((packed)) nameentry_t; + +typedef struct +{ + char name[16]; + uint16_t index; + uint8_t type, unknown; + uint8_t pad1[6]; + uint16_t headersize; + uint8_t pad2[10]; + uint32_t dataofs, datasize; + uint8_t pad3[8]; +} __attribute__((packed)) resnamefileentry_t; + +typedef struct +{ + uint8_t r, g, b, x; +} __attribute__((packed)) color_t; + +// global stuff +FILE *f; +resourcefiletrailer_t tail; +nameentry_t *names; +resnamefileentry_t *imports; +resnamefileentry_t *exports; +size_t *eheaders; + +int writepng( const char *filename, unsigned char *fdata, int fw, int fh, + png_color *fpal, int fpalsiz ) +{ + if ( !filename ) return 0; + png_structp pngp; + png_infop infp; + FILE *pf; + if ( !(pf = fopen(filename,"wb")) ) return 0; + pngp = png_create_write_struct(PNG_LIBPNG_VER_STRING,0,0,0); + if ( !pngp ) + { + fclose(pf); + return 0; + } + infp = png_create_info_struct(pngp); + if ( !infp ) + { + fclose(pf); + png_destroy_write_struct(&pngp,0); + return 0; + } + if ( setjmp(png_jmpbuf(pngp)) ) + { + png_destroy_write_struct(&pngp,&infp); + fclose(pf); + return 0; + } + png_init_io(pngp,pf); + if ( fpal ) + { + png_set_IHDR(pngp,infp,fw,fh,8,PNG_COLOR_TYPE_PALETTE, + PNG_INTERLACE_NONE,PNG_COMPRESSION_TYPE_DEFAULT, + PNG_FILTER_TYPE_DEFAULT); + png_set_PLTE(pngp,infp,fpal,fpalsiz); + unsigned char t = 0; + png_set_tRNS(pngp,infp,&t,1,0); + png_write_info(pngp,infp); + for ( int i=0; i\n"); + return 0; + } + f = fopen(argv[1],"rb"); + if ( !f ) + { + fprintf(stderr,"Cannot open %s: %s\n",argv[1],strerror(errno)); + return 1; + } + fseek(f,-sizeof(resourcefiletrailer_t),SEEK_END); + fread(&tail,sizeof(resourcefiletrailer_t),1,f); + if ( strncmp(tail.tag,RES_FILE_TAG,16) ) + { + fprintf(stderr,"Not a valid Unreal Resource file.\n"); + fclose(f); + return 1; + } + if ( tail.version != 1 ) + { + fprintf(stderr,"Unreal Resource version %04x not supported.\n", + tail.version); + fclose(f); + return 1; + } + printf("%s - %u names, %u imports, %u exports\n",argv[1],tail.nnames, + tail.nimports,tail.nexports); + names = calloc(tail.nnames,sizeof(nameentry_t)); + imports = calloc(tail.nimports,sizeof(resnamefileentry_t)); + exports = calloc(tail.nexports,sizeof(resnamefileentry_t)); + eheaders = calloc(tail.nexports,sizeof(size_t)); + fseek(f,tail.onames,SEEK_SET); + fread(names,sizeof(nameentry_t),tail.nnames,f); + fseek(f,tail.oimports,SEEK_SET); + fread(imports,sizeof(resnamefileentry_t),tail.nimports,f); + fseek(f,tail.oexports,SEEK_SET); + fread(exports,sizeof(resnamefileentry_t),tail.nexports,f); + fseek(f,tail.oheaders,SEEK_SET); + for ( int i=0; ipkgver >= 60 ) fpos += 4; + if ( head->pkgver >= 55 ) fpos += 4; else readindex(); return readindex(); } @@ -170,7 +170,7 @@ void readimport2( int32_t *cpkg, int32_t *cname, int32_t *pkg, int32_t *name ) { *cpkg = readindex(); *cname = readindex(); - if ( head->pkgver >= 60 ) *pkg = readdword(); + if ( head->pkgver >= 55 ) *pkg = readdword(); else *pkg = readindex(); *name = readindex(); } @@ -190,7 +190,7 @@ void readexport( int32_t *class, int32_t *ofs, int32_t *siz, int32_t *name ) { *class = readindex(); readindex(); - if ( head->pkgver >= 60 ) fpos += 4; + if ( head->pkgver >= 55 ) fpos += 4; *name = readindex(); fpos += 4; *siz = readindex(); @@ -213,7 +213,7 @@ void readexport2( int32_t *class, int32_t *super, int32_t *pkg, int32_t *name, { *class = readindex(); *super = readindex(); - if ( head->pkgver >= 60 ) *pkg = readdword(); + if ( head->pkgver >= 55 ) *pkg = readdword(); else *pkg = readindex(); *name = readindex(); *flags = readdword(); @@ -281,7 +281,7 @@ typedef struct typedef struct { - uint8_t texcount; + uint32_t texcount; ufonttex_t *tex; } ufonthdr_t; @@ -296,12 +296,14 @@ void savefont( int32_t namelen, char *name ) } ufonthdr_t fhead; memset(&fhead,0,sizeof(ufonthdr_t)); - fhead.texcount = readbyte(); + fhead.texcount = readindex(); fhead.tex = calloc(fhead.texcount,sizeof(ufonttex_t)); + uint32_t tchars = 0; for ( int i=0; i 256 ) fprintf(f,"0x%04x: ",cc); + else fprintf(f,"0x%02x: ",cc); construct_fullname(f,fhead.tex[i].texture); fprintf(f," (%d,%d)-(%d,%d)\n",fhead.tex[i].chars[j].x, fhead.tex[i].chars[j].y,fhead.tex[i].chars[j].w, @@ -413,8 +414,10 @@ int main( int argc, char **argv ) // begin reading data size_t prev = fpos; fpos = ofs; - if ( head->pkgver < 40 ) fpos += 8; - if ( head->pkgver < 60 ) fpos += 16; + if ( head->pkgver < 45 ) fpos += 4; + if ( head->pkgver < 55 ) fpos += 16; + if ( head->pkgver <= 44 ) fpos -= 6; // ??? + if ( head->pkgver <= 35 ) fpos += 8; // ??? int32_t prop = readindex(); if ( (uint32_t)prop >= head->nnames ) { @@ -427,6 +430,8 @@ retry: if ( strncasecmp(pname,"none",l) ) { uint8_t info = readbyte(); + int array = info&0x80; + int type = info&0xf; int psiz = (info>>4)&0x7; switch ( psiz ) { @@ -455,6 +460,10 @@ retry: psiz = readdword(); break; } + if ( array && (type != 3) ) + readindex(); + if ( type == 10 ) + readindex(); // skip struct name fpos += psiz; printf(" Skipping property %.*s\n",l,pname); prop = readindex(); diff --git a/unrundeleter.c b/unrundeleter.c new file mode 100644 index 0000000..256f33f --- /dev/null +++ b/unrundeleter.c @@ -0,0 +1,361 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define UPKG_MAGIC 0x9E2A83C1 + +typedef struct +{ + uint32_t magic; + uint16_t pkgver, license; + uint32_t flags, nnames, onames, nexports, oexports, nimports, oimports; +} upkg_header_t; + +uint8_t *pkgfile; +upkg_header_t *head; +size_t fpos = 0; + +uint8_t readbyte( void ) +{ + uint8_t val = pkgfile[fpos]; + fpos++; + return val; +} + +uint16_t readword( void ) +{ + uint16_t val = *(uint16_t*)(pkgfile+fpos); + fpos += 2; + return val; +} + +uint32_t readdword( void ) +{ + uint32_t val = *(uint32_t*)(pkgfile+fpos); + fpos += 4; + return val; +} + +float readfloat( void ) +{ + float val = *(float*)(pkgfile+fpos); + fpos += 4; + return val; +} + +#define READSTRUCT(x,y) {memcpy(&x,pkgfile+fpos,sizeof(y));fpos+=sizeof(y);} + +// reads a compact index value +int32_t readindex( void ) +{ + uint8_t byte[5] = {0}; + byte[0] = readbyte(); + if ( !byte[0] ) return 0; + if ( byte[0]&0x40 ) + { + for ( int i=1; i<5; i++ ) + { + byte[i] = readbyte(); + 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; +} + +// reads a name table entry +size_t readname( int *olen ) +{ + size_t pos = fpos; + if ( head->pkgver >= 64 ) + { + int32_t len = readindex(); + pos = fpos; + if ( olen ) *olen = len; + if ( len <= 0 ) return pos; + fpos += len; + } + else + { + int c, p = 0; + while ( (c = readbyte()) ) p++; + if ( olen ) *olen = p; + } + fpos += 4; + return pos; +} + +size_t getname( int index, int *olen ) +{ + size_t prev = fpos; + fpos = head->onames; + size_t npos = 0; + for ( int i=0; i<=index; i++ ) + npos = readname(olen); + fpos = prev; + return npos; +} + +// checks if a name exists +int hasname( const char *needle ) +{ + if ( !needle ) return 0; + size_t prev = fpos; + fpos = head->onames; + int found = 0; + int nlen = strlen(needle); + for ( uint32_t i=0; innames; i++ ) + { + int32_t len = 0; + if ( head->pkgver >= 64 ) + { + len = readindex(); + if ( len <= 0 ) continue; + } + int c = 0, p = 0, match = 1; + while ( (c = readbyte()) ) + { + if ( (p >= nlen) || (needle[p] != c) ) match = 0; + p++; + if ( len && (p > len) ) break; + } + if ( match ) + { + found = 1; + break; + } + fpos += 4; + } + fpos = prev; + return found; +} + +// read import table entry and return index of its object name +int32_t readimport( void ) +{ + readindex(); + readindex(); + if ( head->pkgver >= 60 ) fpos += 4; + else readindex(); + return readindex(); +} + +int32_t getimport( int index ) +{ + size_t prev = fpos; + fpos = head->oimports; + int32_t iname = 0; + for ( int i=0; i<=index; i++ ) + iname = readimport(); + fpos = prev; + return iname; +} + +// fully read import table entry +void readimport2( int32_t *cpkg, int32_t *cname, int32_t *pkg, int32_t *name ) +{ + *cpkg = readindex(); + *cname = readindex(); + if ( head->pkgver >= 60 ) *pkg = readdword(); + else *pkg = readindex(); + *name = readindex(); +} + +void getimport2( int index, int32_t *cpkg, int32_t *cname, int32_t *pkg, + int32_t *name ) +{ + size_t prev = fpos; + fpos = head->oimports; + for ( int i=0; i<=index; i++ ) + readimport2(cpkg,cname,pkg,name); + fpos = prev; +} + +// read export table entry +void readexport( int32_t *class, int32_t *ofs, int32_t *siz, int32_t *name ) +{ + *class = readindex(); + readindex(); + if ( head->pkgver >= 60 ) fpos += 4; + *name = readindex(); + fpos += 4; + *siz = readindex(); + if ( *siz > 0 ) *ofs = readindex(); +} + +void getexport( int index, int32_t *class, int32_t *ofs, int32_t *siz, + int32_t *name ) +{ + size_t prev = fpos; + fpos = head->oexports; + for ( int i=0; i<=index; i++ ) + readexport(class,ofs,siz,name); + fpos = prev; +} + +// fully read export table entry +void readexport2( int32_t *class, int32_t *super, int32_t *pkg, int32_t *name, + uint32_t *flags, int32_t *siz, int32_t *ofs ) +{ + *class = readindex(); + *super = readindex(); + if ( head->pkgver >= 60 ) *pkg = readdword(); + else *pkg = readindex(); + *name = readindex(); + *flags = readdword(); + *siz = readindex(); + if ( *siz > 0 ) *ofs = readindex(); +} + +void getexport2( int index, int32_t *class, int32_t *super, int32_t *pkg, + int32_t *name, uint32_t *flags, int32_t *siz, int32_t *ofs ) +{ + size_t prev = fpos; + fpos = head->oexports; + for ( int i=0; i<=index; i++ ) + readexport2(class,super,pkg,name,flags,siz,ofs); + fpos = prev; +} + +int main( int argc, char **argv ) +{ + if ( argc < 2 ) + { + printf("Usage: unrundeleter \n"); + return 1; + } + int fd = open(argv[1],O_RDWR); + if ( fd == -1 ) + { + printf("Failed to open file %s: %s\n",argv[1],strerror(errno)); + return 1; + } + struct stat st; + fstat(fd,&st); + pkgfile = malloc(st.st_size); + memset(pkgfile,0,st.st_size); + head = (upkg_header_t*)pkgfile; + int r = 0; + do + { + r = read(fd,pkgfile+fpos,131072); + if ( r == -1 ) + { + close(fd); + free(pkgfile); + printf("Read failed for file %s: %s\n",argv[1], + strerror(errno)); + return 4; + } + fpos += r; + } + while ( r > 0 ); + close(fd); + fpos = 0; + if ( head->magic != UPKG_MAGIC ) + { + printf("File %s is not a valid unreal package!\n",argv[1]); + free(pkgfile); + return 2; + } + if ( !hasname("bDeleteMe") ) + { + printf("Package %s does not contain deleted exports\n",argv[1]); + free(pkgfile); + return 4; + } + // loop through exports and search for bDeleteMe properties + fpos = head->oexports; + for ( uint32_t i=0; inexports; i++ ) + { + int32_t class, ofs, siz, name; + readexport(&class,&ofs,&siz,&name); + if ( (siz <= 0) || (class >= 0) ) continue; + // check the class + class = -class-1; + if ( (uint32_t)class > head->nimports ) continue; + // read name + int32_t l = 0; + char *ename = (char*)(pkgfile+getname(name,&l)); + printf("checking %.*s\n",l,ename); + // begin reading data + size_t prev = fpos; + fpos = ofs; + if ( head->pkgver < 40 ) fpos += 8; + if ( head->pkgver < 60 ) fpos += 16; + int32_t prop = readindex(); + if ( (uint32_t)prop >= head->nnames ) + { + printf("Unknown property %d, skipping\n",prop); + fpos = prev; + continue; + } + char *pname = (char*)(pkgfile+getname(prop,&l)); +retry: + if ( strncasecmp(pname,"none",l) ) + { + uint8_t info = readbyte(); + int ptype = info&0xf; + int psiz = (info>>4)&0x7; + int parr = (info>>7)&0x1; + switch ( psiz ) + { + case 0: + psiz = 1; + break; + case 1: + psiz = 2; + break; + case 2: + psiz = 4; + break; + case 3: + psiz = 12; + break; + case 4: + psiz = 16; + break; + case 5: + psiz = readbyte(); + break; + case 6: + psiz = readword(); + break; + case 7: + psiz = readdword(); + break; + } + fpos += psiz; + printf(" %zu: Skipping property %.*s of size %d and type %d (%d)\n",fpos,l,pname,psiz,ptype,parr); + if ( (ptype != 3) && parr ) + { + printf("%d\n",readbyte()); + return 1; + } + prop = readindex(); + printf("prop: %d\n",prop); + pname = (char*)(pkgfile+getname(prop,&l)); + // TODO figure out how the fuck structs and arrays actually are structured + // because the documentation for this is illegible + if ( ptype == 10 ) + { + + } + goto retry; + } + fpos = prev; + } + free(pkgfile); + return 0; +} diff --git a/usndextract.c b/usndextract.c new file mode 100644 index 0000000..805680b --- /dev/null +++ b/usndextract.c @@ -0,0 +1,420 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define UPKG_MAGIC 0x9E2A83C1 + +// uncomment if you want full data dumps, helpful if you need to reverse engineer some unsupported format +//#define _DEBUG 1 + +typedef struct +{ + uint32_t magic; + uint16_t pkgver, license; + uint32_t flags, nnames, onames, nexports, oexports, nimports, oimports; +} upkg_header_t; + +uint8_t *pkgfile; +upkg_header_t *head; +size_t fpos = 0; + +uint8_t readbyte( void ) +{ + uint8_t val = pkgfile[fpos]; + fpos++; + return val; +} + +uint16_t readword( void ) +{ + uint16_t val = *(uint16_t*)(pkgfile+fpos); + fpos += 2; + return val; +} + +uint32_t readdword( void ) +{ + uint32_t val = *(uint32_t*)(pkgfile+fpos); + fpos += 4; + return val; +} + +float readfloat( void ) +{ + float val = *(float*)(pkgfile+fpos); + fpos += 4; + return val; +} + +#define READSTRUCT(x,y) {memcpy(&x,pkgfile+fpos,sizeof(y));fpos+=sizeof(y);} + +// reads a compact index value +int32_t readindex( void ) +{ + uint8_t byte[5] = {0}; + byte[0] = readbyte(); + if ( !byte[0] ) return 0; + if ( byte[0]&0x40 ) + { + for ( int i=1; i<5; i++ ) + { + byte[i] = readbyte(); + 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; +} + +// reads a name table entry +size_t readname( int *olen ) +{ + size_t pos = fpos; + if ( head->pkgver >= 64 ) + { + int32_t len = readindex(); + pos = fpos; + if ( olen ) *olen = len; + if ( len <= 0 ) return pos; + fpos += len; + } + else + { + int c, p = 0; + while ( (c = readbyte()) ) p++; + if ( olen ) *olen = p; + } + fpos += 4; + return pos; +} + +size_t getname( int index, int *olen ) +{ + size_t prev = fpos; + fpos = head->onames; + size_t npos = 0; + for ( int i=0; i<=index; i++ ) + npos = readname(olen); + fpos = prev; + return npos; +} + +// checks if a name exists +int hasname( const char *needle ) +{ + if ( !needle ) return 0; + size_t prev = fpos; + fpos = head->onames; + int found = 0; + int nlen = strlen(needle); + for ( uint32_t i=0; innames; i++ ) + { + int32_t len = 0; + if ( head->pkgver >= 64 ) + { + len = readindex(); + if ( len <= 0 ) continue; + } + int c = 0, p = 0, match = 1; + while ( (c = readbyte()) ) + { + if ( (p >= nlen) || (needle[p] != c) ) match = 0; + p++; + if ( len && (p > len) ) break; + } + if ( match ) + { + found = 1; + break; + } + fpos += 4; + } + fpos = prev; + return found; +} + +// read import table entry and return index of its object name +int32_t readimport( void ) +{ + readindex(); + readindex(); + if ( head->pkgver >= 55 ) fpos += 4; + else readindex(); + return readindex(); +} + +int32_t getimport( int index ) +{ + size_t prev = fpos; + fpos = head->oimports; + int32_t iname = 0; + for ( int i=0; i<=index; i++ ) + iname = readimport(); + fpos = prev; + return iname; +} + +// fully read import table entry +void readimport2( int32_t *cpkg, int32_t *cname, int32_t *pkg, int32_t *name ) +{ + *cpkg = readindex(); + *cname = readindex(); + if ( head->pkgver >= 55 ) *pkg = readdword(); + else *pkg = readindex(); + *name = readindex(); +} + +void getimport2( int index, int32_t *cpkg, int32_t *cname, int32_t *pkg, + int32_t *name ) +{ + size_t prev = fpos; + fpos = head->oimports; + for ( int i=0; i<=index; i++ ) + readimport2(cpkg,cname,pkg,name); + fpos = prev; +} + +// read export table entry +void readexport( int32_t *class, int32_t *ofs, int32_t *siz, int32_t *name ) +{ + *class = readindex(); + readindex(); + if ( head->pkgver >= 55 ) fpos += 4; + *name = readindex(); + fpos += 4; + *siz = readindex(); + if ( *siz > 0 ) *ofs = readindex(); +} + +void getexport( int index, int32_t *class, int32_t *ofs, int32_t *siz, + int32_t *name ) +{ + size_t prev = fpos; + fpos = head->oexports; + for ( int i=0; i<=index; i++ ) + readexport(class,ofs,siz,name); + fpos = prev; +} + +// fully read export table entry +void readexport2( int32_t *class, int32_t *super, int32_t *pkg, int32_t *name, + uint32_t *flags, int32_t *siz, int32_t *ofs ) +{ + *class = readindex(); + *super = readindex(); + if ( head->pkgver >= 55 ) *pkg = readdword(); + else *pkg = readindex(); + *name = readindex(); + *flags = readdword(); + *siz = readindex(); + if ( *siz > 0 ) *ofs = readindex(); +} + +void getexport2( int index, int32_t *class, int32_t *super, int32_t *pkg, + int32_t *name, uint32_t *flags, int32_t *siz, int32_t *ofs ) +{ + size_t prev = fpos; + fpos = head->oexports; + for ( int i=0; i<=index; i++ ) + readexport2(class,super,pkg,name,flags,siz,ofs); + fpos = prev; +} + +void savesound( int32_t namelen, char *name, int version ) +{ + char fname[256] = {0}; + int32_t fmt = readindex(); // not really needed, always assume wav + int32_t grp, grouplen; + char *group; + if ( version > 35 ) + { + // ???? + readindex(); + readindex(); + grp = readindex(); + grouplen = 0; + group = (char*)(pkgfile+getname(grp,&grouplen)); + // ???? + readindex(); + } + else + { + grp = readindex(); + grouplen = 0; + group = (char*)(pkgfile+getname(grp,&grouplen)); + readindex(); // unknown + } + uint32_t ofsnext = 0; + if ( version >= 63 ) ofsnext = readdword(); // not needed but gotta read + // the actual important info starts now + int32_t sndsize = readindex(); + char *snddata = (char*)(pkgfile+fpos); + if ( strncmp(group,"None",grouplen) ) snprintf(fname,256,"%.*s.%.*s.wav",grouplen,group,namelen,name); + else snprintf(fname,256,"%.*s.wav",namelen,name); + FILE *f = fopen(fname,"wb"); + fwrite(snddata,sndsize,1,f); + fclose(f); +} + +int main( int argc, char **argv ) +{ + if ( argc < 2 ) + { + printf("Usage: usndextract \n"); + return 1; + } + int fd = open(argv[1],O_RDONLY); + if ( fd == -1 ) + { + printf("Failed to open file %s: %s\n",argv[1],strerror(errno)); + return 1; + } + struct stat st; + fstat(fd,&st); + pkgfile = malloc(st.st_size); + memset(pkgfile,0,st.st_size); + head = (upkg_header_t*)pkgfile; + int r = 0; + do + { + r = read(fd,pkgfile+fpos,131072); + if ( r == -1 ) + { + close(fd); + free(pkgfile); + printf("Read failed for file %s: %s\n",argv[1], + strerror(errno)); + return 4; + } + fpos += r; + } + while ( r > 0 ); + close(fd); + fpos = 0; + if ( head->magic != UPKG_MAGIC ) + { + printf("File %s is not a valid unreal package!\n",argv[1]); + free(pkgfile); + return 2; + } + if ( !hasname("Sound") ) + { + printf("Package %s does not contain sounds\n",argv[1]); + free(pkgfile); + return 4; + } + // loop through exports and search for sounds + fpos = head->oexports; + for ( uint32_t i=0; inexports; i++ ) + { + int32_t class, ofs, siz, name; + readexport(&class,&ofs,&siz,&name); + if ( (siz <= 0) || (class >= 0) ) continue; + // get the class name + class = -class-1; + if ( (uint32_t)class > head->nimports ) continue; + int32_t l = 0; + char *n = (char*)(pkgfile+getname(getimport(class),&l)); + int ismesh = !strncmp(n,"Sound",l); + if ( !ismesh ) continue; + char *snd = (char*)(pkgfile+getname(name,&l)); + printf("Sound found: %.*s\n",l,snd); + int32_t sndl = l; +#ifdef _DEBUG + char fname[256] = {0}; + snprintf(fname,256,"%.*s.object",sndl,snd); + printf(" Dumping full object data to %s\n",fname); + FILE *f = fopen(fname,"wb"); + fwrite(pkgfile+ofs,siz,1,f); + fclose(f); + continue; +#endif + // begin reading data + size_t prev = fpos; + fpos = ofs; + if ( head->pkgver < 45 ) fpos += 4; + if ( head->pkgver < 55 ) fpos += 16; + if ( head->pkgver <= 44 ) fpos -= 6; // ??? + if ( head->pkgver <= 35 ) fpos += 8; // ??? + // only very old packages have properties for sound classes + if ( head->pkgver > 35 ) + goto noprop; + // process properties + int32_t prop = readindex(); + if ( (uint32_t)prop >= head->nnames ) + { + printf("Unknown property %d, skipping\n",prop); + fpos = prev; + continue; + } + char *pname = (char*)(pkgfile+getname(prop,&l)); +retry: + if ( strncasecmp(pname,"None",l) ) + { + uint8_t info = readbyte(); + int array = info&0x80; + int type = info&0xf; + int psiz = (info>>4)&0x7; + switch ( psiz ) + { + case 0: + psiz = 1; + break; + case 1: + psiz = 2; + break; + case 2: + psiz = 4; + break; + case 3: + psiz = 12; + break; + case 4: + psiz = 16; + break; + case 5: + psiz = readbyte(); + break; + case 6: + psiz = readword(); + break; + case 7: + psiz = readdword(); + break; + } + printf(" prop %.*s (%u, %u, %u, %u)\n",l,pname,array,type,(info>>4)&7,psiz); + if ( array && (type != 3) ) + { + int idx = readindex(); + printf(" index: %d\n",idx); + } + if ( type == 10 ) + { + int32_t tl, sn; + sn = readindex(); + char *sname = (char*)(pkgfile+getname(sn,&tl)); + printf(" struct: %.*s\n",tl,sname); + } + fpos += psiz; + prop = readindex(); + pname = (char*)(pkgfile+getname(prop,&l)); + goto retry; + } +noprop: + savesound(sndl,snd,head->pkgver); + fpos = prev; + } + free(pkgfile); + return 0; +} diff --git a/utxextract.c b/utxextract.c new file mode 100644 index 0000000..e9857b2 --- /dev/null +++ b/utxextract.c @@ -0,0 +1,581 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define UPKG_MAGIC 0x9E2A83C1 + +// uncomment if you want full data dumps, helpful if you need to reverse engineer some unsupported format +//#define _DEBUG 1 + +typedef struct +{ + uint32_t magic; + uint16_t pkgver, license; + uint32_t flags, nnames, onames, nexports, oexports, nimports, oimports; +} upkg_header_t; + +uint8_t *pkgfile; +upkg_header_t *head; +size_t fpos = 0; + +uint8_t readbyte( void ) +{ + uint8_t val = pkgfile[fpos]; + fpos++; + return val; +} + +uint16_t readword( void ) +{ + uint16_t val = *(uint16_t*)(pkgfile+fpos); + fpos += 2; + return val; +} + +uint32_t readdword( void ) +{ + uint32_t val = *(uint32_t*)(pkgfile+fpos); + fpos += 4; + return val; +} + +float readfloat( void ) +{ + float val = *(float*)(pkgfile+fpos); + fpos += 4; + return val; +} + +#define READSTRUCT(x,y) {memcpy(&x,pkgfile+fpos,sizeof(y));fpos+=sizeof(y);} + +// reads a compact index value +int32_t readindex( void ) +{ + uint8_t byte[5] = {0}; + byte[0] = readbyte(); + if ( !byte[0] ) return 0; + if ( byte[0]&0x40 ) + { + for ( int i=1; i<5; i++ ) + { + byte[i] = readbyte(); + 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; +} + +// reads a name table entry +size_t readname( int *olen ) +{ + size_t pos = fpos; + if ( head->pkgver >= 64 ) + { + int32_t len = readindex(); + pos = fpos; + if ( olen ) *olen = len; + if ( len <= 0 ) return pos; + fpos += len; + } + else + { + int c, p = 0; + while ( (c = readbyte()) ) p++; + if ( olen ) *olen = p; + } + fpos += 4; + return pos; +} + +size_t getname( int index, int *olen ) +{ + size_t prev = fpos; + fpos = head->onames; + size_t npos = 0; + for ( int i=0; i<=index; i++ ) + npos = readname(olen); + fpos = prev; + return npos; +} + +// checks if a name exists +int hasname( const char *needle ) +{ + if ( !needle ) return 0; + size_t prev = fpos; + fpos = head->onames; + int found = 0; + int nlen = strlen(needle); + for ( uint32_t i=0; innames; i++ ) + { + int32_t len = 0; + if ( head->pkgver >= 64 ) + { + len = readindex(); + if ( len <= 0 ) continue; + } + int c = 0, p = 0, match = 1; + while ( (c = readbyte()) ) + { + if ( (p >= nlen) || (needle[p] != c) ) match = 0; + p++; + if ( len && (p > len) ) break; + } + if ( match ) + { + found = 1; + break; + } + fpos += 4; + } + fpos = prev; + return found; +} + +// read import table entry and return index of its object name +int32_t readimport( void ) +{ + readindex(); + readindex(); + if ( head->pkgver >= 55 ) fpos += 4; + else readindex(); + return readindex(); +} + +int32_t getimport( int index ) +{ + size_t prev = fpos; + fpos = head->oimports; + int32_t iname = 0; + for ( int i=0; i<=index; i++ ) + iname = readimport(); + fpos = prev; + return iname; +} + +// fully read import table entry +void readimport2( int32_t *cpkg, int32_t *cname, int32_t *pkg, int32_t *name ) +{ + *cpkg = readindex(); + *cname = readindex(); + if ( head->pkgver >= 55 ) *pkg = readdword(); + else *pkg = readindex(); + *name = readindex(); +} + +void getimport2( int index, int32_t *cpkg, int32_t *cname, int32_t *pkg, + int32_t *name ) +{ + size_t prev = fpos; + fpos = head->oimports; + for ( int i=0; i<=index; i++ ) + readimport2(cpkg,cname,pkg,name); + fpos = prev; +} + +// read export table entry +void readexport( int32_t *class, int32_t *ofs, int32_t *siz, int32_t *name ) +{ + *class = readindex(); + readindex(); + if ( head->pkgver >= 55 ) fpos += 4; + *name = readindex(); + fpos += 4; + *siz = readindex(); + if ( *siz > 0 ) *ofs = readindex(); +} + +void getexport( int index, int32_t *class, int32_t *ofs, int32_t *siz, + int32_t *name ) +{ + size_t prev = fpos; + fpos = head->oexports; + for ( int i=0; i<=index; i++ ) + readexport(class,ofs,siz,name); + fpos = prev; +} + +// fully read export table entry +void readexport2( int32_t *class, int32_t *super, int32_t *pkg, int32_t *name, + uint32_t *flags, int32_t *siz, int32_t *ofs ) +{ + *class = readindex(); + *super = readindex(); + if ( head->pkgver >= 55 ) *pkg = readdword(); + else *pkg = readindex(); + *name = readindex(); + *flags = readdword(); + *siz = readindex(); + if ( *siz > 0 ) *ofs = readindex(); +} + +void getexport2( int index, int32_t *class, int32_t *super, int32_t *pkg, + int32_t *name, uint32_t *flags, int32_t *siz, int32_t *ofs ) +{ + size_t prev = fpos; + fpos = head->oexports; + for ( int i=0; i<=index; i++ ) + readexport2(class,super,pkg,name,flags,siz,ofs); + fpos = prev; +} + +int writepng( const char *filename, unsigned char *fdata, int fw, int fh, + png_color *fpal, int fpalsiz, int masked ) +{ + if ( !filename ) return 0; + png_structp pngp; + png_infop infp; + FILE *pf; + if ( !(pf = fopen(filename,"wb")) ) return 0; + pngp = png_create_write_struct(PNG_LIBPNG_VER_STRING,0,0,0); + if ( !pngp ) + { + fclose(pf); + return 0; + } + infp = png_create_info_struct(pngp); + if ( !infp ) + { + fclose(pf); + png_destroy_write_struct(&pngp,0); + return 0; + } + if ( setjmp(png_jmpbuf(pngp)) ) + { + png_destroy_write_struct(&pngp,&infp); + fclose(pf); + return 0; + } + png_init_io(pngp,pf); + if ( fpal ) + { + png_set_IHDR(pngp,infp,fw,fh,8,PNG_COLOR_TYPE_PALETTE, + PNG_INTERLACE_NONE,PNG_COMPRESSION_TYPE_DEFAULT, + PNG_FILTER_TYPE_DEFAULT); + png_set_PLTE(pngp,infp,fpal,fpalsiz); + if ( masked ) + { + unsigned char t = 0; + png_set_tRNS(pngp,infp,&t,1,0); + } + png_write_info(pngp,infp); + for ( int i=0; ioexports; + for ( uint32_t i=0; inexports; i++ ) + { + int32_t class, ofs, siz, name; + readexport(&class,&ofs,&siz,&name); + if ( i != pal-1 ) continue; + int32_t l = 0; + char *n = (char*)(pkgfile+getname(name,&l)); + // begin reading data + fpos = ofs; + if ( head->pkgver < 45 ) fpos += 4; + if ( head->pkgver < 55 ) fpos += 16; + if ( head->pkgver <= 44 ) fpos -= 6; // ??? + if ( head->pkgver <= 35 ) fpos += 8; // ??? + // process properties + int32_t prop = readindex(); + if ( (uint32_t)prop >= head->nnames ) + { + printf("Unknown property %d, skipping\n",prop); + fpos = prev; + return; + } + char *pname = (char*)(pkgfile+getname(prop,&l)); +retrypal: + if ( strncasecmp(pname,"none",l) ) + { + uint8_t info = readbyte(); + int array = info&0x80; + int type = info&0xf; + int psiz = (info>>4)&0x7; + switch ( psiz ) + { + case 0: + psiz = 1; + break; + case 1: + psiz = 2; + break; + case 2: + psiz = 4; + break; + case 3: + psiz = 12; + break; + case 4: + psiz = 16; + break; + case 5: + psiz = readbyte(); + break; + case 6: + psiz = readword(); + break; + case 7: + psiz = readdword(); + break; + } + if ( type == 10 ) + readindex(); // skip struct name + fpos += psiz; + prop = readindex(); + pname = (char*)(pkgfile+getname(prop,&l)); + goto retrypal; + } + if ( (head->pkgver <= 56) ) + { + // group? + fpos++; + readindex(); + } + *num = readindex(); + printf(" palette: %u colors\n",*num); + *col = calloc(sizeof(color_t),*num); + memcpy(*col,pkgfile+fpos,*num*sizeof(color_t)); + fpos = prev; + return; + } + fpos = prev; +} + +void savetexture( int32_t namelen, char *name, int32_t pal, int masked, + int version ) +{ + uint32_t ncolors = 256; + color_t *paldata = 0; + readpalette(pal,&ncolors,&paldata); + if ( version <= 56 ) + { + // group? + fpos++; + readindex(); + } + uint32_t mipcnt = readbyte(); + printf(" %u mips\n",mipcnt); + uint32_t ofs = 0; + if ( version >= 63 ) ofs = readdword(); + uint32_t datasiz = readindex(); + printf(" %u size\n",datasiz); + uint8_t *imgdata = malloc(datasiz); + memcpy(imgdata,pkgfile+fpos,datasiz); + imgdata = malloc(datasiz); + memcpy(imgdata,pkgfile+fpos,datasiz); + if ( version >= 63 ) fpos = ofs; + else fpos += datasiz; + uint32_t w = readdword(); + uint32_t h = readdword(); + png_color fpal[256] = {{0}}; + if ( !paldata || (ncolors <= 0) ) + { + ncolors = 256; + // generate dummy palette + for ( int i=0; i<256; i++ ) + { + fpal[i].red = i; + fpal[i].green = i; + fpal[i].blue = i; + } + } + else + { + for ( int i=0; i\n"); + return 1; + } + int fd = open(argv[1],O_RDONLY); + if ( fd == -1 ) + { + printf("Failed to open file %s: %s\n",argv[1],strerror(errno)); + return 1; + } + struct stat st; + fstat(fd,&st); + pkgfile = malloc(st.st_size); + memset(pkgfile,0,st.st_size); + head = (upkg_header_t*)pkgfile; + int r = 0; + do + { + r = read(fd,pkgfile+fpos,131072); + if ( r == -1 ) + { + close(fd); + free(pkgfile); + printf("Read failed for file %s: %s\n",argv[1], + strerror(errno)); + return 4; + } + fpos += r; + } + while ( r > 0 ); + close(fd); + fpos = 0; + if ( head->magic != UPKG_MAGIC ) + { + printf("File %s is not a valid unreal package!\n",argv[1]); + free(pkgfile); + return 2; + } + // loop through exports and search for textures + fpos = head->oexports; + for ( uint32_t i=0; inexports; i++ ) + { + int32_t class, ofs, siz, name; + readexport(&class,&ofs,&siz,&name); + if ( (siz <= 0) || (class >= 0) ) continue; + // get the class name + class = -class-1; + if ( (uint32_t)class > head->nimports ) continue; + int32_t l = 0; + char *n = (char*)(pkgfile+getname(getimport(class),&l)); + int istex = !strncasecmp(n,"Texture",l); + if ( !istex ) continue; + char *tex = (char*)(pkgfile+getname(name,&l)); + printf("Texture found: %.*s\n",l,tex); + int32_t texl = l; +#ifdef _DEBUG + char fname[256] = {0}; + snprintf(fname,256,"%.*s.object",texl,tex); + printf(" Dumping full object data to %s\n",fname); + FILE *f = fopen(fname,"wb"); + fwrite(pkgfile+ofs,siz,1,f); + fclose(f); + continue; +#endif + // begin reading data + size_t prev = fpos; + fpos = ofs; + if ( head->pkgver < 45 ) fpos += 4; + if ( head->pkgver < 55 ) fpos += 16; + if ( head->pkgver <= 44 ) fpos -= 6; // ??? + if ( head->pkgver <= 35 ) fpos += 8; // ??? + // process properties + int32_t prop = readindex(); + if ( (uint32_t)prop >= head->nnames ) + { + printf("Unknown property %d, skipping\n",prop); + fpos = prev; + continue; + } + int32_t pal = 0; // we need to fetch this + char *pname = (char*)(pkgfile+getname(prop,&l)); + int masked = 0; // and this (which may not be available) +retry: + if ( strncasecmp(pname,"None",l) ) + { + uint8_t info = readbyte(); + int array = info&0x80; + int type = info&0xf; + int psiz = (info>>4)&0x7; + switch ( psiz ) + { + case 0: + psiz = 1; + break; + case 1: + psiz = 2; + break; + case 2: + psiz = 4; + break; + case 3: + psiz = 12; + break; + case 4: + psiz = 16; + break; + case 5: + psiz = readbyte(); + break; + case 6: + psiz = readword(); + break; + case 7: + psiz = readdword(); + break; + } + //printf(" prop %.*s (%u, %u, %u, %u)\n",l,pname,array,type,(info>>4)&7,psiz); + if ( array && (type != 3) ) + { + int idx = readindex(); + //printf(" index: %d\n",idx); + } + if ( !strncasecmp(pname,"Palette",l) ) + pal = readindex(); + if ( !strncasecmp(pname,"bMasked",l) ) + masked = array; + else + { + if ( type == 10 ) + { + int32_t tl, sn; + sn = readindex(); + //char *sname = (char*)(pkgfile+getname(sn,&tl)); + //printf(" struct: %.*s\n",tl,sname); + } + fpos += psiz; + } + prop = readindex(); + pname = (char*)(pkgfile+getname(prop,&l)); + goto retry; + } + if ( !pal ) continue; + savetexture(texl,tex,pal,masked,head->pkgver); + fpos = prev; + } + free(pkgfile); + return 0; +} diff --git a/wavrip.c b/wavrip.c new file mode 100644 index 0000000..a52c103 --- /dev/null +++ b/wavrip.c @@ -0,0 +1,46 @@ +#include +#include +#include + +#define min(a,b) (((a)<(b))?(a):(b)) + +int main( int argc, char **argv ) +{ + if ( argc < 2 ) return 1; + FILE *fin, *fout; + if ( !(fin = fopen(argv[1],"rb")) ) + return 2; + unsigned char buf[131072]; + size_t nread = 0, pread = 0; + int i = 0; +keepreading: + nread = fread(buf,1,4,fin); + if ( feof(fin) || (nread < 4) ) + { + fclose(fin); + return 0; + } + if ( strncmp(buf,"RIFF",4) ) + { + fseek(fin,-3,SEEK_CUR); + goto keepreading; + } + char fname[256]; + snprintf(fname,256,"SND%d.wav",i); + fout = fopen(fname,"wb"); + fwrite(buf,1,4,fout); + nread = fread(buf,4,1,fin); + fwrite(buf,1,4,fout); + pread = (*(uint32_t*)buf)-4; + while ( pread > 0 ) + { + nread = fread(buf,1,min(131072,pread),fin); + fwrite(buf,1,nread,fout); + pread -= nread; + } + fclose(fout); + i++; + if ( !feof(fin) ) goto keepreading; + fclose(fin); + return 0; +}