mirror of
https://github.com/Elpisdev/unsegaREBORN.git
synced 2026-09-22 22:28:11 +03:00
sorting improvement
This commit is contained in:
@@ -241,6 +241,7 @@ typedef struct {
|
||||
uint64_t data_start_offset;
|
||||
bool silent;
|
||||
bool verbose;
|
||||
bool scan_only;
|
||||
uint64_t extracted_bytes;
|
||||
uint64_t files_extracted;
|
||||
int highest_extracted_vhd;
|
||||
|
||||
+156
-109
@@ -93,6 +93,40 @@ static inline void path_join(char* dest, size_t size, const char* dir, const cha
|
||||
}
|
||||
}
|
||||
|
||||
static void finalize_group(AppContext* ctx) {
|
||||
if (ctx->cached_parent.vhd) {
|
||||
if (!ctx->cached_parent_consumed || ctx->keep_versions) {
|
||||
uint64_t vf = 0, vb = 0;
|
||||
if (vhd_extract_ntfs(ctx->cached_parent.vhd, ctx->cached_parent_dir,
|
||||
ctx->silent, ctx->verbose, &vf, &vb)) {
|
||||
ctx->total_files_extracted += vf;
|
||||
ctx->total_bytes_extracted += vb;
|
||||
}
|
||||
}
|
||||
}
|
||||
free_cached_parent(&ctx->cached_parent);
|
||||
if (ctx->stacked_final_name[0] && ctx->cached_parent_output_dir[0]) {
|
||||
rename(ctx->cached_parent_output_dir, ctx->stacked_final_name);
|
||||
if (ctx->stacked_final_inner[0]) {
|
||||
size_t outer_len = strlen(ctx->cached_parent_output_dir);
|
||||
const char* inner_suffix = ctx->cached_parent_dir + outer_len;
|
||||
char old_inner[MAX_PATH_LENGTH];
|
||||
snprintf(old_inner, sizeof(old_inner), "%s%s",
|
||||
ctx->stacked_final_name, inner_suffix);
|
||||
char new_inner[MAX_PATH_LENGTH];
|
||||
path_join(new_inner, sizeof(new_inner),
|
||||
ctx->stacked_final_name, ctx->stacked_final_inner);
|
||||
if (strcmp(old_inner, new_inner) != 0)
|
||||
rename(old_inner, new_inner);
|
||||
}
|
||||
}
|
||||
ctx->cached_parent_consumed = false;
|
||||
ctx->stacked_to_parent = false;
|
||||
ctx->cached_parent_output_dir[0] = '\0';
|
||||
ctx->stacked_final_name[0] = '\0';
|
||||
ctx->stacked_final_inner[0] = '\0';
|
||||
}
|
||||
|
||||
static void remove_dir_tree(const char* dir_path) {
|
||||
char search[MAX_PATH_LENGTH];
|
||||
snprintf(search, sizeof(search), "%s%s*", dir_path, PATH_SEPARATOR);
|
||||
@@ -286,6 +320,7 @@ static void copy_runs_from_opt(RunSource* dst, const PendingOpt* opt, void* ntfs
|
||||
|
||||
#define EXTRACT_F_ALLOW_VHD 0x01
|
||||
#define EXTRACT_F_RMDIR_ON_ORPHAN 0x02
|
||||
#define EXTRACT_F_SCAN_ONLY 0x04
|
||||
|
||||
static void strip_extension(char* path) {
|
||||
char* ext = strrchr(path, '.');
|
||||
@@ -529,6 +564,7 @@ static bool extract_stream_fs(AppContext* app_ctx, DecryptStream* stream,
|
||||
VERBOSE(app_ctx, "MFT=%llu c=%u\n",
|
||||
(unsigned long long)ctx->total_mft_records, ctx->bytes_per_cluster);
|
||||
|
||||
if (flags & EXTRACT_F_SCAN_ONLY) ctx->scan_only = true;
|
||||
ctx->silent = true;
|
||||
if (ntfs_extract_all(ctx)) {
|
||||
ctx->silent = app_ctx->silent;
|
||||
@@ -924,6 +960,8 @@ static ErrorCode do_stream(AppContext* app_ctx, const char* path) {
|
||||
VERBOSE(app_ctx, "MFT=%llu c=%u\n",
|
||||
(unsigned long long)ctx->total_mft_records, ctx->bytes_per_cluster);
|
||||
|
||||
if (!app_ctx->caching_parent && app_ctx->cached_parent.vhd && !app_ctx->keep_versions)
|
||||
ctx->scan_only = true;
|
||||
ctx->silent = true;
|
||||
|
||||
if (ntfs_extract_all(ctx)) {
|
||||
@@ -982,8 +1020,10 @@ static ErrorCode do_stream(AppContext* app_ctx, const char* path) {
|
||||
}
|
||||
}
|
||||
else {
|
||||
extraction_success = extract_stream_fs(app_ctx, stream, output_dir, FS_NTFS,
|
||||
EXTRACT_F_ALLOW_VHD);
|
||||
uint32_t eflags = EXTRACT_F_ALLOW_VHD;
|
||||
if (!app_ctx->caching_parent && app_ctx->cached_parent.vhd && !app_ctx->keep_versions)
|
||||
eflags |= EXTRACT_F_SCAN_ONLY;
|
||||
extraction_success = extract_stream_fs(app_ctx, stream, output_dir, FS_NTFS, eflags);
|
||||
|
||||
if (app_ctx->cached_parent.inner_ntfs &&
|
||||
app_ctx->cached_parent.inner_ntfs->stream == stream) {
|
||||
@@ -1206,7 +1246,7 @@ int lib_main(int argc, char** argv) {
|
||||
else if (strcmp(a, "-w") == 0) app_ctx.write_intermediate = true;
|
||||
else if (strcmp(a, "-o") == 0 && i + 1 < argc) app_ctx.output_dir = argv[++i];
|
||||
else if (strcmp(a, "-p") == 0 && i + 1 < argc) app_ctx.parent_file = argv[++i];
|
||||
else if (strcmp(a, "-k") == 0 || strcmp(a, "--keep") == 0) app_ctx.keep_versions = true;
|
||||
else if (strcmp(a, "-k") == 0) app_ctx.keep_versions = true;
|
||||
continue;
|
||||
}
|
||||
input_files[input_file_count++] = a;
|
||||
@@ -1215,114 +1255,143 @@ int lib_main(int argc, char** argv) {
|
||||
if (input_file_count == 0) { fprintf(stderr, "no files\n"); return 1; }
|
||||
if (!key_any()) fprintf(stderr, "no keys\n");
|
||||
|
||||
uint8_t sort_key[256][10]; // game_id(4) + container_type(1) + seq(1) + version(4)
|
||||
for (int i = 0; i < input_file_count; i++) {
|
||||
memset(sort_key[i], 0xFF, 10);
|
||||
FILE* f = FOPEN(input_files[i], "rb");
|
||||
if (!f) continue;
|
||||
uint8_t enc[96];
|
||||
if (fread(enc, 1, 96, f) == 96) {
|
||||
BootId id;
|
||||
decrypt_bootid(enc, &id);
|
||||
memcpy(sort_key[i], id.game_id, 4);
|
||||
sort_key[i][4] = id.container_type;
|
||||
sort_key[i][5] = id.sequence_number;
|
||||
memcpy(sort_key[i] + 6, id.target_version.option, 4);
|
||||
}
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
for (int i = 1; i < input_file_count; i++) {
|
||||
for (int j = i; j > 0; j--) {
|
||||
if (strcmp(get_basename(input_files[j-1]), get_basename(input_files[j])) > 0) {
|
||||
const char* tmp = input_files[j-1];
|
||||
input_files[j-1] = input_files[j];
|
||||
input_files[j] = tmp;
|
||||
if (memcmp(sort_key[j-1], sort_key[j], 10) > 0) {
|
||||
uint8_t tk[10]; memcpy(tk, sort_key[j-1], 10); memcpy(sort_key[j-1], sort_key[j], 10); memcpy(sort_key[j], tk, 10);
|
||||
const char* tp = input_files[j-1]; input_files[j-1] = input_files[j]; input_files[j] = tp;
|
||||
} else break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!app_ctx.parent_file && input_file_count > 1) {
|
||||
app_ctx.parent_file = input_files[0];
|
||||
for (int i = 0; i < input_file_count - 1; i++)
|
||||
input_files[i] = input_files[i + 1];
|
||||
input_file_count--;
|
||||
}
|
||||
int group_starts[257];
|
||||
int group_count;
|
||||
|
||||
if (app_ctx.parent_file) {
|
||||
app_ctx.caching_parent = true;
|
||||
ErrorCode perr = do_file(&app_ctx, app_ctx.parent_file);
|
||||
app_ctx.caching_parent = false;
|
||||
app_ctx.parent_file = NULL;
|
||||
|
||||
if (perr == ERR_OK && app_ctx.write_intermediate &&
|
||||
app_ctx.extract_fs && app_ctx.output_filename) {
|
||||
const char* pbn = get_basename(app_ctx.output_filename);
|
||||
char pbn_no_ext[MAX_PATH_LENGTH];
|
||||
strncpy(pbn_no_ext, pbn, sizeof(pbn_no_ext) - 1);
|
||||
pbn_no_ext[sizeof(pbn_no_ext) - 1] = '\0';
|
||||
strip_extension(pbn_no_ext);
|
||||
|
||||
char parent_out[MAX_PATH_LENGTH];
|
||||
if (app_ctx.output_dir)
|
||||
path_join(parent_out, sizeof(parent_out), app_ctx.output_dir, pbn_no_ext);
|
||||
else {
|
||||
strncpy(parent_out, app_ctx.output_filename, sizeof(parent_out) - 1);
|
||||
parent_out[sizeof(parent_out) - 1] = '\0';
|
||||
strip_extension(parent_out);
|
||||
}
|
||||
|
||||
FsType pfs = strstr(app_ctx.output_filename, ".exfat") ? FS_EXFAT : FS_NTFS;
|
||||
strncpy(app_ctx.cached_parent_output_dir, parent_out, MAX_PATH_LENGTH - 1);
|
||||
app_ctx.cached_parent_output_dir[MAX_PATH_LENGTH - 1] = '\0';
|
||||
app_ctx.caching_parent = true;
|
||||
extract_file_fs(&app_ctx, app_ctx.output_filename, parent_out, pfs,
|
||||
EXTRACT_F_ALLOW_VHD);
|
||||
app_ctx.caching_parent = false;
|
||||
|
||||
free(app_ctx.output_filename);
|
||||
app_ctx.output_filename = NULL;
|
||||
}
|
||||
|
||||
if (perr != ERR_OK || !app_ctx.cached_parent.vhd) {
|
||||
fprintf(stderr, "parent failed\n");
|
||||
group_starts[0] = 0;
|
||||
group_starts[1] = input_file_count;
|
||||
group_count = 1;
|
||||
} else {
|
||||
group_starts[0] = 0;
|
||||
group_count = 1;
|
||||
for (int i = 1; i < input_file_count; i++) {
|
||||
if (memcmp(sort_key[i], sort_key[i-1], 5) != 0)
|
||||
group_starts[group_count++] = i;
|
||||
}
|
||||
group_starts[group_count] = input_file_count;
|
||||
}
|
||||
|
||||
bool any_failed = false;
|
||||
for (int i = 0; i < input_file_count; ++i) {
|
||||
const char* file_path = input_files[i];
|
||||
if (do_file(&app_ctx, file_path) == ERR_OK) {
|
||||
if (app_ctx.extract_fs && app_ctx.output_filename) {
|
||||
const char* basename = get_basename(app_ctx.output_filename);
|
||||
for (int gi = 0; gi < group_count; gi++) {
|
||||
int gs = group_starts[gi];
|
||||
int ge = group_starts[gi + 1];
|
||||
int ds = gs;
|
||||
|
||||
char basename_no_ext[MAX_PATH_LENGTH];
|
||||
strncpy(basename_no_ext, basename, sizeof(basename_no_ext) - 1);
|
||||
basename_no_ext[sizeof(basename_no_ext) - 1] = '\0';
|
||||
strip_extension(basename_no_ext);
|
||||
const char* parent_path = app_ctx.parent_file;
|
||||
if (!parent_path && ge - gs > 1) {
|
||||
parent_path = input_files[gs];
|
||||
ds = gs + 1;
|
||||
}
|
||||
|
||||
char output_dir[MAX_PATH_LENGTH];
|
||||
if (app_ctx.output_dir) {
|
||||
path_join(output_dir, sizeof(output_dir), app_ctx.output_dir, basename_no_ext);
|
||||
} else {
|
||||
strncpy(output_dir, app_ctx.output_filename, sizeof(output_dir) - 1);
|
||||
output_dir[sizeof(output_dir) - 1] = '\0';
|
||||
strip_extension(output_dir);
|
||||
if (parent_path) {
|
||||
app_ctx.caching_parent = true;
|
||||
ErrorCode perr = do_file(&app_ctx, parent_path);
|
||||
app_ctx.caching_parent = false;
|
||||
app_ctx.parent_file = NULL;
|
||||
|
||||
if (perr == ERR_OK && app_ctx.write_intermediate &&
|
||||
app_ctx.extract_fs && app_ctx.output_filename) {
|
||||
const char* pbn = get_basename(app_ctx.output_filename);
|
||||
char pbn_no_ext[MAX_PATH_LENGTH];
|
||||
strncpy(pbn_no_ext, pbn, sizeof(pbn_no_ext) - 1);
|
||||
pbn_no_ext[sizeof(pbn_no_ext) - 1] = '\0';
|
||||
strip_extension(pbn_no_ext);
|
||||
|
||||
char parent_out[MAX_PATH_LENGTH];
|
||||
if (app_ctx.output_dir)
|
||||
path_join(parent_out, sizeof(parent_out), app_ctx.output_dir, pbn_no_ext);
|
||||
else {
|
||||
strncpy(parent_out, app_ctx.output_filename, sizeof(parent_out) - 1);
|
||||
parent_out[sizeof(parent_out) - 1] = '\0';
|
||||
strip_extension(parent_out);
|
||||
}
|
||||
|
||||
FsType fs_type = strstr(app_ctx.output_filename, ".exfat") ? FS_EXFAT : FS_NTFS;
|
||||
extract_file_fs(&app_ctx, app_ctx.output_filename, output_dir, fs_type,
|
||||
FsType pfs = strstr(app_ctx.output_filename, ".exfat") ? FS_EXFAT : FS_NTFS;
|
||||
strncpy(app_ctx.cached_parent_output_dir, parent_out, MAX_PATH_LENGTH - 1);
|
||||
app_ctx.cached_parent_output_dir[MAX_PATH_LENGTH - 1] = '\0';
|
||||
app_ctx.caching_parent = true;
|
||||
extract_file_fs(&app_ctx, app_ctx.output_filename, parent_out, pfs,
|
||||
EXTRACT_F_ALLOW_VHD);
|
||||
|
||||
if (app_ctx.stacked_to_parent) {
|
||||
remove_dir_tree(output_dir);
|
||||
strncpy(app_ctx.stacked_final_name, output_dir, MAX_PATH_LENGTH - 1);
|
||||
app_ctx.stacked_final_name[MAX_PATH_LENGTH - 1] = '\0';
|
||||
app_ctx.stacked_to_parent = false;
|
||||
}
|
||||
app_ctx.caching_parent = false;
|
||||
|
||||
free(app_ctx.output_filename);
|
||||
app_ctx.output_filename = NULL;
|
||||
}
|
||||
}
|
||||
else {
|
||||
fprintf(stderr, "fail:%s\n", file_path);
|
||||
any_failed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (app_ctx.cached_parent.vhd) {
|
||||
if (!app_ctx.cached_parent_consumed || app_ctx.keep_versions) {
|
||||
uint64_t vhd_files = 0, vhd_bytes = 0;
|
||||
if (vhd_extract_ntfs(app_ctx.cached_parent.vhd, app_ctx.cached_parent_dir,
|
||||
app_ctx.silent, app_ctx.verbose, &vhd_files, &vhd_bytes)) {
|
||||
app_ctx.total_files_extracted += vhd_files;
|
||||
app_ctx.total_bytes_extracted += vhd_bytes;
|
||||
if (perr != ERR_OK || !app_ctx.cached_parent.vhd) {
|
||||
fprintf(stderr, "parent failed\n");
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = ds; i < ge; i++) {
|
||||
const char* file_path = input_files[i];
|
||||
if (do_file(&app_ctx, file_path) == ERR_OK) {
|
||||
if (app_ctx.extract_fs && app_ctx.output_filename) {
|
||||
const char* basename = get_basename(app_ctx.output_filename);
|
||||
|
||||
char basename_no_ext[MAX_PATH_LENGTH];
|
||||
strncpy(basename_no_ext, basename, sizeof(basename_no_ext) - 1);
|
||||
basename_no_ext[sizeof(basename_no_ext) - 1] = '\0';
|
||||
strip_extension(basename_no_ext);
|
||||
|
||||
char output_dir[MAX_PATH_LENGTH];
|
||||
if (app_ctx.output_dir) {
|
||||
path_join(output_dir, sizeof(output_dir), app_ctx.output_dir, basename_no_ext);
|
||||
} else {
|
||||
strncpy(output_dir, app_ctx.output_filename, sizeof(output_dir) - 1);
|
||||
output_dir[sizeof(output_dir) - 1] = '\0';
|
||||
strip_extension(output_dir);
|
||||
}
|
||||
|
||||
FsType fs_type = strstr(app_ctx.output_filename, ".exfat") ? FS_EXFAT : FS_NTFS;
|
||||
extract_file_fs(&app_ctx, app_ctx.output_filename, output_dir, fs_type,
|
||||
EXTRACT_F_ALLOW_VHD);
|
||||
|
||||
if (app_ctx.stacked_to_parent) {
|
||||
remove_dir_tree(output_dir);
|
||||
strncpy(app_ctx.stacked_final_name, output_dir, MAX_PATH_LENGTH - 1);
|
||||
app_ctx.stacked_final_name[MAX_PATH_LENGTH - 1] = '\0';
|
||||
app_ctx.stacked_to_parent = false;
|
||||
}
|
||||
|
||||
free(app_ctx.output_filename);
|
||||
app_ctx.output_filename = NULL;
|
||||
}
|
||||
}
|
||||
else {
|
||||
fprintf(stderr, "fail:%s\n", file_path);
|
||||
any_failed = true;
|
||||
}
|
||||
}
|
||||
|
||||
finalize_group(&app_ctx);
|
||||
}
|
||||
|
||||
if (!app_ctx.silent && app_ctx.total_files_extracted > 0) {
|
||||
@@ -1337,27 +1406,5 @@ int lib_main(int argc, char** argv) {
|
||||
free(app_ctx.output_filename);
|
||||
}
|
||||
|
||||
free_cached_parent(&app_ctx.cached_parent);
|
||||
|
||||
if (app_ctx.stacked_final_name[0] && app_ctx.cached_parent_output_dir[0]) {
|
||||
rename(app_ctx.cached_parent_output_dir, app_ctx.stacked_final_name);
|
||||
|
||||
if (app_ctx.stacked_final_inner[0]) {
|
||||
size_t outer_len = strlen(app_ctx.cached_parent_output_dir);
|
||||
const char* inner_suffix = app_ctx.cached_parent_dir + outer_len;
|
||||
|
||||
char old_inner[MAX_PATH_LENGTH];
|
||||
snprintf(old_inner, sizeof(old_inner), "%s%s",
|
||||
app_ctx.stacked_final_name, inner_suffix);
|
||||
|
||||
char new_inner[MAX_PATH_LENGTH];
|
||||
path_join(new_inner, sizeof(new_inner),
|
||||
app_ctx.stacked_final_name, app_ctx.stacked_final_inner);
|
||||
|
||||
if (strcmp(old_inner, new_inner) != 0)
|
||||
rename(old_inner, new_inner);
|
||||
}
|
||||
}
|
||||
|
||||
return any_failed ? 1 : 0;
|
||||
}
|
||||
|
||||
+18
-15
@@ -538,6 +538,7 @@ static bool extract_file(NTFSContext* ctx, const MFTRecordHeader* record,
|
||||
if (is_opt_file) {
|
||||
return store_pending_opt(ctx, filename, record);
|
||||
}
|
||||
if (ctx->scan_only) return true;
|
||||
}
|
||||
|
||||
uint64_t file_size = 0;
|
||||
@@ -740,21 +741,23 @@ static bool process_mft_record(NTFSContext* ctx, const uint8_t* record_data) {
|
||||
}
|
||||
|
||||
if (is_directory) {
|
||||
if (!create_directories(full_path)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (modification_time != 0) {
|
||||
DeferredDirTime* dirs = (DeferredDirTime*)ctx->deferred_dirs;
|
||||
if (ctx->deferred_count >= ctx->deferred_capacity) {
|
||||
if (grow_deferred_dirs(&dirs, &ctx->deferred_capacity))
|
||||
ctx->deferred_dirs = dirs;
|
||||
if (!ctx->scan_only) {
|
||||
if (!create_directories(full_path)) {
|
||||
return false;
|
||||
}
|
||||
if (ctx->deferred_count < ctx->deferred_capacity) {
|
||||
DeferredDirTime* d = &dirs[ctx->deferred_count++];
|
||||
STRCPY_S(d->path, sizeof(d->path), full_path);
|
||||
d->mtime = modification_time;
|
||||
d->atime = access_time;
|
||||
|
||||
if (modification_time != 0) {
|
||||
DeferredDirTime* dirs = (DeferredDirTime*)ctx->deferred_dirs;
|
||||
if (ctx->deferred_count >= ctx->deferred_capacity) {
|
||||
if (grow_deferred_dirs(&dirs, &ctx->deferred_capacity))
|
||||
ctx->deferred_dirs = dirs;
|
||||
}
|
||||
if (ctx->deferred_count < ctx->deferred_capacity) {
|
||||
DeferredDirTime* d = &dirs[ctx->deferred_count++];
|
||||
STRCPY_S(d->path, sizeof(d->path), full_path);
|
||||
d->mtime = modification_time;
|
||||
d->atime = access_time;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1392,7 +1395,7 @@ bool ntfs_init_stream(NTFSContext* ctx, DecryptStream* stream, const char* extra
|
||||
#define MFT_BATCH_RECORDS 1024
|
||||
|
||||
bool ntfs_extract_all(NTFSContext* ctx) {
|
||||
if (!create_directories(ctx->base_path)) return false;
|
||||
if (!ctx->scan_only && !create_directories(ctx->base_path)) return false;
|
||||
|
||||
bool ok = true;
|
||||
ctx->extracted_bytes = 0;
|
||||
|
||||
Reference in New Issue
Block a user