diff options
| author | Felix Morgner <felix.morgner@gmail.com> | 2026-08-24 11:16:07 +0200 |
|---|---|---|
| committer | Felix Morgner <felix.morgner@gmail.com> | 2026-08-24 11:16:07 +0200 |
| commit | c068f22329d5cc722622a2183bbb22eef2093df7 (patch) | |
| tree | 12d56c1aede67988a55e241364606bfbb4dba933 /src/parsec/t_pack.cpp | |
| download | openparsec-main.tar.xz openparsec-main.zip | |
Diffstat (limited to 'src/parsec/t_pack.cpp')
| -rw-r--r-- | src/parsec/t_pack.cpp | 769 |
1 files changed, 769 insertions, 0 deletions
diff --git a/src/parsec/t_pack.cpp b/src/parsec/t_pack.cpp new file mode 100644 index 0000000..9baedc0 --- /dev/null +++ b/src/parsec/t_pack.cpp @@ -0,0 +1,769 @@ +/* + * PARSEC - Data Package Tools + * + * $Author: uberlinuxguy $ - $Date: 2004/09/26 03:43:42 $ + * + * Orginally written by: + * Copyright (c) Markus Hadwiger <msh@parsec.org> 1996-2000 + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +// C library +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <stdarg.h> + +// compilation flags/debug support +#include "config.h" +#include "debug.h" + +// general definitions +#include "general.h" +#include "gd_heads.h" +#include "objstruc.h" + +// global externals +#include "globals.h" + +// local module header +#include "t_pack.h" + +// proprietary module headers +#include "e_getopt.h" +#include "sys_swap.h" + + + +// string constants ----------------------------------------------------------- +// +static char pack_signature[] = "msh DataPackage"; +static char options_invalid[] = "invalid command line option.\n"; +static char pack_name_missing[] = "name of package file missing.\n"; + + +// variables settable via command line options -------------------------------- +// +static char arg_package_name[ PATH_MAX + 1 ]; +static size_t arg_package_file_offset = 0; +static char arg_file_name[ PATH_MAX + 1 ]; +static char arg_list_name[ PATH_MAX + 1 ]; +static int arg_view_files = FALSE; + + +// message output wrappers ---------------------------------------------------- +// +#define Printf MSGOUT +#define PrintfNoNL MSGPUT +#define Err_Printf MSGOUT + + +// exit function that makes sure that messages can be read by the user -------- +// +PRIVATE +void Exit( int code ) +{ + SYSs_MsgPrompt(); + exit( code ); +} + + +// set name of package -------------------------------------------------------- +// +PRIVATE +int Tm_SetPackageName( char **packname ) +{ + strncpy( arg_package_name, packname[ 0 ], PATH_MAX ); + arg_package_name[ PATH_MAX ] = 0; + + return TRUE; +} + + +// set offset of package start in package file -------------------------------- +// +PRIVATE +int Tm_SetPackageOffset( int *packofs ) +{ + arg_package_file_offset = packofs[ 0 ]; + + return TRUE; +} + + +// set file name argument ----------------------------------------------------- +// +PRIVATE +int Tm_SetFileName( char **filename ) +{ + strncpy( arg_file_name, filename[ 0 ], PATH_MAX ); + arg_file_name[ PATH_MAX ] = 0; + + return TRUE; +} + + +// set list name argument ----------------------------------------------------- +// +PRIVATE +int Tm_SetListName( char **listname ) +{ + strncpy( arg_list_name, listname[ 0 ], PATH_MAX ); + arg_list_name[ PATH_MAX ] = 0; + + return TRUE; +} + + +// set flag to display file names --------------------------------------------- +// +PRIVATE +int Tm_ViewFileList() +{ + arg_view_files = TRUE; + return TRUE; +} + + +// data file list item (info read from text file) ----------------------------- +// +struct list_item_s { + + char file[ PATH_MAX + 1 ]; + dword foffset; + dword flength; +}; + + +// read file list ------------------------------------------------------------- +// +PRIVATE +int ReadFileList( const char *filename, list_item_s *listitems, int maxitems ) +{ + ASSERT( filename != NULL ); + ASSERT( listitems != NULL ); + ASSERT( maxitems > 0 ); + + FILE *fp = fopen( filename, "r" ); + if ( fp == NULL ) { + Err_Printf( "file list \"%s\" not found.\n", filename ); + return -1; + } + + Printf( "processing file list ..." ); + + int pcount = 32; + int numitems = 0; + + static char list_line[ PATH_MAX + 1 ]; + + // read file list + while ( fgets( list_line, PATH_MAX + 1, fp ) != NULL ) { + + char *scan = strtok( list_line, " \t\n\r" ); + if ( scan == NULL ) { + continue; + } + + if ( ( *scan == ';' ) || ( *scan == '#' ) ) { + continue; + } + + if ( --pcount == 0 ) { + PrintfNoNL( "." ); + pcount = 32; + } + + if ( numitems >= maxitems ) { + Err_Printf( "\ntoo many files. (%d max)\n", maxitems ); + fclose( fp ); + return -1; + } + + strncpy( listitems[ numitems ].file, scan, PATH_MAX ); + listitems[ numitems ].file[ PATH_MAX ] = 0; + numitems++; + } + + Printf( "\n" ); + Printf( "file list read.\n" ); + Printf( "%d items found.\n", numitems ); + + fclose( fp ); + return numitems; +} + + +// copy string and strip path ------------------------------------------------- +// +PRIVATE +void StrcpyStripPath( char *dst, const char *src ) +{ + ASSERT( dst != NULL ); + ASSERT( src != NULL ); + + int baselen = 0; + int pos = 0; + for ( pos = strlen( src ) - 1; pos >= 0; pos-- ) { + + if ( src[ pos ] == '\\' ) + break; + if ( src[ pos ] == '/' ) + break; + if ( src[ pos ] == ':' ) + break; + + if ( ++baselen >= 12 ) { + pos--; + break; + } + } + + if ( baselen > 0 ) { + strncpy( dst, &src[ pos + 1 ], baselen ); + dst[ baselen ] = 0; + } +} + + +// write header/file info list into output package ---------------------------- +// +PRIVATE +int WritePackageHeader( FILE *fp, list_item_s *listitems, int numitems ) +{ + ASSERT( fp != NULL ); + ASSERT( listitems != NULL ); + ASSERT( numitems > 0 ); + + // alloc file info list + size_t headersize = ( numitems + 1 ) * sizeof( pfileinfodisk_s ); + pfileinfodisk_s *items = (pfileinfodisk_s *) ALLOCMEM( headersize ); + if ( items == NULL ) + return FALSE; + memset( items, 0, headersize ); + + size_t datasize = 0; + size_t curoffset = sizeof( packageheader_s ) + headersize; + + // create file info list + int curit = 0; + for ( curit = 0; curit < numitems; curit++ ) { + + PrintfNoNL( "." ); + + StrcpyStripPath( items[ curit ].file, listitems[ curit ].file ); + + FILE *itfp = fopen( listitems[ curit ].file, "rb" ); + if ( itfp == NULL ) { + Err_Printf( "\ndata file \"%s\" not found: %s\n", + listitems[ curit ].file, strerror( errno ) ); + FREEMEM( items ); + return FALSE; + } + if ( fseek( itfp, 0, SEEK_END ) != 0 ) { + Err_Printf( "\ndata file \"%s\" access error: %s\n", + listitems[ curit ].file, strerror( errno ) ); + fclose( itfp ); + FREEMEM( items ); + return FALSE; + } + + items[ curit ].flength = (dword) ftell( itfp ); + listitems[ curit ].flength = items[ curit ].flength; + + items[ curit ].foffset = curoffset; + listitems[ curit ].foffset = items[ curit ].foffset; + + datasize += items[ curit ].flength; + curoffset += items[ curit ].flength; + + fclose( itfp ); + } + + // create fixed-size header + packageheader_s packheader; + packheader.numitems = numitems; + packheader.headersize = headersize; + packheader.datasize = datasize; + packheader.packsize = sizeof( packageheader_s ) + headersize + datasize; + strcpy( packheader.signature, pack_signature ); + + Printf( "\ndata size: %d\n", datasize ); + + // swap endianness of fixed-size part of header + SYS_SwapPackageHeader( &packheader ); + + // write fixed-size header + size_t byteswritten = fwrite( &packheader, 1, sizeof( packheader ), fp ); + if ( byteswritten != sizeof( packheader ) ) { + FREEMEM( items ); + return FALSE; + } + + // swap endianness of file info list + for ( curit = 0; curit < numitems; curit++ ) { + SYS_SwapPFileInfo( &items[ curit ] ); + } + + // write file info list + byteswritten = fwrite( items, 1, headersize, fp ); + if ( byteswritten != headersize ) { + FREEMEM( items ); + return FALSE; + } + + FREEMEM( items ); + return TRUE; +} + + +// write actual file data into output package --------------------------------- +// +PRIVATE +int WritePackageData( FILE *fp, list_item_s *listitems, int numitems ) +{ + ASSERT( fp != NULL ); + ASSERT( listitems != NULL ); + ASSERT( numitems > 0 ); + + for ( int curit = 0; curit < numitems; curit++ ) { + + PrintfNoNL( "." ); + + FILE *itfp = fopen( listitems[ curit ].file, "rb" ); + if ( itfp == NULL ) { + Err_Printf( "\ndata file \"%s\" not found: %s\n", + listitems[ curit ].file, strerror( errno ) ); + return FALSE; + } + + char *filedata = (char *) ALLOCMEM( listitems[ curit ].flength ); + if ( filedata == NULL ) { + fclose( itfp ); + return FALSE; + } + + size_t bytesread = fread( filedata, 1, listitems[ curit ].flength, itfp ); + if ( bytesread != listitems[ curit ].flength ) { + FREEMEM( filedata ); + fclose( itfp ); + return FALSE; + } + + size_t byteswritten = fwrite( filedata, 1, listitems[ curit ].flength, fp ); + if ( byteswritten != listitems[ curit ].flength ) { + FREEMEM( filedata ); + fclose( itfp ); + return FALSE; + } + + fclose( itfp ); + FREEMEM( filedata ); + } + + return TRUE; +} + + + +// write package data --------------------------------------------------------- +// +PRIVATE +int WritePackageFile( const char* packname, list_item_s *listitems, int numitems ) +{ + ASSERT( packname != NULL ); + ASSERT( listitems != NULL ); + ASSERT( numitems > 0 ); + + Printf( "creating file package \"%s\" ...", packname ); + fflush( stdout ); + + // create package file + FILE *fp = fopen( packname, "wb" ); + if ( fp == NULL ) { + Err_Printf( "\ncannot create output file: %s\n", strerror( errno ) ); + return FALSE; + } + + if ( !WritePackageHeader( fp, listitems, numitems ) ) { + fclose( fp ); + return FALSE; + } + + if ( !WritePackageData( fp, listitems, numitems ) ) { + fclose( fp ); + return FALSE; + } + + fclose( fp ); + return TRUE; +} + + +// critical error exit -------------------------------------------------------- +// +PRIVATE +void MakepackError() +{ + Err_Printf( "-makepack: critical error.\n" ); + Exit( EXIT_FAILURE ); +} + + +// create data package -------------------------------------------------------- +// +PRIVATE +int CreatePackage( const char* packname, const char *listname ) +{ + ASSERT( packname != NULL ); + ASSERT( listname != NULL ); + + #define MAX_DATA_FILES 4096 + + list_item_s *listitems = (list_item_s *) ALLOCMEM( MAX_DATA_FILES * sizeof( list_item_s ) ); + if ( listitems == NULL ) { + return FALSE; + } + + int numitems = ReadFileList( listname, listitems, MAX_DATA_FILES ); + if ( numitems == -1 ) { + FREEMEM( listitems ); + MakepackError(); + } + + if ( !WritePackageFile( packname, listitems, numitems ) ) { + FREEMEM( listitems ); + MakepackError(); + } + + FREEMEM( listitems ); + return TRUE; +} + + +// strip extension from supplied file name ------------------------------------ +// +PRIVATE +void StripExtension( char *fname ) +{ + ASSERT( fname != NULL ); + + for ( int len = strlen( fname ); len >= 0; len-- ) { + if ( fname[ len ] == '.' ) { + fname[ len ] = 0; + break; + } + if ( fname[ len ] == '\\' ) { + break; + } + if ( fname[ len ] == '/' ) { + break; + } + if ( fname[ len ] == ':' ) { + break; + } + } +} + + +// package creation tool main function ---------------------------------------- +// +void MAKEPACK_main( int argc, char **argv ) +{ + // clear options of main app + OPT_ClearOptions(); + + // register local options + OPT_RegisterStringOption( "p", "pack", Tm_SetPackageName ); + OPT_RegisterStringOption( "f", "list", Tm_SetListName ); + + // exec all registered command line options + if ( !OPT_ExecRegisteredOptions( argc, argv ) ) { + Err_Printf( options_invalid ); + Exit( EXIT_FAILURE ); + } + + // package name is mandatory + if ( arg_package_name[ 0 ] == 0 ) { + Err_Printf( pack_name_missing ); + Exit( EXIT_FAILURE ); + } + + // list name is optional (use package with different extension by default) + if ( arg_list_name[ 0 ] == 0 ) { + strcpy( arg_list_name, arg_package_name ); + StripExtension( arg_list_name ); + strcat( arg_list_name, ".lst" ); + } + + // create package + CreatePackage( arg_package_name, arg_list_name ); + + // end of sub-application + Exit( EXIT_SUCCESS ); +} + + +// optional file name parameters ---------------------------------------------- +// +static char *extract_file_name = NULL; +static char *list_file_name = NULL; + + +// extract data of packaged file(s) ------------------------------------------- +// +PRIVATE +int ExtractPackageData( FILE *fp, FILE *listfp, size_t baseofs, int numitems, pfileinfodisk_s *items ) +{ + ASSERT( fp != NULL ); + ASSERT( numitems >= 0 ); + ASSERT( items != NULL ); + + int numextracted = 0; + + // write out separate data files + for ( int curit = 0; curit < numitems; curit++ ) { + + // extract only specified file if name set + if ( extract_file_name != NULL ) { + if ( stricmp( items[ curit ].file, extract_file_name ) != 0 ) { + continue; + } + } + + // check for viewing mode (no actual file extraction) + if ( arg_view_files ) { + Printf( "file %s size %d\n", items[ curit ].file, items[ curit ].flength ); + continue; + } + + char *curdata = (char *) ALLOCMEM( items[ curit ].flength ); + if ( curdata == NULL ) { + return -1; + } + + // set read position + size_t readpos = baseofs + items[ curit ].foffset; + if ( fseek( fp, readpos, SEEK_SET ) != 0 ) { + FREEMEM( curdata ); + return -1; + } + + // read packaged file data + size_t bytesread = fread( curdata, 1, items[ curit ].flength, fp ); + if ( bytesread != items[ curit ].flength ) { + FREEMEM( curdata ); + return -1; + } + + // display current file and write it to list if open + Printf( "*extracting %s\n", items[ curit ].file ); + if ( listfp != NULL ) { + fprintf( listfp, "%s\n", items[ curit ].file ); + } + + // create output file + FILE *wfp = fopen( items[ curit ].file, "wb" ); + if ( wfp == NULL ) { + FREEMEM( curdata ); + return -1; + } + + // write file data to separate file + size_t byteswritten = fwrite( curdata, 1, items[ curit ].flength, wfp ); + if ( byteswritten != items[ curit ].flength ) { + FREEMEM( curdata ); + fclose( wfp ); + return -1; + } + + if ( fclose( wfp ) != 0 ) { + FREEMEM( curdata ); + return -1; + } + + FREEMEM( curdata ); + numextracted++; + } + + return numextracted; +} + + +// critical error exit -------------------------------------------------------- +// +PRIVATE +void GetpackError() +{ + Err_Printf( "-getpack: critical error.\n" ); + Exit( EXIT_FAILURE ); +} + + +// extract file(s) from package ----------------------------------------------- +// +PRIVATE +int ExtractPackage( const char* packname, size_t baseofs ) +{ + ASSERT( packname != NULL ); + + // open package for reading at specified offset + FILE *fp = fopen( packname, "rb" ); + if ( fp == NULL ) { + Err_Printf( "package %s not found.\n", packname ); + GetpackError(); + } + if ( fseek( fp, baseofs, SEEK_SET ) != 0 ) { + fclose( fp ); + GetpackError(); + } + + packageheader_s packheader; + + // read fixed-size part of header + size_t bytesread = fread( &packheader, 1, sizeof( packheader ), fp ); + if ( bytesread != sizeof( packheader ) ) { + fclose( fp ); + GetpackError(); + } + + // file format validation + if ( stricmp( packheader.signature, pack_signature ) != 0 ) { + Err_Printf( "package %s is invalid.\n", packname ); + fclose( fp ); + GetpackError(); + } + + // swap endianness of fixed-size part of header + SYS_SwapPackageHeader( &packheader ); + + // fetch size of remaining header and number of contained items + dword headersize = packheader.headersize; + dword numitems = packheader.numitems; + + // display number of contained items if in viewing mode + if ( arg_view_files ) { + Printf( "package contains %d items.\n\n", numitems ); + } + + // read file list + pfileinfodisk_s *items = (pfileinfodisk_s *) ALLOCMEM( headersize ); + if ( items == NULL ) { + fclose( fp ); + GetpackError(); + } + if ( fread( items, 1, headersize, fp ) != headersize ) { + FREEMEM( items ); + fclose( fp ); + GetpackError(); + } + + // swap endianness of file info list + for ( unsigned int curit = 0; curit < numitems; curit++ ) { + SYS_SwapPFileInfo( &items[ curit ] ); + } + + // open list output file if specified + FILE *listfp = NULL; + if ( ( list_file_name != NULL ) && ( extract_file_name == NULL ) ) { + listfp = fopen( list_file_name, "w" ); + fprintf( listfp, "\n" ); + fprintf( listfp, "; file list of package \"%s\"\n", packname ); + fprintf( listfp, "\n" ); + } + + // extract file data + Printf( "extracting from package %s ...\n", packname ); + int numextracted = ExtractPackageData( fp, listfp, baseofs, numitems, items ); + + // close/free resources + if ( listfp != NULL ) { + fprintf( listfp, "\n" ); + fclose( listfp ); + listfp = NULL; + } + fclose( fp ); + FREEMEM( items ); + + if ( numextracted == -1 ) { + GetpackError(); + } + + if ( extract_file_name == NULL ) { + Printf( "%d files extracted.\n", numextracted ); + return TRUE; + } + + if ( numextracted < 1 ) { + Printf( "file %s not found in package %s\n", extract_file_name, packname ); + return FALSE; + } + + return TRUE; +} + + +// package extraction tool main function -------------------------------------- +// +void GETPACK_main( int argc, char **argv ) +{ + // clear options of main app + OPT_ClearOptions(); + + // register local options + OPT_RegisterStringOption( "p", "pack", Tm_SetPackageName ); + OPT_RegisterIntOption( "o", "offset", Tm_SetPackageOffset ); + OPT_RegisterStringOption( "f", "file", Tm_SetFileName ); + OPT_RegisterStringOption( "l", "list", Tm_SetListName ); + OPT_RegisterSetOption( "v", "view", Tm_ViewFileList ); + + // exec all registered command line options + if ( !OPT_ExecRegisteredOptions( argc, argv ) ) { + Err_Printf( options_invalid ); + Exit( EXIT_FAILURE ); + } + + // package name is mandatory + if ( arg_package_name[ 0 ] == 0 ) { + Err_Printf( pack_name_missing ); + Exit( EXIT_FAILURE ); + } + + // file name is optional (operate on entire package by default) + extract_file_name = ( arg_file_name[ 0 ] != 0 ) ? arg_file_name : NULL; + + // list name is optional (do not create a list file by default) + list_file_name = ( arg_list_name[ 0 ] != 0 ) ? arg_list_name : NULL; + + // extract one file or all files from package + ExtractPackage( arg_package_name, arg_package_file_offset ); + + // end of sub-application + Exit( EXIT_SUCCESS ); +} + + +// module registration function ----------------------------------------------- +// +REGISTER_MODULE( T_PACK ) +{ + OPT_RegisterApplication( "-makepack", MAKEPACK_main ); + OPT_RegisterApplication( "-getpack", GETPACK_main ); +} + + + |
