adding tools from pokeruby

This commit is contained in:
xenonnsmb
2017-12-03 19:55:01 -06:00
parent eeaa59d837
commit e649e3d248
88 changed files with 10976 additions and 0 deletions
+1
View File
@@ -0,0 +1 @@
ramscrgen
+19
View File
@@ -0,0 +1,19 @@
Copyright (c) 2016 YamaArashi
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
+15
View File
@@ -0,0 +1,15 @@
CXX := g++
CXXFLAGS := -std=c++11 -O2 -s -Wall -Wno-switch -Werror
SRCS := main.cpp sym_file.cpp elf.cpp
HEADERS := ramscrgen.h sym_file.h elf.h char_util.h
.PHONY: clean
ramscrgen: $(SRCS) $(HEADERS)
$(CXX) $(CXXFLAGS) $(SRCS) -o $@ $(LDFLAGS)
clean:
$(RM) ramscrgen ramscrgen.exe
+71
View File
@@ -0,0 +1,71 @@
// Copyright(c) 2016 YamaArashi
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#ifndef CHAR_UTIL_H
#define CHAR_UTIL_H
#include <cstdint>
#include <cassert>
inline bool IsAscii(unsigned char c)
{
return (c < 128);
}
inline bool IsAsciiAlpha(unsigned char c)
{
return ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'));
}
inline bool IsAsciiDigit(unsigned char c)
{
return (c >= '0' && c <= '9');
}
inline bool IsAsciiHexDigit(unsigned char c)
{
return ((c >= '0' && c <= '9')
|| (c >= 'a' && c <= 'f')
|| (c >= 'A' && c <= 'F'));
}
inline bool IsAsciiAlphanum(unsigned char c)
{
return (IsAsciiAlpha(c) || IsAsciiDigit(c));
}
inline bool IsAsciiPrintable(unsigned char c)
{
return (c >= ' ' && c <= '~');
}
// Returns whether the character can start the identifier of a "{FOO}" constant in strings.
inline bool IsIdentifierStartingChar(unsigned char c)
{
return IsAsciiAlpha(c) || c == '_';
}
// Returns whether the character can be used in the identifier of a "{FOO}" constant in strings.
inline bool IsIdentifierChar(unsigned char c)
{
return IsAsciiAlphanum(c) || c == '_';
}
#endif // CHAR_UTIL_H
+195
View File
@@ -0,0 +1,195 @@
#include <cstdio>
#include <cstring>
#include <cstdint>
#include <map>
#include <vector>
#include <string>
#include "ramscrgen.h"
#include "elf.h"
#define SHN_COMMON 0xFFF2
static std::string s_elfPath;
static FILE *s_file;
static std::uint32_t s_sectionHeaderOffset;
static int s_sectionHeaderEntrySize;
static int s_sectionCount;
static int s_shstrtabIndex;
static std::uint32_t s_symtabOffset;
static std::uint32_t s_strtabOffset;
static std::uint32_t s_symbolCount;
struct Symbol
{
std::uint32_t nameOffset;
std::uint32_t size;
};
static void Seek(long offset)
{
if (std::fseek(s_file, offset, SEEK_SET) != 0)
FATAL_ERROR("error: failed to seek to %ld in \"%s\"", offset, s_elfPath.c_str());
}
static void Skip(long offset)
{
if (std::fseek(s_file, offset, SEEK_CUR) != 0)
FATAL_ERROR("error: failed to skip %ld bytes in \"%s\"", offset, s_elfPath.c_str());
}
static std::uint32_t ReadInt8()
{
int c = std::fgetc(s_file);
if (c < 0)
FATAL_ERROR("error: unexpected EOF when reading ELF file \"%s\"\n", s_elfPath.c_str());
return c;
}
static std::uint32_t ReadInt16()
{
std::uint32_t val = 0;
val |= ReadInt8();
val |= ReadInt8() << 8;
return val;
}
static std::uint32_t ReadInt32()
{
std::uint32_t val = 0;
val |= ReadInt8();
val |= ReadInt8() << 8;
val |= ReadInt8() << 16;
val |= ReadInt8() << 24;
return val;
}
static std::string ReadString()
{
std::string s;
char c;
while ((c = ReadInt8()) != 0)
s += c;
return s;
}
static void VerifyElfIdent()
{
char expectedMagic[4] = { 0x7F, 'E', 'L', 'F' };
char magic[4];
if (std::fread(magic, 4, 1, s_file) != 1)
FATAL_ERROR("error: failed to read ELF magic from \"%s\"\n", s_elfPath.c_str());
if (std::memcmp(magic, expectedMagic, 4) != 0)
FATAL_ERROR("error: ELF magic did not match in \"%s\"\n", s_elfPath.c_str());
if (std::fgetc(s_file) != 1)
FATAL_ERROR("error: \"%s\" not 32-bit ELF\n", s_elfPath.c_str());
if (std::fgetc(s_file) != 1)
FATAL_ERROR("error: \"%s\" not little-endian ELF\n", s_elfPath.c_str());
}
static void ReadElfHeader()
{
Seek(0x20);
s_sectionHeaderOffset = ReadInt32();
Seek(0x2E);
s_sectionHeaderEntrySize = ReadInt16();
s_sectionCount = ReadInt16();
s_shstrtabIndex = ReadInt16();
}
static std::string GetSectionName(std::uint32_t shstrtabOffset, int index)
{
Seek(s_sectionHeaderOffset + s_sectionHeaderEntrySize * index);
std::uint32_t nameOffset = ReadInt32();
Seek(shstrtabOffset + nameOffset);
return ReadString();
}
static void FindTableOffsets()
{
s_symtabOffset = 0;
s_strtabOffset = 0;
Seek(s_sectionHeaderOffset + s_sectionHeaderEntrySize * s_shstrtabIndex + 0x10);
std::uint32_t shstrtabOffset = ReadInt32();
for (int i = 0; i < s_sectionCount; i++)
{
std::string name = GetSectionName(shstrtabOffset, i);
if (name == ".symtab")
{
if (s_symtabOffset)
FATAL_ERROR("error: mutiple .symtab sections found in \"%s\"\n", s_elfPath.c_str());
Seek(s_sectionHeaderOffset + s_sectionHeaderEntrySize * i + 0x10);
s_symtabOffset = ReadInt32();
std::uint32_t size = ReadInt32();
s_symbolCount = size / 16;
}
else if (name == ".strtab")
{
if (s_strtabOffset)
FATAL_ERROR("error: mutiple .strtab sections found in \"%s\"\n", s_elfPath.c_str());
Seek(s_sectionHeaderOffset + s_sectionHeaderEntrySize * i + 0x10);
s_strtabOffset = ReadInt32();
}
}
if (!s_symtabOffset)
FATAL_ERROR("error: couldn't find .symtab section in \"%s\"\n", s_elfPath.c_str());
if (!s_strtabOffset)
FATAL_ERROR("error: couldn't find .strtab section in \"%s\"\n", s_elfPath.c_str());
}
std::map<std::string, std::uint32_t> GetCommonSymbols(std::string path)
{
s_elfPath = path;
std::map<std::string, std::uint32_t> commonSymbols;
s_file = std::fopen(s_elfPath.c_str(), "rb");
if (s_file == NULL)
FATAL_ERROR("error: failed to open \"%s\" for reading\n", path.c_str());
VerifyElfIdent();
ReadElfHeader();
FindTableOffsets();
std::vector<Symbol> commonSymbolVec;
Seek(s_symtabOffset);
for (std::uint32_t i = 0; i < s_symbolCount; i++)
{
Symbol sym;
sym.nameOffset = ReadInt32();
Skip(4);
sym.size = ReadInt32();
Skip(2);
std::uint16_t sectionIndex = ReadInt16();
if (sectionIndex == SHN_COMMON)
commonSymbolVec.push_back(sym);
}
for (const Symbol& sym : commonSymbolVec)
{
Seek(s_strtabOffset + sym.nameOffset);
std::string name = ReadString();
commonSymbols[name] = sym.size;
}
return commonSymbols;
}
+30
View File
@@ -0,0 +1,30 @@
// Copyright(c) 2016 YamaArashi
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#ifndef ELF_H
#define ELF_H
#include <cstdint>
#include <map>
#include <string>
std::map<std::string, std::uint32_t> GetCommonSymbols(std::string path);
#endif // ELF_H
+173
View File
@@ -0,0 +1,173 @@
// Copyright(c) 2016 YamaArashi
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#include <cstdio>
#include <cstring>
#include <string>
#include "ramscrgen.h"
#include "sym_file.h"
#include "elf.h"
void HandleCommonInclude(std::string filename, std::string sourcePath, std::string symOrderPath, std::string lang)
{
auto commonSymbols = GetCommonSymbols(sourcePath + "/" + filename);
std::size_t dotIndex = filename.find_last_of('.');
if (dotIndex == std::string::npos)
FATAL_ERROR("error: \"%s\" doesn't have a file extension\n", filename.c_str());
std::string symOrderFilename = filename.substr(0, dotIndex + 1) + "txt";
SymFile symFile(symOrderPath + "/" + symOrderFilename);
while (!symFile.IsAtEnd())
{
symFile.HandleLangConditional(lang);
std::string label = symFile.GetLabel(false);
if (label.length() == 0)
{
unsigned long length;
if (symFile.ReadInteger(length))
{
if (length & 3)
symFile.RaiseWarning("gap length %d is not multiple of 4", length);
printf(". += 0x%lX;\n", length);
}
}
else
{
if (commonSymbols.count(label) == 0)
symFile.RaiseError("no common symbol named \"%s\"", label.c_str());
unsigned long size = commonSymbols[label];
int alignment = 4;
if (size > 4)
alignment = 8;
if (size > 8)
alignment = 16;
printf(". = ALIGN(%d);\n", alignment);
printf("%s = .;\n", label.c_str());
printf(". += 0x%lX;\n", size);
}
symFile.ExpectEmptyRestOfLine();
}
}
void ConvertSymFile(std::string filename, std::string sectionName, std::string lang, bool common, std::string sourcePath, std::string commonSymPath)
{
SymFile symFile(filename);
while (!symFile.IsAtEnd())
{
symFile.HandleLangConditional(lang);
Directive directive = symFile.GetDirective();
switch (directive)
{
case Directive::Include:
{
std::string incFilename = symFile.ReadPath();
symFile.ExpectEmptyRestOfLine();
printf(". = ALIGN(4);\n");
if (common)
HandleCommonInclude(incFilename, sourcePath, commonSymPath, lang);
else
printf("%s(%s);\n", incFilename.c_str(), sectionName.c_str());
break;
}
case Directive::Space:
{
unsigned long length;
if (!symFile.ReadInteger(length))
symFile.RaiseError("expected integer after .space directive");
symFile.ExpectEmptyRestOfLine();
printf(". += 0x%lX;\n", length);
break;
}
case Directive::Align:
{
unsigned long amount;
if (!symFile.ReadInteger(amount))
symFile.RaiseError("expected integer after .align directive");
if (amount > 4)
symFile.RaiseError("max alignment amount is 4");
amount = 1UL << amount;
symFile.ExpectEmptyRestOfLine();
printf(". = ALIGN(%lu);\n", amount);
break;
}
case Directive::Unknown:
{
std::string label = symFile.GetLabel();
if (label.length() != 0)
{
printf("%s = .;\n", label.c_str());
}
symFile.ExpectEmptyRestOfLine();
break;
}
}
}
}
int main(int argc, char **argv)
{
if (argc < 4)
{
fprintf(stderr, "Usage: %s SECTION_NAME SYM_FILE LANG [-c SRC_PATH,COMMON_SYM_PATH]", argv[0]);
return 1;
}
bool common = false;
std::string sectionName = std::string(argv[1]);
std::string symFileName = std::string(argv[2]);
std::string lang = std::string(argv[3]);
std::string sourcePath;
std::string commonSymPath;
if (argc > 4)
{
if (std::strcmp(argv[4], "-c") != 0)
FATAL_ERROR("error: unrecognized argument \"%s\"\n", argv[4]);
if (argc < 6)
FATAL_ERROR("error: missing SRC_PATH,COMMON_SYM_PATH after \"-c\"\n");
common = true;
std::string paths = std::string(argv[5]);
std::size_t commaPos = paths.find(',');
if (commaPos == std::string::npos)
FATAL_ERROR("error: missing comma in argument after \"-c\"\n");
sourcePath = paths.substr(0, commaPos);
commonSymPath = paths.substr(commaPos + 1);
}
ConvertSymFile(symFileName, sectionName, lang, common, sourcePath, commonSymPath);
return 0;
}
+49
View File
@@ -0,0 +1,49 @@
// Copyright(c) 2016 YamaArashi
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#ifndef RAMSCRGEN_H
#define RAMSCRGEN_H
#include <cstdio>
#include <cstdlib>
#ifdef _MSC_VER
#define FATAL_ERROR(format, ...) \
do \
{ \
std::fprintf(stderr, format, __VA_ARGS__); \
std::exit(1); \
} while (0)
#else
#define FATAL_ERROR(format, ...) \
do \
{ \
std::fprintf(stderr, format, ##__VA_ARGS__); \
std::exit(1); \
} while (0)
#endif // _MSC_VER
const int kMaxPath = 256;
#endif // RAMSCRGEN_H
+492
View File
@@ -0,0 +1,492 @@
// Copyright(c) 2016 YamaArashi
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#include <cstdio>
#include <cstdarg>
#include <climits>
#include "ramscrgen.h"
#include "sym_file.h"
#include "char_util.h"
SymFile::SymFile(std::string filename) : m_filename(filename)
{
FILE *fp = std::fopen(filename.c_str(), "rb");
if (fp == NULL)
FATAL_ERROR("Failed to open \"%s\" for reading.\n", filename.c_str());
std::fseek(fp, 0, SEEK_END);
m_size = std::ftell(fp);
if (m_size < 0)
FATAL_ERROR("File size of \"%s\" is less than zero.\n", filename.c_str());
m_buffer = new char[m_size + 1];
std::rewind(fp);
if (std::fread(m_buffer, m_size, 1, fp) != 1)
FATAL_ERROR("Failed to read \"%s\".\n", filename.c_str());
m_buffer[m_size] = 0;
std::fclose(fp);
m_pos = 0;
m_lineNum = 1;
m_lineStart = 0;
m_inLangConditional = false;
RemoveComments();
}
SymFile::SymFile(SymFile&& other) : m_filename(std::move(other.m_filename))
{
m_buffer = other.m_buffer;
m_pos = other.m_pos;
m_size = other.m_size;
m_lineNum = other.m_lineNum;
m_lineStart = other.m_lineStart;
other.m_buffer = nullptr;
}
SymFile::~SymFile()
{
delete[] m_buffer;
}
// Removes comments to simplify further processing.
// It stops upon encountering a null character,
// which may or may not be the end of file marker.
// If it's not, the error will be caught later.
void SymFile::RemoveComments()
{
long pos = 0;
char stringChar = 0;
for (;;)
{
if (m_buffer[pos] == 0)
return;
if (stringChar != 0)
{
if (m_buffer[pos] == '\\' && m_buffer[pos + 1] == stringChar)
{
pos += 2;
}
else
{
if (m_buffer[pos] == stringChar)
stringChar = 0;
pos++;
}
}
else if (m_buffer[pos] == '@' && (pos == 0 || m_buffer[pos - 1] != '\\'))
{
while (m_buffer[pos] != '\n' && m_buffer[pos] != 0)
m_buffer[pos++] = ' ';
}
else if (m_buffer[pos] == '/' && m_buffer[pos + 1] == '*')
{
m_buffer[pos++] = ' ';
m_buffer[pos++] = ' ';
char commentStringChar = 0;
for (;;)
{
if (m_buffer[pos] == 0)
return;
if (commentStringChar != 0)
{
if (m_buffer[pos] == '\\' && m_buffer[pos + 1] == commentStringChar)
{
m_buffer[pos++] = ' ';
m_buffer[pos++] = ' ';
}
else
{
if (m_buffer[pos] == commentStringChar)
commentStringChar = 0;
if (m_buffer[pos] != '\n')
m_buffer[pos] = ' ';
pos++;
}
}
else
{
if (m_buffer[pos] == '*' && m_buffer[pos + 1] == '/')
{
m_buffer[pos++] = ' ';
m_buffer[pos++] = ' ';
break;
}
else
{
if (m_buffer[pos] == '"' || m_buffer[pos] == '\'')
commentStringChar = m_buffer[pos];
if (m_buffer[pos] != '\n')
m_buffer[pos] = ' ';
pos++;
}
}
}
}
else
{
if (m_buffer[pos] == '"' || m_buffer[pos] == '\'')
stringChar = m_buffer[pos];
pos++;
}
}
}
// Checks if we're at a particular directive and if so, consumes it.
// Returns whether the directive was found.
bool SymFile::CheckForDirective(std::string name)
{
long i;
long length = static_cast<long>(name.length());
for (i = 0; i < length && m_pos + i < m_size; i++)
if (name[i] != m_buffer[m_pos + i])
return false;
if (i < length)
return false;
m_pos += length;
return true;
}
// Checks if we're at a known directive and if so, consumes it.
// Returns which directive was found.
Directive SymFile::GetDirective()
{
SkipWhitespace();
if (CheckForDirective(".include"))
return Directive::Include;
else if (CheckForDirective(".space"))
return Directive::Space;
else if (CheckForDirective(".align"))
return Directive::Align;
else
return Directive::Unknown;
}
// Checks if we're at label.
// Returns the name if so and an empty string if not.
std::string SymFile::GetLabel(bool requireColon)
{
long start = m_pos;
long pos = m_pos;
if (IsIdentifierStartingChar(m_buffer[pos]))
{
pos++;
while (IsIdentifierChar(m_buffer[pos]))
pos++;
}
if (requireColon)
{
if (m_buffer[pos] == ':')
{
if (pos != start)
m_pos = pos + 1;
}
else
{
pos = start;
}
}
else
{
m_pos = pos;
}
return std::string(&m_buffer[start], pos - start);
}
// Skips tabs and spaces.
void SymFile::SkipWhitespace()
{
while (m_buffer[m_pos] == '\t' || m_buffer[m_pos] == ' ')
m_pos++;
}
// Reads include path.
std::string SymFile::ReadPath()
{
SkipWhitespace();
if (m_buffer[m_pos] != '"')
RaiseError("expected file path");
m_pos++;
int length = 0;
long startPos = m_pos;
while (m_buffer[m_pos] != '"')
{
unsigned char c = m_buffer[m_pos++];
if (c == 0)
{
if (m_pos >= m_size)
RaiseError("unexpected EOF in include string");
else
RaiseError("unexpected null character in include string");
}
if (!IsAsciiPrintable(c))
RaiseError("unexpected character '\\x%02X' in include string", c);
// Don't bother allowing any escape sequences.
if (c == '\\')
{
c = m_buffer[m_pos];
RaiseError("unexpected escape '\\%c' in include string", c);
}
length++;
if (length > kMaxPath)
RaiseError("path is too long");
}
m_pos++; // Go past the right quote.
return std::string(&m_buffer[startPos], length);
}
// If we're at a comma, consumes it.
// Returns whether a comma was found.
bool SymFile::ConsumeComma()
{
if (m_buffer[m_pos] == ',')
{
m_pos++;
return true;
}
return false;
}
// Converts digit character to numerical value.
static int ConvertDigit(char c, int radix)
{
int digit;
if (c >= '0' && c <= '9')
digit = c - '0';
else if (c >= 'A' && c <= 'F')
digit = 10 + c - 'A';
else if (c >= 'a' && c <= 'f')
digit = 10 + c - 'a';
else
return -1;
return (digit < radix) ? digit : -1;
}
// Reads an integer.
bool SymFile::ReadInteger(unsigned long& n)
{
SkipWhitespace();
if (!IsAsciiDigit(m_buffer[m_pos]))
return false;
int startPos = m_pos;
int radix = 10;
if (m_buffer[m_pos] == '0' && m_buffer[m_pos + 1] == 'x')
{
radix = 16;
m_pos += 2;
}
unsigned long cutoff = ULONG_MAX / radix;
unsigned long cutoffRemainder = ULONG_MAX % radix;
int digit;
n = 0;
while ((digit = ConvertDigit(m_buffer[m_pos], radix)) != -1)
{
if (n < cutoff || (n == cutoff && (unsigned long)digit <= cutoffRemainder))
{
n = n * radix + digit;
}
else
{
m_pos++;
while (ConvertDigit(m_buffer[m_pos], radix) != -1)
m_pos++;
RaiseError("integer is too large (%s)", std::string(&m_buffer[startPos], m_pos - startPos).c_str());
}
m_pos++;
}
return true;
}
// Asserts that the rest of the line is empty and moves to the next one.
void SymFile::ExpectEmptyRestOfLine()
{
SkipWhitespace();
if (m_buffer[m_pos] == 0)
{
if (m_pos >= m_size)
RaiseWarning("file doesn't end with newline");
else
RaiseError("unexpected null character");
}
else if (m_buffer[m_pos] == '\n')
{
m_pos++;
m_lineStart = m_pos;
m_lineNum++;
}
else if (m_buffer[m_pos] == '\r')
{
RaiseError("only Unix-style LF newlines are supported");
}
else
{
RaiseError("junk at end of line");
}
}
void SymFile::SkipLine()
{
while (m_buffer[m_pos] != 0 && m_buffer[m_pos] != '\n')
m_pos++;
if (m_buffer[m_pos] == '\n')
m_pos++;
}
// Checks if we're at the end of the file.
bool SymFile::IsAtEnd()
{
return (m_pos >= m_size);
}
void SymFile::HandleLangConditional(std::string lang)
{
if (m_buffer[m_pos] != '#')
return;
m_pos++;
if (CheckForDirective("begin"))
{
if (m_inLangConditional)
RaiseError("already inside language conditional");
SkipWhitespace();
std::string label = GetLabel(false);
if (label.length() == 0)
RaiseError("no language name after #begin");
ExpectEmptyRestOfLine();
if (lang == label)
{
m_inLangConditional = true;
}
else
{
while (!IsAtEnd() && m_buffer[m_pos] != '#')
SkipLine();
if (m_buffer[m_pos] != '#')
RaiseError("unterminated language conditional");
m_pos++;
if (!CheckForDirective("end"))
RaiseError("expected #end");
ExpectEmptyRestOfLine();
}
}
else if (CheckForDirective("end"))
{
if (!m_inLangConditional)
RaiseError("not inside language conditional");
m_inLangConditional = false;
ExpectEmptyRestOfLine();
}
else
{
RaiseError("unknown # directive");
}
}
// Reports a diagnostic message.
void SymFile::ReportDiagnostic(const char* type, const char* format, std::va_list args)
{
const int bufferSize = 1024;
char buffer[bufferSize];
std::vsnprintf(buffer, bufferSize, format, args);
std::fprintf(stderr, "%s:%ld: %s: %s\n", m_filename.c_str(), m_lineNum, type, buffer);
}
#define DO_REPORT(type) \
do \
{ \
std::va_list args; \
va_start(args, format); \
ReportDiagnostic(type, format, args); \
va_end(args); \
} while (0)
// Reports an error diagnostic and terminates the program.
void SymFile::RaiseError(const char* format, ...)
{
DO_REPORT("error");
std::exit(1);
}
// Reports a warning diagnostic.
void SymFile::RaiseWarning(const char* format, ...)
{
DO_REPORT("warning");
}
+71
View File
@@ -0,0 +1,71 @@
// Copyright(c) 2016 YamaArashi
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#ifndef SYM_FILE_H
#define SYM_FILE_H
#include <cstdarg>
#include <cstdint>
#include <string>
#include "ramscrgen.h"
enum class Directive
{
Include,
Space,
Align,
Unknown
};
class SymFile
{
public:
SymFile(std::string filename);
SymFile(SymFile&& other);
SymFile(const SymFile&) = delete;
~SymFile();
Directive GetDirective();
std::string GetLabel(bool requireColon = true);
std::string ReadPath();
bool ReadInteger(unsigned long& value);
void ExpectEmptyRestOfLine();
void SkipLine();
bool IsAtEnd();
void HandleLangConditional(std::string lang);
void RaiseError(const char* format, ...);
void RaiseWarning(const char* format, ...);
private:
char* m_buffer;
long m_pos;
long m_size;
long m_lineNum;
long m_lineStart;
std::string m_filename;
bool m_inLangConditional;
bool ConsumeComma();
void RemoveComments();
bool CheckForDirective(std::string name);
void SkipWhitespace();
void ReportDiagnostic(const char* type, const char* format, std::va_list args);
};
#endif // SYM_FILE_H