mirror of
https://github.com/imaginaryPineapple/OpenRayman.git
synced 2024-10-19 22:47:37 -04:00
Big changes
* OpenRayman can now read and extract from CNT archives (thanks Szymski/Szymekk!). Check --help for more information. In the future this, along with other functions, will be exported in a library called "libopenrayman". The plan is for libopenrayman to be able to read, inspect and extract all relevant files from the Rayman 2 engine. libopenrayman should be licensed as LGPL, compared to GPL for OpenRayman. * OpenRayman can now, naturally, also read and convert GF files (CNT files are archives full of textures). * OpenRayman no longer includes a DSB interpreter, and the decompiler has significantly changed. Only parts of the DSB that we need are now decompiled. Instead of direct decompilation into a .odsb, the decompiler produces several .json files that define the same information as the DSB in a much more human/mod friendly fashion. * Some minor GTK and engine related changes.
This commit is contained in:
parent
dfdb375801
commit
e71a336693
|
@ -30,5 +30,4 @@ endif()
|
||||||
set(BuildTests OFF)
|
set(BuildTests OFF)
|
||||||
add_subdirectory(lib/gl3w)
|
add_subdirectory(lib/gl3w)
|
||||||
add_subdirectory(lib/json)
|
add_subdirectory(lib/json)
|
||||||
add_subdirectory(lib/glm)
|
|
||||||
add_subdirectory(src)
|
add_subdirectory(src)
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
{
|
{
|
||||||
"dependencies": ["rayman2"],
|
"dependencies": [
|
||||||
|
"rayman2"
|
||||||
|
],
|
||||||
"info": {
|
"info": {
|
||||||
"description": "Modifications for OpenRayman",
|
"description": "Modifications for OpenRayman",
|
||||||
"name": "Rayman 2: The Great Escape (OpenRayman)"
|
"name": "Rayman 2: The Great Escape (OpenRayman)"
|
||||||
|
|
|
@ -11,7 +11,6 @@ add_executable(openrayman WIN32 ${SOURCES} "${CMAKE_SOURCE_DIR}/lib/lodepng/lode
|
||||||
set(EXECUTABLE_OUTPUT_PATH "${CMAKE_SOURCE_DIR}/bin")
|
set(EXECUTABLE_OUTPUT_PATH "${CMAKE_SOURCE_DIR}/bin")
|
||||||
target_link_libraries(openrayman gl3w)
|
target_link_libraries(openrayman gl3w)
|
||||||
target_link_libraries(openrayman nlohmann_json)
|
target_link_libraries(openrayman nlohmann_json)
|
||||||
target_link_libraries(openrayman glm)
|
|
||||||
|
|
||||||
if(WIN32)
|
if(WIN32)
|
||||||
target_link_libraries(openrayman shlwapi)
|
target_link_libraries(openrayman shlwapi)
|
||||||
|
|
164
src/data_extractor/cnt/cnt_archive.cc
Normal file
164
src/data_extractor/cnt/cnt_archive.cc
Normal file
|
@ -0,0 +1,164 @@
|
||||||
|
#include <data_extractor/cnt/cnt_archive.h>
|
||||||
|
|
||||||
|
namespace openrayman
|
||||||
|
{
|
||||||
|
std::string cnt_file::get_full_path() const
|
||||||
|
{
|
||||||
|
return parent.get_full_path() + "/" + name;
|
||||||
|
}
|
||||||
|
|
||||||
|
cnt_file* cnt_directory_node::find_file(const std::string& name)
|
||||||
|
{
|
||||||
|
cnt_directory_node& archive_node = owner.get_archive_node();
|
||||||
|
if(name.find(archive_node.name) == 0)
|
||||||
|
return archive_node.find_file(std::string(name).erase(0, archive_node.name.size()));
|
||||||
|
|
||||||
|
std::string in_this = std::string(name).substr(0, name.find("/"));
|
||||||
|
for(cnt_file& file : m_files)
|
||||||
|
{
|
||||||
|
if(file.name == in_this)
|
||||||
|
return &file;
|
||||||
|
}
|
||||||
|
for(cnt_directory_node& child : m_children)
|
||||||
|
{
|
||||||
|
if(child.name == in_this)
|
||||||
|
return child.find_file(std::string(name).erase(0, std::min(name.size(), in_this.size() + 1)));
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
cnt_directory_node* cnt_directory_node::find_child(const std::string& name)
|
||||||
|
{
|
||||||
|
if(name == "")
|
||||||
|
return this;
|
||||||
|
|
||||||
|
cnt_directory_node& archive_node = owner.get_archive_node();
|
||||||
|
if(name.find(archive_node.name) == 0)
|
||||||
|
return archive_node.find_child(std::string(name).erase(0, archive_node.name.size()));
|
||||||
|
|
||||||
|
std::string in_this = std::string(name).substr(0, name.find("/"));
|
||||||
|
for(cnt_directory_node& child : m_children)
|
||||||
|
{
|
||||||
|
if(child.name == in_this)
|
||||||
|
return child.find_child(std::string(name).erase(0, std::min(name.size(), in_this.size() + 1)));
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::uint8_t> cnt_file::read(std::int32_t start, std::int32_t count) const
|
||||||
|
{
|
||||||
|
std::vector<std::uint8_t> read(0, 0);
|
||||||
|
read.reserve(count);
|
||||||
|
std::size_t start_in_archive = ptr_in_archive + start;
|
||||||
|
std::size_t end = ptr_in_archive + start + size;
|
||||||
|
std::size_t pos = start_in_archive;
|
||||||
|
|
||||||
|
parent.owner.get_raw_stream().seekg(pos);
|
||||||
|
std::uint8_t c;
|
||||||
|
while(pos < end && pos < ptr_in_archive + start + count)
|
||||||
|
{
|
||||||
|
if(!parent.owner.get_raw_stream().read((char*)&c, 1))
|
||||||
|
break;
|
||||||
|
c ^= xor_key[(pos - start_in_archive) % 4];
|
||||||
|
read.push_back(c);
|
||||||
|
pos++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return read;
|
||||||
|
}
|
||||||
|
|
||||||
|
cnt_archive::cnt_archive(const std::string& path) :
|
||||||
|
cnt_archive(*(new std::ifstream(path, std::ifstream::in | std::ifstream::binary)))
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
cnt_archive::cnt_archive(std::ifstream& stream) :
|
||||||
|
m_archive_node(*(new cnt_directory_node(*this, nullptr, "/"))), m_stream(stream), m_valid(false)
|
||||||
|
{
|
||||||
|
if(!stream.is_open())
|
||||||
|
return;
|
||||||
|
|
||||||
|
std::int32_t num_directories, num_files;
|
||||||
|
m_stream.read((char*)&num_directories, sizeof(std::int32_t));
|
||||||
|
m_stream.read((char*)&num_files, sizeof(std::int32_t));
|
||||||
|
if(num_directories <= 0 && num_files <= 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
char signature[2];
|
||||||
|
m_stream.read(signature, 2);
|
||||||
|
if(signature[0] != 0x1 || signature[1] != 0x1)
|
||||||
|
return;
|
||||||
|
|
||||||
|
char xor_key;
|
||||||
|
m_stream.read(&xor_key, 1);
|
||||||
|
|
||||||
|
for(std::int32_t dir = 0; dir < num_directories; dir++)
|
||||||
|
{
|
||||||
|
std::int32_t dir_name_len;
|
||||||
|
m_stream.read((char*)&dir_name_len, sizeof(std::int32_t));
|
||||||
|
std::string dir_name = "";
|
||||||
|
for(std::int32_t n = 0; n < dir_name_len; n++)
|
||||||
|
{
|
||||||
|
char c;
|
||||||
|
m_stream.read(&c, 1);
|
||||||
|
dir_name += c ^ xor_key;
|
||||||
|
}
|
||||||
|
std::vector<std::string> components;
|
||||||
|
std::size_t last_begun = 0;
|
||||||
|
for(std::size_t n = 0; n < dir_name.size(); n++)
|
||||||
|
{
|
||||||
|
if(dir_name[n] == '\\' || dir_name[n] == '/')
|
||||||
|
{
|
||||||
|
if(n - last_begun > 0)
|
||||||
|
components.push_back(dir_name.substr(last_begun, n - last_begun));
|
||||||
|
n++;
|
||||||
|
last_begun = n;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if((dir_name.size() - 1 - last_begun) > 0)
|
||||||
|
components.push_back(dir_name.substr(last_begun, dir_name.size() - 1 - last_begun));
|
||||||
|
cnt_directory_node* parent = &m_archive_node;
|
||||||
|
for(std::size_t n = 0; n < components.size() - 1; n++)
|
||||||
|
{
|
||||||
|
if(!parent->has_child(components[n]))
|
||||||
|
parent->push_child(components[n]);
|
||||||
|
parent = parent->find_child(components[n]);
|
||||||
|
}
|
||||||
|
parent->push_child(components[components.size() - 1], dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<cnt_directory_node*> all_directories = m_archive_node.get_children();
|
||||||
|
char tmp;
|
||||||
|
m_stream.read(&tmp, 1);
|
||||||
|
|
||||||
|
for(std::int32_t file = 0; file < num_files; file++)
|
||||||
|
{
|
||||||
|
std::int32_t dir_index, file_name_len;
|
||||||
|
m_stream.read((char*)&dir_index, sizeof(std::int32_t));
|
||||||
|
m_stream.read((char*)&file_name_len, sizeof(std::int32_t));
|
||||||
|
cnt_directory_node* parent = &m_archive_node;
|
||||||
|
for(cnt_directory_node* node : all_directories)
|
||||||
|
{
|
||||||
|
if(node->cnt_index == dir_index)
|
||||||
|
parent = node;
|
||||||
|
}
|
||||||
|
std::string file_name = "";
|
||||||
|
for(std::int32_t n = 0; n < file_name_len; n++)
|
||||||
|
{
|
||||||
|
char c;
|
||||||
|
m_stream.read(&c, 1);
|
||||||
|
file_name += c ^ xor_key;
|
||||||
|
}
|
||||||
|
char f_xor_key[4];
|
||||||
|
m_stream.read(f_xor_key, 4);
|
||||||
|
std::int32_t tmp, ptr_in_archive, file_size;
|
||||||
|
m_stream.read((char*)&tmp, sizeof(std::int32_t));
|
||||||
|
m_stream.read((char*)&ptr_in_archive, sizeof(std::int32_t));
|
||||||
|
m_stream.read((char*)&file_size, sizeof(std::int32_t));
|
||||||
|
parent->push_file(*parent, file_name, ptr_in_archive, file_size, f_xor_key);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_valid = true;
|
||||||
|
}
|
||||||
|
}
|
187
src/data_extractor/cnt/cnt_archive.h
Normal file
187
src/data_extractor/cnt/cnt_archive.h
Normal file
|
@ -0,0 +1,187 @@
|
||||||
|
#ifndef CNT_ARCHIVE_H
|
||||||
|
#define CNT_ARCHIVE_H
|
||||||
|
|
||||||
|
#include <data_extractor/data_decoder.h>
|
||||||
|
#include <string>
|
||||||
|
#include <iostream>
|
||||||
|
#include <fstream>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace openrayman
|
||||||
|
{
|
||||||
|
// Forward declare
|
||||||
|
class cnt_archive;
|
||||||
|
class cnt_file;
|
||||||
|
class cnt_directory_node;
|
||||||
|
|
||||||
|
// CNT archives are used for storing textures.
|
||||||
|
// This is all heavily adapted from Rayman2Lib.
|
||||||
|
class cnt_archive
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
cnt_archive(const std::string& path);
|
||||||
|
cnt_archive(std::ifstream& stream);
|
||||||
|
|
||||||
|
~cnt_archive()
|
||||||
|
{
|
||||||
|
if(m_stream.is_open())
|
||||||
|
m_stream.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns true if the archive was loaded successfully and was valid.
|
||||||
|
bool valid() const
|
||||||
|
{
|
||||||
|
return m_valid;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns a reference to the top most directory node, known as "/".
|
||||||
|
inline cnt_directory_node& get_archive_node()
|
||||||
|
{
|
||||||
|
return m_archive_node;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns a reference to underlying stream used by the CNT archive.
|
||||||
|
inline std::ifstream& get_raw_stream()
|
||||||
|
{
|
||||||
|
return m_stream;
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
cnt_directory_node& m_archive_node;
|
||||||
|
std::ifstream& m_stream;
|
||||||
|
bool m_valid;
|
||||||
|
};
|
||||||
|
|
||||||
|
class cnt_file
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
cnt_file(cnt_directory_node& parent, const std::string& name, std::int32_t ptr_in_archive, std::int32_t size, char* xor_key) :
|
||||||
|
parent(parent), name(name), ptr_in_archive(ptr_in_archive), size(size)
|
||||||
|
{
|
||||||
|
for(std::size_t n = 0; n < 4; n++)
|
||||||
|
this->xor_key[n] = xor_key[n];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the full path specifier of this file.
|
||||||
|
// Cant inline because incomplete.
|
||||||
|
std::string get_full_path() const;
|
||||||
|
|
||||||
|
// Reads a max of "count" bytes from this file, starting at "start".
|
||||||
|
std::vector<std::uint8_t> read(std::int32_t start, std::int32_t count) const;
|
||||||
|
|
||||||
|
cnt_directory_node& parent;
|
||||||
|
std::string name;
|
||||||
|
std::int32_t size;
|
||||||
|
private:
|
||||||
|
std::int32_t ptr_in_archive;
|
||||||
|
char xor_key[4];
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class cnt_directory_node
|
||||||
|
{
|
||||||
|
friend class cnt_archive;
|
||||||
|
public:
|
||||||
|
cnt_directory_node(cnt_archive& owner, cnt_directory_node* parent, const std::string& name, std::int32_t cnt_index = -1) :
|
||||||
|
owner(owner), parent(parent), name(name), cnt_index(cnt_index)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pushes a new directory as a child of this node.
|
||||||
|
inline void push_child(const std::string& child)
|
||||||
|
{
|
||||||
|
m_children.push_back(cnt_directory_node(owner, this, child));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pushes a new directory as a child of this node.
|
||||||
|
inline void push_child(const std::string& child, std::int32_t cnt_index)
|
||||||
|
{
|
||||||
|
m_children.push_back(cnt_directory_node(owner, this, child, cnt_index));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pushes a new file as a child of this node.
|
||||||
|
inline void push_file(cnt_directory_node& parent, const std::string& name, std::int32_t ptr_in_archive, std::int32_t size, char* xor_key)
|
||||||
|
{
|
||||||
|
m_files.push_back(cnt_file(parent, name, ptr_in_archive, size, xor_key));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns all children of this directory node, recursively.
|
||||||
|
inline std::vector<cnt_directory_node*> get_children()
|
||||||
|
{
|
||||||
|
std::vector<cnt_directory_node*> values;
|
||||||
|
for(cnt_directory_node& node : m_children)
|
||||||
|
{
|
||||||
|
values.push_back(&node);
|
||||||
|
std::vector<cnt_directory_node*> node_children = node.get_children();
|
||||||
|
for(std::size_t n = 0; n < node_children.size(); n++)
|
||||||
|
values.push_back(node_children[n]);
|
||||||
|
}
|
||||||
|
return values;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns a reference to all files that are children of this node.
|
||||||
|
inline std::vector<cnt_file*> get_files()
|
||||||
|
{
|
||||||
|
std::vector<cnt_file*> values;
|
||||||
|
for(cnt_file& file : m_files)
|
||||||
|
values.push_back(&file);
|
||||||
|
for(cnt_directory_node* child : get_children())
|
||||||
|
{
|
||||||
|
for(cnt_file& file : child->m_files)
|
||||||
|
values.push_back(&file);
|
||||||
|
}
|
||||||
|
return values;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns a reference to all children that are explicit children of this node.
|
||||||
|
inline std::vector<cnt_directory_node>& get_local_children()
|
||||||
|
{
|
||||||
|
return m_children;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns a reference to all files that are explicit children of this node.
|
||||||
|
inline std::vector<cnt_file>& get_local_files()
|
||||||
|
{
|
||||||
|
return m_files;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the full path specifier of this directory.
|
||||||
|
inline std::string get_full_path() const
|
||||||
|
{
|
||||||
|
if(parent == nullptr)
|
||||||
|
return name;
|
||||||
|
if(parent == &owner.get_archive_node())
|
||||||
|
return parent->get_full_path() + name;
|
||||||
|
return parent->get_full_path() + "/" + name;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns true if the file exists in this directory.
|
||||||
|
inline bool has_file(const std::string& name)
|
||||||
|
{
|
||||||
|
return find_file(name) != nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns true if the subdirectory exists in this directory.
|
||||||
|
inline bool has_child(const std::string& name)
|
||||||
|
{
|
||||||
|
return find_child(name) != nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns a reference to the file if it was found, or nullptr if it was not found.
|
||||||
|
cnt_file* find_file(const std::string& name);
|
||||||
|
|
||||||
|
// Returns a reference to the child directory if it was found, or nullptr if it was not found.
|
||||||
|
cnt_directory_node* find_child(const std::string& name);
|
||||||
|
|
||||||
|
cnt_archive& owner;
|
||||||
|
cnt_directory_node* parent;
|
||||||
|
|
||||||
|
std::string name;
|
||||||
|
std::int32_t cnt_index;
|
||||||
|
private:
|
||||||
|
std::vector<cnt_directory_node> m_children;
|
||||||
|
std::vector<cnt_file> m_files;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -1,8 +1,7 @@
|
||||||
#include <data_extractor/data_extractor.h>
|
#include <data_extractor/data_extractor.h>
|
||||||
#include <data_extractor/dsb/dsb_decompiler.h>
|
#include <data_extractor/dsb/dsb_decompiler.h>
|
||||||
#include <dsb_interpreter/dsb_instruction.h>
|
#include <data_extractor/cnt/cnt_archive.h>
|
||||||
#include <dsb_interpreter/dsb_interpreter.h>
|
#include <data_extractor/gf/gf_converter.h>
|
||||||
#include <dsb_interpreter/instructions/all.h>
|
|
||||||
#include <platform/message_box.h>
|
#include <platform/message_box.h>
|
||||||
#include <platform/file.h>
|
#include <platform/file.h>
|
||||||
#include <json.hpp>
|
#include <json.hpp>
|
||||||
|
@ -13,11 +12,19 @@ namespace openrayman
|
||||||
{
|
{
|
||||||
bool data_extractor::extract(const std::string& install_folder)
|
bool data_extractor::extract(const std::string& install_folder)
|
||||||
{
|
{
|
||||||
return
|
if(check_prerequisites(install_folder) &&
|
||||||
check_prerequisites(install_folder) &&
|
|
||||||
create_base() &&
|
create_base() &&
|
||||||
decompile_game_dsb(install_folder) &&
|
decompile_game_dsb(install_folder) &&
|
||||||
make_game_resources(install_folder);
|
make_game_resources(install_folder))
|
||||||
|
{
|
||||||
|
std::cout << "[openrayman::data_extractor] All tasks succeeded" << std::endl;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
file::delete_directory(m_backend_specifics.get_data_path() + "/games/rayman2");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool data_extractor::check_prerequisites(const std::string& install_folder)
|
bool data_extractor::check_prerequisites(const std::string& install_folder)
|
||||||
|
@ -81,22 +88,75 @@ namespace openrayman
|
||||||
{
|
{
|
||||||
std::cout << "[openrayman::data_extractor] Decompiling Game.dsb" << std::endl;
|
std::cout << "[openrayman::data_extractor] Decompiling Game.dsb" << std::endl;
|
||||||
dsb_decompiler decompiler;
|
dsb_decompiler decompiler;
|
||||||
return decompiler.decompile_dsb(install_folder + "/Data/Game.dsb", m_backend_specifics.get_data_path() + "/games/rayman2/game.odsb", dsb_format::openrayman);
|
return decompiler.decompile_dsb(install_folder + "/Data/Game.dsb", m_backend_specifics.get_data_path() + "/games/rayman2", dsb_format::openrayman);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool data_extractor::extract_recursive(std::string base, cnt_directory_node& parent)
|
||||||
|
{
|
||||||
|
gf_converter converter;
|
||||||
|
for(cnt_file& file : parent.get_local_files())
|
||||||
|
{
|
||||||
|
std::cout << "Extracting " << file.name << " which is " << (file.size / 1024.0) << " KB... ";
|
||||||
|
std::ofstream stream(file::fix_string(base + "/" + file.name.substr(0, file.name.length() - 3) + ".png"), std::ofstream::out | std::ofstream::binary);
|
||||||
|
if(!stream.is_open())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
std::vector<std::uint8_t> array;
|
||||||
|
array.reserve(file.size);
|
||||||
|
std::size_t at = 0;
|
||||||
|
while(at < file.size)
|
||||||
|
{
|
||||||
|
std::vector<std::uint8_t> read = file.read(at, 1024 * 4096);
|
||||||
|
for(char c : read)
|
||||||
|
array.push_back(c);
|
||||||
|
at += read.size();
|
||||||
|
if(read.size() == 0)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
std::cout << "OK" << std::endl;
|
||||||
|
|
||||||
|
std::cout << "Converting " << file.name << " to png via gf_converter::convert_to_png... ";
|
||||||
|
std::vector<std::uint8_t> data = converter.convert_to_png(array);
|
||||||
|
stream.write((char*)data.data(), data.size());
|
||||||
|
std::cout << "OK" << std::endl;
|
||||||
|
}
|
||||||
|
for(cnt_directory_node& child : parent.get_local_children())
|
||||||
|
{
|
||||||
|
file::create_directory(base + "/" + child.name);
|
||||||
|
if(!extract_recursive(base + "/" + child.name, child))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool data_extractor::make_game_resources(const std::string& install_folder)
|
bool data_extractor::make_game_resources(const std::string& install_folder)
|
||||||
{
|
{
|
||||||
dsb_interpreter interpreter(m_backend_specifics.get_data_path() + "/games/rayman2/game.odsb");
|
message_box::display("[openrayman::data_extractor] Info", "OpenRayman will now extract and convert all texture files used within the game."
|
||||||
if(interpreter.success())
|
"\nThis can take a couple of minutes depending on your disk and CPU speed.", false);
|
||||||
|
file::create_directory(m_backend_specifics.get_data_path() + "/games/rayman2/textures");
|
||||||
|
file::create_directory(m_backend_specifics.get_data_path() + "/games/rayman2/textures/vignettes");
|
||||||
|
cnt_archive textures(install_folder + "/Data/Textures.cnt");
|
||||||
|
cnt_archive vignettes(install_folder + "/Data/Vignette.cnt");
|
||||||
|
if(!textures.valid())
|
||||||
{
|
{
|
||||||
return true;
|
message_box::display("[openrayman::data_extractor] Error!", "The texture archive could not be read.", true);
|
||||||
}
|
|
||||||
dsb_instruction_invalid_dsb* error_reason = interpreter.get_instruction<dsb_instruction_invalid_dsb>
|
|
||||||
(dsb_instruction_type::invalid_dsb);
|
|
||||||
message_box::display("[openrayman::data_extractor] Error!", "The DSB interpreter failed to interpret the file game.odsb.\n\n" +
|
|
||||||
std::to_string(error_reason->line) + " : " + std::to_string(error_reason->column) + "\n" +
|
|
||||||
error_reason->error + "\n" +
|
|
||||||
error_reason->trace, true);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if(!vignettes.valid())
|
||||||
|
{
|
||||||
|
message_box::display("[openrayman::data_extractor] Error!", "The texture archive containing vignettes could not be read.", true);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if(!extract_recursive(m_backend_specifics.get_data_path() + "/games/rayman2/textures", textures.get_archive_node()))
|
||||||
|
{
|
||||||
|
message_box::display("[openrayman::data_extractor] Error!", "A texture file could not be extracted or read.", true);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if(!extract_recursive(m_backend_specifics.get_data_path() + "/games/rayman2/textures/vignettes", vignettes.get_archive_node()))
|
||||||
|
{
|
||||||
|
message_box::display("[openrayman::data_extractor] Error!", "A texture file could not be extracted or read.", true);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,8 @@
|
||||||
|
|
||||||
namespace openrayman
|
namespace openrayman
|
||||||
{
|
{
|
||||||
|
class cnt_directory_node;
|
||||||
|
|
||||||
// Extracts data from a valid Rayman 2: The Great Escape installation.
|
// Extracts data from a valid Rayman 2: The Great Escape installation.
|
||||||
class data_extractor
|
class data_extractor
|
||||||
{
|
{
|
||||||
|
@ -22,6 +24,7 @@ private:
|
||||||
bool check_prerequisites(const std::string& install_folder);
|
bool check_prerequisites(const std::string& install_folder);
|
||||||
bool create_base();
|
bool create_base();
|
||||||
bool decompile_game_dsb(const std::string& install_folder);
|
bool decompile_game_dsb(const std::string& install_folder);
|
||||||
|
bool extract_recursive(std::string base, cnt_directory_node& parent);
|
||||||
bool make_game_resources(const std::string& install_folder);
|
bool make_game_resources(const std::string& install_folder);
|
||||||
|
|
||||||
const backend_specifics& m_backend_specifics;
|
const backend_specifics& m_backend_specifics;
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
|
#include <json.hpp>
|
||||||
|
|
||||||
namespace openrayman
|
namespace openrayman
|
||||||
{
|
{
|
||||||
|
@ -31,15 +32,16 @@ namespace openrayman
|
||||||
decoder.set_virtual_position(0);
|
decoder.set_virtual_position(0);
|
||||||
decoder.decode_array(buffer, length - 4);
|
decoder.decode_array(buffer, length - 4);
|
||||||
|
|
||||||
|
if(fmt == dsb_format::rayman2_decoded)
|
||||||
|
{
|
||||||
std::ofstream target_stream(target, std::ofstream::out | std::ofstream::binary);
|
std::ofstream target_stream(target, std::ofstream::out | std::ofstream::binary);
|
||||||
if(target_stream.is_open())
|
if(target_stream.is_open())
|
||||||
{
|
|
||||||
if(fmt == dsb_format::openrayman)
|
|
||||||
decompile_sections(buffer, length - 4, target_stream);
|
|
||||||
else
|
|
||||||
target_stream.write(buffer, length - 4);
|
target_stream.write(buffer, length - 4);
|
||||||
return true;
|
return target_stream.is_open();
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
decompile_sections(buffer, length - 4, target);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -52,10 +54,8 @@ namespace openrayman
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
void dsb_decompiler::decompile_sections(char* source, std::size_t source_length, std::ofstream& target)
|
void dsb_decompiler::decompile_sections(char* source, std::size_t source_length, const std::string& target)
|
||||||
{
|
{
|
||||||
target << "# Generated by OpenRayman " << openrayman::version << "\n";
|
|
||||||
target << "\n";
|
|
||||||
memorybuf streambuf(source, source + source_length);
|
memorybuf streambuf(source, source + source_length);
|
||||||
std::istream in(&streambuf);
|
std::istream in(&streambuf);
|
||||||
std::int32_t id;
|
std::int32_t id;
|
||||||
|
@ -64,15 +64,13 @@ namespace openrayman
|
||||||
{
|
{
|
||||||
|
|
||||||
#define DECOMPILE_SECTION(id, name, function) \
|
#define DECOMPILE_SECTION(id, name, function) \
|
||||||
case (id): \
|
case id: \
|
||||||
{ \
|
{ \
|
||||||
std::cout << "[openrayman::dsb_decompiler] Decompiling " \
|
std::cout << "[openrayman::dsb_decompiler] Decompiling " \
|
||||||
<< std::hex << "0x" << std::setfill('0') << std::setw(2) << (id) \
|
<< std::hex << "0x" << std::setfill('0') << std::setw(2) << id\
|
||||||
<< " (decimal " << std::dec << id << ") \"" \
|
<< " (decimal " << std::dec << id << ") \"" \
|
||||||
<< (name) << "\"" << std::endl; \
|
<< name << "\"" << std::endl; \
|
||||||
target << "section " << (name) << "\n"; \
|
function(in, target); \
|
||||||
(function)(in, target); \
|
|
||||||
target << "\n"; \
|
|
||||||
break; \
|
break; \
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -102,8 +100,7 @@ namespace openrayman
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// this allocates variables?!?!
|
void dsb_decompiler::decompile_alloc(std::istream& source, const std::string& target)
|
||||||
void dsb_decompiler::decompile_alloc(std::istream& source, std::ofstream& target)
|
|
||||||
{
|
{
|
||||||
std::int32_t id;
|
std::int32_t id;
|
||||||
source.read((char*)&id, sizeof(std::int32_t));
|
source.read((char*)&id, sizeof(std::int32_t));
|
||||||
|
@ -111,30 +108,27 @@ namespace openrayman
|
||||||
{
|
{
|
||||||
if(id == 0x10)
|
if(id == 0x10)
|
||||||
{
|
{
|
||||||
// skip(?, ?);
|
|
||||||
target << " skip(";
|
|
||||||
std::int32_t a, b;
|
std::int32_t a, b;
|
||||||
source.read((char*)&a, sizeof(std::int32_t));
|
source.read((char*)&a, sizeof(std::int32_t));
|
||||||
source.read((char*)&b, sizeof(std::int32_t));
|
source.read((char*)&b, sizeof(std::int32_t));
|
||||||
target << std::to_string(a) << ", " << std::to_string(b) << ")\n";
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// alloc(slot, value);
|
|
||||||
target << " alloc(" << std::to_string(id) << ", ";
|
|
||||||
std::int32_t a;
|
std::int32_t a;
|
||||||
source.read((char*)&a, sizeof(std::int32_t));
|
source.read((char*)&a, sizeof(std::int32_t));
|
||||||
if(a == 0xFFFF)
|
if(a == 0xFFFF)
|
||||||
return;
|
return;
|
||||||
target << std::to_string(a) << ")\n";
|
|
||||||
}
|
}
|
||||||
source.read((char*)&id, sizeof(std::int32_t));
|
source.read((char*)&id, sizeof(std::int32_t));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Adds levels
|
void dsb_decompiler::decompile_lvl_list(std::istream& source, const std::string& target)
|
||||||
void dsb_decompiler::decompile_lvl_list(std::istream& source, std::ofstream& target)
|
|
||||||
{
|
{
|
||||||
|
std::ofstream levels_file(target + "/levels.json");
|
||||||
|
nlohmann::json levels_json;
|
||||||
|
levels_json["levels"] = nlohmann::json::array();
|
||||||
|
levels_json["initial"] = "";
|
||||||
std::int32_t num_levels;
|
std::int32_t num_levels;
|
||||||
source.read((char*)&num_levels, sizeof(std::int32_t));
|
source.read((char*)&num_levels, sizeof(std::int32_t));
|
||||||
std::int32_t id;
|
std::int32_t id;
|
||||||
|
@ -143,17 +137,21 @@ namespace openrayman
|
||||||
{
|
{
|
||||||
if(id == 0x1F)
|
if(id == 0x1F)
|
||||||
{
|
{
|
||||||
// add(name);
|
std::string str = read_string_with_length_u16(source);
|
||||||
target << " add(\"";
|
// levels have four spaced appended for some reason, trim them
|
||||||
push_string_with_length_u16(source, target);
|
str.erase(str.find_last_not_of(" \t") + 1);
|
||||||
target << " \")\n";
|
// this seems to be the case for all games, makes sense too
|
||||||
|
if(levels_json["initial"] == "")
|
||||||
|
levels_json["initial"] = str;
|
||||||
|
levels_json["levels"].push_back(str);
|
||||||
}
|
}
|
||||||
source.read((char*)&id, sizeof(std::int32_t));
|
source.read((char*)&id, sizeof(std::int32_t));
|
||||||
}
|
}
|
||||||
|
if(levels_file.is_open())
|
||||||
|
levels_file << std::setw(4) << levels_json;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Data directories
|
void dsb_decompiler::decompile_data_directories(std::istream& source, const std::string& target)
|
||||||
void dsb_decompiler::decompile_data_directories(std::istream& source, std::ofstream& target)
|
|
||||||
{
|
{
|
||||||
std::int32_t id;
|
std::int32_t id;
|
||||||
source.read((char*)&id, sizeof(std::int32_t));
|
source.read((char*)&id, sizeof(std::int32_t));
|
||||||
|
@ -181,22 +179,15 @@ namespace openrayman
|
||||||
source.read((char*)&str_length, sizeof(std::uint16_t));
|
source.read((char*)&str_length, sizeof(std::uint16_t));
|
||||||
char str[str_length];
|
char str[str_length];
|
||||||
source.read(str, str_length);
|
source.read(str, str_length);
|
||||||
std::cout << "[openrayman::dsb_decompiler] Data directory " << std::hex << "0x" << id << ": " << str << std::endl;
|
|
||||||
// dir(name, path);
|
|
||||||
if(dir != "")
|
|
||||||
target << " dir(" << dir << ", \"" << str << "\")\n";
|
|
||||||
source.read((char*)&id, sizeof(std::int32_t));
|
source.read((char*)&id, sizeof(std::int32_t));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: what is this?!?
|
void dsb_decompiler::decompile_unknown_blob_0x20(std::istream& source, const std::string& target)
|
||||||
// is this ever found?!?!
|
|
||||||
void dsb_decompiler::decompile_unknown_blob_0x20(std::istream& source, std::ofstream& target)
|
|
||||||
{
|
{
|
||||||
std::cout << "[openrayman::dsb_decompiler] Warning! encountered 0x20" << std::endl;
|
std::cout << "[openrayman::dsb_decompiler] Warning! encountered 0x20" << std::endl;
|
||||||
std::uint32_t size;
|
std::uint32_t size;
|
||||||
source.read((char*)&size, sizeof(std::uint32_t));
|
source.read((char*)&size, sizeof(std::uint32_t));
|
||||||
target << " size(" << size << ")\n";
|
|
||||||
std::int32_t id;
|
std::int32_t id;
|
||||||
source.read((char*)&id, sizeof(std::int32_t));
|
source.read((char*)&id, sizeof(std::int32_t));
|
||||||
std::unordered_map<std::int32_t, int> encountered_ids;
|
std::unordered_map<std::int32_t, int> encountered_ids;
|
||||||
|
@ -207,13 +198,14 @@ namespace openrayman
|
||||||
encountered_ids[id]++;
|
encountered_ids[id]++;
|
||||||
source.read((char*)&id, sizeof(std::int32_t));
|
source.read((char*)&id, sizeof(std::int32_t));
|
||||||
}
|
}
|
||||||
for(std::pair<std::int32_t, int> pair : encountered_ids)
|
|
||||||
target << " id_encounter(" << std::hex << "0x" << pair.first << std::dec << ", " << pair.second << ")\n";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// vignettes are basically loading screens
|
void dsb_decompiler::decompile_vignette(std::istream& source, const std::string& target)
|
||||||
void dsb_decompiler::decompile_vignette(std::istream& source, std::ofstream& target)
|
|
||||||
{
|
{
|
||||||
|
std::ofstream vignette_file(target + "/vignette.json");
|
||||||
|
nlohmann::json vignette_json;
|
||||||
|
vignette_json["bar"] = nlohmann::json::object();
|
||||||
|
vignette_json["image"] = "";
|
||||||
std::int32_t id;
|
std::int32_t id;
|
||||||
source.read((char*)&id, sizeof(std::int32_t));
|
source.read((char*)&id, sizeof(std::int32_t));
|
||||||
while(id != 0xFFFF)
|
while(id != 0xFFFF)
|
||||||
|
@ -223,14 +215,22 @@ namespace openrayman
|
||||||
case 71:
|
case 71:
|
||||||
case 72:
|
case 72:
|
||||||
{
|
{
|
||||||
target << " load_img(\"";
|
std::string str = read_string_with_length_u16(source);
|
||||||
push_string_with_length_u16(source, target);
|
if(str.find_first_of("Random<") == 0)
|
||||||
target << "\")\n";
|
{
|
||||||
|
// this is supposed to select a random vignette
|
||||||
|
// but we just use the first one
|
||||||
|
str = str.substr(7, std::string::npos);
|
||||||
|
str = str.substr(0, str.find_first_of(','));
|
||||||
|
}
|
||||||
|
if(str.find_last_of(".bmp") != std::string::npos)
|
||||||
|
str = str.substr(0, str.find_last_of(".bmp") - 3);
|
||||||
|
vignette_json["image"] = str;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 75:
|
case 75:
|
||||||
{
|
{
|
||||||
target << " display()\n";
|
// display
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 76:
|
case 76:
|
||||||
|
@ -243,74 +243,62 @@ namespace openrayman
|
||||||
source.read((char*)&tmp, sizeof(std::int32_t));
|
source.read((char*)&tmp, sizeof(std::int32_t));
|
||||||
std::uint8_t r, g, b, a;
|
std::uint8_t r, g, b, a;
|
||||||
source.read((char*)&r, 1); source.read((char*)&g, 1); source.read((char*)&b, 1); source.read((char*)&a, 1);
|
source.read((char*)&r, 1); source.read((char*)&g, 1); source.read((char*)&b, 1); source.read((char*)&a, 1);
|
||||||
target << " color("
|
if(id == 76)
|
||||||
<< (id == 76 ? "outline" : "inside")
|
vignette_json["bar"]["outside"] = { r, g, b, a };
|
||||||
<< ", " << std::to_string(r)
|
else
|
||||||
<< ", " << std::to_string(g)
|
vignette_json["bar"]["inside"] = { r, g, b, a };
|
||||||
<< ", " << std::to_string(b)
|
|
||||||
<< ", " << std::to_string(a)
|
|
||||||
<< ")\n";
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 78:
|
case 78:
|
||||||
{
|
{
|
||||||
std::uint8_t colors[(sizeof(std::uint8_t) * 4) * 4];
|
std::uint8_t colors[(sizeof(std::uint8_t) * 4) * 4];
|
||||||
source.read((char*)colors, (sizeof(std::uint8_t) * 4) * 4);
|
source.read((char*)colors, (sizeof(std::uint8_t) * 4) * 4);
|
||||||
target << " color(bar";
|
vignette_json["bar"]["main"] = nlohmann::json::object();
|
||||||
for(int n = 0; n < 4; n++)
|
// TODO: are these coordinates right?!?
|
||||||
{
|
vignette_json["bar"]["main"]["(x, -)"] = { colors[0], colors[1], colors[2], colors[3] };
|
||||||
target << ", " << std::to_string(colors[(n * 4)])
|
vignette_json["bar"]["main"]["(y, -)"] = { colors[4], colors[5], colors[6], colors[7] };
|
||||||
<< ", " << std::to_string(colors[(n * 4) + 1])
|
vignette_json["bar"]["main"]["(x, +)"] = { colors[8], colors[9], colors[10], colors[11] };
|
||||||
<< ", " << std::to_string(colors[(n * 4) + 2])
|
vignette_json["bar"]["main"]["(y, +)"] = { colors[12], colors[13], colors[14], colors[15] };
|
||||||
<< ", " << std::to_string(colors[(n * 4) + 3]);
|
|
||||||
}
|
|
||||||
target << ")\n";
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 79:
|
case 79:
|
||||||
{
|
{
|
||||||
std::int32_t values[4];
|
std::int32_t vec[4];
|
||||||
source.read((char*)values, sizeof(std::int32_t) * 4);
|
source.read((char*)vec, sizeof(std::int32_t) * 4);
|
||||||
target << " create_bar(";
|
vignette_json["bar"]["pos"] = { vec[0], vec[1], vec[2], vec[3] };
|
||||||
for(int n = 0; n < 4; n++)
|
|
||||||
target << (n == 0 ? "" : ", ") << values[n];
|
|
||||||
target << ")\n";
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 80:
|
case 80:
|
||||||
{
|
{
|
||||||
target << " add_bar()\n";
|
// add_bar
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 81:
|
case 81:
|
||||||
{
|
{
|
||||||
std::int32_t max;
|
std::int32_t max;
|
||||||
source.read((char*)&max, sizeof(std::int32_t));
|
source.read((char*)&max, sizeof(std::int32_t));
|
||||||
target << " bar_max(" << max << ")\n";
|
vignette_json["bar"]["max"] = max;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
source.read((char*)&id, sizeof(std::int32_t));
|
source.read((char*)&id, sizeof(std::int32_t));
|
||||||
}
|
}
|
||||||
|
if(vignette_file.is_open())
|
||||||
|
vignette_file << std::setw(4) << vignette_json;
|
||||||
}
|
}
|
||||||
|
|
||||||
void dsb_decompiler::decompile_texture_files(std::istream& source, std::ofstream& target)
|
void dsb_decompiler::decompile_texture_files(std::istream& source, const std::string& target)
|
||||||
{
|
{
|
||||||
std::int32_t id;
|
std::int32_t id;
|
||||||
source.read((char*)&id, sizeof(std::int32_t));
|
source.read((char*)&id, sizeof(std::int32_t));
|
||||||
while(id != 0xFFFF)
|
while(id != 0xFFFF)
|
||||||
{
|
{
|
||||||
if(id == 66)
|
read_string_with_length_u16(source);
|
||||||
target << " add(textures, \"";
|
|
||||||
else
|
|
||||||
target << " add(vignettes, \"";
|
|
||||||
push_string_with_length_u16(source, target);
|
|
||||||
target << "\")\n";
|
|
||||||
source.read((char*)&id, sizeof(std::int32_t));
|
source.read((char*)&id, sizeof(std::int32_t));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void dsb_decompiler::decompile_unknown_blob_0x6e(std::istream& source, std::ofstream& target)
|
void dsb_decompiler::decompile_unknown_blob_0x6e(std::istream& source, const std::string& target)
|
||||||
{
|
{
|
||||||
std::cout << "[openrayman::dsb_decompiler] Warning! encountered 0x6e" << std::endl;
|
std::cout << "[openrayman::dsb_decompiler] Warning! encountered 0x6e" << std::endl;
|
||||||
std::uint8_t tmp = 0x00;
|
std::uint8_t tmp = 0x00;
|
||||||
|
@ -320,7 +308,7 @@ namespace openrayman
|
||||||
source.read((char*)&tmp, 1);
|
source.read((char*)&tmp, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
void dsb_decompiler::decompile_game_options(std::istream& source, std::ofstream& target)
|
void dsb_decompiler::decompile_game_options(std::istream& source, const std::string& target)
|
||||||
{
|
{
|
||||||
std::int32_t id;
|
std::int32_t id;
|
||||||
source.read((char*)&id, sizeof(std::int32_t));
|
source.read((char*)&id, sizeof(std::int32_t));
|
||||||
|
@ -329,17 +317,9 @@ namespace openrayman
|
||||||
switch(id)
|
switch(id)
|
||||||
{
|
{
|
||||||
case 0x65:
|
case 0x65:
|
||||||
{
|
|
||||||
target << " default_file(\"";
|
|
||||||
push_string_with_length_u16(source, target);
|
|
||||||
target << "\")\n";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 0x66:
|
case 0x66:
|
||||||
{
|
{
|
||||||
target << " current_file(\"";
|
read_string_with_length_u16(source);
|
||||||
push_string_with_length_u16(source, target);
|
|
||||||
target << "\")\n";
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 0x67:
|
case 0x67:
|
||||||
|
@ -353,8 +333,7 @@ namespace openrayman
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// idk what this is?!?!
|
void dsb_decompiler::decompile_sound_banks(std::istream& source, const std::string& target)
|
||||||
void dsb_decompiler::decompile_sound_banks(std::istream& source, std::ofstream& target)
|
|
||||||
{
|
{
|
||||||
std::int32_t id;
|
std::int32_t id;
|
||||||
source.read((char*)&id, sizeof(std::int32_t));
|
source.read((char*)&id, sizeof(std::int32_t));
|
||||||
|
@ -366,14 +345,12 @@ namespace openrayman
|
||||||
{
|
{
|
||||||
std::int32_t unknown;
|
std::int32_t unknown;
|
||||||
source.read((char*)&unknown, sizeof(std::int32_t));
|
source.read((char*)&unknown, sizeof(std::int32_t));
|
||||||
target << " unknown(" << unknown << ")\n";
|
|
||||||
}
|
}
|
||||||
source.read((char*)&id, sizeof(std::int32_t));
|
source.read((char*)&id, sizeof(std::int32_t));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// idk what this is either?!
|
void dsb_decompiler::decompile_load_sound_banks(std::istream& source, const std::string& target)
|
||||||
void dsb_decompiler::decompile_load_sound_banks(std::istream& source, std::ofstream& target)
|
|
||||||
{
|
{
|
||||||
std::int32_t id;
|
std::int32_t id;
|
||||||
source.read((char*)&id, sizeof(std::int32_t));
|
source.read((char*)&id, sizeof(std::int32_t));
|
||||||
|
@ -383,7 +360,6 @@ namespace openrayman
|
||||||
{
|
{
|
||||||
std::int32_t bank;
|
std::int32_t bank;
|
||||||
source.read((char*)&bank, sizeof(std::int32_t));
|
source.read((char*)&bank, sizeof(std::int32_t));
|
||||||
target << " add_bank2(" << bank << ")\n";
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -391,17 +367,14 @@ namespace openrayman
|
||||||
source.read((char*)&size, sizeof(std::int32_t));
|
source.read((char*)&size, sizeof(std::int32_t));
|
||||||
char bytes[size * 4];
|
char bytes[size * 4];
|
||||||
source.read((char*)&bytes, size * 4);
|
source.read((char*)&bytes, size * 4);
|
||||||
target << " add_bank(";
|
|
||||||
for(int n = 0; n < size * 4; n++)
|
|
||||||
target << (n == 0 ? "" : ", ") << std::hex << "0x" << (int)bytes[n];
|
|
||||||
target << ")\n";
|
|
||||||
}
|
}
|
||||||
source.read((char*)&id, sizeof(std::int32_t));
|
source.read((char*)&id, sizeof(std::int32_t));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void dsb_decompiler::push_string_null_terminated(std::istream& source, std::ofstream& target)
|
const std::string dsb_decompiler::read_string_null_terminated(std::istream& source)
|
||||||
{
|
{
|
||||||
|
std::stringstream target;
|
||||||
char c = 0x00;
|
char c = 0x00;
|
||||||
source.read(&c, 1);
|
source.read(&c, 1);
|
||||||
while(c != '\0')
|
while(c != '\0')
|
||||||
|
@ -409,14 +382,15 @@ namespace openrayman
|
||||||
target << c;
|
target << c;
|
||||||
source.read(&c, 1);
|
source.read(&c, 1);
|
||||||
}
|
}
|
||||||
|
return target.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
void dsb_decompiler::push_string_with_length_u16(std::istream& source, std::ofstream& target)
|
const std::string dsb_decompiler::read_string_with_length_u16(std::istream& source)
|
||||||
{
|
{
|
||||||
std::uint16_t length;
|
std::uint16_t length;
|
||||||
source.read((char*)&length, sizeof(std::uint16_t));
|
source.read((char*)&length, sizeof(std::uint16_t));
|
||||||
char str[length];
|
char str[length];
|
||||||
source.read(str, length);
|
source.read(str, length);
|
||||||
target << str;
|
return std::string(str);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,7 @@ namespace openrayman
|
||||||
// The default format.
|
// The default format.
|
||||||
// This is a more human readable/editable format than the binary format used by Rayman 2.
|
// This is a more human readable/editable format than the binary format used by Rayman 2.
|
||||||
// This can be interpreted by the dsb engine.
|
// This can be interpreted by the dsb engine.
|
||||||
// Uses the extension .odsb
|
// Creates several files such as vignette.json, levels.json
|
||||||
openrayman,
|
openrayman,
|
||||||
|
|
||||||
// The format used by the Rayman 2: The Great Escape engine.
|
// The format used by the Rayman 2: The Great Escape engine.
|
||||||
|
@ -25,34 +25,34 @@ namespace openrayman
|
||||||
rayman2_decoded
|
rayman2_decoded
|
||||||
};
|
};
|
||||||
|
|
||||||
// Decodes and optionally decompiles DSB files.
|
// Decodes and decompiles DSB files.
|
||||||
// DSB files are compiled scripts, with different sections.
|
// DSB files are compiled scripts, with different sections.
|
||||||
// This is all heavily adapted from Rayman2Lib.
|
// This is all heavily adapted from Rayman2Lib.
|
||||||
class dsb_decompiler
|
class dsb_decompiler
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
// Decodes and optionally decompiles the specified DSB file into the target file.
|
// Decodes and optionally decompiles the specified DSB file into several files in the target directory.
|
||||||
// This function assumes that the source file exists and that the target location is accessible.
|
// This function assumes that the source file exists and that the target directory is accessible.
|
||||||
// Returns true if the function succeeded.
|
// Returns true if the function succeeded.
|
||||||
bool decompile_dsb(const std::string& source, const std::string& target, dsb_format fmt);
|
bool decompile_dsb(const std::string& source, const std::string& target, dsb_format fmt);
|
||||||
private:
|
private:
|
||||||
void decompile_sections(char* source, std::size_t source_length, std::ofstream& target);
|
void decompile_sections(char* source, std::size_t source_length, const std::string& target);
|
||||||
void decompile_alloc(std::istream& source, std::ofstream& target);
|
void decompile_alloc(std::istream& source, const std::string& target);
|
||||||
void decompile_lvl_list(std::istream& source, std::ofstream& target);
|
void decompile_lvl_list(std::istream& source, const std::string& target);
|
||||||
void decompile_data_directories(std::istream& source, std::ofstream& target);
|
void decompile_data_directories(std::istream& source, const std::string& target);
|
||||||
void decompile_unknown_blob_0x20(std::istream& source, std::ofstream& target);
|
void decompile_unknown_blob_0x20(std::istream& source, const std::string& target);
|
||||||
void decompile_vignette(std::istream& source, std::ofstream& target);
|
void decompile_vignette(std::istream& source, const std::string& target);
|
||||||
void decompile_texture_files(std::istream& source, std::ofstream& target);
|
void decompile_texture_files(std::istream& source, const std::string& target);
|
||||||
void decompile_unknown_blob_0x6e(std::istream& source, std::ofstream& target);
|
void decompile_unknown_blob_0x6e(std::istream& source, const std::string& target);
|
||||||
void decompile_game_options(std::istream& source, std::ofstream& target);
|
void decompile_game_options(std::istream& source, const std::string& target);
|
||||||
void decompile_sound_banks(std::istream& source, std::ofstream& target);
|
void decompile_sound_banks(std::istream& source, const std::string& target);
|
||||||
void decompile_load_sound_banks(std::istream& source, std::ofstream& target);
|
void decompile_load_sound_banks(std::istream& source, const std::string& target);
|
||||||
|
|
||||||
// Reads a null-terminated string from source to target.
|
// Reads a null-terminated string from source.
|
||||||
void push_string_null_terminated(std::istream& source, std::ofstream& target);
|
const std::string read_string_null_terminated(std::istream& source);
|
||||||
|
|
||||||
// Reads the length of the string from source and then writes it to target.
|
// Reads the length of the string from source.
|
||||||
void push_string_with_length_u16(std::istream& source, std::ofstream& target);
|
const std::string read_string_with_length_u16(std::istream& source);
|
||||||
|
|
||||||
data_decoder m_decoder;
|
data_decoder m_decoder;
|
||||||
};
|
};
|
||||||
|
|
131
src/data_extractor/gf/gf_converter.cc
Normal file
131
src/data_extractor/gf/gf_converter.cc
Normal file
|
@ -0,0 +1,131 @@
|
||||||
|
#include <data_extractor/gf/gf_converter.h>
|
||||||
|
#include <lodepng.h>
|
||||||
|
|
||||||
|
namespace openrayman
|
||||||
|
{
|
||||||
|
struct memorybuf : std::streambuf
|
||||||
|
{
|
||||||
|
memorybuf(char* begin, char* end)
|
||||||
|
{
|
||||||
|
setg(begin, begin, end);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
bool gf_converter::get_gf_info(std::vector<std::uint8_t>& in, std::size_t* w, std::size_t* h, std::uint8_t* num_channels, std::uint8_t* repeat_byte)
|
||||||
|
{
|
||||||
|
memorybuf streambuf((char*)in.data(), (char*)in.data() + in.size());
|
||||||
|
std::istream in_stream(&streambuf);
|
||||||
|
if(in.size() <= 0)
|
||||||
|
return false;
|
||||||
|
std::int32_t signature;
|
||||||
|
in_stream.read((char*)&signature, sizeof(std::int32_t));
|
||||||
|
if(signature != 0x378 && signature != 0x22b8)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
std::int32_t width, height;
|
||||||
|
in_stream.read((char*)&width, sizeof(std::int32_t));
|
||||||
|
in_stream.read((char*)&height, sizeof(std::int32_t));
|
||||||
|
*w = width;
|
||||||
|
*h = height;
|
||||||
|
|
||||||
|
in_stream.read((char*)num_channels, 1);
|
||||||
|
in_stream.read((char*)repeat_byte, 1);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::uint8_t> gf_converter::convert_to_rgba(std::vector<std::uint8_t>& in, std::size_t* w, std::size_t* h)
|
||||||
|
{
|
||||||
|
#define FAIL \
|
||||||
|
{ \
|
||||||
|
w = h = 0; \
|
||||||
|
return std::vector<std::uint8_t>(0, 0); \
|
||||||
|
}
|
||||||
|
|
||||||
|
memorybuf streambuf((char*)in.data(), (char*)in.data() + in.size());
|
||||||
|
std::istream in_stream(&streambuf);
|
||||||
|
if(in.size() <= 0)
|
||||||
|
FAIL;
|
||||||
|
std::int32_t signature;
|
||||||
|
in_stream.read((char*)&signature, sizeof(std::int32_t));
|
||||||
|
if(signature != 0x378 && signature != 0x22b8)
|
||||||
|
FAIL;
|
||||||
|
|
||||||
|
std::int32_t width, height;
|
||||||
|
in_stream.read((char*)&width, sizeof(std::int32_t));
|
||||||
|
in_stream.read((char*)&height, sizeof(std::int32_t));
|
||||||
|
*w = width;
|
||||||
|
*h = height;
|
||||||
|
|
||||||
|
std::vector<std::uint8_t> data(width * height * 4, 255);
|
||||||
|
|
||||||
|
std::uint8_t channels, repeat_byte;
|
||||||
|
in_stream.read((char*)&channels, 1);
|
||||||
|
in_stream.read((char*)&repeat_byte, 1);
|
||||||
|
if(channels != 3 && channels != 4)
|
||||||
|
FAIL;
|
||||||
|
|
||||||
|
std::cout << "size: " << width << "x" << height << " channels: " << std::to_string(channels) << " ";
|
||||||
|
std::vector<std::uint8_t> r_ch(width * height, 0), g_ch(width * height, 0), b_ch(width * height, 0), a_ch(width * height, 255);
|
||||||
|
read_channel(in_stream, repeat_byte, b_ch);
|
||||||
|
read_channel(in_stream, repeat_byte, g_ch);
|
||||||
|
read_channel(in_stream, repeat_byte, r_ch);
|
||||||
|
if(channels == 4)
|
||||||
|
read_channel(in_stream, repeat_byte, a_ch);
|
||||||
|
|
||||||
|
std::size_t x, y;
|
||||||
|
for(y = 0; y < height; y++)
|
||||||
|
{
|
||||||
|
for(x = 0; x < width; x++)
|
||||||
|
{
|
||||||
|
data[(((y * width) + x) * 4)] = r_ch[((height - 1 - y) * width) + x];
|
||||||
|
data[(((y * width) + x) * 4) + 1] = g_ch[((height - 1 - y) * width) + x];
|
||||||
|
data[(((y * width) + x) * 4) + 2] = b_ch[((height - 1 - y) * width) + x];
|
||||||
|
data[(((y * width) + x) * 4) + 3] = a_ch[((height - 1 - y) * width) + x];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
void gf_converter::read_channel(std::istream& data, std::uint8_t repeat_byte, std::vector<std::uint8_t>& channel)
|
||||||
|
{
|
||||||
|
std::size_t at = 0;
|
||||||
|
while(at < channel.size())
|
||||||
|
{
|
||||||
|
std::uint8_t value;
|
||||||
|
data.read((char*)&value, 1);
|
||||||
|
if(value == repeat_byte)
|
||||||
|
{
|
||||||
|
data.read((char*)&value, 1);
|
||||||
|
std::uint8_t count;
|
||||||
|
data.read((char*)&count, 1);
|
||||||
|
for(std::size_t n = 0; n < count; n++)
|
||||||
|
{
|
||||||
|
// either we are wrong or some files are formatted weird?
|
||||||
|
// this seems to turn out ok
|
||||||
|
if(at + n < channel.size())
|
||||||
|
channel[at + n] = value;
|
||||||
|
}
|
||||||
|
at += count;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
channel[at] = value;
|
||||||
|
at++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::uint8_t> gf_converter::convert_to_png(std::vector<std::uint8_t>& in)
|
||||||
|
{
|
||||||
|
std::size_t w, h;
|
||||||
|
std::vector<std::uint8_t> data = convert_to_rgba(in, &w, &h);
|
||||||
|
if(data.size() == 0)
|
||||||
|
return std::vector<std::uint8_t>(0, 0);
|
||||||
|
std::vector<std::uint8_t> png;
|
||||||
|
std::uint32_t error = lodepng::encode(png, data, w, h);
|
||||||
|
if(error)
|
||||||
|
return std::vector<std::uint8_t>(0, 0);
|
||||||
|
return png;
|
||||||
|
}
|
||||||
|
}
|
30
src/data_extractor/gf/gf_converter.h
Normal file
30
src/data_extractor/gf/gf_converter.h
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
#ifndef GF_CONVERTER_H
|
||||||
|
#define GF_CONVERTER_H
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <iostream>
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
namespace openrayman
|
||||||
|
{
|
||||||
|
// GF (graphics texture) files are used as textures.
|
||||||
|
// This is all heavily adapted from Rayman2Lib.
|
||||||
|
class gf_converter
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
// Returns true if information about the GF file could be extracted.
|
||||||
|
bool get_gf_info(std::vector<std::uint8_t>& in, std::size_t* w, std::size_t* h, std::uint8_t* num_channels, std::uint8_t* repeat_byte);
|
||||||
|
|
||||||
|
// Converts a GF file in memory into a raw array of bytes, consisting of a RGBA32 image.
|
||||||
|
// This function will return an array of size 0 if it fails.
|
||||||
|
// Alternatively, check w or h if they have a value of 0.
|
||||||
|
std::vector<std::uint8_t> convert_to_rgba(std::vector<std::uint8_t>& in, std::size_t* w, std::size_t* h);
|
||||||
|
|
||||||
|
// Converts a GF file in memory into a raw array of bytes, consisting of a PNG image.
|
||||||
|
std::vector<std::uint8_t> convert_to_png(std::vector<std::uint8_t>& in);
|
||||||
|
private:
|
||||||
|
void read_channel(std::istream& data, std::uint8_t repeat_byte, std::vector<std::uint8_t>& channel);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -1,217 +0,0 @@
|
||||||
#ifndef DSB_INSTRUCTION_H
|
|
||||||
#define DSB_INSTRUCTION_H
|
|
||||||
|
|
||||||
#include <cstdlib>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
namespace openrayman
|
|
||||||
{
|
|
||||||
enum class dsb_instruction_type
|
|
||||||
{
|
|
||||||
// Special value used to specify that no instruction could be provided.
|
|
||||||
none,
|
|
||||||
|
|
||||||
// This instruction implies that the DSB could not be interpreted for some reason.
|
|
||||||
// It is paried with a dsb_instruction_invalid_dsb, which specifies a string that describes what went
|
|
||||||
// wrong during interpretation, and at what line / column.
|
|
||||||
// No instructions should follow or precede this one.
|
|
||||||
invalid_dsb,
|
|
||||||
|
|
||||||
// This instruction specifies that a comment was specified in the DSB.
|
|
||||||
// It is paried with a dsb_instruction_comment, which specifies the comment encountered.
|
|
||||||
comment,
|
|
||||||
|
|
||||||
// This instruction implies the start of a new section in the DSB.
|
|
||||||
// It is paired with a dsb_instruction_begin_section, which specifies what
|
|
||||||
// type of section is starting.
|
|
||||||
// An instruction of this type that starts a "none" section implies
|
|
||||||
// the end of the instruction array.
|
|
||||||
begin_section,
|
|
||||||
|
|
||||||
// This instruction sets a data directory to a path.
|
|
||||||
// It is paried with a dsb_instruction_set_data_dir, which specifies what data directory to set
|
|
||||||
// and which path to set it to.
|
|
||||||
set_data_dir,
|
|
||||||
|
|
||||||
// This instruction sets a texture file id to an archive.
|
|
||||||
// It is paried with a dsb_instruction_set_texture_file, which specifies what texture file id to set
|
|
||||||
// and which archive to set it to.
|
|
||||||
set_texture_file,
|
|
||||||
|
|
||||||
// This instruction loads a background image into the vignette.
|
|
||||||
// This can contain "random" expression, in the format "Random<img, ?, ?, ...>".
|
|
||||||
// We don't currently know how to interpret this, so we just use the first image.
|
|
||||||
// Additionally, names can contain .bmp. Ignore this.
|
|
||||||
// TODO: is this ok?!? probably
|
|
||||||
// It is paired with a dsb_instruction_vignette_load_img, which specifies image file to load.
|
|
||||||
vignette_load_img,
|
|
||||||
|
|
||||||
// This instruction sets one of several color values.
|
|
||||||
// It is paried with a dsb_instruction_vignette_set_color, which specifies what id to set
|
|
||||||
// and what color(s) to set.
|
|
||||||
vignette_set_color,
|
|
||||||
|
|
||||||
// This instruction creates a bar with the current specifed colors.
|
|
||||||
// It is paried with a dsb_instruction_vignette_create_bar, which specifies the position and size of
|
|
||||||
// the vignette.
|
|
||||||
vignette_create_bar,
|
|
||||||
|
|
||||||
// This instruction adds a bar to the vignette.
|
|
||||||
// It is not paired with a class, as it doesn't need any additional values.
|
|
||||||
vignette_add_bar,
|
|
||||||
|
|
||||||
// This instruction displays the vignette on the screen.
|
|
||||||
// It is not paired with a class, as it doesn't need any additional values.
|
|
||||||
vignette_display,
|
|
||||||
|
|
||||||
// This instruction adds a level.
|
|
||||||
// It is paired with a dsb_instruction_level_add, which specifies what level to add.
|
|
||||||
level_add
|
|
||||||
};
|
|
||||||
|
|
||||||
inline std::string instruction_to_string(dsb_instruction_type instruction)
|
|
||||||
{
|
|
||||||
#define VALID_INSTRUCTION_TO(name) \
|
|
||||||
if(instruction == dsb_instruction_type::name) \
|
|
||||||
return #name;
|
|
||||||
|
|
||||||
VALID_INSTRUCTION_TO(invalid_dsb);
|
|
||||||
VALID_INSTRUCTION_TO(comment);
|
|
||||||
VALID_INSTRUCTION_TO(begin_section);
|
|
||||||
VALID_INSTRUCTION_TO(set_data_dir);
|
|
||||||
VALID_INSTRUCTION_TO(set_texture_file);
|
|
||||||
VALID_INSTRUCTION_TO(vignette_load_img);
|
|
||||||
VALID_INSTRUCTION_TO(vignette_set_color);
|
|
||||||
VALID_INSTRUCTION_TO(vignette_create_bar);
|
|
||||||
VALID_INSTRUCTION_TO(vignette_add_bar);
|
|
||||||
VALID_INSTRUCTION_TO(vignette_display);
|
|
||||||
VALID_INSTRUCTION_TO(level_add);
|
|
||||||
|
|
||||||
return "none";
|
|
||||||
}
|
|
||||||
|
|
||||||
inline dsb_instruction_type string_to_instruction(const std::string& instruction)
|
|
||||||
{
|
|
||||||
#define VALID_INSTRUCTION_FROM(name) \
|
|
||||||
if(instruction == #name) \
|
|
||||||
return dsb_instruction_type::name;
|
|
||||||
|
|
||||||
VALID_INSTRUCTION_FROM(invalid_dsb);
|
|
||||||
VALID_INSTRUCTION_FROM(comment);
|
|
||||||
VALID_INSTRUCTION_FROM(begin_section);
|
|
||||||
VALID_INSTRUCTION_FROM(set_data_dir);
|
|
||||||
VALID_INSTRUCTION_FROM(set_texture_file);
|
|
||||||
VALID_INSTRUCTION_FROM(vignette_load_img);
|
|
||||||
VALID_INSTRUCTION_FROM(vignette_set_color);
|
|
||||||
VALID_INSTRUCTION_FROM(vignette_create_bar);
|
|
||||||
VALID_INSTRUCTION_FROM(vignette_add_bar);
|
|
||||||
VALID_INSTRUCTION_FROM(vignette_display);
|
|
||||||
VALID_INSTRUCTION_FROM(level_add);
|
|
||||||
|
|
||||||
return dsb_instruction_type::none;
|
|
||||||
}
|
|
||||||
|
|
||||||
enum class dsb_section_type
|
|
||||||
{
|
|
||||||
// Special value used to specify that no section could be provided.
|
|
||||||
none,
|
|
||||||
|
|
||||||
// Section to allocate values into slots.
|
|
||||||
// This section is not present in a raw Rayman 2: The Great Escape .pgb.
|
|
||||||
// Unused.
|
|
||||||
alloc,
|
|
||||||
|
|
||||||
// Section to set data directories.
|
|
||||||
// This is only used by the data extractor to find game data in a
|
|
||||||
// valid Rayman 2: The Great Escape installation.
|
|
||||||
data_directories,
|
|
||||||
|
|
||||||
// Section to set texture archives.
|
|
||||||
// This is only used by the data extractor to find game data in a
|
|
||||||
// valid Rayman 2: The Great Escape installation.
|
|
||||||
texture_files,
|
|
||||||
|
|
||||||
// Section that builds a vignette.
|
|
||||||
vignette,
|
|
||||||
|
|
||||||
// Unknown section.
|
|
||||||
// Unused.
|
|
||||||
unknown_blob_0x6e,
|
|
||||||
|
|
||||||
// Unknown section.
|
|
||||||
// Unused.
|
|
||||||
unknown_blob_0x20,
|
|
||||||
|
|
||||||
// Section that specifies what game option files to use.
|
|
||||||
// Unused.
|
|
||||||
game_options,
|
|
||||||
|
|
||||||
// Sections that adds levels.
|
|
||||||
// This is only used by the data extractor to find game data in a
|
|
||||||
// valid Rayman 2: The Great Escape installation.
|
|
||||||
levels,
|
|
||||||
|
|
||||||
// Unknown section (for now).
|
|
||||||
// Unused.
|
|
||||||
sound_banks,
|
|
||||||
|
|
||||||
// Unknown section (for now).
|
|
||||||
// Unused.
|
|
||||||
load_sound_banks
|
|
||||||
};
|
|
||||||
|
|
||||||
inline std::string section_to_string(dsb_section_type section)
|
|
||||||
{
|
|
||||||
#define VALID_SECTION_TO(name) \
|
|
||||||
if(section == dsb_section_type::name) \
|
|
||||||
return #name;
|
|
||||||
|
|
||||||
VALID_SECTION_TO(alloc);
|
|
||||||
VALID_SECTION_TO(data_directories);
|
|
||||||
VALID_SECTION_TO(texture_files);
|
|
||||||
VALID_SECTION_TO(vignette);
|
|
||||||
VALID_SECTION_TO(unknown_blob_0x6e);
|
|
||||||
VALID_SECTION_TO(unknown_blob_0x20);
|
|
||||||
VALID_SECTION_TO(game_options);
|
|
||||||
VALID_SECTION_TO(levels);
|
|
||||||
VALID_SECTION_TO(sound_banks);
|
|
||||||
VALID_SECTION_TO(load_sound_banks);
|
|
||||||
|
|
||||||
return "none";
|
|
||||||
}
|
|
||||||
|
|
||||||
inline dsb_section_type string_to_section(const std::string& section)
|
|
||||||
{
|
|
||||||
#define VALID_SECTION_FROM(name) \
|
|
||||||
if(section == #name) \
|
|
||||||
return dsb_section_type::name;
|
|
||||||
|
|
||||||
VALID_SECTION_FROM(alloc);
|
|
||||||
VALID_SECTION_FROM(data_directories);
|
|
||||||
VALID_SECTION_FROM(texture_files);
|
|
||||||
VALID_SECTION_FROM(vignette);
|
|
||||||
VALID_SECTION_FROM(unknown_blob_0x6e);
|
|
||||||
VALID_SECTION_FROM(unknown_blob_0x20);
|
|
||||||
VALID_SECTION_FROM(game_options);
|
|
||||||
VALID_SECTION_FROM(levels);
|
|
||||||
VALID_SECTION_FROM(sound_banks);
|
|
||||||
VALID_SECTION_FROM(load_sound_banks);
|
|
||||||
|
|
||||||
return dsb_section_type::none;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct dsb_instruction
|
|
||||||
{
|
|
||||||
dsb_instruction(dsb_instruction_type type) :
|
|
||||||
type(type)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// The type of instruction that this class holds.
|
|
||||||
// The class should be cast via static_cast<dsb_instruction_?> or similar.
|
|
||||||
dsb_instruction_type type;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,219 +0,0 @@
|
||||||
#include <dsb_interpreter/dsb_interpreter.h>
|
|
||||||
#include <dsb_interpreter/dsb_interpreter_string_utils.h>
|
|
||||||
#include <dsb_interpreter/instructions/all.h>
|
|
||||||
#include <platform/file.h>
|
|
||||||
#include <glm/glm.hpp>
|
|
||||||
#include <cstdint>
|
|
||||||
#include <sstream>
|
|
||||||
#include <iomanip>
|
|
||||||
|
|
||||||
namespace openrayman
|
|
||||||
{
|
|
||||||
dsb_interpreter::dsb_interpreter(const std::string& odsb_path)
|
|
||||||
{
|
|
||||||
std::ifstream stream(file::fix_string(odsb_path), std::ifstream::in);
|
|
||||||
if(!stream.is_open())
|
|
||||||
{
|
|
||||||
set_error(0, 0, "The file \"" + odsb_path + "\" does not exist.", dsb_section_type::none, "dsb_interpreter::dsb_interpreter (constructor)");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
interpret_sections(stream);
|
|
||||||
}
|
|
||||||
|
|
||||||
void dsb_interpreter::set_error(std::size_t line, std::size_t column, const std::string& error, dsb_section_type current_section, const std::string& function)
|
|
||||||
{
|
|
||||||
std::stringstream trace;
|
|
||||||
trace << "Occured when section was: " << std::hex << "0x" << std::setfill('0') << std::setw(2) << (int)current_section << " (" << section_to_string(current_section) << ")" << "\n";
|
|
||||||
trace << "Originated from function: " << function;
|
|
||||||
m_interpreted_dsb.clear();
|
|
||||||
m_interpreted_dsb.push_back(new dsb_instruction_invalid_dsb(line, column, error, trace.str()));
|
|
||||||
}
|
|
||||||
|
|
||||||
void dsb_interpreter::interpret_sections(std::ifstream& stream)
|
|
||||||
{
|
|
||||||
dsb_section_type current_section = dsb_section_type::none;
|
|
||||||
std::string line;
|
|
||||||
std::size_t line_n = 0;
|
|
||||||
while(std::getline(stream, line))
|
|
||||||
{
|
|
||||||
if(m_interpreted_dsb.size() > 0 && m_interpreted_dsb[0]->type == dsb_instruction_type::invalid_dsb)
|
|
||||||
return;
|
|
||||||
std::size_t n = 0;
|
|
||||||
n += skip_whitespace(line, n);
|
|
||||||
// if the string wasn't all whitespace
|
|
||||||
if(line.length() > 0 && n != line.length() - 1)
|
|
||||||
{
|
|
||||||
bool comment = false;
|
|
||||||
if(line[n] == '#')
|
|
||||||
{
|
|
||||||
n++;
|
|
||||||
n += skip_whitespace(line, n);
|
|
||||||
comment = true;
|
|
||||||
}
|
|
||||||
if(comment)
|
|
||||||
{
|
|
||||||
std::string comment_string = read_until_end(line, n);
|
|
||||||
if(comment_string.length() > 0)
|
|
||||||
m_interpreted_dsb.push_back(new dsb_instruction_comment(line_n, comment_string));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if(read_ahead_match(line, n, "section"))
|
|
||||||
{
|
|
||||||
n += sizeof("section");
|
|
||||||
n += skip_whitespace(line, n);
|
|
||||||
std::string section = read_until_end(line, n);
|
|
||||||
dsb_section_type new_section = string_to_section(section);
|
|
||||||
|
|
||||||
if(new_section == dsb_section_type::none)
|
|
||||||
{
|
|
||||||
set_error(line_n, n, "Invalid syntax (expected section type, got \"" + section + "\").", current_section, "dsb_interpreter::interpret_sections");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_interpreted_dsb.push_back(new dsb_instruction_begin_section(current_section, new_section));
|
|
||||||
current_section = new_section;
|
|
||||||
}
|
|
||||||
else if(current_section == dsb_section_type::none)
|
|
||||||
{
|
|
||||||
set_error(line_n, n, "Invalid syntax (expected section, got \"" + read_until_whitespace(line, n) + "\").", current_section, "dsb_interpreter::interpret_sections");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
interpret_line(current_section, line, n, line_n);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
line_n++;
|
|
||||||
}
|
|
||||||
// end of instruction array
|
|
||||||
if(m_interpreted_dsb.size() == 0)
|
|
||||||
set_error(0, 0, "No valid instructions could be interpreted from the specified file.", current_section, "dsb_interpreter::interpret_sections");
|
|
||||||
else
|
|
||||||
m_interpreted_dsb.push_back(new dsb_instruction_begin_section(current_section, dsb_section_type::none));
|
|
||||||
}
|
|
||||||
|
|
||||||
void dsb_interpreter::interpret_line(dsb_section_type current_section, const std::string& line, std::size_t statement_begins_at, std::size_t line_n)
|
|
||||||
{
|
|
||||||
std::size_t n = statement_begins_at;
|
|
||||||
std::string instruction_name = read_until_char(line, n, '(');
|
|
||||||
n += instruction_name.length() + 1;
|
|
||||||
std::size_t instr_begins_at = n - 1;
|
|
||||||
std::vector<std::string> instruction_args;
|
|
||||||
n += skip_whitespace(line, n);
|
|
||||||
std::string arg = "";
|
|
||||||
while(n < line.length())
|
|
||||||
{
|
|
||||||
if(line[n] == '"')
|
|
||||||
{
|
|
||||||
arg = read_quote_escaped_string(line, n);
|
|
||||||
n += arg.length() + 1;
|
|
||||||
}
|
|
||||||
else if(line[n] != ',' && line[n] != ')')
|
|
||||||
arg += line[n];
|
|
||||||
if(line[n] == ',')
|
|
||||||
{
|
|
||||||
instruction_args.push_back(arg);
|
|
||||||
arg = "";
|
|
||||||
n++;
|
|
||||||
n += skip_whitespace(line, n);
|
|
||||||
}
|
|
||||||
else if(line[n] == ')')
|
|
||||||
break;
|
|
||||||
else
|
|
||||||
n++;
|
|
||||||
}
|
|
||||||
if(arg.length() > 0)
|
|
||||||
instruction_args.push_back(arg);
|
|
||||||
interpret_instruction(current_section, instruction_name, instruction_args, instr_begins_at, line_n);
|
|
||||||
}
|
|
||||||
|
|
||||||
void dsb_interpreter::interpret_instruction(dsb_section_type current_section, const std::string& name, const std::vector<std::string>& args, std::size_t instr_begins_at, std::size_t line_n)
|
|
||||||
{
|
|
||||||
dsb_instruction* instr = nullptr;
|
|
||||||
std::vector<float> args_float(args.size(), 0);
|
|
||||||
std::stringstream stream;
|
|
||||||
for(const std::string& arg : args)
|
|
||||||
{
|
|
||||||
stream << arg;
|
|
||||||
float val = 0;
|
|
||||||
stream >> val;
|
|
||||||
args_float.push_back(val);
|
|
||||||
}
|
|
||||||
|
|
||||||
#define MAP_INSTRUCTION(section, from, to, min_num_args, ...) \
|
|
||||||
if(current_section == section && name == from) \
|
|
||||||
{ \
|
|
||||||
if(args.size() < min_num_args) \
|
|
||||||
{ \
|
|
||||||
set_error(line_n, instr_begins_at, "Instruction " + name + " requires at least " + std::to_string(min_num_args) + " arguments.", current_section, "dsb_interpreter::interpret_instruction"); \
|
|
||||||
return; \
|
|
||||||
} \
|
|
||||||
instr = new to(__VA_ARGS__); \
|
|
||||||
}
|
|
||||||
|
|
||||||
#define IGNORE_INSTRUCTION(section, instr_name) \
|
|
||||||
if(current_section == section && name == instr_name) \
|
|
||||||
{ \
|
|
||||||
std::cout << "[openrayman::dsb_interpreter] Warning! Ignored instruction with name " << instr_name << std::endl; \
|
|
||||||
return; \
|
|
||||||
}
|
|
||||||
|
|
||||||
// dir
|
|
||||||
MAP_INSTRUCTION(dsb_section_type::data_directories,
|
|
||||||
"dir",
|
|
||||||
dsb_instruction_set_data_dir,
|
|
||||||
2, string_to_data_directory(args[0]), args[1]);
|
|
||||||
|
|
||||||
// add
|
|
||||||
MAP_INSTRUCTION(dsb_section_type::texture_files,
|
|
||||||
"add",
|
|
||||||
dsb_instruction_set_texture_file,
|
|
||||||
2, args[0] == "vignettes" ? dsb_texture_file_id::vignettes : dsb_texture_file_id::textures, args[1]);
|
|
||||||
|
|
||||||
// load_img
|
|
||||||
MAP_INSTRUCTION(dsb_section_type::vignette,
|
|
||||||
"load_img",
|
|
||||||
dsb_instruction_vignette_load_img,
|
|
||||||
1, args[0]);
|
|
||||||
|
|
||||||
// color
|
|
||||||
MAP_INSTRUCTION(dsb_section_type::vignette,
|
|
||||||
"color",
|
|
||||||
dsb_instruction_vignette_set_color,
|
|
||||||
5, string_to_vignette_id(args[0]),
|
|
||||||
glm::vec4(args_float[1] / 255, args_float[2] / 255, args_float[3] / 255, args_float[4] / 255),
|
|
||||||
args.size() > 5 ? glm::vec4(args_float[5] / 255, args_float[6] / 255, args_float[7] / 255, args_float[8] / 255) : glm::vec4(0, 0, 0, 0),
|
|
||||||
args.size() > 5 ? glm::vec4(args_float[9] / 255, args_float[10] / 255, args_float[11] / 255, args_float[12] / 255) : glm::vec4(0, 0, 0, 0),
|
|
||||||
args.size() > 5 ? glm::vec4(args_float[13] / 255, args_float[14] / 255, args_float[15] / 255, args_float[16] / 255) : glm::vec4(0, 0, 0, 0));
|
|
||||||
|
|
||||||
// create_bar
|
|
||||||
MAP_INSTRUCTION(dsb_section_type::vignette,
|
|
||||||
"create_bar",
|
|
||||||
dsb_instruction_vignette_create_bar,
|
|
||||||
4, args_float[0], args_float[1], args_float[2], args_float[3]);
|
|
||||||
|
|
||||||
// level_add
|
|
||||||
MAP_INSTRUCTION(dsb_section_type::levels,
|
|
||||||
"add",
|
|
||||||
dsb_instruction_level_add,
|
|
||||||
1, args[0]);
|
|
||||||
|
|
||||||
// TODO: some of these at least
|
|
||||||
IGNORE_INSTRUCTION(dsb_section_type::alloc, "alloc");
|
|
||||||
IGNORE_INSTRUCTION(dsb_section_type::alloc, "skip");
|
|
||||||
IGNORE_INSTRUCTION(dsb_section_type::vignette, "bar_max");
|
|
||||||
IGNORE_INSTRUCTION(dsb_section_type::vignette, "add_bar");
|
|
||||||
IGNORE_INSTRUCTION(dsb_section_type::vignette, "display");
|
|
||||||
IGNORE_INSTRUCTION(dsb_section_type::unknown_blob_0x20, "size");
|
|
||||||
IGNORE_INSTRUCTION(dsb_section_type::unknown_blob_0x20, "id_encounter");
|
|
||||||
IGNORE_INSTRUCTION(dsb_section_type::game_options, "default_file");
|
|
||||||
IGNORE_INSTRUCTION(dsb_section_type::game_options, "current_file");
|
|
||||||
IGNORE_INSTRUCTION(dsb_section_type::sound_banks, "unknown");
|
|
||||||
IGNORE_INSTRUCTION(dsb_section_type::load_sound_banks, "add_bank");
|
|
||||||
|
|
||||||
if(instr == nullptr)
|
|
||||||
set_error(line_n, instr_begins_at, "Invalid instruction (got \"" + name + "\" in section " + section_to_string(current_section) + ").", current_section, "dsb_interpreter::interpret_instruction");
|
|
||||||
else
|
|
||||||
m_interpreted_dsb.push_back(instr);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,87 +0,0 @@
|
||||||
#ifndef DSB_INTERPRETER_H
|
|
||||||
#define DSB_INTERPRETER_H
|
|
||||||
|
|
||||||
#include <dsb_interpreter/dsb_instruction.h>
|
|
||||||
#include <cstdlib>
|
|
||||||
#include <string>
|
|
||||||
#include <iostream>
|
|
||||||
#include <fstream>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
namespace openrayman
|
|
||||||
{
|
|
||||||
// DSB interpreter.
|
|
||||||
// Builds an array of instructions from a valid odsb file.
|
|
||||||
class dsb_interpreter
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
dsb_interpreter(const std::string& odsb_path);
|
|
||||||
|
|
||||||
~dsb_interpreter()
|
|
||||||
{
|
|
||||||
for(dsb_instruction* instruction : m_interpreted_dsb)
|
|
||||||
delete instruction;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If this DSB was interpreted successfully.
|
|
||||||
inline bool success() const
|
|
||||||
{
|
|
||||||
return section_count(dsb_instruction_type::invalid_dsb) == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns the number of times that the specified section type occurs in this DSB.
|
|
||||||
inline std::size_t section_count(dsb_instruction_type type) const
|
|
||||||
{
|
|
||||||
std::size_t count = 0;
|
|
||||||
for(dsb_instruction* instruction : m_interpreted_dsb)
|
|
||||||
{
|
|
||||||
if(instruction->type == type)
|
|
||||||
count++;
|
|
||||||
}
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns the first instance of the specified instruction type, or nullptr if one doesn't exist.
|
|
||||||
template<typename T>
|
|
||||||
inline T* get_instruction(dsb_instruction_type type) const
|
|
||||||
{
|
|
||||||
for(dsb_instruction* instruction : m_interpreted_dsb)
|
|
||||||
{
|
|
||||||
if(instruction->type == type)
|
|
||||||
return static_cast<T*>(instruction);
|
|
||||||
}
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns all instances of the specified instruction type.
|
|
||||||
template<typename T>
|
|
||||||
inline std::vector<T*> get_instructions(dsb_instruction_type type) const
|
|
||||||
{
|
|
||||||
std::vector<T*> values;
|
|
||||||
for(dsb_instruction* instruction : m_interpreted_dsb)
|
|
||||||
{
|
|
||||||
if(instruction->type == type)
|
|
||||||
values.push_back(static_cast<T*>(instruction));
|
|
||||||
}
|
|
||||||
return values;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns a reference to the raw instruction array.
|
|
||||||
// This can be manually searched and interpreted.
|
|
||||||
inline const std::vector<dsb_instruction*>& get_complete_instruction_array() const
|
|
||||||
{
|
|
||||||
return m_interpreted_dsb;
|
|
||||||
}
|
|
||||||
private:
|
|
||||||
void interpret_sections(std::ifstream& stream);
|
|
||||||
void interpret_line(dsb_section_type current_section, const std::string& line, std::size_t statement_begins_at, std::size_t line_n);
|
|
||||||
void interpret_instruction(dsb_section_type current_section, const std::string& name, const std::vector<std::string>& args, std::size_t func_begins_at, std::size_t line_n);
|
|
||||||
|
|
||||||
// Sets the error at the top of the dsb instruction array.
|
|
||||||
void set_error(std::size_t line, std::size_t column, const std::string& error, dsb_section_type current_section, const std::string& function);
|
|
||||||
|
|
||||||
std::vector<dsb_instruction*> m_interpreted_dsb;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,89 +0,0 @@
|
||||||
#include <dsb_interpreter/dsb_interpreter_debugger.h>
|
|
||||||
#include <dsb_interpreter/dsb_instruction.h>
|
|
||||||
#include <dsb_interpreter/instructions/all.h>
|
|
||||||
#include <glm/gtx/string_cast.hpp>
|
|
||||||
#include <iostream>
|
|
||||||
|
|
||||||
namespace openrayman
|
|
||||||
{
|
|
||||||
void dsb_interpreter_debugger::print_summary() const
|
|
||||||
{
|
|
||||||
if(m_interpreter.success())
|
|
||||||
{
|
|
||||||
std::cout << std::endl << "DSB interpreted -> "
|
|
||||||
<< m_interpreter.get_complete_instruction_array().size() << " instructions emitted" << std::endl << std::endl;
|
|
||||||
for(dsb_instruction* instruction : m_interpreter.get_complete_instruction_array())
|
|
||||||
{
|
|
||||||
std::cout << " dsb_instruction_type::" << instruction_to_string(instruction->type) << std::endl;
|
|
||||||
switch(instruction->type)
|
|
||||||
{
|
|
||||||
case dsb_instruction_type::comment:
|
|
||||||
{
|
|
||||||
dsb_instruction_comment* cast = static_cast<dsb_instruction_comment*>(instruction);
|
|
||||||
std::cout << " comment -> \"" << cast->comment << "\"" << std::endl;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case dsb_instruction_type::begin_section:
|
|
||||||
{
|
|
||||||
dsb_instruction_begin_section* cast = static_cast<dsb_instruction_begin_section*>(instruction);
|
|
||||||
std::cout << " end -> dsb_section_type::" << section_to_string(cast->end) << std::endl;
|
|
||||||
std::cout << " begin -> dsb_section_type::" << section_to_string(cast->begin) << std::endl;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case dsb_instruction_type::set_data_dir:
|
|
||||||
{
|
|
||||||
dsb_instruction_set_data_dir* cast = static_cast<dsb_instruction_set_data_dir*>(instruction);
|
|
||||||
std::cout << " directory -> dsb_data_directory::" << data_directory_to_string(cast->directory) << std::endl;
|
|
||||||
std::cout << " path -> \"" << cast->path << "\"" << std::endl;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case dsb_instruction_type::set_texture_file:
|
|
||||||
{
|
|
||||||
dsb_instruction_set_texture_file* cast = static_cast<dsb_instruction_set_texture_file*>(instruction);
|
|
||||||
std::cout << " id -> dsb_texture_file_id::" << (cast->id == dsb_texture_file_id::vignettes ? "vignettes" : "textures") << std::endl;
|
|
||||||
std::cout << " archive -> \"" << cast->archive << "\"" << std::endl;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case dsb_instruction_type::vignette_load_img:
|
|
||||||
{
|
|
||||||
dsb_instruction_vignette_load_img* cast = static_cast<dsb_instruction_vignette_load_img*>(instruction);
|
|
||||||
std::cout << " image -> \"" << cast->image << "\"" << std::endl;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case dsb_instruction_type::vignette_set_color:
|
|
||||||
{
|
|
||||||
dsb_instruction_vignette_set_color* cast = static_cast<dsb_instruction_vignette_set_color*>(instruction);
|
|
||||||
std::cout << " id -> dsb_vignette_id::" << vignette_id_to_string(cast->id) << std::endl;
|
|
||||||
std::cout << " color -> [" << cast->color.r << ", " << cast->color.g << ", " << cast->color.b << ", " << cast->color.a << "]" << std::endl;
|
|
||||||
std::cout << " color2 -> [" << cast->color2.r << ", " << cast->color2.g << ", " << cast->color2.b << ", " << cast->color2.a << "]" << std::endl;
|
|
||||||
std::cout << " color3 -> [" << cast->color3.r << ", " << cast->color3.g << ", " << cast->color3.b << ", " << cast->color3.a << "]" << std::endl;
|
|
||||||
std::cout << " color4 -> [" << cast->color4.r << ", " << cast->color4.g << ", " << cast->color4.b << ", " << cast->color4.a << "]" << std::endl;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case dsb_instruction_type::vignette_create_bar:
|
|
||||||
{
|
|
||||||
dsb_instruction_vignette_create_bar* cast = static_cast<dsb_instruction_vignette_create_bar*>(instruction);
|
|
||||||
std::cout << " pos -> [" << cast->x << ", " << cast->y << ", " << cast->w << ", " << cast->h << "]" << std::endl;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case dsb_instruction_type::level_add:
|
|
||||||
{
|
|
||||||
dsb_instruction_level_add* cast = static_cast<dsb_instruction_level_add*>(instruction);
|
|
||||||
std::cout << " level -> \"" << cast->level << "\"" << std::endl;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
std::cout << std::endl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
std::cout << "Operation failed" << std::endl;
|
|
||||||
dsb_instruction_invalid_dsb* error_reason = m_interpreter.get_instruction<dsb_instruction_invalid_dsb>
|
|
||||||
(dsb_instruction_type::invalid_dsb);
|
|
||||||
std::cout << "At " << error_reason->line << " : " << error_reason->column << std::endl;
|
|
||||||
std::cout << error_reason->error << std::endl;
|
|
||||||
std::cout << error_reason->trace << std::endl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,23 +0,0 @@
|
||||||
#ifndef DSB_INTERPRETER_DEBUGGER_H
|
|
||||||
#define DSB_INTERPRETER_DEBUGGER_H
|
|
||||||
|
|
||||||
#include <dsb_interpreter/dsb_interpreter.h>
|
|
||||||
|
|
||||||
namespace openrayman
|
|
||||||
{
|
|
||||||
// Used for printing some information about an interpreted DSB.
|
|
||||||
class dsb_interpreter_debugger
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
dsb_interpreter_debugger(const dsb_interpreter& interpreter) :
|
|
||||||
m_interpreter(interpreter)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void print_summary() const;
|
|
||||||
private:
|
|
||||||
const dsb_interpreter& m_interpreter;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,83 +0,0 @@
|
||||||
#ifndef DSB_INTERPRETER_STRING_UTILS_H
|
|
||||||
#define DSB_INTERPRETER_STRING_UTILS_H
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
namespace openrayman
|
|
||||||
{
|
|
||||||
// Reads ahead in the string and returns true if a match is found.
|
|
||||||
bool read_ahead_match(const std::string& source, std::size_t begin_at, const std::string& match)
|
|
||||||
{
|
|
||||||
if((int)source.length() - (int)begin_at + (int)match.length() < 0)
|
|
||||||
return false;
|
|
||||||
for(std::size_t n = 0; n < match.length(); n++)
|
|
||||||
{
|
|
||||||
if(source[n + begin_at] != match[n])
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns the difference needed to skip whitespace from the point "at".
|
|
||||||
std::size_t skip_whitespace(const std::string& source, std::size_t at)
|
|
||||||
{
|
|
||||||
std::size_t new_at = at;
|
|
||||||
while((source[new_at] == ' ' || source[new_at] == '\t') && new_at < source.length())
|
|
||||||
new_at++;
|
|
||||||
return new_at - at;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns the string in the range from point "at" to whitespace.
|
|
||||||
std::string read_until_whitespace(const std::string& source, std::size_t at)
|
|
||||||
{
|
|
||||||
std::string result = "";
|
|
||||||
while((source[at] != ' ' && source[at] != '\t') && at < source.length())
|
|
||||||
{
|
|
||||||
result += source[at];
|
|
||||||
at++;
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns the string from point "at" until the specified character is found.
|
|
||||||
std::string read_until_char(const std::string& source, std::size_t at, char until)
|
|
||||||
{
|
|
||||||
std::string result = "";
|
|
||||||
while((source[at] != until) && at < source.length())
|
|
||||||
{
|
|
||||||
result += source[at];
|
|
||||||
at++;
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns the string in the range from point "at" to end.
|
|
||||||
std::string read_until_end(const std::string& source, std::size_t at)
|
|
||||||
{
|
|
||||||
std::string result = "";
|
|
||||||
while(at < source.length())
|
|
||||||
{
|
|
||||||
result += source[at];
|
|
||||||
at++;
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reads a quote escaped string that starts at point "at".
|
|
||||||
std::string read_quote_escaped_string(const std::string& source, std::size_t at)
|
|
||||||
{
|
|
||||||
std::string result = "";
|
|
||||||
at++;
|
|
||||||
bool next_escaped = false;
|
|
||||||
while(at < source.length())
|
|
||||||
{
|
|
||||||
if(source[at] == '"')
|
|
||||||
break;
|
|
||||||
result += source[at];
|
|
||||||
at++;
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,9 +0,0 @@
|
||||||
#include <dsb_interpreter/instructions/begin_section/dsb_instruction_begin_section.h>
|
|
||||||
#include <dsb_interpreter/instructions/comment/dsb_instruction_comment.h>
|
|
||||||
#include <dsb_interpreter/instructions/invalid_dsb/dsb_instruction_invalid_dsb.h>
|
|
||||||
#include <dsb_interpreter/instructions/level_add/dsb_instruction_level_add.h>
|
|
||||||
#include <dsb_interpreter/instructions/set_data_dir/dsb_instruction_set_data_dir.h>
|
|
||||||
#include <dsb_interpreter/instructions/set_texture_file/dsb_instruction_set_texture_file.h>
|
|
||||||
#include <dsb_interpreter/instructions/vignette/create_bar/dsb_instruction_vignette_create_bar.h>
|
|
||||||
#include <dsb_interpreter/instructions/vignette/load_img/dsb_instruction_vignette_load_img.h>
|
|
||||||
#include <dsb_interpreter/instructions/vignette/set_color/dsb_instruction_vignette_set_color.h>
|
|
|
@ -1,24 +0,0 @@
|
||||||
#ifndef DSB_INSTRUCTION_BEGIN_SECTION_H
|
|
||||||
#define DSB_INSTRUCTION_BEGIN_SECTION_H
|
|
||||||
|
|
||||||
#include <dsb_interpreter/dsb_instruction.h>
|
|
||||||
|
|
||||||
namespace openrayman
|
|
||||||
{
|
|
||||||
struct dsb_instruction_begin_section : public dsb_instruction
|
|
||||||
{
|
|
||||||
dsb_instruction_begin_section(dsb_section_type end, dsb_section_type begin) :
|
|
||||||
dsb_instruction(dsb_instruction_type::begin_section),
|
|
||||||
end(end), begin(begin)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
// The previous section that is ending.
|
|
||||||
dsb_section_type end;
|
|
||||||
|
|
||||||
// The section that is following this instruction.
|
|
||||||
dsb_section_type begin;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,24 +0,0 @@
|
||||||
#ifndef DSB_INSTRUCTION_COMMENT_H
|
|
||||||
#define DSB_INSTRUCTION_COMMENT_H
|
|
||||||
|
|
||||||
#include <dsb_interpreter/dsb_instruction.h>
|
|
||||||
|
|
||||||
namespace openrayman
|
|
||||||
{
|
|
||||||
struct dsb_instruction_comment : public dsb_instruction
|
|
||||||
{
|
|
||||||
dsb_instruction_comment(std::size_t line, const std::string& comment) :
|
|
||||||
dsb_instruction(dsb_instruction_type::comment),
|
|
||||||
line(line), comment(comment)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
// The line that the interpreter encountered the comment.
|
|
||||||
std::size_t line;
|
|
||||||
|
|
||||||
// The user specified comment that was found.
|
|
||||||
std::string comment;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,30 +0,0 @@
|
||||||
#ifndef DSB_INSTRUCTION_INVALID_DSB_H
|
|
||||||
#define DSB_INSTRUCTION_INVALID_DSB_H
|
|
||||||
|
|
||||||
#include <dsb_interpreter/dsb_instruction.h>
|
|
||||||
|
|
||||||
namespace openrayman
|
|
||||||
{
|
|
||||||
struct dsb_instruction_invalid_dsb : public dsb_instruction
|
|
||||||
{
|
|
||||||
dsb_instruction_invalid_dsb(std::size_t line, std::size_t column, const std::string& error, const std::string& trace) :
|
|
||||||
dsb_instruction(dsb_instruction_type::invalid_dsb),
|
|
||||||
line(line), column(column), error(error), trace(trace)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
// The line that the interpreter encountered the error.
|
|
||||||
std::size_t line;
|
|
||||||
|
|
||||||
// The column that the interpreter encountered the error.
|
|
||||||
std::size_t column;
|
|
||||||
|
|
||||||
// The error that the interpreter encountered.
|
|
||||||
std::string error;
|
|
||||||
|
|
||||||
// Trace info about the error.
|
|
||||||
std::string trace;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,21 +0,0 @@
|
||||||
#ifndef DSB_INSTRUCTION_LEVEL_ADD_H
|
|
||||||
#define DSB_INSTRUCTION_LEVEL_ADD_H
|
|
||||||
|
|
||||||
#include <dsb_interpreter/dsb_instruction.h>
|
|
||||||
|
|
||||||
namespace openrayman
|
|
||||||
{
|
|
||||||
struct dsb_instruction_level_add : public dsb_instruction
|
|
||||||
{
|
|
||||||
dsb_instruction_level_add(const std::string& level) :
|
|
||||||
dsb_instruction(dsb_instruction_type::level_add),
|
|
||||||
level(level)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
// The level name, relative to the levels folder.
|
|
||||||
std::string level;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,92 +0,0 @@
|
||||||
#ifndef DSB_INSTRUCTION_SET_DATA_DIR_H
|
|
||||||
#define DSB_INSTRUCTION_SET_DATA_DIR_H
|
|
||||||
|
|
||||||
#include <dsb_interpreter/dsb_instruction.h>
|
|
||||||
|
|
||||||
namespace openrayman
|
|
||||||
{
|
|
||||||
enum class dsb_data_directory
|
|
||||||
{
|
|
||||||
// Special value used to specify that no directory could be provided.
|
|
||||||
none,
|
|
||||||
|
|
||||||
// Specifies the directory where engine dlls are located.
|
|
||||||
dll,
|
|
||||||
|
|
||||||
// Specifies the root of game data.
|
|
||||||
// This seems to be incorrect in Rayman 2?!?
|
|
||||||
root,
|
|
||||||
|
|
||||||
// Specifies the directory where data related to the game world is located.
|
|
||||||
world,
|
|
||||||
|
|
||||||
// Specifies the directory where levels are located.
|
|
||||||
levels,
|
|
||||||
|
|
||||||
// Specifies the directory where sound files are located.
|
|
||||||
sound,
|
|
||||||
|
|
||||||
// Specifies the directory where save files are located.
|
|
||||||
saves,
|
|
||||||
|
|
||||||
// Specifies the directory where "vignettes" are located.
|
|
||||||
// Vignettes are 2D textures that are used in loading screens.
|
|
||||||
vignette,
|
|
||||||
|
|
||||||
// Specifies the directory where user options are saved.
|
|
||||||
options
|
|
||||||
};
|
|
||||||
|
|
||||||
inline std::string data_directory_to_string(dsb_data_directory dir)
|
|
||||||
{
|
|
||||||
#define VALID_DIRECTORY_TO(name) \
|
|
||||||
if(dir == dsb_data_directory::name) \
|
|
||||||
return #name;
|
|
||||||
|
|
||||||
VALID_DIRECTORY_TO(dll);
|
|
||||||
VALID_DIRECTORY_TO(root);
|
|
||||||
VALID_DIRECTORY_TO(world);
|
|
||||||
VALID_DIRECTORY_TO(levels);
|
|
||||||
VALID_DIRECTORY_TO(sound);
|
|
||||||
VALID_DIRECTORY_TO(saves);
|
|
||||||
VALID_DIRECTORY_TO(vignette);
|
|
||||||
VALID_DIRECTORY_TO(options);
|
|
||||||
|
|
||||||
return "none";
|
|
||||||
}
|
|
||||||
|
|
||||||
inline dsb_data_directory string_to_data_directory(const std::string& dir)
|
|
||||||
{
|
|
||||||
#define VALID_DIRECTORY_FROM(name) \
|
|
||||||
if(dir == #name) \
|
|
||||||
return dsb_data_directory::name;
|
|
||||||
|
|
||||||
VALID_DIRECTORY_FROM(dll);
|
|
||||||
VALID_DIRECTORY_FROM(root);
|
|
||||||
VALID_DIRECTORY_FROM(world);
|
|
||||||
VALID_DIRECTORY_FROM(levels);
|
|
||||||
VALID_DIRECTORY_FROM(sound);
|
|
||||||
VALID_DIRECTORY_FROM(saves);
|
|
||||||
VALID_DIRECTORY_FROM(vignette);
|
|
||||||
VALID_DIRECTORY_FROM(options);
|
|
||||||
|
|
||||||
return dsb_data_directory::none;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct dsb_instruction_set_data_dir : public dsb_instruction
|
|
||||||
{
|
|
||||||
dsb_instruction_set_data_dir(dsb_data_directory directory, const std::string& path) :
|
|
||||||
dsb_instruction(dsb_instruction_type::set_data_dir),
|
|
||||||
directory(directory), path(path)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
// The data directory to set.
|
|
||||||
dsb_data_directory directory;
|
|
||||||
|
|
||||||
// The path which it should be set to.
|
|
||||||
std::string path;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,33 +0,0 @@
|
||||||
#ifndef DSB_INSTRUCTION_SET_TEXTURE_FILE_H
|
|
||||||
#define DSB_INSTRUCTION_SET_TEXTURE_FILE_H
|
|
||||||
|
|
||||||
#include <dsb_interpreter/dsb_instruction.h>
|
|
||||||
|
|
||||||
namespace openrayman
|
|
||||||
{
|
|
||||||
enum class dsb_texture_file_id
|
|
||||||
{
|
|
||||||
// Stores vignettes.
|
|
||||||
vignettes,
|
|
||||||
|
|
||||||
// Stores all other (world, etc) textures.
|
|
||||||
textures
|
|
||||||
};
|
|
||||||
|
|
||||||
struct dsb_instruction_set_texture_file : public dsb_instruction
|
|
||||||
{
|
|
||||||
dsb_instruction_set_texture_file(dsb_texture_file_id id, const std::string& archive) :
|
|
||||||
dsb_instruction(dsb_instruction_type::set_texture_file),
|
|
||||||
id(id), archive(archive)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
// The texture file to set.
|
|
||||||
dsb_texture_file_id id;
|
|
||||||
|
|
||||||
// The archive which it should be set to.
|
|
||||||
std::string archive;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,20 +0,0 @@
|
||||||
#ifndef DSB_INSTRUCTION_VIGNETTE_CREATE_BAR_H
|
|
||||||
#define DSB_INSTRUCTION_VIGNETTE_CREATE_BAR_H
|
|
||||||
|
|
||||||
#include <dsb_interpreter/dsb_instruction.h>
|
|
||||||
|
|
||||||
namespace openrayman
|
|
||||||
{
|
|
||||||
struct dsb_instruction_vignette_create_bar : public dsb_instruction
|
|
||||||
{
|
|
||||||
dsb_instruction_vignette_create_bar(int x, int y, int w, int h) :
|
|
||||||
dsb_instruction(dsb_instruction_type::vignette_create_bar),
|
|
||||||
x(x), y(y), w(w), h(h)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
int x, y, w, h;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,20 +0,0 @@
|
||||||
#ifndef DSB_INSTRUCTION_VIGNETTE_LOAD_IMG_H
|
|
||||||
#define DSB_INSTRUCTION_VIGNETTE_LOAD_IMG_H
|
|
||||||
|
|
||||||
#include <dsb_interpreter/dsb_instruction.h>
|
|
||||||
|
|
||||||
namespace openrayman
|
|
||||||
{
|
|
||||||
struct dsb_instruction_vignette_load_img : public dsb_instruction
|
|
||||||
{
|
|
||||||
dsb_instruction_vignette_load_img(const std::string& image) :
|
|
||||||
dsb_instruction(dsb_instruction_type::vignette_load_img),
|
|
||||||
image(image)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string image;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,62 +0,0 @@
|
||||||
#ifndef DSB_INSTRUCTION_VIGNETTE_SET_COLOR_H
|
|
||||||
#define DSB_INSTRUCTION_VIGNETTE_SET_COLOR_H
|
|
||||||
|
|
||||||
#include <dsb_interpreter/dsb_instruction.h>
|
|
||||||
#include <cstdint>
|
|
||||||
#include <glm/glm.hpp>
|
|
||||||
|
|
||||||
namespace openrayman
|
|
||||||
{
|
|
||||||
enum class dsb_vignette_id
|
|
||||||
{
|
|
||||||
none,
|
|
||||||
// TODO: document these
|
|
||||||
outline,
|
|
||||||
inside,
|
|
||||||
bar
|
|
||||||
};
|
|
||||||
|
|
||||||
inline std::string vignette_id_to_string(dsb_vignette_id id)
|
|
||||||
{
|
|
||||||
#define VALID_ID_TO(name) \
|
|
||||||
if(id == dsb_vignette_id::name) \
|
|
||||||
return #name;
|
|
||||||
|
|
||||||
VALID_ID_TO(outline);
|
|
||||||
VALID_ID_TO(inside);
|
|
||||||
VALID_ID_TO(bar);
|
|
||||||
|
|
||||||
return "none";
|
|
||||||
}
|
|
||||||
|
|
||||||
inline dsb_vignette_id string_to_vignette_id(const std::string& id)
|
|
||||||
{
|
|
||||||
#define VALID_ID_FROM(name) \
|
|
||||||
if(id == #name) \
|
|
||||||
return dsb_vignette_id::name;
|
|
||||||
|
|
||||||
VALID_ID_FROM(outline);
|
|
||||||
VALID_ID_FROM(inside);
|
|
||||||
VALID_ID_FROM(bar);
|
|
||||||
|
|
||||||
return dsb_vignette_id::none;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct dsb_instruction_vignette_set_color : public dsb_instruction
|
|
||||||
{
|
|
||||||
dsb_instruction_vignette_set_color(dsb_vignette_id id,
|
|
||||||
glm::vec4 color, glm::vec4 color2, glm::vec4 color3, glm::vec4 color4) :
|
|
||||||
dsb_instruction(dsb_instruction_type::vignette_set_color),
|
|
||||||
color(color), color2(color2), color3(color3), color4(color4)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
// The id of the color to set.
|
|
||||||
dsb_vignette_id id;
|
|
||||||
|
|
||||||
// Color gradient, all are only used when id is "bar".
|
|
||||||
glm::vec4 color, color2, color3, color4;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -103,18 +103,16 @@ namespace openrayman
|
||||||
if(m_current_input.command(input_command::toggle_fullscreen) && !m_last_input.command(input_command::toggle_fullscreen))
|
if(m_current_input.command(input_command::toggle_fullscreen) && !m_last_input.command(input_command::toggle_fullscreen))
|
||||||
m_config.fullscreen = !m_config.fullscreen;
|
m_config.fullscreen = !m_config.fullscreen;
|
||||||
|
|
||||||
std::cout << "[openrayman::engine] Update: " << m_current_delta_time * 1000 << "ms, " << m_total_frames << std::endl;
|
std::cout << "[openrayman::engine] FPS: " << get_fps() << std::endl;
|
||||||
m_total_frames++;
|
m_total_frames++;
|
||||||
m_accumulated_frames_fps++;
|
m_accumulated_frames_fps++;
|
||||||
if(m_accumulated_time_fps >= 1)
|
if(m_accumulated_time_fps >= 1)
|
||||||
{
|
{
|
||||||
m_fps = m_accumulated_frames_fps;
|
m_fps = m_accumulated_frames_fps;
|
||||||
std::cout << "[openrayman::engine] FPS: " << m_fps << std::endl;
|
|
||||||
m_accumulated_time_fps = m_accumulated_frames_fps = 0;
|
m_accumulated_time_fps = m_accumulated_frames_fps = 0;
|
||||||
}
|
}
|
||||||
while(m_accumulated_time_fixed >= 1 / 60.0)
|
while(m_accumulated_time_fixed >= 1 / 60.0)
|
||||||
{
|
{
|
||||||
std::cout << "[openrayman::engine] Fixed update: " << m_total_fixed_updates << std::endl;
|
|
||||||
m_total_fixed_updates++;
|
m_total_fixed_updates++;
|
||||||
m_accumulated_time_fixed -= 1 / 60.0;
|
m_accumulated_time_fixed -= 1 / 60.0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -117,10 +117,11 @@ public:
|
||||||
return m_total_fixed_updates;
|
return m_total_fixed_updates;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns the amount of frames that were executed the previous second.
|
// Returns the amount of frames that were executed during the previous second.
|
||||||
inline std::uint64_t get_fps() const
|
inline double get_fps() const
|
||||||
{
|
{
|
||||||
return m_fps;
|
// this is more accurate
|
||||||
|
return (m_fps + (1 / m_current_delta_time)) / 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns a reference to the active config.
|
// Returns a reference to the active config.
|
||||||
|
|
|
@ -82,15 +82,19 @@ namespace openrayman
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::string game::find_file(const std::string& file) const
|
const std::string game::find_file(const std::string& file, bool show_error_msg) const
|
||||||
{
|
{
|
||||||
if(has_file(file, true))
|
if(has_file(file, true))
|
||||||
return m_location + "/" + file;
|
return m_location + "/" + file;
|
||||||
for(const game& game : m_dependencies)
|
for(const game& game : m_dependencies)
|
||||||
{
|
{
|
||||||
if(game.has_file(file, false))
|
if(game.has_file(file, false))
|
||||||
return game.find_file(file);
|
return game.find_file(file, show_error_msg);
|
||||||
}
|
}
|
||||||
|
if(show_error_msg)
|
||||||
|
message_box::display("[openrayman::game] Error!", "The file \"" + file + "\" could not be found in the game \"" + m_info.name + "\"."
|
||||||
|
"\nor any of its dependencies."
|
||||||
|
"\nThis could be the result of a broken dependency.", true);
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,7 +53,7 @@ public:
|
||||||
// Dependencies are searched from first to last specified in the manifest.
|
// Dependencies are searched from first to last specified in the manifest.
|
||||||
// An empty string is returned if the file was not found.
|
// An empty string is returned if the file was not found.
|
||||||
// File existence can be checked via the function has_file.
|
// File existence can be checked via the function has_file.
|
||||||
const std::string find_file(const std::string& file) const;
|
const std::string find_file(const std::string& file, bool show_error_msg) const;
|
||||||
private:
|
private:
|
||||||
bool m_valid;
|
bool m_valid;
|
||||||
std::string m_location;
|
std::string m_location;
|
||||||
|
|
80
src/main.cc
80
src/main.cc
|
@ -1,32 +1,13 @@
|
||||||
#include <info.h>
|
#include <info.h>
|
||||||
#include <engine.h>
|
#include <engine.h>
|
||||||
|
#include <tools/common_tools.h>
|
||||||
#include <data_extractor/dsb/dsb_decompiler.h>
|
#include <data_extractor/dsb/dsb_decompiler.h>
|
||||||
#include <dsb_interpreter/dsb_interpreter.h>
|
#include <tools/gf_tools.h>
|
||||||
#include <dsb_interpreter/dsb_interpreter_debugger.h>
|
#include <tools/cnt_tools.h>
|
||||||
#include <cstdlib>
|
#include <vector>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <locale>
|
#include <platform/file.h>
|
||||||
|
#include <platform/standalone_backend_specifics.h>
|
||||||
bool console_open = false;
|
|
||||||
|
|
||||||
void make_sure_console_open()
|
|
||||||
{
|
|
||||||
#ifdef _WIN32
|
|
||||||
if(!console_open)
|
|
||||||
{
|
|
||||||
if(!AttachConsole(-1))
|
|
||||||
AllocConsole();
|
|
||||||
freopen("CONOUT$", "w", stdout);
|
|
||||||
console_open = true;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
int fail_and_print(const std::string& msg)
|
|
||||||
{
|
|
||||||
make_sure_console_open(); std::cout << msg << std::endl;
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, char** argv)
|
int main(int argc, char** argv)
|
||||||
{
|
{
|
||||||
|
@ -80,7 +61,8 @@ int main(int argc, char** argv)
|
||||||
else
|
else
|
||||||
return fail_and_print("Operation failed");
|
return fail_and_print("Operation failed");
|
||||||
}
|
}
|
||||||
else
|
else if(format == "png")
|
||||||
|
return openrayman::gf_tools::convert_to_png(path, target);
|
||||||
return fail_and_print("Invalid format specified");
|
return fail_and_print("Invalid format specified");
|
||||||
}
|
}
|
||||||
if(str == "--inspect")
|
if(str == "--inspect")
|
||||||
|
@ -93,16 +75,33 @@ int main(int argc, char** argv)
|
||||||
if(n >= argc)
|
if(n >= argc)
|
||||||
return fail_and_print("No path was specified");
|
return fail_and_print("No path was specified");
|
||||||
std::string path(argv[n]);
|
std::string path(argv[n]);
|
||||||
if(format == "odsb")
|
if(format == "cnt")
|
||||||
{
|
return openrayman::cnt_tools::print_hierarchy(path);
|
||||||
openrayman::dsb_interpreter interpreter(path);
|
else if(format == "gf")
|
||||||
openrayman::dsb_interpreter_debugger debugger(interpreter);
|
return openrayman::gf_tools::print_info(path);
|
||||||
debugger.print_summary();
|
|
||||||
return interpreter.success() ? EXIT_SUCCESS : EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
return fail_and_print("Invalid format was specified");
|
return fail_and_print("Invalid format was specified");
|
||||||
}
|
}
|
||||||
|
if(str == "--force-reset-rayman2")
|
||||||
|
{
|
||||||
|
openrayman::standalone_backend_specifics backend_specifics;
|
||||||
|
openrayman::file::delete_directory(backend_specifics.get_data_path() + "/games/rayman2");
|
||||||
|
}
|
||||||
|
if(str == "--extract-cnt-to")
|
||||||
|
{
|
||||||
|
n++;
|
||||||
|
if(n >= argc)
|
||||||
|
return fail_and_print("No archive was specified");
|
||||||
|
std::string archive(argv[n]);
|
||||||
|
n++;
|
||||||
|
if(n >= argc)
|
||||||
|
return fail_and_print("No path was specified");
|
||||||
|
std::string path(argv[n]);
|
||||||
|
n++;
|
||||||
|
if(n >= argc)
|
||||||
|
return fail_and_print("No target was specified");
|
||||||
|
std::string target(argv[n]);
|
||||||
|
return openrayman::cnt_tools::extract(archive, path, target);
|
||||||
|
}
|
||||||
// Follow GNU format
|
// Follow GNU format
|
||||||
if(str == "--help")
|
if(str == "--help")
|
||||||
{
|
{
|
||||||
|
@ -117,12 +116,14 @@ int main(int argc, char** argv)
|
||||||
std::cout << " This can also be done by starting the game without extracted data" << std::endl;
|
std::cout << " This can also be done by starting the game without extracted data" << std::endl;
|
||||||
std::cout << " In that case, you will get a directory picker" << std::endl;
|
std::cout << " In that case, you will get a directory picker" << std::endl;
|
||||||
std::cout << " --convert-to \"format\" \"path\" \"target\" Converts the specified file into the target format" << std::endl;
|
std::cout << " --convert-to \"format\" \"path\" \"target\" Converts the specified file into the target format" << std::endl;
|
||||||
std::cout << " Format can be any of:" << std::endl;
|
std::cout << " \"odsb\": Decompiles the DSB into the specified directory (several files will be created)" << std::endl;
|
||||||
std::cout << " \"odsb\": Creates an OpenRayman DSB file" << std::endl;
|
|
||||||
std::cout << " \"rdsb\": Decodes a DSB file" << std::endl;
|
std::cout << " \"rdsb\": Decodes a DSB file" << std::endl;
|
||||||
std::cout << " --inspect \"format\" \"path\" Inspects and prints info about the specified file" << std::endl;
|
std::cout << " \"png\": Decodes a GF (graphics texture) file into a png file" << std::endl;
|
||||||
std::cout << " Format can be any of:" << std::endl;
|
std::cout << " --inspect \"format\" \"path\" Inspects and prints info about the specified file or directory" << std::endl;
|
||||||
std::cout << " \"odsb\": Interprets the DSB and outputs all instructions" << std::endl;
|
std::cout << " \"cnt\": Prints file hierarchy" << std::endl;
|
||||||
|
std::cout << " \"gf\": Prints width, height and number of channels" << std::endl;
|
||||||
|
std::cout << " --extract-cnt-to \"archive\" \"path\" \"target\" Extracts a file from a CNT archive" << std::endl;
|
||||||
|
std::cout << " --force-reset-rayman2 Forces a removal of the rayman2 base game, if it exists" << std::endl;
|
||||||
return EXIT_SUCCESS;
|
return EXIT_SUCCESS;
|
||||||
}
|
}
|
||||||
if(str == "--version")
|
if(str == "--version")
|
||||||
|
@ -146,7 +147,6 @@ int main(int argc, char** argv)
|
||||||
|
|
||||||
INT WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR lpCmdLine, INT nCmdShow)
|
INT WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR lpCmdLine, INT nCmdShow)
|
||||||
{
|
{
|
||||||
// fuck windows
|
|
||||||
return main(__argc, __argv);
|
return main(__argc, __argv);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
#include <shlwapi.h>
|
#include <shlwapi.h>
|
||||||
|
#include <shellapi.h>
|
||||||
#include <locale>
|
#include <locale>
|
||||||
#include <codecvt>
|
#include <codecvt>
|
||||||
#else
|
#else
|
||||||
|
@ -10,6 +11,8 @@
|
||||||
#include <libgen.h>
|
#include <libgen.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
|
#include <ftw.h>
|
||||||
|
#include <cstdio>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
namespace openrayman
|
namespace openrayman
|
||||||
|
@ -43,4 +46,33 @@ namespace openrayman
|
||||||
mkdir(fix_string(path).c_str(), 0775);
|
mkdir(fix_string(path).c_str(), 0775);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
|
// http://stackoverflow.com/questions/3184445/how-to-clear-directory-contents-in-c-on-linux-basically-i-want-to-do-rm-rf/3184915#3184915
|
||||||
|
int unlink_cb(const char *fpath, const struct stat *sb, int typeflag, struct FTW *ftwbuf)
|
||||||
|
{
|
||||||
|
int rv = remove(fpath);
|
||||||
|
if (rv)
|
||||||
|
perror(fpath);
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void file::delete_directory(const std::string& path)
|
||||||
|
{
|
||||||
|
#ifdef _WIN32
|
||||||
|
SHFILEOPSTRUCT fop;
|
||||||
|
fop.hwnd = nullptr;
|
||||||
|
fop.wFunc = FO_DELETE;
|
||||||
|
fop.pFrom = std::string(path + "\0").c_str();
|
||||||
|
fop.pTo = nullptr;
|
||||||
|
fop.fFlags = FOF_NOCONFIRMATION | FOF_SILENT | FOF_ALLOWUNDO;
|
||||||
|
fop.fAnyOperationsAborted = false;
|
||||||
|
fop.lpszProgressTitle = nullptr;
|
||||||
|
fop.hNameMappings = nullptr;
|
||||||
|
SHFileOperation(&fop);
|
||||||
|
#else
|
||||||
|
nftw(path.c_str(), &unlink_cb, 64, FTW_DEPTH | FTW_PHYS);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,9 @@ public:
|
||||||
// Creates a new directory with the specified path.
|
// Creates a new directory with the specified path.
|
||||||
static void create_directory(const std::string& path);
|
static void create_directory(const std::string& path);
|
||||||
|
|
||||||
|
// Deletes the directory with the specified path.
|
||||||
|
static void delete_directory(const std::string& path);
|
||||||
|
|
||||||
// The path separator of the current platform.
|
// The path separator of the current platform.
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
static const char path_separator = '\\';
|
static const char path_separator = '\\';
|
||||||
|
|
|
@ -30,6 +30,7 @@ namespace openrayman
|
||||||
GtkWidget* dialog = gtk_message_dialog_new(nullptr, flags, error ? GTK_MESSAGE_ERROR : GTK_MESSAGE_INFO, GTK_BUTTONS_OK, msg.c_str());
|
GtkWidget* dialog = gtk_message_dialog_new(nullptr, flags, error ? GTK_MESSAGE_ERROR : GTK_MESSAGE_INFO, GTK_BUTTONS_OK, msg.c_str());
|
||||||
gtk_window_set_title(GTK_WINDOW(dialog), title.c_str());
|
gtk_window_set_title(GTK_WINDOW(dialog), title.c_str());
|
||||||
gtk_dialog_run(GTK_DIALOG(dialog));
|
gtk_dialog_run(GTK_DIALOG(dialog));
|
||||||
|
gtk_widget_hide(dialog);
|
||||||
gtk_widget_destroy(dialog);
|
gtk_widget_destroy(dialog);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -53,6 +54,7 @@ namespace openrayman
|
||||||
GtkWidget* dialog = gtk_message_dialog_new(nullptr, flags, error ? GTK_MESSAGE_ERROR : GTK_MESSAGE_INFO, GTK_BUTTONS_YES_NO, msg.c_str());
|
GtkWidget* dialog = gtk_message_dialog_new(nullptr, flags, error ? GTK_MESSAGE_ERROR : GTK_MESSAGE_INFO, GTK_BUTTONS_YES_NO, msg.c_str());
|
||||||
gtk_window_set_title(GTK_WINDOW(dialog), title.c_str());
|
gtk_window_set_title(GTK_WINDOW(dialog), title.c_str());
|
||||||
gint result = gtk_dialog_run(GTK_DIALOG(dialog));
|
gint result = gtk_dialog_run(GTK_DIALOG(dialog));
|
||||||
|
gtk_widget_hide(dialog);
|
||||||
gtk_widget_destroy(dialog);
|
gtk_widget_destroy(dialog);
|
||||||
return result == GTK_RESPONSE_YES;
|
return result == GTK_RESPONSE_YES;
|
||||||
}
|
}
|
||||||
|
@ -99,9 +101,11 @@ namespace openrayman
|
||||||
char* directory_cstr = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
|
char* directory_cstr = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
|
||||||
std::string directory = std::string(directory_cstr);
|
std::string directory = std::string(directory_cstr);
|
||||||
g_free(directory_cstr);
|
g_free(directory_cstr);
|
||||||
|
gtk_widget_hide(dialog);
|
||||||
gtk_widget_destroy(dialog);
|
gtk_widget_destroy(dialog);
|
||||||
return directory;
|
return directory;
|
||||||
}
|
}
|
||||||
|
gtk_widget_hide(dialog);
|
||||||
gtk_widget_destroy(dialog);
|
gtk_widget_destroy(dialog);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
55
src/tools/cnt_tools.cc
Normal file
55
src/tools/cnt_tools.cc
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
#include <tools/cnt_tools.h>
|
||||||
|
#include <data_extractor/cnt/cnt_archive.h>
|
||||||
|
#include <tools/common_tools.h>
|
||||||
|
#include <cstdlib>
|
||||||
|
|
||||||
|
namespace openrayman
|
||||||
|
{
|
||||||
|
namespace cnt_tools
|
||||||
|
{
|
||||||
|
int extract(const std::string& path, const std::string& name_in, const std::string& target)
|
||||||
|
{
|
||||||
|
cnt_archive archive(path);
|
||||||
|
if(!archive.valid())
|
||||||
|
return fail_and_print("Operation failed");
|
||||||
|
cnt_file* file;
|
||||||
|
if((file = archive.get_archive_node().find_file(name_in)) == nullptr)
|
||||||
|
return fail_and_print("Operation failed");
|
||||||
|
std::ofstream output(target, std::ofstream::out | std::ofstream::binary);
|
||||||
|
if(!output.is_open())
|
||||||
|
return fail_and_print("Operation failed");
|
||||||
|
std::size_t at = 0;
|
||||||
|
while(at < file->size)
|
||||||
|
{
|
||||||
|
std::vector<std::uint8_t> read = file->read(at, 1024 * 4096);
|
||||||
|
output.write((char*)read.data(), read.size());
|
||||||
|
at += read.size();
|
||||||
|
if(read.size() == 0)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
void print_recursive(cnt_directory_node& node, int indent)
|
||||||
|
{
|
||||||
|
std::string indent_str(indent, ' ');
|
||||||
|
std::cout << indent_str << node.name << " -> " << std::endl;
|
||||||
|
for(cnt_file& file : node.get_local_files())
|
||||||
|
{
|
||||||
|
std::cout << indent_str << " " << file.name << " -> " << std::endl;
|
||||||
|
std::cout << indent_str << " " << " " << (file.size / 1024.0) << " KB" << std::endl;
|
||||||
|
}
|
||||||
|
for(cnt_directory_node& child : node.get_local_children())
|
||||||
|
print_recursive(child, indent + 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
int print_hierarchy(const std::string& path)
|
||||||
|
{
|
||||||
|
cnt_archive archive(path);
|
||||||
|
if(!archive.valid())
|
||||||
|
return fail_and_print("Operation failed");
|
||||||
|
print_recursive(archive.get_archive_node(), 0);
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
17
src/tools/cnt_tools.h
Normal file
17
src/tools/cnt_tools.h
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
#ifndef CNT_TOOLS_H
|
||||||
|
#define CNT_TOOLS_H
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace openrayman
|
||||||
|
{
|
||||||
|
// Tools for extracting from and printing information about CNT files.
|
||||||
|
// Used by main.cc
|
||||||
|
namespace cnt_tools
|
||||||
|
{
|
||||||
|
int extract(const std::string& path, const std::string& name_in, const std::string& target);
|
||||||
|
int print_hierarchy(const std::string& path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
35
src/tools/common_tools.h
Normal file
35
src/tools/common_tools.h
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
#ifndef COMMON_TOOLS_H
|
||||||
|
#define COMMON_TOOLS_H
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#include <windows.h>
|
||||||
|
#endif
|
||||||
|
#include <iostream>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
// Common tools used by main.cc and other tools
|
||||||
|
|
||||||
|
static bool console_open = false;
|
||||||
|
|
||||||
|
inline void make_sure_console_open()
|
||||||
|
{
|
||||||
|
#ifdef _WIN32
|
||||||
|
if(!console_open)
|
||||||
|
{
|
||||||
|
if(!AttachConsole(-1))
|
||||||
|
AllocConsole();
|
||||||
|
freopen("CONOUT$", "w", stdout);
|
||||||
|
console_open = true;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
inline int fail_and_print(const std::string& msg)
|
||||||
|
{
|
||||||
|
make_sure_console_open(); std::cout << msg << std::endl;
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
69
src/tools/gf_tools.cc
Normal file
69
src/tools/gf_tools.cc
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
#include <tools/gf_tools.h>
|
||||||
|
#include <data_extractor/gf/gf_converter.h>
|
||||||
|
#include <tools/common_tools.h>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <iostream>
|
||||||
|
#include <fstream>
|
||||||
|
#include <iomanip>
|
||||||
|
|
||||||
|
namespace openrayman
|
||||||
|
{
|
||||||
|
namespace gf_tools
|
||||||
|
{
|
||||||
|
int convert_to_png(const std::string& path, const std::string& target)
|
||||||
|
{
|
||||||
|
openrayman::gf_converter converter;
|
||||||
|
|
||||||
|
std::ifstream gf_stream(path, std::ifstream::in | std::ifstream::binary);
|
||||||
|
if(!gf_stream.is_open())
|
||||||
|
return fail_and_print("Operation failed");
|
||||||
|
|
||||||
|
gf_stream.seekg(0, std::ifstream::end);
|
||||||
|
std::size_t size = gf_stream.tellg();
|
||||||
|
gf_stream.seekg(0);
|
||||||
|
|
||||||
|
std::uint8_t gf_data[size];
|
||||||
|
gf_stream.read((char*)gf_data, size);
|
||||||
|
|
||||||
|
std::vector<std::uint8_t> vector(gf_data, gf_data + size);
|
||||||
|
std::vector<std::uint8_t> png_data = converter.convert_to_png(vector);
|
||||||
|
if(png_data.size() == 0)
|
||||||
|
return fail_and_print("Operation failed");
|
||||||
|
std::cout << std::endl;
|
||||||
|
std::ofstream png_stream(target, std::ofstream::out | std::ofstream::binary);
|
||||||
|
if(!png_stream.is_open())
|
||||||
|
return fail_and_print("Operation failed");
|
||||||
|
|
||||||
|
return png_stream.write((char*)png_data.data(), png_data.size()) ? EXIT_SUCCESS : EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
int print_info(const std::string& path)
|
||||||
|
{
|
||||||
|
openrayman::gf_converter converter;
|
||||||
|
|
||||||
|
std::ifstream gf_stream(path, std::ifstream::in | std::ifstream::binary);
|
||||||
|
if(!gf_stream.is_open())
|
||||||
|
return fail_and_print("Operation failed");
|
||||||
|
|
||||||
|
gf_stream.seekg(0, std::ifstream::end);
|
||||||
|
std::size_t size = gf_stream.tellg();
|
||||||
|
gf_stream.seekg(0);
|
||||||
|
|
||||||
|
std::uint8_t gf_data[size];
|
||||||
|
gf_stream.read((char*)gf_data, size);
|
||||||
|
|
||||||
|
std::size_t w, h;
|
||||||
|
std::uint8_t num_channels, repeat_byte;
|
||||||
|
std::vector<std::uint8_t> vector(gf_data, gf_data + size);
|
||||||
|
if(!converter.get_gf_info(vector, &w, &h, &num_channels, &repeat_byte))
|
||||||
|
return fail_and_print("Operation failed");
|
||||||
|
|
||||||
|
std::cout << "Width: " << w << std::endl;
|
||||||
|
std::cout << "Height: " << h << std::endl;
|
||||||
|
std::cout << "Channels: " << (num_channels == 4 ? "Blue, Green, Red, Alpha" : "Blue, Green, Red") << std::endl;
|
||||||
|
std::cout << "Repeat byte (for compression): 0x" << std::hex << std::setfill('0') << std::setw(2) << (std::int32_t)repeat_byte << std::endl;
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
17
src/tools/gf_tools.h
Normal file
17
src/tools/gf_tools.h
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
#ifndef GF_TOOLS_H
|
||||||
|
#define GF_TOOLS_H
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace openrayman
|
||||||
|
{
|
||||||
|
// Tools for converting and printing information about GF files.
|
||||||
|
// Used by main.cc
|
||||||
|
namespace gf_tools
|
||||||
|
{
|
||||||
|
int convert_to_png(const std::string& path, const std::string& target);
|
||||||
|
int print_info(const std::string& path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
Loading…
Reference in a new issue