/* xar.c -- Xash ARchives (XAR) Copyright (C) 2023 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 "port.h" #include "build.h" #include #include #include #include #include "filesystem.h" #if XASH_POSIX #include #include #define LoadLibrary( x ) dlopen( x, RTLD_NOW ) #define GetProcAddress( x, y ) dlsym( x, y ) #define FreeLibrary( x ) dlclose( x ) #elif XASH_WIN32 #include #endif static void *g_hModule; static FSAPI g_pfnGetFSAPI; static fs_api_t g_fs; static fs_globals_t *g_nullglobals; static qboolean LoadFilesystem( void ) { #if 0 string cwd; if( getcwd( cwd, sizeof( cwd )) == NULL ) { printf( "getcwd() failed: %s\n", strerror( errno )); return false; } #endif g_hModule = LoadLibrary( "filesystem_stdio." OS_LIB_EXT ); if( !g_hModule ) return false; g_pfnGetFSAPI = (void*)GetProcAddress( g_hModule, GET_FS_API ); if( !g_pfnGetFSAPI ) return false; if( !g_pfnGetFSAPI( FS_API_VERSION, &g_fs, &g_nullglobals, NULL )) return false; // g_fs.InitStdio( true, cwd, ); return true; } static void FS_CreatePath( char *path ) { char *ofs, save; for( ofs = path + 1; *ofs; ofs++ ) { if( *ofs == '/' || *ofs == '\\' ) { // create the directory save = *ofs; *ofs = 0; _mkdir( path ); *ofs = save; } } } void usage( const char *arg0 ) { printf( "%s: [option...] \n", arg0 ); puts( "XAR is a simple frontend to Xash3D FWGS's filesystem_stdio library" ); puts( "that allows interacting with archive types supported by it" ); puts( "Options:" ); puts( "\tx\t\teXtract the archive" ); puts( "\tt\t\tlisT the archive" ); // TODO: make an interface for modifying // puts( "\tc\t\tCreate the archive" ); // puts( "\tu\t\tUpdate the archive" ); // puts( "\tr\t\tRemove from the archive" ); puts( "Extract and list options:" ); puts( "\t-wads\tauto-mount WADs inside archives" ); exit( 1 ); } int main( int argc, char **argv ) { const char *filename; search_t *search; char action; int i, flags = FS_NOWRITE_PATH | FS_SKIP_ARCHIVED_WADS; if( argc < 3 ) usage( argv[0] ); if( !LoadFilesystem()) { puts( "Can't load filesystem_stdio!" ); return 2; } action = argv[1][0]; if( action != 'x' && action != 't' ) { printf( "Unknown action: %c\n", action ); usage( argv[0] ); } for( i = 2; i < argc - 1; i++ ) { if( !strcmp( argv[i], "-wads" )) ClearBits( flags, FS_SKIP_ARCHIVED_WADS ); else { printf( "Unknown option: %s\n", argv[i] ); usage( argv[0] ); } } filename = argv[argc-1]; if( g_fs.MountArchive_Fullpath( filename, flags ) == NULL ) { printf( "Can't mount %s\n", filename ); return 3; } // suboptimal, but that's what is available with current FS API search = g_fs.Search( "*", false, false ); if( !search ) { printf( "Can't find any files in %s\n", filename ); return 4; } if( action == 't' ) { puts( "File list:" ); for( i = 0; i < search->numfilenames; i++ ) { printf( "\t%s\n", search->filenames[i] ); } } else if( action == 'x' ) { for( i = 0; i < search->numfilenames; i++ ) { struct stat st; char *path = search->filenames[i]; file_t *from; char buffer[4096]; FILE *to; if(( from = g_fs.Open( path, "rb", false )) == NULL ) { printf( "Can't open %s in archive, skipping...\n", path ); continue; } FS_CreatePath( path ); if( stat( path, &st ) == 0 ) { printf( "Will not overwrite existing %s file\n", path ); g_fs.Close( from ); continue; } if(( to = fopen( search->filenames[i], "wb" )) == NULL ) { printf( "fopen() failed: %s\n", strerror( errno )); g_fs.Close( from ); continue; } printf( "Unpacking %s...\n", path ); while( !g_fs.Eof( from )) { size_t len = g_fs.Read( from, buffer, sizeof( buffer )); fwrite( buffer, 1, len, to ); } fclose( to ); g_fs.Close( from ); } } free( search ); return 0; }