/* 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; }