Wow I haven't updated this in a long time.

This commit is contained in:
Marisa the Magician 2019-11-19 17:28:33 +01:00
commit 55010c8b48
15 changed files with 3980 additions and 23 deletions

View file

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

220
cube2enviro.c Normal file
View file

@ -0,0 +1,220 @@
#include <epoxy/gl.h>
#include <SDL2/SDL.h>
#include <SDL2/SDL_image.h>
#include <stdio.h>
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 <basename> [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;
}

View file

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

44
dood.c
View file

@ -1,30 +1,56 @@
#include <stdio.h>
#include <errno.h>
#include <string.h>
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 <input> <output>\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;
}

522
mazestuff.c Normal file
View file

@ -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 <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <time.h>
#include <SDL2/SDL.h>
// 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; y<MAP_HEIGHT; y++ )
for ( int x=0; x<MAP_WIDTH; x++ )
{
for ( int yy=0; yy<CH; yy++ )
for ( int xx=0; xx<CW; xx++ )
px[(y*CH+yy)*MAP_WIDTH*CH+(x*CW+xx)] = pal[map[x][y]];
}
SDL_Event e;
while ( SDL_PollEvent(&e) )
{
if ( e.type == SDL_QUIT )
active = 0;
}
if ( !active )
{
SDL_Quit();
exit(0);
}
SDL_UpdateWindowSurface(w);
#ifdef GENDELAY
SDL_Delay(GENDELAY);
#endif
}
// the basic cell editor function
// can also "uncarve" back walls
void carve( int x, int y, int col )
{
map[x][y] = col;
region[x][y] = col?current_region:0; // un-carving resets region
// debug
// comment out if you don't want to see the generation frame by frame
drawmap();
}
// build a whole room
void carve_room( room_t* r )
{
current_region++;
for ( int j=r->y1; j<r->y2; j++ ) for ( int i=r->x1; i<r->x2; 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<ROOM_TRIES; i++ )
{
rw = (Random(ROOM_MIN,ROOM_MAX)/2)*2+1;
rh = (Random(ROOM_MIN,ROOM_MAX)/2)*2+1;
rx = (Random(0,MAP_WIDTH-rw-1)/2)*2+1;
ry = (Random(0,MAP_HEIGHT-rh-1)/2)*2+1;
room_t room = {rx,ry,rx+rw,ry+rh};
// overlap check
int failed = 0;
for ( int j=0; j<nrooms; j++ )
{
if ( !room_intersect(&room,&rooms[j]) ) continue;
failed = 1;
break;
}
if ( failed ) continue;
// add it
nrooms++;
rooms = realloc(rooms,nrooms*sizeof(room_t));
memcpy(rooms+nrooms-1,&room,sizeof(room_t));
carve_room(&room);
}
// feel free to do something here like picking which room has the start
// and which the exit
free(rooms);
}
// check if the maze can advance in this direction
int can_carve( int sx, int sy, int dir )
{
int x = sx+dirs[dir].x*3;
int y = sy+dirs[dir].y*3;
if ( (x < 0) || (x >= 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; i<ndirs; i++ )
{
int j = Random(i,ndirs-1);
int tmp = wdir[i];
wdir[i] = wdir[j];
wdir[j] = tmp;
}
// pick
dir = wdir[0];
}
int x = ccell->x+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<MAP_HEIGHT-1; j++ )
for ( int i=1; i<MAP_WIDTH-1; i++ )
{
if ( map[i][j] ) continue;
// check regions this wall is touching
for ( int d=0; d<4; d++ )
{
int r = region[i+dirs[d].x][j+dirs[d].y];
creg[i][j][d+1] = r;
if ( r <= 0 ) continue;
creg[i][j][0]++;
}
}
// list all those walls that touch two regions in a straight line
conn_t *conn = malloc(0);
int nconn = 0;
current_region++;
for ( int j=0; j<MAP_HEIGHT; j++ )
for ( int i=0; i<MAP_WIDTH; i++ )
{
if ( creg[i][j][0] != 2 ) continue;
if ( !((creg[i][j][1] && creg[i][j][2])
|| (creg[i][j][3] && creg[i][j][4])) ) continue;
conn_t cconn;
cconn.x = i;
cconn.y = j;
int k = 0, l = 1;
while ( k < 2 )
{
if ( creg[i][j][l] )
{
cconn.r[k] = creg[i][j][l];
k++;
}
l++;
}
// discard doors connecting between the same region
// by random chance
if ( (cconn.r[0] == cconn.r[1]) && (FRandom(0,1) > 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<current_region; i++ )
{
// locate all connections to this region
int *connl = malloc(0);
int nconnl = 0;
for ( int j=0; j<nconn; j++ )
{
if ( (conn[j].r[0] != i) && (conn[j].r[1] != i) )
continue;
nconnl++;
connl = realloc(connl,nconnl*sizeof(int));
connl[nconnl-1] = j;
}
// pick a random index
int rconn = Random(0,nconnl-1);
// open additional doors, making sure they're not too close
// to any other
for ( int j=0; j<nconnl; j++ )
{
if ( j == rconn )
{
// this one's always guaranteed
carve(conn[connl[j]].x,conn[connl[j]].y,TILE_DOOR);
continue;
}
if ( FRandom(0,1) > 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<nconn; i++ )
{
int cleanme = 0;
for ( int j=0; j<4; j++ )
{
if ( map[conn[i].x+dirs[j].x][conn[i].y+dirs[j].y] == TILE_DOOR )
cleanme = 1;
}
if ( cleanme ) carve(conn[i].x,conn[i].y,TILE_WALL);
}
free(conn);
}
// get rid of every single cell that is surrounded by at least 3 walls
void clean_deadends()
{
int found;
do
{
found = 0;
for ( int j=1; j<MAP_HEIGHT-1; j++ )
for ( int i=1; i<MAP_WIDTH-1; i++ )
{
// needs to be air
if ( map[i][j] != TILE_AIR ) continue;
int nwalls = 0;
for ( int k=0; k<4; k++ )
{
if ( map[i+dirs[k].x][j+dirs[k].y] == TILE_WALL )
nwalls++;
}
if ( nwalls < 3 ) continue;
// un-carve nearby doors
// so as to not end up with doors to nowhere
for ( int k=0; k<4; k++ )
{
if ( map[i+dirs[k].x][j+dirs[k].y] == TILE_DOOR )
carve(i+dirs[k].x,j+dirs[k].y,TILE_WALL);
}
carve(i,j,TILE_WALL);
found++;
}
} while ( found > 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<MAP_HEIGHT-1; j++ )
for ( int i=1; i<MAP_WIDTH-1; i++ )
{
if ( (map[i][j] == TILE_WALL) || sregion[i][j] ) continue;
current_sregion++;
ssizes = realloc(ssizes,sizeof(int)*current_sregion);
int ssz = spread_sregion(i,j);
ssizes[current_sregion-1] = ssz;
}
// find largest superregion
int largest = 0;
for ( int i=0; i<current_sregion; i++ )
if ( ssizes[i] > ssizes[largest] ) largest = i;
for ( int j=1; j<MAP_HEIGHT-1; j++ )
for ( int i=1; i<MAP_WIDTH-1; i++ )
{
if ( (map[i][j] == TILE_WALL)
|| (sregion[i][j] == largest+1) ) continue;
// uncarve it
carve(i,j,TILE_WALL);
sregion[i][j] = 0;
// note: if you kept track of rooms, you might also want to
// make sure to erase any rooms caught by this uncarving
// (just check if the position is inside the room's bounds)
}
free(ssizes);
}
// this is where the magic happens
void dungeon_make()
{
add_rooms();
for ( int j=1; j<MAP_HEIGHT-1; j+=2 )
for ( int i=1; i<MAP_WIDTH-1; i+=2 )
{
if ( map[i][j] != TILE_WALL ) continue;
grow_maze(i,j);
}
connect_regions();
clean_deadends();
clean_isolated();
}
// this is just here for displaying the stuff(tm)
int main( int argc, char **argv )
{
unsigned seed = 0;
if ( argc > 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;
}

25
pcxpalex.c Normal file
View file

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

View file

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

889
u083extract.c Normal file
View file

@ -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 <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <png.h>
#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; i<fh; i++ ) png_write_row(pngp,fdata+(fw*i));
}
else
{
png_set_IHDR(pngp,infp,fw,fh,8,PNG_COLOR_TYPE_RGB,
PNG_INTERLACE_NONE,PNG_COMPRESSION_TYPE_DEFAULT,
PNG_FILTER_TYPE_DEFAULT);
png_write_info(pngp,infp);
for ( int i=0; i<fh; i++ ) png_write_row(pngp,fdata+(fw*3*i));
}
png_write_end(pngp,infp);
png_destroy_write_struct(&pngp,&infp);
fclose(pf);
return 1;
}
void extract_pal( uint32_t i, upalette_t *pal )
{
mkdir("Palettes",0755);
color_t cpal[256];
png_color fpal[256];
uint8_t dat[16384];
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;
}
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 <archive>\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<tail.nexports; i++ )
{
// for debug
/*printf("%d %.32s %.32s &%08x (%08x)\n",i,exports[i].name,
typenames[exports[i].type],
tail.oheaders+(ptr-headers),*(uint32_t*)ptr);*/
if ( !types[exports[i].type].hdrsize )
{
fprintf(stderr,"Type %.32s at %x not supported, bailing"
" out\n",typenames[exports[i].type],
tail.oheaders+(ptr-headers));
if ( i < (tail.nexports-1) )
fprintf(stderr,"Next export is %.32s with type %.32s\n",
exports[i+1].name,typenames[exports[i+1].type]);
return 128;
}
eheaders[i] = ptr;
prev = ptr;
ptr += types[exports[i].type].hdrsize;
}
for ( int i=0; i<tail.nexports; i++ )
{
switch( exports[i].type )
{
case RES_TextBuffer:
extract_txt(i,(utextbuffer_t*)eheaders[i]);
break;
case RES_Sound:
extract_snd(i,(usound_t*)eheaders[i]);
break;
case RES_Palette:
extract_pal(i,(upalette_t*)eheaders[i]);
break;
case RES_Texture:
if ( tail.version == RES_FILE_VERSION_083 )
extract_tex083(i,(utexture083_t*)eheaders[i]);
else extract_tex084(i,(utexture084_t*)eheaders[i]);
break;
case RES_Mesh:
if ( tail.version == RES_FILE_VERSION_083 )
extract_msh083(i,(umesh083_t*)eheaders[i]);
else extract_msh084(i,(umesh084_t*)eheaders[i]);
break;
}
}
free(names);
free(imports);
free(exports);
free(headers);
free(eheaders);
fclose(f);
return 0;
}

