gist/project-83113-unpacker.c

151 lines
3.6 KiB
C

/*
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 <stdio.h>
#include <stdint.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <errno.h>
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 <data.pap or main.2.corp.ncsoft.belle.obb>\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;
}