diff --git a/CMakeLists.txt b/CMakeLists.txt
index 943c637b..0295c325 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -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
diff --git a/src/common/streamwriter.cpp b/src/common/streamwriter.cpp
index e1958e52..0c40626a 100644
--- a/src/common/streamwriter.cpp
+++ b/src/common/streamwriter.cpp
@@ -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();
}
diff --git a/src/common/streamwriter.h b/src/common/streamwriter.h
index 5196cd1f..31d9e521 100644
--- a/src/common/streamwriter.h
+++ b/src/common/streamwriter.h
@@ -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;
diff --git a/src/resource/format/rimwriter.cpp b/src/resource/format/rimwriter.cpp
new file mode 100644
index 00000000..954eef09
--- /dev/null
+++ b/src/resource/format/rimwriter.cpp
@@ -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 .
+ */
+
+#include "rimwriter.h"
+
+#include
+
+#include
+
+#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(path, ios::binary);
+ StreamWriter writer(rim);
+ uint32_t numResources = static_cast(_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(res.data.size());
+
+ string resRef(res.resRef);
+ resRef.resize(16);
+ writer.putString(resRef);
+
+ writer.putUint32(static_cast(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
diff --git a/src/resource/format/rimwriter.h b/src/resource/format/rimwriter.h
new file mode 100644
index 00000000..f115cebc
--- /dev/null
+++ b/src/resource/format/rimwriter.h
@@ -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 .
+ */
+
+#pragma once
+
+#include
+#include
+
+#include
+
+#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 _resources;
+};
+
+} // namespace resource
+
+} // namespace reone
diff --git a/src/resource/typeutil.cpp b/src/resource/typeutil.cpp
index 7322ebf9..d56c6183 100644
--- a/src/resource/typeutil.cpp
+++ b/src/resource/typeutil.cpp
@@ -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;
}
diff --git a/src/resource/typeutil.h b/src/resource/typeutil.h
index 9491c3ea..3f796672 100644
--- a/src/resource/typeutil.h
+++ b/src/resource/typeutil.h
@@ -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
diff --git a/tools/program.cpp b/tools/program.cpp
index af53e21b..c7f86282 100644
--- a/tools/program.cpp
+++ b/tools/program.cpp
@@ -49,7 +49,8 @@ static const unordered_map 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(), "target name or path to input file");
}
diff --git a/tools/rimtool.cpp b/tools/rimtool.cpp
index 964c6af5..4d5009a1 100644
--- a/tools/rimtool.cpp
+++ b/tools/rimtool.cpp
@@ -19,6 +19,7 @@
#include
+#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
diff --git a/tools/tools.h b/tools/tools.h
index 818e6b64..9cffc89c 100644
--- a/tools/tools.h
+++ b/tools/tools.h
@@ -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 {
diff --git a/tools/types.h b/tools/types.h
index 314e0bd4..18acfbe1 100644
--- a/tools/types.h
+++ b/tools/types.h
@@ -28,7 +28,8 @@ enum class Operation {
ToJSON,
ToTGA,
To2DA,
- ToGFF
+ ToGFF,
+ ToRIM
};
} // namespace tools