Wow I haven't updated this in a long time.
This commit is contained in:
parent
66d9eb0629
commit
55010c8b48
15 changed files with 3980 additions and 23 deletions
22
README.md
22
README.md
|
|
@ -2,13 +2,22 @@ Random single-file programs I've written in my spare time for small tasks.
|
|||
|
||||
* bleep: I got bored and decided to write a pc speaker music program.
|
||||
* ckmextract: Extracts ESP and BSA from Skyrim steam workshop mod archives.
|
||||
* cube2enviro: A simple GL 4.4 program. Loads a cubemap and draws a flattened
|
||||
hemisphere environment map that can be used in Unreal.
|
||||
* ddsinfo: Shows contents of a DDS header.
|
||||
* dood: Reads an ENDOOM lump and mirrors every word "down the middle".
|
||||
* dtexdupes: Small tool I've used once or twice to clean up my doom projects of
|
||||
duplicate textures.
|
||||
* fmod\_playbank (formerly fuck\_fmod): Tool for playback of .fsb files.
|
||||
* fuzz: A fancy blocky noise filter using proto-AliceGL designs.
|
||||
* glfuzz: OpenGL version of the filter.
|
||||
* iwad64ex: A small, failed experiment for ripping the Hexen 64 IWAD.
|
||||
* lutconv: A program for converting various "3D" LUT textures to actual 3D LUTs
|
||||
in DDS volume maps. Successor to mkvolume. Used for MariENB.
|
||||
in DDS volume maps. Successor to mkvolume. Used for MariENB. Plus two
|
||||
additional tools for "deconverting" volume maps, and one for smoothing them
|
||||
out to reduce potential banding.
|
||||
* mazestuff: A dungeon generator for roguelikes. This was made as part of a
|
||||
commission for a friend, hence the very detailed comments.
|
||||
* memrd/memsk/memwr: Quick 'n dirty tools for memory manipulation on running
|
||||
programs.
|
||||
* mkfont: A tool I use to convert UE fonts exported with UTPT into fonts for
|
||||
|
|
@ -22,6 +31,7 @@ Random single-file programs I've written in my spare time for small tasks.
|
|||
* mkwall: A program I use on a daily basis to set my wallpaper on every Linux
|
||||
machine.
|
||||
* osnorm: Experiment for generating object-space normals from an .obj model.
|
||||
* pcxpalex.c: Extracts the palette from PCX images.
|
||||
* pframes: Short utility for automating long FrameIndex lists for MODELDEF.
|
||||
* schange: Program used along with mkwall to update the wallpaper on screen
|
||||
geometry changes.
|
||||
|
|
@ -30,11 +40,21 @@ Random single-file programs I've written in my spare time for small tasks.
|
|||
can be generated in bulk.
|
||||
* totty: Sends text from stdin to tty1. Used to send certain commands when
|
||||
remoting into a Raspberry Pi.
|
||||
* u95/u083/u086extract: Programs for extracting data from Unreal alpha
|
||||
packages. This and other Unreal tools might be shifted to another repo.
|
||||
* udmfvis: dmvis clone in C for UDMF maps. No external dependencies.
|
||||
* ufontext: companion to mkfont, for extracting UE fonts. Currently does not
|
||||
yet extract the textures themselves.
|
||||
* umxunpack: Extractor for music in UE archives, with support for Unreal 227's
|
||||
UMX files containing vorbis audio.
|
||||
* unrundeleter: WIP program to unset the bDeleteMe flag on stuff in UE1 maps.
|
||||
Yes, some mappers are so hellbent on preventing modification that they delete
|
||||
all brushes after baking the geometry.
|
||||
* usndextract: Extracts sounds from UE archives.
|
||||
* utxextract: Extracts textures from UE archives.
|
||||
* vc2sdl: Passes the contents of the VC4 framebuffer to a SDL window. Was used
|
||||
for video playback experiments on a Raspberry Pi with a SPI LCD.
|
||||
* wavrip: Cheap WAV file extractor that naively searchs for RIFF headers.
|
||||
* withhands: Talk like W.D. Gaster.
|
||||
* zfs-rootfs.patch: The original patch for archzfs to support my specific
|
||||
rootfs dataset mountpoint quirks.
|
||||
|
|
|
|||
220
cube2enviro.c
Normal file
220
cube2enviro.c
Normal 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;
|
||||
}
|
||||
|
||||
|
|
@ -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
44
dood.c
|
|
@ -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
522
mazestuff.c
Normal 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
25
pcxpalex.c
Normal 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;
|
||||
}
|
||||
|
|
@ -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
889
u083extract.c
Normal 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
404
u086extract.c
Normal 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
430
u95extract.c
Normal 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;
|
||||
}
|
||||
33
ufontext.c
33
ufontext.c
|
|
@ -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
361
unrundeleter.c
Normal 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
420
usndextract.c
Normal 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
581
utxextract.c
Normal 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
46
wavrip.c
Normal 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;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue