import from github

This commit is contained in:
2022-05-19 17:14:13 +00:00
parent 5247c34f50
commit ab32b30591
12612 changed files with 1905035 additions and 83 deletions

1
tools/ramscrgen/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
ramscrgen

19
tools/ramscrgen/LICENSE Normal file
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.

24
tools/ramscrgen/Makefile Normal file
View File

@ -0,0 +1,24 @@
CXX ?= g++
CXXFLAGS := -std=c++11 -O2 -Wall -Wno-switch -Werror
SRCS := main.cpp sym_file.cpp elf.cpp
HEADERS := ramscrgen.h sym_file.h elf.h char_util.h
.PHONY: all clean
ifeq ($(OS),Windows_NT)
EXE := .exe
else
EXE :=
endif
all: ramscrgen$(EXE)
@:
ramscrgen$(EXE): $(SRCS) $(HEADERS)
$(CXX) $(CXXFLAGS) $(SRCS) -o $@ $(LDFLAGS)
clean:
$(RM) ramscrgen ramscrgen.exe

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

272
tools/ramscrgen/elf.cpp Normal file
View File

@ -0,0 +1,272 @@
#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 std::string s_archiveFilePath;
static std::string s_archiveObjectPath;
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;
static std::uint32_t s_elfFileOffset;
struct Symbol
{
std::uint32_t nameOffset;
std::uint32_t size;
};
static void Seek(long offset)
{
if (std::fseek(s_file, s_elfFileOffset + 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 VerifyAr()
{
char expectedMagic[8] = {'!', '<', 'a', 'r', 'c', 'h', '>', '\n'};
char magic[8];
if (std::fread(magic, 8, 1, s_file) != 1)
FATAL_ERROR("error: failed to read AR magic from \"%s\"\n", s_archiveFilePath.c_str());
if (std::memcmp(magic, expectedMagic, 8) != 0)
FATAL_ERROR("error: AR magic did not match in \"%s\"\n", s_archiveFilePath.c_str());
}
static void ReadElfHeader()
{
Seek(0x20);
s_sectionHeaderOffset = ReadInt32();
Seek(0x2E);
s_sectionHeaderEntrySize = ReadInt16();
s_sectionCount = ReadInt16();
s_shstrtabIndex = ReadInt16();
}
static void FindArObj()
{
char file_ident[17] = {0};
char filesize_s[11] = {0};
char expectedEndMagic[2] = { 0x60, 0x0a };
char end_magic[2];
std::size_t filesize;
Seek(8);
while (!std::feof(s_file)) {
if (std::fread(file_ident, 16, 1, s_file) != 1)
FATAL_ERROR("error: failed to read file ident in \"%s\"\n", s_archiveFilePath.c_str());
Skip(32);
if (std::fread(filesize_s, 10, 1, s_file) != 1)
FATAL_ERROR("error: failed to read filesize in \"%s\"\n", s_archiveFilePath.c_str());
if (std::fread(end_magic, 2, 1, s_file) != 1)
FATAL_ERROR("error: failed to read end sentinel in \"%s\"\n", s_archiveFilePath.c_str());
if (std::memcmp(end_magic, expectedEndMagic, 2) != 0)
FATAL_ERROR("error: corrupted archive header in \"%s\" at \"%s\"\n", s_archiveFilePath.c_str(), file_ident);
char * ptr = std::strchr(file_ident, '/');
if (ptr != nullptr)
*ptr = 0;
filesize = std::strtoul(filesize_s, nullptr, 10);
if (std::strncmp(s_archiveObjectPath.c_str(), file_ident, 16) == 0) {
s_elfFileOffset = std::ftell(s_file);
return;
}
Skip(filesize);
}
FATAL_ERROR("error: could not find object \"%s\" in archive \"%s\"\n", s_archiveObjectPath.c_str(), s_archiveFilePath.c_str());
}
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());
}
static std::map<std::string, std::uint32_t> GetCommonSymbols_Shared()
{
VerifyElfIdent();
ReadElfHeader();
FindTableOffsets();
std::map<std::string, std::uint32_t> commonSymbols;
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;
}
std::map<std::string, std::uint32_t> GetCommonSymbolsFromLib(std::string sourcePath, std::string libpath)
{
std::size_t colonPos = libpath.find(':');
if (colonPos == std::string::npos)
FATAL_ERROR("error: missing colon separator in libfile \"%s\"\n", s_elfPath.c_str());
s_archiveObjectPath = libpath.substr(colonPos + 1);
s_archiveFilePath = sourcePath + "/" + libpath.substr(1, colonPos - 1);
s_elfPath = sourcePath + "/" + libpath.substr(1);
s_file = std::fopen(s_archiveFilePath.c_str(), "rb");
if (s_file == NULL)
FATAL_ERROR("error: failed to open \"%s\" for reading\n", s_archiveFilePath.c_str());
VerifyAr();
FindArObj();
return GetCommonSymbols_Shared();
}
std::map<std::string, std::uint32_t> GetCommonSymbols(std::string sourcePath, std::string path)
{
s_elfFileOffset = 0;
if (path[0] == '*')
return GetCommonSymbolsFromLib(sourcePath, path);
s_elfPath = sourcePath + "/" + path;
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());
return GetCommonSymbols_Shared();
}

30
tools/ramscrgen/elf.h Normal file
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 sourcePath, std::string path);
#endif // ELF_H

187
tools/ramscrgen/main.cpp Normal file
View File

@ -0,0 +1,187 @@
// 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;
if (filename[0] == '*') {
dotIndex = filename.find_last_of(':');
filename = filename.substr(dotIndex + 1);
}
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, std::string libSourcePath)
{
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, incFilename[0] == '*' ? libSourcePath : 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;
std::string libSourcePath;
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);
commaPos = commonSymPath.find(',');
if (commaPos == std::string::npos) {
libSourcePath = "tools/agbcc/lib";
} else {
libSourcePath = commonSymPath.substr(commaPos + 1);
commonSymPath = commonSymPath.substr(0, commaPos);
}
}
ConvertSymFile(symFileName, sectionName, lang, common, sourcePath, commonSymPath, libSourcePath);
return 0;
}

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

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");
}

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