diff --git a/utils/xar/wscript b/utils/xar/wscript new file mode 100644 index 00000000..eba2402f --- /dev/null +++ b/utils/xar/wscript @@ -0,0 +1,20 @@ +#! /usr/bin/env python +# encoding: utf-8 +# a1batross, mittorn, 2018 + +def options(opt): + pass + +def configure(conf): + pass + +def build(bld): + bld(source = bld.path.ant_glob('*.c'), + target = 'xar', + features = 'c cprogram', + includes = '.', + use = 'public filesystem_includes', + rpath = bld.env.DEFAULT_RPATH, + install_path = bld.env.BINDIR, + subsystem = bld.env.CONSOLE_SUBSYSTEM + ) diff --git a/utils/xar/xar.c b/utils/xar/xar.c new file mode 100644 index 00000000..a8665973 --- /dev/null +++ b/utils/xar/xar.c @@ -0,0 +1,203 @@ +/* +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; +} diff --git a/wscript b/wscript index ae8f59c0..be1ec094 100644 --- a/wscript +++ b/wscript @@ -85,6 +85,7 @@ SUBDIRS = [ # enabled optionally Subproject('utils/mdldec', lambda x: x.env.ENABLE_UTILS), +# Subproject('utils/xar', lambda x: x.env.ENABLE_UTILS), Subproject('utils/run-fuzzer', lambda x: x.env.ENABLE_FUZZER), # enabled on PSVita only