feat(tools): Add tool to create RIM archives from folders

This commit is contained in:
Vsevolod Kremianskii 2021-03-11 16:12:28 +07:00
parent 9ac90910ac
commit be572da467
11 changed files with 208 additions and 16 deletions

View file

@ -108,6 +108,7 @@ set(RESOURCE_HEADERS
src/resource/format/lytreader.h
src/resource/format/pereader.h
src/resource/format/rimreader.h
src/resource/format/rimwriter.h
src/resource/format/ssfreader.h
src/resource/format/tlkreader.h
src/resource/format/visreader.h
@ -135,6 +136,7 @@ set(RESOURCE_SOURCES
src/resource/format/lytreader.cpp
src/resource/format/pereader.cpp
src/resource/format/rimreader.cpp
src/resource/format/rimwriter.cpp
src/resource/format/ssfreader.cpp
src/resource/format/tlkreader.cpp
src/resource/format/visreader.cpp

View file

@ -68,6 +68,11 @@ void StreamWriter::putBytes(const ByteArray &bytes) {
_stream->write(&bytes[0], bytes.size());
}
void StreamWriter::putBytes(int count, uint8_t val) {
ByteArray data(count, val);
_stream->write(&data[0], count);
}
size_t StreamWriter::tell() const {
return _stream->tellp();
}

View file

@ -42,6 +42,7 @@ public:
void putString(const std::string &str);
void putCString(const std::string &str);
void putBytes(const ByteArray &bytes);
void putBytes(int count, uint8_t val = 0);
size_t tell() const;

View file

@ -0,0 +1,76 @@
/*
* Copyright (c) 2020-2021 The reone project contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "rimwriter.h"
#include <memory>
#include <boost/filesystem.hpp>
#include "../../common/streamwriter.h"
using namespace std;
namespace fs = boost::filesystem;
namespace reone {
namespace resource {
void RimWriter::add(Resource &&res) {
_resources.push_back(res);
}
void RimWriter::save(const fs::path &path) {
auto rim = make_shared<fs::ofstream>(path, ios::binary);
StreamWriter writer(rim);
uint32_t numResources = static_cast<uint32_t>(_resources.size());
writer.putString("RIM V1.0");
writer.putUint32(0); // reserved
writer.putUint32(numResources);
writer.putUint32(0x78); // offset to resource headers
writer.putBytes(100); // reserved
uint32_t id = 0;
uint32_t offset = 0x78 + numResources * 32;
// Write resource headers
for (auto &res : _resources) {
auto size = static_cast<uint32_t>(res.data.size());
string resRef(res.resRef);
resRef.resize(16);
writer.putString(resRef);
writer.putUint32(static_cast<uint32_t>(res.resType));
writer.putUint32(id++);
writer.putUint32(offset);
writer.putUint32(size);
offset += size;
}
// Write resources data
for (auto &res : _resources) {
writer.putBytes(res.data);
}
}
} // namespace resource
} // namespace reone

View file

@ -0,0 +1,50 @@
/*
* Copyright (c) 2020-2021 The reone project contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include <string>
#include <vector>
#include <boost/filesystem/path.hpp>
#include "../../common/types.h"
#include "../types.h"
namespace reone {
namespace resource {
class RimWriter {
public:
struct Resource {
std::string resRef;
ResourceType resType { ResourceType::Invalid };
ByteArray data;
};
void add(Resource &&res);
void save(const boost::filesystem::path &path);
private:
std::vector<Resource> _resources;
};
} // namespace resource
} // namespace reone

View file

@ -97,7 +97,7 @@ const string &getExtByResType(ResourceType type) {
return g_extByType[type];
}
ResourceType getResTypeByExt(const string &ext) {
ResourceType getResTypeByExt(const string &ext, bool logNotFound) {
if (!g_typeByExtInited) {
for (auto &entry : g_extByType) {
g_typeByExt.insert(make_pair(entry.second, entry.first));
@ -106,7 +106,9 @@ ResourceType getResTypeByExt(const string &ext) {
}
auto it = g_typeByExt.find(ext);
if (it == g_typeByExt.end()) {
warn("Resource type not found by extension: " + ext);
if (logNotFound) {
warn("Resource type not found by extension: " + ext);
}
return ResourceType::Invalid;
}

View file

@ -26,7 +26,7 @@ namespace reone {
namespace resource {
const std::string &getExtByResType(ResourceType type);
ResourceType getResTypeByExt(const std::string &ext);
ResourceType getResTypeByExt(const std::string &ext, bool logNotFound = true);
} // namespace resource

View file

@ -49,7 +49,8 @@ static const unordered_map<string, Operation> g_operations {
{ "to-json", Operation::ToJSON },
{ "to-tga", Operation::ToTGA },
{ "to-2da", Operation::To2DA },
{ "to-gff", Operation::ToGFF }
{ "to-gff", Operation::ToGFF },
{ "to-rim", Operation::ToRIM },
};
Program::Program(int argc, char **argv) : _argc(argc), _argv(argv) {
@ -93,6 +94,7 @@ void Program::initOptions() {
("to-tga", "convert TPC image to TGA")
("to-2da", "convert JSON to 2DA")
("to-gff", "convert JSON to GFF")
("to-rim", "create RIM archive from directory")
("target", po::value<string>(), "target name or path to input file");
}

View file

@ -19,6 +19,7 @@
#include <iostream>
#include "../src/resource/format/rimwriter.h"
#include "../src/resource/typeutil.h"
using namespace std;
@ -32,13 +33,23 @@ namespace reone {
namespace tools {
void RimTool::invoke(Operation operation, const fs::path &target, const fs::path &gamePath, const fs::path &destPath) {
RimReader rim;
rim.load(target);
if (operation == Operation::List) {
list(rim);
} else if (operation == Operation::Extract) {
extract(rim, destPath);
switch (operation) {
case Operation::List:
case Operation::Extract: {
RimReader rim;
rim.load(target);
if (operation == Operation::List) {
list(rim);
} else if (operation == Operation::Extract) {
extract(rim, destPath);
}
break;
}
case Operation::ToRIM:
toRIM(target);
break;
default:
break;
}
}
@ -65,11 +76,52 @@ void RimTool::extract(RimReader &rim, const fs::path &destPath) {
}
}
void RimTool::toRIM(const fs::path &target) {
RimWriter rim;
for (auto &entry : fs::directory_iterator(target)) {
fs::path path(entry);
if (fs::is_directory(path)) continue;
string ext(path.extension().string());
ext.erase(0, 1);
ResourceType resType = getResTypeByExt(ext, false);
if (resType == ResourceType::Invalid) continue;
fs::ifstream in(path, ios::binary);
in.seekg(0, ios::end);
size_t size = in.tellg();
ByteArray data(size);
in.seekg(0);
in.read(&data[0], size);
fs::path resRef(path.filename());
resRef.replace_extension("");
RimWriter::Resource res;
res.resRef = resRef.string();
res.resType = resType;
res.data = move(data);
rim.add(move(res));
}
fs::path rimPath(target.parent_path());
rimPath.append(target.filename().string() + ".rim");
rim.save(rimPath);
}
bool RimTool::supports(Operation operation, const fs::path &target) const {
return
!fs::is_directory(target) &&
target.extension() == ".rim" &&
(operation == Operation::List || operation == Operation::Extract);
switch (operation) {
case Operation::List:
case Operation::Extract:
return !fs::is_directory(target) && target.extension() == ".rim";
case Operation::ToRIM:
return fs::is_directory(target);
default:
return false;
}
}
} // namespace tools

View file

@ -86,6 +86,7 @@ public:
private:
void list(const resource::RimReader &rim);
void extract(resource::RimReader &rim, const boost::filesystem::path &destPath);
void toRIM(const boost::filesystem::path &target);
};
class TwoDaTool : public ITool {

View file

@ -28,7 +28,8 @@ enum class Operation {
ToJSON,
ToTGA,
To2DA,
ToGFF
ToGFF,
ToRIM
};
} // namespace tools