Add basic DSB interpreter

We might need to replace this with something more sophisticated if some scripts use more flow control (e.g. if, while)
This commit is contained in:
Hannes Mann 2016-06-27 23:14:12 +02:00
parent ff22900319
commit ae50ccf7f6
26 changed files with 1065 additions and 72 deletions

4
.editorconfig Normal file
View file

@ -0,0 +1,4 @@
[*.{cc, h, cpp, hpp}]
charset = utf-8
indent_style = tab
indent_size = 4

View file

@ -1,7 +1,7 @@
project(OpenRayman)
cmake_minimum_required(VERSION 2.6)
set(CMAKE_BUILD_TYPE Release CACHE STRING "The type of build")
set(CMAKE_BUILD_TYPE Debug CACHE STRING "The type of build")
set(USE_LIBRETRO OFF CACHE BOOL "If OpenRayman should be built as a libretro core (TODO)")
set(CMAKE_CXX_STANDARD 11)

View file

@ -1,6 +1,9 @@
This file attempts to document the Rayman 2: The Great Escape file formats and engine as best possible.
You should probably check out https://drive.google.com/folderview?id=0Bzt0AW9Obi4-Tjg2MVAyLWNUVXM&usp=sharing, too.
Note: OpenRayman could not have been possible without the amazing szymski on GitHub (https://github.com/szymski/Rayman2Lib), who seems to have reverse engineered the encoding algorithm used in Rayman 2 and the file formats that are used within the engine. Most of this information was scrapped together from the Rayman2Lib repository.
@ -16,26 +19,6 @@ Note: some of this information may not be incorrect, as i have not verified any
## Formats
.cnt = archive
.gf (graphics file?) (TODO: rayman2lib has both gf3 and gf?) = textures
.sna = ? scripts / events / level / everything?!?! (TODO: figure out what rayman2lib is doing with seeking)
.dat = pointers to file?!? file database?!?!
.dsb = same?!? scripts?!?!
.gpt = ?!?!?!?
.bnm = probably sound files (based off of rayman2lib)
## Decoding

5
src/.clang_complete Normal file
View file

@ -0,0 +1,5 @@
-I.
-I../lib/lodepng
-I../lib/gl3w/include
-I../lib/json/src
-std=gnu++11

View file

@ -1,5 +1,8 @@
#include <data_extractor/data_extractor.h>
#include <data_extractor/dsb/dsb_decompiler.h>
#include <dsb_interpreter/dsb_instruction.h>
#include <dsb_interpreter/dsb_interpreter.h>
#include <dsb_interpreter/instructions/all.h>
#include <platform/message_box.h>
#include <platform/file.h>
#include <json.hpp>
@ -10,14 +13,11 @@ namespace openrayman
{
bool data_extractor::extract(const std::string& install_folder)
{
#ifdef _WIN32
AllocConsole();
freopen("CONOUT$", "w", stdout);
#endif
return
check_prerequisites(install_folder) &&
create_base() &&
decompile_game_dsb(install_folder);
decompile_game_dsb(install_folder) &&
make_game_resources(install_folder);
}
bool data_extractor::check_prerequisites(const std::string& install_folder)
@ -83,4 +83,20 @@ namespace openrayman
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);
}
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())
{
return 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;
}
}

View file

@ -22,6 +22,7 @@ private:
bool check_prerequisites(const std::string& install_folder);
bool create_base();
bool decompile_game_dsb(const std::string& install_folder);
bool make_game_resources(const std::string& install_folder);
const backend_specifics& m_backend_specifics;
};

View file

