mirror of
https://github.com/open-goal/jak-project.git
synced 2024-10-20 21:27:52 -04:00
233 lines
5.8 KiB
C++
233 lines
5.8 KiB
C++
#include <stdexcept>
|
|
#include "common/util/assert.h"
|
|
#include "DataParser.h"
|
|
#include "third-party/fmt/core.h"
|
|
|
|
/*
|
|
* Allowable lines:
|
|
* L123: - label
|
|
* L123: (offset 2) - label with an offset (only 2 allowed)
|
|
* .word 0xbeef - a hex word
|
|
* .word L123 - a label word
|
|
* .symbol sym - a symbol
|
|
* .empty-list - the empty list
|
|
* .type typ - a type
|
|
*/
|
|
|
|
namespace decompiler {
|
|
namespace {
|
|
std::vector<std::string> string_to_lines(const std::string& str) {
|
|
std::vector<std::string> result;
|
|
std::string::size_type i;
|
|
std::string::size_type start = 0;
|
|
while (true) {
|
|
i = str.find('\n', start);
|
|
if (i == std::string::npos) {
|
|
if (start < str.length()) {
|
|
result.push_back(str.substr(start));
|
|
}
|
|
return result;
|
|
} else {
|
|
result.push_back(str.substr(start, i - start));
|
|
start = i + 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
std::string get_until_space(std::string& instr) {
|
|
assert(!instr.empty());
|
|
size_t i;
|
|
for (i = 0; i < instr.length(); i++) {
|
|
if (instr[i] == ' ') {
|
|
break;
|
|
}
|
|
}
|
|
auto name = instr.substr(0, i);
|
|
if (i == instr.length()) {
|
|
instr.clear();
|
|
} else {
|
|
instr = instr.substr(i + 1);
|
|
}
|
|
return name;
|
|
}
|
|
|
|
} // namespace
|
|
ParsedData parse_data(const std::string& str) {
|
|
ParsedData result;
|
|
struct LabelInfo {
|
|
int idx = -1;
|
|
bool defined = false;
|
|
};
|
|
|
|
std::unordered_map<std::string, LabelInfo> label_map;
|
|
|
|
const std::string offset_2 = ": (offset 2)";
|
|
auto lines = string_to_lines(str);
|
|
int byte_offset = 0;
|
|
for (auto& line : lines) {
|
|
// strip off leading white space
|
|
size_t i;
|
|
for (i = 0; i < line.length(); i++) {
|
|
if (line[i] != ' ') {
|
|
line = line.substr(i);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (line.empty()) {
|
|
continue;
|
|
}
|
|
|
|
// try as label definition.
|
|
if (line.front() == 'L') {
|
|
int offset = 0;
|
|
if (line.back() == ':') {
|
|
line.pop_back();
|
|
} else {
|
|
if (line.length() >= (2 + offset_2.length()) &&
|
|
line.substr(line.length() - offset_2.length()) == offset_2) {
|
|
line = line.substr(0, line.length() - offset_2.length());
|
|
offset = 2;
|
|
} else {
|
|
throw std::runtime_error(fmt::format("Invalid label line: {}", line));
|
|
}
|
|
}
|
|
|
|
auto& l = label_map[line];
|
|
if (l.defined) {
|
|
throw std::runtime_error(fmt::format("Label {} is multiply defined.", line));
|
|
}
|
|
|
|
l.defined = true;
|
|
if (l.idx == -1) {
|
|
l.idx = result.labels.size();
|
|
result.labels.emplace_back();
|
|
}
|
|
|
|
auto& label = result.labels.at(l.idx);
|
|
label.target_segment = 0;
|
|
label.offset = byte_offset + offset;
|
|
label.name = line;
|
|
continue;
|
|
}
|
|
|
|
auto first_thing = get_until_space(line);
|
|
|
|
// try as .type
|
|
if (first_thing == ".type") {
|
|
LinkedWord word(0);
|
|
word.kind = LinkedWord::TYPE_PTR;
|
|
word.symbol_name = line;
|
|
result.words.push_back(word);
|
|
byte_offset += 4;
|
|
continue;
|
|
}
|
|
|
|
if (first_thing == ".symbol") {
|
|
LinkedWord word(0);
|
|
word.kind = LinkedWord::SYM_PTR;
|
|
word.symbol_name = line;
|
|
result.words.push_back(word);
|
|
byte_offset += 4;
|
|
continue;
|
|
}
|
|
|
|
if (first_thing == ".empty-list") {
|
|
if (!line.empty()) {
|
|
throw std::runtime_error("Got something after .empty-list, this is not allowed");
|
|
}
|
|
LinkedWord word(0);
|
|
word.kind = LinkedWord::EMPTY_PTR;
|
|
result.words.push_back(word);
|
|
byte_offset += 4;
|
|
continue;
|
|
}
|
|
|
|
if (first_thing == ".word") {
|
|
if (!line.empty() && line.at(0) == 'L') {
|
|
auto& l = label_map[line];
|
|
if (l.idx == -1) {
|
|
l.idx = result.labels.size();
|
|
result.labels.emplace_back();
|
|
}
|
|
LinkedWord word(0);
|
|
word.kind = LinkedWord::PTR;
|
|
word.label_id = l.idx;
|
|
result.words.push_back(word);
|
|
byte_offset += 4;
|
|
continue;
|
|
} else {
|
|
auto val = std::stoull(line, nullptr, 16);
|
|
assert(val <= UINT32_MAX);
|
|
LinkedWord word(val);
|
|
word.kind = LinkedWord::PLAIN_DATA;
|
|
result.words.push_back(word);
|
|
byte_offset += 4;
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
for (auto& kv : label_map) {
|
|
if (!kv.second.defined) {
|
|
throw std::runtime_error(fmt::format("Label {} was used but not defined.", kv.first));
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
std::string ParsedData::print() const {
|
|
std::string result;
|
|
std::unordered_map<int, const DecompilerLabel*> label_map;
|
|
for (const auto& x : labels) {
|
|
label_map[x.offset] = &x;
|
|
}
|
|
|
|
for (size_t idx = 0; idx < words.size(); idx++) {
|
|
// print label
|
|
auto kv = label_map.find(idx * 4);
|
|
if (kv != label_map.end()) {
|
|
result += fmt::format("{}:\n", kv->second->name);
|
|
}
|
|
auto kv_offset = label_map.find(idx * 4 + 2);
|
|
if (kv_offset != label_map.end()) {
|
|
result += fmt::format("{}: (offset 2)\n", kv_offset->second->name);
|
|
}
|
|
|
|
// print word
|
|
auto& word = words.at(idx);
|
|
switch (word.kind) {
|
|
case LinkedWord::PLAIN_DATA:
|
|
result += fmt::format(" .word 0x{:x}\n", word.data);
|
|
break;
|
|
case LinkedWord::PTR:
|
|
result += fmt::format(" .word {}\n", labels.at(word.label_id).name);
|
|
break;
|
|
case LinkedWord::SYM_PTR:
|
|
result += fmt::format(" .symbol {}\n", word.symbol_name);
|
|
break;
|
|
case LinkedWord::TYPE_PTR:
|
|
result += fmt::format(" .type {}\n", word.symbol_name);
|
|
break;
|
|
case LinkedWord::EMPTY_PTR:
|
|
result += " .empty-list\n";
|
|
break;
|
|
default:
|
|
assert(false);
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
const DecompilerLabel& ParsedData::label(const std::string& name) const {
|
|
for (auto& x : labels) {
|
|
if (x.name == name) {
|
|
return x;
|
|
}
|
|
}
|
|
throw std::runtime_error("Could not find label " + name);
|
|
}
|
|
} // namespace decompiler
|