diff --git a/project-83113-unpacker.c b/project-83113-unpacker.c new file mode 100644 index 0000000..e3428a3 --- /dev/null +++ b/project-83113-unpacker.c @@ -0,0 +1,150 @@ +/* +project-83113-unpacker.c -- some obscure mobile game assets unpacker +Copyright (C) 2024 Alibek Omarov + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +*/ +#include +#include +#include +#include +#include +#include +#include +#include + +struct archive_file_t +{ + uint32_t offset; + uint32_t size; + uint32_t namelen; + struct archive_file_t *next; + char name[]; +}; + +static struct archive_file_t *file_list; + +static void error( const char *fmt, ... ) +{ + va_list va; + + va_start( va, fmt ); + vprintf( fmt, va ); + va_end( va ); + + exit( 1 ); +} + +int read_directory( FILE *f, uint32_t directory_size ) +{ + struct archive_file_t *archive_file; + uint32_t offset; + uint32_t size; + uint32_t namelen; + + if( fread( &namelen, 1, sizeof( namelen ), f ) != sizeof( namelen )) + error( "can't read file name length at %d\n", ftell( f )); + + archive_file = malloc( sizeof( *archive_file ) + namelen + 1 ); + archive_file->namelen = namelen; + if( fread( archive_file->name, namelen, 1, f ) != 1 ) + error( "can't read file name at %d\n", ftell( f )); + archive_file->name[namelen] = 0; + + if( fread( &size, 1, sizeof( size ), f ) != sizeof( size )) + error( "can't read file size at %d\n", ftell( f )); + + archive_file->size = size; + + if( fread( &offset, 1, sizeof( offset ), f ) != sizeof( offset )) + error( "can't read file offset at %d\n", ftell( f )); + + archive_file->offset = offset + directory_size + 8; + archive_file->next = file_list; + file_list = archive_file; + + printf( "discovered %s at 0x%x size 0x%x\n", archive_file->name, offset, size ); + + return 1; +} + +void create_directory( char *path ) +{ + char *p = path; + int err; + + while(( p = strchr( p, '/' ))) + { + *p = 0; + err = mkdir( path, 0777 ); + if( err < 0 && errno != EEXIST ) + error( "can't create directory at %s: %s\n", path, strerror( errno )); + *p = '/'; + p++; + } +} + +void extract_files( FILE *f ) +{ + struct archive_file_t *file; + + for( file = file_list; file; file = file->next ) + { + char *tmp = malloc( file->size ); + if( fseek( f, file->offset, SEEK_SET ) < 0 ) + error( "can't seek to %d: %s\n", file->offset, strerror( errno )); + + if( fread( tmp, file->size, 1, f ) != 1 ) + error( "can't read file %s\n", file->name ); + + create_directory( file->name ); + + FILE *out = fopen( file->name, "wb" ); + if( !out ) + error( "can't create file %s: %s\n", file->name, strerror( errno )); + + if( fwrite( tmp, file->size, 1, out ) != 1 ) + error( "can't write file %s: %s\n", file->name, strerror( errno )); + + printf( "extracted %s...\n", file->name ); + free( tmp ); + fclose( out ); + } +} + +int main( int argc, char **argv ) +{ + FILE *f; + + if( argc != 2 ) + error( "usage: %s \n", argv[0] ); + + f = fopen( argv[1], "rb" ); + uint32_t file_size, directory_size; + + fseek( f, 0, SEEK_END ); + file_size = ftell( f ); + fseek( f, 0, SEEK_SET ); + + printf( "file size is %d\n", file_size ); + if( fread( &directory_size, 1, sizeof( directory_size ), f ) != sizeof( directory_size )) + error( "can't read directory size\n" ); + + // skip 4 bytes of unknown stuff + fseek( f, 4, SEEK_CUR ); + + while( ftell( f ) < directory_size + 8 ) + read_directory( f, directory_size ); + + extract_files( f ); + + return 0; +}