@ -3,6 +3,7 @@
#include <info.h>
#include <cstdint>
#include <unordered_map>
#include <iomanip>
namespace openrayman
{
@ -62,11 +63,11 @@ namespace openrayman
while(!in.eof() && id != 0xFFFF)
{
#define DECOMPILE_SECTION(id, name, function) \
#define DECOMPILE_SECTION(id, name, function) \
case (id): \
{ \
std::cout << "[openrayman::dsb_decompiler].decompile_sections: decompiling " \
<< std::hex << "0x" << (id) \
std::cout << "[openrayman::dsb_decompiler] Decompiling " \
<< std::hex << "0x" << std::setfill('0') << std::setw(2) << (id) \
<< " (decimal " << std::dec << id << ") \"" \
<< (name) << "\"" << std::endl; \
target << "section " << (name) << "\n"; \
@ -90,7 +91,7 @@ namespace openrayman
DECOMPILE_SECTION(0x5C, "load_sound_banks", decompile_load_sound_banks);
default:
{
std::cout << "[openrayman::dsb_decompiler].decompile_sections: Encountered unknown id 0x"
std::cout << "[openrayman::dsb_decompiler] Encountered unknown id 0x"
<< std::hex << id
<< " (decimal " << std::dec << id << ")"
<< std::endl;
@ -180,7 +181,7 @@ 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].decompile_data_directories: " << std::hex << "0x" << id << ": " << str << std::endl;
std::cout << "[openrayman::dsb_decompiler] Data directory " << std::hex << "0x" << id << ": " << str << std::endl;
// dir(name, path);
if(dir != "")
target << " dir(" << dir << ", \"" << str << "\")\n";
@ -192,7 +193,7 @@ namespace openrayman
// is this ever found?!?!
void dsb_decompiler::decompile_unknown_blob_0x20(std::istream& source, std::ofstream& target)
{
std::cout << "[openrayman::dsb_decompiler].decompile_unknown_blob_0x20: Warning! encountered 0x20" << std::endl;
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";
@ -243,7 +244,7 @@ namespace openrayman
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" : "inline")
<< (id == 76 ? "outline" : "inside")
<< ", " << std::to_string(r)
<< ", " << std::to_string(g)
<< ", " << std::to_string(b)
@ -311,7 +312,7 @@ namespace openrayman
void dsb_decompiler::decompile_unknown_blob_0x6e(std::istream& source, std::ofstream& target)
{
std::cout << "[openrayman::dsb_decompiler].decompile_unknown_blob_0x6e: Warning! encountered 0x6e" << std::endl;
std::cout << "[openrayman::dsb_decompiler] Warning! encountered 0x6e" << std::endl;
std::uint8_t tmp = 0x00;
while(tmp != 0xFF)
source.read((char*)&tmp, 1);

View file

@ -26,7 +26,8 @@ namespace openrayman
};
// Decodes and optionally decompiles DSB files.
// This is all heavily adapted from Rayman2Lib's DSBDecompiler
// DSB files are compiled scripts, with different sections.
// This is all heavily adapted from Rayman2Lib.
class dsb_decompiler
{
public:

View file

@ -0,0 +1,217 @@
#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

View file

@ -0,0 +1,127 @@
#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 <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))
{
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 function_name = read_until_char(line, n, '(');
n += function_name.length() + 1;
std::vector<std::string> function_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] == ',')
{
function_args.push_back(arg);
arg = "";
n++;
n += skip_whitespace(line, n);
}
else if(line[n] == ')')
break;
else
n++;
}
if(arg.length() > 0)
function_args.push_back(arg);
std::cout << "[openrayman::dsb_interpreter] Function: \"" << function_name << "\", " << function_args.size() << std::endl;
for(const std::string& str : function_args)
std::cout << " Argument: \"" << str << "\"" << std::endl;
}
}

View file

@ -0,0 +1,86 @@
#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);
// 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

View file

@ -0,0 +1,88 @@
#include <dsb_interpreter/dsb_interpreter_debugger.h>
#include <dsb_interpreter/dsb_instruction.h>
#include <dsb_interpreter/instructions/all.h>
#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 << " pair1 -> [" << cast->r << ", " << cast->g << ", " << cast->b << ", " << cast->a << "]" << std::endl;
std::cout << " pair2 -> [" << cast->pair2_r << ", " << cast->pair2_g << ", " << cast->pair2_b << ", " << cast->pair2_a << "]" << std::endl;
std::cout << " pair3 -> [" << cast->pair3_r << ", " << cast->pair3_g << ", " << cast->pair3_b << ", " << cast->pair3_a << "]" << std::endl;
std::cout << " pair4 -> [" << cast->pair4_r << ", " << cast->pair4_g << ", " << cast->pair4_b << ", " << cast->pair4_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;
}
}
}

View file

@ -0,0 +1,23 @@
#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

View file

@ -0,0 +1,83 @@
#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

View file

@ -0,0 +1,9 @@
#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>

View file

@ -0,0 +1,24 @@
#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

View file

@ -0,0 +1,24 @@
#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

View file

@ -0,0 +1,30 @@
#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

View file

@ -0,0 +1,21 @@
#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

View file

@ -0,0 +1,92 @@
#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

View file

@ -0,0 +1,33 @@
#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

View file

@ -0,0 +1,20 @@
#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

View file

@ -0,0 +1,20 @@
#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

View file

