505505#define XISO_MEDIA_ENABLE_LENGTH 8
506506#define XISO_MEDIA_ENABLE_BYTE_POS 7
507507
508+ #define n_dword (offset ) ( (offset) / XISO_DWORD_SIZE + ( (offset) % XISO_DWORD_SIZE ? 1 : 0 ) )
509+
508510#define EMPTY_SUBDIRECTORY ( (dir_node_avl *) 1 )
509511
510512#define READWRITE_BUFFER_SIZE 0x00200000
@@ -522,6 +524,7 @@ typedef enum bm_constants { k_default_alphabet_size = 256 } bm_constants;
522524
523525typedef enum modes { k_generate_avl , k_extract , k_list , k_rewrite } modes ;
524526typedef enum errors { err_end_of_sector = -5001 , err_iso_rewritten = -5002 , err_iso_no_files = -5003 } errors ;
527+ typedef enum strategies { tree_strategy , discover_strategy } strategies ;
525528
526529typedef void (* progress_callback )( xoff_t in_current_value , xoff_t in_final_value );
527530typedef int (* traversal_callback )( void * in_node , void * in_context , long in_depth );
@@ -597,8 +600,8 @@ int free_dir_node_avl( void *in_dir_node_avl, void *, long );
597600int extract_file ( int in_xiso , dir_node * in_file , modes in_mode , char * path );
598601int decode_xiso ( char * in_xiso , char * in_path , modes in_mode , char * * out_iso_path );
599602int verify_xiso ( int in_xiso , int32_t * out_root_dir_sector , int32_t * out_root_dir_size , char * in_iso_name );
600- int traverse_xiso (int in_xiso , xoff_t in_dir_start , uint16_t entry_offset , char * in_path , modes in_mode , dir_node_avl * * in_root );
601- int process_node (int in_xiso , dir_node * node , char * in_path , modes in_mode , dir_node_avl * * in_root );
603+ int traverse_xiso (int in_xiso , xoff_t in_dir_start , uint16_t entry_offset , uint16_t end_offset , char * in_path , modes in_mode , dir_node_avl * * in_root , strategies strategy );
604+ int process_node (int in_xiso , dir_node * node , char * in_path , modes in_mode , dir_node_avl * * in_root , strategies strategy );
602605int create_xiso ( char * in_root_directory , char * in_output_directory , dir_node_avl * in_root , int in_xiso , char * * out_iso_path , char * in_name , progress_callback in_progress_callback );
603606
604607FILE_TIME * alloc_filetime_now ( void );
@@ -1112,7 +1115,9 @@ int create_xiso( char *in_root_directory, char *in_output_directory, dir_node_av
11121115int decode_xiso ( char * in_xiso , char * in_path , modes in_mode , char * * out_iso_path ) {
11131116 dir_node_avl * root = nil ;
11141117 bool repair = false;
1118+ xoff_t root_dir_start ;
11151119 int32_t root_dir_sect , root_dir_size ;
1120+ uint16_t root_end_offset ;
11161121 int xiso , err = 0 , len , path_len = 0 , add_slash = 0 ;
11171122 char * buf , * cwd = nil , * name = nil , * short_name = nil , * iso_name , * folder = nil ;
11181123
@@ -1171,14 +1176,18 @@ int decode_xiso( char *in_xiso, char *in_path, modes in_mode, char **out_iso_pat
11711176 if (!err ) {
11721177 if (asprintf (& buf , "%s%s%s%c" , in_path ? in_path : "" , add_slash && (!in_path ) ? PATH_CHAR_STR : "" , in_mode != k_list && (!in_path ) ? iso_name : "" , PATH_CHAR ) == -1 ) mem_err ()
11731178
1174- if (!err && lseek (xiso , (xoff_t )root_dir_sect * XISO_SECTOR_SIZE + s_xbox_disc_lseek , SEEK_SET ) == -1 ) seek_err ();
1179+ root_dir_start = (xoff_t )root_dir_sect * XISO_SECTOR_SIZE + s_xbox_disc_lseek ;
1180+ root_end_offset = n_sectors (root_dir_size ) * XISO_SECTOR_SIZE / XISO_DWORD_SIZE ;
11751181
1182+ if (!err && lseek (xiso , root_dir_start , SEEK_SET ) == -1 ) seek_err ();
1183+
11761184 if ( in_mode == k_rewrite ) {
1177- if (!err ) err = traverse_xiso (xiso , (xoff_t )root_dir_sect * XISO_SECTOR_SIZE + s_xbox_disc_lseek , 0 , buf , k_generate_avl , & root );
1185+ if (!err ) err = traverse_xiso (xiso , root_dir_start , 0 , root_end_offset , buf , k_generate_avl , & root , tree_strategy );
1186+ if (!err ) err = traverse_xiso (xiso , root_dir_start , 0 , root_end_offset , buf , k_generate_avl , & root , discover_strategy );
11781187 if (!err ) err = create_xiso ( iso_name , in_path , root , xiso , out_iso_path , nil , nil );
11791188 }
11801189 else {
1181- if (!err ) err = traverse_xiso (xiso , ( xoff_t ) root_dir_sect * XISO_SECTOR_SIZE + s_xbox_disc_lseek , 0 , buf , in_mode , nil );
1190+ if (!err ) err = traverse_xiso (xiso , root_dir_start , 0 , root_end_offset , buf , in_mode , nil , discover_strategy );
11821191 }
11831192
11841193 if (buf ) free (buf );
@@ -1202,97 +1211,122 @@ int decode_xiso( char *in_xiso, char *in_path, modes in_mode, char **out_iso_pat
12021211}
12031212
12041213
1205- int traverse_xiso (int in_xiso , xoff_t in_dir_start , uint16_t entry_offset , char * in_path , modes in_mode , dir_node_avl * * in_root ) {
1206- dir_node_avl * avl = nil ;
1207- dir_node * node = nil ;
1208- uint16_t l_offset , r_offset ;
1214+ int traverse_xiso (int in_xiso , xoff_t in_dir_start , uint16_t entry_offset , uint16_t end_offset , char * in_path , modes in_mode , dir_node_avl * * in_root , strategies strategy ) {
1215+ dir_node_avl * avl = nil ;
1216+ dir_node * node = nil ;
1217+ uint16_t l_offset = 0 , r_offset = 0 ;
12091218 int err = 0 ;
12101219
1211- if (lseek ( in_xiso , in_dir_start + ( xoff_t ) entry_offset * XISO_DWORD_SIZE , SEEK_SET ) == -1 ) seek_err ( );
1220+ if (entry_offset >= end_offset ) misc_err ( "attempt to read node entry beyond directory end" );
12121221
1213- if (!err && read (in_xiso , & l_offset , XISO_TABLE_OFFSET_SIZE ) != XISO_TABLE_OFFSET_SIZE ) read_err ();
1214- if (!err && l_offset == XISO_PAD_SHORT ) {
1215- if (entry_offset == 0 ) { // Empty directory
1216- if (in_mode == k_generate_avl ) err = (avl_insert (in_root , EMPTY_SUBDIRECTORY ) == k_avl_error );
1217- }
1218- else {
1219- exiso_warn ("Invalid node found and skipped!" );
1222+ do {
1223+ if (!err && lseek (in_xiso , in_dir_start + (xoff_t )entry_offset * XISO_DWORD_SIZE , SEEK_SET ) == -1 ) seek_err ();
1224+
1225+ if (!err && read (in_xiso , & l_offset , XISO_TABLE_OFFSET_SIZE ) != XISO_TABLE_OFFSET_SIZE ) read_err ();
1226+ if (!err && l_offset == XISO_PAD_SHORT ) {
1227+ if (entry_offset == 0 ) { // Empty directories have padding starting at the beginning
1228+ if (in_mode == k_generate_avl ) err = (avl_insert (in_root , EMPTY_SUBDIRECTORY ) == k_avl_error );
1229+ return err ; // Done
1230+ }
1231+ else if (strategy != discover_strategy ) { // When discovering, the padding means end of sector
1232+ exiso_warn ("Invalid node found and skipped!" ); // When not discovering, the padding means a bad entry, skip it without failing
1233+ return err ; // We're done if not discovering
1234+ }
1235+ // We're discovering, so set the offset to the start of the next sector
1236+ if (!err ) entry_offset = n_sectors (entry_offset * XISO_DWORD_SIZE ) * XISO_SECTOR_SIZE / XISO_DWORD_SIZE ;
1237+ continue ;
12201238 }
1221- return err ;
1222- }
12231239
1224- // Read node
1225- if (!err ) if ((node = calloc (1 , sizeof (dir_node ))) == nil ) mem_err ();
1226- if (!err && read (in_xiso , & r_offset , XISO_TABLE_OFFSET_SIZE ) != XISO_TABLE_OFFSET_SIZE ) read_err ();
1227- if (!err && read (in_xiso , & node -> start_sector , XISO_SECTOR_OFFSET_SIZE ) != XISO_SECTOR_OFFSET_SIZE ) read_err ();
1228- if (!err && read (in_xiso , & node -> file_size , XISO_FILESIZE_SIZE ) != XISO_FILESIZE_SIZE ) read_err ();
1229- if (!err && read (in_xiso , & node -> attributes , XISO_ATTRIBUTES_SIZE ) != XISO_ATTRIBUTES_SIZE ) read_err ();
1230- if (!err && read (in_xiso , & node -> filename_length , XISO_FILENAME_LENGTH_SIZE ) != XISO_FILENAME_LENGTH_SIZE ) read_err ();
1240+ // Read node
1241+ if (!err ) if ((node = calloc (1 , sizeof (dir_node ))) == nil ) mem_err ();
1242+ if (!err && read (in_xiso , & r_offset , XISO_TABLE_OFFSET_SIZE ) != XISO_TABLE_OFFSET_SIZE ) read_err ();
1243+ if (!err && read (in_xiso , & node -> start_sector , XISO_SECTOR_OFFSET_SIZE ) != XISO_SECTOR_OFFSET_SIZE ) read_err ();
1244+ if (!err && read (in_xiso , & node -> file_size , XISO_FILESIZE_SIZE ) != XISO_FILESIZE_SIZE ) read_err ();
1245+ if (!err && read (in_xiso , & node -> attributes , XISO_ATTRIBUTES_SIZE ) != XISO_ATTRIBUTES_SIZE ) read_err ();
1246+ if (!err && read (in_xiso , & node -> filename_length , XISO_FILENAME_LENGTH_SIZE ) != XISO_FILENAME_LENGTH_SIZE ) read_err ();
12311247
1232- if (!err ) {
1233- little16 (l_offset );
1234- little16 (r_offset );
1235- little32 (node -> file_size );
1236- little32 (node -> start_sector );
1248+ if (!err && (entry_offset * XISO_DWORD_SIZE + XISO_FILENAME_OFFSET + node -> filename_length ) > (end_offset * XISO_DWORD_SIZE )) misc_err ("node entry spans beyond directory end" );
12371249
1238- if ((node -> filename = (char * )malloc (node -> filename_length + 1 )) == nil ) mem_err ();
1239- }
1250+ if (!err ) {
1251+ little16 (l_offset );
1252+ little16 (r_offset );
1253+ little32 (node -> file_size );
1254+ little32 (node -> start_sector );
1255+
1256+ if ((node -> filename = (char * )malloc (node -> filename_length + 1 )) == nil ) mem_err ();
1257+ }
12401258
1241- if (!err ) {
1242- if (read (in_xiso , node -> filename , node -> filename_length ) != node -> filename_length ) read_err ();
12431259 if (!err ) {
1244- node -> filename [node -> filename_length ] = 0 ;
1260+ if (read (in_xiso , node -> filename , node -> filename_length ) != node -> filename_length ) read_err ();
1261+ if (!err ) {
1262+ node -> filename [node -> filename_length ] = 0 ;
12451263
1246- // security patch (Chris Bainbridge), modified by in to support "...", etc. 02.14.06 (in)
1247- if (!strcmp (node -> filename , "." ) || !strcmp (node -> filename , ".." ) || strchr (node -> filename , '/' ) || strchr (node -> filename , '\\' )) {
1248- log_err (__FILE__ , __LINE__ , "filename '%s' contains invalid character(s), aborting." , node -> filename );
1249- exit (1 );
1264+ // security patch (Chris Bainbridge), modified by in to support "...", etc. 02.14.06 (in)
1265+ if (!strcmp (node -> filename , "." ) || !strcmp (node -> filename , ".." ) || strchr (node -> filename , '/' ) || strchr (node -> filename , '\\' )) {
1266+ log_err (__FILE__ , __LINE__ , "filename '%s' contains invalid character(s), aborting." , node -> filename );
1267+ exit (1 );
1268+ }
12501269 }
12511270 }
1252- }
12531271
1254- // Insert node in tree
1255- if (!err && in_mode == k_generate_avl ) {
1256- if ((avl = (dir_node_avl * )calloc (1 , sizeof (dir_node_avl ))) == nil ) mem_err ();
1257- if (!err ) if ((avl -> filename = strdup (node -> filename )) == nil ) mem_err ();
1272+ // Process the node according to the mode
12581273 if (!err ) {
1259- avl -> file_size = node -> file_size ;
1260- avl -> old_start_sector = node -> start_sector ;
1261- if (avl_insert (in_root , avl ) == k_avl_error ) misc_err ("this iso appears to be corrupt" );
1274+ if (in_mode == k_generate_avl ) {
1275+ if ((avl = (dir_node_avl * )calloc (1 , sizeof (dir_node_avl ))) == nil ) mem_err ();
1276+ if (!err ) if ((avl -> filename = strdup (node -> filename )) == nil ) mem_err ();
1277+ if (!err ) {
1278+ avl -> file_size = node -> file_size ;
1279+ avl -> old_start_sector = node -> start_sector ;
1280+ if (avl_insert (in_root , avl ) == k_avl_error ) { // Insert node in tree
1281+ // If we're discovering files outside trees, we don't care about avl_insert errors,
1282+ // since they represent nodes already discovered before, and we don't want to process them again
1283+ if (strategy != discover_strategy ) misc_err ("this iso appears to be corrupt" );
1284+ }
1285+ else err = process_node (in_xiso , node , in_path , in_mode , & avl -> subdirectory , strategy );
1286+ }
1287+ }
1288+ else {
1289+ err = process_node (in_xiso , node , in_path , in_mode , nil , strategy );
1290+ }
12621291 }
1263- }
12641292
1265- // Process the node, according to mode
1266- if (!err ) err = process_node ( in_xiso , node , in_path , in_mode , ( in_mode == k_generate_avl ) ? & avl -> subdirectory : nil );
1293+ // Save next offset for discovery
1294+ if (!err ) entry_offset = n_dword ( entry_offset * XISO_DWORD_SIZE + XISO_FILENAME_OFFSET + node -> filename_length );
12671295
1268- // Free memory before recurring
1269- if (node ) {
1270- if (node -> filename ) free (node -> filename );
1271- free (node );
1272- }
1296+ // Free memory before recurring or iterating
1297+ if (node ) {
1298+ if (node -> filename ) free (node -> filename );
1299+ free (node );
1300+ }
12731301
1274- // Repeat on left node
1275- if (!err && l_offset ) {
1276- if (!err ) if (lseek (in_xiso , in_dir_start + (xoff_t )l_offset * XISO_DWORD_SIZE , SEEK_SET ) == -1 ) seek_err ();
1277- if (!err ) err = traverse_xiso (in_xiso , in_dir_start , l_offset , in_path , in_mode , & avl );
1278- }
1302+ } while (!err && entry_offset < end_offset && strategy == discover_strategy ); // Iterate only if using discover_strategy
1303+
1304+ if (strategy != discover_strategy ) {
1305+ // Recurse on left node
1306+ if (!err && l_offset ) {
1307+ if (lseek (in_xiso , in_dir_start + (xoff_t )l_offset * XISO_DWORD_SIZE , SEEK_SET ) == -1 ) seek_err ();
1308+ if (!err ) err = traverse_xiso (in_xiso , in_dir_start , l_offset , end_offset , in_path , in_mode , & avl , strategy );
1309+ }
12791310
1280- // Repeat on right node
1281- if (!err && r_offset ) {
1282- if (!err ) if (lseek (in_xiso , in_dir_start + (xoff_t )r_offset * XISO_DWORD_SIZE , SEEK_SET ) == -1 ) seek_err ();
1283- if (!err ) err = traverse_xiso (in_xiso , in_dir_start , r_offset , in_path , in_mode , & avl );
1311+ // Recurse on right node
1312+ if (!err && r_offset ) {
1313+ if (lseek (in_xiso , in_dir_start + (xoff_t )r_offset * XISO_DWORD_SIZE , SEEK_SET ) == -1 ) seek_err ();
1314+ if (!err ) err = traverse_xiso (in_xiso , in_dir_start , r_offset , end_offset , in_path , in_mode , & avl , strategy );
1315+ }
12841316 }
12851317
12861318 return err ;
12871319}
12881320
1289- int process_node (int in_xiso , dir_node * node , char * in_path , modes in_mode , dir_node_avl * * in_root ) {
1290- char * path = nil ;
1291- int err = 0 ;
1321+ int process_node (int in_xiso , dir_node * node , char * in_path , modes in_mode , dir_node_avl * * in_root , strategies strategy ) {
1322+ char * path = nil ;
1323+ int err = 0 ;
1324+ xoff_t dir_start = (xoff_t )node -> start_sector * XISO_SECTOR_SIZE + s_xbox_disc_lseek ;
1325+ uint16_t end_offset ;
12921326
12931327 if (node -> attributes & XISO_ATTRIBUTE_DIR ) { // Process directory
12941328
1295- if (!err ) if (lseek (in_xiso , ( xoff_t ) node -> start_sector * XISO_SECTOR_SIZE + s_xbox_disc_lseek , SEEK_SET ) == -1 ) seek_err ();
1329+ if (!err ) if (lseek (in_xiso , dir_start , SEEK_SET ) == -1 ) seek_err ();
12961330
12971331 if (!err ) {
12981332 if (!s_remove_systemupdate || !strstr (node -> filename , s_systemupdate ))
@@ -1309,16 +1343,19 @@ int process_node(int in_xiso, dir_node* node, char* in_path, modes in_mode, dir_
13091343
13101344 if (!err ) {
13111345 // Recurse on subdirectory
1312- if (in_path ) if (asprintf (& path , "%s%s%c" , in_path , node -> filename , PATH_CHAR ) == -1 ) mem_err ();
1313- if (!err && node -> file_size > 0 ) err = traverse_xiso (in_xiso , (xoff_t )node -> start_sector * XISO_SECTOR_SIZE + s_xbox_disc_lseek , 0 , path , in_mode , in_root );
1346+ if (!err && node -> file_size > 0 ) {
1347+ if (in_path ) if (asprintf (& path , "%s%s%c" , in_path , node -> filename , PATH_CHAR ) == -1 ) mem_err ();
1348+ end_offset = n_sectors (node -> file_size ) * XISO_SECTOR_SIZE / XISO_DWORD_SIZE ;
1349+ err = traverse_xiso (in_xiso , dir_start , 0 , end_offset , path , in_mode , in_root , strategy );
1350+ if (path ) free (path );
1351+ }
13141352
13151353 if (!s_remove_systemupdate || !strstr (node -> filename , s_systemupdate ))
13161354 {
13171355 if (!err && in_mode == k_extract && (err = chdir (".." ))) chdir_err (".." );
13181356 }
13191357 }
1320-
1321- if (path ) free (path );
1358+
13221359 }
13231360 else if (in_mode != k_generate_avl ) { // Write file
13241361 if (!err ) {
@@ -1605,19 +1642,17 @@ char *boyer_moore_search( char *in_text, long in_text_len ) {
16051642int extract_file ( int in_xiso , dir_node * in_file , modes in_mode , char * path ) {
16061643 int err = 0 ;
16071644 bool warn = false;
1645+ xoff_t file_start = (xoff_t )in_file -> start_sector * XISO_SECTOR_SIZE + s_xbox_disc_lseek ;
16081646 uint32_t i , size , read_size , totalsize = 0 ;
16091647 float totalpercent = 0.0f ;
16101648 int out ;
16111649
1612- if ( s_remove_systemupdate && strstr ( path , s_systemupdate ) ){
1613- if ( ! err && lseek ( in_xiso , (xoff_t ) in_file -> start_sector * XISO_SECTOR_SIZE + s_xbox_disc_lseek , SEEK_SET ) == -1 ) seek_err ();
1614- }
1615- else {
1650+ if (lseek (in_xiso , file_start , SEEK_SET ) == -1 ) seek_err ();
1651+
1652+ if ( !s_remove_systemupdate || !strstr ( path , s_systemupdate ) ) {
16161653 if ( in_mode == k_extract ) {
1617- if ( ( out = open ( in_file -> filename , WRITEFLAGS , 0644 ) ) == -1 ) open_err ( in_file -> filename );
1654+ if (! err && ( out = open (in_file -> filename , WRITEFLAGS , 0644 ) ) == -1 ) open_err (in_file -> filename );
16181655 } else err = 1 ;
1619-
1620- if ( ! err && lseek ( in_xiso , (xoff_t ) in_file -> start_sector * XISO_SECTOR_SIZE + s_xbox_disc_lseek , SEEK_SET ) == -1 ) seek_err ();
16211656
16221657 if ( ! err ) {
16231658 exiso_log ("\n" );
@@ -1812,11 +1847,13 @@ int write_file( dir_node_avl *in_avl, write_tree_context *in_context, int in_dep
18121847
18131848
18141849int write_directory ( dir_node_avl * in_avl , write_tree_context * in_context , int in_depth ) {
1815- xoff_t pos ;
1816- int err = 0 , pad ;
1817- uint16_t l_offset , r_offset ;
1818- uint32_t file_size = in_avl -> file_size + (in_avl -> subdirectory ? (XISO_SECTOR_SIZE - (in_avl -> file_size % XISO_SECTOR_SIZE )) % XISO_SECTOR_SIZE : 0 );
1819- char length = (char ) strlen ( in_avl -> filename ), attributes = in_avl -> subdirectory ? XISO_ATTRIBUTE_DIR : XISO_ATTRIBUTE_ARC , sector [ XISO_SECTOR_SIZE ];
1850+ xoff_t pos ;
1851+ int err = 0 , pad ;
1852+ uint16_t l_offset , r_offset ;
1853+ uint32_t file_size = in_avl -> file_size + (in_avl -> subdirectory ? (XISO_SECTOR_SIZE - (in_avl -> file_size % XISO_SECTOR_SIZE )) % XISO_SECTOR_SIZE : 0 );
1854+ char length = (char )strlen (in_avl -> filename );
1855+ char attributes = in_avl -> subdirectory ? XISO_ATTRIBUTE_DIR : XISO_ATTRIBUTE_ARC ;
1856+ char sector [XISO_SECTOR_SIZE ];
18201857
18211858 little32 ( in_avl -> file_size );
18221859 little32 ( in_avl -> start_sector );
0 commit comments