summaryrefslogtreecommitdiff
path: root/.github
diff options
context:
space:
mode:
Diffstat (limited to '.github')
-rw-r--r--.github/calcrom/BuildAnalyzer.cpp143
-rw-r--r--.github/calcrom/BuildAnalyzer.h68
-rw-r--r--.github/calcrom/ElfFile.cpp130
-rw-r--r--.github/calcrom/ElfFile.h60
-rw-r--r--.github/calcrom/Glob.cpp16
-rw-r--r--.github/calcrom/Glob.h20
-rw-r--r--.github/calcrom/Makefile31
-rw-r--r--.github/calcrom/calcrom.cpp181
-rw-r--r--.github/calcrom/webhook.sh6
9 files changed, 534 insertions, 121 deletions
diff --git a/.github/calcrom/BuildAnalyzer.cpp b/.github/calcrom/BuildAnalyzer.cpp
new file mode 100644
index 00000000..4fc36d81
--- /dev/null
+++ b/.github/calcrom/BuildAnalyzer.cpp
@@ -0,0 +1,143 @@
+#include <algorithm>
+#include <cstring>
+#include <iostream>
+#include "BuildAnalyzer.h"
+#include "Glob.h"
+#include "ElfFile.h"
+
+string default_version("");
+
+void BuildAnalyzer::AnalyzeObject(path fname_s) {
+#ifndef NDEBUG
+ cerr << fname_s << endl;
+#endif //NDEBUG
+ string ext = fname_s.extension();
+ SourceType sourceType = ext == ".s" ? SOURCE_ASM : SOURCE_C;
+ fname_s = builddir / relative(fname_s, srcbase);
+ fname_s = fname_s.replace_extension(".o");
+ if (!exists(fname_s)) {
+ throw runtime_error("No such file: " + fname_s.string());
+ }
+
+ Elf32File elf(fname_s);
+
+ // Analyze sections
+ for (Elf32_Shdr & hdr : elf.GetSectionHeaders()) {
+ string shname = elf.GetSectionName(hdr);
+ SectionType sectionType = GetSectionType(shname);
+ if (sectionType != SECTION_OTHER) {
+ sizes[sectionType][sourceType] += (hdr.sh_size + 3) & ~3;
+ auto data = elf.ReadSectionData<unsigned>(hdr);
+//#ifndef NDEBUG
+// unordered_set<unsigned> unique_addrs;
+//#endif
+ for (const auto & word : data) {
+ if (word == 0) {
+ continue; // might be a relocation
+ }
+ if (find_if(program.GetProgramHeaders().cbegin(), program.GetProgramHeaders().cend(), [&word](const auto & phdr) {
+ return phdr.p_vaddr <= word && word < phdr.p_vaddr + phdr.p_memsz;
+ }) != program.GetProgramHeaders().cend()) {
+//#ifndef NDEBUG
+// unique_addrs.insert(word);
+//#endif
+ n_hardcoded++;
+ }
+ }
+//#ifndef NDEBUG
+// if (!version.empty()) {
+// for (const auto & word : unique_addrs) {
+// cerr << "hardcoded " << version << " pointer to " << hex << word << endl;
+// }
+// }
+//#endif
+ } else if (hdr.sh_type == SHT_RELA) {
+ n_relocations += elf.GetSectionElementCount<Elf32_Rela>(hdr);
+ }
+ }
+}
+
+void BuildAnalyzer::reset() {
+ memset(sizes, 0, sizeof(sizes));
+ if (!version.empty()) {
+ sizes[SECTION_TEXT][SOURCE_ASM] = 0x800; // libsyscall.a
+ }
+ n_hardcoded = 0;
+ n_relocations = 0;
+}
+
+BuildAnalyzer::BuildAnalyzer(path &_basedir, path &_subdir, string &_version) :
+ basedir(_basedir),
+ subdir(_subdir),
+ version(_version)
+{
+ reset();
+ srcbase = basedir / subdir;
+ builddir = srcbase / "build" / version;
+ if (!exists(srcbase)) {
+ throw runtime_error("No such directory: " + srcbase.string());
+ }
+
+ string elfpat = builddir + "/*.elf";
+ Glob globber(elfpat, GLOB_TILDE | GLOB_BRACE | GLOB_NOSORT);
+ if (globber.size() == 0) {
+ throw runtime_error("unable to find an ELF file with section data");
+ }
+ program.open(globber[0], Elf32File::sections | Elf32File::programs);
+}
+
+BuildAnalyzer &BuildAnalyzer::operator()() {
+ if (analyzed) {
+ reset();
+ }
+ string pattern = srcbase.string() + "/{src,asm,lib/{src,asm},lib/*/{src,asm},modules/*/{asm,src}}/*.{c,s,cpp}";
+ for (char const * & fname : Glob(pattern, GLOB_TILDE | GLOB_BRACE | GLOB_NOSORT)) {
+ if (string(fname).find("lib/syscall/") == string::npos) {
+ AnalyzeObject(fname);
+ }
+ }
+ analyzed = true;
+ return *this;
+}
+
+ostream &operator<<(ostream &strm, BuildAnalyzer &_this) {
+ if (!_this.analyzed) {
+ strm << "Haven't analyzed this ROM, please call the BuildAnalyzer instance" << endl;
+ return strm;
+ }
+
+ strm << "Analysis of " << (_this.version.empty() ? _this.subdir.string() : _this.version) << " binary:" << endl;
+ // Report code
+ unsigned total_text = _this.sizes[SECTION_TEXT][SOURCE_C] + _this.sizes[SECTION_TEXT][SOURCE_ASM];
+ if (total_text != 0) {
+ double total_text_d = total_text;
+ double src_text_d = _this.sizes[SECTION_TEXT][SOURCE_C];
+ double asm_text_d = _this.sizes[SECTION_TEXT][SOURCE_ASM];
+ strm << " " << total_text << " total bytes of code" << endl;
+ strm << " " << _this.sizes[SECTION_TEXT][SOURCE_C] << " bytes of code in src (" << (src_text_d / total_text_d * 100.0) << "%)" << endl;
+ strm << " " << _this.sizes[SECTION_TEXT][SOURCE_ASM] << " bytes of code in asm (" << (asm_text_d / total_text_d * 100.0) << "%)" << endl;
+ }
+ strm << endl;
+ // Report data
+ unsigned total_data = _this.sizes[SECTION_DATA][SOURCE_C] + _this.sizes[SECTION_DATA][SOURCE_ASM];
+ if (total_data != 0) {
+ double total_data_d = total_data;
+ double src_data_d = _this.sizes[SECTION_DATA][SOURCE_C];
+ double asm_data_d = _this.sizes[SECTION_DATA][SOURCE_ASM];
+ strm << " " << total_data << " total bytes of data" << endl;
+ strm << " " << _this.sizes[SECTION_DATA][SOURCE_C] << " bytes of data in src (" << (src_data_d / total_data_d * 100.0) << "%)" << endl;
+ strm << " " << _this.sizes[SECTION_DATA][SOURCE_ASM] << " bytes of data in asm (" << (asm_data_d / total_data_d * 100.0) << "%)" << endl;
+ }
+ strm << endl;
+ // Report hardcoded pointers
+ unsigned total_pointers = _this.n_hardcoded + _this.n_relocations;
+ if (total_pointers != 0) {
+ double total_pointers_d = total_pointers;
+ double hardcoded_pointers_d = _this.n_hardcoded;
+ double relocated_pointers_d = _this.n_relocations;
+ strm << " " << total_pointers << " total pointers" << endl;
+ strm << " " << _this.n_relocations << " properly-linked pointers (" << (relocated_pointers_d / total_pointers_d * 100.0) << "%)" << endl;
+ strm << " " << _this.n_hardcoded << " hard-coded pointers (" << (hardcoded_pointers_d / total_pointers_d * 100.0) << "%)" << endl;
+ }
+ return strm;
+}
diff --git a/.github/calcrom/BuildAnalyzer.h b/.github/calcrom/BuildAnalyzer.h
new file mode 100644
index 00000000..0b13c2cf
--- /dev/null
+++ b/.github/calcrom/BuildAnalyzer.h
@@ -0,0 +1,68 @@
+#ifndef CALCROM_BUILDANALYZER_H
+#define CALCROM_BUILDANALYZER_H
+
+#include <filesystem>
+#include <utility>
+#include <vector>
+#include <unordered_set>
+#include "ElfFile.h"
+
+using namespace std;
+using namespace std::filesystem;
+
+extern string default_version;
+
+enum SectionType {
+ SECTION_DATA,
+ SECTION_TEXT,
+ SECTION_MAX,
+ SECTION_OTHER = -1
+};
+
+enum SourceType {
+ SOURCE_C,
+ SOURCE_ASM,
+ SOURCE_MAX
+};
+
+static enum SourceType GetSourceType(const path &fname) {
+ string ext = fname.extension();
+ return ext == ".s" ? SOURCE_ASM : SOURCE_C;
+}
+
+static enum SectionType GetSectionType(const string &shname) {
+ if (shname == ".text" || shname == ".init" || shname == ".itcm") {
+ return SECTION_TEXT;
+ } else if (shname == ".data" || shname == ".rodata" || shname == ".sdata" || shname == ".dtcm") {
+ return SECTION_DATA;
+ } else {
+ return SECTION_OTHER;
+ }
+}
+
+class BuildAnalyzer {
+ bool analyzed = false;
+ path basedir;
+ path subdir;
+ string version = "";
+ path srcbase;
+ string builddir;
+ Elf32File program;
+
+ // Accumulate sizes
+ // src asm
+ // data _____|_____
+ // text |
+ unsigned sizes[SECTION_MAX][SOURCE_MAX] = {{0, 0}, {0, 0}};
+ unsigned n_hardcoded = 0;
+ unsigned n_relocations = 0;
+
+ void reset();
+ void AnalyzeObject(path fname_s);
+public:
+ BuildAnalyzer(path &_basedir, path &_subdir, string &_version = default_version);
+ BuildAnalyzer &operator()();
+ friend ostream &operator<<(ostream &strm, BuildAnalyzer &_this);
+};
+
+#endif //CALCROM_BUILDANALYZER_H
diff --git a/.github/calcrom/ElfFile.cpp b/.github/calcrom/ElfFile.cpp
new file mode 100644
index 00000000..52ad2d06
--- /dev/null
+++ b/.github/calcrom/ElfFile.cpp
@@ -0,0 +1,130 @@
+#include <iostream>
+#include <cassert>
+#include <algorithm>
+#include <cstring>
+#include "ElfFile.h"
+
+void Elf32File::ReadElfHeaderAndVerify() {
+ handle.seekg(0);
+ handle.read((char *)ehdr.e_ident, EI_NIDENT);
+ assert(memcmp(ehdr.e_ident, ELFMAG, SELFMAG) == 0);
+ assert(ehdr.e_ident[EI_CLASS] == ELFCLASS32);
+ assert(ehdr.e_ident[EI_DATA] == ELFDATA2LSB);
+ assert(ehdr.e_ident[EI_VERSION] == EV_CURRENT);
+ handle.read((char*)&ehdr + EI_NIDENT, sizeof(ehdr) - EI_NIDENT);
+ assert(ehdr.e_shentsize == sizeof(Elf32_Shdr));
+}
+
+void Elf32File::ReadSectionHeaders() {
+ assert(shdr.empty());
+ shdr.resize(ehdr.e_shnum);
+ handle.seekg(ehdr.e_shoff);
+ handle.read((char*)shdr.data(), ehdr.e_shnum * ehdr.e_shentsize);
+}
+
+void Elf32File::ReadProgramHeaders() {
+ assert(phdr.empty());
+ phdr.resize(ehdr.e_phnum);
+ handle.seekg(ehdr.e_phoff);
+ handle.read((char*)phdr.data(), ehdr.e_phnum * ehdr.e_phentsize);
+}
+
+void Elf32File::ReadShstrtab() {
+ assert(shstrtab.empty());
+ shstrtab.resize(shdr[ehdr.e_shstrndx].sh_size);
+ handle.seekg(shdr[ehdr.e_shstrndx].sh_offset);
+ handle.read((char*)shstrtab.data(), shdr[ehdr.e_shstrndx].sh_size);
+}
+
+void Elf32File::ReadStrtab() {
+ assert(strtab.empty());
+ int i;
+ for (i = 1; i < ehdr.e_shnum; i++) {
+ if (i == ehdr.e_shstrndx) continue;
+ if (shdr[i].sh_type == SHT_STRTAB) break;
+ }
+ assert(i != ehdr.e_shnum);
+ strtab.resize(shdr[i].sh_size);
+ handle.seekg(shdr[i].sh_offset);
+ handle.read((char*)strtab.data(), shdr[i].sh_size);
+}
+
+void Elf32File::ReadSymtab() {
+ assert(symtab.empty());
+ auto sec = find_if(shdr.begin(), shdr.end(), [](Elf32_Shdr const& candidate) { return candidate.sh_type == SHT_SYMTAB; });
+ assert(sec != shdr.end());
+ symtab.resize(sec->sh_size);
+ handle.seekg(sec->sh_offset);
+ handle.read((char*)symtab.data(), sec->sh_size);
+}
+
+Elf32File::Elf32File(path const& filename, elfload load) {
+ open(filename, load);
+}
+
+void Elf32File::open(path const& filename, elfload load) {
+ assert(!is_open());
+ handle.open(filename, ios::binary);
+ assert(handle.good());
+ ReadElfHeaderAndVerify();
+ if (load & sections) {
+ ReadSectionHeaders();
+ ReadShstrtab();
+ }
+ if (load & programs) {
+ ReadProgramHeaders();
+ }
+ if (load & symbols) {
+ assert(load & sections);
+ ReadStrtab();
+ ReadSymtab();
+ }
+}
+
+void Elf32File::close() {
+ if (is_open()) {
+ handle.close();
+ }
+ memset(&ehdr, 0, sizeof(ehdr));
+ shdr.clear();
+ symtab.clear();
+ phdr.clear();
+ shstrtab.clear();
+ strtab.clear();
+}
+
+bool Elf32File::is_open() const {
+ return handle.is_open();
+}
+
+Elf32File::~Elf32File() {
+ close();
+}
+
+vector<Elf32_Shdr> &Elf32File::GetSectionHeaders() {
+ return shdr;
+}
+
+vector<Elf32_Phdr> &Elf32File::GetProgramHeaders() {
+ return phdr;
+}
+
+string Elf32File::GetSectionName(const Elf32_Shdr &section) const {
+ return string(shstrtab.data() + section.sh_name);
+}
+
+string Elf32File::GetSymbolName(const Elf32_Sym &symbol) const {
+ return string(strtab.data() + symbol.st_name);
+}
+
+Elf32_Sym &Elf32File::operator[](const string &name) {
+ return *find_if(symtab.begin(), symtab.end(), [&](Elf32_Sym const &sym) { return name == GetSymbolName(sym); });
+}
+
+Elf32_Sym &Elf32File::at(const string &name) {
+ auto ret = find_if(symtab.begin(), symtab.end(), [&](Elf32_Sym const &sym) { return name == GetSymbolName(sym); });
+ if (ret == symtab.end()) {
+ throw runtime_error("no symbol named " + name);
+ }
+ return *ret;
+}
diff --git a/.github/calcrom/ElfFile.h b/.github/calcrom/ElfFile.h
new file mode 100644
index 00000000..85e01800
--- /dev/null
+++ b/.github/calcrom/ElfFile.h
@@ -0,0 +1,60 @@
+#ifndef CALCROM_ELFFILE_H
+#define CALCROM_ELFFILE_H
+
+#include <vector>
+#include <fstream>
+#include <filesystem>
+#include <elf.h>
+
+using namespace std;
+using namespace std::filesystem;
+
+class Elf32File {
+ ifstream handle;
+ Elf32_Ehdr ehdr;
+ vector<Elf32_Shdr> shdr;
+ vector<Elf32_Sym> symtab;
+ vector<Elf32_Phdr> phdr;
+ vector<char> shstrtab;
+ vector<char> strtab;
+
+private:
+ void ReadElfHeaderAndVerify();
+ void ReadSectionHeaders();
+ void ReadProgramHeaders();
+ void ReadShstrtab();
+ void ReadStrtab();
+ void ReadSymtab();
+public:
+ typedef unsigned int elfload;
+ static const elfload sections = 1;
+ static const elfload programs = 2;
+ static const elfload symbols = 4;
+
+ Elf32File() = default;
+ Elf32File(path const& filename, elfload load = sections);
+ ~Elf32File();
+ void open(path const& filename, elfload load = sections);
+ void close();
+ bool is_open() const;
+ template <typename _V>
+ vector<_V> ReadSectionData(const Elf32_Shdr &_shdr) {
+ vector<_V> ret;
+ ret.resize((_shdr.sh_size + sizeof(_V) - 1) / sizeof(_V));
+ handle.seekg(_shdr.sh_offset);
+ handle.read((char *)ret.data(), _shdr.sh_size);
+ return ret;
+ }
+ template <typename _V>
+ size_t GetSectionElementCount(const Elf32_Shdr &_shdr) {
+ return (_shdr.sh_size + sizeof(_V) - 1) / sizeof(_V);
+ }
+ vector<Elf32_Shdr>& GetSectionHeaders();
+ vector<Elf32_Phdr>& GetProgramHeaders();
+ string GetSectionName(const Elf32_Shdr &section) const;
+ string GetSymbolName(const Elf32_Sym &symbol) const;
+ Elf32_Sym &operator[](const string &name);
+ Elf32_Sym &at(const string &name);
+};
+
+#endif //CALCROM_ELFFILE_H
diff --git a/.github/calcrom/Glob.cpp b/.github/calcrom/Glob.cpp
new file mode 100644
index 00000000..cb519233
--- /dev/null
+++ b/.github/calcrom/Glob.cpp
@@ -0,0 +1,16 @@
+#include <stdexcept>
+#include "Glob.h"
+
+Glob::Glob(const char *pattern, int _glob_flags) : glob_flags(_glob_flags) {
+ int result = glob(pattern, glob_flags, nullptr, &glob_result);
+ if (result) {
+ throw runtime_error(string("Glob(") + pattern + ") failed with error " + to_string(result));
+ }
+ assign(glob_result.gl_pathv, glob_result.gl_pathv + glob_result.gl_pathc);
+}
+
+Glob::Glob(const string& pattern, int _glob_flags) : Glob(pattern.c_str(), _glob_flags) {}
+
+Glob::~Glob() {
+ globfree(&glob_result);
+}
diff --git a/.github/calcrom/Glob.h b/.github/calcrom/Glob.h
new file mode 100644
index 00000000..69667628
--- /dev/null
+++ b/.github/calcrom/Glob.h
@@ -0,0 +1,20 @@
+#ifndef CALCROM_GLOB_H
+#define CALCROM_GLOB_H
+
+#include <vector>
+#include <string>
+#include <glob.h>
+
+using namespace std;
+
+class Glob : public vector<const char *> {
+ glob_t glob_result;
+ int glob_flags;
+public:
+ // Call glob with the supplied pattern
+ Glob(const char * pattern, int _glob_flags);
+ Glob(const string& pattern, int _glob_flags);
+ ~Glob();
+};
+
+#endif //CALCROM_GLOB_H
diff --git a/.github/calcrom/Makefile b/.github/calcrom/Makefile
index e3b6ff50..31b58803 100644
--- a/.github/calcrom/Makefile
+++ b/.github/calcrom/Makefile
@@ -1,5 +1,8 @@
CXX := g++
-CXXFLAGS := -O3 -std=c++11
+CXXFLAGS := -g -O2 -std=c++17
+ifeq ($(DEBUG),)
+CXXFLAGS += -DNDEBUG
+endif
ifeq ($(OS),Windows_NT)
EXE := .exe
@@ -7,11 +10,31 @@ else
EXE :=
endif
+CXXSRCS := calcrom.cpp BuildAnalyzer.cpp ElfFile.cpp Glob.cpp
+CXXOBJS := $(CXXSRCS:%.cpp=%.o)
+
+DEPDIR := .deps
+DEPFLAGS = -MT $@ -MMD -MP -MF $(DEPDIR)/$*.d
+
TARGET := calcrom$(EXE)
-.PHONY: all
+.PHONY: all clean
all: $(TARGET)
-$(TARGET): calcrom.cpp
- $(CXX) $(CXXFLAGS) -o $@ $^
+clean:
+ $(RM) -r $(TARGET) $(CXXOBJS) $(DEPDIR)
+
+$(TARGET): $(CXXOBJS)
+ $(CXX) $(LDFLAGS) -o $@ $^
+
+%.o: %.cpp
+%.o: %.cpp $(DEPDIR)/%.d | $(DEPDIR)
+ $(CXX) $(CXXFLAGS) $(DEPFLAGS) -c -o $@ $<
+
+$(DEPDIR): ; @mkdir -p $@
+
+DEPFILES := $(CXXSRCS:%.cpp=$(DEPDIR)/%.d)
+$(DEPFILES):
+
+include $(wildcard $(DEPFILES))
diff --git a/.github/calcrom/calcrom.cpp b/.github/calcrom/calcrom.cpp
index 1cb6f9fe..8b0c1b42 100644
--- a/.github/calcrom/calcrom.cpp
+++ b/.github/calcrom/calcrom.cpp
@@ -1,14 +1,18 @@
/*
* CALCROM.CPP
- * © PikalaxALT 2020
+ * © PikalaxALT 2020-2021
*
- * Simple C++ executable to measure the completion rate of Pokémon Diamond
+ * Permission is granted to copy and/or modify this code under GPL 3.0.
+ *
+ * Simple C++ executable to measure the completion rate of Nintendo DS
* reverse engineering (decompilation).
+ * Similar in scope to calcrom.pl from pret-agb projects, but designed
+ * to cope with restrictions imposed by the DS toolchain.
*
* Requirements:
* - Must have C++11 compliant compiler.
* - MacOS X: Must provide elf.h on the include (-I) path.
- * - Must be placed in ".travis/calcrom/".
+ * - Must be placed in ".github/calcrom/".
*
* Changelog:
* - 0.1.0 (26 May 2020):
@@ -19,135 +23,82 @@
* Extra security on ELF header
* - 0.1.3 (30 Jun 2020):
* Account for diamond/pearl split
+ * - 0.2.0 (30 Aug 2021):
+ * Support hgss
+ * - 0.2.1 (31 Aug 2021):
+ * Make calcrom more generic and configurable via command line
+ * - 0.2.2 (18 Sep 2021):
+ * Handle errors when paths are missing
+ * - 0.2.3 (10 Nov 2021):
+ * Refactor classes into separate objects to improve future maintainability
+ * Report hardcoded pointers
*/
#include <iostream>
#include <fstream>
-#include <sstream>
-#include <elf.h>
-#include <glob.h>
-#include <string.h>
#include <vector>
#include <string>
+#include <algorithm>
+#include <filesystem>
+#include "BuildAnalyzer.h"
using namespace std;
+using namespace std::filesystem;
-struct Glob : public vector<char const *> {
- glob_t glob_result;
+class missing_option : public invalid_argument {
public:
- Glob(string const & pattern) {
- int result = glob(pattern.c_str(), GLOB_TILDE | GLOB_BRACE, NULL, &glob_result);
- if (result) {
- stringstream ss;
- ss << "Glob(" << pattern << ") failed with error " << result << endl;
- throw runtime_error(ss.str());
- }
- assign(glob_result.gl_pathv, glob_result.gl_pathv + glob_result.gl_pathc);
- };
- void operator~() {
- globfree(&glob_result);
- }
+ missing_option(string& error) : invalid_argument{error.c_str()} {}
};
-void analyze(string basedir, string subdir, string version) {
- fstream elf;
- Elf32_Ehdr ehdr;
- vector<Elf32_Shdr> shdr;
- stringstream pattern;
-
- // Accumulate sizes
- // src asm
- // data _____|_____
- // text |
- unsigned sizes[2][2] = {{0, 0}, {0, 0}};
- char * shstrtab = NULL;
- size_t shstrsz = 0;
- stringstream builddir;
- builddir << subdir << "/build/" << version;
- pattern << basedir << "/" << subdir << "/{src,asm,lib/src,lib/{libc,libnns,NitroSDK}/src,modules/*/{src,asm}}/*.{c,s,cpp}";
- for (char const * & fname : Glob(pattern.str()))
- {
- string fname_s(fname);
- string ext = fname_s.substr(fname_s.rfind('.'), 4);
- bool is_asm = ext == ".s";
- fname_s = fname_s.replace(fname_s.find(subdir), 4, builddir.str());
- fname_s = fname_s.replace(fname_s.rfind('.'), 4, ".o");
- elf.open(fname_s, ios_base::in | ios_base::binary);
- if (!elf.good()) {
- cerr << "Error: file not found: " << fname_s << endl;
- return;
- }
- elf.read((char *)&ehdr, sizeof(ehdr));
- if (memcmp(ehdr.e_ident, ELFMAG, SELFMAG) != 0
- || ehdr.e_ehsize != sizeof(Elf32_Ehdr)
- || ehdr.e_shentsize != sizeof(Elf32_Shdr))
- {
- elf.close();
- stringstream ss;
- ss << "Error validating " << fname_s << " as an ELF file" << endl;
- throw runtime_error(ss.str());
- }
- // Read ELF sections
- elf.seekg(ehdr.e_shoff);
- shdr.resize(ehdr.e_shnum);
- elf.read((char *)shdr.data(), ehdr.e_shnum * ehdr.e_shentsize);
-
- // Read .shstrtab
- if (shstrsz < shdr[ehdr.e_shstrndx].sh_size) {
- shstrtab = (char *)realloc(shstrtab, shdr[ehdr.e_shstrndx].sh_size);
- shstrsz = shdr[ehdr.e_shstrndx].sh_size;
- }
- elf.seekg(shdr[ehdr.e_shstrndx].sh_offset);
- elf.read(shstrtab, shdr[ehdr.e_shstrndx].sh_size);
- elf.close();
-
- // Analyze sections
- for (Elf32_Shdr & hdr : shdr) {
- string shname = shstrtab + hdr.sh_name;
- bool is_text = (shname == ".text" || shname == ".init" || shname == ".itcm");
- bool is_data = (shname == ".data" || shname == ".rodata" || shname == ".sdata" || shname == ".dtcm");
- size_t size = hdr.sh_size + (hdr.sh_size & 3 ? 4 - (hdr.sh_size & 3) : 0);
- if (is_text || is_data)
- {
- sizes[is_text][is_asm] += size;
+struct Options {
+ path arm9subdir = "";
+ path arm7subdir = "sub";
+ path projectdir = ".";
+ vector<string> romnames;
+ Options(int argc, char ** argv) {
+ for (int i = 1; i < argc; i++) {
+ string arg = argv[i];
+ if (arg == "-9") {
+ if (++i == argc) throw missing_option(arg);
+ arm9subdir = argv[i];
+ } else if (arg == "-7") {
+ if (++i == argc) throw missing_option(arg);
+ arm7subdir = argv[i];
+ } else if (arg == "-d") {
+ if (++i == argc) throw missing_option(arg);
+ projectdir = argv[i];
+ } else if (arg[0] != '-') {
+ romnames.push_back(arg);
+ } else {
+ throw invalid_argument(arg);
}
}
}
- free(shstrtab);
-
- cout << "Analysis of " << (version.empty() ? subdir : version) << " binary:" << endl;
- // Report code
- unsigned total_text = sizes[1][0] + sizes[1][1];
- double total_text_d = total_text;
- double src_text_d = sizes[1][0];
- double asm_text_d = sizes[1][1];
- cout << " " << total_text << " total bytes of code" << endl;
- cout << " " << sizes[1][0] << " bytes of code in src (" << (src_text_d / total_text_d * 100.0) << "%)" << endl;
- cout << " " << sizes[1][1] << " bytes of code in asm (" << (asm_text_d / total_text_d * 100.0) << "%)" << endl;
- cout << endl;
- // Report data
- unsigned total_data = sizes[0][0] + sizes[0][1];
- double total_data_d = total_data;
- double src_data_d = sizes[0][0];
- double asm_data_d = sizes[0][1];
- cout << " " << total_data << " total bytes of data" << endl;
- cout << " " << sizes[0][0] << " bytes of data in src (" << (src_data_d / total_data_d * 100.0) << "%)" << endl;
- cout << " " << sizes[0][1] << " bytes of data in asm (" << (asm_data_d / total_data_d * 100.0) << "%)" << endl;
- // Let vectors fall to gc
-}
+ int main() {
+ for (string &romname: romnames) {
+ cout << BuildAnalyzer(projectdir, arm9subdir, romname)() << endl;
+ }
+ cout << BuildAnalyzer(projectdir, arm7subdir)();
+ return 0;
+ }
+};
int main(int argc, char ** argv)
{
- if (argc < 2) {
- cout << "usage: calcrom PROJECT_DIR" << endl;
- throw invalid_argument("missing required argument: PROJECT_DIR\n");
+ try {
+ Options options(argc, argv);
+ return options.main();
+ } catch (missing_option& e) {
+ cerr << "Missing value for option " << e.what() << endl;
+ return 1;
+ } catch (invalid_argument& e) {
+ cerr << "Unrecognized option flag: " << e.what() << endl;
+ return 1;
+ } catch (runtime_error& e) {
+ cerr << e.what() << endl;
+ return 1;
+ } catch (exception& e) {
+ cerr << "Unhandled exception: " << e.what() << endl;
+ return 1;
}
-
- analyze(argv[1], "arm9", "diamond.us");
- cout << endl;
- analyze(argv[1], "arm9", "pearl.us");
- cout << endl;
- analyze(argv[1], "arm7", "");
-
- return 0;
}
diff --git a/.github/calcrom/webhook.sh b/.github/calcrom/webhook.sh
index c15e9b50..60726c7d 100644
--- a/.github/calcrom/webhook.sh
+++ b/.github/calcrom/webhook.sh
@@ -7,12 +7,14 @@ fi
build_name=$1
url=$2
-map_file=$(dirname "$0")/../../arm9/build/diamond.us/arm9.elf.xMAP
+build_subdir=${1//poke/}.us
+arm9name=${arm9name:-arm9.elf}
+map_file=$(dirname "$0")/../../arm9/build/${build_subdir}/${arm9name}.xMAP
if [ ! -f $map_file ]; then
echo "$map_file does not exist!"
exit 1
fi
make -C ${GITHUB_WORKSPACE}/.github/calcrom
-output=$(${GITHUB_WORKSPACE}/.github/calcrom/calcrom ${GITHUB_WORKSPACE} | sed -E ':a;N;$!ba;s/\r{0,1}\n/\\n/g')
+output=$(${GITHUB_WORKSPACE}/.github/calcrom/calcrom -d ${GITHUB_WORKSPACE} -9 arm9 -7 arm7 diamond.us pearl.us | sed -E ':a;N;$!ba;s/\r{0,1}\n/\\n/g')
curl -d "{\"username\": \"$CALCROM_DISCORD_WEBHOOK_USERNAME\", \"avatar_url\": \"$CALCROM_DISCORD_WEBHOOK_AVATAR_URL\", \"content\":\"\`\`\`$build_name progress:\\n$output\`\`\`\"}" -H "Content-Type: application/json" -X POST $url