diff --git a/CMakeLists.txt b/CMakeLists.txt index c30a4d2..42522af 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -30,5 +30,4 @@ endif() set(BuildTests OFF) add_subdirectory(lib/gl3w) add_subdirectory(lib/json) -add_subdirectory(lib/glm) add_subdirectory(src) diff --git a/data/games/rayman2_openrayman/manifest.json b/data/games/rayman2_openrayman/manifest.json index 8ea07cb..b0546af 100644 --- a/data/games/rayman2_openrayman/manifest.json +++ b/data/games/rayman2_openrayman/manifest.json @@ -1,5 +1,7 @@ { - "dependencies": ["rayman2"], + "dependencies": [ + "rayman2" + ], "info": { "description": "Modifications for OpenRayman", "name": "Rayman 2: The Great Escape (OpenRayman)" diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 8058777..9c48691 100755 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -11,7 +11,6 @@ add_executable(openrayman WIN32 ${SOURCES} "${CMAKE_SOURCE_DIR}/lib/lodepng/lode set(EXECUTABLE_OUTPUT_PATH "${CMAKE_SOURCE_DIR}/bin") target_link_libraries(openrayman gl3w) target_link_libraries(openrayman nlohmann_json) -target_link_libraries(openrayman glm) if(WIN32) target_link_libraries(openrayman shlwapi) diff --git a/src/data_extractor/cnt/cnt_archive.cc b/src/data_extractor/cnt/cnt_archive.cc new file mode 100644 index 0000000..c6989ea --- /dev/null +++ b/src/data_extractor/cnt/cnt_archive.cc @@ -0,0 +1,164 @@ +#include + +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 cnt_file::read(std::int32_t start, std::int32_t count) const + { + std::vector 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 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 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; + } +} diff --git a/src/data_extractor/cnt/cnt_archive.h b/src/data_extractor/cnt/cnt_archive.h new file mode 100644 index 0000000..e82dc24 --- /dev/null +++ b/src/data_extractor/cnt/cnt_archive.h @@ -0,0 +1,187 @@ +#ifndef CNT_ARCHIVE_H +#define CNT_ARCHIVE_H + +#include +#include +#include +#include +#include + +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 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 get_children() + { + std::vector values; + for(cnt_directory_node& node : m_children) + { + values.push_back(&node); + std::vector 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 get_files() + { + std::vector 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& get_local_children() + { + return m_children; + } + + // Returns a reference to all files that are explicit children of this node. + inline std::vector& 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 m_children; + std::vector m_files; + }; +} + +#endif diff --git a/src/data_extractor/data_extractor.cc b/src/data_extractor/data_extractor.cc index 8b68e29..68f9cc8 100644 --- a/src/data_extractor/data_extractor.cc +++ b/src/data_extractor/data_extractor.cc @@ -1,8 +1,7 @@ #include #include -#include -#include -#include +#include +#include #include #include #include @@ -13,11 +12,19 @@ namespace openrayman { bool data_extractor::extract(const std::string& install_folder) { - return - check_prerequisites(install_folder) && + if(check_prerequisites(install_folder) && create_base() && 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) @@ -81,22 +88,75 @@ namespace openrayman { std::cout << "[openrayman::data_extractor] Decompiling Game.dsb" << std::endl; 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 array; + array.reserve(file.size); + std::size_t at = 0; + while(at < file.size) + { + std::vector 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 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) { - dsb_interpreter interpreter(m_backend_specifics.get_data_path() + "/games/rayman2/game.odsb"); - if(interpreter.success()) + message_box::display("[openrayman::data_extractor] Info", "OpenRayman will now extract and convert all texture files used within the game." + "\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); + return false; } - dsb_instruction_invalid_dsb* error_reason = interpreter.get_instruction - (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; + 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; } } diff --git a/src/data_extractor/data_extractor.h b/src/data_extractor/data_extractor.h index 39eda97..a5cb8da 100644 --- a/src/data_extractor/data_extractor.h +++ b/src/data_extractor/data_extractor.h @@ -6,6 +6,8 @@ namespace openrayman { + class cnt_directory_node; + // Extracts data from a valid Rayman 2: The Great Escape installation. class data_extractor { @@ -22,6 +24,7 @@ private: bool check_prerequisites(const std::string& install_folder); bool create_base(); 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); const backend_specifics& m_backend_specifics; diff --git a/src/data_extractor/dsb/dsb_decompiler.cc b/src/data_extractor/dsb/dsb_decompiler.cc index a6e9dc3..49846e8 100644 --- a/src/data_extractor/dsb/dsb_decompiler.cc +++ b/src/data_extractor/dsb/dsb_decompiler.cc @@ -4,6 +4,7 @@ #include #include #include +#include namespace openrayman { @@ -31,15 +32,16 @@ namespace openrayman decoder.set_virtual_position(0); decoder.decode_array(buffer, length - 4); - std::ofstream target_stream(target, std::ofstream::out | std::ofstream::binary); - if(target_stream.is_open()) + if(fmt == dsb_format::rayman2_decoded) { - if(fmt == dsb_format::openrayman) - decompile_sections(buffer, length - 4, target_stream); - else + std::ofstream target_stream(target, std::ofstream::out | std::ofstream::binary); + if(target_stream.is_open()) target_stream.write(buffer, length - 4); - return true; + return target_stream.is_open(); } + else + decompile_sections(buffer, length - 4, target); + return true; } 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); std::istream in(&streambuf); std::int32_t id; @@ -64,15 +64,13 @@ namespace openrayman { #define DECOMPILE_SECTION(id, name, function) \ - case (id): \ + case id: \ { \ 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 << ") \"" \ - << (name) << "\"" << std::endl; \ - target << "section " << (name) << "\n"; \ - (function)(in, target); \ - target << "\n"; \ + << name << "\"" << std::endl; \ + function(in, target); \ break; \ } @@ -102,8 +100,7 @@ namespace openrayman } } - // this allocates variables?!?! - void dsb_decompiler::decompile_alloc(std::istream& source, std::ofstream& target) + void dsb_decompiler::decompile_alloc(std::istream& source, const std::string& target) { std::int32_t id; source.read((char*)&id, sizeof(std::int32_t)); @@ -111,30 +108,27 @@ namespace openrayman { if(id == 0x10) { - // skip(?, ?); - target << " skip("; std::int32_t a, b; source.read((char*)&a, sizeof(std::int32_t)); source.read((char*)&b, sizeof(std::int32_t)); - target << std::to_string(a) << ", " << std::to_string(b) << ")\n"; } else { - // alloc(slot, value); - target << " alloc(" << std::to_string(id) << ", "; std::int32_t a; source.read((char*)&a, sizeof(std::int32_t)); if(a == 0xFFFF) return; - target << std::to_string(a) << ")\n"; } source.read((char*)&id, sizeof(std::int32_t)); } } - // Adds levels - void dsb_decompiler::decompile_lvl_list(std::istream& source, std::ofstream& target) + void dsb_decompiler::decompile_lvl_list(std::istream& source, const std::string& 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; source.read((char*)&num_levels, sizeof(std::int32_t)); std::int32_t id; @@ -143,17 +137,21 @@ namespace openrayman { if(id == 0x1F) { - // add(name); - target << " add(\""; - push_string_with_length_u16(source, target); - target << " \")\n"; + std::string str = read_string_with_length_u16(source); + // levels have four spaced appended for some reason, trim them + str.erase(str.find_last_not_of(" \t") + 1); + // 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)); } + if(levels_file.is_open()) + levels_file << std::setw(4) << levels_json; } - // Data directories - void dsb_decompiler::decompile_data_directories(std::istream& source, std::ofstream& target) + void dsb_decompiler::decompile_data_directories(std::istream& source, const std::string& target) { std::int32_t id; source.read((char*)&id, sizeof(std::int32_t)); @@ -181,22 +179,15 @@ namespace openrayman source.read((char*)&str_length, sizeof(std::uint16_t)); char 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)); } } - // TODO: what is this?!? - // is this ever found?!?! - void dsb_decompiler::decompile_unknown_blob_0x20(std::istream& source, std::ofstream& target) + void dsb_decompiler::decompile_unknown_blob_0x20(std::istream& source, const std::string& target) { std::cout << "[openrayman::dsb_decompiler] Warning! encountered 0x20" << std::endl; std::uint32_t size; source.read((char*)&size, sizeof(std::uint32_t)); - target << " size(" << size << ")\n"; std::int32_t id; source.read((char*)&id, sizeof(std::int32_t)); std::unordered_map encountered_ids; @@ -207,13 +198,14 @@ namespace openrayman encountered_ids[id]++; source.read((char*)&id, sizeof(std::int32_t)); } - for(std::pair 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, std::ofstream& target) + void dsb_decompiler::decompile_vignette(std::istream& source, const std::string& 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; source.read((char*)&id, sizeof(std::int32_t)); while(id != 0xFFFF) @@ -223,14 +215,22 @@ namespace openrayman case 71: case 72: { - target << " load_img(\""; - push_string_with_length_u16(source, target); - target << "\")\n"; + std::string str = read_string_with_length_u16(source); + if(str.find_first_of("Random<") == 0) + { + // 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; } case 75: { - target << " display()\n"; + // display break; } case 76: @@ -243,74 +243,62 @@ namespace openrayman source.read((char*)&tmp, sizeof(std::int32_t)); 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); - target << " color(" - << (id == 76 ? "outline" : "inside") - << ", " << std::to_string(r) - << ", " << std::to_string(g) - << ", " << std::to_string(b) - << ", " << std::to_string(a) - << ")\n"; + if(id == 76) + vignette_json["bar"]["outside"] = { r, g, b, a }; + else + vignette_json["bar"]["inside"] = { r, g, b, a }; break; } case 78: { std::uint8_t colors[(sizeof(std::uint8_t) * 4) * 4]; source.read((char*)colors, (sizeof(std::uint8_t) * 4) * 4); - target << " color(bar"; - for(int n = 0; n < 4; n++) - { - target << ", " << std::to_string(colors[(n * 4)]) - << ", " << std::to_string(colors[(n * 4) + 1]) - << ", " << std::to_string(colors[(n * 4) + 2]) - << ", " << std::to_string(colors[(n * 4) + 3]); - } - target << ")\n"; + vignette_json["bar"]["main"] = nlohmann::json::object(); + // TODO: are these coordinates right?!? + vignette_json["bar"]["main"]["(x, -)"] = { colors[0], colors[1], colors[2], colors[3] }; + vignette_json["bar"]["main"]["(y, -)"] = { colors[4], colors[5], colors[6], colors[7] }; + vignette_json["bar"]["main"]["(x, +)"] = { colors[8], colors[9], colors[10], colors[11] }; + vignette_json["bar"]["main"]["(y, +)"] = { colors[12], colors[13], colors[14], colors[15] }; break; } case 79: { - std::int32_t values[4]; - source.read((char*)values, sizeof(std::int32_t) * 4); - target << " create_bar("; - for(int n = 0; n < 4; n++) - target << (n == 0 ? "" : ", ") << values[n]; - target << ")\n"; + std::int32_t vec[4]; + source.read((char*)vec, sizeof(std::int32_t) * 4); + vignette_json["bar"]["pos"] = { vec[0], vec[1], vec[2], vec[3] }; break; } case 80: { - target << " add_bar()\n"; + // add_bar break; } case 81: { std::int32_t max; source.read((char*)&max, sizeof(std::int32_t)); - target << " bar_max(" << max << ")\n"; + vignette_json["bar"]["max"] = max; break; } } 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; source.read((char*)&id, sizeof(std::int32_t)); while(id != 0xFFFF) { - if(id == 66) - target << " add(textures, \""; - else - target << " add(vignettes, \""; - push_string_with_length_u16(source, target); - target << "\")\n"; + read_string_with_length_u16(source); 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::uint8_t tmp = 0x00; @@ -320,7 +308,7 @@ namespace openrayman 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; source.read((char*)&id, sizeof(std::int32_t)); @@ -329,17 +317,9 @@ namespace openrayman switch(id) { case 0x65: - { - target << " default_file(\""; - push_string_with_length_u16(source, target); - target << "\")\n"; - break; - } case 0x66: { - target << " current_file(\""; - push_string_with_length_u16(source, target); - target << "\")\n"; + read_string_with_length_u16(source); break; } case 0x67: @@ -353,8 +333,7 @@ namespace openrayman } } - // idk what this is?!?! - void dsb_decompiler::decompile_sound_banks(std::istream& source, std::ofstream& target) + void dsb_decompiler::decompile_sound_banks(std::istream& source, const std::string& target) { std::int32_t id; source.read((char*)&id, sizeof(std::int32_t)); @@ -366,14 +345,12 @@ namespace openrayman { std::int32_t unknown; source.read((char*)&unknown, sizeof(std::int32_t)); - target << " unknown(" << unknown << ")\n"; } source.read((char*)&id, sizeof(std::int32_t)); } } - // idk what this is either?! - void dsb_decompiler::decompile_load_sound_banks(std::istream& source, std::ofstream& target) + void dsb_decompiler::decompile_load_sound_banks(std::istream& source, const std::string& target) { std::int32_t id; source.read((char*)&id, sizeof(std::int32_t)); @@ -383,7 +360,6 @@ namespace openrayman { std::int32_t bank; source.read((char*)&bank, sizeof(std::int32_t)); - target << " add_bank2(" << bank << ")\n"; } else { @@ -391,17 +367,14 @@ namespace openrayman source.read((char*)&size, sizeof(std::int32_t)); 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)); } } - 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; source.read(&c, 1); while(c != '\0') @@ -409,14 +382,15 @@ namespace openrayman target << c; 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; source.read((char*)&length, sizeof(std::uint16_t)); char str[length]; source.read(str, length); - target << str; + return std::string(str); } } diff --git a/src/data_extractor/dsb/dsb_decompiler.h b/src/data_extractor/dsb/dsb_decompiler.h index b5fec1f..08de546 100644 --- a/src/data_extractor/dsb/dsb_decompiler.h +++ b/src/data_extractor/dsb/dsb_decompiler.h @@ -13,7 +13,7 @@ namespace openrayman // The default format. // This is a more human readable/editable format than the binary format used by Rayman 2. // This can be interpreted by the dsb engine. - // Uses the extension .odsb + // Creates several files such as vignette.json, levels.json openrayman, // The format used by the Rayman 2: The Great Escape engine. @@ -25,34 +25,34 @@ namespace openrayman rayman2_decoded }; - // Decodes and optionally decompiles DSB files. + // Decodes and decompiles DSB files. // DSB files are compiled scripts, with different sections. // This is all heavily adapted from Rayman2Lib. class dsb_decompiler { public: - // Decodes and optionally decompiles the specified DSB file into the target file. - // This function assumes that the source file exists and that the target location is accessible. + // 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 directory is accessible. // Returns true if the function succeeded. bool decompile_dsb(const std::string& source, const std::string& target, dsb_format fmt); private: - void decompile_sections(char* source, std::size_t source_length, std::ofstream& target); - void decompile_alloc(std::istream& source, std::ofstream& target); - void decompile_lvl_list(std::istream& source, std::ofstream& target); - void decompile_data_directories(std::istream& source, std::ofstream& target); - void decompile_unknown_blob_0x20(std::istream& source, std::ofstream& target); - void decompile_vignette(std::istream& source, std::ofstream& target); - void decompile_texture_files(std::istream& source, std::ofstream& target); - void decompile_unknown_blob_0x6e(std::istream& source, std::ofstream& target); - void decompile_game_options(std::istream& source, std::ofstream& target); - void decompile_sound_banks(std::istream& source, std::ofstream& target); - void decompile_load_sound_banks(std::istream& source, std::ofstream& target); + void decompile_sections(char* source, std::size_t source_length, const std::string& target); + void decompile_alloc(std::istream& source, const std::string& target); + void decompile_lvl_list(std::istream& source, const std::string& target); + void decompile_data_directories(std::istream& source, const std::string& target); + void decompile_unknown_blob_0x20(std::istream& source, const std::string& target); + void decompile_vignette(std::istream& source, const std::string& target); + void decompile_texture_files(std::istream& source, const std::string& target); + void decompile_unknown_blob_0x6e(std::istream& source, const std::string& target); + void decompile_game_options(std::istream& source, const std::string& target); + void decompile_sound_banks(std::istream& source, const std::string& target); + void decompile_load_sound_banks(std::istream& source, const std::string& target); - // Reads a null-terminated string from source to target. - void push_string_null_terminated(std::istream& source, std::ofstream& target); + // Reads a null-terminated string from source. + const std::string read_string_null_terminated(std::istream& source); - // Reads the length of the string from source and then writes it to target. - void push_string_with_length_u16(std::istream& source, std::ofstream& target); + // Reads the length of the string from source. + const std::string read_string_with_length_u16(std::istream& source); data_decoder m_decoder; }; diff --git a/src/data_extractor/gf/gf_converter.cc b/src/data_extractor/gf/gf_converter.cc new file mode 100644 index 0000000..c7393bc --- /dev/null +++ b/src/data_extractor/gf/gf_converter.cc @@ -0,0 +1,131 @@ +#include +#include + +namespace openrayman +{ + struct memorybuf : std::streambuf + { + memorybuf(char* begin, char* end) + { + setg(begin, begin, end); + } + }; + + bool gf_converter::get_gf_info(std::vector& 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 gf_converter::convert_to_rgba(std::vector& in, std::size_t* w, std::size_t* h) + { + #define FAIL \ + { \ + w = h = 0; \ + return std::vector(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 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 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& 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 gf_converter::convert_to_png(std::vector& in) + { + std::size_t w, h; + std::vector data = convert_to_rgba(in, &w, &h); + if(data.size() == 0) + return std::vector(0, 0); + std::vector png; + std::uint32_t error = lodepng::encode(png, data, w, h); + if(error) + return std::vector(0, 0); + return png; + } +} diff --git a/src/data_extractor/gf/gf_converter.h b/src/data_extractor/gf/gf_converter.h new file mode 100644 index 0000000..9932766 --- /dev/null +++ b/src/data_extractor/gf/gf_converter.h @@ -0,0 +1,30 @@ +#ifndef GF_CONVERTER_H +#define GF_CONVERTER_H + +#include +#include +#include + +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& 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 convert_to_rgba(std::vector& 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 convert_to_png(std::vector& in); +private: + void read_channel(std::istream& data, std::uint8_t repeat_byte, std::vector& channel); + }; +} + +#endif diff --git a/src/dsb_interpreter/dsb_instruction.h b/src/dsb_interpreter/dsb_instruction.h deleted file mode 100644 index effceb9..0000000 --- a/src/dsb_interpreter/dsb_instruction.h +++ /dev/null @@ -1,217 +0,0 @@ -#ifndef DSB_INSTRUCTION_H -#define DSB_INSTRUCTION_H - -#include -#include - -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". - // 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 or similar. - dsb_instruction_type type; - }; -} - -#endif diff --git a/src/dsb_interpreter/dsb_interpreter.cc b/src/dsb_interpreter/dsb_interpreter.cc deleted file mode 100644 index 7b7367f..0000000 --- a/src/dsb_interpreter/dsb_interpreter.cc +++ /dev/null @@ -1,219 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include - -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 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& args, std::size_t instr_begins_at, std::size_t line_n) - { - dsb_instruction* instr = nullptr; - std::vector 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); - } -} diff --git a/src/dsb_interpreter/dsb_interpreter.h b/src/dsb_interpreter/dsb_interpreter.h deleted file mode 100644 index d0de0a0..0000000 --- a/src/dsb_interpreter/dsb_interpreter.h +++ /dev/null @@ -1,87 +0,0 @@ -#ifndef DSB_INTERPRETER_H -#define DSB_INTERPRETER_H - -#include -#include -#include -#include -#include -#include - -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 - inline T* get_instruction(dsb_instruction_type type) const - { - for(dsb_instruction* instruction : m_interpreted_dsb) - { - if(instruction->type == type) - return static_cast(instruction); - } - return nullptr; - } - - // Returns all instances of the specified instruction type. - template - inline std::vector get_instructions(dsb_instruction_type type) const - { - std::vector values; - for(dsb_instruction* instruction : m_interpreted_dsb) - { - if(instruction->type == type) - values.push_back(static_cast(instruction)); - } - return values; - } - - // Returns a reference to the raw instruction array. - // This can be manually searched and interpreted. - inline const std::vector& 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& 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 m_interpreted_dsb; - }; -} - -#endif diff --git a/src/dsb_interpreter/dsb_interpreter_debugger.cc b/src/dsb_interpreter/dsb_interpreter_debugger.cc deleted file mode 100644 index 3286abc..0000000 --- a/src/dsb_interpreter/dsb_interpreter_debugger.cc +++ /dev/null @@ -1,89 +0,0 @@ -#include -#include -#include -#include -#include - -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(instruction); - std::cout << " comment -> \"" << cast->comment << "\"" << std::endl; - break; - } - case dsb_instruction_type::begin_section: - { - dsb_instruction_begin_section* cast = static_cast(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(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(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(instruction); - std::cout << " image -> \"" << cast->image << "\"" << std::endl; - break; - } - case dsb_instruction_type::vignette_set_color: - { - dsb_instruction_vignette_set_color* cast = static_cast(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(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(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_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; - } - } -} diff --git a/src/dsb_interpreter/dsb_interpreter_debugger.h b/src/dsb_interpreter/dsb_interpreter_debugger.h deleted file mode 100644 index 61f6859..0000000 --- a/src/dsb_interpreter/dsb_interpreter_debugger.h +++ /dev/null @@ -1,23 +0,0 @@ -#ifndef DSB_INTERPRETER_DEBUGGER_H -#define DSB_INTERPRETER_DEBUGGER_H - -#include - -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 diff --git a/src/dsb_interpreter/dsb_interpreter_string_utils.h b/src/dsb_interpreter/dsb_interpreter_string_utils.h deleted file mode 100644 index 15067c6..0000000 --- a/src/dsb_interpreter/dsb_interpreter_string_utils.h +++ /dev/null @@ -1,83 +0,0 @@ -#ifndef DSB_INTERPRETER_STRING_UTILS_H -#define DSB_INTERPRETER_STRING_UTILS_H - -#include - -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 diff --git a/src/dsb_interpreter/instructions/all.h b/src/dsb_interpreter/instructions/all.h deleted file mode 100644 index 9ccc002..0000000 --- a/src/dsb_interpreter/instructions/all.h +++ /dev/null @@ -1,9 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include diff --git a/src/dsb_interpreter/instructions/begin_section/dsb_instruction_begin_section.h b/src/dsb_interpreter/instructions/begin_section/dsb_instruction_begin_section.h deleted file mode 100644 index 3b0b29c..0000000 --- a/src/dsb_interpreter/instructions/begin_section/dsb_instruction_begin_section.h +++ /dev/null @@ -1,24 +0,0 @@ -#ifndef DSB_INSTRUCTION_BEGIN_SECTION_H -#define DSB_INSTRUCTION_BEGIN_SECTION_H - -#include - -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 diff --git a/src/dsb_interpreter/instructions/comment/dsb_instruction_comment.h b/src/dsb_interpreter/instructions/comment/dsb_instruction_comment.h deleted file mode 100644 index 2befc83..0000000 --- a/src/dsb_interpreter/instructions/comment/dsb_instruction_comment.h +++ /dev/null @@ -1,24 +0,0 @@ -#ifndef DSB_INSTRUCTION_COMMENT_H -#define DSB_INSTRUCTION_COMMENT_H - -#include - -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 diff --git a/src/dsb_interpreter/instructions/invalid_dsb/dsb_instruction_invalid_dsb.h b/src/dsb_interpreter/instructions/invalid_dsb/dsb_instruction_invalid_dsb.h deleted file mode 100644 index 2f271e8..0000000 --- a/src/dsb_interpreter/instructions/invalid_dsb/dsb_instruction_invalid_dsb.h +++ /dev/null @@ -1,30 +0,0 @@ -#ifndef DSB_INSTRUCTION_INVALID_DSB_H -#define DSB_INSTRUCTION_INVALID_DSB_H - -#include - -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 diff --git a/src/dsb_interpreter/instructions/level_add/dsb_instruction_level_add.h b/src/dsb_interpreter/instructions/level_add/dsb_instruction_level_add.h deleted file mode 100644 index 737867e..0000000 --- a/src/dsb_interpreter/instructions/level_add/dsb_instruction_level_add.h +++ /dev/null @@ -1,21 +0,0 @@ -#ifndef DSB_INSTRUCTION_LEVEL_ADD_H -#define DSB_INSTRUCTION_LEVEL_ADD_H - -#include - -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 diff --git a/src/dsb_interpreter/instructions/set_data_dir/dsb_instruction_set_data_dir.h b/src/dsb_interpreter/instructions/set_data_dir/dsb_instruction_set_data_dir.h deleted file mode 100644 index 80df521..0000000 --- a/src/dsb_interpreter/instructions/set_data_dir/dsb_instruction_set_data_dir.h +++ /dev/null @@ -1,92 +0,0 @@ -#ifndef DSB_INSTRUCTION_SET_DATA_DIR_H -#define DSB_INSTRUCTION_SET_DATA_DIR_H - -#include - -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 diff --git a/src/dsb_interpreter/instructions/set_texture_file/dsb_instruction_set_texture_file.h b/src/dsb_interpreter/instructions/set_texture_file/dsb_instruction_set_texture_file.h deleted file mode 100644 index f2cd3d1..0000000 --- a/src/dsb_interpreter/instructions/set_texture_file/dsb_instruction_set_texture_file.h +++ /dev/null @@ -1,33 +0,0 @@ -#ifndef DSB_INSTRUCTION_SET_TEXTURE_FILE_H -#define DSB_INSTRUCTION_SET_TEXTURE_FILE_H - -#include - -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 diff --git a/src/dsb_interpreter/instructions/vignette/create_bar/dsb_instruction_vignette_create_bar.h b/src/dsb_interpreter/instructions/vignette/create_bar/dsb_instruction_vignette_create_bar.h deleted file mode 100644 index 51f565b..0000000 --- a/src/dsb_interpreter/instructions/vignette/create_bar/dsb_instruction_vignette_create_bar.h +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef DSB_INSTRUCTION_VIGNETTE_CREATE_BAR_H -#define DSB_INSTRUCTION_VIGNETTE_CREATE_BAR_H - -#include - -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 diff --git a/src/dsb_interpreter/instructions/vignette/load_img/dsb_instruction_vignette_load_img.h b/src/dsb_interpreter/instructions/vignette/load_img/dsb_instruction_vignette_load_img.h deleted file mode 100644 index 71e8b19..0000000 --- a/src/dsb_interpreter/instructions/vignette/load_img/dsb_instruction_vignette_load_img.h +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef DSB_INSTRUCTION_VIGNETTE_LOAD_IMG_H -#define DSB_INSTRUCTION_VIGNETTE_LOAD_IMG_H - -#include - -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 diff --git a/src/dsb_interpreter/instructions/vignette/set_color/dsb_instruction_vignette_set_color.h b/src/dsb_interpreter/instructions/vignette/set_color/dsb_instruction_vignette_set_color.h deleted file mode 100644 index f50e93b..0000000 --- a/src/dsb_interpreter/instructions/vignette/set_color/dsb_instruction_vignette_set_color.h +++ /dev/null @@ -1,62 +0,0 @@ -#ifndef DSB_INSTRUCTION_VIGNETTE_SET_COLOR_H -#define DSB_INSTRUCTION_VIGNETTE_SET_COLOR_H - -#include -#include -#include - -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 diff --git a/src/engine.cc b/src/engine.cc index d9e9097..8ec7e4d 100644 --- a/src/engine.cc +++ b/src/engine.cc @@ -103,18 +103,16 @@ namespace openrayman if(m_current_input.command(input_command::toggle_fullscreen) && !m_last_input.command(input_command::toggle_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_accumulated_frames_fps++; if(m_accumulated_time_fps >= 1) { m_fps = m_accumulated_frames_fps; - std::cout << "[openrayman::engine] FPS: " << m_fps << std::endl; m_accumulated_time_fps = m_accumulated_frames_fps = 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_accumulated_time_fixed -= 1 / 60.0; } diff --git a/src/engine.h b/src/engine.h index 560940c..29c27e5 100644 --- a/src/engine.h +++ b/src/engine.h @@ -117,10 +117,11 @@ public: return m_total_fixed_updates; } - // Returns the amount of frames that were executed the previous second. - inline std::uint64_t get_fps() const + // Returns the amount of frames that were executed during the previous second. + 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. diff --git a/src/game.cc b/src/game.cc index 4634374..34f3ed6 100644 --- a/src/game.cc +++ b/src/game.cc @@ -82,15 +82,19 @@ namespace openrayman 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)) return m_location + "/" + file; for(const game& game : m_dependencies) { 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 ""; } } diff --git a/src/game.h b/src/game.h index 38beef8..6250957 100644 --- a/src/game.h +++ b/src/game.h @@ -53,7 +53,7 @@ public: // Dependencies are searched from first to last specified in the manifest. // An empty string is returned if the file was not found. // 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: bool m_valid; std::string m_location; diff --git a/src/main.cc b/src/main.cc index 98ba3b1..58819b4 100755 --- a/src/main.cc +++ b/src/main.cc @@ -1,32 +1,13 @@ #include #include +#include #include -#include -#include -#include +#include +#include +#include #include -#include - -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; -} +#include +#include int main(int argc, char** argv) { @@ -80,8 +61,9 @@ int main(int argc, char** argv) else return fail_and_print("Operation failed"); } - else - return fail_and_print("Invalid format specified"); + else if(format == "png") + return openrayman::gf_tools::convert_to_png(path, target); + return fail_and_print("Invalid format specified"); } if(str == "--inspect") { @@ -93,15 +75,32 @@ int main(int argc, char** argv) if(n >= argc) return fail_and_print("No path was specified"); std::string path(argv[n]); - if(format == "odsb") - { - openrayman::dsb_interpreter interpreter(path); - openrayman::dsb_interpreter_debugger debugger(interpreter); - debugger.print_summary(); - return interpreter.success() ? EXIT_SUCCESS : EXIT_FAILURE; - } - else - return fail_and_print("Invalid format was specified"); + if(format == "cnt") + return openrayman::cnt_tools::print_hierarchy(path); + else if(format == "gf") + return openrayman::gf_tools::print_info(path); + 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 if(str == "--help") @@ -116,13 +115,15 @@ int main(int argc, char** argv) std::cout << " This is needed to ease modding support" << 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 << " --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\": Creates an OpenRayman DSB file" << std::endl; + std::cout << " --convert-to \"format\" \"path\" \"target\" Converts the specified file into the target format" << std::endl; + std::cout << " \"odsb\": Decompiles the DSB into the specified directory (several files will be created)" << 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 << " Format can be any of:" << std::endl; - std::cout << " \"odsb\": Interprets the DSB and outputs all instructions" << std::endl; + std::cout << " \"png\": Decodes a GF (graphics texture) file into a png file" << std::endl; + std::cout << " --inspect \"format\" \"path\" Inspects and prints info about the specified file or directory" << 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; } if(str == "--version") @@ -146,7 +147,6 @@ int main(int argc, char** argv) INT WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR lpCmdLine, INT nCmdShow) { - // fuck windows return main(__argc, __argv); } diff --git a/src/platform/file.cc b/src/platform/file.cc index 6ea370c..e9d9681 100644 --- a/src/platform/file.cc +++ b/src/platform/file.cc @@ -2,6 +2,7 @@ #ifdef _WIN32 #include #include +#include #include #include #else @@ -10,6 +11,8 @@ #include #include #include +#include +#include #endif namespace openrayman @@ -43,4 +46,33 @@ namespace openrayman mkdir(fix_string(path).c_str(), 0775); #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 + } } diff --git a/src/platform/file.h b/src/platform/file.h index eccecf6..c46dcd8 100644 --- a/src/platform/file.h +++ b/src/platform/file.h @@ -18,6 +18,9 @@ public: // Creates a new directory with the specified 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. #ifdef _WIN32 static const char path_separator = '\\'; diff --git a/src/platform/message_box.cc b/src/platform/message_box.cc index 79f0eb0..f507209 100644 --- a/src/platform/message_box.cc +++ b/src/platform/message_box.cc @@ -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()); gtk_window_set_title(GTK_WINDOW(dialog), title.c_str()); gtk_dialog_run(GTK_DIALOG(dialog)); + gtk_widget_hide(dialog); gtk_widget_destroy(dialog); } #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()); gtk_window_set_title(GTK_WINDOW(dialog), title.c_str()); gint result = gtk_dialog_run(GTK_DIALOG(dialog)); + gtk_widget_hide(dialog); gtk_widget_destroy(dialog); return result == GTK_RESPONSE_YES; } @@ -99,9 +101,11 @@ namespace openrayman char* directory_cstr = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog)); std::string directory = std::string(directory_cstr); g_free(directory_cstr); + gtk_widget_hide(dialog); gtk_widget_destroy(dialog); return directory; } + gtk_widget_hide(dialog); gtk_widget_destroy(dialog); } #endif diff --git a/src/tools/cnt_tools.cc b/src/tools/cnt_tools.cc new file mode 100644 index 0000000..d74b877 --- /dev/null +++ b/src/tools/cnt_tools.cc @@ -0,0 +1,55 @@ +#include +#include +#include +#include + +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 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; + } + } +} diff --git a/src/tools/cnt_tools.h b/src/tools/cnt_tools.h new file mode 100644 index 0000000..1d705e7 --- /dev/null +++ b/src/tools/cnt_tools.h @@ -0,0 +1,17 @@ +#ifndef CNT_TOOLS_H +#define CNT_TOOLS_H + +#include + +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 diff --git a/src/tools/common_tools.h b/src/tools/common_tools.h new file mode 100644 index 0000000..157b42b --- /dev/null +++ b/src/tools/common_tools.h @@ -0,0 +1,35 @@ +#ifndef COMMON_TOOLS_H +#define COMMON_TOOLS_H + +#ifdef _WIN32 +#include +#endif +#include +#include +#include +#include + +// 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 diff --git a/src/tools/gf_tools.cc b/src/tools/gf_tools.cc new file mode 100644 index 0000000..fc3cbd7 --- /dev/null +++ b/src/tools/gf_tools.cc @@ -0,0 +1,69 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +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 vector(gf_data, gf_data + size); + std::vector 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 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; + } + } +} diff --git a/src/tools/gf_tools.h b/src/tools/gf_tools.h new file mode 100644 index 0000000..630da5f --- /dev/null +++ b/src/tools/gf_tools.h @@ -0,0 +1,17 @@ +#ifndef GF_TOOLS_H +#define GF_TOOLS_H + +#include + +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