2021-10-02 14:57:12 +00:00
# include "FsProcess.h"
2021-10-08 10:05:51 +00:00
# include "util.h"
# include <memory>
# include <tc/io/FileNotFoundException.h>
# include <tc/io/DirectoryNotFoundException.h>
2021-10-02 14:57:12 +00:00
nstool : : FsProcess : : FsProcess ( ) :
mModuleLabel ( " nstool::FsProcess " ) ,
mInputFs ( ) ,
2021-10-08 10:05:51 +00:00
mFsFormatName ( ) ,
mShowFsInfo ( false ) ,
mProperties ( ) ,
mShowFsTree ( false ) ,
mFsRootLabel ( ) ,
mExtractJobs ( ) ,
mDataCache ( 0x10000 )
2021-10-02 14:57:12 +00:00
{
}
void nstool : : FsProcess : : setInputFileSystem ( const std : : shared_ptr < tc : : io : : IStorage > & input_fs )
{
mInputFs = input_fs ;
}
2021-10-08 10:05:51 +00:00
void nstool : : FsProcess : : setFsFormatName ( const std : : string & fs_format_name )
{
mFsFormatName = fs_format_name ;
}
void nstool : : FsProcess : : setShowFsInfo ( bool show_fs_info )
2021-10-02 14:57:12 +00:00
{
2021-10-08 10:05:51 +00:00
mShowFsInfo = show_fs_info ;
2021-10-02 14:57:12 +00:00
}
2021-10-08 10:05:51 +00:00
void nstool : : FsProcess : : setFsProperties ( const std : : vector < std : : string > & properties )
2021-10-02 14:57:12 +00:00
{
2021-10-08 10:05:51 +00:00
mProperties = properties ;
2021-10-02 14:57:12 +00:00
}
2021-10-08 10:05:51 +00:00
void nstool : : FsProcess : : setShowFsTree ( bool show_fs_tree )
2021-10-02 14:57:12 +00:00
{
2021-10-08 10:05:51 +00:00
mShowFsTree = show_fs_tree ;
}
void nstool : : FsProcess : : setFsRootLabel ( const std : : string & root_label )
{
mFsRootLabel = root_label ;
}
void nstool : : FsProcess : : setExtractJobs ( const std : : vector < nstool : : ExtractJob > & extract_jobs )
{
mExtractJobs = extract_jobs ;
2021-10-02 14:57:12 +00:00
}
void nstool : : FsProcess : : process ( )
{
if ( mInputFs = = nullptr )
{
throw tc : : InvalidOperationException ( mModuleLabel , " No input filesystem " ) ;
}
2021-10-08 10:05:51 +00:00
if ( mShowFsInfo )
{
fmt : : print ( " [{:s}] \n " , mFsFormatName . isSet ( ) ? mFsFormatName . get ( ) : " FileSystem/Info " ) ;
for ( auto itr = mProperties . begin ( ) ; itr ! = mProperties . end ( ) ; itr + + )
{
fmt : : print ( " {:s} \n " , * itr ) ;
}
}
if ( mShowFsTree )
{
2021-10-02 14:57:12 +00:00
printFs ( ) ;
2021-10-08 10:05:51 +00:00
}
2021-10-02 14:57:12 +00:00
2021-10-08 10:05:51 +00:00
if ( mExtractJobs . empty ( ) = = false )
{
2021-10-02 14:57:12 +00:00
extractFs ( ) ;
2021-10-08 10:05:51 +00:00
}
2021-10-02 14:57:12 +00:00
}
void nstool : : FsProcess : : printFs ( )
{
2021-10-08 10:05:51 +00:00
fmt : : print ( " [{:s}/Tree] \n " , ( mFsFormatName . isSet ( ) ? mFsFormatName . get ( ) : " FileSystem " ) ) ;
2021-10-02 14:57:12 +00:00
visitDir ( tc : : io : : Path ( " / " ) , tc : : io : : Path ( " / " ) , false , true ) ;
}
void nstool : : FsProcess : : extractFs ( )
{
2021-10-08 10:05:51 +00:00
fmt : : print ( " [{:s}/Extract] \n " , ( mFsFormatName . isSet ( ) ? mFsFormatName . get ( ) : " FileSystem " ) ) ;
for ( auto itr = mExtractJobs . begin ( ) ; itr ! = mExtractJobs . end ( ) ; itr + + )
{
std : : string path_str ;
tc : : io : : PathUtil : : pathToUnixUTF8 ( itr - > virtual_path , path_str ) ;
// check if root path (legacy case)
if ( itr - > virtual_path = = tc : : io : : Path ( " / " ) )
{
visitDir ( tc : : io : : Path ( " / " ) , itr - > extract_path , true , false ) ;
//fmt::print("Root Dir Virtual Path: \"{:s}\"\n", path_str);
// root directory extract successful, continue to next job
continue ;
}
// otherwise determine if this is a file or subdirectory
try {
std : : shared_ptr < tc : : io : : IStream > file_stream ;
mInputFs - > openFile ( itr - > virtual_path , tc : : io : : FileMode : : Open , tc : : io : : FileAccess : : Read , file_stream ) ;
//fmt::print("Valid File Path: \"{:s}\"\n", path_str);
// the output path for this file will depend on the user specified extract path
std : : shared_ptr < tc : : io : : IStorage > local_fs = std : : make_shared < tc : : io : : LocalStorage > ( tc : : io : : LocalStorage ( ) ) ;
// case: the extract_path is a valid path to an existing directory
// behaviour: extract the file, preserving the original filename, to the specified directory
// method: try getDirectoryListing(itr->extract_path), if this is does not throw, then we can be sure this is a valid path to a directory, file_extract_path = itr->extract_path + itr->virtual_path.back()
try {
tc : : io : : sDirectoryListing dir_listing ;
local_fs - > getDirectoryListing ( itr - > extract_path , dir_listing ) ;
tc : : io : : Path file_extract_path = itr - > extract_path + itr - > virtual_path . back ( ) ;
std : : string file_extract_path_str ;
tc : : io : : PathUtil : : pathToUnixUTF8 ( file_extract_path , file_extract_path_str ) ;
fmt : : print ( " Saving {:s}... \n " , file_extract_path_str ) ;
writeStreamToFile ( file_stream , itr - > extract_path + itr - > virtual_path . back ( ) , mDataCache ) ;
continue ;
2021-10-08 10:52:57 +00:00
} catch ( tc : : io : : DirectoryNotFoundException & ) {
2021-10-08 10:05:51 +00:00
// acceptable exception, just means directory didn't exist
}
// case: the extract_path up until the last element is a valid path to an existing directory, but the full path specifies neither a directory or a file
// behaviour: treat extract_path as the intended location to write the extracted file (the original filename is not preserved, instead specified by the user in the final element of the extract path)
// method: since this checks n-1 elements, it implies a path with more than one element, so that must be accounted for, as relative paths are valid and single element paths aren't always root
try {
std : : string test_path_str ;
// get path to parent directory
tc : : io : : Path parent_dir_path = itr - > extract_path ;
// replace final path element with the current directory alias
parent_dir_path . pop_back ( ) ; // remove filename
parent_dir_path . push_back ( " . " ) ; // replace with the current dir name alias
tc : : io : : PathUtil : : pathToUnixUTF8 ( parent_dir_path , test_path_str ) ;
// test parent directory exists
tc : : io : : sDirectoryListing dir_listing ;
local_fs - > getDirectoryListing ( parent_dir_path , dir_listing ) ;
std : : string file_extract_path_str ;
tc : : io : : PathUtil : : pathToUnixUTF8 ( itr - > extract_path , file_extract_path_str ) ;
fmt : : print ( " Saving {:s} as {:s}... \n " , path_str , file_extract_path_str ) ;
writeStreamToFile ( file_stream , itr - > extract_path , mDataCache ) ;
continue ;
2021-10-08 10:52:57 +00:00
} catch ( tc : : io : : DirectoryNotFoundException & ) {
2021-10-08 10:05:51 +00:00
// acceptable exception, just means the parent directory didn't exist
}
// extract path could not be determined, inform the user and skip this job
std : : string literal_extract_path_str ;
tc : : io : : PathUtil : : pathToUnixUTF8 ( itr - > extract_path , literal_extract_path_str ) ;
fmt : : print ( " [WARNING] Extract path was invalid, and was skipped: {:s} \n " , literal_extract_path_str ) ;
continue ;
2021-10-08 10:52:57 +00:00
} catch ( tc : : io : : FileNotFoundException & ) {
2021-10-08 10:05:51 +00:00
// acceptable exception, just means file didn't exist
}
// not a file, attempt to process this as a directory
try {
tc : : io : : sDirectoryListing dir_listing ;
mInputFs - > getDirectoryListing ( itr - > virtual_path , dir_listing ) ;
visitDir ( itr - > virtual_path , itr - > extract_path , true , false ) ;
//fmt::print("Valid Directory Path: \"{:s}\"\n", path_str);
// directory extract successful, continue to next job
continue ;
2021-10-08 10:52:57 +00:00
} catch ( tc : : io : : DirectoryNotFoundException & ) {
2021-10-08 10:05:51 +00:00
// acceptable exception, just means directory didn't exist
}
fmt : : print ( " [WARNING] Failed to extract virtual path: \" {:s} \" \n " , path_str ) ;
}
2021-10-02 14:57:12 +00:00
}
void nstool : : FsProcess : : visitDir ( const tc : : io : : Path & v_path , const tc : : io : : Path & l_path , bool extract_fs , bool print_fs )
{
tc : : io : : LocalStorage local_fs ;
// get listing for directory
tc : : io : : sDirectoryListing info ;
mInputFs - > getDirectoryListing ( v_path , info ) ;
if ( print_fs )
{
for ( size_t i = 0 ; i < v_path . size ( ) ; i + + )
2021-10-08 10:05:51 +00:00
fmt : : print ( " " ) ;
2021-10-02 14:57:12 +00:00
2021-10-08 10:05:51 +00:00
fmt : : print ( " {:s}/ \n " , ( ( v_path . size ( ) = = 1 ) ? ( mFsRootLabel . isSet ( ) ? ( mFsRootLabel . get ( ) + " : " ) : " Root: " ) : v_path . back ( ) ) ) ;
2021-10-02 14:57:12 +00:00
}
if ( extract_fs )
{
// create local dir
local_fs . createDirectory ( l_path ) ;
}
// iterate thru child files
size_t cache_read_len ;
tc : : io : : Path out_path ;
std : : string out_path_str ;
std : : shared_ptr < tc : : io : : IStream > in_stream ;
std : : shared_ptr < tc : : io : : IStream > out_stream ;
for ( auto itr = info . file_list . begin ( ) ; itr ! = info . file_list . end ( ) ; itr + + )
{
if ( print_fs )
{
for ( size_t i = 0 ; i < v_path . size ( ) ; i + + )
fmt : : print ( " " ) ;
fmt : : print ( " {:s} \n " , * itr ) ;
}
if ( extract_fs )
{
// build out path
out_path = l_path + * itr ;
tc : : io : : PathUtil : : pathToUnixUTF8 ( out_path , out_path_str ) ;
fmt : : print ( " Saving {:s}... \n " , out_path_str ) ;
// begin export
mInputFs - > openFile ( v_path + * itr , tc : : io : : FileMode : : Open , tc : : io : : FileAccess : : Read , in_stream ) ;
local_fs . openFile ( out_path , tc : : io : : FileMode : : OpenOrCreate , tc : : io : : FileAccess : : Write , out_stream ) ;
in_stream - > seek ( 0 , tc : : io : : SeekOrigin : : Begin ) ;
out_stream - > seek ( 0 , tc : : io : : SeekOrigin : : Begin ) ;
for ( int64_t remaining_data = in_stream - > length ( ) ; remaining_data > 0 ; )
{
2021-10-08 10:05:51 +00:00
cache_read_len = in_stream - > read ( mDataCache . data ( ) , mDataCache . size ( ) ) ;
2021-10-02 14:57:12 +00:00
if ( cache_read_len = = 0 )
{
2021-10-08 10:05:51 +00:00
throw tc : : io : : IOException ( mModuleLabel , fmt : : format ( " Failed to read from {:s}file. " , ( mFsFormatName . isSet ( ) ? ( mFsFormatName . get ( ) + " " ) : " " ) ) ) ;
2021-10-02 14:57:12 +00:00
}
2021-10-08 10:05:51 +00:00
out_stream - > write ( mDataCache . data ( ) , cache_read_len ) ;
2021-10-02 14:57:12 +00:00
remaining_data - = int64_t ( cache_read_len ) ;
}
}
}
// iterate thru child dirs
for ( auto itr = info . dir_list . begin ( ) ; itr ! = info . dir_list . end ( ) ; itr + + )
{
visitDir ( v_path + * itr , l_path + * itr , extract_fs , print_fs ) ;
}
}