Compare commits

..

No commits in common. "c91968ca4310b17830dde9c9a72b29218ffaf3ad" and "955819c03b8c8327fbf174c845dc2a78db6ed9db" have entirely different histories.

15 changed files with 934 additions and 3244 deletions

View file

@ -1,4 +1,4 @@
Copyright (c) 2015-2023 Marisa the Magician, UnSX Team
Copyright (c) 2020-2022 Marisa the Magician, UnSX Team
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View file

@ -1,6 +1,4 @@
Random small programs I've written in my spare time for small tasks.
Some of these are unfinished, but I keep the code here anyway.
Makefiles or other build scripts are not provided, so you are on your own.
Random single-file programs I've written in my spare time for small tasks.
* **bleep:** I got bored and decided to write a pc speaker music program.
* **ckmextract:** Extracts ESP and BSA from Skyrim steam workshop mod archives.
@ -10,50 +8,45 @@ Makefiles or other build scripts are not provided, so you are on your own.
* **dood:** Reads an ENDOOM lump and mirrors every word "down the middle".
* **dtexdupes:** Small tool I've used once or twice to clean up my doom projects of duplicate textures.
* **endoomview:** Renders ENDOOM lumps onto a terminal. Requires an Unicode terminal with 256-color support (not xterm, basically).
* **fmod\_playbank (formerly fuck\_fmod):** Tool for playback of .fsb files.
* **fontatlas:** Tool used in Demolitionist development, converts json font atlas data to plaintext for easier ZScript parsing.
* **fontspread:** Tool used in Demolitionist development, converts back and forth between "compressed" font atlas texture and a linear spread of all glyphs. Useful for adding gradient effects and others, since all glyphs are properly centered.
* **fmod\_playbank (formerly fuck\_fmod):** Tool for playback of .fsb files.
* **fuzrip:** Tool for awkwardly extracting audio from Creation Engine .fuz voice files.
* **fuzz:** A fancy blocky noise filter using proto-AliceGL designs.
* **glfuzz:** OpenGL version of the filter. A Vulkan version might be made at some point.
* **grabpng:** (TODO) Batch processing of grAb chunks in PNG files.
* **glfuzz:** OpenGL version of the filter.
* **iwad64ex:** A small, failed experiment for ripping the Hexen 64 IWAD.
* **lutconv2/lutflat/lutsmooth:** A program for converting various "3D" LUT textures to actual 3D LUTs 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.
* **lutconv:** A program for converting various "3D" LUT textures to actual 3D LUTs in DDS volume maps. Successor to mkvolume. Used for MariENB. Plus two additional tools for "deconverting" volume maps, and one for smoothing them out to reduce potential banding.
* **mazestuff:** A dungeon generator for roguelikes. This was made as part of a commission for a friend, hence the very detailed comments.
* **memrd/memsk/memwr:** Quick 'n dirty tools for memory manipulation on running programs.
* **mkbmfont:** Adaptation of the next tool for use in Godot. Creates a font atlas in BMFont format (either text or binary, depending on compile-time options).
* **mkfont:** A tool originally written for the Demolitionist project. Creates GZDoom fonts from any given font, with built-in gradients and drop shadows.
* **mkfont_unr:** A tool I use to convert UE1 fonts exported with UTPT into fonts for GZDoom. Requires ImageMagick to be installed.
* **mkfont_unr:** A tool I use to convert UE fonts exported with UTPT into fonts for GZDoom. Requires ImageMagick to be installed.
* **mkgauss:** Make an array of gaussian blur kernel values with passed radius and sigma. Used for shader development.
* **mksoundwad:** Program used during the early days of Tim Allen Doom. Deprecated as notsanae now also replaces sounds through an OpenAL hook.
* **mkssao:** Make an array of SSAO samples. Also for shader development.
* **mkstartup:** Tool for creating Hexen startup screen graphics. Originally for Demolitionist, now deprecated since full-color graphics can be used.
* **mkvolume:** Old program for making LUT volume maps.
* **mkwall:** A program I used on a daily basis to set my wallpaper on every Linux machine. Currently abandoned, but might be eventually updated w/ Wayland support.
* **mkwall:** A program I use on a daily basis to set my wallpaper on every Linux machine.
* **osnorm:** Experiment for generating object-space normals from an .obj model.
* **pcxpalex:** Extracts the palette from PCX images.
* **pframes:** Short utility for automating long FrameIndex lists for MODELDEF.
* **pframes_named:** Same tool but for IQM models, where the Frame directive can be used to pair action names and frame indices.
* **pngread:** Unfinished program for debugging PNG chunks.
* **schange:** Program used along with mkwall to update the wallpaper on screen geometry changes. No longer used.
* **schange:** Program used along with mkwall to update the wallpaper on screen geometry changes.
* **skse_cosave:** Experiment for dumping information in SKSE co-saves.
* **soapstone:** Random soapstone messages from all 3 dark souls games. Messages can be generated in bulk.
* **startuptest:** Tool for showing Hexen-style startup screens (Used for testing until GZDoom eventually supported these on Linux).
* **startuptest:** Tool for showing Hexen-style startup screens (something GZDoom can't do on Linux yet).
* **totty:** Sends text from stdin to tty1. Used to send certain commands when remoting into a Raspberry Pi.
* **u95/u083/u086extract:** Programs for extracting data from Unreal alpha packages. This and other Unreal tools might be shifted to another repo.
* **ucextract:** Extracts UnrealScript source files from Unreal packages. This tool is not yet functional.
* **udmfvis:** dmvis clone in C for UDMF maps. No external dependencies.
* **ufontext:** companion to mkfont, for extracting UE1 fonts. Currently does not yet extract the textures themselves.
* **uftxextract:** Extracts FireTextures from UE1 archives, converted to a format that can be parsed by [my ZScript library](https://git.marisa.pw/OrdinaryMagician/firetexture_m).
* **ufontext:** companion to mkfont, for extracting UE fonts. Currently does not yet extract the textures themselves.
* **umodextract:** .umod archive extractor (sort of).
* **umxunpack:** Extractor for music in UE1 archives, with support for Unreal 227's UMX files containing vorbis audio.
* **umxunpack:** Extractor for music in UE archives, with support for Unreal 227's UMX files containing vorbis audio.
* **unrundeleter:** WIP program to unset the bDeleteMe flag on stuff in UE1 maps. Yes, some mappers are so hellbent on preventing modification that they delete all brushes after baking the geometry.
* **usndextract:** Extracts sounds from UE1 archives.
* **utxextract:** Extracts textures from UE1 archives.
* **usndextract:** Extracts sounds from UE archives.
* **utxextract:** Extracts textures from UE archives.
* **vc2sdl:** Passes the contents of the VC4 framebuffer to a SDL window. Was used for video playback experiments on a Raspberry Pi with a SPI LCD.
* **wavrip:** Cheap WAV file extractor that naively searches for RIFF headers.
* **wavrip:** Cheap WAV file extractor that naively searchs for RIFF headers.
* **wavsmpl:** Extracts loop information from WAV files that have it, because only certain proprietary audio editors support this directly.
* **withhands:** Talk like W.D. Gaster. Done as a bit ages ago just to tease Undertale streamers.
* **withhands:** Talk like W.D. Gaster.
* **zimagekver:** Quick program to extract version info from an ARM Linux kernel image.
All programs and code here are under the MIT license.

View file

@ -286,17 +286,9 @@ void processpic( const char *fname )
if ( trns ) free(trns);
return;
}
uint8_t *odata = calloc(cropw*croph,pxsize);
uint32_t ow = cropw, oh = croph;
int32_t ox = x-cropx, oy = y-cropy;
if ( (ow == w) && (oh == h) && (ox == x) && (oy == y) )
{
printf(" Nothing to crop, skipping.\n");
free(idata);
if ( plte ) free(plte);
if ( trns ) free(trns);
return;
}
uint8_t *odata = calloc(cropw*croph,pxsize);
for ( uint32_t cy=0; cy<croph; cy++ )
{
uint32_t ccy = cy+cropy;

View file

@ -1 +0,0 @@
// TODO cheap fast drop-in replacement for the old grabpng tool

View file

@ -1,802 +0,0 @@
/*
mkbmfont.c : Like mkfont but the output is in BMFont format.
The code is rather messy, since it's a tool meant for personal use,
apologies in advance.
Copyright (c) 2026 Marisa the Magician, UnSX Team
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/*
Known bugs/limitations/quirks:
- Unlike my old mkfont tool, proportional fonts are handled just fine,
and non-bitmap font handling "just works". Unicode blocks beyond the
BMP are also covered too, funny enough.
- Packing algorithm for the texture atlas is very naive and may not
offer the finest density, but at least it's fast and dead simple to
implement in plain C.
- Does not recognize visually identical glyphs that share the same code
point, so there will be potential duplicates in the atlas.
- Compiling with -DASCII_ONLY is required if you want to use this with
Godot, as it doesn't support unicode bitmap fonts for some reason.
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <errno.h>
#include <png.h>
#include <ft2build.h>
#include FT_FREETYPE_H
typedef struct
{
int16_t fontSize;
uint8_t bitField;
uint8_t charSet;
uint16_t stretchH;
uint8_t aa, paddingUp, paddingRight, paddingDown, paddingLeft, spacingHoriz, spacingVert, outline;
} __attribute__((packed)) bmfinfo_t;
typedef struct
{
uint16_t lineHeight, base, scaleW, scaleH, pages;
uint8_t bitField, alphaChnl, redChnl, greenChnl, blueChnl;
} __attribute__((packed)) bmfcommon_t;
typedef struct
{
uint32_t id;
uint16_t x, y, width, height;
int16_t xoffset, yoffset, xadvance;
uint8_t page, chnl;
} __attribute__((packed)) bmfchar_t;
typedef struct
{
uint32_t unicode, glyph;
uint16_t x, y, w, h;
int16_t xofs, yofs, xadv;
uint8_t page, packed;
// channels not needed (always defaults to 15)
} packglyph_t;
// BEGIN naive packing algo
static int cmpglyph( const void *p1, const void *p2 )
{
// cast for convenience
packglyph_t *a = (packglyph_t*)p1,
*b = (packglyph_t*)p2;
// check which is taller first
int hdiff = b->h-a->h;
if ( hdiff ) return hdiff;
// if equal height, check which is wider
int wdiff = b->w-a->w;
if ( wdiff ) return wdiff;
// equal height and width, just sort by unicode value
return a->unicode-b->unicode;
}
static void nextbox( int* boxw, int* boxh )
{
// increase width before height
if ( *boxw > *boxh ) *boxh <<= 1;
else *boxw <<= 1;
}
// some sane defaults here
#ifndef PACK_TRIES
#define PACK_TRIES 100
#endif
#ifndef MAX_PACK_SIDE
#define MAX_PACK_SIDE 1024
#endif
#ifndef MAX_PAGES
#define MAX_PAGES 36
#endif
int packglyphs( packglyph_t *glyphs, uint32_t nglyphs, int* boxw, int* boxh )
{
// sort the glyphs first
qsort(glyphs,nglyphs,sizeof(packglyph_t),cmpglyph);
int pageno = 0;
size_t packed = 0;
for ( int i=0; i<PACK_TRIES; i++ )
{
int x = 0;
int y = 0;
int lh = 0;
for ( uint32_t j=0; j<nglyphs; j++ )
{
if ( glyphs[j].packed ) continue;
// past box width, next row
if ( (x+glyphs[j].w+1) > *boxw )
{
y += lh;
x = 0;
lh = 0;
}
// past box height, bail out
if ( y+glyphs[j].h+1 > *boxh ) break;
// otherwise pack it and advance
glyphs[j].x = x;
glyphs[j].y = y;
x += glyphs[j].w+1;
// save tallest in row
if ( (glyphs[j].h+1) > lh ) lh = glyphs[j].h+1;
glyphs[j].page = pageno;
glyphs[j].packed = 1;
packed++;
}
if ( packed == nglyphs ) return 0;
// box is already at size limit, make a new page
if ( (*boxw >= MAX_PACK_SIDE) && (*boxh >= MAX_PACK_SIDE) )
{
pageno++;
// if we're past the page limit, fail early
if ( pageno >= MAX_PAGES ) return 2;
continue;
}
// try again from scratch with a larger box
packed = 0;
for ( uint32_t j=0; j<nglyphs; j++ )
glyphs[j].packed = 0;
nextbox(boxw,boxh);
}
// failed to pack
return 1;
}
// what could be improved: for optimal column coverage, we could keep looping
// until we find a glyph whose width fits the space before moving on to the
// next row. though this would require storing a bit more data inside the
// struct itself, and would increase the complexity of the inner loop
// (potentially taking even longer to execute, especially if multiple retries
// are needed until we find the optimal box size)
// END naive packing algo
uint8_t pal[768];
int palsize = 0;
int aw = 64, ah = 64; // generous default?
uint8_t *adata = 0;
// dead simple png writing, with raw data, width, height and row pitch
int writepng( const char *filename, uint8_t *fdata, int w, int h, int p )
{
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);
png_set_IHDR(pngp,infp,w,h,8,PNG_COLOR_TYPE_RGBA,
PNG_INTERLACE_NONE,PNG_COMPRESSION_TYPE_DEFAULT,
PNG_FILTER_TYPE_DEFAULT);
png_write_info(pngp,infp);
for ( int i=0; i<h; i++ ) png_write_row(pngp,fdata+(p*i));
png_write_end(pngp,infp);
png_destroy_write_struct(&pngp,&infp);
fclose(pf);
return 1;
}
FT_Library ftlib;
FT_Face fnt;
int lh, bl;
int w = 0, h = 0;
uint8_t *idata = 0;
int calcsize = 1;
int gradient = 0;
int gw = 0, gh = 0, gx = 0, gy = 0;
// dead simple, we don't need to check if we draw out of bounds or anything
// as those are guaranteed to never be the case once glyphs are successfully
// packed into the atlas texture
int oob = 0;
void blitglyph( packglyph_t *g )
{
if ( (g->x < 0) || (g->x >= (aw-g->w)) || (g->y < 0) || (g->y >= (ah-g->h)) )
{
oob = 1;
return;
}
uint8_t *gpos = idata;
uint8_t *apos = adata+(g->x+g->y*aw)*4;
for ( int i=0; i<(g->h); i++ )
{
memcpy(apos,gpos,(g->w)*4);
gpos += w*4;
apos += aw*4;
}
}
void putpixel_grayscale( uint8_t v, uint8_t a, int x, int y )
{
uint32_t tpos = (x+y*w)*4;
// add alpha
int alph = idata[tpos+3];
alph += a;
if ( alph > 255 ) alph = 255;
idata[tpos+3] = alph;
// blend color
int col = idata[tpos]*(a-255);
col += v*a;
col /= 255;
idata[tpos] = col;
idata[tpos+1] = col;
idata[tpos+2] = col;
}
void putpixel_color( uint8_t v, uint8_t a, int x, int y )
{
uint32_t tpos = (x+y*w)*4;
// add alpha
int alph = idata[tpos+3];
alph += a;
if ( alph > 255 ) alph = 255;
idata[tpos+3] = alph;
// blend color (RGB)
for ( int i=0; i<3; i++ )
{
int col = idata[tpos+i]*(a-255);
int palent = (v*palsize)/256;
col += pal[palent*3+i]*a;
col /= 255;
idata[tpos+i] = col;
}
}
void putpixel( uint8_t v, uint8_t a, int x, int y )
{
if ( (x < 0) || (x >= w) || (y < 0) || (y >= h) )
{
oob = 1;
return;
}
if ( palsize == 0 ) putpixel_grayscale(v,a,x,y);
else putpixel_color(v,a,x,y);
}
uint8_t lerpg( float a )
{
if ( a >= 1. ) return 255;
if ( a <= 0. ) return 64;
return (uint8_t)(a*191+64);
}
int draw_glyph( FT_Bitmap *bmp, uint8_t v, int px, int py, int oy )
{
if ( !bmp->buffer ) return 0;
int drawn = 0;
unsigned i, j;
for ( j=0; j<bmp->rows; j++ )
{
uint8_t rv = v;
// apply gradient, if any
if ( v == 255 )
{
float a;
int ofs = j+oy;
if ( ofs < 0 ) a = 0.;
else a = ofs/(float)(h-gh);
if ( (gradient&3) == 1 ) rv = lerpg(1.-a);
else if ( (gradient&3) == 2 ) rv = lerpg(a);
else if ( (gradient&3) == 3 ) rv = lerpg((a>.5)?((1.-a)*2.):(a*2.));
}
for ( i=0; i<bmp->width; i++ )
{
if ( bmp->pixel_mode == FT_PIXEL_MODE_GRAY )
{
uint8_t a = bmp->buffer[i+j*bmp->pitch];
drawn |= (a > 0);
putpixel(rv,a,i+px,j+py);
}
else if ( bmp->pixel_mode == FT_PIXEL_MODE_MONO )
{
// thanks to https://stackoverflow.com/a/14905971
unsigned p = bmp->pitch;
uint8_t *row = &bmp->buffer[p*j];
uint8_t a = ((row[i>>3])&(128>>(i&7)))?255:0;
drawn |= (a > 0);
putpixel(rv,a,i+px,j+py);
}
}
}
return drawn;
}
int palinv = 0;
void loadpalette( const char *path )
{
FILE *f = fopen(path,"rb");
if ( !f )
{
fprintf(stderr,"warning: could not open palette file '%s', falling back to grayscale\n",path);
return;
}
fseek(f,0,SEEK_END);
long sz = ftell(f);
fseek(f,0,SEEK_SET);
if ( sz <= 0 )
{
fprintf(stderr,"warning: palette is empty, falling back to grayscale\n");
goto palend;
}
if ( !(sz%3) )
{
// RGB8 palette
if ( sz > 768 )
{
fprintf(stderr,"warning: palette has more than 256 entries, extra colors will be ignored\n");
palsize = 256;
}
else palsize = sz/3;
for ( int i=0; i<palsize; i++ )
{
int j = palinv?(palsize-(i+1)):i;
pal[j*3] = fgetc(f);
pal[j*3+1] = fgetc(f);
pal[j*3+2] = fgetc(f);
}
fprintf(stderr,"info: RGB8 palette loaded with %d colors.\n",palsize);
}
else if ( !(sz%4) )
{
// RGBA8 palette
if ( sz > 1024 )
{
fprintf(stderr,"warning: palette has more than 256 entries, extra colors will be ignored\n");
palsize = 256;
}
else palsize = sz/4;
for ( int i=0; i<palsize; i++ )
{
int j = palinv?(palsize-(i+1)):i;
pal[j*3] = fgetc(f);
pal[j*3+1] = fgetc(f);
pal[j*3+2] = fgetc(f);
fgetc(f); // skip alpha
}
fprintf(stderr,"info: RGBA8 palette loaded with %d colors (alpha will be ignored).\n",palsize);
}
else fprintf(stderr,"warning: palette is in an unsupported format, falling back to grayscale\n");
palend:
fclose(f);
}
const char grads[12][20] =
{
"Flat (Shadow)",
"Top-Bottom (Shadow)",
"Bottom-Top (Shadow)",
"Centered (Shadow)",
"Flat (Border)",
"Top-Bottom (Border)",
"Bottom-Top (Border)",
"Centered (Border)",
"Flat",
"Top-Bottom",
"Bottom-Top",
"Centered"
};
// grad stuff: shadow adds 1 to glyph width and height
// border adds 2 to glyph width and height
// and subtracts 1 from glyph x/y offsets
// ez stuff...
#define LOADFLAGS FT_LOAD_PEDANTIC
#define RENDERMODE FT_RENDER_MODE_NORMAL
#ifdef ASCII_ONLY
// only 00-FF, which Godot should expect for bitmap fonts
#define MAX_GLYPHS 256
#define MAX_UNICODE 0xFF
#else
// values as of Unicode 17.0
#define MAX_GLYPHS 303808
#define MAX_UNICODE 0x10FFFF
#endif
static int cmpglyph_final( const void *p1, const void *p2 )
{
// cast for convenience
packglyph_t *a = (packglyph_t*)p1,
*b = (packglyph_t*)p2;
// sort from lowest to highest page number first
if ( a->page != b->page )
return a->page-b->page;
// sort from lowest to highest unicode value second
return a->unicode-b->unicode;
}
// used for naming a grand total of 36 potential pages
char base36( uint8_t val )
{
if ( val < 10 ) return '0'+val;
else return 'A'+(val-10);
}
int main( int argc, char **argv )
{
if ( argc < 4 )
{
fprintf(stderr,"usage: mkbmfont <font name>[:face index] <pxsize> <bmfont name>"
" [gradient type (0,11)] [color palette] [-palinv]\n");
return 1;
}
if ( FT_Init_FreeType(&ftlib) )
{
fprintf(stderr,"error: failed to init freetype library\n");
return 2;
}
int pxsiz;
sscanf(argv[2],"%d",&pxsiz);
char bmname[256], pname[256];
snprintf(bmname,256,"%s.fnt",argv[3]);
if ( argc > 4 )
{
sscanf(argv[4],"%d",&gradient);
if ( (gradient < 0) || (gradient >= 12) )
{
fprintf(stderr,"warning: gradient type out of range, falling back to type 0.\n");
gradient = 0;
}
}
if ( !(gradient&8) )
{
gw++;
gh++;
if ( gradient&4 )
{
gx--;
gy--;
gw++;
gh++;
}
}
fprintf(stderr,"info: gradient selected is '%s'.\n",grads[gradient]);
if ( argc > 6 ) palinv = !strcmp(argv[6],"-palinv");
if ( argc > 5 ) loadpalette(argv[5]);
char *fidx = strrchr(argv[1],':');
int fnum = 0;
if ( fidx )
{
*fidx = 0;
sscanf(fidx+1,"%d",&fnum);
}
if ( FT_New_Face(ftlib,argv[1],fnum,&fnt) )
{
if ( fidx ) fprintf(stderr,"error: failed to open font '%s' face %d.\n",argv[1],fnum);
else fprintf(stderr,"error: failed to open font '%s'.\n",argv[1]);
return 4;
}
fprintf(stderr,"info: loaded font \'%s %s\'.\n",fnt->family_name,fnt->style_name);
int nfnt = fnt->num_faces;
if ( !fidx && (nfnt > 1) )
{
fprintf(stderr,"info: loaded font has %d faces, you may want to load a specific one (see below).\n",nfnt);
FT_Done_Face(fnt);
for ( int i=0; i<nfnt; i++ )
{
if ( FT_New_Face(ftlib,argv[1],i,&fnt) )
{
fprintf(stderr,"error: failed to open font '%s' face %d. Skipping.\n",argv[1],i);
continue;
}
fprintf(stderr,"info: face %d: \'%s %s\'.\n",i,fnt->family_name,fnt->style_name);
FT_Done_Face(fnt);
}
FT_Done_FreeType(ftlib);
return 0;
}
if ( FT_Set_Pixel_Sizes(fnt,0,pxsiz) )
{
if ( fnt->num_fixed_sizes <= 0 )
{
fprintf(stderr,"error: font pixel size of '%d' not available.\n",pxsiz);
return 8;
}
fprintf(stderr,"warning: failed to set pixel size '%d', trying fixed sizes.\n",pxsiz);
int match = -1;
for ( int i=0; i<fnt->num_fixed_sizes; i++ )
{
if ( fnt->available_sizes[i].height == pxsiz ) continue;
match = i;
break;
}
if ( (match == -1) || FT_Select_Size(fnt,match) )
{
fprintf(stderr,"error: font fixed size of '%d' not available\n",pxsiz);
fprintf(stderr,"available size(s): ");
for ( int i=0; i<fnt->num_fixed_sizes; i++ )
fprintf(stderr,"%u%s",fnt->available_sizes[i].height,(i<(fnt->num_fixed_sizes-1))?", ":".\n");
FT_Done_Face(fnt);
FT_Done_FreeType(ftlib);
return 8;
}
}
lh = (fnt->size->metrics.height>>6);
FT_Select_Charmap(fnt,FT_ENCODING_UNICODE);
// pre-allocate
uint32_t nglyphs = MAX_GLYPHS;
packglyph_t* glyphs = calloc(nglyphs,sizeof(packglyph_t));
uint32_t cur = 0;
// count up and populate valid glyphs
for ( uint32_t i=0; i<=MAX_UNICODE; i++ )
{
FT_UInt glyph = FT_Get_Char_Index(fnt,i);
if ( !glyph || FT_Load_Glyph(fnt,glyph,LOADFLAGS) ) continue;
FT_Render_Glyph(fnt->glyph,RENDERMODE);
glyphs[cur].unicode = i;
glyphs[cur].glyph = glyph;
glyphs[cur].x = 0;
glyphs[cur].y = 0;
if ( !fnt->glyph->bitmap.buffer )
{
glyphs[cur].w = 0;
glyphs[cur].h = 0;
glyphs[cur].xofs = 0;
glyphs[cur].yofs = 0;
}
else
{
glyphs[cur].w = fnt->glyph->bitmap.width+gw;
glyphs[cur].h = fnt->glyph->bitmap.rows+gh;
glyphs[cur].xofs = fnt->glyph->bitmap_left+gx;
glyphs[cur].yofs = (-fnt->glyph->bitmap_top)+gy;
}
glyphs[cur].xadv = fnt->glyph->advance.x>>6;
// calculate baseline using 'n' as reference
if ( i == 'n' ) bl = pxsiz-(-fnt->glyph->bitmap_top+fnt->glyph->bitmap.rows);
cur++;
if ( cur > nglyphs )
{
fprintf(stderr,"warning: font somehow covers more code points than expected\n");
break;
}
}
if ( cur <= 0 )
{
fprintf(stderr,"error: no valid glyphs in font.\n");
FT_Done_Face(fnt);
FT_Done_FreeType(ftlib);
return 16;
}
// reduce array size if smaller, for convenience
if ( nglyphs > cur )
{
nglyphs = cur;
glyphs = realloc(glyphs,nglyphs*sizeof(packglyph_t));
}
// update offsets to account for baseline
for ( uint32_t i=0; i<nglyphs; i++ )
{
// skip glyphs with no visual
if ( !(glyphs[i].w|glyphs[i].h) ) continue;
glyphs[i].yofs += bl;
}
fprintf(stderr,"info: total glyphs is %u.\n",nglyphs);
// second pass, compute maximum glyph bitmap size, then allocate the drawing data
for ( uint32_t i=0; i<nglyphs; i++ )
{
if ( glyphs[i].w > w ) w = glyphs[i].w;
if ( glyphs[i].h > h ) h = glyphs[i].h;
}
fprintf(stderr,"info: max cell size is %dx%d.\n",w,h);
idata = calloc(4,w*h);
// third pass, pack, allocate atlas, then draw
int retval = packglyphs(glyphs,nglyphs,&aw,&ah);
if ( retval )
{
char errormsg[][32] = {"max tries hit","max pages hit"};
fprintf(stderr,"error: failed to pack glyphs into an adequate atlas: %s.\n",errormsg[retval-1]);
free(idata);
FT_Done_Face(fnt);
FT_Done_FreeType(ftlib);
return 32;
}
// re-sort
qsort(glyphs,nglyphs,sizeof(packglyph_t),cmpglyph_final);
adata = calloc(4,aw*ah);
int npages = 1;
for ( uint32_t i=0; i<nglyphs; i++ )
{
if ( !glyphs[i].glyph || FT_Load_Glyph(fnt,glyphs[i].glyph,LOADFLAGS) ) continue;
FT_Render_Glyph(fnt->glyph,RENDERMODE);
if ( !fnt->glyph->bitmap.buffer ) continue;
int oy = (h-gh) - (2+fnt->glyph->bitmap_top);
if ( gradient&8 )
draw_glyph(&fnt->glyph->bitmap,255,0,0,oy);
else if ( gradient&4 )
{
// outline first
for ( int x=-1; x<=1; x++ )
for ( int y=-1; y<=1; y++ )
{
// ignore center
if ( !(x|y) ) continue;
draw_glyph(&fnt->glyph->bitmap,0,1+x,1+y,oy);
}
draw_glyph(&fnt->glyph->bitmap,255,1,1,oy);
}
else
{
// drop shadow first
draw_glyph(&fnt->glyph->bitmap,0,1,1,oy);
draw_glyph(&fnt->glyph->bitmap,255,0,0,oy);
}
// are we switching pages?
if ( glyphs[i].page >= npages )
{
snprintf(pname,256,"%s%c.png",argv[3],base36(npages-1));
writepng(pname,adata,aw,ah,aw*4);
memset(adata,0,aw*ah*4);
npages++;
}
// blit to canvas
blitglyph(glyphs+i);
// clear for next draw
memset(idata,0,w*h*4);
}
fprintf(stderr,"info: texture atlas size is %ux%u, with %d page(s).\n",aw,ah,npages);
free(idata);
if ( npages == 1 ) snprintf(pname,256,"%s.png",argv[3]);
else snprintf(pname,256,"%s%c.png",argv[3],base36(npages-1));
writepng(pname,adata,aw,ah,aw*4);
// don't forget to write the bmfont file
#ifdef USE_BINARY_BMFONT
FILE *bmf = fopen(bmname,"wb");
if ( !bmf )
{
fprintf(stderr,"error: failed to open '%s' for writing: %s\n",bmname,strerror(errno));
free(adata);
FT_Done_Face(fnt);
FT_Done_FreeType(ftlib);
return 64;
}
uint32_t bsiz = 0;
// header
fputc(66,bmf);
fputc(77,bmf);
fputc(70,bmf);
fputc(3,bmf);
// info
char fontName[256];
snprintf(fontName,256,"%s %s",fnt->family_name,fnt->style_name);
bmfinfo_t info = { pxsiz, 2, 0, 100, 1, 0, 0, 0, 0, 1, 1, 0 };
bsiz = sizeof(bmfinfo_t)+strlen(fontName)+1;
fputc(1,bmf);
fwrite(&bsiz,4,1,bmf);
fwrite(&info,sizeof(bmfinfo_t),1,bmf);
fwrite(fontName,strlen(fontName),1,bmf);
fputc(0,bmf);
// common
bmfcommon_t cmn = { lh, bl, aw, ah, npages, 0, 0, 4, 4, 4 };
bsiz = sizeof(bmfcommon_t);
fputc(2,bmf);
fwrite(&bsiz,4,1,bmf);
fwrite(&cmn,sizeof(bmfcommon_t),1,bmf);
// pages
fputc(3,bmf);
if ( npages == 1 )
{
snprintf(pname,256,"%s.png",argv[3]);
bsiz = strlen(pname)+1;
fwrite(&bsiz,4,1,bmf);
fwrite(pname,strlen(pname),1,bmf);
fputc(0,bmf);
}
else
{
snprintf(pname,256,"%s0.png",argv[3]);
bsiz = (strlen(pname)+1)*npages;
fwrite(&bsiz,4,1,bmf);
for ( int i=0; i<npages; i++ )
{
fwrite(pname,strlen(pname),1,bmf);
fputc(0,bmf);
snprintf(pname,256,"%s%c.png",argv[3],base36(i+1));
}
}
// chars
bmfchar_t *chars = calloc(nglyphs,sizeof(bmfchar_t));
for ( uint32_t i=0; i<nglyphs; i++ )
{
chars[i].id = glyphs[i].unicode;
chars[i].x = glyphs[i].x;
chars[i].y = glyphs[i].y;
chars[i].width = glyphs[i].w;
chars[i].height = glyphs[i].h;
chars[i].xoffset = glyphs[i].xofs;
chars[i].yoffset = glyphs[i].yofs;
chars[i].xadvance = glyphs[i].xadv;
chars[i].page = glyphs[i].page;
chars[i].chnl = 15;
}
fputc(4,bmf);
bsiz = nglyphs*sizeof(bmfchar_t);
fwrite(&bsiz,4,1,bmf);
fwrite(chars,sizeof(bmfchar_t),nglyphs,bmf);
free(chars);
#else
FILE *bmf = fopen(bmname,"w");
if ( !bmf )
{
fprintf(stderr,"error: failed to open '%s' for writing: %s\n",bmname,strerror(errno));
free(adata);
FT_Done_Face(fnt);
FT_Done_FreeType(ftlib);
return 64;
}
// info
fprintf(bmf,"info face=\"%s %s\" size=%u bold=0 italic=0 charset=\"\" unicode=1 stretchH=100 smooth=0 aa=1 padding=0,0,0,0 spacing=1,1 outline=0\n",fnt->family_name,fnt->style_name,pxsiz);
// common
fprintf(bmf,"common lineHeight=%u base=%u scaleW=%u scaleH=%u pages=%d packed=0 alphaChnl=0 redChnl=4 greenChnl=4 blueChnl=4\n",lh,bl,aw,ah,npages);
// pages
if ( npages == 1 )
{
snprintf(pname,256,"%s.png",argv[3]);
fprintf(bmf,"page id=0 file=\"%s\"\n",pname);
}
else for ( int i=0; i<npages; i++ )
{
snprintf(pname,256,"%s%c.png",argv[3],base36(i));
fprintf(bmf,"page id=%d file=\"%s\"\n",i,pname);
}
// chars
fprintf(bmf,"chars count=%u\n",nglyphs);
for ( uint32_t i=0; i<nglyphs; i++ )
fprintf(bmf,"char id=%u x=%u y=%u width=%u height=%u xoffset=%d yoffset=%d xadvance=%d page=%d chnl=15\n",glyphs[i].unicode,glyphs[i].x,glyphs[i].y,glyphs[i].w,glyphs[i].h,glyphs[i].xofs,glyphs[i].yofs,glyphs[i].xadv,glyphs[i].page);
#endif
fclose(bmf);
free(adata);
FT_Done_Face(fnt);
FT_Done_FreeType(ftlib);
return 0;
}

View file

@ -352,7 +352,7 @@ int main( int argc, char **argv )
{
if ( argc < 3 )
{
fprintf(stderr,"usage: mkfont <font name>[:face index] <pxsize> <unicode range (hex)>"
fprintf(stderr,"usage: mkfont <font name> <pxsize> <unicode range (hex)>"
" [gradient type (0,11)] [color palette] [-palinv]\n");
return 1;
}
@ -376,38 +376,12 @@ int main( int argc, char **argv )
}
if ( argc > 6 ) palinv = !strcmp(argv[6],"-palinv");
if ( argc > 5 ) loadpalette(argv[5]);
char *fidx = strrchr(argv[1],':');
int fnum = 0;
if ( fidx )
if ( FT_New_Face(ftlib,argv[1],0,&fnt) )
{
*fidx = 0;
sscanf(fidx+1,"%d",&fnum);
}
if ( FT_New_Face(ftlib,argv[1],fnum,&fnt) )
{
if ( fidx ) fprintf(stderr,"error: failed to open font '%s' face %d.\n",argv[1],fnum);
else fprintf(stderr,"error: failed to open font '%s'.\n",argv[1]);
fprintf(stderr,"error: failed to open font '%s'\n",argv[1]);
return 4;
}
fprintf(stderr,"info: loaded font \'%s %s\'.\n",fnt->family_name,fnt->style_name);
int nfnt = fnt->num_faces;
if ( !fidx && (nfnt > 1) )
{
fprintf(stderr,"info: loaded font has %d faces, you may want to load a specific one (see below).\n",nfnt);
FT_Done_Face(fnt);
for ( int i=0; i<nfnt; i++ )
{
if ( FT_New_Face(ftlib,argv[1],i,&fnt) )
{
fprintf(stderr,"error: failed to open font '%s' face %d. Skipping.\n",argv[1],i);
continue;
}
fprintf(stderr,"info: face %d: \'%s %s\'.\n",i,fnt->family_name,fnt->style_name);
FT_Done_Face(fnt);
}
FT_Done_FreeType(ftlib);
return 0;
}
if ( !strcmp(fnt->family_name,"tewi") ) tewi_hotfix = 1;
else if ( !strcmp(fnt->family_name,"miniwi") ) miniwi_hotfix = 1;
if ( FT_Set_Pixel_Sizes(fnt,0,pxsiz) )
@ -507,7 +481,7 @@ int main( int argc, char **argv )
int adv2 = fnt->glyph->advance.x>>6;
if ( adv2 ) adv = adv2;
w = adv;
iw = w+((gradient&8)?0:(gradient&4)?2:1);
iw = w+((gradient&4)?2:1);
}
int xx = 0;
int yy = upshift+tewi_hotfix+miniwi_hotfix;

483
pngread.c
View file

@ -4,7 +4,7 @@
#include <string.h>
#include <zlib.h>
uint32_t endianswap32( uint32_t n )
uint32_t endianswap( uint32_t n )
{
// if we're in a big endian system, we don't need this
uint16_t testme = 0x1234;
@ -15,17 +15,6 @@ uint32_t endianswap32( uint32_t n )
return on;
}
uint16_t endianswap16( uint16_t n )
{
// if we're in a big endian system, we don't need this
uint16_t testme = 0x1234;
if ( *(uint8_t*)(&testme) == 0x12 ) return n;
uint16_t on;
for ( int i=0; i<2; i++ )
*(((uint8_t*)(&on))+i) = *(((uint8_t*)(&n))+(1-i));
return on;
}
typedef struct
{
uint8_t highbitcheck;
@ -55,465 +44,69 @@ typedef struct
uint8_t unit;
} phys_t;
int verbose = 0;
int exitval = 0;
int bailout = 0;
FILE *f = 0;
int nchunk = 0;
pnghead_t hdr = {0,{0},{0},0,0};
pngchunk_t chk = {0,{0},0,0}; // make sure this is null at the very start
uint8_t coldepth = 0;
uint8_t coltype = 0;
pnghead_t hdr = {0,0,0,0,0};
pngchunk_t chk = {0,0,0,0}; // make sure this is null at the very start
uint32_t curcrc = 0;
int validcrc = 0;
uint8_t *tdat = 0; // accumulated data for all IDAT chunks
uint32_t tdatsiz = 0; // size of it
typedef struct
void readIHDR( ihdr_t* ihdr )
{
char name[4];
void(*func)(pngchunk_t *chk);
} pngfunc_t;
void dumpData( uint8_t *dat, uint32_t len )
{
for ( uint32_t i=0; i<len; i++ )
{
printf(" %02x",dat[i]);
if ( !((i+1)&15) || ((i+1) == len) ) putchar('\n');
}
printf(" width: %u\n height: %u\n depth: %u\n type: %u\n compression: %u\n filter: %u\n interlace: %u\n",
endianswap(ihdr->width),endianswap(ihdr->height),ihdr->depth,ihdr->type,ihdr->compression,ihdr->filter,ihdr->interlace);
}
void dumpChunk( pngchunk_t *chk )
// read up a stream chunk
void readIDAT( uint8_t* dat, uint32_t len )
{
printf(" !!parser for this chunk not implemented, hex dump follows\n");
dumpData(chk->data,chk->length);
if ( tdat ) tdat = realloc(tdat,tdatsiz+len);
else tdat = malloc(len);
memcpy(tdat+tdatsiz,dat,len);
tdatsiz += len;
}
const char* getcoltype( uint8_t type )
// process the whole thing
void processIDAT( void )
{
switch ( type )
{
case 0:
return "Grayscale";
case 2:
return "RGB";
case 3:
return "Indexed";
case 4:
return "Grayscale Alpha";
case 6:
return "RGB Alpha";
}
return "Invalid";
}
const char* getcompression( uint8_t compression )
void readpHYs( phys_t* phys )
{
if ( !compression )
return "Deflate";
return "Invalid";
printf(" xppu: %u\n yppu: %u\n unit: %u\n",endianswap(phys->xppu),endianswap(phys->yppu),phys->unit);
}
const char* getfilter( uint8_t filter )
void readtEXt( uint8_t* data, uint32_t len )
{
if ( !filter )
return "Adaptive";
return "Invalid";
}
const char* getinterlace( uint8_t interlace )
{
switch ( interlace )
{
case 0:
return "None";
case 1:
return "Adam7";
}
return "Invalid";
}
void readIHDR( pngchunk_t *chk )
{
if ( chk->length != 13 )
{
printf(" !!IHDR size is not 13, bailing out\n");
bailout = 1;
return;
}
ihdr_t* ihdr = (ihdr_t*)(chk->data);
printf(" width: %u\n height: %u\n depth: %u\n type: %u (%s)\n"
" compression: %u (%s)\n filter: %u (%s)\n interlace: %u (%s)\n",
endianswap32(ihdr->width),endianswap32(ihdr->height),
ihdr->depth,ihdr->type,getcoltype(ihdr->type),
ihdr->compression,getcompression(ihdr->compression),
ihdr->filter,getfilter(ihdr->filter),
ihdr->interlace,getinterlace(ihdr->interlace));
coldepth = ihdr->type;
coltype = ihdr->type;
}
void readPLTE( pngchunk_t *chk )
{
if ( chk->length%3 )
{
printf(" !!palette length not divisible by 3, may be corrupted, skipping\n");
return;
}
unsigned ncol = chk->length/3;
for ( unsigned i=0; i<ncol; i++ )
printf(" %d: (%hhu,%hhu,%hhu)\n",i,chk->data[i*3],chk->data[i*3+1],chk->data[i*3+2]);
}
void readIDAT( pngchunk_t *chk )
{
if ( tdat ) tdat = realloc(tdat,tdatsiz+chk->length);
else tdat = malloc(chk->length);
memcpy(tdat+tdatsiz,chk->data,chk->length);
tdatsiz += chk->length;
}
void readtRNS( pngchunk_t *chk )
{
for ( unsigned i=0; i<chk->length; i++ )
printf(" %d: %hhu\n",i,chk->data[i]);
}
void readgAMA( pngchunk_t *chk )
{
if ( chk->length != 4 )
{
printf(" !!gAMA size is not 4, skipping\n");
return;
}
uint32_t rg = endianswap32(*(uint32_t*)(chk->data));
printf(" 1/%g\n",1./(rg/100000.));
}
void readcHRM( pngchunk_t *chk )
{
if ( chk->length != 32 )
{
printf(" !!cHRM size is not 64, skipping\n");
return;
}
uint32_t rwx = endianswap32(*(uint32_t*)(chk->data)),
rwy = endianswap32(*(uint32_t*)(chk->data+4)),
rrx = endianswap32(*(uint32_t*)(chk->data+8)),
rry = endianswap32(*(uint32_t*)(chk->data+12)),
rgx = endianswap32(*(uint32_t*)(chk->data+16)),
rgy = endianswap32(*(uint32_t*)(chk->data+20)),
rbx = endianswap32(*(uint32_t*)(chk->data+24)),
rby = endianswap32(*(uint32_t*)(chk->data+28));
printf(" White Point x: %g\n White Point y: %g\n"
" Red x: %g\n Red y: %g\n"
" Green x: %g\n Green y: %g\n"
" Blue x: %g\n Blue y: %g\n",
rwx/100000.,rwy/100000.,
rrx/100000.,rry/100000.,
rgx/100000.,rgy/100000.,
rbx/100000.,rby/100000.);
}
void readsRGB( pngchunk_t *chk )
{
if ( chk->length != 1 )
{
printf(" !!sRGB size is not 1, skipping\n");
return;
}
switch ( chk->data[0] )
{
case 0:
printf(" Perceptual\n");
break;
case 1:
printf(" Relative colorimetric\n");
break;
case 2:
printf(" Saturation\n");
break;
case 3:
printf(" Absolute colorimetric\n");
break;
default:
printf(" Unknown\n");
break;
}
}
void readiCCP( pngchunk_t *chk )
{
// TODO process this
dumpChunk(chk);
}
void readtEXt( pngchunk_t *chk )
{
char *key = (char*)chk->data;
char *val = key+strlen(key)+1;
uint32_t vallen = chk->length-(val-key);
uint8_t *key = data;
uint8_t *val = data+strlen(data)+1;
uint32_t vallen = len-(val-key);
printf(" '%s' :: '%.*s'\n",key,vallen,val);
}
void readzTXt( pngchunk_t *chk )
{
// TODO decompress this stuff
dumpChunk(chk);
}
void readiTXt( pngchunk_t *chk )
{
char *key = (char*)chk->data;
uint8_t cmpflag = chk->data[strlen(key)+1];
if ( cmpflag )
{
printf(" !!compressed iTXt not supported, skipping");
return;
}
char *lang = (char*)(chk->data+strlen(key)+3);
char *kword = (char*)(chk->data+strlen(key)+3+strlen(lang)+1);
char *text = (char*)(chk->data+strlen(key)+3+strlen(lang)+1+strlen(kword)+1);
uint32_t textlen = chk->length-(text-key);
printf(" '%s' :: ",key);
if ( lang[0] ) printf("[%s] ",lang);
if ( kword[0] ) printf("'%s' ",kword);
if ( lang[0] || kword[0] ) printf(":: ");
printf("'%.*s'\n",textlen,text);
}
void readbKGD( pngchunk_t *chk )
{
switch ( coltype )
{
case 3:
if ( chk->length != 1 )
{
printf(" !!bKGD size is not 1, skipping\n");
return;
}
printf(" BG: %hhu\n",chk->data[0]);
break;
case 0:
case 4:
if ( chk->length != 2 )
{
printf(" !!bKGD size is not 2, skipping\n");
return;
}
printf(" BG: %hu\n",endianswap16(*(uint16_t*)(chk->data)));
break;
case 2:
case 6:
if ( chk->length != 6 )
{
printf(" !!bKGD size is not 6, skipping\n");
return;
}
printf(" BG: (%hu,%hu,%hu)\n",
endianswap16(*(uint16_t*)(chk->data)),
endianswap16(*(uint16_t*)(chk->data+2)),
endianswap16(*(uint16_t*)(chk->data+4)));
break;
default:
printf(" !!bKGD present for unhandled color type %d, skipping\n",coltype);
break;
}
}
void readpHYs( pngchunk_t *chk )
{
if ( chk->length != 9 )
{
printf(" !!pHYs size is not 9, skipping\n");
return;
}
phys_t* phys = (phys_t*)(chk->data);
printf(" xppu: %u\n yppu: %u\n unit: %u\n",endianswap32(phys->xppu),endianswap32(phys->yppu),phys->unit);
}
void readsBIT( pngchunk_t *chk )
{
switch ( coltype )
{
case 0:
if ( chk->length != 1 )
{
printf(" !!sBIT size is not 1, skipping\n");
return;
}
printf(" %hhu\n",chk->data[0]);
break;
case 2:
case 3:
if ( chk->length != 3 )
{
printf(" !!sBIT size is not 3, skipping\n");
return;
}
printf(" (%hhu,%hhu,%hhu)\n",chk->data[0],chk->data[1],chk->data[2]);
break;
case 4:
if ( chk->length != 2 )
{
printf(" !!sBIT size is not 2, skipping\n");
return;
}
printf(" (%hhu,%hhu)\n",chk->data[0],chk->data[1]);
break;
case 6:
if ( chk->length != 4 )
{
printf(" !!sBIT size is not 4, skipping\n");
return;
}
printf(" (%hhu,%hhu,%hhu,%hhu)\n",chk->data[0],chk->data[1],chk->data[2],chk->data[3]);
break;
default:
printf(" !!sBIT present for unhandled color type %d, skipping\n",coltype);
break;
}
}
void readsPLT( pngchunk_t *chk )
{
char *name = (char*)chk->data;
uint8_t depth = chk->data[strlen(name)+1];
printf(" suggested palette: %s\n sample depth: %hhu\n",name,depth);
int div;
if ( depth == 16 )
div = 10;
else if ( depth == 8 )
div = 6;
else
{
printf(" !!invalid depth of %u, skipping\n",depth);
return;
}
uint32_t psiz = (chk->length-(strlen(name)+2));
if ( psiz%div )
{
printf(" !!palette length not divisible by %d, skipping\n",div);
return;
}
unsigned ncol = psiz/div;
uint8_t *pdat = chk->data+(strlen(name)+2);
for ( unsigned i=0; i<ncol; i++ )
{
if ( div == 10 )
printf(" %u: (%hu,%hu,%hu) [%hu]\n",i,
endianswap16(*(uint16_t*)(pdat+i*10)),
endianswap16(*(uint16_t*)(pdat+i*10+2)),
endianswap16(*(uint16_t*)(pdat+i*10+4)),
endianswap16(*(uint16_t*)(pdat+i*10+6)));
else if ( div == 6 )
printf(" %u: (%hhu,%hhu,%hhu) [%hu]\n",i,
*(uint8_t*)(pdat+i*6),
*(uint8_t*)(pdat+i*6+1),
*(uint8_t*)(pdat+i*6+2),
endianswap16(*(uint16_t*)(pdat+i*6+3)));
}
}
void readhIST( pngchunk_t *chk )
{
if ( chk->length%2 )
{
printf(" !!hIST size not divisible by two, skipping\n");
return;
}
unsigned ncol = chk->length/2;
for ( unsigned i=0; i<ncol; i++ )
printf(" %u: %hu\n",i,endianswap16(*(uint16_t*)(chk->data+i*2)));
}
void readtIME( pngchunk_t *chk )
{
if ( chk->length != 7 )
{
printf(" !!tIME size is not 7, skipping\n");
return;
}
printf(" %hu-%hhu-%hhu %hhu:%hhu:%hhu\n",
endianswap16(*(uint16_t*)(chk->data)),chk->data[2],chk->data[3],
chk->data[4],chk->data[5],chk->data[6]);
}
void readgrAb( pngchunk_t *chk )
{
if ( chk->length != 8 )
{
printf(" !!grAb size is not 8, skipping\n");
return;
}
uint32_t rx = endianswap32(*(uint32_t*)(chk->data)), ry = endianswap32(*(uint32_t*)(chk->data+4));
int32_t x = *(int32_t*)(&rx), y = *(int32_t*)(&ry);
printf(" %+d,%+d\n",x,y);
}
#define NUMREADERS 20
pngfunc_t readers[NUMREADERS] =
{
// critical
{"IHDR",readIHDR},
{"PLTE",readPLTE},
{"IDAT",readIDAT},
{"IEND",0},
// transparency
{"tRNS",readtRNS},
// colorspace
{"gAMA",readgAMA},
{"cHRM",readcHRM},
{"sRGB",readsRGB},
{"iCCP",readiCCP},
// text
{"tEXt",readtEXt},
{"zTXt",readzTXt},
{"iTXt",readiTXt},
// misc
{"bKGD",readbKGD},
{"pHYs",readpHYs},
{"sBIT",readsBIT},
{"sPLT",readsPLT},
{"hIST",readhIST},
{"tIME",readtIME},
// zdoom
{"grAb",readgrAb},
{"alPh",0}
};
void readchunkdata( pngchunk_t* chk )
{
for ( int i=0; i<NUMREADERS; i++ )
{
if ( strncmp(chk->type,readers[i].name,4) ) continue;
if ( readers[i].func ) readers[i].func(chk);
return;
}
printf(" !!no reader for this chunk type\n");
if ( !strncmp(chk->type,"IHDR",4) )
readIHDR((ihdr_t*)chk->data);
else if ( !strncmp(chk->type,"pHYs",4) )
readpHYs((phys_t*)chk->data);
else if ( !strncmp(chk->type,"tEXt",4) )
readtEXt(chk->data,chk->length);
}
// TODO process the whole thing (might not bother actually)
void processIDAT( void )
{
if ( !verbose ) return;
printf("full datastream dump follows:\n");
dumpData(tdat,tdatsiz);
}
int main( int argc, char **argv )
{
if ( (argc > 1) && (!strncmp(argv[1],"-v",2) || !strncmp(argv[1],"--verbose",9)) )
verbose = 1;
if ( argc < (2+verbose) )
if ( argc < 2 )
{
printf("No file supplied.\n");
exitval = 0;
goto endmii;
}
f = fopen(argv[1+verbose],"rb");
f = fopen(argv[1],"rb");
if ( !f )
{
printf("Failed to open file.\n");
@ -553,7 +146,7 @@ int main( int argc, char **argv )
}
readchunk:
fread(&chk,1,8,f); // read size and type first
chk.length = endianswap32(chk.length); // swap from BE
chk.length = endianswap(chk.length); // swap from BE
if ( (nchunk == 0) && strncmp(chk.type,"IHDR",4) )
{
printf("First chunk is not IHDR.\n");
@ -565,33 +158,21 @@ readchunk:
else chk.data = malloc(chk.length);
fread(chk.data,1,chk.length,f); // read data
fread(&(chk.crc),1,4,f); // read CRC
chk.crc = endianswap32(chk.crc);
curcrc = crc32(0,(uint8_t*)chk.type,4);
chk.crc = endianswap(chk.crc);
curcrc = crc32(0,chk.type,4);
if ( chk.length ) curcrc = crc32(curcrc,chk.data,chk.length);
validcrc = (curcrc == chk.crc);
printf("%.4s chunk of length %u (CRC32: %08X, %s).\n",chk.type,chk.length,chk.crc,validcrc?"OK":"FAILED");
if ( !validcrc ) printf(" CRC32 ACTUAL: %08X\n",curcrc);
else readchunkdata(&chk);
nchunk++;
if ( bailout ) goto endmii;
if ( strncmp(chk.type,"IEND",4) ) goto readchunk;
if ( tdat ) processIDAT();
long cpos = ftell(f);
fseek(f,0,SEEK_END);
long epos = ftell(f);
if ( epos > cpos )
{
printf(" !!there are %lu bytes of extra data after IEND\n",epos-cpos);
tdatsiz = epos-cpos;
if ( tdat ) tdat = realloc(tdat,tdatsiz);
else tdat = malloc(tdatsiz);
fseek(f,cpos,SEEK_SET);
fread(tdat,1,tdatsiz,f);
if ( verbose ) dumpData(tdat,tdatsiz);
}
if ( epos > cpos ) printf("%lu bytes of extra data after IEND.\n",epos-cpos);
endmii:
if ( f ) fclose(f);
if ( chk.data ) free(chk.data);
if ( tdat ) free(tdat);
return exitval;
}

View file

@ -9,10 +9,874 @@
#include <string.h>
#include <time.h>
#include "soapstone_ds1.h"
#include "soapstone_ds2.h"
#include "soapstone_ds3.h"
//#include "soapstone_er.h"
const char templates1[13][32] =
{
"%s ahead",
"Be wary of %s",
"Try %s",
"Need %s",
"Imminent %s...",
"Weakness: %s",
"%s",
"%s?",
"Good luck",
"I did it!",
"Here!",
"I can't take this...",
"Praise the Sun!"
};
const char words1[173][32] =
{
"Enemy",
"Tough enemy",
"Hollow",
"Soldier",
"Knight",
"Sniper",
"Caster",
"Giant",
"Skeleton",
"Ghost",
"Bug",
"Poison bug",
"Lizard",
"Drake",
"Flier",
"Golem",
"Statue",
"Monster",
"Strange creature",
"Demon",
"Darkwraith",
"Dragon",
"Boss",
"Saint",
"Wretch",
"Charmer",
"Miscreant",
"Liar",
"Fatty",
"Beanpole",
"Merchant",
"Blacksmith",
"Master",
"Prisoner",
"Bonfire",
"Fog wall",
"Humanity",
"Lever",
"Switch",
"Key",
"Treasure",
"Chest",
"Weapon",
"Shield",
"Projectile",
"Armour",
"Item",
"Ring",
"Sorcery scroll",
"Pyromancy scroll",
"Miracle scroll",
"Ember",
"Trap",
"Covenant",
"Amazing key",
"Amazing treasure",
"Amazing chest",
"Amazing weapon",
"Amazing armour",
"Amazing item",
"Amazing ring",
"Amazing sorcery scroll",
"Amazing pyromancy scroll",
"Amazing miracle scroll",
"Amazing ember",
"Amazing trap",
"Close-ranged battle",
"Ranged battle",
"Eliminating one at a time",
"Luring it out",
"Beating to a pulp",
"Lying in ambush",
"Stealth",
"Mimicry",
"Pincer attack",
"Hitting them in one swoop",
"Fleeing",
"Charging",
"Stabbing in the back",
"Sweeping attack",
"Shield breaking",
"Head shots",
"Sorcery",
"Pyromancy",
"Miracles",
"Jumping off",
"Sliding down",
"Dashing through",
"Rolling",
"Backstepping",
"Jumping",
"Attacking",
"Holding with both hands",
"Kicking",
"A plunging attack",
"Blocking",
"Parrying",
"Locking-on",
"Path",
"Hidden path",
"Shortcut",
"Detour",
"Illusory wall",
"Shortcut",
"Dead end",
"Swamp",
"Lava",
"Forest",
"Cave",
"Labyrinth",
"Safe zone",
"Danger zone",
"Sniper spot",
"Bright spot",
"Dark spot",
"Open area",
"Tight spot",
"Hiding place",
"Exchange",
"Gorgeous view",
"Fall",
"Front",
"Back",
"Left",
"Right",
"Up",
"Down",
"Feet",
"Head",
"Back",
"Head",
"Neck",
"Stomach",
"Back",
"Arm",
"Leg",
"Heel",
"Rear",
"Tail",
"Wings",
"Anywhere",
"Strike",
"Thrust",
"Slash",
"Magic",
"Fire",
"Lightning",
"Critical hits",
"Bleeding",
"Poison",
"Strong poison",
"Curses",
"Divine",
"Occult",
"Crystal",
"Chance",
"Hint",
"Secret",
"Happiness",
"Sorrow",
"Life",
"Death",
"Undead",
"Elation",
"Grief",
"Hope",
"Despair",
"Light",
"Dark",
"Bravery",
"Resignation",
"Comfort",
"Tears"
};
const char templates2[11][32] =
{
"%s ahead",
"%s required ahead",
"be wary of %s",
"try %s",
"weakness: %s",
"visions of %s...",
"%s",
"%s!",
"%s?",
"%s...",
"hurrah for %s!",
};
const char conjunctions2[7][32] =
{
" and then ",
" but ",
" therefore ",
" in short ",
" or ",
" by the way ",
", "
};
const char words2[247][32] =
{
"enemy",
"monster",
"lesser foe",
"tough enemy",
"boss",
"Hollow",
"skeleton",
"ghost",
"bug",
"Gyrm",
"beast",
"giant",
"dwarf",
"sniper",
"caster",
"duo",
"trio",
"saint",
"wretch",
"charmer",
"poor soul",
"oddball",
"nimble one",
"laggard",
"moneybags",
"beggar",
"miscreant",
"liar",
"fatty",
"beanpole",
"merchant",
"artisan",
"master",
"friend",
"ally",
"Dark Spirit",
"Phantom",
"Shade",
"bonfire",
"fog wall",
"lever",
"switch",
"key",
"trap",
"torch",
"door",
"treasure",
"chest",
"something",
"quite something",
"weapon",
"shield",
"projectile",
"armor",
"item",
"ring",
"scroll",
"ore",
"message",
"bloodstain",
"illusion",
"close-ranged battle",
"ranged battle",
"eliminating one at a time",
"luring it out",
"beating to a pulp",
"ambush",
"pincer attack",
"hitting them in one swoop",
"dual-wielding",
"stealth",
"mimicry",
"fleeing",
"charging",
"jumping off",
"dashing through",
"circling around",
"trapping inside",
"rescue",
"sorcery",
"pyromancy",
"miracles",
"hexes",
"pure luck",
"prudence",
"brief respite",
"play dead",
"jog",
"dash",
"rolling",
"backstepping",
"jumping",
"attacking",
"jump attack",
"dash attack",
"counter attack",
"stabbing in the back",
"guard stun & stab",
"parry stun & stab",
"plunging attack",
"sweeping attack",
"shield breaking",
"blocking",
"parrying",
"spell parry",
"locking-on",
"no lock-on",
"two-handing",
"gesture",
"control",
"destroy",
"boulder",
"lava",
"poison gas",
"enemy horde",
"forest",
"cave",
"arena",
"hidden path",
"detour",
"shortcut",
"dead end",
"labyrinth",
"hole",
"bright spot",
"dark spot",
"open area",
"tight spot",
"safe zone",
"danger zone",
"sniper spot",
"hiding place",
"illusory wall",
"ladder",
"lift",
"exchange",
"gorgeous view",
"looking away",
"overconfidence",
"slip-up",
"oversight",
"fatigue",
"bad luck",
"inattention",
"loss of stamina",
"front",
"back",
"left",
"right",
"up",
"down",
"below",
"above",
"behind",
"head",
"neck",
"stomach",
"back",
"arm",
"leg",
"rear",
"tail",
"wings",
"anywhere",
"tongue",
"right arm",
"left arm",
"right leg",
"left leg",
"right side",
"left side",
"pincer",
"wheel",
"core",
"horse",
"strike",
"thrust",
"slash",
"magic",
"sorcery",
"fire",
"lightning",
"critical hits",
"bleeding",
"poison",
"toxic",
"curse",
"equipment breakage",
"chance",
"quagmire",
"hint",
"secret",
"happiness",
"misfortune",
"life",
"death",
"joy",
"sadness",
"tears",
"hope",
"despair",
"victory",
"defeat",
"light",
"dark",
"bravery",
"confidence",
"vigor",
"revenge",
"resignation",
"overwhelming",
"regret",
"pointless",
"man",
"woman",
"recklessness",
"composure",
"guts",
"comfort",
"silence",
"good luck",
"fine work",
"I did it!",
"I've failed...",
"here!",
"not here!",
"I can't take this...",
"don't you dare!",
"do it!",
"look carefully",
"listen carefully",
"think carefully",
"this place again?",
"now the real fight begins",
"keep moving",
"pull back",
"give it up",
"don't give up",
"help me...",
"impossible...",
"bloody expensive...",
"nice job",
"let me out of here...",
"stay calm",
"like a dream...",
"are you ready?",
"praise the Sun!"
};
const char templates3[17][32] =
{
"%s ahead",
"No %s ahead",
"%s required ahead",
"be wary of %s",
"try %s",
"Could this be a %s?",
"If only I had a %s...",
"visions of %s...",
"Time for %s",
"%s",
"%s!",
"%s?",
"%s...",
"Huh. It's a %s...",
"praise the %s!",
"Let there be %s",
"Ahh, %s..."
};
const char conjunctions3[10][32] =
{
" and then ",
" but ",
" therefore ",
" in short ",
" or ",
" only ",
" by the way ",
" so to speak ",
" all the more ",
", ",
};
const char gestures3[33][32] =
{
"Point forward",
"Point up",
"Point down",
"Wave",
"Beckon",
"Call over",
"Welcome",
"Applause",
"Quiet Resolve",
"Jump for joy",
"Joy",
"Rejoice",
"Hurrah!",
"Praise the Sun",
"My thanks!",
"Bow",
"Proper bow",
"Dignified bow",
"Duel bow",
"Legion Etiquette",
"Darkmoon Loyalty",
"By my sword",
"Prayer",
"Silent Ally",
"Rest",
"Collapse",
"Patches Squat",
"Prostration",
"Toast",
"Sleep",
"Curl up",
"Stretch out",
"Path of the Dragon"
};
const char words3[322][32] =
{
"enemy",
"monster",
"mob enemy",
"tough enemy",
"critical foe",
"Hollow",
"pilgrim",
"prisoner",
"monstrosity",
"skeleton",
"ghost",
"beast",
"lizard",
"bug",
"grub",
"crab",
"dwarf",
"giant",
"demon",
"dragon",
"knight",
"sellsword",
"warrior",
"herald",
"bandit",
"assassin",
"sorcerer",
"pyromancer",
"cleric",
"deprived",
"sniper",
"duo",
"trio",
"you",
"you bastard",
"good fellow",
"saint",
"wretch",
"charmer",
"poor soul",
"oddball",
"nimble one",
"laggard",
"moneybags",
"beggar",
"miscreant",
"liar",
"fatty",
"beanpole",
"youth",
"elder",
"old codger",
"old dear",
"miscreant",
"artisan",
"master",
"sage",
"champion",
"Lord of Cinder",
"king",
"queen",
"prince",
"princess",
"angel",
"god",
"friend",
"ally",
"spouse",
"covenantor",
"Phantom",
"Dark Spirit",
"bonfire",
"ember",
"fog wall",
"lever",
"contraption",
"key",
"trap",
"torch",
"door",
"treasure",
"chest",
"something",
"quite something",
"rubbish",
"filth",
"weapon",
"shield",
"projectile",
"armor",
"item",
"ring",
"ore",
"coal",
"transposing kiln",
"scroll",
"umbral ash",
"throne",
"rite",
"coffin",
"cinder",
"ash",
"moon",
"eye",
"brew",
"soup",
"message",
"bloodstain",
"illusion",
"close-ranged battle",
"ranged battle",
"eliminating one at a time",
"luring it out",
"beating to a pulp",
"ambush",
"pincer attack",
"hitting them in one swoop",
"dual-wielding",
"stealth",
"mimicry",
"fleeing",
"charging",
"jumping off",
"dashing through",
"circling around",
"trapping inside",
"rescue",
"Skill",
"sorcery",
"pyromancy",
"miracles",
"pure luck",
"prudence",
"brief respite",
"play dead",
"jog",
"dash",
"rolling",
"backstepping",
"jumping",
"attacking",
"jump attack",
"dash attack",
"counter attack",
"stabbing in the back",
"guard stun & stab",
"plunging attack",
"sweeping attack",
"shield breaking",
"blocking",
"parrying",
"locking-on",
"no lock-on",
"two-handing",
"gesture",
"control",
"destroy",
"boulder",
"lava",
"poison gas",
"enemy horde",
"forest",
"swamp",
"cave",
"shortcut",
"detour",
"hidden path",
"secret passage",
"dead end",
"labyrinth",
"hole",
"bright spot",
"dark spot",
"open area",
"tight spot",
"safe zone",
"danger zone",
"sniper spot",
"hiding place",
"illusory wall",
"ladder",
"lift",
"gorgeous view",
"looking away",
"overconfidence",
"slip-up",
"oversight",
"fatigue",
"bad luck",
"inattention",
"loss of stamina",
"chance encounter",
"planned encounter",
"front",
"back",
"left",
"right",
"up",
"down",
"below",
"above",
"behind",
"head",
"neck",
"stomach",
"back",
"arm",
"finger",
"leg",
"rear",
"tail",
"wings",
"anywhere",
"tongue",
"right arm",
"left arm",
"thumb",
"indexfinger",
"longfinger",
"ringfinger",
"smallfinger",
"right leg",
"left leg",
"right side",
"left side",
"pincer",
"wheel",
"core",
"mount",
"regular",
"strike",
"thrust",
"slash",
"magic",
"crystal",
"fire",
"chaos",
"lightning",
"blessing",
"dark",
"critical hits",
"bleeding",
"poison",
"toxic",
"frost",
"curse",
"equipment breakage",
"chance",
"quagmire",
"hint",
"secret",
"sleeptalk",
"happiness",
"misfortune",
"life",
"death",
"demise",
"joy",
"fury",
"agony",
"sadness",
"tears",
"loyalty",
"betrayal",
"hope",
"despair",
"fear",
"losing sanity",
"victory",
"defeat",
"sacrifice",
"light",
"dark",
"bravery",
"confidence",
"vigor",
"revenge",
"resignation",
"overwhelming",
"regret",
"pointless",
"man",
"woman",
"friendship",
"love",
"recklessness",
"composure",
"guts",
"comfort",
"silence",
"deep",
"good luck",
"fine work",
"I did it!",
"I've failed...",
"here!",
"not here!",
"I can't take this...",
"lonely...",
"don't you dare!",
"do it!",
"look carefully",
"listen carefully",
"think carefully",
"this place again?",
"now the real fight begins",
"you don't deserve this",
"keep moving",
"pull back",
"give it up",
"don't give up",
"help me...",
"impossible...",
"bloody expensive...",
"let me out of here...",
"stay calm",
"like a dream...",
"seems familiar...",
"are you ready?",
"it'll happen to you too",
"praise the Sun!",
"may the flames guide thee"
};
void print_soapstone_ds1( void )
{
@ -26,16 +890,16 @@ void print_soapstone_ds1( void )
void print_soapstone_ds2( void )
{
int template = rand()%DS2_TEMPLATES;
int word = rand()%(((template>5)&&(template<10))?DS2_WORDS:DS2_WORDS2);
int template = rand()%11;
int word = rand()%(((template>5)&&(template<10))?247:220);
char mesg[256];
sprintf(mesg,templates2[template],words2[word]);
if ( rand()&1 )
{
char mesg2[256];
int template2 = rand()%DS2_TEMPLATES;
int word2 = rand()%(((template2>5)&&(template2<10))?DS2_WORDS:DS2_WORDS2);
int conj = rand()%DS2_CONJUNCTIONS;
int template2 = rand()%11;
int word2 = rand()%(((template2>5)&&(template2<10))?247:220);
int conj = rand()%7;
sprintf(mesg2,templates2[template2],words2[word2]);
printf("%s%s%s\n",mesg,conjunctions2[conj],mesg2);
}
@ -44,17 +908,17 @@ void print_soapstone_ds2( void )
void print_soapstone_ds3( void )
{
if ( rand()&1 ) printf("[%s] ",gestures3[rand()%DS3_GESTURES]);
int template = rand()%DS3_TEMPLATES;
int word = rand()%DS3_WORDS;
if ( rand()&1 ) printf("[%s] ",gestures3[rand()%33]);
int template = rand()%17;
int word = rand()%322;
char mesg[256];
sprintf(mesg,templates3[template],words3[word]);
if ( rand()&1 )
{
char mesg2[256];
int template2 = rand()%DS3_TEMPLATES;
int word2 = rand()%DS3_WORDS2;
int conj = rand()%DS3_CONJUNCTIONS;
int template2 = rand()%17;
int word2 = rand()%211;
int conj = rand()%10;
sprintf(mesg2,templates3[template2],words3[word2]);
printf("%s%s%s\n",mesg,conjunctions3[conj],mesg2);
}

View file

@ -1,197 +0,0 @@
// Dark Souls messages
#define DS1_TEMPLATES 13
const char templates1[DS1_TEMPLATES][32] =
{
"%s ahead",
"Be wary of %s",
"Try %s",
"Need %s",
"Imminent %s...",
"Weakness: %s",
"%s",
"%s?",
"Good luck",
"I did it!",
"Here!",
"I can't take this...",
"Praise the Sun!"
};
#define DS1_WORDS 173
const char words1[DS1_WORDS][32] =
{
"Enemy",
"Tough enemy",
"Hollow",
"Soldier",
"Knight",
"Sniper",
"Caster",
"Giant",
"Skeleton",
"Ghost",
"Bug",
"Poison bug",
"Lizard",
"Drake",
"Flier",
"Golem",
"Statue",
"Monster",
"Strange creature",
"Demon",
"Darkwraith",
"Dragon",
"Boss",
"Saint",
"Wretch",
"Charmer",
"Miscreant",
"Liar",
"Fatty",
"Beanpole",
"Merchant",
"Blacksmith",
"Master",
"Prisoner",
"Bonfire",
"Fog wall",
"Humanity",
"Lever",
"Switch",
"Key",
"Treasure",
"Chest",
"Weapon",
"Shield",
"Projectile",
"Armour",
"Item",
"Ring",
"Sorcery scroll",
"Pyromancy scroll",
"Miracle scroll",
"Ember",
"Trap",
"Covenant",
"Amazing key",
"Amazing treasure",
"Amazing chest",
"Amazing weapon",
"Amazing armour",
"Amazing item",
"Amazing ring",
"Amazing sorcery scroll",
"Amazing pyromancy scroll",
"Amazing miracle scroll",
"Amazing ember",
"Amazing trap",
"Close-ranged battle",
"Ranged battle",
"Eliminating one at a time",
"Luring it out",
"Beating to a pulp",
"Lying in ambush",
"Stealth",
"Mimicry",
"Pincer attack",
"Hitting them in one swoop",
"Fleeing",
"Charging",
"Stabbing in the back",
"Sweeping attack",
"Shield breaking",
"Head shots",
"Sorcery",
"Pyromancy",
"Miracles",
"Jumping off",
"Sliding down",
"Dashing through",
"Rolling",
"Backstepping",
"Jumping",
"Attacking",
"Holding with both hands",
"Kicking",
"A plunging attack",
"Blocking",
"Parrying",
"Locking-on",
"Path",
"Hidden path",
"Shortcut",
"Detour",
"Illusory wall",
"Shortcut",
"Dead end",
"Swamp",
"Lava",
"Forest",
"Cave",
"Labyrinth",
"Safe zone",
"Danger zone",
"Sniper spot",
"Bright spot",
"Dark spot",
"Open area",
"Tight spot",
"Hiding place",
"Exchange",
"Gorgeous view",
"Fall",
"Front",
"Back",
"Left",
"Right",
"Up",
"Down",
"Feet",
"Head",
"Back",
"Head",
"Neck",
"Stomach",
"Back",
"Arm",
"Leg",
"Heel",
"Rear",
"Tail",
"Wings",
"Anywhere",
"Strike",
"Thrust",
"Slash",
"Magic",
"Fire",
"Lightning",
"Critical hits",
"Bleeding",
"Poison",
"Strong poison",
"Curses",
"Divine",
"Occult",
"Crystal",
"Chance",
"Hint",
"Secret",
"Happiness",
"Sorrow",
"Life",
"Death",
"Undead",
"Elation",
"Grief",
"Hope",
"Despair",
"Light",
"Dark",
"Bravery",
"Resignation",
"Comfort",
"Tears"
};

View file

@ -1,282 +0,0 @@
// Dark Souls 2 messages
#define DS2_TEMPLATES 11
const char templates2[DS2_TEMPLATES][32] =
{
"%s ahead",
"%s required ahead",
"be wary of %s",
"try %s",
"weakness: %s",
"visions of %s...",
"%s",
"%s!",
"%s?",
"%s...",
"hurrah for %s!",
};
#define DS2_CONJUNCTIONS 7
const char conjunctions2[DS2_CONJUNCTIONS][32] =
{
" and then ",
" but ",
" therefore ",
" in short ",
" or ",
" by the way ",
", "
};
#define DS2_WORDS 247
#define DS2_WORDS2 220
const char words2[DS2_WORDS][32] =
{
"enemy",
"monster",
"lesser foe",
"tough enemy",
"boss",
"Hollow",
"skeleton",
"ghost",
"bug",
"Gyrm",
"beast",
"giant",
"dwarf",
"sniper",
"caster",
"duo",
"trio",
"saint",
"wretch",
"charmer",
"poor soul",
"oddball",
"nimble one",
"laggard",
"moneybags",
"beggar",
"miscreant",
"liar",
"fatty",
"beanpole",
"merchant",
"artisan",
"master",
"friend",
"ally",
"Dark Spirit",
"Phantom",
"Shade",
"bonfire",
"fog wall",
"lever",
"switch",
"key",
"trap",
"torch",
"door",
"treasure",
"chest",
"something",
"quite something",
"weapon",
"shield",
"projectile",
"armor",
"item",
"ring",
"scroll",
"ore",
"message",
"bloodstain",
"illusion",
"close-ranged battle",
"ranged battle",
"eliminating one at a time",
"luring it out",
"beating to a pulp",
"ambush",
"pincer attack",
"hitting them in one swoop",
"dual-wielding",
"stealth",
"mimicry",
"fleeing",
"charging",
"jumping off",
"dashing through",
"circling around",
"trapping inside",
"rescue",
"sorcery",
"pyromancy",
"miracles",
"hexes",
"pure luck",
"prudence",
"brief respite",
"play dead",
"jog",
"dash",
"rolling",
"backstepping",
"jumping",
"attacking",
"jump attack",
"dash attack",
"counter attack",
"stabbing in the back",
"guard stun & stab",
"parry stun & stab",
"plunging attack",
"sweeping attack",
"shield breaking",
"blocking",
"parrying",
"spell parry",
"locking-on",
"no lock-on",
"two-handing",
"gesture",
"control",
"destroy",
"boulder",
"lava",
"poison gas",
"enemy horde",
"forest",
"cave",
"arena",
"hidden path",
"detour",
"shortcut",
"dead end",
"labyrinth",
"hole",
"bright spot",
"dark spot",
"open area",
"tight spot",
"safe zone",
"danger zone",
"sniper spot",
"hiding place",
"illusory wall",
"ladder",
"lift",
"exchange",
"gorgeous view",
"looking away",
"overconfidence",
"slip-up",
"oversight",
"fatigue",
"bad luck",
"inattention",
"loss of stamina",
"front",
"back",
"left",
"right",
"up",
"down",
"below",
"above",
"behind",
"head",
"neck",
"stomach",
"back",
"arm",
"leg",
"rear",
"tail",
"wings",
"anywhere",
"tongue",
"right arm",
"left arm",
"right leg",
"left leg",
"right side",
"left side",
"pincer",
"wheel",
"core",
"horse",
"strike",
"thrust",
"slash",
"magic",
"sorcery",
"fire",
"lightning",
"critical hits",
"bleeding",
"poison",
"toxic",
"curse",
"equipment breakage",
"chance",
"quagmire",
"hint",
"secret",
"happiness",
"misfortune",
"life",
"death",
"joy",
"sadness",
"tears",
"hope",
"despair",
"victory",
"defeat",
"light",
"dark",
"bravery",
"confidence",
"vigor",
"revenge",
"resignation",
"overwhelming",
"regret",
"pointless",
"man",
"woman",
"recklessness",
"composure",
"guts",
"comfort",
"silence",
"good luck",
"fine work",
"I did it!",
"I've failed...",
"here!",
"not here!",
"I can't take this...",
"don't you dare!",
"do it!",
"look carefully",
"listen carefully",
"think carefully",
"this place again?",
"now the real fight begins",
"keep moving",
"pull back",
"give it up",
"don't give up",
"help me...",
"impossible...",
"bloody expensive...",
"nice job",
"let me out of here...",
"stay calm",
"like a dream...",
"are you ready?",
"praise the Sun!"
};

View file

@ -1,404 +0,0 @@
// Dark Souls 3 messages
#define DS3_TEMPLATES 17
const char templates3[DS3_TEMPLATES][32] =
{
"%s ahead",
"No %s ahead",
"%s required ahead",
"be wary of %s",
"try %s",
"Could this be a %s?",
"If only I had a %s...",
"visions of %s...",
"Time for %s",
"%s",
"%s!",
"%s?",
"%s...",
"Huh. It's a %s...",
"praise the %s!",
"Let there be %s",
"Ahh, %s..."
};
#define DS3_CONJUNCTIONS 10
const char conjunctions3[DS3_CONJUNCTIONS][32] =
{
" and then ",
" but ",
" therefore ",
" in short ",
" or ",
" only ",
" by the way ",
" so to speak ",
" all the more ",
", ",
};
#define DS3_GESTURES 33
const char gestures3[DS3_GESTURES][32] =
{
"Point forward",
"Point up",
"Point down",
"Wave",
"Beckon",
"Call over",
"Welcome",
"Applause",
"Quiet Resolve",
"Jump for joy",
"Joy",
"Rejoice",
"Hurrah!",
"Praise the Sun",
"My thanks!",
"Bow",
"Proper bow",
"Dignified bow",
"Duel bow",
"Legion Etiquette",
"Darkmoon Loyalty",
"By my sword",
"Prayer",
"Silent Ally",
"Rest",
"Collapse",
"Patches Squat",
"Prostration",
"Toast",
"Sleep",
"Curl up",
"Stretch out",
"Path of the Dragon"
};
#define DS3_WORDS 322
#define DS3_WORDS2 211
const char words3[DS3_WORDS][32] =
{
"enemy",
"monster",
"mob enemy",
"tough enemy",
"critical foe",
"Hollow",
"pilgrim",
"prisoner",
"monstrosity",
"skeleton",
"ghost",
"beast",
"lizard",
"bug",
"grub",
"crab",
"dwarf",
"giant",
"demon",
"dragon",
"knight",
"sellsword",
"warrior",
"herald",
"bandit",
"assassin",
"sorcerer",
"pyromancer",
"cleric",
"deprived",
"sniper",
"duo",
"trio",
"you",
"you bastard",
"good fellow",
"saint",
"wretch",
"charmer",
"poor soul",
"oddball",
"nimble one",
"laggard",
"moneybags",
"beggar",
"miscreant",
"liar",
"fatty",
"beanpole",
"youth",
"elder",
"old codger",
"old dear",
"miscreant",
"artisan",
"master",
"sage",
"champion",
"Lord of Cinder",
"king",
"queen",
"prince",
"princess",
"angel",
"god",
"friend",
"ally",
"spouse",
"covenantor",
"Phantom",
"Dark Spirit",
"bonfire",
"ember",
"fog wall",
"lever",
"contraption",
"key",
"trap",
"torch",
"door",
"treasure",
"chest",
"something",
"quite something",
"rubbish",
"filth",
"weapon",
"shield",
"projectile",
"armor",
"item",
"ring",
"ore",
"coal",
"transposing kiln",
"scroll",
"umbral ash",
"throne",
"rite",
"coffin",
"cinder",
"ash",
"moon",
"eye",
"brew",
"soup",
"message",
"bloodstain",
"illusion",
"close-ranged battle",
"ranged battle",
"eliminating one at a time",
"luring it out",
"beating to a pulp",
"ambush",
"pincer attack",
"hitting them in one swoop",
"dual-wielding",
"stealth",
"mimicry",
"fleeing",
"charging",
"jumping off",
"dashing through",
"circling around",
"trapping inside",
"rescue",
"Skill",
"sorcery",
"pyromancy",
"miracles",
"pure luck",
"prudence",
"brief respite",
"play dead",
"jog",
"dash",
"rolling",
"backstepping",
"jumping",
"attacking",
"jump attack",
"dash attack",
"counter attack",
"stabbing in the back",
"guard stun & stab",
"plunging attack",
"sweeping attack",
"shield breaking",
"blocking",
"parrying",
"locking-on",
"no lock-on",
"two-handing",
"gesture",
"control",
"destroy",
"boulder",
"lava",
"poison gas",
"enemy horde",
"forest",
"swamp",
"cave",
"shortcut",
"detour",
"hidden path",
"secret passage",
"dead end",
"labyrinth",
"hole",
"bright spot",
"dark spot",
"open area",
"tight spot",
"safe zone",
"danger zone",
"sniper spot",
"hiding place",
"illusory wall",
"ladder",
"lift",
"gorgeous view",
"looking away",
"overconfidence",
"slip-up",
"oversight",
"fatigue",
"bad luck",
"inattention",
"loss of stamina",
"chance encounter",
"planned encounter",
"front",
"back",
"left",
"right",
"up",
"down",
"below",
"above",
"behind",
"head",
"neck",
"stomach",
"back",
"arm",
"finger",
"leg",
"rear",
"tail",
"wings",
"anywhere",
"tongue",
"right arm",
"left arm",
"thumb",
"indexfinger",
"longfinger",
"ringfinger",
"smallfinger",
"right leg",
"left leg",
"right side",
"left side",
"pincer",
"wheel",
"core",
"mount",
"regular",
"strike",
"thrust",
"slash",
"magic",
"crystal",
"fire",
"chaos",
"lightning",
"blessing",
"dark",
"critical hits",
"bleeding",
"poison",
"toxic",
"frost",
"curse",
"equipment breakage",
"chance",
"quagmire",
"hint",
"secret",
"sleeptalk",
"happiness",
"misfortune",
"life",
"death",
"demise",
"joy",
"fury",
"agony",
"sadness",
"tears",
"loyalty",
"betrayal",
"hope",
"despair",
"fear",
"losing sanity",
"victory",
"defeat",
"sacrifice",
"light",
"dark",
"bravery",
"confidence",
"vigor",
"revenge",
"resignation",
"overwhelming",
"regret",
"pointless",
"man",
"woman",
"friendship",
"love",
"recklessness",
"composure",
"guts",
"comfort",
"silence",
"deep",
"good luck",
"fine work",
"I did it!",
"I've failed...",
"here!",
"not here!",
"I can't take this...",
"lonely...",
"don't you dare!",
"do it!",
"look carefully",
"listen carefully",
"think carefully",
"this place again?",
"now the real fight begins",
"you don't deserve this",
"keep moving",
"pull back",
"give it up",
"don't give up",
"help me...",
"impossible...",
"bloody expensive...",
"let me out of here...",
"stay calm",
"like a dream...",
"seems familiar...",
"are you ready?",
"it'll happen to you too",
"praise the Sun!",
"may the flames guide thee"
};

View file

@ -1 +0,0 @@
// TODO Elden Ring messages

View file

@ -1,402 +0,0 @@
#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 = 0;
*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 = 0;
*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 savetext( int32_t namelen, char *name )
{
// TODO
}
int main( int argc, char **argv )
{
if ( argc < 2 )
{
printf("Usage: ucextract <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("TextBuffer") )
{
printf("Package %s does not contain script sources\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, super, pkg, name, flags, siz, ofs;
readexport2(&class,&super,&pkg,&name,&flags,&siz,&ofs);
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,"TextBuffer",l);
if ( !ismesh ) continue;
mkdir("Scripts",0775);
// get the highest group name (must be an export)
char *pkgn = 0;
int32_t pkgl = 0;
while ( pkg > 0 )
{
int32_t pclass, psuper, ppkg, pname, pflags, psiz, pofs;
getexport2(pkg-1,&pclass,&psuper,&ppkg,&pname,&pflags,&psiz,&pofs);
pkgn = (char*)(pkgfile+getname(pname,&pkgl));
pkg = ppkg;
}
char *snd = (char*)(pkgfile+getname(name,&l));
if ( pkgn && strncmp(pkgn,"None",pkgl) )
printf("Script found: %.*s.%.*s\n",pkgl,pkgn,l,snd);
else printf("Script 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 == 45 ) fpos -= 2; // ???
if ( head->pkgver == 41 ) fpos += 2; // ???
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;
}
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;
int32_t tl = 0;
char *sname;
if ( type == 10 )
{
int32_t sn;
sn = readindex();
sname = (char*)(pkgfile+getname(sn,&tl));
}
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 ( tl ) printf(" struct: %.*s\n",tl,sname);
if ( array && (type != 3) )
{
int idx = readindex();
//printf(" index: %d\n",idx);
}
fpos += psiz;
prop = readindex();
pname = (char*)(pkgfile+getname(prop,&l));
goto retry;
}
savetext(sndl,snd,head->pkgver,pkgl,pkgn);
fpos = prev;
}
free(pkgfile);
return 0;
}

View file

@ -1,620 +0,0 @@
#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 = 0;
*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;
}
typedef struct
{
uint8_t r, g, b, x;
} __attribute__((packed)) color_t;
typedef struct
{
uint8_t r, g, b;
} __attribute__((packed)) pal8_t;
const char sparktypes[29][32] =
{
"Burn", "Sparkle", "Pulse", "Signal", "Blaze",
"OzHasSpoken", "Cone", "BlazeRight", "BlazeLeft",
"Cylinder", "Cylinder3D", "Lissajous", "Jugglers",
"Emit", "Fountain", "Flocks", "Eels", "Organic",
"WanderOrganic", "RandomCloud", "CustomCloud",
"LocalCloud", "Stars", "LineLighting", "RampLighting",
"SphereLighting", "Wheel", "Gametes", "Sprinkler"
};
void savepalette( uint32_t pal, int32_t *palnamelen, char **palname )
{
size_t prev = fpos;
int32_t class, ofs, siz, name;
getexport(pal-1,&class,&ofs,&siz,&name);
*palname = (char*)(pkgfile+getname(name,palnamelen));
// 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 == 45 ) fpos -= 2; // ???
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;
}
int32_t l = 0;
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;
}
int32_t grouplen = 0;
char *group = 0;
if ( head->pkgver < 55 )
{
// group
fpos++;
int32_t grp = readindex();
group = (char*)(pkgfile+getname(grp,&grouplen));
}
// number of colors
int32_t num = readindex();
char fname[256];
if ( group && strncmp(group,"None",grouplen) )
{
snprintf(fname,256,"Palettes/%.*s",grouplen,group);
mkdir(fname,0775);
snprintf(fname,256,"Palettes/%.*s/%.*s.pal",grouplen,group,
*palnamelen,*palname);
}
else snprintf(fname,256,"Palettes/%.*s.pal",*palnamelen,*palname);
color_t *paldat = (color_t*)(pkgfile+fpos);
pal8_t *palconv = calloc(sizeof(pal8_t),num);
for ( int i=0; i<num; i++ )
{
palconv[i].r = paldat[i].r;
palconv[i].g = paldat[i].g;
palconv[i].b = paldat[i].b;
// discard X
}
fpos = prev;
int fd = open(fname,O_WRONLY|O_CREAT|O_TRUNC,0644);
if ( fd == -1 )
{
printf("Failed to open file %s: %s\n",fname,strerror(errno));
free(palconv);
return;
}
int w = write(fd,palconv,sizeof(pal8_t)*num);
if ( w == -1 )
{
close(fd);
free(palconv);
printf("Write failed for file %s: %s\n",fname,strerror(errno));
return;
}
close(fd);
free(palconv);
}
void savetexture( int32_t namelen, char *name, uint32_t pal, int brising,
uint8_t renderheat, uint32_t sparklimit, int version, int32_t grouplen,
char *group )
{
if ( version < 55 )
{
// group
fpos++;
int32_t grp = readindex();
group = (char*)(pkgfile+getname(grp,&grouplen));
}
uint8_t mipcnt = readbyte();
uint32_t ofs = 0;
if ( version >= 63 ) ofs = readdword();
uint32_t datasiz = readindex();
if ( version >= 63 ) fpos = ofs;
else fpos += datasiz;
uint32_t w = readdword();
uint32_t h = readdword();
readbyte(); // ubits
readbyte(); // vbits
// skip all the other mipmaps, we only need the dimensions of mip zero
for ( int i=1; i<mipcnt; i++ )
{
if ( version >= 63 ) ofs = readdword();
datasiz = readindex();
if ( version >= 63 ) fpos = ofs;
else fpos += datasiz;
readdword();
readdword();
readbyte();
readbyte();
}
char fname[256];
if ( group && strncmp(group,"None",grouplen) )
{
snprintf(fname,256,"Textures/%.*s",grouplen,group);
mkdir(fname,0775);
snprintf(fname,256,"Textures/%.*s/%.*s.ftx",grouplen,group,
namelen,name);
}
else snprintf(fname,256,"Textures/%.*s.ftx",namelen,name);
// we're using stdio for this one, it's cheaper
FILE *f = fopen(fname,"wb");
if ( !f )
{
printf("Failed to open file %s: %s\n",fname,strerror(errno));
return;
}
// texture name, width and height
fprintf(f,"%.*s %u %u\n",namelen,name,w,h);
// palette name
int32_t palnamelen = 0;
char *palname = 0;
savepalette(pal,&palnamelen,&palname);
fprintf(f,"%.*s\n",palnamelen,palname);
// bRising, RenderHeat and SparkLimit
fprintf(f,"%d %hhu %u\n",!!brising,renderheat,sparklimit);
// individual sparks (type as string, heat, x, y, args[4])
int32_t nsparks = readindex(); // TArray<FSpark> size
for ( int32_t i=0; i<nsparks; i++ )
{
uint8_t type, heat, x, y, args[4];
type = readbyte();
heat = readbyte();
x = readbyte();
y = readbyte();
args[0] = readbyte();
args[1] = readbyte();
args[2] = readbyte();
args[3] = readbyte();
if ( type >= 29 ) continue; // discard unknown types
fprintf(f,"%s %hhu %hhu %hhu %hhu %hhu %hhu %hhu\n",
sparktypes[type],heat,x,y,
args[0],args[1],args[2],args[3]);
}
fclose(f);
}
int main( int argc, char **argv )
{
if ( argc < 2 )
{
printf("Usage: uftxextract <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, super, pkg, name, siz, ofs;
uint32_t flags;
readexport2(&class,&super,&pkg,&name,&flags,&siz,&ofs);
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,"FireTexture",l);
if ( !istex ) continue;
mkdir("Textures",0775);
mkdir("Palettes",0775);
// get the highest group name (must be an export)
char *pkgn = 0;
int32_t pkgl = 0;
while ( pkg > 0 )
{
int32_t pclass, psuper, ppkg, pname, psiz, pofs;
uint32_t pflags;
getexport2(pkg-1,&pclass,&psuper,&ppkg,&pname,&pflags,&psiz,&pofs);
pkgn = (char*)(pkgfile+getname(pname,&pkgl));
pkg = ppkg;
}
char *tex = (char*)(pkgfile+getname(name,&l));
if ( pkgn && strncmp(pkgn,"None",pkgl) )
printf("FireTexture found: %.*s.%.*s\n",pkgl,pkgn,l,tex);
else printf("FireTexture 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 == 45 ) fpos -= 2; // ???
if ( head->pkgver == 41 ) fpos += 2; // ???
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));
uint8_t rising = 0; // as well as these
uint8_t heat = 200;
uint32_t limit = 1024;
retry:
if ( strncasecmp(pname,"None",l) )
{
uint8_t info = readbyte();
int array = info&0x80;
int type = info&0xf;
int psiz = (info>>4)&0x7;
//int32_t tl = 0;
//char *sname = 0;
if ( type == 10 )
{
//int32_t sn;
/*sn = */readindex();
//sname = (char*)(pkgfile+getname(sn,&tl));
}
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 ( tl ) printf(" struct: %.*s\n",tl,sname);
if ( array && (type != 3) )
{
/*int idx = */readindex();
//printf(" index: %d\n",idx);
}
if ( !strncasecmp(pname,"Palette",l) )
pal = readindex();
else if ( !strncasecmp(pname,"bRising",l) )
rising = (info>>7)&1;
else if ( !strncasecmp(pname,"RenderHeat",l) )
heat = readbyte();
else if ( !strncasecmp(pname,"SparksLimit",l) )
limit = readdword();
else fpos += psiz;
prop = readindex();
pname = (char*)(pkgfile+getname(prop,&l));
goto retry;
}
if ( pal )
savetexture(texl,tex,pal,rising,heat,limit,head->pkgver,pkgl,pkgn);
fpos = prev;
}
free(pkgfile);
return 0;
}

