Skip to content

Instantly share code, notes, and snippets.

@hogsy
Last active November 26, 2023 23:32
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save hogsy/90ae893ded9c2b42796c5eff8788d1ba to your computer and use it in GitHub Desktop.
Save hogsy/90ae893ded9c2b42796c5eff8788d1ba to your computer and use it in GitHub Desktop.
/*
FreshEngine Bin Packer
Copyright (C) 2023 Mark E. Sowden <hogsy@snortysoft.net>
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 2 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.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "plcore/pl.h"
#include "plcore/pl_filesystem.h"
#include "plcore/pl_linkedlist.h"
#include "plcore/pl_package.h"
static PLLinkedList *filesList;
static const unsigned int BLOCK_SIZE = 2048U;
static size_t calculate_stream_length( size_t dataSize )
{
return ceil( ( double ) dataSize / BLOCK_SIZE ) * BLOCK_SIZE;
}
#define F3D_BIN_NAME "DATA.BIN"
#define F3D_BIN_IDENT "DATA "
#define F3D_BIN_IDENT_SIZE 8
#define F3D_BIN_FILENAME_SIZE 12
typedef struct F3dBinHeader
{
char ident[ F3D_BIN_IDENT_SIZE ];
uint32_t x;
uint32_t y;
uint32_t numFiles;
} F3dBinHeader;
static_assert( sizeof( F3dBinHeader ) == 20, "invalid struct size" );
typedef struct F3dBinIndex
{
char filename[ F3D_BIN_FILENAME_SIZE ];
uint32_t block;
uint32_t size;
} F3dBinIndex;
static_assert( sizeof( F3dBinIndex ) == 20, "invalid struct size" );
static void add_pack_file( const char *path, void * )
{
const char *filename = PlGetFileName( path );
size_t bufSize = strlen( filename ) + 1;
char *buf = PL_NEW_( char, bufSize );
snprintf( buf, bufSize, "%s", filename );
PlInsertLinkedListNode( filesList, buf );
}
static void *cache_file( const char *path, unsigned int *size )
{
PLFile *file = PlOpenLocalFile( path, true );
if ( file == NULL )
return NULL;
*size = PlGetFileSize( file );
char *data = PL_NEW_( char, *size + 1 );
memcpy( data, PlGetFileData( file ), *size );
PlCloseFile( file );
return data;
}
int main( int argc, char **argv )
{
PlInitialize( argc, argv );
filesList = PlCreateLinkedList();
const char *arg;
if ( ( arg = PlGetCommandLineArgumentValue( "/o" ) ) != NULL )
{
// This route ensures that the order of the files in the package match the original,
// by using the original toc as a reference
FILE *file = fopen( arg, "rb" );
if ( file == NULL )
{
perror( "Failed to open origin package" );
return EXIT_FAILURE;
}
F3dBinHeader header;
fread( &header, sizeof( F3dBinHeader ), 1, file );
if ( strncmp( header.ident, "DATA ", 8 ) != 0 )
{
fprintf( stderr, "Package is not a FreshEngine package!\n" );
return EXIT_FAILURE;
}
for ( unsigned int i = 0; i < header.numFiles; ++i )
{
F3dBinIndex index;
fread( &index, sizeof( F3dBinIndex ), 1, file );
char *buf = PL_NEW_( char, sizeof( index.filename ) + 1 );
for ( unsigned int j = 0; j < sizeof( index.filename ); ++j )
buf[ j ] = ( index.filename[ j ] == ' ' ) ? '\0' : index.filename[ j ];
PlInsertLinkedListNode( filesList, buf );
}
fclose( file );
}
else
PlScanDirectory( "./", NULL, add_pack_file, false, NULL );
unsigned int numFiles = PlGetNumLinkedListNodes( filesList );
if ( numFiles == 0 )
{
fprintf( stderr, "No files found in directory!\n" );
return EXIT_FAILURE;
}
PlCreateDirectory( "out" );
FILE *file = fopen( "out/" F3D_BIN_NAME, "wb" );
if ( file == NULL )
{
perror( "Failed to open write destination" );
return EXIT_FAILURE;
}
F3dBinHeader header = { .ident = F3D_BIN_IDENT, .numFiles = numFiles };
fwrite( &header, sizeof( F3dBinHeader ), 1, file );
size_t totalSize = sizeof( header ) + ( sizeof( F3dBinIndex ) * numFiles );
PLLinkedListNode *node = PlGetFirstNode( filesList );
while ( node != NULL )
{
char *filename = PlGetLinkedListNodeUserData( node );
F3dBinIndex index;
strncpy( index.filename, filename, sizeof( index.filename ) );
for ( unsigned int i = 0; i < sizeof( index.filename ); ++i )
index.filename[ i ] = index.filename[ i ] == '\0' ? ' ' : toupper( index.filename[ i ] );
index.block = calculate_stream_length( totalSize ) / BLOCK_SIZE;
index.size = PlGetLocalFileSize( filename );
fwrite( &index, sizeof( F3dBinIndex ), 1, file );
node = PlGetNextLinkedListNode( node );
totalSize = index.block * BLOCK_SIZE + index.size;
}
node = PlGetFirstNode( filesList );
while ( true )
{
size_t pos = calculate_stream_length( ftell( file ) );
printf( "padding to %lu (block %zu)\n", pos, ( pos / BLOCK_SIZE ) );
while ( ftell( file ) != pos )
fputc( 0, file );
if ( node == NULL )
break;
unsigned int size;
char *filename = PlGetLinkedListNodeUserData( node );
void *data = cache_file( filename, &size );
if ( data == NULL )
{
fprintf( stderr, "Failed to cache a specific file (%s): %s\n", filename, PlGetError() );
return EXIT_FAILURE;
}
fwrite( data, sizeof( char ), size, file );
PL_DELETEN( data );
node = PlGetNextLinkedListNode( node );
}
fclose( file );
return EXIT_SUCCESS;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment