Skip to content

Commit 36e10b9

Browse files
committed
Add new strategy to list files in directory
Discover files not in tree in rewrite mode
1 parent dcd2f53 commit 36e10b9

File tree

1 file changed

+122
-85
lines changed

1 file changed

+122
-85
lines changed

extract-xiso.c

Lines changed: 122 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -505,6 +505,8 @@
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

523525
typedef enum modes { k_generate_avl, k_extract, k_list, k_rewrite } modes;
524526
typedef 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

526529
typedef void (*progress_callback)( xoff_t in_current_value, xoff_t in_final_value );
527530
typedef 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 );
597600
int extract_file( int in_xiso, dir_node *in_file, modes in_mode, char *path );
598601
int decode_xiso( char *in_xiso, char *in_path, modes in_mode, char **out_iso_path );
599602
int 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);
602605
int 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

604607
FILE_TIME *alloc_filetime_now( void );
@@ -1112,7 +1115,9 @@ int create_xiso( char *in_root_directory, char *in_output_directory, dir_node_av
11121115
int 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 ) {
16051642
int 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

18141849
int 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

Comments
 (0)