404
u086extract.c Normal file
View file

@ -0,0 +1,404 @@
/*
should support anything from 0.86~0.87, when the arctive format changed
to something that's kind of an intermediate point between the early and
final formats
fortunately this one is more third party tool friendly as you don't
need to know the structure of all object types to read the data
*/
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <png.h>
#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<objects[i].headersiz; j++ )
printf("%02x%c",header[j],((j+1)%8)?' ':'\n');
if ( j%8 ) printf("\n");
free(header);
}
void extract_txt( uint32_t i )
{
if ( !objects[i].dataofs || !objects[i].datasiz ) return;
mkdir("Classes",0755);
fseek(f,objects[i].dataofs,SEEK_SET);
char *rbuf = malloc(objects[i].datasiz);
fread(rbuf,1,objects[i].datasiz-1,f);
char fname[256];
snprintf(fname,256,"Classes/%.32s.uc",names[objects[i].name-1].name);
FILE *out = fopen(fname,"wb");
fwrite(rbuf,1,objects[i].datasiz-1,out);
fclose(out);
free(rbuf);
}
void extract_snd( uint32_t i )
{
if ( !objects[i].dataofs || !objects[i].datasiz ) return;
/*debug_printheader(i);
return;*/
mkdir("Sounds",0755);
// skip to what we need
int fofs = 20;
if ( head.version >= 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<fh; i++ ) png_write_row(pngp,fdata+(fw*i));
}
else
{
png_set_IHDR(pngp,infp,fw,fh,8,PNG_COLOR_TYPE_RGB,
PNG_INTERLACE_NONE,PNG_COMPRESSION_TYPE_DEFAULT,
PNG_FILTER_TYPE_DEFAULT);
png_write_info(pngp,infp);
for ( int i=0; i<fh; i++ ) png_write_row(pngp,fdata+(fw*3*i));
}
png_write_end(pngp,infp);
png_destroy_write_struct(&pngp,&infp);
fclose(pf);
return 1;
}
typedef struct
{
uint8_t r, g, b, x;
} color_t;
void extract_pal( uint32_t i )
{
if ( !objects[i].dataofs || !objects[i].datasiz ) return;
mkdir("Palettes",0755);
color_t cpal[256];
png_color fpal[256];
uint8_t dat[16384];
fseek(f,objects[i].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",names[objects[i].name-1].name);
writepng(fname,dat,128,128,fpal,256);
}
void extract_tex( uint32_t i )
{
if ( !objects[i].dataofs || !objects[i].datasiz ) return;
mkdir("Textures",0755);
int oofs = 40;
if ( objects[i].headersiz == 138 ) oofs = 24;
if ( head.version >= 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 <archive>\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<head.nnames; i++ )
{
readstr(names[i].name,32,f);
fread(&names[i].flags,4,1,f);
}
fseek(f,head.oobjects,SEEK_SET);
objects = calloc(head.nobjects,sizeof(unrealobject_t));
for ( int i=0; i<head.nobjects; i++ )
{
fread(&objects[i].name,2,1,f);
if ( head.version >= 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<head.nnames; i++ )
printf("%x %.32s\n",i,names[i].name);*/
for ( int i=0; i<head.nobjects; i++ )
{
/*printf("%.32s (%.32s)\n",names[objects[i].name-1].name,
names[objects[i].class-1].name);
fflush(stdout);*/
if ( !strcmp(names[objects[i].class-1].name,"TextBuffer") )
extract_txt(i);
else if ( !strcmp(names[objects[i].class-1].name,"Sound") )
extract_snd(i);
else if ( !strcmp(names[objects[i].class-1].name,"Palette") )
extract_pal(i);
else if ( !strcmp(names[objects[i].class-1].name,"Texture") )
extract_tex(i);
else if ( !strcmp(names[objects[i].class-1].name,"Mesh") )
extract_msh(i);
}
free(names);
free(objects);
fclose(f);
return 0;
}

430
u95extract.c Normal file
View file

@ -0,0 +1,430 @@
/*
extract text, meshes, textures, palettes and sounds from 1995 packages.
TODO: extract animseq and meshmap data for meshes
*/
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <png.h>
#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<fh; i++ ) png_write_row(pngp,fdata+(fw*i));
}
else
{
png_set_IHDR(pngp,infp,fw,fh,8,PNG_COLOR_TYPE_RGB,
PNG_INTERLACE_NONE,PNG_COMPRESSION_TYPE_DEFAULT,
PNG_FILTER_TYPE_DEFAULT);
png_write_info(pngp,infp);
for ( int i=0; i<fh; i++ ) png_write_row(pngp,fdata+(fw*3*i));
}
png_write_end(pngp,infp);
png_destroy_write_struct(&pngp,&infp);
fclose(pf);
return 1;
}
void extract_pal( uint32_t i )
{
mkdir("Palettes",0755);
color_t cpal[256];
png_color fpal[256];
uint8_t dat[16384];
fseek(f,exports[i].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/%.16s.png",exports[i].name);
writepng(fname,dat,128,128,fpal,256);
}
png_color defpal[256] =
{
{ 0, 0, 0}, {128, 0, 0}, { 0,128, 0}, {128,128, 0},
{ 0, 0,128}, {128, 0,128}, { 0,128,128}, {192,192,192},
{192,220,192}, {166,202,240}, { 0,255,255}, { 0,255,255},
{ 0,255,255}, { 0,255,255}, { 0,255,255}, { 0,255,255},
{255,227,203}, {255,215,179}, {211,179,151}, {243,179,123},
{239,219,131}, {215,115, 59}, {131,175,215}, {223,187,127},
{235,215,199}, {231,203,175}, {203,163,135}, {223,163,107},
{207,207,107}, {207, 91, 43}, {115,163,207}, {219,175, 95},
{219,207,195}, {211,191,171}, {195,151,127}, {203,151, 95},
{171,191, 91}, {199, 67, 31}, {103,151,199}, {223,159, 47},
{199,195,187}, {191,179,167}, {187,139,115}, {187,139, 83},
{143,175, 79}, {191, 43, 19}, { 91,139,191}, {211,139, 39},
{183,183,183}, {183,171,159}, {183,131,107}, {179,131, 75},
{135,163, 75}, {183, 19, 7}, { 79,127,183}, {199,123, 31},
{175,175,175}, {175,163,151}, {171,123, 99}, {167,123, 71},
{127,155, 71}, {175, 0, 0}, { 71,119,175}, {187,107, 27},
{163,163,163}, {163,151,139}, {159,115, 91}, {159,115, 67},
{119,143, 67}, {163, 0, 0}, { 63,111,163}, {175, 95, 19},
{151,151,151}, {151,139,127}, {151,103, 87}, {151,111, 63},
{111,135, 63}, {151, 0, 0}, { 59,103,155}, {163, 79, 15},
{139,139,139}, {139,127,119}, {139, 95, 79}, {139,103, 59},
{103,127, 59}, {139, 0, 0}, { 55, 95,147}, {151, 67, 11},
{131,131,131}, {131,119,107}, {127, 87, 71}, {131, 95, 55},
{ 95,119, 55}, {131, 7, 0}, { 47, 87,135}, {139, 55, 7},
{119,119,119}, {119,107, 99}, {119, 83, 67}, {123, 91, 51},
{ 91,111, 51}, {119, 7, 0}, { 43, 83,127}, {131, 51, 7},
{107,107,107}, {107, 95, 87}, {107, 75, 59}, {115, 87, 47},
{ 87,103, 47}, {107, 7, 0}, { 39, 75,115}, {123, 43, 7},
{ 99, 99, 99}, { 99, 87, 79}, { 99, 67, 55}, {103, 79, 43},
{ 79, 95, 43}, { 99, 7, 0}, { 35, 67,107}, {115, 35, 0},
{ 91, 91, 91}, { 91, 79, 75}, { 91, 63, 51}, { 95, 71, 39},
{ 75, 87, 39}, { 91, 7, 0}, { 31, 63, 99}, {107, 27, 0},
{ 83, 83, 83}, { 87, 75, 67}, { 83, 55, 47}, { 83, 63, 35},
{ 67, 79, 39}, { 83, 7, 0}, { 27, 55, 91}, { 99, 23, 0},
{ 79, 79, 79}, { 79, 71, 63}, { 79, 51, 43}, { 75, 55, 31},
{ 63, 75, 35}, { 75, 0, 0}, { 23, 51, 83}, { 95, 19, 0},
{ 71, 71, 71}, { 71, 63, 59}, { 71, 47, 39}, { 67, 51, 27},
{ 59, 67, 31}, { 71, 0, 0}, { 23, 43, 75}, { 87, 15, 0},
{ 63, 63, 63}, { 67, 59, 55}, { 67, 43, 35}, { 63, 47, 23},
{ 51, 63, 27}, { 63, 0, 0}, { 19, 39, 71}, { 79, 11, 0},
{ 59, 59, 59}, { 59, 55, 47}, { 59, 39, 31}, { 59, 43, 23},
{ 47, 59, 27}, { 59, 0, 0}, { 19, 35, 63}, { 71, 7, 0},
{ 51, 51, 51}, { 55, 47, 43}, { 55, 35, 27}, { 51, 39, 19},
{ 43, 51, 23}, { 51, 0, 0}, { 15, 31, 55}, { 67, 7, 0},
{ 47, 47, 47}, { 47, 43, 39}, { 47, 31, 27}, { 47, 35, 15},
{ 39, 47, 19}, { 47, 0, 0}, { 15, 27, 51}, { 59, 0, 0},
{ 43, 43, 43}, { 43, 39, 35}, { 43, 27, 23}, { 43, 31, 15},
{ 35, 43, 19}, { 39, 0, 0}, { 11, 23, 43}, { 51, 0, 0},
{ 39, 39, 39}, { 35, 31, 27}, { 35, 19, 15}, { 35, 27, 11},
{ 27, 35, 11}, { 35, 0, 0}, { 7, 23, 35}, { 47, 0, 0},
{ 35, 35, 35}, { 31, 27, 23}, { 31, 11, 11}, { 31, 23, 7},
{ 23, 31, 7}, { 27, 0, 0}, { 7, 19, 31}, { 35, 0, 0},
{ 27, 27, 27}, { 23, 23, 19}, { 23, 7, 7}, { 23, 19, 0},
{ 19, 23, 7}, { 23, 0, 0}, { 0, 19, 23}, { 23, 0, 0},
{ 23, 23, 23}, { 15, 15, 15}, { 19, 0, 0}, { 19, 15, 0},
{ 15, 19, 0}, { 15, 0, 0}, { 0, 15, 19}, { 15, 15, 0},
{ 15, 15, 15}, { 11, 11, 7}, { 11, 0, 0}, { 11, 11, 0},
{ 11, 11, 0}, { 11, 0, 0}, { 0, 11, 11}, { 0, 11, 0},
{ 7, 7, 7}, { 7, 7, 7}, { 7, 0, 0}, { 7, 7, 0},
{ 7, 7, 0}, { 7, 0, 0}, { 0, 7, 7}, { 0, 7, 7},
{ 0,255,255}, { 0,255,255}, { 0,255,255}, { 0,255,255},
{ 0,255,255}, { 0,255,255}, {255,251,240}, {160,160,164},
{128,128,128}, {255, 0, 0}, { 0,255, 0}, {255,255, 0},
{ 0, 0,255}, {255, 0,255}, { 0,255,255}, {255,255,255},
};
void extract_tex( uint32_t i )
{
mkdir("Textures",0755);
fseek(f,eheaders[i],SEEK_SET);
uint16_t width, height;
fread(&width,2,1,f);
fread(&height,2,1,f);
uint8_t *pxdata = malloc(width*height);
fseek(f,exports[i].dataofs,SEEK_SET);
fread(pxdata,1,width*height,f);
char fname[256];
snprintf(fname,256,"Textures/%.16s.png",exports[i].name);
writepng(fname,pxdata,width,height,&defpal[0],256);
free(pxdata);
}
void debug_printheader( uint32_t i )
{
uint8_t *header = malloc(exports[i].headersize);
fseek(f,eheaders[i],SEEK_SET);
fread(header,exports[i].headersize,1,f);
int j = 0;
printf("%.16s header (%u):\n",exports[i].name,exports[i].headersize);
for ( j=0; j<exports[i].headersize; j++ )
{
if ( !(j%8) ) printf(" ");
printf("%02x%c",header[j],((j+1)%16)?' ':'\n');
}
if ( j%16 ) printf("\n");
free(header);
}
void debug_printdata( uint32_t i )
{
uint8_t *data = malloc(exports[i].datasize);
fseek(f,exports[i].dataofs,SEEK_SET);
fread(data,exports[i].datasize,1,f);
int j = 0;
printf("%.16s data (%u):\n",exports[i].name,exports[i].datasize);
for ( j=0; j<exports[i].datasize; j++ )
{
if ( !(j%8) ) printf(" ");
printf("%02x%c",data[j],((j+1)%16)?' ':'\n');
}
if ( j%16 ) printf("\n");
free(data);
}
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 )
{
/*debug_printheader(i);
debug_printdata(i);
return*/
mkdir("Models",0755);
fseek(f,eheaders[i],SEEK_SET);
uint16_t npolys, nverts, nframes;
fread(&npolys,2,1,f);
fseek(f,2,SEEK_CUR);
fread(&nverts,2,1,f);
fseek(f,2,SEEK_CUR);
fread(&nframes,2,1,f);
char fname[256];
FILE *out;
// write datafile
snprintf(fname,256,"Models/%.16s_d.3d",exports[i].name);
out = fopen(fname,"wb");
dataheader_t dhead =
{
.numpolys = npolys,
.numverts = nverts,
};
fwrite(&dhead,sizeof(dataheader_t),1,out);
fseek(f,exports[i].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/%.16s_a.3d",exports[i].name);
out = fopen(fname,"wb");
aniheader_t ahead =
{
.numframes = nframes,
.framesize = nverts*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: u95extract <archive>\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; i<tail.nexports; i++ )
{
eheaders[i] = ftell(f);
fseek(f,exports[i].headersize,SEEK_CUR);
}
for ( int i=0; i<tail.nexports; i++ )
{
switch( exports[i].type )
{
case RES_Palette:
extract_pal(i);
break;
case RES_Texture:
extract_tex(i);
break;
case RES_Mesh:
extract_msh(i);
break;
}
}
free(names);
free(imports);
free(exports);
free(eheaders);
fclose(f);
return 0;
}

View file

@ -149,7 +149,7 @@ int32_t readimport( void )
{
readindex();
readindex();
if ( head->pkgver >= 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<fhead.texcount; i++ )
{
fhead.tex[i].texture = readindex();
fhead.tex[i].charcount = readindex();
tchars += fhead.tex[i].charcount;
fhead.tex[i].chars = calloc(fhead.tex[i].charcount,
sizeof(ufontchar_t));
for ( int j=0; j<fhead.tex[i].charcount; j++ )
@ -315,14 +317,13 @@ void savefont( int32_t namelen, char *name )
// save to text
snprintf(fname,256,"%.*s.txt",namelen,name);
f = fopen(fname,"w");
printf(" Dumping Font to %s in UTPT format\n",fname);
fprintf(f,"Character: TextureName (X,Y)-(Width,Height)\n"
"-------------------------------------------\n");
printf(" Dumping Font to %s\n",fname);
int cc = 0;
for ( int i=0; i<fhead.texcount; i++ )
for ( int j=0; j<fhead.tex[i].charcount; j++ )
{
fprintf(f,"0x%02x: ",cc);
if ( tchars > 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();

361
unrundeleter.c Normal file
View file

@ -0,0 +1,361 @@
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#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; i<head->nnames; 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 <map file>\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; i<head->nexports; 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;
}

420
usndextract.c Normal file
View file

@ -0,0 +1,420 @@
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#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; i<head->nnames; 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 <archive>\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; i<head->nexports; 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;
}

581
utxextract.c Normal file
View file

@ -0,0 +1,581 @@
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <png.h>
#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; i<head->nnames; 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; i<fh; i++ ) png_write_row(pngp,fdata+(fw*i));
}
else
{
png_set_IHDR(pngp,infp,fw,fh,8,PNG_COLOR_TYPE_RGB,
PNG_INTERLACE_NONE,PNG_COMPRESSION_TYPE_DEFAULT,
PNG_FILTER_TYPE_DEFAULT);
png_write_info(pngp,infp);
for ( int i=0; i<fh; i++ ) png_write_row(pngp,fdata+(fw*3*i));
}
png_write_end(pngp,infp);
png_destroy_write_struct(&pngp,&infp);
fclose(pf);
return 1;
}
typedef struct
{
uint8_t r, g, b, x;
} __attribute__((packed)) color_t;
void readpalette( uint32_t pal, int32_t *num, color_t **col )
{
size_t prev = fpos;
fpos = head->oexports;
for ( uint32_t i=0; i<head->nexports; 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<ncolors; i++ )
{
fpal[i].red = paldata[i].r;
fpal[i].green = paldata[i].g;
fpal[i].blue = paldata[i].b;
}
}
char fname[256];
snprintf(fname,256,"%.*s.png",namelen,name);
writepng(fname,imgdata,w,h,fpal,ncolors,masked);
}
int main( int argc, char **argv )
{
if ( argc < 2 )
{
printf("Usage: utxextract <archive>\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; i<head->nexports; 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;
}

46
wavrip.c Normal file
View file

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