diff options
author | yenatch <yenatch@gmail.com> | 2017-03-22 18:34:29 -0400 |
---|---|---|
committer | yenatch <yenatch@gmail.com> | 2017-03-22 18:34:29 -0400 |
commit | 7acbfe40778bfbd03b0e2d357efef9ee9b0a34e9 (patch) | |
tree | 90c0b95146f3d768b536658eac6a63ff9adcaaec /tools | |
parent | c0a27d5455014bfe54f7ea73e8a38ce0ed8da6da (diff) | |
parent | e3c26e52ae6ab881ade913ca1fad2cd9629bbcb1 (diff) |
Merge remote-tracking branch 'origin/master'
Conflicts:
Makefile
Diffstat (limited to 'tools')
-rw-r--r-- | tools/preproc/asm_file.cpp | 39 | ||||
-rw-r--r-- | tools/preproc/c_file.cpp | 334 | ||||
-rw-r--r-- | tools/preproc/c_file.h | 8 | ||||
-rw-r--r-- | tools/preproc/char_util.h | 4 | ||||
-rw-r--r-- | tools/preproc/preproc.cpp | 14 | ||||
-rw-r--r-- | tools/scaninc/Makefile | 6 | ||||
-rw-r--r-- | tools/scaninc/asm_file.cpp | 191 | ||||
-rw-r--r-- | tools/scaninc/asm_file.h | 119 | ||||
-rw-r--r-- | tools/scaninc/c_file.cpp | 287 | ||||
-rw-r--r-- | tools/scaninc/c_file.h | 53 | ||||
-rw-r--r-- | tools/scaninc/scaninc.cpp | 382 | ||||
-rw-r--r-- | tools/scaninc/scaninc.h | 59 |
12 files changed, 1092 insertions, 404 deletions
diff --git a/tools/preproc/asm_file.cpp b/tools/preproc/asm_file.cpp index 7deaccc8a..bb296b78b 100644 --- a/tools/preproc/asm_file.cpp +++ b/tools/preproc/asm_file.cpp @@ -111,45 +111,22 @@ void AsmFile::RemoveComments() 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] == '/') { - 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++; - } + m_buffer[pos++] = ' '; + m_buffer[pos++] = ' '; + break; } 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++; - } + if (m_buffer[pos] != '\n') + m_buffer[pos] = ' '; + pos++; } } } @@ -517,7 +494,7 @@ bool AsmFile::IsAtEnd() // Output the current location to set gas's logical file and line numbers. void AsmFile::OutputLocation() { - printf("# %ld \"%s\"\n", m_lineNum, m_filename.c_str()); + std::printf("# %ld \"%s\"\n", m_lineNum, m_filename.c_str()); } // Reports a diagnostic message. diff --git a/tools/preproc/c_file.cpp b/tools/preproc/c_file.cpp index aed53d44b..5bfdee086 100644 --- a/tools/preproc/c_file.cpp +++ b/tools/preproc/c_file.cpp @@ -20,6 +20,8 @@ #include <cstdio> #include <cstdarg> +#include <string> +#include <memory> #include "preproc.h" #include "c_file.h" #include "char_util.h" @@ -72,8 +74,6 @@ CFile::~CFile() void CFile::Preproc() { - bool inConcatMode = false; - bool noTerminator = false; char stringChar = 0; while (m_pos < m_size) @@ -94,85 +94,297 @@ void CFile::Preproc() } else { + if (m_buffer[m_pos] == '\n') + m_lineNum++; std::putchar(m_buffer[m_pos]); m_pos++; } } else { - if (inConcatMode ? m_buffer[m_pos] == '"' - : (m_buffer[m_pos] == '_' || m_buffer[m_pos] == '@') && m_buffer[m_pos + 1] == '"') + TryConvertString(); + TryConvertIncbin(); + + if (m_pos >= m_size) + break; + + char c = m_buffer[m_pos++]; + + std::putchar(c); + + if (c == '\n') + m_lineNum++; + else if (c == '"') + stringChar = '"'; + else if (c == '\'') + stringChar = '\''; + } + } +} + +bool CFile::ConsumeHorizontalWhitespace() +{ + if (m_buffer[m_pos] == '\t' || m_buffer[m_pos] == ' ') + { + m_pos++; + return true; + } + + return false; +} + +bool CFile::ConsumeNewline() +{ + if (m_buffer[m_pos] == '\r' && m_buffer[m_pos + 1] == '\n') + { + m_pos += 2; + m_lineNum++; + return true; + } + + if (m_buffer[m_pos] == '\n') + { + m_pos++; + m_lineNum++; + return true; + } + + return false; +} + +void CFile::SkipWhitespace() +{ + while (ConsumeHorizontalWhitespace() || ConsumeNewline()) + ; +} + +void CFile::TryConvertString() +{ + long oldPos = m_pos; + long oldLineNum = m_lineNum; + bool noTerminator = false; + + if (m_buffer[m_pos] != '_' || (m_pos > 0 && IsIdentifierChar(m_buffer[m_pos - 1]))) + return; + + m_pos++; + + if (m_buffer[m_pos] == '_') + { + noTerminator = true; + m_pos++; + } + + SkipWhitespace(); + + if (m_buffer[m_pos] != '(') + { + m_pos = oldPos; + m_lineNum = oldLineNum; + return; + } + + m_pos++; + + SkipWhitespace(); + + std::printf("{ "); + + while (1) + { + SkipWhitespace(); + + if (m_buffer[m_pos] == '"') + { + unsigned char s[kMaxStringLength]; + int length; + StringParser stringParser(m_buffer, m_size); + + try { - if (!inConcatMode) - { - noTerminator = (m_buffer[m_pos] == '@'); - m_pos++; // skip past underscore or at-sign - } - - unsigned char s[kMaxStringLength]; - int length; - StringParser stringParser(m_buffer, m_size); - - try - { - m_pos += stringParser.ParseString(m_pos, s, length); - } - catch (std::runtime_error e) - { - RaiseError(e.what()); - } - - if (!inConcatMode) - { - std::printf("{ "); - } - - inConcatMode = true; - - for (int i = 0; i < length; i++) - printf("0x%02X, ", s[i]); + m_pos += stringParser.ParseString(m_pos, s, length); } - else + catch (std::runtime_error e) { - char c = m_buffer[m_pos++]; + RaiseError(e.what()); + } + + for (int i = 0; i < length; i++) + printf("0x%02X, ", s[i]); + } + else if (m_buffer[m_pos] == ')') + { + m_pos++; + break; + } + else + { + if (m_pos >= m_size) + RaiseError("unexpected EOF"); + if (IsAsciiPrintable(m_buffer[m_pos])) + RaiseError("unexpected character '%c'", m_buffer[m_pos]); + else + RaiseError("unexpected character '\\x%02X'", m_buffer[m_pos]); + } + } + + if (noTerminator) + std::printf(" }"); + else + std::printf("0xFF }"); +} + +bool CFile::CheckIdentifier(const std::string& ident) +{ + unsigned int i; - if (c == '\r') - { - if (m_buffer[m_pos] == '\n') - { - m_pos++; - } + for (i = 0; i < ident.length() && m_pos + i < (unsigned)m_size; i++) + if (ident[i] != m_buffer[m_pos + i]) + return false; - c = '\n'; - } + return (i == ident.length()); +} - if ((c != ' ' && c != '\t' && c != '\n') && inConcatMode) - { - if (noTerminator) - std::printf(" }"); - else - std::printf("0xFF }"); +std::unique_ptr<unsigned char[]> CFile::ReadWholeFile(const std::string& path, int& size) +{ + FILE* fp = std::fopen(path.c_str(), "rb"); - inConcatMode = false; - } + if (fp == nullptr) + RaiseError("Failed to open \"%s\" for reading.\n", path.c_str()); - std::putchar(c); + std::fseek(fp, 0, SEEK_END); - if (c == '\n') - m_lineNum++; - else if (c == '"') - stringChar = '"'; - else if (m_buffer[m_pos] == '\'') - stringChar = '\''; - } + size = std::ftell(fp); + + std::unique_ptr<unsigned char[]> buffer = std::unique_ptr<unsigned char[]>(new unsigned char[size]); + + std::rewind(fp); + + if (std::fread(buffer.get(), size, 1, fp) != 1) + RaiseError("Failed to read \"%s\".\n", path.c_str()); + + std::fclose(fp); + + return buffer; +} + +int ExtractData(const std::unique_ptr<unsigned char[]>& buffer, int offset, int size) +{ + switch (size) + { + case 1: + return buffer[offset]; + case 2: + return (buffer[offset + 1] << 8) + | buffer[offset]; + case 4: + return (buffer[offset + 3] << 24) + | (buffer[offset + 2] << 16) + | (buffer[offset + 1] << 8) + | buffer[offset]; + default: + FATAL_ERROR("Invalid size passed to ExtractData.\n"); + } +} + +void CFile::TryConvertIncbin() +{ + std::string idents[6] = { "INCBIN_S8", "INCBIN_U8", "INCBIN_S16", "INCBIN_U16", "INCBIN_S32", "INCBIN_U32" }; + int incbinType = -1; + + for (int i = 0; i < 6; i++) + { + if (CheckIdentifier(idents[i])) + { + incbinType = i; + break; } } - if (inConcatMode) + if (incbinType == -1) + return; + + int size = 1 << (incbinType / 2); + bool isSigned = ((incbinType % 2) == 0); + + long oldPos = m_pos; + long oldLineNum = m_lineNum; + + m_pos += idents[incbinType].length(); + + SkipWhitespace(); + + if (m_buffer[m_pos] != '(') { - printf("0xFF }"); - RaiseWarning("string at end of file"); + m_pos = oldPos; + m_lineNum = oldLineNum; + return; } + + m_pos++; + + SkipWhitespace(); + + if (m_buffer[m_pos] != '"') + RaiseError("expected double quote"); + + m_pos++; + + int startPos = m_pos; + + while (m_buffer[m_pos] != '"') + { + if (m_buffer[m_pos] == 0) + { + if (m_pos >= m_size) + RaiseError("unexpected EOF in path string"); + else + RaiseError("unexpected null character in path string"); + } + + if (m_buffer[m_pos] == '\r' || m_buffer[m_pos] == '\n') + RaiseError("unexpected end of line character in path string"); + + if (m_buffer[m_pos] == '\\') + RaiseError("unexpected escape in path string"); + + m_pos++; + } + + std::string path(&m_buffer[startPos], m_pos - startPos); + + m_pos++; + + SkipWhitespace(); + + if (m_buffer[m_pos] != ')') + RaiseError("expected ')'"); + + m_pos++; + + std::printf("{"); + + int fileSize; + std::unique_ptr<unsigned char[]> buffer = ReadWholeFile(path, fileSize); + + if ((fileSize % size) != 0) + RaiseError("Size %d doesn't evenly divide file size %d.\n", size, fileSize); + + int count = fileSize / size; + int offset = 0; + + for (int i = 0; i < count; i++) + { + int data = ExtractData(buffer, offset, size); + offset += size; + + if (isSigned) + std::printf("%d,", data); + else + std::printf("%uu,", data); + } + + std::printf("}"); } // Reports a diagnostic message. diff --git a/tools/preproc/c_file.h b/tools/preproc/c_file.h index b6041cc00..7369aba85 100644 --- a/tools/preproc/c_file.h +++ b/tools/preproc/c_file.h @@ -24,6 +24,7 @@ #include <cstdarg> #include <cstdint> #include <string> +#include <memory> #include "preproc.h" class CFile @@ -42,6 +43,13 @@ private: long m_lineNum; std::string m_filename; + bool ConsumeHorizontalWhitespace(); + bool ConsumeNewline(); + void SkipWhitespace(); + void TryConvertString(); + std::unique_ptr<unsigned char[]> ReadWholeFile(const std::string& path, int& size); + bool CheckIdentifier(const std::string& ident); + void TryConvertIncbin(); void ReportDiagnostic(const char* type, const char* format, std::va_list args); void RaiseError(const char* format, ...); void RaiseWarning(const char* format, ...); diff --git a/tools/preproc/char_util.h b/tools/preproc/char_util.h index ab20dbc53..02a6e1ceb 100644 --- a/tools/preproc/char_util.h +++ b/tools/preproc/char_util.h @@ -56,13 +56,13 @@ inline bool IsAsciiPrintable(unsigned char c) return (c >= ' ' && c <= '~'); } -// Returns whether the character can start the identifier of a "{FOO}" constant in strings. +// Returns whether the character can start a C identifier or 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. +// Returns whether the character can be used in a C identifier or the identifier of a "{FOO}" constant in strings. inline bool IsIdentifierChar(unsigned char c) { return IsAsciiAlphanum(c) || c == '_'; diff --git a/tools/preproc/preproc.cpp b/tools/preproc/preproc.cpp index b51861580..8320a2d27 100644 --- a/tools/preproc/preproc.cpp +++ b/tools/preproc/preproc.cpp @@ -31,15 +31,15 @@ void PrintAsmBytes(unsigned char *s, int length) { if (length > 0) { - printf("\t.byte "); + std::printf("\t.byte "); for (int i = 0; i < length; i++) { - printf("0x%02X", s[i]); + std::printf("0x%02X", s[i]); if (i < length - 1) - printf(", "); + std::printf(", "); } - putchar('\n'); + std::putchar('\n'); } } @@ -89,8 +89,8 @@ void PreprocAsmFile(std::string filename) if (globalLabel.length() != 0) { - printf("\t.global %s\n", globalLabel.c_str()); - printf("%s:\n", globalLabel.c_str()); + std::printf("\t.global %s\n", globalLabel.c_str()); + std::printf("%s:\n", globalLabel.c_str()); } else { @@ -134,7 +134,7 @@ int main(int argc, char **argv) { if (argc != 3) { - fprintf(stderr, "Usage: %s SRC_FILE CHARMAP_FILE", argv[0]); + std::fprintf(stderr, "Usage: %s SRC_FILE CHARMAP_FILE", argv[0]); return 1; } diff --git a/tools/scaninc/Makefile b/tools/scaninc/Makefile index 0efe283f1..71c4389ef 100644 --- a/tools/scaninc/Makefile +++ b/tools/scaninc/Makefile @@ -2,11 +2,13 @@ CXX = g++ CXXFLAGS = -Wall -std=c++11 -O2 -SRCS = scaninc.cpp +SRCS = scaninc.cpp c_file.cpp asm_file.cpp + +HEADERS := scaninc.h asm_file.h c_file.h .PHONY: clean -scaninc: $(SRCS) +scaninc: $(SRCS) $(HEADERS) $(CXX) $(CXXFLAGS) $(SRCS) -o $@ clean: diff --git a/tools/scaninc/asm_file.cpp b/tools/scaninc/asm_file.cpp new file mode 100644 index 000000000..c3d140bb1 --- /dev/null +++ b/tools/scaninc/asm_file.cpp @@ -0,0 +1,191 @@ +// Copyright(c) 2015-2017 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 <string> +#include "scaninc.h" +#include "asm_file.h" + +AsmFile::AsmFile(std::string path) +{ + m_path = path; + + FILE *fp = std::fopen(path.c_str(), "rb"); + + if (fp == NULL) + FATAL_ERROR("Failed to open \"%s\" for reading.\n", path.c_str()); + + std::fseek(fp, 0, SEEK_END); + + m_size = std::ftell(fp); + + m_buffer = new char[m_size]; + + std::rewind(fp); + + if (std::fread(m_buffer, m_size, 1, fp) != 1) + FATAL_ERROR("Failed to read \"%s\".\n", path.c_str()); + + std::fclose(fp); + + m_pos = 0; + m_lineNum = 1; +} + +AsmFile::~AsmFile() +{ + delete[] m_buffer; +} + +IncDirectiveType AsmFile::ReadUntilIncDirective(std::string &path) +{ + // At the beginning of each loop iteration, the current file position + // should be at the start of a line or at the end of the file. + for (;;) + { + SkipTabsAndSpaces(); + + IncDirectiveType incDirectiveType = IncDirectiveType::None; + + if (PeekChar() == '.') + { + m_pos++; + + if (MatchIncDirective("incbin", path)) + incDirectiveType = IncDirectiveType::Incbin; + else if (MatchIncDirective("include", path)) + incDirectiveType = IncDirectiveType::Include; + } + + for (;;) + { + int c = GetChar(); + + if (c == -1) + return incDirectiveType; + + if (c == ';') + { + SkipEndOfLineComment(); + break; + } + else if (c == '/' && PeekChar() == '*') + { + m_pos++; + SkipMultiLineComment(); + } + else if (c == '"') + { + SkipString(); + } + else if (c == '\n') + { + break; + } + } + + if (incDirectiveType != IncDirectiveType::None) + return incDirectiveType; + } +} + +std::string AsmFile::ReadPath() +{ + int length = 0; + int startPos = m_pos; + + for (;;) + { + int c = GetChar(); + + if (c == '"') + break; + + if (c == -1) + FATAL_INPUT_ERROR("unexpected EOF in include string\n"); + + if (c == 0) + FATAL_INPUT_ERROR("unexpected NUL character in include string\n"); + + if (c == '\n') + FATAL_INPUT_ERROR("unexpected end of line character in include string\n"); + + // Don't bother allowing any escape sequences. + if (c == '\\') + FATAL_INPUT_ERROR("unexpected escape in include string\n"); + + length++; + + if (length > SCANINC_MAX_PATH) + FATAL_INPUT_ERROR("path is too long"); + } + + return std::string(m_buffer, startPos, length); +} + +void AsmFile::SkipEndOfLineComment() +{ + int c; + + do + { + c = GetChar(); + } while (c != -1 && c != '\n'); +} + +void AsmFile::SkipMultiLineComment() +{ + for (;;) + { + int c = GetChar(); + + if (c == '*') + { + if (PeekChar() == '/') + { + m_pos++; + return; + } + } + else if (c == -1) + { + return; + } + } +} + +void AsmFile::SkipString() +{ + for (;;) + { + int c = GetChar(); + + if (c == '"') + break; + + if (c == -1) + FATAL_INPUT_ERROR("unexpected EOF in string\n"); + + if (c == '\\') + { + c = GetChar(); + } + } +} diff --git a/tools/scaninc/asm_file.h b/tools/scaninc/asm_file.h new file mode 100644 index 000000000..ad99b757e --- /dev/null +++ b/tools/scaninc/asm_file.h @@ -0,0 +1,119 @@ +// Copyright(c) 2015-2017 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 ASM_FILE_H +#define ASM_FILE_H + +#include <string> +#include "scaninc.h" + +enum class IncDirectiveType +{ + None, + Include, + Incbin +}; + +class AsmFile +{ +public: + AsmFile(std::string path); + ~AsmFile(); + IncDirectiveType ReadUntilIncDirective(std::string& path); + +private: + char *m_buffer; + int m_pos; + int m_size; + int m_lineNum; + std::string m_path; + + int GetChar() + { + if (m_pos >= m_size) + return -1; + + int c = m_buffer[m_pos++]; + + if (c == '\r') + { + if (m_pos < m_size && m_buffer[m_pos++] == '\n') + { + m_lineNum++; + return '\n'; + } + else + { + FATAL_INPUT_ERROR("CR line endings are not supported\n"); + } + } + + if (c == '\n') + m_lineNum++; + + return c; + } + + // No newline translation because it's not needed for any use of this function. + int PeekChar() + { + if (m_pos >= m_size) + return -1; + + return m_buffer[m_pos]; + } + + void SkipTabsAndSpaces() + { + while (m_pos < m_size && (m_buffer[m_pos] == '\t' || m_buffer[m_pos] == ' ')) + m_pos++; + } + + bool MatchIncDirective(std::string directiveName, std::string& path) + { + int length = directiveName.length(); + int i; + + for (i = 0; i < length && m_pos + i < m_size; i++) + if (directiveName[i] != m_buffer[m_pos + i]) + return false; + + if (i < length) + return false; + + m_pos += length; + + SkipTabsAndSpaces(); + + if (GetChar() != '"') + FATAL_INPUT_ERROR("no path after \".%s\" directive\n", directiveName.c_str()); + + path = ReadPath(); + + return true; + } + + std::string ReadPath(); + void SkipEndOfLineComment(); + void SkipMultiLineComment(); + void SkipString(); +}; + +#endif // ASM_FILE_H diff --git a/tools/scaninc/c_file.cpp b/tools/scaninc/c_file.cpp new file mode 100644 index 000000000..b82276dd6 --- /dev/null +++ b/tools/scaninc/c_file.cpp @@ -0,0 +1,287 @@ +// Copyright(c) 2017 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 "c_file.h" + +CFile::CFile(std::string path) +{ + m_path = path; + + FILE *fp = std::fopen(path.c_str(), "rb"); + + if (fp == NULL) + FATAL_ERROR("Failed to open \"%s\" for reading.\n", path.c_str()); + + std::fseek(fp, 0, SEEK_END); + + m_size = std::ftell(fp); + + m_buffer = new char[m_size + 1]; + m_buffer[m_size] = 0; + + std::rewind(fp); + + if (std::fread(m_buffer, m_size, 1, fp) != 1) + FATAL_ERROR("Failed to read \"%s\".\n", path.c_str()); + + std::fclose(fp); + + m_pos = 0; + m_lineNum = 1; + + RemoveComments(); +} + +CFile::~CFile() +{ + 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 CFile::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] == '/' && 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++] = ' '; + + for (;;) + { + if (m_buffer[pos] == 0) + return; + + if (m_buffer[pos] == '*' && m_buffer[pos + 1] == '/') + { + m_buffer[pos++] = ' '; + m_buffer[pos++] = ' '; + break; + } + else + { + if (m_buffer[pos] != '\n') + m_buffer[pos] = ' '; + pos++; + } + } + } + else + { + if (m_buffer[pos] == '"' || m_buffer[pos] == '\'') + stringChar = m_buffer[pos]; + pos++; + } + } +} + +void CFile::FindIncbins() +{ + char stringChar = 0; + + while (m_pos < m_size) + { + if (stringChar) + { + if (m_buffer[m_pos] == stringChar) + { + m_pos++; + stringChar = 0; + } + else if (m_buffer[m_pos] == '\\' && m_buffer[m_pos + 1] == stringChar) + { + m_pos += 2; + } + else + { + if (m_buffer[m_pos] == '\n') + m_lineNum++; + m_pos++; + } + } + else + { + CheckIncbin(); + + if (m_pos >= m_size) + break; + + char c = m_buffer[m_pos++]; + + if (c == '\n') + m_lineNum++; + else if (c == '"') + stringChar = '"'; + else if (c == '\'') + stringChar = '\''; + else if (c == 0) + FATAL_INPUT_ERROR("unexpected null character"); + } + } +} + +bool CFile::ConsumeHorizontalWhitespace() +{ + if (m_buffer[m_pos] == '\t' || m_buffer[m_pos] == ' ') + { + m_pos++; + return true; + } + + return false; +} + +bool CFile::ConsumeNewline() +{ + if (m_buffer[m_pos] == '\r' && m_buffer[m_pos + 1] == '\n') + { + m_pos += 2; + m_lineNum++; + return true; + } + + if (m_buffer[m_pos] == '\n') + { + m_pos++; + m_lineNum++; + return true; + } + + return false; +} + +void CFile::SkipWhitespace() +{ + while (ConsumeHorizontalWhitespace() || ConsumeNewline()) + ; +} + +bool CFile::CheckIdentifier(const std::string& ident) +{ + unsigned int i; + + for (i = 0; i < ident.length() && m_pos + i < (unsigned)m_size; i++) + if (ident[i] != m_buffer[m_pos + i]) + return false; + + return (i == ident.length()); +} + +void CFile::CheckIncbin() +{ + std::string idents[6] = { "INCBIN_S8", "INCBIN_U8", "INCBIN_S16", "INCBIN_U16", "INCBIN_S32", "INCBIN_U32" }; + int incbinType = -1; + + for (int i = 0; i < 6; i++) + { + if (CheckIdentifier(idents[i])) + { + incbinType = i; + break; + } + } + + if (incbinType == -1) + return; + + long oldPos = m_pos; + long oldLineNum = m_lineNum; + + m_pos += idents[incbinType].length(); + + SkipWhitespace(); + + if (m_buffer[m_pos] != '(') + { + m_pos = oldPos; + m_lineNum = oldLineNum; + return; + } + + m_pos++; + + SkipWhitespace(); + + if (m_buffer[m_pos] != '"') + FATAL_INPUT_ERROR("expected double quote"); + + m_pos++; + + int startPos = m_pos; + + while (m_buffer[m_pos] != '"') + { + if (m_buffer[m_pos] == 0) + { + if (m_pos >= m_size) + FATAL_INPUT_ERROR("unexpected EOF in path string"); + else + FATAL_INPUT_ERROR("unexpected null character in path string"); + } + + if (m_buffer[m_pos] == '\r' || m_buffer[m_pos] == '\n') + FATAL_INPUT_ERROR("unexpected end of line character in path string"); + + if (m_buffer[m_pos] == '\\') + FATAL_INPUT_ERROR("unexpected escape in path string"); + + m_pos++; + } + + std::string path(&m_buffer[startPos], m_pos - startPos); + + m_pos++; + + SkipWhitespace(); + + if (m_buffer[m_pos] != ')') + FATAL_INPUT_ERROR("expected ')'"); + + m_pos++; + + m_incbins.emplace(path); +} diff --git a/tools/scaninc/c_file.h b/tools/scaninc/c_file.h new file mode 100644 index 000000000..922cb4639 --- /dev/null +++ b/tools/scaninc/c_file.h @@ -0,0 +1,53 @@ +// Copyright(c) 2017 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 C_FILE_H +#define C_FILE_H + +#include <string> +#include <set> +#include <memory> +#include "scaninc.h" + +class CFile +{ +public: + CFile(std::string path); + ~CFile(); + void FindIncbins(); + const std::set<std::string>& GetIncbins() { return m_incbins; } + +private: + char *m_buffer; + int m_pos; + int m_size; + int m_lineNum; + std::string m_path; + std::set<std::string> m_incbins; + + void RemoveComments(); + bool ConsumeHorizontalWhitespace(); + bool ConsumeNewline(); + void SkipWhitespace(); + bool CheckIdentifier(const std::string& ident); + void CheckIncbin(); +}; + +#endif // C_FILE_H diff --git a/tools/scaninc/scaninc.cpp b/tools/scaninc/scaninc.cpp index 3bd6b81a5..b6f7ba767 100644 --- a/tools/scaninc/scaninc.cpp +++ b/tools/scaninc/scaninc.cpp @@ -1,317 +1,97 @@ -// Copyright (c) 2015 YamaArashi +// Copyright(c) 2015-2017 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 <cstdlib> #include <stack> #include <set> #include <string> - -#ifdef _MSC_VER - -#define FATAL_INPUT_ERROR(format, ...) \ -do { \ - fprintf(stderr, "%s:%d " format, m_path.c_str(), m_lineNum, __VA_ARGS__); \ - exit(1); \ -} while (0) - -#define FATAL_ERROR(format, ...) \ -do { \ - fprintf(stderr, format, __VA_ARGS__); \ - exit(1); \ -} while (0) - -#else - -#define FATAL_INPUT_ERROR(format, ...) \ -do { \ - fprintf(stderr, "%s:%d " format, m_path.c_str(), m_lineNum, ##__VA_ARGS__); \ - exit(1); \ -} while (0) - -#define FATAL_ERROR(format, ...) \ -do { \ - fprintf(stderr, format, ##__VA_ARGS__); \ - exit(1); \ -} while (0) - -#endif // _MSC_VER - -#define SCANINC_MAX_PATH 255 - -enum class IncDirectiveType { - None, - Include, - Incbin -}; - -class AsmFile -{ -public: - AsmFile(std::string path); - ~AsmFile(); - IncDirectiveType ReadUntilIncDirective(std::string &path); - -private: - char *m_buffer; - int m_pos; - int m_size; - int m_lineNum; - std::string m_path; - - int GetChar() - { - if (m_pos >= m_size) - return -1; - - int c = m_buffer[m_pos++]; - - if (c == '\r') { - if (m_pos < m_size && m_buffer[m_pos++] == '\n') { - m_lineNum++; - return '\n'; - } else { - FATAL_INPUT_ERROR("CR line endings are not supported\n"); - } - } - - if (c == '\n') - m_lineNum++; - - return c; - } - - // No newline translation because it's not needed for any use of this function. - int PeekChar() - { - if (m_pos >= m_size) - return -1; - - return m_buffer[m_pos]; - } - - void SkipTabsAndSpaces() - { - while (m_pos < m_size && (m_buffer[m_pos] == '\t' || m_buffer[m_pos] == ' ')) - m_pos++; - } - - bool MatchIncDirective(std::string directiveName, std::string &path) - { - int length = directiveName.length(); - int i; - - for (i = 0; i < length && m_pos + i < m_size; i++) - if (directiveName[i] != m_buffer[m_pos + i]) - return false; - - if (i < length) - return false; - - m_pos += length; - - SkipTabsAndSpaces(); - - if (GetChar() != '"') - FATAL_INPUT_ERROR("no path after \".%s\" directive\n", directiveName.c_str()); - - path = ReadPath(); - - return true; - } - - std::string ReadPath(); - void SkipEndOfLineComment(); - void SkipMultiLineComment(); - void SkipString(); -}; - -AsmFile::AsmFile(std::string path) -{ - m_path = path; - - FILE *fp = fopen(path.c_str(), "rb"); - - if (fp == NULL) - FATAL_ERROR("Failed to open \"%s\" for reading.\n", path.c_str()); - - fseek(fp, 0, SEEK_END); - - m_size = ftell(fp); - - m_buffer = new char[m_size]; - - rewind(fp); - - if (fread(m_buffer, m_size, 1, fp) != 1) - FATAL_ERROR("Failed to read \"%s\".\n", path.c_str()); - - fclose(fp); - - m_pos = 0; - m_lineNum = 1; -} - -AsmFile::~AsmFile() -{ - delete[] m_buffer; -} - -IncDirectiveType AsmFile::ReadUntilIncDirective(std::string &path) -{ - // At the beginning of each loop iteration, the current file position - // should be at the start of a line or at the end of the file. - for (;;) { - SkipTabsAndSpaces(); - - IncDirectiveType incDirectiveType = IncDirectiveType::None; - - if (PeekChar() == '.') { - m_pos++; - - if (MatchIncDirective("incbin", path)) - incDirectiveType = IncDirectiveType::Incbin; - else if (MatchIncDirective("include", path)) - incDirectiveType = IncDirectiveType::Include; - } - - for (;;) { - int c = GetChar(); - - if (c == -1) - return incDirectiveType; - - if (c == ';') { - SkipEndOfLineComment(); - break; - } else if (c == '/' && PeekChar() == '*') { - m_pos++; - SkipMultiLineComment(); - } else if (c == '"') { - SkipString(); - } else if (c == '\n') { - break; - } - } - - if (incDirectiveType != IncDirectiveType::None) - return incDirectiveType; - } -} - -std::string AsmFile::ReadPath() -{ - int length = 0; - int startPos = m_pos; - - for (;;) { - int c = GetChar(); - - if (c == '"') - break; - - if (c == -1) - FATAL_INPUT_ERROR("unexpected EOF in include string\n"); - - if (c == 0) - FATAL_INPUT_ERROR("unexpected NUL character in include string\n"); - - if (c == '\n') - FATAL_INPUT_ERROR("unexpected end of line character in include string\n"); - - // Don't bother allowing any escape sequences. - if (c == '\\') - FATAL_INPUT_ERROR("unexpected escape '\\%c' in include string\n", c); - - length++; - - if (length > SCANINC_MAX_PATH) - FATAL_INPUT_ERROR("path is too long"); - } - - return std::string(m_buffer, startPos, length); -} - -void AsmFile::SkipEndOfLineComment() -{ - int c; - - do { - c = GetChar(); - } while (c != -1 && c != '\n'); -} - -void AsmFile::SkipMultiLineComment() -{ - for (;;) { - int c = GetChar(); - - if (c == '*') { - if (PeekChar() == '/') { - m_pos++; - return; - } - } else if (c == -1) { - return; - } - } -} - -void AsmFile::SkipString() -{ - for (;;) { - int c = GetChar(); - - if (c == '"') - break; - - if (c == -1) - FATAL_INPUT_ERROR("unexpected EOF in string\n"); - - if (c == '\\') { - c = GetChar(); - } - } -} +#include "scaninc.h" +#include "asm_file.h" +#include "c_file.h" bool CanOpenFile(std::string path) { - FILE *fp = fopen(path.c_str(), "rb"); + FILE *fp = std::fopen(path.c_str(), "rb"); - if (fp == NULL) - return false; + if (fp == NULL) + return false; - fclose(fp); - return true; + std::fclose(fp); + return true; } int main(int argc, char **argv) { - if (argc < 2) - FATAL_ERROR("Usage: scaninc ASM_FILE_PATH\n"); - - std::stack<std::string> filesToProcess; - std::set<std::string> dependencies; - - filesToProcess.push(std::string(argv[1])); - - while (!filesToProcess.empty()) { - AsmFile file(filesToProcess.top()); - - filesToProcess.pop(); - - IncDirectiveType incDirectiveType; - std::string path; - - while ((incDirectiveType = file.ReadUntilIncDirective(path)) != IncDirectiveType::None) { - bool inserted = dependencies.insert(path).second; - if (inserted - && incDirectiveType == IncDirectiveType::Include - && CanOpenFile(path)) - filesToProcess.push(path); - } - } - - for (const std::string &path : dependencies) { - printf("%s\n", path.c_str()); - } + if (argc < 2) + FATAL_ERROR("Usage: scaninc FILE_PATH\n"); + + std::stack<std::string> filesToProcess; + std::set<std::string> dependencies; + + std::string initialPath(argv[1]); + + std::size_t pos = initialPath.find_last_of('.'); + + if (pos == std::string::npos) + FATAL_ERROR("no file extension in path \"%s\"\n", initialPath.c_str()); + + std::string extension = initialPath.substr(pos + 1); + + if (extension == "c") + { + CFile file(initialPath); + + file.FindIncbins(); + dependencies = file.GetIncbins(); + } + else if (extension == "s") + { + filesToProcess.push(std::string(argv[1])); + + while (!filesToProcess.empty()) + { + AsmFile file(filesToProcess.top()); + + filesToProcess.pop(); + + IncDirectiveType incDirectiveType; + std::string path; + + while ((incDirectiveType = file.ReadUntilIncDirective(path)) != IncDirectiveType::None) + { + bool inserted = dependencies.insert(path).second; + if (inserted + && incDirectiveType == IncDirectiveType::Include + && CanOpenFile(path)) + filesToProcess.push(path); + } + } + } + else + { + FATAL_ERROR("unknown extension \"%s\"\n", extension.c_str()); + } + + for (const std::string &path : dependencies) + { + std::printf("%s\n", path.c_str()); + } } diff --git a/tools/scaninc/scaninc.h b/tools/scaninc/scaninc.h new file mode 100644 index 000000000..30cc9611c --- /dev/null +++ b/tools/scaninc/scaninc.h @@ -0,0 +1,59 @@ +// Copyright(c) 2015-2017 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 SCANINC_H +#define SCANINC_H + +#include <cstdio> +#include <cstdlib> + +#ifdef _MSC_VER + +#define FATAL_INPUT_ERROR(format, ...) \ +do { \ + fprintf(stderr, "%s:%d " format, m_path.c_str(), m_lineNum, __VA_ARGS__); \ + exit(1); \ +} while (0) + +#define FATAL_ERROR(format, ...) \ +do { \ + fprintf(stderr, format, __VA_ARGS__); \ + exit(1); \ +} while (0) + +#else + +#define FATAL_INPUT_ERROR(format, ...) \ +do { \ + fprintf(stderr, "%s:%d " format, m_path.c_str(), m_lineNum, ##__VA_ARGS__); \ + exit(1); \ +} while (0) + +#define FATAL_ERROR(format, ...) \ +do { \ + fprintf(stderr, format, ##__VA_ARGS__); \ + exit(1); \ +} while (0) + +#endif // _MSC_VER + +#define SCANINC_MAX_PATH 255 + +#endif // SCANINC_H |