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.
|
* bleep: I got bored and decided to write a pc speaker music program.
|
||||||
* ckmextract: Extracts ESP and BSA from Skyrim steam workshop mod archives.
|
* 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.
|
* ddsinfo: Shows contents of a DDS header.
|
||||||
* dood: Reads an ENDOOM lump and mirrors every word "down the middle".
|
* 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.
|
* fmod\_playbank (formerly fuck\_fmod): Tool for playback of .fsb files.
|
||||||
* fuzz: A fancy blocky noise filter using proto-AliceGL designs.
|
* fuzz: A fancy blocky noise filter using proto-AliceGL designs.
|
||||||
* glfuzz: OpenGL version of the filter.
|
* 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
|
* 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
|
* memrd/memsk/memwr: Quick 'n dirty tools for memory manipulation on running
|
||||||
programs.
|
programs.
|
||||||
* mkfont: A tool I use to convert UE fonts exported with UTPT into fonts for
|
* 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
|
* mkwall: A program I use on a daily basis to set my wallpaper on every Linux
|
||||||
machine.
|
machine.
|
||||||
* osnorm: Experiment for generating object-space normals from an .obj model.
|
* 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.
|
* pframes: Short utility for automating long FrameIndex lists for MODELDEF.
|
||||||
* schange: Program used along with mkwall to update the wallpaper on screen
|
* schange: Program used along with mkwall to update the wallpaper on screen
|
||||||
geometry changes.
|
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.
|
can be generated in bulk.
|
||||||
* totty: Sends text from stdin to tty1. Used to send certain commands when
|
* totty: Sends text from stdin to tty1. Used to send certain commands when
|
||||||
remoting into a Raspberry Pi.
|
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.
|
* 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
|
* umxunpack: Extractor for music in UE archives, with support for Unreal 227's
|
||||||
UMX files containing vorbis audio.
|
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
|
* 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.
|
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.
|
* withhands: Talk like W.D. Gaster.
|
||||||
* zfs-rootfs.patch: The original patch for archzfs to support my specific
|
* zfs-rootfs.patch: The original patch for archzfs to support my specific
|
||||||
rootfs dataset mountpoint quirks.
|
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_POSITIVEZ ) printf(" POSITIVEZ");
|
||||||
if ( head.caps[1]&DDSCAPS2_CUBEMAP_NEGATIVEZ ) printf(" NEGATIVEZ");
|
if ( head.caps[1]&DDSCAPS2_CUBEMAP_NEGATIVEZ ) printf(" NEGATIVEZ");
|
||||||
if ( head.caps[1]&DDSCAPS2_VOLUME ) printf(" VOLUME");
|
if ( head.caps[1]&DDSCAPS2_VOLUME ) printf(" VOLUME");
|
||||||
|
if ( !strncmp(head.pf_fourcc,"DX10",4) )
|
||||||
|
{
|
||||||
|
printf("\ndxgi format: %d",head10.dxgiformat);
|
||||||
|
}
|
||||||
printf("\n");
|
printf("\n");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
44
dood.c
44
dood.c
|
|
@ -1,30 +1,56 @@
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
unsigned short endoom[80*25];
|
int nspace( unsigned short ch )
|
||||||
|
|
||||||
int val( unsigned short ch )
|
|
||||||
{
|
{
|
||||||
return ((ch>0x2F)&&(ch<0x3A))||((ch>0x40)&&(ch<0x5B))
|
return ((ch>0x2F)&&(ch<0x3A))||((ch>0x40)&&(ch<0x5B))
|
||||||
||((ch>0x60)&&(ch<0x7B));
|
||((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;
|
int c = 0;
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
if ( val(endoom[c]&0xFF) )
|
if ( nspace(endoom[c]&0xFF) )
|
||||||
{
|
{
|
||||||
int s = c;
|
int s = c;
|
||||||
while ( (c++ < 80*25 ) && val(endoom[c]&0xFF) );
|
while ( (c++ < 2000 ) && nspace(endoom[c]&0xFF) );
|
||||||
int e = c;
|
int e = c;
|
||||||
for ( int i=0; i<(e-s)/2; i++ )
|
for ( int i=0; i<(e-s)/2; i++ )
|
||||||
endoom[e-i-1] = endoom[s+i];
|
endoom[e-i-1] = endoom[s+i];
|
||||||
}
|
}
|
||||||
else c++;
|
else c++;
|
||||||
}
|
}
|
||||||
while ( c < 80*25 );
|
while ( c < 2000 );
|
||||||
fwrite(endoom,sizeof(short),80*25,stdout);
|
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;
|
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);
|
printf("\tFrameIndex %.4s %c 0 %d\n",s,f,fi);
|
||||||
fi+=sk;
|
fi+=sk;
|
||||||
f++;
|
f++;
|
||||||
if ( f > 'Z' )
|
if ( f > ']' )
|
||||||
{
|
{
|
||||||
sp++;
|
sp++;
|
||||||
if ( sp == 2 )
|
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();
|
||||||
readindex();
|
readindex();
|
||||||
if ( head->pkgver >= 60 ) fpos += 4;
|
if ( head->pkgver >= 55 ) fpos += 4;
|
||||||
else readindex();
|
else readindex();
|
||||||
return readindex();
|
return readindex();
|
||||||
}
|
}
|
||||||
|
|
@ -170,7 +170,7 @@ void readimport2( int32_t *cpkg, int32_t *cname, int32_t *pkg, int32_t *name )
|
||||||
{
|
{
|
||||||
*cpkg = readindex();
|
*cpkg = readindex();
|
||||||
*cname = readindex();
|
*cname = readindex();
|
||||||
if ( head->pkgver >= 60 ) *pkg = readdword();
|
if ( head->pkgver >= 55 ) *pkg = readdword();
|
||||||
else *pkg = readindex();
|
else *pkg = readindex();
|
||||||
*name = readindex();
|
*name = readindex();
|
||||||
}
|
}
|
||||||
|
|
@ -190,7 +190,7 @@ void readexport( int32_t *class, int32_t *ofs, int32_t *siz, int32_t *name )
|
||||||
{
|
{
|
||||||
*class = readindex();
|
*class = readindex();
|
||||||
readindex();
|
readindex();
|
||||||
if ( head->pkgver >= 60 ) fpos += 4;
|
if ( head->pkgver >= 55 ) fpos += 4;
|
||||||
*name = readindex();
|
*name = readindex();
|
||||||
fpos += 4;
|
fpos += 4;
|
||||||
*siz = readindex();
|
*siz = readindex();
|
||||||
|
|
@ -213,7 +213,7 @@ void readexport2( int32_t *class, int32_t *super, int32_t *pkg, int32_t *name,
|
||||||
{
|
{
|
||||||
*class = readindex();
|
*class = readindex();
|
||||||
*super = readindex();
|
*super = readindex();
|
||||||
if ( head->pkgver >= 60 ) *pkg = readdword();
|
if ( head->pkgver >= 55 ) *pkg = readdword();
|
||||||
else *pkg = readindex();
|
else *pkg = readindex();
|
||||||
*name = readindex();
|
*name = readindex();
|
||||||
*flags = readdword();
|
*flags = readdword();
|
||||||
|
|
@ -281,7 +281,7 @@ typedef struct
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
uint8_t texcount;
|
uint32_t texcount;
|
||||||
ufonttex_t *tex;
|
ufonttex_t *tex;
|
||||||
} ufonthdr_t;
|
} ufonthdr_t;
|
||||||
|
|
||||||
|
|
@ -296,12 +296,14 @@ void savefont( int32_t namelen, char *name )
|
||||||
}
|
}
|
||||||
ufonthdr_t fhead;
|
ufonthdr_t fhead;
|
||||||
memset(&fhead,0,sizeof(ufonthdr_t));
|
memset(&fhead,0,sizeof(ufonthdr_t));
|
||||||
fhead.texcount = readbyte();
|
fhead.texcount = readindex();
|
||||||
fhead.tex = calloc(fhead.texcount,sizeof(ufonttex_t));
|
fhead.tex = calloc(fhead.texcount,sizeof(ufonttex_t));
|
||||||
|
uint32_t tchars = 0;
|
||||||
for ( int i=0; i<fhead.texcount; i++ )
|
for ( int i=0; i<fhead.texcount; i++ )
|
||||||
{
|
{
|
||||||
fhead.tex[i].texture = readindex();
|
fhead.tex[i].texture = readindex();
|
||||||
fhead.tex[i].charcount = readindex();
|
fhead.tex[i].charcount = readindex();
|
||||||
|
tchars += fhead.tex[i].charcount;
|
||||||
fhead.tex[i].chars = calloc(fhead.tex[i].charcount,
|
fhead.tex[i].chars = calloc(fhead.tex[i].charcount,
|
||||||
sizeof(ufontchar_t));
|
sizeof(ufontchar_t));
|
||||||
for ( int j=0; j<fhead.tex[i].charcount; j++ )
|
for ( int j=0; j<fhead.tex[i].charcount; j++ )
|
||||||
|
|
@ -315,14 +317,13 @@ void savefont( int32_t namelen, char *name )
|
||||||
// save to text
|
// save to text
|
||||||
snprintf(fname,256,"%.*s.txt",namelen,name);
|
snprintf(fname,256,"%.*s.txt",namelen,name);
|
||||||
f = fopen(fname,"w");
|
f = fopen(fname,"w");
|
||||||
printf(" Dumping Font to %s in UTPT format\n",fname);
|
printf(" Dumping Font to %s\n",fname);
|
||||||
fprintf(f,"Character: TextureName (X,Y)-(Width,Height)\n"
|
|
||||||
"-------------------------------------------\n");
|
|
||||||
int cc = 0;
|
int cc = 0;
|
||||||
for ( int i=0; i<fhead.texcount; i++ )
|
for ( int i=0; i<fhead.texcount; i++ )
|
||||||
for ( int j=0; j<fhead.tex[i].charcount; j++ )
|
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);
|
construct_fullname(f,fhead.tex[i].texture);
|
||||||
fprintf(f," (%d,%d)-(%d,%d)\n",fhead.tex[i].chars[j].x,
|
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,
|
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
|
// begin reading data
|
||||||
size_t prev = fpos;
|
size_t prev = fpos;
|
||||||
fpos = ofs;
|
fpos = ofs;
|
||||||
if ( head->pkgver < 40 ) fpos += 8;
|
if ( head->pkgver < 45 ) fpos += 4;
|
||||||
if ( head->pkgver < 60 ) fpos += 16;
|
if ( head->pkgver < 55 ) fpos += 16;
|
||||||
|
if ( head->pkgver <= 44 ) fpos -= 6; // ???
|
||||||
|
if ( head->pkgver <= 35 ) fpos += 8; // ???
|
||||||
int32_t prop = readindex();
|
int32_t prop = readindex();
|
||||||
if ( (uint32_t)prop >= head->nnames )
|
if ( (uint32_t)prop >= head->nnames )
|
||||||
{
|
{
|
||||||
|
|
@ -427,6 +430,8 @@ retry:
|
||||||
if ( strncasecmp(pname,"none",l) )
|
if ( strncasecmp(pname,"none",l) )
|
||||||
{
|
{
|
||||||
uint8_t info = readbyte();
|
uint8_t info = readbyte();
|
||||||
|
int array = info&0x80;
|
||||||
|
int type = info&0xf;
|
||||||
int psiz = (info>>4)&0x7;
|
int psiz = (info>>4)&0x7;
|
||||||
switch ( psiz )
|
switch ( psiz )
|
||||||
{
|
{
|
||||||
|
|
@ -455,6 +460,10 @@ retry:
|
||||||
psiz = readdword();
|
psiz = readdword();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
if ( array && (type != 3) )
|
||||||
|
readindex();
|
||||||
|
if ( type == 10 )
|
||||||
|
readindex(); // skip struct name
|
||||||
fpos += psiz;
|
fpos += psiz;
|
||||||
printf(" Skipping property %.*s\n",l,pname);
|
printf(" Skipping property %.*s\n",l,pname);
|
||||||
prop = readindex();
|
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