|
| 1 | +#include "Formats.hh" |
| 2 | + |
| 3 | +#include <stdint.h> |
| 4 | + |
| 5 | +#include <filesystem> |
| 6 | +#include <phosg/Encoding.hh> |
| 7 | +#include <phosg/Strings.hh> |
| 8 | +#include <string> |
| 9 | + |
| 10 | +#include "../ResourceFile.hh" |
| 11 | +#include "../TextCodecs.hh" |
| 12 | + |
| 13 | +using namespace std; |
| 14 | +using namespace phosg; |
| 15 | + |
| 16 | +namespace ResourceDASM { |
| 17 | + |
| 18 | +ResourceFile load_resource_file_from_directory(const string& dir_path) { |
| 19 | + ResourceFile ret; |
| 20 | + for (const auto& type_item : std::filesystem::directory_iterator(dir_path)) { |
| 21 | + if (!type_item.is_directory()) { |
| 22 | + continue; |
| 23 | + } |
| 24 | + |
| 25 | + string type_item_name = type_item.path().filename(); |
| 26 | + uint32_t type = resource_type_for_raw_string(unescape_hex_bytes_for_filename(type_item_name)); |
| 27 | + |
| 28 | + for (const auto& res_item : std::filesystem::directory_iterator(std::filesystem::path(dir_path) / type_item_name)) { |
| 29 | + if (!res_item.is_regular_file()) { |
| 30 | + continue; |
| 31 | + } |
| 32 | + |
| 33 | + string res_item_name = res_item.path().filename(); |
| 34 | + if (!res_item_name.ends_with(".bin")) { |
| 35 | + continue; |
| 36 | + } |
| 37 | + |
| 38 | + // Filename is eiher like 20.bin (ID only) or 20_Resource_name.bin (ID + |
| 39 | + // name; name has _XX => escaped byte). Trim off the .bin first |
| 40 | + res_item_name.resize(res_item_name.size() - 4); |
| 41 | + |
| 42 | + size_t offset = 0; |
| 43 | + int32_t res_id = stol(res_item_name, &offset, 10); |
| 44 | + if (res_id < -0x8000 || res_id > 0x7FFF) { |
| 45 | + throw std::runtime_error(std::format("Invalid resource ID: {}/{}.bin", type_item_name, res_item_name)); |
| 46 | + } |
| 47 | + if (offset > res_item_name.size()) { |
| 48 | + throw std::runtime_error(std::format("Invalid resource filename (parse error): {}/{}.bin", type_item_name, res_item_name)); |
| 49 | + } |
| 50 | + |
| 51 | + string res_name; |
| 52 | + if (offset < res_item_name.size()) { |
| 53 | + // Has resource name |
| 54 | + if (res_item_name[offset] != '_') { |
| 55 | + throw std::runtime_error(std::format("Invalid resource filename (missing separator): {}/{}.bin", type_item_name, res_item_name)); |
| 56 | + } |
| 57 | + res_name = unescape_hex_bytes_for_filename(res_item_name.substr(offset + 1)); |
| 58 | + } |
| 59 | + |
| 60 | + auto res = make_shared<ResourceFile::Resource>(); |
| 61 | + res->type = type; |
| 62 | + res->id = res_id; |
| 63 | + res->flags = 0; |
| 64 | + res->name = res_name; |
| 65 | + res->data = phosg::load_file(res_item.path().string()); |
| 66 | + ret.add(res); |
| 67 | + } |
| 68 | + } |
| 69 | + |
| 70 | + return ret; |
| 71 | +} |
| 72 | + |
| 73 | +void save_resource_file_to_directory(const ResourceFile& rf, const std::string& dir_path) { |
| 74 | + std::filesystem::path base_path = dir_path; |
| 75 | + // TODO: This is kinda dumb. It'd be nice if we could use a generator to just |
| 76 | + // iterate the shared_ptr<const Resource> objects directly |
| 77 | + for (auto [res_type, res_id] : rf.all_resources()) { |
| 78 | + auto res = rf.get_resource(res_type, res_id); |
| 79 | + string type_item_name = escape_hex_bytes_for_filename(raw_string_for_resource_type(res_type)); |
| 80 | + std::filesystem::create_directories(base_path / type_item_name); |
| 81 | + string res_item_name = res->name.empty() |
| 82 | + ? std::format("{}.bin", res->id) |
| 83 | + : std::format("{}_{}.bin", res->id, escape_hex_bytes_for_filename(res->name)); |
| 84 | + phosg::save_file((base_path / type_item_name / res_item_name).string(), res->data); |
| 85 | + } |
| 86 | +} |
| 87 | + |
| 88 | +} // namespace ResourceDASM |
0 commit comments