View file

@ -3,7 +3,6 @@
#include <stdint.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
#define UMOD_MAGIC 0x9FE3C5A3
@ -95,8 +94,6 @@ int main( int argc, char **argv )
int32_t ndir = readindex(f);
//printf("ndir: %d\n",ndir);
umoddir_t *dir = calloc(ndir,sizeof(umoddir_t));
char cwd[4096];
getcwd(&cwd[0],4096);
for ( int32_t i=0; i<ndir; i++ )
{
int32_t flen = readindex(f);
@ -136,23 +133,21 @@ int main( int argc, char **argv )
FILE *fout;
// subdivide folders
char *nxt = 0;
int j = 0;
//int i = 0;
do
{
nxt = strchr(rpath,'\\');
if ( nxt )
{
*nxt = '\0';
//printf("path%d: %s\n",j,rpath);
//printf("path%d: %s\n",i,rpath);
mkdir(rpath,0755);
chdir(rpath);
*nxt = '/';
rpath = nxt+1;
}
else
{
// the file itself
chdir(cwd);
//printf("fout: %s\n",dir[i].fname);
printf("==== EXTRACTING %s (%u bytes) ====\n",dir[i].fname,dir[i].len);
fout = fopen(dir[i].fname,"wb");
@ -162,7 +157,7 @@ int main( int argc, char **argv )
free(dat);
fclose(fout);
}
j++;
//i++;
} while ( nxt );
}
}