@ -0,0 +1,75 @@
#ifndef DSB_INSTRUCTION_VIGNETTE_SET_COLOR_H
#define DSB_INSTRUCTION_VIGNETTE_SET_COLOR_H
#include <dsb_interpreter/dsb_instruction.h>
#include <cstdint>
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
{
// TODO: refactor this..........
// ugh
dsb_instruction_vignette_set_color(dsb_vignette_id id,
std::uint8_t r, std::uint8_t g, std::uint8_t b, std::uint8_t a) :
dsb_instruction(dsb_instruction_type::vignette_set_color),
r(r), g(g), b(b), a(a)
{
}
// The id of the color to set.
dsb_vignette_id id;
// The first pair of colors.
std::uint8_t r, g, b, a;
// The second pair of colors.
// Only used when id is bar, to specify gradient.
std::uint8_t pair2_r, pair2_g, pair2_b, pair2_a;
// The third pair of colors.
// Only used when id is bar, to specify gradient.
std::uint8_t pair3_r, pair3_g, pair3_b, pair3_a;
// The fourth pair of colors.
// Only used when id is bar, to specify gradient.
std::uint8_t pair4_r, pair4_g, pair4_b, pair4_a;
};
}
#endif

View file

@ -103,18 +103,18 @@ 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 << "Update: " << m_current_delta_time * 1000 << "ms, " << m_total_frames << std::endl;
std::cout << "[openrayman::engine] Update: " << m_current_delta_time * 1000 << "ms, " << m_total_frames << std::endl;
m_total_frames++;
m_accumulated_frames_fps++;
if(m_accumulated_time_fps >= 1)
{
m_fps = m_accumulated_frames_fps;
std::cout << "FPS: " << m_fps << std::endl;
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 << "Fixed update: " << m_total_fixed_updates << std::endl;
std::cout << "[openrayman::engine] Fixed update: " << m_total_fixed_updates << std::endl;
m_total_fixed_updates++;
m_accumulated_time_fixed -= 1 / 60.0;
}

View file

@ -3,6 +3,8 @@
#include <info.h>
#include <engine.h>
#include <data_extractor/dsb/dsb_decompiler.h>
#include <dsb_interpreter/dsb_interpreter.h>
#include <dsb_interpreter/dsb_interpreter_debugger.h>
bool console_open = false;
@ -19,6 +21,12 @@ void make_sure_console_open()
#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)
{
std::string selected_game = "";
@ -30,10 +38,7 @@ int main(int argc, char** argv)
{
n++;
if(n >= argc)
{
make_sure_console_open(); std::cout << "No game was specified." << std::endl;
return EXIT_FAILURE;
}
return fail_and_print("No game was specified");;
std::string game(argv[n]);
selected_game = game;
}
@ -41,59 +46,61 @@ int main(int argc, char** argv)
{
n++;
if(n >= argc)
{
make_sure_console_open(); std::cout << "No install folder was specified." << std::endl;
return EXIT_FAILURE;
}
return fail_and_print("No install folder was specified");
std::string install_folder(argv[n]);
selected_install_folder = install_folder;
}
if(str == "--decompile")
if(str == "--convert-to")
{
n++;
if(n >= argc)
{
make_sure_console_open(); std::cout << "No type was specified." << std::endl;
return EXIT_FAILURE;
}
std::string type(argv[n]);
return fail_and_print("No format was specified");
std::string format(argv[n]);
n++;
if(n >= argc)
{
make_sure_console_open(); std::cout << "No path was specified." << std::endl;
return EXIT_FAILURE;
}
return fail_and_print("No path was specified");
std::string path(argv[n]);
n++;
if(n >= argc)
{
make_sure_console_open(); std::cout << "No target was specified." << std::endl;
return EXIT_FAILURE;
}
return fail_and_print("No target was specified");
std::string target(argv[n]);
if(type == "odsb" || type == "rdsb")
if(format == "odsb" || format == "rdsb")
{
openrayman::dsb_decompiler decompiler;
if(decompiler.decompile_dsb(
path,
target,
type == "odsb" ?
format == "odsb" ?
openrayman::dsb_format::openrayman :
openrayman::dsb_format::rayman2_decoded))
{
return EXIT_SUCCESS;
}
else
{
std::cout << "Operation failed" << std::endl;
return EXIT_FAILURE;
}
return fail_and_print("Operation failed");
}
else
return fail_and_print("Invalid format specified");
}
if(str == "--inspect")
{
n++;
if(n >= argc)
return fail_and_print("No format was specified");
std::string format(argv[n]);
n++;
if(n >= argc)
return fail_and_print("No path was specified");
std::string path(argv[n]);
if(format == "odsb")
{
make_sure_console_open(); std::cout << "Invalid type specified." << std::endl;
return EXIT_FAILURE;
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");
}
// Follow GNU format
if(str == "--help")
@ -108,10 +115,13 @@ 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 << " --decompile \"type\" \"path\" \"target\" Decompiles the specified file into target." << std::endl;
std::cout << " Type can be any of:" << std::endl;
std::cout << " \"odsb\": Creates an OpenRayman dsb file" << std::endl;
std::cout << " \"rdsb\": Decodes a dsb file" << 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 << " \"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;
return EXIT_SUCCESS;
}
if(str == "--version")