2025-08-06 22:11:26 +01:00
|
|
|
#include "reader.h"
|
|
|
|
|
|
2026-06-22 17:45:54 +01:00
|
|
|
#include <fx/compression/cstream.h>
|
|
|
|
|
#include <fx/compression/zstd.h>
|
|
|
|
|
#include <fx/error.h>
|
|
|
|
|
#include <fx/io/directory.h>
|
|
|
|
|
#include <ropkg/package.h>
|
2025-08-06 22:11:26 +01:00
|
|
|
#include <ropkg/reader.h>
|
2026-06-22 17:45:54 +01:00
|
|
|
#include <stdlib.h>
|
2025-08-06 22:11:26 +01:00
|
|
|
|
2026-06-22 17:45:54 +01:00
|
|
|
struct ropkg_package {
|
|
|
|
|
fx_file *pkg_file;
|
|
|
|
|
fx_cstream *pkg_file_cstream;
|
|
|
|
|
|
|
|
|
|
struct ropkg_reader *pkg_reader;
|
|
|
|
|
fx_directory *pkg_metadata;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static fx_result extract_file(
|
2025-08-06 22:11:26 +01:00
|
|
|
struct ropkg_reader *pkg,
|
|
|
|
|
const char *path_cstr,
|
2026-06-22 17:45:54 +01:00
|
|
|
fx_directory *dest)
|
2025-08-06 22:11:26 +01:00
|
|
|
{
|
2026-06-22 17:45:54 +01:00
|
|
|
fx_path *path = fx_path_create_from_cstr(path_cstr);
|
2025-08-06 22:11:26 +01:00
|
|
|
if (!path) {
|
|
|
|
|
return ROPKG_RESULT_ERR(NO_MEMORY);
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-22 17:45:54 +01:00
|
|
|
fx_path *dir_path = NULL;
|
|
|
|
|
fx_path_get_directory(path, &dir_path);
|
2025-08-06 22:11:26 +01:00
|
|
|
if (!dir_path) {
|
2026-06-22 17:45:54 +01:00
|
|
|
fx_path_unref(path);
|
2025-08-06 22:11:26 +01:00
|
|
|
return ROPKG_RESULT_ERR(NO_MEMORY);
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-22 17:45:54 +01:00
|
|
|
fx_result result = FX_RESULT_SUCCESS;
|
|
|
|
|
if (fx_path_length(dir_path) > 0) {
|
|
|
|
|
fx_directory *parent_dir;
|
|
|
|
|
result = fx_directory_open(
|
2025-08-06 22:11:26 +01:00
|
|
|
dest,
|
|
|
|
|
dir_path,
|
2026-06-22 17:45:54 +01:00
|
|
|
FX_DIRECTORY_OPEN_CREATE_INTERMEDIATE,
|
2025-08-06 22:11:26 +01:00
|
|
|
&parent_dir);
|
2026-06-22 17:45:54 +01:00
|
|
|
fx_directory_unref(parent_dir);
|
2025-08-06 22:11:26 +01:00
|
|
|
}
|
|
|
|
|
|
2026-06-22 17:45:54 +01:00
|
|
|
if (fx_result_is_error(result)) {
|
2025-08-06 22:11:26 +01:00
|
|
|
goto end;
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-22 17:45:54 +01:00
|
|
|
fx_file *dest_file = NULL;
|
|
|
|
|
result = fx_file_open(
|
2025-08-06 22:11:26 +01:00
|
|
|
dest,
|
|
|
|
|
path,
|
2026-06-22 17:45:54 +01:00
|
|
|
FX_FILE_WRITE_ONLY | FX_FILE_CREATE | FX_FILE_BINARY,
|
2025-08-06 22:11:26 +01:00
|
|
|
&dest_file);
|
2026-06-22 17:45:54 +01:00
|
|
|
if (fx_result_is_error(result)) {
|
2025-08-06 22:11:26 +01:00
|
|
|
goto end;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
char data[] = "Hello";
|
|
|
|
|
size_t nr_written = 0;
|
2026-06-22 17:45:54 +01:00
|
|
|
fx_file_write(dest_file, 0, sizeof data, data, &nr_written);
|
|
|
|
|
fx_file_unref(dest_file);
|
2025-08-06 22:11:26 +01:00
|
|
|
|
|
|
|
|
end:
|
2026-06-22 17:45:54 +01:00
|
|
|
fx_path_unref(path);
|
|
|
|
|
fx_path_unref(dir_path);
|
2025-08-06 22:11:26 +01:00
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-22 17:45:54 +01:00
|
|
|
static fx_result extract_subpackage(
|
2025-08-06 22:11:26 +01:00
|
|
|
struct ropkg_reader *pkg,
|
|
|
|
|
const char *name,
|
2026-06-22 17:45:54 +01:00
|
|
|
fx_directory *dest)
|
2025-08-06 22:11:26 +01:00
|
|
|
{
|
2026-06-22 17:45:54 +01:00
|
|
|
fx_directory *dest_subdir;
|
|
|
|
|
fx_result result = fx_directory_open(
|
2025-08-06 22:11:26 +01:00
|
|
|
dest,
|
2026-06-22 17:45:54 +01:00
|
|
|
FX_RV_PATH(name),
|
|
|
|
|
FX_DIRECTORY_OPEN_CREATE,
|
2025-08-06 22:11:26 +01:00
|
|
|
&dest_subdir);
|
2026-06-22 17:45:54 +01:00
|
|
|
if (fx_result_is_error(result)) {
|
2025-08-06 22:11:26 +01:00
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-22 17:45:54 +01:00
|
|
|
fx_cstream_begin_compressed_section(pkg->r_stream, NULL);
|
2025-08-06 22:11:26 +01:00
|
|
|
|
|
|
|
|
struct ropkg_reader *subpkg = NULL;
|
|
|
|
|
enum ropkg_status status = ropkg_reader_open(pkg->r_stream, &subpkg);
|
|
|
|
|
if (status != ROPKG_SUCCESS) {
|
|
|
|
|
result = ROPKG_RESULT_STATUS(status);
|
|
|
|
|
goto end;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
while (!ropkg_reader_eof(subpkg)) {
|
|
|
|
|
const struct ropkg_file_info *file
|
|
|
|
|
= ropkg_reader_current_file(subpkg);
|
|
|
|
|
result = extract_file(subpkg, file->f_filename, dest_subdir);
|
2026-06-22 17:45:54 +01:00
|
|
|
if (fx_result_is_error(result)) {
|
2025-08-06 22:11:26 +01:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
status = ropkg_reader_move_next(subpkg);
|
|
|
|
|
if (status != ROPKG_SUCCESS) {
|
|
|
|
|
result = ROPKG_RESULT_STATUS(status);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
end:
|
2026-06-22 17:45:54 +01:00
|
|
|
fx_cstream_end_compressed_section(pkg->r_stream, NULL, NULL);
|
2025-08-06 22:11:26 +01:00
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-22 17:45:54 +01:00
|
|
|
static fx_result extract_metadata(struct ropkg_reader *pkg, fx_directory *dest)
|
2025-08-06 22:11:26 +01:00
|
|
|
{
|
2026-06-22 17:45:54 +01:00
|
|
|
fx_result result = FX_RESULT_SUCCESS;
|
2025-08-06 22:11:26 +01:00
|
|
|
|
|
|
|
|
while (!ropkg_reader_eof(pkg)) {
|
|
|
|
|
const struct ropkg_file_info *file
|
|
|
|
|
= ropkg_reader_current_file(pkg);
|
|
|
|
|
|
|
|
|
|
int section = -1;
|
|
|
|
|
|
|
|
|
|
if (!strncmp(file->f_filename, "meta.tar", 8)) {
|
|
|
|
|
result = extract_subpackage(pkg, "meta", dest);
|
|
|
|
|
} else if (!strncmp(file->f_filename, "control.tar", 11)) {
|
|
|
|
|
result = extract_subpackage(pkg, "control", dest);
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-22 17:45:54 +01:00
|
|
|
if (fx_result_is_error(result)) {
|
2025-08-06 22:11:26 +01:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
enum ropkg_status status = ropkg_reader_move_next(pkg);
|
|
|
|
|
if (status != ROPKG_SUCCESS) {
|
|
|
|
|
result = ROPKG_RESULT_STATUS(status);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
}
|
2026-06-22 17:45:54 +01:00
|
|
|
|
|
|
|
|
fx_result ropkg_package_open(
|
|
|
|
|
fx_directory *dir,
|
|
|
|
|
const char *path,
|
|
|
|
|
struct ropkg_package **out)
|
|
|
|
|
{
|
|
|
|
|
struct ropkg_package *pkg = malloc(sizeof *pkg);
|
|
|
|
|
if (!pkg) {
|
|
|
|
|
return ROPKG_RESULT_ERR(NO_MEMORY);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
memset(pkg, 0x0, sizeof *pkg);
|
|
|
|
|
|
|
|
|
|
fx_result result = fx_file_open(
|
|
|
|
|
dir,
|
|
|
|
|
FX_RV_PATH(path),
|
|
|
|
|
FX_FILE_READ_ONLY | FX_FILE_BINARY,
|
|
|
|
|
&pkg->pkg_file);
|
|
|
|
|
if (fx_result_is_error(result)) {
|
|
|
|
|
ropkg_package_close(pkg);
|
|
|
|
|
return fx_result_propagate(result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fx_status bstatus = fx_cstream_open(
|
|
|
|
|
pkg->pkg_file,
|
|
|
|
|
FX_TYPE_ZSTD_COMPRESSOR,
|
|
|
|
|
FX_COMPRESSOR_MODE_DECOMPRESS,
|
|
|
|
|
&pkg->pkg_file_cstream);
|
|
|
|
|
if (!FX_OK(bstatus)) {
|
|
|
|
|
ropkg_package_close(pkg);
|
|
|
|
|
return FX_RESULT_STATUS(bstatus);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
enum ropkg_status status
|
|
|
|
|
= ropkg_reader_open(pkg->pkg_file_cstream, &pkg->pkg_reader);
|
|
|
|
|
if (status != ROPKG_SUCCESS) {
|
|
|
|
|
ropkg_package_close(pkg);
|
|
|
|
|
return ROPKG_RESULT_STATUS(status);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
result = fx_directory_open_temp(&pkg->pkg_metadata);
|
|
|
|
|
if (fx_result_is_error(result)) {
|
|
|
|
|
ropkg_package_close(pkg);
|
|
|
|
|
return fx_result_propagate(result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
result = extract_metadata(pkg->pkg_reader, pkg->pkg_metadata);
|
|
|
|
|
if (fx_result_is_error(result)) {
|
|
|
|
|
ropkg_package_close(pkg);
|
|
|
|
|
return fx_result_propagate(result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
*out = pkg;
|
|
|
|
|
return ROPKG_RESULT_SUCCESS;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fx_result ropkg_package_close(struct ropkg_package *pkg)
|
|
|
|
|
{
|
|
|
|
|
if (pkg->pkg_reader) {
|
|
|
|
|
ropkg_reader_close(pkg->pkg_reader);
|
|
|
|
|
pkg->pkg_reader = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (pkg->pkg_file_cstream) {
|
|
|
|
|
fx_cstream_unref(pkg->pkg_file_cstream);
|
|
|
|
|
pkg->pkg_file_cstream = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (pkg->pkg_file) {
|
|
|
|
|
fx_file_unref(pkg->pkg_file);
|
|
|
|
|
pkg->pkg_file = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (pkg->pkg_metadata) {
|
|
|
|
|
fx_directory_unref(pkg->pkg_metadata);
|
|
|
|
|
pkg->pkg_metadata = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
free(pkg);
|
|
|
|
|
return FX_RESULT_SUCCESS;
|
|
|
|
|
}
|