@@ -46,7 +46,10 @@ use crate::{
46
46
diff:: { calc_diff_ranges, print_diff, process_code} ,
47
47
dol:: process_dol,
48
48
elf:: { process_elf, write_elf} ,
49
- file:: { buf_writer, touch, verify_hash, FileIterator , FileReadInfo } ,
49
+ file:: {
50
+ buf_copy_with_hash, buf_writer, check_hash_str, touch, verify_hash, FileIterator ,
51
+ FileReadInfo ,
52
+ } ,
50
53
lcf:: { asm_path_for_unit, generate_ldscript, obj_path_for_unit} ,
51
54
map:: apply_map_file,
52
55
path:: { check_path_buf, native_path} ,
@@ -229,6 +232,10 @@ pub struct ProjectConfig {
229
232
/// Optional base path for all object files.
230
233
#[ serde( with = "unix_path_serde_option" , default , skip_serializing_if = "is_default" ) ]
231
234
pub object_base : Option < Utf8UnixPathBuf > ,
235
+ /// Whether to extract objects from a disc image into object base. If false, the files
236
+ /// will be used from the disc image directly without extraction.
237
+ #[ serde( default = "bool_true" , skip_serializing_if = "is_true" ) ]
238
+ pub extract_objects : bool ,
232
239
}
233
240
234
241
impl Default for ProjectConfig {
@@ -248,6 +255,7 @@ impl Default for ProjectConfig {
248
255
fill_gaps : true ,
249
256
export_all : true ,
250
257
object_base : None ,
258
+ extract_objects : true ,
251
259
}
252
260
}
253
261
}
@@ -1103,7 +1111,13 @@ fn split(args: SplitArgs) -> Result<()> {
1103
1111
let mut config_file = open_file ( & args. config , true ) ?;
1104
1112
serde_yaml:: from_reader ( config_file. as_mut ( ) ) ?
1105
1113
} ;
1106
- let object_base = find_object_base ( & config) ?;
1114
+
1115
+ let mut object_base = find_object_base ( & config) ?;
1116
+ if config. extract_objects && matches ! ( object_base, ObjectBase :: Vfs ( ..) ) {
1117
+ // Extract files from the VFS into the object base directory
1118
+ let target_dir = extract_objects ( & config, & object_base) ?;
1119
+ object_base = ObjectBase :: Extracted ( target_dir) ;
1120
+ }
1107
1121
1108
1122
for module_config in config. modules . iter_mut ( ) {
1109
1123
let mut file = object_base. open ( & module_config. object ) ?;
@@ -2001,6 +2015,7 @@ fn apply_add_relocations(obj: &mut ObjInfo, relocations: &[AddRelocationConfig])
2001
2015
pub enum ObjectBase {
2002
2016
None ,
2003
2017
Directory ( Utf8NativePathBuf ) ,
2018
+ Extracted ( Utf8NativePathBuf ) ,
2004
2019
Vfs ( Utf8NativePathBuf , Box < dyn Vfs + Send + Sync > ) ,
2005
2020
}
2006
2021
@@ -2009,6 +2024,7 @@ impl ObjectBase {
2009
2024
match self {
2010
2025
ObjectBase :: None => path. with_encoding ( ) ,
2011
2026
ObjectBase :: Directory ( base) => base. join ( path. with_encoding ( ) ) ,
2027
+ ObjectBase :: Extracted ( base) => extracted_path ( base, path) ,
2012
2028
ObjectBase :: Vfs ( base, _) => Utf8NativePathBuf :: from ( format ! ( "{}:{}" , base, path) ) ,
2013
2029
}
2014
2030
}
@@ -2017,12 +2033,22 @@ impl ObjectBase {
2017
2033
match self {
2018
2034
ObjectBase :: None => open_file ( & path. with_encoding ( ) , true ) ,
2019
2035
ObjectBase :: Directory ( base) => open_file ( & base. join ( path. with_encoding ( ) ) , true ) ,
2036
+ ObjectBase :: Extracted ( base) => open_file ( & extracted_path ( base, path) , true ) ,
2020
2037
ObjectBase :: Vfs ( vfs_path, vfs) => {
2021
2038
open_file_with_fs ( vfs. clone ( ) , & path. with_encoding ( ) , true )
2022
2039
. with_context ( || format ! ( "Using disc image {}" , vfs_path) )
2023
2040
}
2024
2041
}
2025
2042
}
2043
+
2044
+ pub fn base_path ( & self ) -> & Utf8NativePath {
2045
+ match self {
2046
+ ObjectBase :: None => Utf8NativePath :: new ( "" ) ,
2047
+ ObjectBase :: Directory ( base) => base,
2048
+ ObjectBase :: Extracted ( base) => base,
2049
+ ObjectBase :: Vfs ( base, _) => base,
2050
+ }
2051
+ }
2026
2052
}
2027
2053
2028
2054
pub fn find_object_base ( config : & ProjectConfig ) -> Result < ObjectBase > {
@@ -2037,7 +2063,6 @@ pub fn find_object_base(config: &ProjectConfig) -> Result<ObjectBase> {
2037
2063
let format = nodtool:: nod:: Disc :: detect ( file. as_mut ( ) ) ?;
2038
2064
if let Some ( format) = format {
2039
2065
file. rewind ( ) ?;
2040
- log:: info!( "Using disc image {}" , path) ;
2041
2066
let fs = open_fs ( file, ArchiveKind :: Disc ( format) ) ?;
2042
2067
return Ok ( ObjectBase :: Vfs ( path, fs) ) ;
2043
2068
}
@@ -2047,3 +2072,83 @@ pub fn find_object_base(config: &ProjectConfig) -> Result<ObjectBase> {
2047
2072
}
2048
2073
Ok ( ObjectBase :: None )
2049
2074
}
2075
+
2076
+ /// Extracts object files from the disc image into the object base directory.
2077
+ fn extract_objects ( config : & ProjectConfig , object_base : & ObjectBase ) -> Result < Utf8NativePathBuf > {
2078
+ let target_dir: Utf8NativePathBuf = match config. object_base . as_ref ( ) {
2079
+ Some ( path) => path. with_encoding ( ) ,
2080
+ None => bail ! ( "No object base specified" ) ,
2081
+ } ;
2082
+ let mut object_paths = Vec :: < ( & Utf8UnixPath , Option < & str > , Utf8NativePathBuf ) > :: new ( ) ;
2083
+ {
2084
+ let target_path = extracted_path ( & target_dir, & config. base . object ) ;
2085
+ if !fs:: exists ( & target_path)
2086
+ . with_context ( || format ! ( "Failed to check path '{}'" , target_path) ) ?
2087
+ {
2088
+ object_paths. push ( ( & config. base . object , config. base . hash . as_deref ( ) , target_path) ) ;
2089
+ }
2090
+ }
2091
+ if let Some ( selfile) = & config. selfile {
2092
+ let target_path = extracted_path ( & target_dir, selfile) ;
2093
+ if !fs:: exists ( & target_path)
2094
+ . with_context ( || format ! ( "Failed to check path '{}'" , target_path) ) ?
2095
+ {
2096
+ object_paths. push ( ( selfile, config. selfile_hash . as_deref ( ) , target_path) ) ;
2097
+ }
2098
+ }
2099
+ for module_config in & config. modules {
2100
+ let target_path = extracted_path ( & target_dir, & module_config. object ) ;
2101
+ if !fs:: exists ( & target_path)
2102
+ . with_context ( || format ! ( "Failed to check path '{}'" , target_path) ) ?
2103
+ {
2104
+ object_paths. push ( ( & module_config. object , module_config. hash . as_deref ( ) , target_path) ) ;
2105
+ }
2106
+ }
2107
+ if object_paths. is_empty ( ) {
2108
+ return Ok ( target_dir) ;
2109
+ }
2110
+ log:: info!(
2111
+ "Extracting {} file{} from {}" ,
2112
+ object_paths. len( ) ,
2113
+ if object_paths. len( ) == 1 { "" } else { "s" } ,
2114
+ object_base. base_path( )
2115
+ ) ;
2116
+ let start = Instant :: now ( ) ;
2117
+ for ( source_path, hash, target_path) in object_paths {
2118
+ let mut file = object_base. open ( source_path) ?;
2119
+ if let Some ( parent) = target_path. parent ( ) {
2120
+ fs:: create_dir_all ( parent)
2121
+ . with_context ( || format ! ( "Failed to create directory '{}'" , parent) ) ?;
2122
+ }
2123
+ let mut out = fs:: File :: create ( & target_path)
2124
+ . with_context ( || format ! ( "Failed to create file '{}'" , target_path) ) ?;
2125
+ let hash_bytes = buf_copy_with_hash ( & mut file, & mut out)
2126
+ . with_context ( || format ! ( "Failed to extract file '{}'" , target_path) ) ?;
2127
+ if let Some ( hash) = hash {
2128
+ check_hash_str ( hash_bytes, hash) . with_context ( || {
2129
+ format ! ( "Source file failed verification: '{}'" , object_base. join( source_path) )
2130
+ } ) ?;
2131
+ }
2132
+ }
2133
+ let duration = start. elapsed ( ) ;
2134
+ log:: info!( "Extraction completed in {}.{:03}s" , duration. as_secs( ) , duration. subsec_millis( ) ) ;
2135
+ Ok ( target_dir)
2136
+ }
2137
+
2138
+ /// Converts VFS paths like `path/to/container.arc:file` to `path/to/container/file`.
2139
+ fn extracted_path ( target_dir : & Utf8NativePath , path : & Utf8UnixPath ) -> Utf8NativePathBuf {
2140
+ let mut target_path = target_dir. to_owned ( ) ;
2141
+ let mut split = path. as_str ( ) . split ( ':' ) . peekable ( ) ;
2142
+ while let Some ( path) = split. next ( ) {
2143
+ let path = Utf8UnixPath :: new ( path) ;
2144
+ if split. peek ( ) . is_some ( ) {
2145
+ if let Some ( parent) = path. parent ( ) {
2146
+ target_path. push ( parent. with_encoding ( ) ) ;
2147
+ }
2148
+ target_path. push ( path. file_stem ( ) . unwrap ( ) ) ;
2149
+ } else {
2150
+ target_path. push ( path. with_encoding ( ) ) ;
2151
+ }
2152
+ }
2153
+ target_path
2154
+ }
0 commit comments