mirror of
https://github.com/nunuhara/xsystem4.git
synced 2026-10-05 13:28:04 +03:00
alice-ar: support extracting ALD and AFA files
Support whole archive, by-index and by-name extraction of files from ALD and AFA archives.
This commit is contained in:
+162
-9
@@ -17,9 +17,17 @@
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <ctype.h>
|
||||
#include <dirent.h>
|
||||
#include <getopt.h>
|
||||
#include <limits.h>
|
||||
#include <libgen.h>
|
||||
#include "system4.h"
|
||||
#include "system4/afa.h"
|
||||
#include "system4/ald.h"
|
||||
#include "file.h"
|
||||
#include "system4/string.h"
|
||||
#include "system4/utfsjis.h"
|
||||
|
||||
@@ -52,6 +60,144 @@ enum {
|
||||
LOPT_FORCE,
|
||||
};
|
||||
|
||||
// dirname is allowed to return a pointer to static memory OR modify its input.
|
||||
// This works around the braindamage by ALWAYS returning a pointer to static
|
||||
// memory, at the cost of a string copy.
|
||||
char *xdirname(const char *path)
|
||||
{
|
||||
static char buf[PATH_MAX];
|
||||
strncpy(buf, path, PATH_MAX-1);
|
||||
return dirname(buf);
|
||||
}
|
||||
|
||||
char *xbasename(const char *path)
|
||||
{
|
||||
static char buf[PATH_MAX];
|
||||
strncpy(buf, path, PATH_MAX-1);
|
||||
return basename(buf);
|
||||
}
|
||||
|
||||
static struct archive *open_ald_archive(const char *path, int *error)
|
||||
{
|
||||
int count = 0;
|
||||
char *dir_name = xdirname(path);
|
||||
char *base_name = xbasename(path);
|
||||
char *ald_filenames[ALD_FILEMAX];
|
||||
int prefix_len = strlen(base_name) - 5;
|
||||
if (prefix_len <= 0)
|
||||
return NULL;
|
||||
|
||||
memset(ald_filenames, 0, sizeof(char*) * ALD_FILEMAX);
|
||||
|
||||
DIR *dir;
|
||||
struct dirent *d;
|
||||
char filepath[PATH_MAX];
|
||||
|
||||
if (!(dir = opendir(dir_name))) {
|
||||
ERROR("Failed to open directory: %s", path);
|
||||
}
|
||||
|
||||
while ((d = readdir(dir))) {
|
||||
printf("checking %s\n", d->d_name);
|
||||
int len = strlen(d->d_name);
|
||||
if (len < prefix_len + 5 || strcasecmp(d->d_name+len-4, ".ald"))
|
||||
continue;
|
||||
if (strncasecmp(d->d_name, base_name, prefix_len))
|
||||
continue;
|
||||
|
||||
int dno = toupper(*(d->d_name+len-5)) - 'A';
|
||||
if (dno < 0 || dno >= ALD_FILEMAX)
|
||||
continue;
|
||||
|
||||
snprintf(filepath, PATH_MAX-1, "%s/%s", dir_name, d->d_name);
|
||||
ald_filenames[dno] = strdup(filepath);
|
||||
count = max(count, dno+1);
|
||||
}
|
||||
|
||||
struct archive *ar = ald_open(ald_filenames, count, ARCHIVE_MMAP, error);
|
||||
|
||||
for (int i = 0; i < ALD_FILEMAX; i++) {
|
||||
free(ald_filenames[i]);
|
||||
}
|
||||
|
||||
return ar;
|
||||
}
|
||||
|
||||
static struct archive *open_archive(const char *path, int *error)
|
||||
{
|
||||
size_t len = strlen(path);
|
||||
if (len < 4)
|
||||
goto err;
|
||||
|
||||
const char *ext = path + len - 4;
|
||||
if (!strcasecmp(ext, ".ald")) {
|
||||
return open_ald_archive(path, error);
|
||||
} else if (!strcasecmp(ext, ".afa")) {
|
||||
return (struct archive*)afa_open(path, ARCHIVE_MMAP, error);
|
||||
}
|
||||
|
||||
err:
|
||||
usage();
|
||||
ERROR("Couldn't determine archive type for '%s'", path);
|
||||
}
|
||||
|
||||
static void mkdir_for_file(const char *filename)
|
||||
{
|
||||
char *tmp = strdup(filename);
|
||||
char *dir = dirname(tmp);
|
||||
mkdir_p(dir);
|
||||
free(tmp);
|
||||
}
|
||||
|
||||
static void write_file(struct archive_data *data, const char *output_file)
|
||||
{
|
||||
FILE *f = NULL;
|
||||
|
||||
if (!output_file) {
|
||||
char *u = sjis2utf(data->name, strlen(data->name));
|
||||
mkdir_for_file(u);
|
||||
if (!(f = fopen(u, "wb")))
|
||||
ERROR("fopen failed: %s", strerror(errno));
|
||||
free(u);
|
||||
} else if (strcmp(output_file, "-")) {
|
||||
if (!(f = fopen(output_file, "wb")))
|
||||
ERROR("fopen failed: %s", strerror(errno));
|
||||
}
|
||||
|
||||
if (fwrite(data->data, data->size, 1, f ? f : stdout) != 1)
|
||||
ERROR("fwrite failed: %s", strerror(errno));
|
||||
|
||||
if (f)
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
static void extract_all_iter(struct archive_data *data, void *_output_dir)
|
||||
{
|
||||
char *output_dir = _output_dir ? _output_dir : ".";
|
||||
char *file_name = sjis2utf(data->name, 0);
|
||||
char output_file[PATH_MAX];
|
||||
|
||||
snprintf(output_file, PATH_MAX, "%s/%s", output_dir, file_name);
|
||||
free(file_name);
|
||||
|
||||
mkdir_for_file(output_file);
|
||||
|
||||
printf("%s\n", output_file);
|
||||
|
||||
if (!archive_load_file(data)) {
|
||||
WARNING("Error loading file: %s", output_file);
|
||||
return;
|
||||
}
|
||||
write_file(data, output_file);
|
||||
}
|
||||
|
||||
static void list_all_iter(struct archive_data *data, possibly_unused void *_)
|
||||
{
|
||||
char *name = sjis2utf(data->name, 0);
|
||||
printf("%d: %s\n", data->no, name);
|
||||
free(name);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
char *output_file = NULL;
|
||||
@@ -64,7 +210,7 @@ int main(int argc, char *argv[])
|
||||
bool add = false;
|
||||
bool delete = false;
|
||||
bool list = false;
|
||||
bool force = false;
|
||||
//bool force = false;
|
||||
|
||||
while (1) {
|
||||
static struct option long_options[] = {
|
||||
@@ -146,25 +292,32 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
|
||||
int error;
|
||||
struct afa_archive *ar = afa_open(argv[0], ARCHIVE_MMAP, &error);
|
||||
struct archive *ar = open_archive(argv[0], &error);
|
||||
if (!ar) {
|
||||
ERROR("Opening archive: %s", archive_strerror(error));
|
||||
}
|
||||
|
||||
if (extract) {
|
||||
WARNING("archive extraction not yet implemented");
|
||||
if (file_index >= 0) {
|
||||
struct archive_data *d = archive_get(ar, file_index);
|
||||
if (!d)
|
||||
ERROR("No file with index %d", file_index);
|
||||
write_file(d, output_file);
|
||||
} else if (file_name) {
|
||||
char *u = utf2sjis(file_name, strlen(file_name));
|
||||
struct archive_data *d = archive_get_by_name(ar, u);
|
||||
write_file(d, output_file);
|
||||
} else {
|
||||
archive_for_each(ar, extract_all_iter, output_file);
|
||||
}
|
||||
} else if (add) {
|
||||
WARNING("adding files to archive not yet implemented");
|
||||
} else if (delete) {
|
||||
WARNING("deleting files from archive not yet implemented");
|
||||
} else if (list) {
|
||||
for (uint32_t i = 0; i < ar->nr_files; i++) {
|
||||
char *u = sjis2utf(ar->files[i].name->text, ar->files[i].name->size);
|
||||
printf("%u: %s\n", i, u);
|
||||
free(u);
|
||||
}
|
||||
archive_for_each(ar, list_all_iter, NULL);
|
||||
}
|
||||
|
||||
archive_free(&ar->ar);
|
||||
archive_free(ar);
|
||||
return 0;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user