diff --git a/.clang-format b/.clang-format new file mode 100644 index 00000000..baa70787 --- /dev/null +++ b/.clang-format @@ -0,0 +1,12 @@ +--- +Language: Cpp +BasedOnStyle: Google +ColumnLimit: 88 +IndentWidth: 4 +AccessModifierOffset: -4 +SortIncludes: true +ConstructorInitializerIndentWidth: 8 +ContinuationIndentWidth: 8 +DerivePointerAlignment: false +MaxEmptyLinesToKeep: 2 +... diff --git a/src/asar-dll-bindings/c/asardll.h b/src/asar-dll-bindings/c/asardll.h index be820ad8..a784af08 100644 --- a/src/asar-dll-bindings/c/asardll.h +++ b/src/asar-dll-bindings/c/asardll.h @@ -1,131 +1,133 @@ // note for asar devs: autogenerated by update_c_bind.py, don't edit this // directly! either edit asardll.h.in or interface-lib.cpp. #ifndef ASAR_DLL_H_INCLUDED -# define ASAR_DLL_H_INCLUDED +#define ASAR_DLL_H_INCLUDED #define expectedapiversion 400 #include -#include // for size_t +#include // for size_t // These structures are returned from various functions struct stackentry { - const char * fullpath; - const char * prettypath; - int lineno; - const char * details; + const char* fullpath; + const char* prettypath; + int lineno; + const char* details; }; struct errordata { - const char * fullerrdata; - const char * rawerrdata; - const char * block; - const char * filename; - int line; - const struct stackentry * callstack; - int callstacksize; - const char * errname; + const char* fullerrdata; + const char* rawerrdata; + const char* block; + const char* filename; + int line; + const struct stackentry* callstack; + int callstacksize; + const char* errname; }; struct labeldata { - const char * name; - int location; + const char* name; + int location; }; struct definedata { - const char * name; - const char * contents; + const char* name; + const char* contents; }; struct warnsetting { - const char * warnid; - bool enabled; + const char* warnid; + bool enabled; }; struct memoryfile { - const char* path; - const void* buffer; - size_t length; + const char* path; + const void* buffer; + size_t length; }; enum mappertype { - invalid_mapper, - lorom, - hirom, - sa1rom, - bigsa1rom, - sfxrom, - exlorom, - exhirom, - norom + invalid_mapper, + lorom, + hirom, + sa1rom, + bigsa1rom, + sfxrom, + exlorom, + exhirom, + norom }; struct writtenblockdata { - int pcoffset; - int snesoffset; - int numbytes; + int pcoffset; + int snesoffset; + int numbytes; }; struct patchparams { - // The size of this struct. Set to (int)sizeof(patchparams). - int structsize; - - // Same parameters as asar_patch() - const char * patchloc; - char * romdata; - int buflen; - int * romlen; - - // Include paths to use when searching files. - const char** includepaths; - int numincludepaths; - - // A list of additional defines to make available to the patch. - const struct definedata* additional_defines; - int additional_define_count; - - // Path to a text file to parse standard include search paths from. - // Set to NULL to not use any standard includes search paths. - const char* stdincludesfile; - - // Path to a text file to parse standard defines from. - // Set to NULL to not use any standard defines. - const char* stddefinesfile; - - // A list of warnings to enable or disable. - // Specify warnings in the format "WXXXX" where XXXX = warning ID. - const struct warnsetting * warning_settings; - int warning_setting_count; - - // List of memory files to create on the virtual filesystem. - const struct memoryfile * memory_files; - int memory_file_count; - - // Set override_checksum_gen to true and generate_checksum to true/false - // to force generating/not generating a checksum. - bool override_checksum_gen; - bool generate_checksum; - - // Set this to true for generated error and warning texts to always - // contain their full call stack. - bool full_call_stack; + // The size of this struct. Set to (int)sizeof(patchparams). + int structsize; + + // Same parameters as asar_patch() + const char* patchloc; + char* romdata; + int buflen; + int* romlen; + + // Include paths to use when searching files. + const char** includepaths; + int numincludepaths; + + // A list of additional defines to make available to the patch. + const struct definedata* additional_defines; + int additional_define_count; + + // Path to a text file to parse standard include search paths from. + // Set to NULL to not use any standard includes search paths. + const char* stdincludesfile; + + // Path to a text file to parse standard defines from. + // Set to NULL to not use any standard defines. + const char* stddefinesfile; + + // A list of warnings to enable or disable. + // Specify warnings in the format "WXXXX" where XXXX = warning ID. + const struct warnsetting* warning_settings; + int warning_setting_count; + + // List of memory files to create on the virtual filesystem. + const struct memoryfile* memory_files; + int memory_file_count; + + // Set override_checksum_gen to true and generate_checksum to true/false + // to force generating/not generating a checksum. + bool override_checksum_gen; + bool generate_checksum; + + // Set this to true for generated error and warning texts to always + // contain their full call stack. + bool full_call_stack; }; #ifdef __cplusplus extern "C" { #endif -//Initializes Asar. Call this before doing anything. -//If it returns false, something went wrong, and you may not use any other Asar functions. This is -//either due to not finding the library, or not finding all expected functions in the library. +// Initializes Asar. Call this before doing anything. +// If it returns false, something went wrong, and you may not use any other Asar +// functions. This is either due to not finding the library, or not finding all expected +// functions in the library. bool asar_init(void); -// Same as above, but instead of automatically looking for and trying to load asar.dll, takes -// a path to the Asar DLL and tries to load it. -// The path is expected to be UTF-8-encoded, even on Windows. -bool asar_init_with_dll_path(const char * dllpath); +// Same as above, but instead of automatically looking for and trying to load asar.dll, +// takes a path to the Asar DLL and tries to load it. The path is expected to be +// UTF-8-encoded, even on Windows. +bool asar_init_with_dll_path(const char* dllpath); -//Frees all of Asar's structures and unloads the module. Only asar_init may be called after calling +// Frees all of Asar's structures and unloads the module. Only asar_init may be called +// after calling // this; anything else will lead to segfaults. void asar_close(void); @@ -159,7 +161,7 @@ extern bool (*asar_reset)(void); * be left unchanged. * See the documentation of struct patchparams for more information. */ -extern bool (*asar_patch)(const struct patchparams *params); +extern bool (*asar_patch)(const struct patchparams* params); /* Returns the maximum possible size of the output ROM from asar_patch(). * Giving this size to buflen guarantees you will not get any buffer too small @@ -173,35 +175,35 @@ extern int (*asar_maxromsize)(void); * called again, or until asar_patch, asar_reset or asar_close is called, * whichever comes first. Copy the contents if you need it for a longer time. */ -extern const struct errordata * (*asar_geterrors)(int * count); +extern const struct errordata* (*asar_geterrors)(int* count); /* Get a list of all warnings. */ -extern const struct errordata * (*asar_getwarnings)(int * count); +extern const struct errordata* (*asar_getwarnings)(int* count); /* Get a list of all printed data. */ -extern const char * const * (*asar_getprints)(int * count); +extern const char* const* (*asar_getprints)(int* count); /* Get a list of all labels. */ -extern const struct labeldata * (*asar_getalllabels)(int * count); +extern const struct labeldata* (*asar_getalllabels)(int* count); /* Get the ROM location of one label. -1 means "not found". */ -extern int (*asar_getlabelval)(const char * name); +extern int (*asar_getlabelval)(const char* name); /* Get the value of a define. */ -extern const char * (*asar_getdefine)(const char * name); +extern const char* (*asar_getdefine)(const char* name); /* Parses all defines in the parameter. Note that it may emit errors. */ -extern const char * (*asar_resolvedefines)(const char * data); +extern const char* (*asar_resolvedefines)(const char* data); /* Gets the values and names of all defines. */ -extern const struct definedata * (*asar_getalldefines)(int * count); +extern const struct definedata* (*asar_getalldefines)(int* count); /* Parses a string containing math. It automatically assumes global scope (no * namespaces), and has access to all functions and labels from the last call @@ -209,12 +211,12 @@ extern const struct definedata * (*asar_getalldefines)(int * count); * if it failed (non-NULL, contains a descriptive string). It does not affect * asar_geterrors. */ -extern double (*asar_math)(const char * math_, const char ** error); +extern double (*asar_math)(const char* math_, const char** error); /* Get a list of all the blocks written to the ROM by calls such as * asar_patch(). */ -extern const struct writtenblockdata * (*asar_getwrittenblocks)(int * count); +extern const struct writtenblockdata* (*asar_getwrittenblocks)(int* count); /* Get the mapper currently used by Asar. */ @@ -222,10 +224,10 @@ extern enum mappertype (*asar_getmapper)(void); /* Generates the contents of a symbols file for in a specific format. */ -extern const char * (*asar_getsymbolsfile)(const char* type); +extern const char* (*asar_getsymbolsfile)(const char* type); #ifdef __cplusplus - } +} #endif -#endif // ASAR_DLL_H_INCLUDED +#endif // ASAR_DLL_H_INCLUDED diff --git a/src/asar-tests/test.cpp b/src/asar-tests/test.cpp index 83dfec3c..b3b973a1 100644 --- a/src/asar-tests/test.cpp +++ b/src/asar-tests/test.cpp @@ -1,229 +1,239 @@ #if defined(_WIN32) -# define NOMINMAX - -# if defined(_MSC_VER) -# pragma warning(push) -# pragma warning(disable : 4365) -# pragma warning(disable : 4571) -# pragma warning(disable : 4623) -# pragma warning(disable : 4625) -# pragma warning(disable : 4626) -# pragma warning(disable : 4668) -# pragma warning(disable : 4711) -# pragma warning(disable : 4774) -# pragma warning(disable : 4987) -# pragma warning(disable : 5026) -# pragma warning(disable : 5027) -# endif - -# include -# include -# include -# include -# include -# include -# include -# include -# include -# include - -# if defined(_MSC_VER) -# pragma warning(pop) -# endif +#define NOMINMAX + +#if defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable : 4365) +#pragma warning(disable : 4571) +#pragma warning(disable : 4623) +#pragma warning(disable : 4625) +#pragma warning(disable : 4626) +#pragma warning(disable : 4668) +#pragma warning(disable : 4711) +#pragma warning(disable : 4774) +#pragma warning(disable : 4987) +#pragma warning(disable : 5026) +#pragma warning(disable : 5027) +#endif + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#if defined(_MSC_VER) +#pragma warning(pop) +#endif #else -# include -# include -# include -# include -# include -# include -# include -# include -# include -# include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include #endif #if defined(ASAR_TEST_DLL) -# include "asardll.h" +#include "asardll.h" #endif -#define die() { numfailed++; free(expectedrom); free(truerom); printf("Failure!\n\n"); continue; } -#define dief(...) { printf(__VA_ARGS__); die(); } - -#define min(a, b) ((a)<(b)?(a):(b)) +#define die() \ + { \ + numfailed++; \ + free(expectedrom); \ + free(truerom); \ + printf("Failure!\n\n"); \ + continue; \ + } +#define dief(...) \ + { \ + printf(__VA_ARGS__); \ + die(); \ + } + +#define min(a, b) ((a) < (b) ? (a) : (b)) static const size_t max_rom_size = 16777216u; #if defined(_WIN32) -std::string utf16_to_utf8(const std::wstring input) -{ - int needed_chars = WideCharToMultiByte(CP_UTF8, 0, input.c_str(), input.length(), NULL, 0, NULL, NULL); - - if (needed_chars == 0) - { - DWORD err_code = GetLastError(); - HRESULT hresult = HRESULT_FROM_WIN32(err_code); - fprintf(stderr, "WideCharToMultiByte() (first call) failed with error code: %u (HRESULT: 0x%08x)\n", - (unsigned int)err_code, (unsigned int)hresult); - return ""; - } - - std::string out; - out.resize(needed_chars); - - int ret_val = WideCharToMultiByte(CP_UTF8, 0, input.c_str(), input.length(), const_cast(out.c_str()), out.length(), NULL, NULL); - - if (ret_val == 0) - { - DWORD err_code = GetLastError(); - HRESULT hresult = HRESULT_FROM_WIN32(err_code); - fprintf(stderr, "WideCharToMultiByte() (second call) failed with error code: %u (HRESULT: 0x%08x)\n", - (unsigned int)err_code, (unsigned int)hresult); - return ""; - } - - return out; +std::string utf16_to_utf8(const std::wstring input) { + int needed_chars = WideCharToMultiByte(CP_UTF8, 0, input.c_str(), input.length(), + NULL, 0, NULL, NULL); + + if (needed_chars == 0) { + DWORD err_code = GetLastError(); + HRESULT hresult = HRESULT_FROM_WIN32(err_code); + fprintf(stderr, + "WideCharToMultiByte() (first call) failed with error code: %u " + "(HRESULT: 0x%08x)\n", + (unsigned int)err_code, (unsigned int)hresult); + return ""; + } + + std::string out; + out.resize(needed_chars); + + int ret_val = WideCharToMultiByte(CP_UTF8, 0, input.c_str(), input.length(), + const_cast(out.c_str()), out.length(), + NULL, NULL); + + if (ret_val == 0) { + DWORD err_code = GetLastError(); + HRESULT hresult = HRESULT_FROM_WIN32(err_code); + fprintf(stderr, + "WideCharToMultiByte() (second call) failed with error code: %u " + "(HRESULT: 0x%08x)\n", + (unsigned int)err_code, (unsigned int)hresult); + return ""; + } + + return out; } -std::wstring utf8_to_utf16(const std::string input) -{ - int needed_chars = MultiByteToWideChar(CP_UTF8, 0, input.c_str(), input.length(), NULL, 0); - - if (needed_chars == 0) - { - DWORD err_code = GetLastError(); - HRESULT hresult = HRESULT_FROM_WIN32(err_code); - fprintf(stderr, "MultiByteToWideChar() (first call) failed for string \"%s\" with error code: %u (HRESULT: 0x%08x)\n", - input.c_str(), (unsigned int)err_code, (unsigned int)hresult); - return L""; - } - - std::wstring out; - out.resize(needed_chars); - - int ret_val = MultiByteToWideChar(CP_UTF8, 0, input.c_str(), input.length(), const_cast(out.c_str()), out.length()); - - if (ret_val == 0) - { - DWORD err_code = GetLastError(); - HRESULT hresult = HRESULT_FROM_WIN32(err_code); - fprintf(stderr, "MultiByteToWideChar() (second call) failed for string \"%s\" with error code: %u (HRESULT: 0x%08x)\n", - input.c_str(), (unsigned int)err_code, (unsigned int)hresult); - return L""; - } - - return out; +std::wstring utf8_to_utf16(const std::string input) { + int needed_chars = + MultiByteToWideChar(CP_UTF8, 0, input.c_str(), input.length(), NULL, 0); + + if (needed_chars == 0) { + DWORD err_code = GetLastError(); + HRESULT hresult = HRESULT_FROM_WIN32(err_code); + fprintf(stderr, + "MultiByteToWideChar() (first call) failed for string \"%s\" with " + "error code: %u (HRESULT: 0x%08x)\n", + input.c_str(), (unsigned int)err_code, (unsigned int)hresult); + return L""; + } + + std::wstring out; + out.resize(needed_chars); + + int ret_val = MultiByteToWideChar(CP_UTF8, 0, input.c_str(), input.length(), + const_cast(out.c_str()), out.length()); + + if (ret_val == 0) { + DWORD err_code = GetLastError(); + HRESULT hresult = HRESULT_FROM_WIN32(err_code); + fprintf(stderr, + "MultiByteToWideChar() (second call) failed for string \"%s\" with " + "error code: %u (HRESULT: 0x%08x)\n", + input.c_str(), (unsigned int)err_code, (unsigned int)hresult); + return L""; + } + + return out; } -inline bool file_exists(const char *filename) -{ - std::wstring filename_u16 = utf8_to_utf16(filename); - return (GetFileAttributesW(filename_u16.c_str()) != INVALID_FILE_ATTRIBUTES); +inline bool file_exists(const char* filename) { + std::wstring filename_u16 = utf8_to_utf16(filename); + return (GetFileAttributesW(filename_u16.c_str()) != INVALID_FILE_ATTRIBUTES); } -std::vector read_file_into_buffer(const char *filename) -{ - std::wstring filename_u16 = utf8_to_utf16(filename); +std::vector read_file_into_buffer(const char* filename) { + std::wstring filename_u16 = utf8_to_utf16(filename); - HANDLE handle = CreateFileW(filename_u16.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + HANDLE handle = CreateFileW(filename_u16.c_str(), GENERIC_READ, FILE_SHARE_READ, + NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); - std::vector out; + std::vector out; - if (handle != INVALID_HANDLE_VALUE) - { - LARGE_INTEGER f_size; + if (handle != INVALID_HANDLE_VALUE) { + LARGE_INTEGER f_size; - GetFileSizeEx(handle, &f_size); + GetFileSizeEx(handle, &f_size); - out.resize((size_t)f_size.QuadPart); + out.resize((size_t)f_size.QuadPart); - ReadFile(handle, out.data(), out.size(), NULL, NULL); + ReadFile(handle, out.data(), out.size(), NULL, NULL); - CloseHandle(handle); - } + CloseHandle(handle); + } - return out; + return out; } -void write_buffer_to_file(const char *filename, const void* data, size_t data_size) -{ - std::wstring filename_u16 = utf8_to_utf16(filename); +void write_buffer_to_file(const char* filename, const void* data, size_t data_size) { + std::wstring filename_u16 = utf8_to_utf16(filename); - HANDLE handle = CreateFileW(filename_u16.c_str(), GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + HANDLE handle = CreateFileW(filename_u16.c_str(), GENERIC_WRITE, 0, NULL, + CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); - if (handle != INVALID_HANDLE_VALUE) - { - WriteFile(handle, data, data_size, NULL, NULL); + if (handle != INVALID_HANDLE_VALUE) { + WriteFile(handle, data, data_size, NULL, NULL); - CloseHandle(handle); - } + CloseHandle(handle); + } } #else -inline bool file_exists(const char *filename) -{ - struct stat buffer; - return (stat(filename, &buffer) == 0); +inline bool file_exists(const char* filename) { + struct stat buffer; + return (stat(filename, &buffer) == 0); } -std::vector read_file_into_buffer(const char *filename) -{ - FILE* handle = fopen(filename, "rb"); +std::vector read_file_into_buffer(const char* filename) { + FILE* handle = fopen(filename, "rb"); - std::vector out; + std::vector out; - if (handle != nullptr) - { - fseek(handle, 0, SEEK_END); - long int f_size = ftell(handle); - fseek(handle, 0, SEEK_SET); + if (handle != nullptr) { + fseek(handle, 0, SEEK_END); + long int f_size = ftell(handle); + fseek(handle, 0, SEEK_SET); - out.resize((size_t)f_size); + out.resize((size_t)f_size); - fread(out.data(), 1, out.size(), handle); + fread(out.data(), 1, out.size(), handle); - fclose(handle); - } + fclose(handle); + } - return out; + return out; } -void write_buffer_to_file(const char *filename, const void* data, size_t data_size) -{ - FILE* handle = fopen(filename, "wb"); +void write_buffer_to_file(const char* filename, const void* data, size_t data_size) { + FILE* handle = fopen(filename, "wb"); - if (handle != nullptr) - { - fwrite(data, 1, data_size, handle); + if (handle != nullptr) { + fwrite(data, 1, data_size, handle); - fclose(handle); - } + fclose(handle); + } } #endif -void write_buffer_to_file(const char *filename, const std::vector& to_write) -{ - write_buffer_to_file(filename, to_write.data(), to_write.size()); +void write_buffer_to_file(const char* filename, const std::vector& to_write) { + write_buffer_to_file(filename, to_write.data(), to_write.size()); } -void write_buffer_to_file(const char *filename, const std::string& to_write) -{ - write_buffer_to_file(filename, to_write.c_str(), to_write.length()); +void write_buffer_to_file(const char* filename, const std::string& to_write) { + write_buffer_to_file(filename, to_write.c_str(), to_write.length()); } // This function is based in part on nall, which is under the following licence. -// This modified version is licenced under the LGPL version 3 or later. See the LICENSE file -// for details. +// This modified version is licenced under the LGPL version 3 or later. See the LICENSE +// file for details. // // Copyright (c) 2006-2015 byuu // @@ -240,167 +250,155 @@ void write_buffer_to_file(const char *filename, const std::string& to_write) // PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER // TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR // PERFORMANCE OF THIS SOFTWARE. -static std::string dir(char const *name) -{ - std::string result = name; - for (signed i = (int)result.length(); i >= 0; i--) - { - if (result[(size_t)i] == '/' || result[(size_t)i] == '\\') - { - result.erase((size_t)(i + 1)); - break; - } - if (i == 0) result = ""; - } - return result; +static std::string dir(char const* name) { + std::string result = name; + for (signed i = (int)result.length(); i >= 0; i--) { + if (result[(size_t)i] == '/' || result[(size_t)i] == '\\') { + result.erase((size_t)(i + 1)); + break; + } + if (i == 0) result = ""; + } + return result; } -inline bool str_ends_with(const char * str, const char * suffix) -{ - if (str == nullptr || suffix == nullptr) - return false; +inline bool str_ends_with(const char* str, const char* suffix) { + if (str == nullptr || suffix == nullptr) return false; - size_t str_len = strlen(str); - size_t suffix_len = strlen(suffix); + size_t str_len = strlen(str); + size_t suffix_len = strlen(suffix); - if (suffix_len > str_len) - return false; + if (suffix_len > str_len) return false; - return (strncmp(str + str_len - suffix_len, suffix, suffix_len) == 0); + return (strncmp(str + str_len - suffix_len, suffix, suffix_len) == 0); } -struct wrapped_file -{ - char file_name[64]; - char file_path[512]; +struct wrapped_file { + char file_name[64]; + char file_path[512]; }; -static bool find_files_in_directory(std::vector& out_array, const char * directory_name) -{ +static bool find_files_in_directory(std::vector& out_array, + const char* directory_name) { #if defined(_WIN32) - char search_path[512]; - bool has_path_seperator = false; - if (str_ends_with(directory_name, "/") || str_ends_with(directory_name, "\\")) - { - snprintf(search_path, sizeof(search_path), "%s*.*", directory_name); - has_path_seperator = true; - } - else - { - snprintf(search_path, sizeof(search_path), "%s/*.*", directory_name); - } - - std::wstring search_path_w = utf8_to_utf16(search_path); - - WIN32_FIND_DATAW fd; - HANDLE hFind = ::FindFirstFileW(search_path_w.c_str(), &fd); - if (hFind != INVALID_HANDLE_VALUE) - { - do - { - if (!(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) - { - std::string filename_u8 = utf16_to_utf8(fd.cFileName); - - if (str_ends_with(filename_u8.c_str(), ".asm")) - { - wrapped_file new_file; - strncpy(new_file.file_name, filename_u8.c_str(), sizeof(new_file.file_name)); - new_file.file_name[sizeof(new_file.file_name) - 1] = '\0'; - strncpy(new_file.file_path, directory_name, sizeof(new_file.file_path)); - if (!has_path_seperator) - { + char search_path[512]; + bool has_path_seperator = false; + if (str_ends_with(directory_name, "/") || str_ends_with(directory_name, "\\")) { + snprintf(search_path, sizeof(search_path), "%s*.*", directory_name); + has_path_seperator = true; + } else { + snprintf(search_path, sizeof(search_path), "%s/*.*", directory_name); + } + + std::wstring search_path_w = utf8_to_utf16(search_path); + + WIN32_FIND_DATAW fd; + HANDLE hFind = ::FindFirstFileW(search_path_w.c_str(), &fd); + if (hFind != INVALID_HANDLE_VALUE) { + do { + if (!(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { + std::string filename_u8 = utf16_to_utf8(fd.cFileName); + + if (str_ends_with(filename_u8.c_str(), ".asm")) { + wrapped_file new_file; + strncpy(new_file.file_name, filename_u8.c_str(), + sizeof(new_file.file_name)); + new_file.file_name[sizeof(new_file.file_name) - 1] = '\0'; + strncpy(new_file.file_path, directory_name, + sizeof(new_file.file_path)); + if (!has_path_seperator) { #if defined(__MINGW32__) - strncat(new_file.file_path, "/", sizeof(new_file.file_path) - strlen(new_file.file_path) - 1); + strncat(new_file.file_path, "/", + sizeof(new_file.file_path) - + strlen(new_file.file_path) - 1); #else - strcat_s(new_file.file_path, sizeof(new_file.file_path), "/"); + strcat_s(new_file.file_path, sizeof(new_file.file_path), "/"); #endif - } + } #if defined(__MINGW32__) - strncat(new_file.file_path, filename_u8.c_str(), sizeof(new_file.file_path) - strlen(new_file.file_path) - 1); + strncat(new_file.file_path, filename_u8.c_str(), + sizeof(new_file.file_path) - strlen(new_file.file_path) - + 1); #else - strcat_s(new_file.file_path, sizeof(new_file.file_path), filename_u8.c_str()); + strcat_s(new_file.file_path, sizeof(new_file.file_path), + filename_u8.c_str()); #endif - new_file.file_path[sizeof(new_file.file_path) - 1] = '\0'; - out_array.push_back(new_file); - } - } - } while (::FindNextFileW(hFind, &fd)); - - ::FindClose(hFind); - } - else - { - DWORD err_code = GetLastError(); - HRESULT hresult = HRESULT_FROM_WIN32(err_code); - fprintf(stderr, "FindFirstFileW() for path \"%s\" failed with error code: %u (HRESULT: 0x%08x)\n", - search_path, (unsigned int)err_code, (unsigned int)hresult); - return false; - } + new_file.file_path[sizeof(new_file.file_path) - 1] = '\0'; + out_array.push_back(new_file); + } + } + } while (::FindNextFileW(hFind, &fd)); + + ::FindClose(hFind); + } else { + DWORD err_code = GetLastError(); + HRESULT hresult = HRESULT_FROM_WIN32(err_code); + fprintf(stderr, + "FindFirstFileW() for path \"%s\" failed with error code: %u (HRESULT: " + "0x%08x)\n", + search_path, (unsigned int)err_code, (unsigned int)hresult); + return false; + } #else - bool has_path_seperator = false; - if (str_ends_with(directory_name, "/") || str_ends_with(directory_name, "\\")) - { - has_path_seperator = true; - } - DIR * dir; - dirent * ent; - if ((dir = opendir(directory_name)) != nullptr) { - while ((ent = readdir(dir)) != nullptr) { - // Only consider regular files - if (ent->d_type == DT_REG) - { - if (str_ends_with(ent->d_name, ".asm")) - { - wrapped_file new_file; - strncpy(new_file.file_name, ent->d_name, sizeof(new_file.file_name)); - new_file.file_name[sizeof(new_file.file_name) - 1] = '\0'; - strncpy(new_file.file_path, directory_name, sizeof(new_file.file_path)); - if (!has_path_seperator) - { - size_t currlen = strlen(new_file.file_path); - if (currlen < sizeof(new_file.file_path)) - { - strncpy(new_file.file_path + currlen, "/", sizeof(new_file.file_path) - currlen); - } - } - size_t currlen = strlen(new_file.file_path); - if (currlen < sizeof(new_file.file_path)) - { - strncpy(new_file.file_path + currlen, ent->d_name, sizeof(new_file.file_path) - currlen); - } - new_file.file_path[sizeof(new_file.file_path) - 1] = '\0'; - out_array.push_back(new_file); - } - } - } - closedir(dir); - } - else { - return false; - } + bool has_path_seperator = false; + if (str_ends_with(directory_name, "/") || str_ends_with(directory_name, "\\")) { + has_path_seperator = true; + } + DIR* dir; + dirent* ent; + if ((dir = opendir(directory_name)) != nullptr) { + while ((ent = readdir(dir)) != nullptr) { + // Only consider regular files + if (ent->d_type == DT_REG) { + if (str_ends_with(ent->d_name, ".asm")) { + wrapped_file new_file; + strncpy(new_file.file_name, ent->d_name, + sizeof(new_file.file_name)); + new_file.file_name[sizeof(new_file.file_name) - 1] = '\0'; + strncpy(new_file.file_path, directory_name, + sizeof(new_file.file_path)); + if (!has_path_seperator) { + size_t currlen = strlen(new_file.file_path); + if (currlen < sizeof(new_file.file_path)) { + strncpy(new_file.file_path + currlen, "/", + sizeof(new_file.file_path) - currlen); + } + } + size_t currlen = strlen(new_file.file_path); + if (currlen < sizeof(new_file.file_path)) { + strncpy(new_file.file_path + currlen, ent->d_name, + sizeof(new_file.file_path) - currlen); + } + new_file.file_path[sizeof(new_file.file_path) - 1] = '\0'; + out_array.push_back(new_file); + } + } + } + closedir(dir); + } else { + return false; + } #endif - return true; + return true; } -static void delete_file(const char * filename) -{ +static void delete_file(const char* filename) { #if defined(_WIN32) - DeleteFileA(filename); + DeleteFileA(filename); #else - remove(filename); + remove(filename); #endif } @@ -412,854 +410,846 @@ static void delete_file(const char * filename) // complicated platform-specific solution. // NOTE: No cont char*, for commandline, because CreateProcess() // actually can mess with this string for some reason... -static bool execute_command_line(char * commandline, const char * stdout_log_file, const char * stderr_log_file) -{ +static bool execute_command_line(char* commandline, const char* stdout_log_file, + const char* stderr_log_file) { #if defined(_WIN32) - HANDLE stdout_read_handle = nullptr; - HANDLE stdout_write_handle = nullptr; - HANDLE stderr_read_handle = nullptr; - HANDLE stderr_write_handle = nullptr; - - SECURITY_ATTRIBUTES sa; - sa.nLength = sizeof(SECURITY_ATTRIBUTES); - sa.bInheritHandle = TRUE; - sa.lpSecurityDescriptor = nullptr; - - SYSTEM_INFO sInfo{}; - GetSystemInfo(&sInfo); - - if (!CreatePipe(&stdout_read_handle, &stdout_write_handle, &sa, sInfo.dwPageSize * 4)) - { - return false; - } - - if (!SetHandleInformation(stdout_read_handle, HANDLE_FLAG_INHERIT, 0)) - { - CloseHandle(stdout_read_handle); - CloseHandle(stdout_write_handle); - return false; - } - - if (!CreatePipe(&stderr_read_handle, &stderr_write_handle, &sa, sInfo.dwPageSize * 4)) - { - CloseHandle(stdout_read_handle); - CloseHandle(stdout_write_handle); - return false; - } - - if (!SetHandleInformation(stderr_read_handle, HANDLE_FLAG_INHERIT, 0)) - { - CloseHandle(stdout_read_handle); - CloseHandle(stdout_write_handle); - CloseHandle(stderr_read_handle); - CloseHandle(stderr_write_handle); - return false; - } - - STARTUPINFOW si; - PROCESS_INFORMATION pi; - ZeroMemory(&si, sizeof(si)); - ZeroMemory(&pi, sizeof(pi)); - si.cb = sizeof(si); - si.hStdError = stderr_write_handle; - si.hStdOutput = stdout_write_handle; - si.dwFlags |= STARTF_USESTDHANDLES; - - std::wstring u16_commandline = utf8_to_utf16(commandline); - - if (!CreateProcessW(nullptr, const_cast(u16_commandline.c_str()), nullptr, nullptr, TRUE, 0, nullptr, nullptr, &si, &pi)) - { - printf("execute_command_line() failed with HRESULT: 0x%8x\n", (unsigned int)HRESULT_FROM_WIN32(GetLastError())); - CloseHandle(stdout_read_handle); - CloseHandle(stdout_write_handle); - CloseHandle(stderr_read_handle); - CloseHandle(stderr_write_handle); - return false; - } - - WaitForSingleObject(pi.hProcess, INFINITE); - - CloseHandle(pi.hThread); - CloseHandle(pi.hProcess); - CloseHandle(stdout_write_handle); - CloseHandle(stderr_write_handle); - - { - std::vector stdout_buffer; - - DWORD dwRead; - CHAR chBuf[4096]; - BOOL bSuccess = FALSE; - - for (;;) - { - bSuccess = ReadFile(stdout_read_handle, chBuf, sizeof(chBuf), &dwRead, nullptr); - if (bSuccess == FALSE || dwRead == 0) break; - stdout_buffer.insert(stdout_buffer.end(), chBuf, chBuf + dwRead); - } - - write_buffer_to_file(stdout_log_file, stdout_buffer); - - CloseHandle(stdout_read_handle); - } - - { - std::vector stderr_buffer; - - DWORD dwRead; - CHAR chBuf[4096]; - BOOL bSuccess = FALSE; - - for (;;) - { - bSuccess = ReadFile(stderr_read_handle, chBuf, sizeof(chBuf), &dwRead, nullptr); - if (bSuccess == FALSE || dwRead == 0) break; - stderr_buffer.insert(stderr_buffer.end(), chBuf, chBuf + dwRead); - } - - write_buffer_to_file(stderr_log_file, stderr_buffer); - - CloseHandle(stderr_read_handle); - } + HANDLE stdout_read_handle = nullptr; + HANDLE stdout_write_handle = nullptr; + HANDLE stderr_read_handle = nullptr; + HANDLE stderr_write_handle = nullptr; + + SECURITY_ATTRIBUTES sa; + sa.nLength = sizeof(SECURITY_ATTRIBUTES); + sa.bInheritHandle = TRUE; + sa.lpSecurityDescriptor = nullptr; + + SYSTEM_INFO sInfo{}; + GetSystemInfo(&sInfo); + + if (!CreatePipe(&stdout_read_handle, &stdout_write_handle, &sa, + sInfo.dwPageSize * 4)) { + return false; + } + + if (!SetHandleInformation(stdout_read_handle, HANDLE_FLAG_INHERIT, 0)) { + CloseHandle(stdout_read_handle); + CloseHandle(stdout_write_handle); + return false; + } + + if (!CreatePipe(&stderr_read_handle, &stderr_write_handle, &sa, + sInfo.dwPageSize * 4)) { + CloseHandle(stdout_read_handle); + CloseHandle(stdout_write_handle); + return false; + } + + if (!SetHandleInformation(stderr_read_handle, HANDLE_FLAG_INHERIT, 0)) { + CloseHandle(stdout_read_handle); + CloseHandle(stdout_write_handle); + CloseHandle(stderr_read_handle); + CloseHandle(stderr_write_handle); + return false; + } + + STARTUPINFOW si; + PROCESS_INFORMATION pi; + ZeroMemory(&si, sizeof(si)); + ZeroMemory(&pi, sizeof(pi)); + si.cb = sizeof(si); + si.hStdError = stderr_write_handle; + si.hStdOutput = stdout_write_handle; + si.dwFlags |= STARTF_USESTDHANDLES; + + std::wstring u16_commandline = utf8_to_utf16(commandline); + + if (!CreateProcessW(nullptr, const_cast(u16_commandline.c_str()), nullptr, + nullptr, TRUE, 0, nullptr, nullptr, &si, &pi)) { + printf("execute_command_line() failed with HRESULT: 0x%8x\n", + (unsigned int)HRESULT_FROM_WIN32(GetLastError())); + CloseHandle(stdout_read_handle); + CloseHandle(stdout_write_handle); + CloseHandle(stderr_read_handle); + CloseHandle(stderr_write_handle); + return false; + } + + WaitForSingleObject(pi.hProcess, INFINITE); + + CloseHandle(pi.hThread); + CloseHandle(pi.hProcess); + CloseHandle(stdout_write_handle); + CloseHandle(stderr_write_handle); + + { + std::vector stdout_buffer; + + DWORD dwRead; + CHAR chBuf[4096]; + BOOL bSuccess = FALSE; + + for (;;) { + bSuccess = ReadFile(stdout_read_handle, chBuf, sizeof(chBuf), &dwRead, + nullptr); + if (bSuccess == FALSE || dwRead == 0) break; + stdout_buffer.insert(stdout_buffer.end(), chBuf, chBuf + dwRead); + } + + write_buffer_to_file(stdout_log_file, stdout_buffer); + + CloseHandle(stdout_read_handle); + } + + { + std::vector stderr_buffer; + + DWORD dwRead; + CHAR chBuf[4096]; + BOOL bSuccess = FALSE; + + for (;;) { + bSuccess = ReadFile(stderr_read_handle, chBuf, sizeof(chBuf), &dwRead, + nullptr); + if (bSuccess == FALSE || dwRead == 0) break; + stderr_buffer.insert(stderr_buffer.end(), chBuf, chBuf + dwRead); + } + + write_buffer_to_file(stderr_log_file, stderr_buffer); + + CloseHandle(stderr_read_handle); + } #else - // RPG Hacker: TODO: Needs support for splitting stderr and stdout. - // Currently, I expect tests to just fail on Linux. + // RPG Hacker: TODO: Needs support for splitting stderr and stdout. + // Currently, I expect tests to just fail on Linux. - fflush(stdout); + fflush(stdout); - std::string line = commandline; - line += std::string(" 2>\"") + stderr_log_file + "\" 1>\"" + stdout_log_file + "\""; - FILE * fp = popen(line.c_str(), "r"); + std::string line = commandline; + line += std::string(" 2>\"") + stderr_log_file + "\" 1>\"" + stdout_log_file + "\""; + FILE* fp = popen(line.c_str(), "r"); - pclose(fp); + pclose(fp); #endif - return true; + return true; } #endif -static std::vector tokenize_string(const char * str, const char * key) -{ - std::string s = str; - std::string delimiter = key; - - size_t pos = 0; - std::string token; - std::vector list; - while ((pos = s.find(delimiter)) != std::string::npos) - { - token = s.substr(0, pos); - // Don't bother adding empty tokens (they're just whitespace) - if (token != "") - { - list.push_back(token); - } - s.erase(0, pos + delimiter.length()); - } - list.push_back(s); - return list; +static std::vector tokenize_string(const char* str, const char* key) { + std::string s = str; + std::string delimiter = key; + + size_t pos = 0; + std::string token; + std::vector list; + while ((pos = s.find(delimiter)) != std::string::npos) { + token = s.substr(0, pos); + // Don't bother adding empty tokens (they're just whitespace) + if (token != "") { + list.push_back(token); + } + s.erase(0, pos + delimiter.length()); + } + list.push_back(s); + return list; } -int main(int argc, char * argv[]) -{ +int main(int argc, char* argv[]) { #if defined(_WIN32) - SetConsoleOutputCP(CP_UTF8); - SetConsoleCP(CP_UTF8); + SetConsoleOutputCP(CP_UTF8); + SetConsoleCP(CP_UTF8); #endif - if (argc != 5) - { - // don't treat this as an error and just return 0 + if (argc != 5) { + // don't treat this as an error and just return 0 #if defined(ASAR_TEST_DLL) - printf("Usage: asar-dll-test.exe [asar_dll_path] [path_to_tests_directory] [path_to_unheadered_SMW_ROM_file] [output_directory]\n"); + printf("Usage: asar-dll-test.exe [asar_dll_path] [path_to_tests_directory] " + "[path_to_unheadered_SMW_ROM_file] [output_directory]\n"); #else - printf("Usage: asar-app-test.exe [asar_exe_path] [path_to_tests_directory] [path_to_unheadered_SMW_ROM_file] [output_directory]\n"); + printf("Usage: asar-app-test.exe [asar_exe_path] [path_to_tests_directory] " + "[path_to_unheadered_SMW_ROM_file] [output_directory]\n"); #endif - return 0; - } + return 0; + } #if defined(ASAR_TEST_DLL) - const char * asar_dll_path = argv[1]; - std::string stdincludespath = dir(asar_dll_path) + "stdincludes.txt"; - std::string stddefinespath = dir(asar_dll_path) + "stddefines.txt"; + const char* asar_dll_path = argv[1]; + std::string stdincludespath = dir(asar_dll_path) + "stdincludes.txt"; + std::string stddefinespath = dir(asar_dll_path) + "stddefines.txt"; #else - const char * asar_exe_path = argv[1]; - std::string stdincludespath = dir(asar_exe_path) + "stdincludes.txt"; - std::string stddefinespath = dir(asar_exe_path) + "stddefines.txt"; + const char* asar_exe_path = argv[1]; + std::string stdincludespath = dir(asar_exe_path) + "stdincludes.txt"; + std::string stddefinespath = dir(asar_exe_path) + "stddefines.txt"; #endif - const char * test_directory = argv[2]; - const char * unheadered_rom_file = argv[3]; - const char * output_directory = argv[4]; - std::vector input_files; + const char* test_directory = argv[2]; + const char* unheadered_rom_file = argv[3]; + const char* output_directory = argv[4]; + std::vector input_files; - { - std::string stdinclude = " "; - stdinclude += test_directory; - if (!str_ends_with(stdinclude.c_str(), "/") && !str_ends_with(stdinclude.c_str(), "\\")) stdinclude += "/"; - stdinclude += "stdinclude \n"; + { + std::string stdinclude = " "; + stdinclude += test_directory; + if (!str_ends_with(stdinclude.c_str(), "/") && + !str_ends_with(stdinclude.c_str(), "\\")) + stdinclude += "/"; + stdinclude += "stdinclude \n"; - write_buffer_to_file(stdincludespath.c_str(), stdinclude); - } + write_buffer_to_file(stdincludespath.c_str(), stdinclude); + } - { - std::string stddefines = "!stddefined=1\n stddefined2=1\n\nstddefined3\nstddefined4 = 1 \nstddefined5 = \" $60,$50,$40 \"\n"; + { + std::string stddefines = + "!stddefined=1\n stddefined2=1\n\nstddefined3\nstddefined4 = 1 " + "\nstddefined5 = \" $60,$50,$40 \"\n"; - write_buffer_to_file(stddefinespath.c_str(), stddefines); - } + write_buffer_to_file(stddefinespath.c_str(), stddefines); + } #if defined(ASAR_TEST_DLL) - if (!asar_init_with_dll_path(asar_dll_path)) - { - printf("Error: Failed to load Asar DLL at '%s'.\n", asar_dll_path); - return 1; - } + if (!asar_init_with_dll_path(asar_dll_path)) { + printf("Error: Failed to load Asar DLL at '%s'.\n", asar_dll_path); + return 1; + } #endif - if (!find_files_in_directory(input_files, test_directory) || input_files.size() <= 0) - { - printf("Error: No ASM files found in %s.\n", test_directory); + if (!find_files_in_directory(input_files, test_directory) || + input_files.size() <= 0) { + printf("Error: No ASM files found in %s.\n", test_directory); #if defined(ASAR_TEST_DLL) - asar_close(); + asar_close(); #endif - return 1; - } + return 1; + } - std::sort(input_files.begin(), input_files.end(), [](const wrapped_file& a, const wrapped_file& b) { - return strcmp(a.file_path, b.file_path) < 0; - }); + std::sort(input_files.begin(), input_files.end(), + [](const wrapped_file& a, const wrapped_file& b) { + return strcmp(a.file_path, b.file_path) < 0; + }); - if (!file_exists(unheadered_rom_file)) - { - printf("Error: '%s' doesn't exist!\n", unheadered_rom_file); + if (!file_exists(unheadered_rom_file)) { + printf("Error: '%s' doesn't exist!\n", unheadered_rom_file); #if defined(ASAR_TEST_DLL) - asar_close(); + asar_close(); #endif - return 1; - } + return 1; + } - static const unsigned int smw_size = 512 * 1024; + static const unsigned int smw_size = 512 * 1024; - std::vector smwrom_buf(read_file_into_buffer(unheadered_rom_file)); - const char* smwrom = smwrom_buf.data(); + std::vector smwrom_buf(read_file_into_buffer(unheadered_rom_file)); + const char* smwrom = smwrom_buf.data(); - if (smwrom_buf.size() != smw_size) - { - printf("Error: '%s' doesn't seem to be an unheadered SMW ROM file! Expected a size of %u bytes, got %u.\n", - unheadered_rom_file, smw_size, (unsigned int)smwrom_buf.size()); + if (smwrom_buf.size() != smw_size) { + printf("Error: '%s' doesn't seem to be an unheadered SMW ROM file! Expected a " + "size of %u bytes, got %u.\n", + unheadered_rom_file, smw_size, (unsigned int)smwrom_buf.size()); #if defined(ASAR_TEST_DLL) - asar_close(); + asar_close(); #endif - return 1; - } - - bool has_path_seperator = false; - if (str_ends_with(output_directory, "/") || str_ends_with(output_directory, "\\")) - { - has_path_seperator = true; - } - - - int numfailed = 0; - - // RPG Hacker: A list of standard prints to ignore from print output verification. They will make tests fail that are - // supposed to be successful. This is kind of stinky solution, but there currently isn't really a good way to distinguish - // between "user prints" and prints coming directly from Asar. - std::set standard_prints; - standard_prints.insert("Errors were detected while assembling the patch. Assembling aborted. Your ROM has not been modified."); - standard_prints.insert("A fatal error was detected while assembling the patch. Assembling aborted. Your ROM has not been modified."); - - bool all_lib_test_passed = false; - - for (size_t testno = 0; testno < input_files.size(); ++testno) - { - char * fname = input_files[testno].file_path; - - char out_rom_name[512]; - snprintf(out_rom_name, sizeof(out_rom_name), "%s%s%s%s", output_directory, (has_path_seperator ? "" : "/"), input_files[testno].file_name, ".sfc"); - char stdout_log_name[512]; - snprintf(stdout_log_name, sizeof(stdout_log_name), "%s%s%s%s", output_directory, (has_path_seperator ? "" : "/"), input_files[testno].file_name, ".stdout.log"); - char stderr_log_name[512]; - snprintf(stderr_log_name, sizeof(stderr_log_name), "%s%s%s%s", output_directory, (has_path_seperator ? "" : "/"), input_files[testno].file_name, ".stderr.log"); - - // Delete files if they already exist, so we don't get leftovers from a previous testrun - delete_file(out_rom_name); - delete_file(stdout_log_name); - delete_file(stderr_log_name); - - char * expectedrom = (char*)malloc(max_rom_size); - char * truerom = (char*)malloc(max_rom_size); - // RPG Hacker: Those memsets are required. - // Without them, tests can fail due to garbage being left in memory. - memset(expectedrom, 0, max_rom_size); - memset(truerom, 0, max_rom_size); - if (!file_exists(fname)) dief("Error: '%s' doesn't exist!\n", fname); - int pos = 0; - int len = 0; - - std::vector asmfile(read_file_into_buffer(fname)); - - // RPG Hacker: Skip BoM if present. - if (asmfile[0] == '\xEF' || asmfile[1] == '\xBB' || asmfile[2] == '\xBF') - { - asmfile.erase(asmfile.begin(), asmfile.begin() + 3); - } - - int numiter = 1; - - std::vector expected_errors; - std::vector expected_warnings; - std::vector expected_prints; - std::vector expected_error_prints; - std::vector expected_warn_prints; - - int asm_file_pos = 0; - - while (true) - { - std::string line; - - long int line_start_pos = asm_file_pos; - - while (asm_file_pos < (int)asmfile.size() && asmfile[asm_file_pos] != '\n') - { - // RPG Hacker: We ignore \r, because we don't support text mode and we want our checks - // to work consistently across platforms. - if (asmfile[asm_file_pos] != '\r') - { - line += asmfile[asm_file_pos]; - } - asm_file_pos++; - } - - // If we haven't reached EoF yet, last read must have been a \n, so skip it. - if (asm_file_pos < (int)asmfile.size()) asm_file_pos++; - - if (line[0] == ';' && line[1] == '`') - { - std::vector words = tokenize_string(line.c_str() + 2, " "); - for (size_t i = 0; i < words.size(); ++i) - { - std::string& cur_word = words[i]; - char * outptr; - int num = (int)strtol(words[i].c_str(), &outptr, 16); - if (*outptr) num = -1; - if (cur_word == "+") - { - if (line[2] == '+') - { - memcpy(expectedrom, smwrom, smw_size); - write_buffer_to_file(out_rom_name, smwrom, smw_size); - len = smw_size; - } - } - if ((cur_word.length() == 5 || cur_word.length() == 6) && num >= 0) pos = num; - if (cur_word.length() == 2 && num >= 0) expectedrom[pos++] = (char)num; - if (cur_word[0] == '#') - { - numiter = atoi(cur_word.c_str() + 1); - } - - const char* token = "errE"; - if (strncmp(cur_word.c_str(), token, strlen(token)) == 0) - { - const char* idstr = cur_word.c_str() + strlen(token); - - expected_errors.push_back(idstr-1); - } - - token = "warnW"; - if (strncmp(cur_word.c_str(), token, strlen(token)) == 0) - { - const char* idstr = cur_word.c_str() + strlen(token); - - expected_warnings.push_back(idstr-1); - } - - if (pos > len) len = pos; - } - } - else if(line[0] == ';' && line[1] != '\0' && line[2] == '>') - { - std::string string_to_print(line, 3, std::string::npos); - if (line[1] == 'P') - { - expected_prints.push_back(string_to_print); - } - else if (line[1] == 'E') - { - expected_error_prints.push_back(string_to_print); - } - else if (line[1] == 'W') - { - expected_warn_prints.push_back(string_to_print); - } - } - else - { - asm_file_pos = line_start_pos; - break; - } - } - - printf("(%u/%u) ", (unsigned int)(testno + 1), (unsigned int)input_files.size()); + return 1; + } + + bool has_path_seperator = false; + if (str_ends_with(output_directory, "/") || str_ends_with(output_directory, "\\")) { + has_path_seperator = true; + } + + + int numfailed = 0; + + // RPG Hacker: A list of standard prints to ignore from print output verification. + // They will make tests fail that are supposed to be successful. This is kind of + // stinky solution, but there currently isn't really a good way to distinguish + // between "user prints" and prints coming directly from Asar. + std::set standard_prints; + standard_prints.insert( + "Errors were detected while assembling the patch. Assembling aborted. Your " + "ROM has not been modified."); + standard_prints.insert( + "A fatal error was detected while assembling the patch. Assembling " + "aborted. Your ROM has not been modified."); + + bool all_lib_test_passed = false; + + for (size_t testno = 0; testno < input_files.size(); ++testno) { + char* fname = input_files[testno].file_path; + + char out_rom_name[512]; + snprintf(out_rom_name, sizeof(out_rom_name), "%s%s%s%s", output_directory, + (has_path_seperator ? "" : "/"), input_files[testno].file_name, + ".sfc"); + char stdout_log_name[512]; + snprintf(stdout_log_name, sizeof(stdout_log_name), "%s%s%s%s", output_directory, + (has_path_seperator ? "" : "/"), input_files[testno].file_name, + ".stdout.log"); + char stderr_log_name[512]; + snprintf(stderr_log_name, sizeof(stderr_log_name), "%s%s%s%s", output_directory, + (has_path_seperator ? "" : "/"), input_files[testno].file_name, + ".stderr.log"); + + // Delete files if they already exist, so we don't get leftovers from a previous + // testrun + delete_file(out_rom_name); + delete_file(stdout_log_name); + delete_file(stderr_log_name); + + char* expectedrom = (char*)malloc(max_rom_size); + char* truerom = (char*)malloc(max_rom_size); + // RPG Hacker: Those memsets are required. + // Without them, tests can fail due to garbage being left in memory. + memset(expectedrom, 0, max_rom_size); + memset(truerom, 0, max_rom_size); + if (!file_exists(fname)) dief("Error: '%s' doesn't exist!\n", fname); + int pos = 0; + int len = 0; + + std::vector asmfile(read_file_into_buffer(fname)); + + // RPG Hacker: Skip BoM if present. + if (asmfile[0] == '\xEF' || asmfile[1] == '\xBB' || asmfile[2] == '\xBF') { + asmfile.erase(asmfile.begin(), asmfile.begin() + 3); + } + + int numiter = 1; + + std::vector expected_errors; + std::vector expected_warnings; + std::vector expected_prints; + std::vector expected_error_prints; + std::vector expected_warn_prints; + + int asm_file_pos = 0; + + while (true) { + std::string line; + + long int line_start_pos = asm_file_pos; + + while (asm_file_pos < (int)asmfile.size() && + asmfile[asm_file_pos] != '\n') { + // RPG Hacker: We ignore \r, because we don't support text mode and we + // want our checks to work consistently across platforms. + if (asmfile[asm_file_pos] != '\r') { + line += asmfile[asm_file_pos]; + } + asm_file_pos++; + } + + // If we haven't reached EoF yet, last read must have been a \n, so skip it. + if (asm_file_pos < (int)asmfile.size()) asm_file_pos++; + + if (line[0] == ';' && line[1] == '`') { + std::vector words = tokenize_string(line.c_str() + 2, " "); + for (size_t i = 0; i < words.size(); ++i) { + std::string& cur_word = words[i]; + char* outptr; + int num = (int)strtol(words[i].c_str(), &outptr, 16); + if (*outptr) num = -1; + if (cur_word == "+") { + if (line[2] == '+') { + memcpy(expectedrom, smwrom, smw_size); + write_buffer_to_file(out_rom_name, smwrom, smw_size); + len = smw_size; + } + } + if ((cur_word.length() == 5 || cur_word.length() == 6) && num >= 0) + pos = num; + if (cur_word.length() == 2 && num >= 0) + expectedrom[pos++] = (char)num; + if (cur_word[0] == '#') { + numiter = atoi(cur_word.c_str() + 1); + } + + const char* token = "errE"; + if (strncmp(cur_word.c_str(), token, strlen(token)) == 0) { + const char* idstr = cur_word.c_str() + strlen(token); + + expected_errors.push_back(idstr - 1); + } + + token = "warnW"; + if (strncmp(cur_word.c_str(), token, strlen(token)) == 0) { + const char* idstr = cur_word.c_str() + strlen(token); + + expected_warnings.push_back(idstr - 1); + } + + if (pos > len) len = pos; + } + } else if (line[0] == ';' && line[1] != '\0' && line[2] == '>') { + std::string string_to_print(line, 3, std::string::npos); + if (line[1] == 'P') { + expected_prints.push_back(string_to_print); + } else if (line[1] == 'E') { + expected_error_prints.push_back(string_to_print); + } else if (line[1] == 'W') { + expected_warn_prints.push_back(string_to_print); + } + } else { + asm_file_pos = line_start_pos; + break; + } + } + + printf("(%u/%u) ", (unsigned int)(testno + 1), + (unsigned int)input_files.size()); #if defined(ASAR_TEST_DLL) - // RPG Hacker: We'll just use truerom directly when testing the DLL, since it takes - // a memory buffer, anyways. Makes everything easier. - std::vector out_rom_data(read_file_into_buffer(out_rom_name)); - int truelen = (int)out_rom_data.size(); - memcpy(truerom, out_rom_data.data(), out_rom_data.size()); - - printf("Patching: %s\n", fname); - - { - std::string base_path_string = dir(fname); - const char* base_path = base_path_string.c_str(); - - patchparams asar_patch_params; - memset(&asar_patch_params, 0, sizeof(asar_patch_params)); - - asar_patch_params.structsize = (int)sizeof(asar_patch_params); - - asar_patch_params.patchloc = fname; - asar_patch_params.romdata = truerom; - asar_patch_params.buflen = (int)max_rom_size; - asar_patch_params.romlen = &truelen; - - asar_patch_params.includepaths = &base_path; - asar_patch_params.numincludepaths = 1; - - asar_patch_params.stdincludesfile = stdincludespath.c_str(); - asar_patch_params.stddefinesfile = stddefinespath.c_str(); - - asar_patch_params.full_call_stack = true; - - const definedata libdefines[] = - { - { "cmddefined", nullptr }, - { "!cmddefined2", "" }, - { " !cmddefined3 ", " $10,$F0,$E0 "}, - // RPG Hacker: 日本語������ǵ in UTF-8 - { "cmdl_define_utf8", "\xe6\x97\xa5\xe6\x9c\xac\xe8\xaa\x9e\xf0\x9f\x87\xaf\xf0\x9f\x87\xb5" }, - }; - - asar_patch_params.additional_defines = libdefines; - asar_patch_params.additional_define_count = sizeof(libdefines) / sizeof(libdefines[0]); - - const char memory_file_path[] = "file.memory"; - const char memory_file_buffer[] = "!fancy_memory_file_define = $123456"; - - const memoryfile libmemoryfiles[] = - { - {memory_file_path, (const void *)memory_file_buffer, sizeof(memory_file_buffer) } - }; - - asar_patch_params.memory_files = libmemoryfiles; - asar_patch_params.memory_file_count = sizeof(libmemoryfiles) / sizeof(libmemoryfiles[0]); - - - asar_patch_params.warning_settings = nullptr; - asar_patch_params.warning_setting_count = 0; - - for (int i = 0;i < numiter;i++) - { - - if (numiter > 1) - { - printf("Iteration %d of %d\n", i+1, numiter); - } - - if (!asar_patch(&asar_patch_params)) - { - //printf("asar_patch_ex() failed on file '%s':\n", fname); - } - else - { - bool test_status = true; - // Applying patch via DLL succeeded; print some stuff (mainly to verify most of our functions) - mappertype mapper = asar_getmapper(); - - test_status &= (unsigned int)mapper == lorom ? 1 : 0; - - int count = 0; - const labeldata * labels = asar_getalllabels(&count); - - if (count > 0) - { - bool current_test = false; - for (int j = 0; j < count; ++j) - { - if(!strcmp("lib_test_file_label", labels[j].name) && - labels[j].location == 0x008000 && asar_getlabelval("lib_test_file_label") == 0x8000) current_test = true; - } - test_status &= current_test; - } - - - const definedata * defines = asar_getalldefines(&count); - - if (count > 0) - { - bool current_test = false; - for (int j = 0; j < count; ++j) - { - if(!strcmp("lib_test_file_define", defines[j].name) && - !strcmp("$123456", defines[j].contents)) current_test = true; - } - test_status &= !strcmp(asar_getdefine("lib_test_file_define"), "$123456"); - test_status &= current_test; - if(test_status) - { - test_status &= !strcmp(asar_resolvedefines("!lib_test_file_define"), "$123456"); - } - } - - - const writtenblockdata * writtenblocks = asar_getwrittenblocks(&count); - - if (count > 0) - { - bool current_test = false; - for (int j = 0; j < count; ++j) - { - if(writtenblocks[j].numbytes == 3 && writtenblocks[j].pcoffset == 0) current_test = true; - } - test_status &= current_test; - } - - const char *error_buffer = new char[512]; - test_status &= (int64_t)asar_math("1+0", &error_buffer) == 1; - delete [] error_buffer; - - test_status &= asar_version() >= 20000; - test_status &= asar_maxromsize() == 16*1024*1024; - - //the cli test verifies the symbol file, just make sure something generated - test_status &= asar_getsymbolsfile("wla")[0] == ';'; - - if(test_status) all_lib_test_passed = true; - } - - std::string stderr_buff; - std::string stdout_buff; - - { - int numerrors; - const struct errordata * errors = asar_geterrors(&numerrors); - for (int j = 0; j < numerrors; ++j) - { - stderr_buff += errors[j].fullerrdata; - stderr_buff += "\n"; - } - } - - { - int numwarnings; - const struct errordata * warnings = asar_getwarnings(&numwarnings); - for (int j = 0; j < numwarnings; ++j) - { - stderr_buff += warnings[j].fullerrdata; - stderr_buff += "\n"; - } - } - - { - int numprints; - const char * const * prints = asar_getprints(&numprints); - for (int j = 0; j < numprints; ++j) - { - stdout_buff += prints[j]; - stdout_buff += "\n"; - } - } - - write_buffer_to_file(stderr_log_name, stderr_buff); - write_buffer_to_file(stdout_log_name, stdout_buff); - } - } + // RPG Hacker: We'll just use truerom directly when testing the DLL, since it + // takes a memory buffer, anyways. Makes everything easier. + std::vector out_rom_data(read_file_into_buffer(out_rom_name)); + int truelen = (int)out_rom_data.size(); + memcpy(truerom, out_rom_data.data(), out_rom_data.size()); + + printf("Patching: %s\n", fname); + + { + std::string base_path_string = dir(fname); + const char* base_path = base_path_string.c_str(); + + patchparams asar_patch_params; + memset(&asar_patch_params, 0, sizeof(asar_patch_params)); + + asar_patch_params.structsize = (int)sizeof(asar_patch_params); + + asar_patch_params.patchloc = fname; + asar_patch_params.romdata = truerom; + asar_patch_params.buflen = (int)max_rom_size; + asar_patch_params.romlen = &truelen; + + asar_patch_params.includepaths = &base_path; + asar_patch_params.numincludepaths = 1; + + asar_patch_params.stdincludesfile = stdincludespath.c_str(); + asar_patch_params.stddefinesfile = stddefinespath.c_str(); + + asar_patch_params.full_call_stack = true; + + const definedata libdefines[] = { + {"cmddefined", nullptr}, + {"!cmddefined2", ""}, + {" !cmddefined3 ", " $10,$F0,$E0 "}, + // RPG Hacker: 日本語������ǵ in UTF-8 + {"cmdl_define_utf8", + "\xe6\x97\xa5\xe6\x9c\xac\xe8\xaa\x9e\xf0\x9f\x87\xaf\xf0\x9f\x87" + "\xb5"}, + }; + + asar_patch_params.additional_defines = libdefines; + asar_patch_params.additional_define_count = + sizeof(libdefines) / sizeof(libdefines[0]); + + const char memory_file_path[] = "file.memory"; + const char memory_file_buffer[] = "!fancy_memory_file_define = $123456"; + + const memoryfile libmemoryfiles[] = {{memory_file_path, + (const void*)memory_file_buffer, + sizeof(memory_file_buffer)}}; + + asar_patch_params.memory_files = libmemoryfiles; + asar_patch_params.memory_file_count = + sizeof(libmemoryfiles) / sizeof(libmemoryfiles[0]); + + + asar_patch_params.warning_settings = nullptr; + asar_patch_params.warning_setting_count = 0; + + for (int i = 0; i < numiter; i++) { + if (numiter > 1) { + printf("Iteration %d of %d\n", i + 1, numiter); + } + + if (!asar_patch(&asar_patch_params)) { + // printf("asar_patch_ex() failed on file '%s':\n", fname); + } else { + bool test_status = true; + // Applying patch via DLL succeeded; print some stuff (mainly to + // verify most of our functions) + mappertype mapper = asar_getmapper(); + + test_status &= (unsigned int)mapper == lorom ? 1 : 0; + + int count = 0; + const labeldata* labels = asar_getalllabels(&count); + + if (count > 0) { + bool current_test = false; + for (int j = 0; j < count; ++j) { + if (!strcmp("lib_test_file_label", labels[j].name) && + labels[j].location == 0x008000 && + asar_getlabelval("lib_test_file_label") == 0x8000) + current_test = true; + } + test_status &= current_test; + } + + + const definedata* defines = asar_getalldefines(&count); + + if (count > 0) { + bool current_test = false; + for (int j = 0; j < count; ++j) { + if (!strcmp("lib_test_file_define", defines[j].name) && + !strcmp("$123456", defines[j].contents)) + current_test = true; + } + test_status &= !strcmp(asar_getdefine("lib_test_file_define"), + "$123456"); + test_status &= current_test; + if (test_status) { + test_status &= !strcmp( + asar_resolvedefines("!lib_test_file_define"), + "$123456"); + } + } + + + const writtenblockdata* writtenblocks = + asar_getwrittenblocks(&count); + + if (count > 0) { + bool current_test = false; + for (int j = 0; j < count; ++j) { + if (writtenblocks[j].numbytes == 3 && + writtenblocks[j].pcoffset == 0) + current_test = true; + } + test_status &= current_test; + } + + const char* error_buffer = new char[512]; + test_status &= (int64_t)asar_math("1+0", &error_buffer) == 1; + delete[] error_buffer; + + test_status &= asar_version() >= 20000; + test_status &= asar_maxromsize() == 16 * 1024 * 1024; + + // the cli test verifies the symbol file, just make sure something + // generated + test_status &= asar_getsymbolsfile("wla")[0] == ';'; + + if (test_status) all_lib_test_passed = true; + } + + std::string stderr_buff; + std::string stdout_buff; + + { + int numerrors; + const struct errordata* errors = asar_geterrors(&numerrors); + for (int j = 0; j < numerrors; ++j) { + stderr_buff += errors[j].fullerrdata; + stderr_buff += "\n"; + } + } + + { + int numwarnings; + const struct errordata* warnings = asar_getwarnings(&numwarnings); + for (int j = 0; j < numwarnings; ++j) { + stderr_buff += warnings[j].fullerrdata; + stderr_buff += "\n"; + } + } + + { + int numprints; + const char* const* prints = asar_getprints(&numprints); + for (int j = 0; j < numprints; ++j) { + stdout_buff += prints[j]; + stdout_buff += "\n"; + } + } + + write_buffer_to_file(stderr_log_name, stderr_buff); + write_buffer_to_file(stdout_log_name, stdout_buff); + } + } #else - { - std::string base_path_string = dir(fname); - const char* base_path = base_path_string.c_str(); - - char cmd[1024]; - // randomdude999: temp workaround: using $ in command line is unsafe on linux, so use dec representation instead (for !cmddefined3) - snprintf(cmd, sizeof(cmd), - "\"%s\" -I\"%s\" -Dcli_only=\\$1 -Dcmddefined -D!cmddefined2= --define \" !cmddefined3 = 16,240,224 \"" - // RPG Hacker: 日本語������ǵ in UTF-8 - " -Dcmdl_define_utf8=\xe6\x97\xa5\xe6\x9c\xac\xe8\xaa\x9e\xf0\x9f\x87\xaf\xf0\x9f\x87\xb5" - " \"%s\" \"%s\"", - asar_exe_path, base_path, fname, out_rom_name); - for (int i = 0;i < numiter;i++) - { - printf("Executing: %s\n", cmd); - if (!execute_command_line(cmd, stdout_log_name, stderr_log_name)) - { - dief("Failed executing command line:\n %s\n", cmd); - } - } - all_lib_test_passed = true; - } + { + std::string base_path_string = dir(fname); + const char* base_path = base_path_string.c_str(); + + char cmd[1024]; + // randomdude999: temp workaround: using $ in command line is unsafe on + // linux, so use dec representation instead (for !cmddefined3) + snprintf(cmd, sizeof(cmd), + "\"%s\" -I\"%s\" -Dcli_only=\\$1 -Dcmddefined -D!cmddefined2= " + "--define \" !cmddefined3 = 16,240,224 \"" + // RPG Hacker: 日本語������ǵ in UTF-8 + " -Dcmdl_define_utf8=" + "\xe6\x97\xa5\xe6\x9c\xac\xe8\xaa\x9e\xf0\x9f\x87\xaf\xf0\x9f\x87" + "\xb5" + " \"%s\" \"%s\"", + asar_exe_path, base_path, fname, out_rom_name); + for (int i = 0; i < numiter; i++) { + printf("Executing: %s\n", cmd); + if (!execute_command_line(cmd, stdout_log_name, stderr_log_name)) { + dief("Failed executing command line:\n %s\n", cmd); + } + } + all_lib_test_passed = true; + } #endif - std::vector actual_errors; - std::vector actual_warnings; - std::vector actual_prints; - std::vector actual_error_prints; - std::vector actual_warn_prints; - std::list lines_to_print; - - { - std::vector buf(read_file_into_buffer(stderr_log_name)); - - std::string log_line; - - for (size_t i = 0; i < buf.size(); ++i) - { - if (buf[i] == '\n' || buf[i] == '\0') - { - const char* token = ": error: (E"; - size_t found = log_line.find(token); - if (found != std::string::npos) - { - size_t found_end = log_line.find(')', found + 1); - - if (found_end == std::string::npos) - { - dief("Error: Failed parsing error name from Asar output!\n"); - } - - size_t start_pos = found + strlen(token) - 1; - actual_errors.push_back(log_line.substr(start_pos, found_end - start_pos)); - - // RPG Hacker: Check if it's the error command. If so, we also need to add a print as well. - { - std::string command_token = ": error command: "; - std::string remainder = log_line.substr(found_end+1); - size_t command_found = remainder.find(command_token); - - if (command_found != std::string::npos) - { - actual_error_prints.push_back(std::string(remainder, command_found + command_token.length(), std::string::npos)); - } - } - - // RPG Hacker: Same goes for the assert command. - { - std::string command_token = ": Assertion failed: "; - std::string remainder = log_line.substr(found_end + 1); - size_t command_found = remainder.find(command_token); - - if (command_found != std::string::npos) - { - size_t string_start_pos = command_found + command_token.length(); - actual_error_prints.push_back(std::string(remainder, string_start_pos, std::string::npos)); - } - } - - lines_to_print.push_back(log_line); - } - - token = ": warning: (W"; - found = log_line.find(token); - if (found != std::string::npos) - { - size_t found_end = log_line.find(')', found + 1); - - if (found_end == std::string::npos) - { - dief("Error: Failed parsing warning code from Asar output!\n"); - } - - size_t start_pos = found + strlen(token) - 1; - actual_warnings.push_back(log_line.substr(start_pos, found_end - start_pos)); - - // RPG Hacker: Check if it's the warn command. If so, we also need to add a print as well. - { - std::string command_token = ": warn command: "; - std::string remainder = log_line.substr(found_end + 1); - size_t command_found = remainder.find(command_token); - - if (command_found != std::string::npos) - { - actual_warn_prints.push_back(std::string(remainder, command_found + command_token.length(), std::string::npos)); - } - } - - lines_to_print.push_back(log_line); - } - - log_line.clear(); - } - else - { - if (buf[i] != '\r') - { - log_line += buf[i]; - } - } - } - } - - { - std::vector buf(read_file_into_buffer(stdout_log_name)); - - std::string log_line; - - for (size_t i = 0; i < buf.size(); ++i) - { - // RPG Hacker: We treat \0 as EOF now. - // I have no idea how these even end up in the output file, but they definitely shouldn't be considered - // part of the actual output, and doing so will make a bunch of tests fail. - if (buf[i] == '\0') - { - break; - } - - if (buf[i] == '\n') - { - if (standard_prints.find(log_line) == standard_prints.cend()) - { - actual_prints.push_back(log_line); - - lines_to_print.push_back(log_line); - } - log_line.clear(); - } - else - { - if (buf[i] != '\r') - { - log_line += buf[i]; - } - } - } - } - - bool did_fail = false; - if (expected_errors.size() != actual_errors.size() - || expected_warnings.size() != actual_warnings.size() - || expected_prints.size() != actual_prints.size() - || expected_error_prints.size() != actual_error_prints.size() - || expected_warn_prints.size() != actual_warn_prints.size() - || !std::equal(expected_errors.begin(), expected_errors.end(), actual_errors.begin()) - || !std::equal(expected_warnings.begin(), expected_warnings.end(), actual_warnings.begin()) - || !std::equal(expected_prints.begin(), expected_prints.end(), actual_prints.begin()) - || !std::equal(expected_error_prints.begin(), expected_error_prints.end(), actual_error_prints.begin()) - || !std::equal(expected_warn_prints.begin(), expected_warn_prints.end(), actual_warn_prints.begin())) - did_fail = true; - - if(did_fail) { - // this thing depends on c++11 already, right? - for(std::string line_to_print : lines_to_print) - { - printf("%s\n", line_to_print.c_str()); - } - printf("\nExpected errors: "); - for (auto it = expected_errors.begin(); it != expected_errors.end(); ++it) - { - printf("%s%s", (it != expected_errors.begin() ? "," : ""), it->c_str()); - } - printf("\n"); - - printf("Actual errors: "); - for (auto it = actual_errors.begin(); it != actual_errors.end(); ++it) - { - printf("%s%s", (it != actual_errors.begin() ? "," : ""), it->c_str()); - } - printf("\n"); - - printf("\nExpected warnings: "); - for (auto it = expected_warnings.begin(); it != expected_warnings.end(); ++it) - { - printf("%s%s", (it != expected_warnings.begin() ? "," : ""), it->c_str()); - } - printf("\n"); - - printf("Actual warnings: "); - for (auto it = actual_warnings.begin(); it != actual_warnings.end(); ++it) - { - printf("%s%s", (it != actual_warnings.begin() ? "," : ""), it->c_str()); - } - printf("\n"); - - printf("\nExpected user prints: \n"); - for (auto it = expected_prints.begin(); it != expected_prints.end(); ++it) - { - printf("(Print) \"%s\"\n", it->c_str()); - } - for (auto it = expected_error_prints.begin(); it != expected_error_prints.end(); ++it) - { - printf("(Error) \"%s\"\n", it->c_str()); - } - for (auto it = expected_warn_prints.begin(); it != expected_warn_prints.end(); ++it) - { - printf("(Warning) \"%s\"\n", it->c_str()); - } - printf("\n"); - - printf("Actual user prints: \n"); - for (auto it = actual_prints.begin(); it != actual_prints.end(); ++it) - { - printf("(Print) \"%s\"\n", it->c_str()); - } - for (auto it = actual_error_prints.begin(); it != actual_error_prints.end(); ++it) - { - printf("(Error) \"%s\"\n", it->c_str()); - } - for (auto it = actual_warn_prints.begin(); it != actual_warn_prints.end(); ++it) - { - printf("(Warning) \"%s\"\n", it->c_str()); - } - printf("\n"); - - dief("Mismatch!\n"); - } + std::vector actual_errors; + std::vector actual_warnings; + std::vector actual_prints; + std::vector actual_error_prints; + std::vector actual_warn_prints; + std::list lines_to_print; + + { + std::vector buf(read_file_into_buffer(stderr_log_name)); + + std::string log_line; + + for (size_t i = 0; i < buf.size(); ++i) { + if (buf[i] == '\n' || buf[i] == '\0') { + const char* token = ": error: (E"; + size_t found = log_line.find(token); + if (found != std::string::npos) { + size_t found_end = log_line.find(')', found + 1); + + if (found_end == std::string::npos) { + dief("Error: Failed parsing error name from Asar " + "output!\n"); + } + + size_t start_pos = found + strlen(token) - 1; + actual_errors.push_back( + log_line.substr(start_pos, found_end - start_pos)); + + // RPG Hacker: Check if it's the error command. If so, we also + // need to add a print as well. + { + std::string command_token = ": error command: "; + std::string remainder = log_line.substr(found_end + 1); + size_t command_found = remainder.find(command_token); + + if (command_found != std::string::npos) { + actual_error_prints.push_back(std::string( + remainder, + command_found + command_token.length(), + std::string::npos)); + } + } + + // RPG Hacker: Same goes for the assert command. + { + std::string command_token = ": Assertion failed: "; + std::string remainder = log_line.substr(found_end + 1); + size_t command_found = remainder.find(command_token); + + if (command_found != std::string::npos) { + size_t string_start_pos = + command_found + command_token.length(); + actual_error_prints.push_back( + std::string(remainder, string_start_pos, + std::string::npos)); + } + } + + lines_to_print.push_back(log_line); + } + + token = ": warning: (W"; + found = log_line.find(token); + if (found != std::string::npos) { + size_t found_end = log_line.find(')', found + 1); + + if (found_end == std::string::npos) { + dief("Error: Failed parsing warning code from Asar " + "output!\n"); + } + + size_t start_pos = found + strlen(token) - 1; + actual_warnings.push_back( + log_line.substr(start_pos, found_end - start_pos)); + + // RPG Hacker: Check if it's the warn command. If so, we also + // need to add a print as well. + { + std::string command_token = ": warn command: "; + std::string remainder = log_line.substr(found_end + 1); + size_t command_found = remainder.find(command_token); + + if (command_found != std::string::npos) { + actual_warn_prints.push_back(std::string( + remainder, + command_found + command_token.length(), + std::string::npos)); + } + } + + lines_to_print.push_back(log_line); + } + + log_line.clear(); + } else { + if (buf[i] != '\r') { + log_line += buf[i]; + } + } + } + } + + { + std::vector buf(read_file_into_buffer(stdout_log_name)); + + std::string log_line; + + for (size_t i = 0; i < buf.size(); ++i) { + // RPG Hacker: We treat \0 as EOF now. + // I have no idea how these even end up in the output file, but they + // definitely shouldn't be considered part of the actual output, and + // doing so will make a bunch of tests fail. + if (buf[i] == '\0') { + break; + } + + if (buf[i] == '\n') { + if (standard_prints.find(log_line) == standard_prints.cend()) { + actual_prints.push_back(log_line); + + lines_to_print.push_back(log_line); + } + log_line.clear(); + } else { + if (buf[i] != '\r') { + log_line += buf[i]; + } + } + } + } + + bool did_fail = false; + if (expected_errors.size() != actual_errors.size() || + expected_warnings.size() != actual_warnings.size() || + expected_prints.size() != actual_prints.size() || + expected_error_prints.size() != actual_error_prints.size() || + expected_warn_prints.size() != actual_warn_prints.size() || + !std::equal(expected_errors.begin(), expected_errors.end(), + actual_errors.begin()) || + !std::equal(expected_warnings.begin(), expected_warnings.end(), + actual_warnings.begin()) || + !std::equal(expected_prints.begin(), expected_prints.end(), + actual_prints.begin()) || + !std::equal(expected_error_prints.begin(), expected_error_prints.end(), + actual_error_prints.begin()) || + !std::equal(expected_warn_prints.begin(), expected_warn_prints.end(), + actual_warn_prints.begin())) + did_fail = true; + + if (did_fail) { + // this thing depends on c++11 already, right? + for (std::string line_to_print : lines_to_print) { + printf("%s\n", line_to_print.c_str()); + } + printf("\nExpected errors: "); + for (auto it = expected_errors.begin(); it != expected_errors.end(); ++it) { + printf("%s%s", (it != expected_errors.begin() ? "," : ""), it->c_str()); + } + printf("\n"); + + printf("Actual errors: "); + for (auto it = actual_errors.begin(); it != actual_errors.end(); ++it) { + printf("%s%s", (it != actual_errors.begin() ? "," : ""), it->c_str()); + } + printf("\n"); + + printf("\nExpected warnings: "); + for (auto it = expected_warnings.begin(); it != expected_warnings.end(); + ++it) { + printf("%s%s", (it != expected_warnings.begin() ? "," : ""), + it->c_str()); + } + printf("\n"); + + printf("Actual warnings: "); + for (auto it = actual_warnings.begin(); it != actual_warnings.end(); ++it) { + printf("%s%s", (it != actual_warnings.begin() ? "," : ""), it->c_str()); + } + printf("\n"); + + printf("\nExpected user prints: \n"); + for (auto it = expected_prints.begin(); it != expected_prints.end(); ++it) { + printf("(Print) \"%s\"\n", it->c_str()); + } + for (auto it = expected_error_prints.begin(); + it != expected_error_prints.end(); ++it) { + printf("(Error) \"%s\"\n", it->c_str()); + } + for (auto it = expected_warn_prints.begin(); + it != expected_warn_prints.end(); ++it) { + printf("(Warning) \"%s\"\n", it->c_str()); + } + printf("\n"); + + printf("Actual user prints: \n"); + for (auto it = actual_prints.begin(); it != actual_prints.end(); ++it) { + printf("(Print) \"%s\"\n", it->c_str()); + } + for (auto it = actual_error_prints.begin(); it != actual_error_prints.end(); + ++it) { + printf("(Error) \"%s\"\n", it->c_str()); + } + for (auto it = actual_warn_prints.begin(); it != actual_warn_prints.end(); + ++it) { + printf("(Warning) \"%s\"\n", it->c_str()); + } + printf("\n"); + + dief("Mismatch!\n"); + } #if !defined(ASAR_TEST_DLL) - std::vector out_rom_data(read_file_into_buffer(out_rom_name)); - int truelen = (int)out_rom_data.size(); - memcpy(truerom, out_rom_data.data(), out_rom_data.size()); + std::vector out_rom_data(read_file_into_buffer(out_rom_name)); + int truelen = (int)out_rom_data.size(); + memcpy(truerom, out_rom_data.data(), out_rom_data.size()); #endif - bool fail = false; - for (int i = 0;i < min(len, truelen); i++) - { - if (truerom[i] != expectedrom[i] && !(i >= 0x07FDC && i <= 0x07FDF && (expectedrom[i] == 0x00 || expectedrom[i] == smwrom[i]))) - { - printf("%s: Mismatch at %.5X: Expected %.2X, got %.2X\n", fname, i, (unsigned char)expectedrom[i], (unsigned char)truerom[i]); - fail = true; - } - } - if (truelen != len) dief("%s: Bad ROM length (expected %X, got %X)\n", fname, len, truelen); - if (fail) die(); - - free(expectedrom); - free(truerom); - - printf("Success!\n\n"); - } - - printf("%u out of %u performed tests succeeded.\n", (unsigned int)(input_files.size() - (size_t)numfailed), (unsigned int)input_files.size()); + bool fail = false; + for (int i = 0; i < min(len, truelen); i++) { + if (truerom[i] != expectedrom[i] && + !(i >= 0x07FDC && i <= 0x07FDF && + (expectedrom[i] == 0x00 || expectedrom[i] == smwrom[i]))) { + printf("%s: Mismatch at %.5X: Expected %.2X, got %.2X\n", fname, i, + (unsigned char)expectedrom[i], (unsigned char)truerom[i]); + fail = true; + } + } + if (truelen != len) + dief("%s: Bad ROM length (expected %X, got %X)\n", fname, len, truelen); + if (fail) die(); + + free(expectedrom); + free(truerom); + + printf("Success!\n\n"); + } + + printf("%u out of %u performed tests succeeded.\n", + (unsigned int)(input_files.size() - (size_t)numfailed), + (unsigned int)input_files.size()); #if defined(ASAR_TEST_DLL) - asar_close(); + asar_close(); #endif - if (numfailed > 0 || !all_lib_test_passed) - { - if(!all_lib_test_passed) printf("Library API failed to verify.\n"); - printf("Some tests failed!\n"); - return 1; - } + if (numfailed > 0 || !all_lib_test_passed) { + if (!all_lib_test_passed) printf("Library API failed to verify.\n"); + printf("Some tests failed!\n"); + return 1; + } - return 0; + return 0; } diff --git a/src/asar/addr2line.cpp b/src/asar/addr2line.cpp index 511b99d7..e885cf09 100644 --- a/src/asar/addr2line.cpp +++ b/src/asar/addr2line.cpp @@ -1,4 +1,5 @@ #include "addr2line.h" + #include "asar.h" #include "crc32.h" @@ -9,49 +10,44 @@ // should be added to this, and then read back during symbol file // generation -void AddressToLineMapping::reset() -{ - m_fileList.reset(); - m_filenameCrcs.reset(); - m_addrToLineInfo.reset(); +void AddressToLineMapping::reset() { + m_fileList.reset(); + m_filenameCrcs.reset(); + m_addrToLineInfo.reset(); } // Adds information of what source file and line number an output rom address is at -void AddressToLineMapping::includeMapping(const char* filename, int line, int addr) -{ - AddrToLineInfo newInfo; - newInfo.fileIdx = getFileIndex(filename); - newInfo.line = line; - newInfo.addr = addr; - - m_addrToLineInfo.append(newInfo); +void AddressToLineMapping::includeMapping(const char* filename, int line, int addr) { + AddrToLineInfo newInfo; + newInfo.fileIdx = getFileIndex(filename); + newInfo.line = line; + newInfo.addr = addr; + + m_addrToLineInfo.append(newInfo); } // Helper to add file to list, and get the index of that file -int AddressToLineMapping::getFileIndex(const char* filename) -{ - // check if the file exists first - uint32_t filenameCrc = crc32((const uint8_t*)filename, (unsigned int)strlen(filename)); - for (int i = 0; i < m_filenameCrcs.count; ++i) - { - if (m_filenameCrcs[i] == filenameCrc) - { - return i; - } - } - - // file doesn't exist, so start tracking it - char* data = nullptr; - int len = 0; - uint32_t fileCrc = 0; - if (readfile(filename, "", &data, &len)) - { - fileCrc = crc32((const uint8_t*)data, (unsigned int)len); - } - free(data); - - m_fileList.append({ string(filename), fileCrc }); - m_filenameCrcs.append(filenameCrc); - - return m_fileList.count - 1; +int AddressToLineMapping::getFileIndex(const char* filename) { + // check if the file exists first + uint32_t filenameCrc = + crc32((const uint8_t*)filename, (unsigned int)strlen(filename)); + for (int i = 0; i < m_filenameCrcs.count; ++i) { + if (m_filenameCrcs[i] == filenameCrc) { + return i; + } + } + + // file doesn't exist, so start tracking it + char* data = nullptr; + int len = 0; + uint32_t fileCrc = 0; + if (readfile(filename, "", &data, &len)) { + fileCrc = crc32((const uint8_t*)data, (unsigned int)len); + } + free(data); + + m_fileList.append({string(filename), fileCrc}); + m_filenameCrcs.append(filenameCrc); + + return m_fileList.count - 1; } diff --git a/src/asar/addr2line.h b/src/asar/addr2line.h index 7e475fc9..807bc869 100644 --- a/src/asar/addr2line.h +++ b/src/asar/addr2line.h @@ -10,40 +10,37 @@ #include "autoarray.h" #include "libstr.h" -class AddressToLineMapping -{ +class AddressToLineMapping { public: - - struct AddrToLineInfo - { - int fileIdx; - int line; - int addr; - }; - - // resets the mapping to initial state - void reset(); - - // Adds information of what source file and line number an output rom address is at - void includeMapping(const char* filename, int line, int addr); - - struct FileInfo - { - string filename; - uint32_t fileCrc; - }; - const autoarray& getFileList() const { return m_fileList; } - const autoarray& getAddrToLineInfo() const { return m_addrToLineInfo; } + struct AddrToLineInfo { + int fileIdx; + int line; + int addr; + }; + + // resets the mapping to initial state + void reset(); + + // Adds information of what source file and line number an output rom address is at + void includeMapping(const char* filename, int line, int addr); + + struct FileInfo { + string filename; + uint32_t fileCrc; + }; + const autoarray& getFileList() const { return m_fileList; } + const autoarray& getAddrToLineInfo() const { + return m_addrToLineInfo; + } private: + // Helper to add file to list, and get the index of that file + int getFileIndex(const char* filename); - // Helper to add file to list, and get the index of that file - int getFileIndex(const char* filename); - - autoarray m_fileList; - // parallel list of crcs of the filenames in fileList, to speed up lookups - autoarray m_filenameCrcs; + autoarray m_fileList; + // parallel list of crcs of the filenames in fileList, to speed up lookups + autoarray m_filenameCrcs; - autoarray m_addrToLineInfo; + autoarray m_addrToLineInfo; }; diff --git a/src/asar/arch-65816.cpp b/src/asar/arch-65816.cpp index 5ab4d264..f417dcae 100644 --- a/src/asar/arch-65816.cpp +++ b/src/asar/arch-65816.cpp @@ -1,221 +1,388 @@ #include "asar.h" -#include "assembleblock.h" #include "asar_math.h" +#include "assembleblock.h" #define write1 write1_pick -void asinit_65816() -{ -} +void asinit_65816() {} -void asend_65816() -{ -} +void asend_65816() {} extern bool fastrom; extern int recent_opcode_num; -bool asblock_65816(char** word, int numwords) -{ +bool asblock_65816(char** word, int numwords) { #define is(test) (!stricmpwithupper(word[0], test)) -//#define par word[1] - string par; - if (word[1]) par = word[1]; - unsigned int num; - int len=0;//declared here for A->generic fallback - bool explicitlen = false; - bool hexconstant = false; - if(0); -#define getvars(optbank) num=(pass==2)?getnum(par):0; hexconstant=is_hex_constant(par); if (word[0][3]=='.') { len=getlenfromchar(word[0][4]); explicitlen=true; word[0][3]='\0'; } else {len=getlen(par, optbank); explicitlen=false;} + //#define par word[1] + string par; + if (word[1]) par = word[1]; + unsigned int num; + int len = 0; // declared here for A->generic fallback + bool explicitlen = false; + bool hexconstant = false; + if (0) + ; +#define getvars(optbank) \ + num = (pass == 2) ? getnum(par) : 0; \ + hexconstant = is_hex_constant(par); \ + if (word[0][3] == '.') { \ + len = getlenfromchar(word[0][4]); \ + explicitlen = true; \ + word[0][3] = '\0'; \ + } else { \ + len = getlen(par, optbank); \ + explicitlen = false; \ + } #define match(left, right) (word[1] && stribegin(par, left) && striend(par, right)) #define matchr(right) (word[1] && striend(par, right)) #define matchl(left) (word[1] && stribegin(par, left)) -#define init(left, right) par.strip_suffix(right); par.strip_prefix(left); getvars(false) -#define init_index(left, right) itrim(par, left, right); getvars(false) -#define bankoptinit(left) par.strip_prefix(left); getvars(true) -#define blankinit() len=1; explicitlen=false; num=0 +#define init(left, right) \ + par.strip_suffix(right); \ + par.strip_prefix(left); \ + getvars(false) +#define init_index(left, right) \ + itrim(par, left, right); \ + getvars(false) +#define bankoptinit(left) \ + par.strip_prefix(left); \ + getvars(true) +#define blankinit() \ + len = 1; \ + explicitlen = false; \ + num = 0 #define end() return false -#define as0( op, byte) if (is(op) ) { write1((unsigned int)byte); return true; } -#define as1( op, byte) if (len==1 && is(op)) { write1((unsigned int)byte); write1(num); return true; } -#define as2( op, byte) if (len==2 && is(op)) { write1((unsigned int)byte); write2(num); return true; } -#define as3( op, byte) if (len==3 && is(op)) { write1((unsigned int)byte); write3(num); return true; } -//#define as23( op, byte) if (is(op) && (len==2 || len==3)) { write1(byte); write2(num); return true; } -#define as32( op, byte) if (is(op) && ((len==2 && !explicitlen) || len==3)) { write1((unsigned int)byte); write3(num); return true; } -#define as_a( op, byte) if (is(op)) { if(!explicitlen && !hexconstant) asar_throw_warning(0, warning_id_implicitly_sized_immediate); if (len==1) { write1(byte); write1(num); } \ - else { write1((unsigned int)byte); write2(num); } return true; } -#define as_xy( op, byte) if (is(op)) { if(!explicitlen && !hexconstant) asar_throw_warning(0, warning_id_implicitly_sized_immediate); if (len==1) { write1(byte); write1(num); } \ - else { write1((unsigned int)byte); write2(num); } return true; } -#define as_rep( op, byte) if (is(op)) { if (pass<2) { num=getnum(par); } if(foundlabel) asar_throw_error(0, error_type_block, error_id_no_labels_here); for (unsigned int i=0;i127)) asar_throw_error(2, error_type_block, error_id_relative_branch_out_of_bounds, dec(pos).data()); \ - return true; } -#define as_rel2(op, byte) if (is(op)) { int pos=(!foundlabel)?(int)num:(int)num-((snespos&0xFFFFFF)+3); write1((unsigned int)byte); write2((unsigned int)pos);\ - if (pass==2 && foundlabel && (pos<-32768 || pos>32767)) asar_throw_error(2, error_type_block, error_id_relative_branch_out_of_bounds, dec(pos).data()); \ - return true; } -#define the8(offset, len) as##len("ORA", offset+0x00); as##len("AND", offset+0x20); as##len("EOR", offset+0x40); as##len("ADC", offset+0x60); \ - as##len("STA", offset+0x80); as##len("LDA", offset+0xA0); as##len("CMP", offset+0xC0); as##len("SBC", offset+0xE0) -#define thenext8(offset, len) as##len("ASL", offset+0x00); as##len("BIT", offset+0x1E); as##len("ROL", offset+0x20); as##len("LSR", offset+0x40); \ - as##len("ROR", offset+0x60); as##len("LDY", offset+0x9E); as##len("DEC", offset+0xC0); as##len("INC", offset+0xE0) -#define thefinal7(offset, len) as##len("TSB", offset+0x00); as##len("TRB", offset+0x10); as##len("STY", offset+0x80); as##len("STX", offset+0x82); \ - as##len("LDX", offset+0xA2); as##len("CPY", offset+0xC0); as##len("CPX", offset+0xE0) -#define onlythe8(left, right, offset) else if (match(left, right)) do { init_index(left, right); the8(offset, 1); end(); } while(0) - else if ((strlen(word[0])!=3 && (strlen(word[0])!=5 || word[0][3]!='.')) || (word[1] && word[2])) return false; - else if (!word[1]) - { - blankinit(); - as0("PHP", 0x08); as0("ASL", 0x0A); as0("PHD", 0x0B); as0("CLC", 0x18); - as0("INC", 0x1A); as0("TCS", 0x1B); as0("PLP", 0x28); as0("ROL", 0x2A); - as0("PLD", 0x2B); as0("SEC", 0x38); as0("DEC", 0x3A); as0("TSC", 0x3B); - as0("RTI", 0x40); as0("PHA", 0x48); as0("LSR", 0x4A); as0("PHK", 0x4B); - as0("CLI", 0x58); as0("PHY", 0x5A); as0("TCD", 0x5B); as0("RTS", 0x60); - as0("PLA", 0x68); as0("ROR", 0x6A); as0("RTL", 0x6B); as0("SEI", 0x78); - as0("PLY", 0x7A); as0("TDC", 0x7B); as0("DEY", 0x88); as0("TXA", 0x8A);//these tables are blatantly stolen from xkas - as0("PHB", 0x8B); as0("TYA", 0x98); as0("TXS", 0x9A); as0("TXY", 0x9B); - as0("TAY", 0xA8); as0("TAX", 0xAA); as0("PLB", 0xAB); as0("CLV", 0xB8); - as0("TSX", 0xBA); as0("TYX", 0xBB); as0("INY", 0xC8); as0("DEX", 0xCA); - as0("WAI", 0xCB); as0("CLD", 0xD8); as0("PHX", 0xDA); as0("STP", 0xDB); - as0("INX", 0xE8); as0("NOP", 0xEA); as0("XBA", 0xEB); as0("SED", 0xF8); - as0("PLX", 0xFA); as0("XCE", 0xFB); - as1("BRK", 0x00); as1("COP", 0x02); as1("WDM", 0x42); - //as0("DEA", 0x3A); as0("INA", 0x1A); as0("TAD", 0x5B); as0("TDA", 0x7B);//nobody cares about these, but keeping them does no harm - //as0("TAS", 0x1B); as0("TSA", 0x3B); as0("SWA", 0xEB); //actually, it does: it may make some users think it's correct. - end(); - } - else if (!stricmp(word[1], "A")) - { - blankinit(); - as0("ASL", 0x0A); as0("LSR", 0x4A); as0("ROL", 0x2A); as0("ROR", 0x6A); - as0("INC", 0x1A); as0("DEC", 0x3A); - goto opAFallback;//yay goto - } - else if (matchl("#")) - { - bankoptinit('#'); - as_a("ORA", 0x09); as_a("AND", 0x29); as_a("EOR", 0x49); as_a("ADC", 0x69); - as_a("BIT", 0x89); as_a("LDA", 0xA9); as_a("CMP", 0xC9); as_a("SBC", 0xE9); - as_xy("CPX", 0xE0); as_xy("CPY", 0xC0); as_xy("LDX", 0xA2); as_xy("LDY", 0xA0); - as_rep("ASL", 0x0A); as_rep("LSR", 0x4A); as_rep("ROL", 0x2A); as_rep("ROR", 0x6A); - as_rep("INC", 0x1A); as_rep("DEC", 0x3A); as_rep("INX", 0xE8); as_rep("DEX", 0xCA); - as_rep("INY", 0xC8); as_rep("DEY", 0x88); as_rep("NOP", 0xEA); - as1("REP", 0xC2); as1("SEP", 0xE2); - as1("BRK", 0x00); as1("COP", 0x02); as1("WDM", 0x42); - end(); - } - onlythe8("(", ",s),y", 0x13); - onlythe8("[", "],y", 0x17); - onlythe8("(", "),y", 0x11); - onlythe8("", ",s", 0x03); - else if (match("[", "]")) - { - init('[', ']'); - the8(0x07, 1); - as2("JMP", 0xDC); as2("JML", 0xDC); - end(); - } - else if (match("(", ",x)")) - { - init_index("(", ",x)"); - the8(0x01, 1); - as2("JMP", 0x7C); as2("JSR", 0xFC); - end(); - } - else if (match("(", ")")) - { - init('(', ')'); - the8(0x12, 1); - as1("PEI", 0xD4); - as2("JMP", 0x6C); - end(); - } - else if (matchr(",x")) - { - init_index("", ",x"); - if (match("(", ")")) asar_throw_warning(0, warning_id_65816_yy_x_does_not_exist); - the8(0x1F, 3); - the8(0x1D, 2); - the8(0x15, 1); - thenext8(0x16, 1); - thenext8(0x1E, 2); - as1("STZ", 0x74); - as1("STY", 0x94); - as2("STZ", 0x9E); - end(); - } - else if (matchr(",y")) - { - init_index("", ",y"); - as1("LDX", 0xB6); - as1("STX", 0x96); - as2("LDX", 0xBE); - if (len==1 && (is("ORA") || is("AND") || is("EOR") || is("ADC") || is("STA") || is("LDA") || is("CMP") || is("SBC"))) - { - asar_throw_warning(0, warning_id_65816_xx_y_assume_16_bit, word[0]); - len=2; - } - the8(0x19, 2); - end(); - } - else - { - if ((is("MVN") || is("MVP"))) - { - int numargs; - autoptrparam=qpsplit(par.temp_raw(), ',', &numargs); - if (numargs ==2) - { - write1(is("MVN")?(unsigned int)0x54:(unsigned int)0x44); - write1(getnum(param[0])); - write1(getnum(param[1])); - return true; - } - } - if (false) - { -opAFallback: - snes_label tmp; - if (pass && !labelval(par, &tmp)) return false; - len=getlen(par); - num=tmp.pos; - } - if (is("JSR") || is("JMP")) - { - int tmp=optimizeforbank; - optimizeforbank=-1; - getvars(false) - optimizeforbank=tmp; - } - else - { - getvars(false) - } - the8(0x0F, 3); - the8(0x0D, 2); - the8(0x05, 1); - thenext8(0x06, 1); - thenext8(0x0E, 2); - thefinal7(0x04, 1); - thefinal7(0x0C, 2); - as1("STZ", 0x64); - as2("STZ", 0x9C); - as2("JMP", 0x4C); - as2("JSR", 0x20); - as32("JML", 0x5C); - as32("JSL", 0x22); - as2("MVN", 0x54); - as2("MVP", 0x44); - as2("PEA", 0xF4); - as_rel1("BRA", 0x80); - as_rel1("BCC", 0x90); - as_rel1("BCS", 0xB0); - as_rel1("BEQ", 0xF0); - as_rel1("BNE", 0xD0); - as_rel1("BMI", 0x30); - as_rel1("BPL", 0x10); - as_rel1("BVC", 0x50); - as_rel1("BVS", 0x70); - as_rel2("BRL", 0x82); - as_rel2("PER", 0x62); - end(); - } - return true; +#define as0(op, byte) \ + if (is(op)) { \ + write1((unsigned int)byte); \ + return true; \ + } +#define as1(op, byte) \ + if (len == 1 && is(op)) { \ + write1((unsigned int)byte); \ + write1(num); \ + return true; \ + } +#define as2(op, byte) \ + if (len == 2 && is(op)) { \ + write1((unsigned int)byte); \ + write2(num); \ + return true; \ + } +#define as3(op, byte) \ + if (len == 3 && is(op)) { \ + write1((unsigned int)byte); \ + write3(num); \ + return true; \ + } +//#define as23( op, byte) if (is(op) && (len==2 || len==3)) { write1(byte); +// write2(num); return true; } +#define as32(op, byte) \ + if (is(op) && ((len == 2 && !explicitlen) || len == 3)) { \ + write1((unsigned int)byte); \ + write3(num); \ + return true; \ + } +#define as_a(op, byte) \ + if (is(op)) { \ + if (!explicitlen && !hexconstant) \ + asar_throw_warning(0, warning_id_implicitly_sized_immediate); \ + if (len == 1) { \ + write1(byte); \ + write1(num); \ + } else { \ + write1((unsigned int)byte); \ + write2(num); \ + } \ + return true; \ + } +#define as_xy(op, byte) \ + if (is(op)) { \ + if (!explicitlen && !hexconstant) \ + asar_throw_warning(0, warning_id_implicitly_sized_immediate); \ + if (len == 1) { \ + write1(byte); \ + write1(num); \ + } else { \ + write1((unsigned int)byte); \ + write2(num); \ + } \ + return true; \ + } +#define as_rep(op, byte) \ + if (is(op)) { \ + if (pass < 2) { \ + num = getnum(par); \ + } \ + if (foundlabel) \ + asar_throw_error(0, error_type_block, error_id_no_labels_here); \ + for (unsigned int i = 0; i < num; i++) { \ + write1((unsigned int)byte); \ + } \ + recent_opcode_num = num; \ + return true; \ + } +#define as_rel1(op, byte) \ + if (is(op)) { \ + int pos = (!foundlabel) ? (int)num : (int)num - ((snespos & 0xFFFFFF) + 2); \ + write1((unsigned int)byte); \ + write1((unsigned int)pos); \ + if (pass == 2 && foundlabel && (pos < -128 || pos > 127)) \ + asar_throw_error(2, error_type_block, \ + error_id_relative_branch_out_of_bounds, dec(pos).data()); \ + return true; \ + } +#define as_rel2(op, byte) \ + if (is(op)) { \ + int pos = (!foundlabel) ? (int)num : (int)num - ((snespos & 0xFFFFFF) + 3); \ + write1((unsigned int)byte); \ + write2((unsigned int)pos); \ + if (pass == 2 && foundlabel && (pos < -32768 || pos > 32767)) \ + asar_throw_error(2, error_type_block, \ + error_id_relative_branch_out_of_bounds, dec(pos).data()); \ + return true; \ + } +#define the8(offset, len) \ + as##len("ORA", offset + 0x00); \ + as##len("AND", offset + 0x20); \ + as##len("EOR", offset + 0x40); \ + as##len("ADC", offset + 0x60); \ + as##len("STA", offset + 0x80); \ + as##len("LDA", offset + 0xA0); \ + as##len("CMP", offset + 0xC0); \ + as##len("SBC", offset + 0xE0) +#define thenext8(offset, len) \ + as##len("ASL", offset + 0x00); \ + as##len("BIT", offset + 0x1E); \ + as##len("ROL", offset + 0x20); \ + as##len("LSR", offset + 0x40); \ + as##len("ROR", offset + 0x60); \ + as##len("LDY", offset + 0x9E); \ + as##len("DEC", offset + 0xC0); \ + as##len("INC", offset + 0xE0) +#define thefinal7(offset, len) \ + as##len("TSB", offset + 0x00); \ + as##len("TRB", offset + 0x10); \ + as##len("STY", offset + 0x80); \ + as##len("STX", offset + 0x82); \ + as##len("LDX", offset + 0xA2); \ + as##len("CPY", offset + 0xC0); \ + as##len("CPX", offset + 0xE0) +#define onlythe8(left, right, offset) \ + else if (match(left, right)) do { \ + init_index(left, right); \ + the8(offset, 1); \ + end(); \ + } \ + while (0) + else if ((strlen(word[0]) != 3 && (strlen(word[0]) != 5 || word[0][3] != '.')) || + (word[1] && word[2])) + return false; + else if (!word[1]) { + blankinit(); + as0("PHP", 0x08); + as0("ASL", 0x0A); + as0("PHD", 0x0B); + as0("CLC", 0x18); + as0("INC", 0x1A); + as0("TCS", 0x1B); + as0("PLP", 0x28); + as0("ROL", 0x2A); + as0("PLD", 0x2B); + as0("SEC", 0x38); + as0("DEC", 0x3A); + as0("TSC", 0x3B); + as0("RTI", 0x40); + as0("PHA", 0x48); + as0("LSR", 0x4A); + as0("PHK", 0x4B); + as0("CLI", 0x58); + as0("PHY", 0x5A); + as0("TCD", 0x5B); + as0("RTS", 0x60); + as0("PLA", 0x68); + as0("ROR", 0x6A); + as0("RTL", 0x6B); + as0("SEI", 0x78); + as0("PLY", 0x7A); + as0("TDC", 0x7B); + as0("DEY", 0x88); + as0("TXA", 0x8A); // these tables are blatantly stolen from xkas + as0("PHB", 0x8B); + as0("TYA", 0x98); + as0("TXS", 0x9A); + as0("TXY", 0x9B); + as0("TAY", 0xA8); + as0("TAX", 0xAA); + as0("PLB", 0xAB); + as0("CLV", 0xB8); + as0("TSX", 0xBA); + as0("TYX", 0xBB); + as0("INY", 0xC8); + as0("DEX", 0xCA); + as0("WAI", 0xCB); + as0("CLD", 0xD8); + as0("PHX", 0xDA); + as0("STP", 0xDB); + as0("INX", 0xE8); + as0("NOP", 0xEA); + as0("XBA", 0xEB); + as0("SED", 0xF8); + as0("PLX", 0xFA); + as0("XCE", 0xFB); + as1("BRK", 0x00); + as1("COP", 0x02); + as1("WDM", 0x42); + // as0("DEA", 0x3A); as0("INA", 0x1A); as0("TAD", 0x5B); as0("TDA", + // 0x7B);//nobody cares about these, but keeping them does no harm as0("TAS", + // 0x1B); as0("TSA", 0x3B); as0("SWA", 0xEB); //actually, it + // does: it may make some users think it's correct. + end(); + } else if (!stricmp(word[1], "A")) { + blankinit(); + as0("ASL", 0x0A); + as0("LSR", 0x4A); + as0("ROL", 0x2A); + as0("ROR", 0x6A); + as0("INC", 0x1A); + as0("DEC", 0x3A); + goto opAFallback; // yay goto + } else if (matchl("#")) { + bankoptinit('#'); + as_a("ORA", 0x09); + as_a("AND", 0x29); + as_a("EOR", 0x49); + as_a("ADC", 0x69); + as_a("BIT", 0x89); + as_a("LDA", 0xA9); + as_a("CMP", 0xC9); + as_a("SBC", 0xE9); + as_xy("CPX", 0xE0); + as_xy("CPY", 0xC0); + as_xy("LDX", 0xA2); + as_xy("LDY", 0xA0); + as_rep("ASL", 0x0A); + as_rep("LSR", 0x4A); + as_rep("ROL", 0x2A); + as_rep("ROR", 0x6A); + as_rep("INC", 0x1A); + as_rep("DEC", 0x3A); + as_rep("INX", 0xE8); + as_rep("DEX", 0xCA); + as_rep("INY", 0xC8); + as_rep("DEY", 0x88); + as_rep("NOP", 0xEA); + as1("REP", 0xC2); + as1("SEP", 0xE2); + as1("BRK", 0x00); + as1("COP", 0x02); + as1("WDM", 0x42); + end(); + } + onlythe8("(", ",s),y", 0x13); + onlythe8("[", "],y", 0x17); + onlythe8("(", "),y", 0x11); + onlythe8("", ",s", 0x03); + else if (match("[", "]")) { + init('[', ']'); + the8(0x07, 1); + as2("JMP", 0xDC); + as2("JML", 0xDC); + end(); + } + else if (match("(", ",x)")) { + init_index("(", ",x)"); + the8(0x01, 1); + as2("JMP", 0x7C); + as2("JSR", 0xFC); + end(); + } + else if (match("(", ")")) { + init('(', ')'); + the8(0x12, 1); + as1("PEI", 0xD4); + as2("JMP", 0x6C); + end(); + } + else if (matchr(",x")) { + init_index("", ",x"); + if (match("(", ")")) + asar_throw_warning(0, warning_id_65816_yy_x_does_not_exist); + the8(0x1F, 3); + the8(0x1D, 2); + the8(0x15, 1); + thenext8(0x16, 1); + thenext8(0x1E, 2); + as1("STZ", 0x74); + as1("STY", 0x94); + as2("STZ", 0x9E); + end(); + } + else if (matchr(",y")) { + init_index("", ",y"); + as1("LDX", 0xB6); + as1("STX", 0x96); + as2("LDX", 0xBE); + if (len == 1 && (is("ORA") || is("AND") || is("EOR") || is("ADC") || + is("STA") || is("LDA") || is("CMP") || is("SBC"))) { + asar_throw_warning(0, warning_id_65816_xx_y_assume_16_bit, word[0]); + len = 2; + } + the8(0x19, 2); + end(); + } + else { + if ((is("MVN") || is("MVP"))) { + int numargs; + autoptr param = qpsplit(par.temp_raw(), ',', &numargs); + if (numargs == 2) { + write1(is("MVN") ? (unsigned int)0x54 : (unsigned int)0x44); + write1(getnum(param[0])); + write1(getnum(param[1])); + return true; + } + } + if (false) { + opAFallback: + snes_label tmp; + if (pass && !labelval(par, &tmp)) return false; + len = getlen(par); + num = tmp.pos; + } + if (is("JSR") || is("JMP")) { + int tmp = optimizeforbank; + optimizeforbank = -1; + getvars(false) optimizeforbank = tmp; + } else { + getvars(false) + } + the8(0x0F, 3); + the8(0x0D, 2); + the8(0x05, 1); + thenext8(0x06, 1); + thenext8(0x0E, 2); + thefinal7(0x04, 1); + thefinal7(0x0C, 2); + as1("STZ", 0x64); + as2("STZ", 0x9C); + as2("JMP", 0x4C); + as2("JSR", 0x20); + as32("JML", 0x5C); + as32("JSL", 0x22); + as2("MVN", 0x54); + as2("MVP", 0x44); + as2("PEA", 0xF4); + as_rel1("BRA", 0x80); + as_rel1("BCC", 0x90); + as_rel1("BCS", 0xB0); + as_rel1("BEQ", 0xF0); + as_rel1("BNE", 0xD0); + as_rel1("BMI", 0x30); + as_rel1("BPL", 0x10); + as_rel1("BVC", 0x50); + as_rel1("BVS", 0x70); + as_rel2("BRL", 0x82); + as_rel2("PER", 0x62); + end(); + } + return true; } diff --git a/src/asar/arch-spc700.cpp b/src/asar/arch-spc700.cpp index 60838744..afdac137 100644 --- a/src/asar/arch-spc700.cpp +++ b/src/asar/arch-spc700.cpp @@ -1,240 +1,284 @@ #include "asar.h" -#include "assembleblock.h" #include "asar_math.h" +#include "assembleblock.h" #define write1 write1_pick -static int writesizeto=-1; -static int inlinestartpos=0; +static int writesizeto = -1; +static int inlinestartpos = 0; -void asinit_spc700() -{ -} +void asinit_spc700() {} -void asend_spc700() -{ -} +void asend_spc700() {} -static bool matchandwrite(const char * str, const char * left, const char * right, string& remainder) -{ - for (int i=0;left[i];i++) - { - if (to_lower(*str)!=left[i]) return false; - str++; - } - int mainlen=(int)(strlen(str)-strlen(right)); - if(mainlen < 0) return false; - for (int i=0;right[i];i++) - { - if (to_lower(str[mainlen+i])!=right[i]) return false; - } - remainder=substr(str, mainlen); - return true; +static bool matchandwrite(const char* str, const char* left, const char* right, + string& remainder) { + for (int i = 0; left[i]; i++) { + if (to_lower(*str) != left[i]) return false; + str++; + } + int mainlen = (int)(strlen(str) - strlen(right)); + if (mainlen < 0) return false; + for (int i = 0; right[i]; i++) { + if (to_lower(str[mainlen + i]) != right[i]) return false; + } + remainder = substr(str, mainlen); + return true; } -static bool bitmatch(const char * opnamein, string& opnameout, const char * str, string& math, int& bit) -{ - const char * opnameend=strchr(opnamein, '\0'); - const char * dot=strrchr(str, '.'); - if (dot && is_digit(dot[1]) && !dot[2]) - { - bit = dot[1]-'0'; - if (bit>=8) return false; - math=substr(str, (int)(dot-str)); - if (opnameend[-1]=='1') opnameout=substr(opnamein, (int)(opnameend-opnamein-1)); - else opnameout=opnamein; - return true; - } - if (opnameend[-1]>='0' && opnameend[-1]<='7') - { - math=str; - bit=opnameend[-1]-'0'; - opnameout=substr(opnamein, (int)(opnameend-opnamein-1)); - return true; - } - return false; +static bool bitmatch(const char* opnamein, string& opnameout, const char* str, + string& math, int& bit) { + const char* opnameend = strchr(opnamein, '\0'); + const char* dot = strrchr(str, '.'); + if (dot && is_digit(dot[1]) && !dot[2]) { + bit = dot[1] - '0'; + if (bit >= 8) return false; + math = substr(str, (int)(dot - str)); + if (opnameend[-1] == '1') + opnameout = substr(opnamein, (int)(opnameend - opnamein - 1)); + else + opnameout = opnamein; + return true; + } + if (opnameend[-1] >= '0' && opnameend[-1] <= '7') { + math = str; + bit = opnameend[-1] - '0'; + opnameout = substr(opnamein, (int)(opnameend - opnamein - 1)); + return true; + } + return false; } #define isop(test) (!stricmp(op, test)) -static bool assinglebitwithc(const char * op, const char * math, int bits) -{ - unsigned int num; - if (math[0]=='!') - { - if(0); - else if (isop("or")) write1(0x2A); - else if (isop("and")) write1(0x6A); - else return false; - num=getnum(math+1); - } - else - { - if(0); - else if (isop("or")) write1(0x0A); - else if (isop("and")) write1(0x4A); - else if (isop("eor")) write1(0x8A); - else if (isop("mov")) write1(0xAA); - else if (isop("not")) write1(0xEA); - else return false; - num=getnum(math); - } - if (num>=0x2000) asar_throw_error(2, error_type_block, error_id_spc700_addr_out_of_range, hex(num, 4).data()); - write2(((unsigned int)bits<<13)|num); - return true; +static bool assinglebitwithc(const char* op, const char* math, int bits) { + unsigned int num; + if (math[0] == '!') { + if (0) + ; + else if (isop("or")) + write1(0x2A); + else if (isop("and")) + write1(0x6A); + else + return false; + num = getnum(math + 1); + } else { + if (0) + ; + else if (isop("or")) + write1(0x0A); + else if (isop("and")) + write1(0x4A); + else if (isop("eor")) + write1(0x8A); + else if (isop("mov")) + write1(0xAA); + else if (isop("not")) + write1(0xEA); + else + return false; + num = getnum(math); + } + if (num >= 0x2000) + asar_throw_error(2, error_type_block, error_id_spc700_addr_out_of_range, + hex(num, 4).data()); + write2(((unsigned int)bits << 13) | num); + return true; } #undef isop -bool asblock_spc700(char** word, int numwords) -{ +bool asblock_spc700(char** word, int numwords) { #define is(test) (!stricmp(word[0], test)) -#define is1(test) (!stricmp(word[0], test) && numwords==2) +#define is1(test) (!stricmp(word[0], test) && numwords == 2) #define par word[1] - if (numwords==1) - { -#define op(name, val) else if (is(name)) do { write1(val); } while(0) - if(0); - op("nop", 0x00); - op("brk", 0x0F); - op("clrp", 0x20); - op("setp", 0x40); - op("clrc", 0x60); - op("ret", 0x6F); - op("reti", 0x7F); - op("setc", 0x80); - op("ei", 0xA0); - op("di", 0xC0); - op("clrv", 0xE0); - op("notc", 0xED); - op("sleep", 0xEF); - op("stop", 0xFF); - op("xcn", 0x9F); - else return false; + if (numwords == 1) { +#define op(name, val) \ + else if (is(name)) do { \ + write1(val); \ + } \ + while (0) + if (0) + ; + op("nop", 0x00); + op("brk", 0x0F); + op("clrp", 0x20); + op("setp", 0x40); + op("clrc", 0x60); + op("ret", 0x6F); + op("reti", 0x7F); + op("setc", 0x80); + op("ei", 0xA0); + op("di", 0xC0); + op("clrv", 0xE0); + op("notc", 0xED); + op("sleep", 0xEF); + op("stop", 0xFF); + op("xcn", 0x9F); + else return false; #undef op - } - else if (numwords==2) - { - int numwordsinner; - //Detect opcode length before continuing - int opLen=0; //In case of .b or .w, this overwrites auto-detection of opcode length - unsigned int periodLocCount=0; - do { - if (word[0][periodLocCount] == '.') { - opLen=getlenfromchar(word[0][periodLocCount+1]); - word[0][periodLocCount]='\0'; - } - periodLocCount++; - } while ((opLen == 0) && (periodLocCount < strlen(word[0]))); - if (opLen > 2) { asar_throw_error(0, error_type_block, error_id_opcode_length_too_long); } - autoptr parcpy= duplicate_string(par); - autoptr arg=qpsplit(parcpy, ',', &numwordsinner); - if (numwordsinner ==1) - { - string op; - string math; - int bits; + } else if (numwords == 2) { + int numwordsinner; + // Detect opcode length before continuing + int opLen = 0; // In case of .b or .w, this overwrites auto-detection of opcode + // length + unsigned int periodLocCount = 0; + do { + if (word[0][periodLocCount] == '.') { + opLen = getlenfromchar(word[0][periodLocCount + 1]); + word[0][periodLocCount] = '\0'; + } + periodLocCount++; + } while ((opLen == 0) && (periodLocCount < strlen(word[0]))); + if (opLen > 2) { + asar_throw_error(0, error_type_block, error_id_opcode_length_too_long); + } + autoptr parcpy = duplicate_string(par); + autoptr arg = qpsplit(parcpy, ',', &numwordsinner); + if (numwordsinner == 1) { + string op; + string math; + int bits; #define isop(str) (!stricmp(word[0], str)) #define isam(str) (!stricmp(arg[0], str)) #define ismatch(left, right) (matchandwrite(arg[0], left, right, math)) #define eq(str) if (isam(str)) -#define w0(hex) do { write1((unsigned int)hex); return true; } while(0) -#define w1(hex) do { write1((unsigned int)hex); write1(getnum(math)); return true; } while(0) -#define w2(hex) do { write1((unsigned int)hex); write2(getnum(math)); return true; } while(0) -#define wv(hex1, hex2) do { if ((opLen == 1) || (opLen == 0 && getlen(math) == 1)) { write1((unsigned int)hex1); write1(getnum(math)); } else { write1((unsigned int)hex2); write2(getnum(math)); } return true; } while(0) -#define wr(hex) do { int len=getlen(math); int num=(int)getnum(math); int pos=(len==1)?num:num-((snespos&0xFFFFFF)+2); write1((unsigned int)hex); write1((unsigned int)pos); \ - if (pass==2 && foundlabel && (pos<-128 || pos>127)) asar_throw_error(2, error_type_block, error_id_relative_branch_out_of_bounds, dec(pos).data()); \ - return true; } while(0) -#define op0(str, hex) if (isop(str)) w0(hex) -#define op1(str, hex) if (isop(str)) w1(hex) -#define op2(str, hex) if (isop(str)) w2(hex) -#define opv(str, hex1, hex2) if (isop(str)) wv(hex1, hex2) -#define opr(str, hex) if (isop(str)) wr(hex) +#define w0(hex) \ + do { \ + write1((unsigned int)hex); \ + return true; \ + } while (0) +#define w1(hex) \ + do { \ + write1((unsigned int)hex); \ + write1(getnum(math)); \ + return true; \ + } while (0) +#define w2(hex) \ + do { \ + write1((unsigned int)hex); \ + write2(getnum(math)); \ + return true; \ + } while (0) +#define wv(hex1, hex2) \ + do { \ + if ((opLen == 1) || (opLen == 0 && getlen(math) == 1)) { \ + write1((unsigned int)hex1); \ + write1(getnum(math)); \ + } else { \ + write1((unsigned int)hex2); \ + write2(getnum(math)); \ + } \ + return true; \ + } while (0) +#define wr(hex) \ + do { \ + int len = getlen(math); \ + int num = (int)getnum(math); \ + int pos = (len == 1) ? num : num - ((snespos & 0xFFFFFF) + 2); \ + write1((unsigned int)hex); \ + write1((unsigned int)pos); \ + if (pass == 2 && foundlabel && (pos < -128 || pos > 127)) \ + asar_throw_error(2, error_type_block, \ + error_id_relative_branch_out_of_bounds, dec(pos).data()); \ + return true; \ + } while (0) +#define op0(str, hex) \ + if (isop(str)) w0(hex) +#define op1(str, hex) \ + if (isop(str)) w1(hex) +#define op2(str, hex) \ + if (isop(str)) w2(hex) +#define opv(str, hex1, hex2) \ + if (isop(str)) wv(hex1, hex2) +#define opr(str, hex) \ + if (isop(str)) wr(hex) #define match(left, right) if (ismatch(left, right)) - eq("a") - { - op0("asl", 0x1C); - op0("das", 0xBE); - op0("daa", 0xDF); - op0("dec", 0x9C); - op0("inc", 0xBC); - op0("lsr", 0x5C); - op0("pop", 0xAE); - op0("push", 0x2D); - op0("rol", 0x3C); - op0("ror", 0x7C); - op0("xcn", 0x9F); - } - eq("x") - { - op0("dec", 0x1D); - op0("inc", 0x3D); - op0("pop", 0xCE); - op0("push", 0x4D); - } - eq("y") - { - op0("dec", 0xDC); - op0("inc", 0xFC); - op0("pop", 0xEE); - op0("push", 0x6D); - } - eq("p") - { - op0("pop", 0x8E); - op0("push", 0x0D); - } - if (isop("mul") && isam("ya")) w0(0xCF); - if (isop("jmp") && ismatch("(", "+x)")) w2(0x1F); - match("", "+x") - { - op1("asl", 0x1B); - op1("dec", 0x9B); - op1("inc", 0xBB); - op1("lsr", 0x5B); - op1("rol", 0x3B); - op1("ror", 0x7B); - } - if (bitmatch(word[0], op, arg[0], math, bits)) - { - if (assinglebitwithc(op, math, bits)) return true; - else if (!stricmp(op, "set")) write1((unsigned int)(0x02|(bits<<5))); - else if (!stricmp(op, "clr")) write1((unsigned int)(0x12|(bits<<5))); - else return false; - unsigned int num=getnum(math); - if (num>=0x100) asar_throw_error(2, error_type_block, error_id_snes_address_out_of_bounds, hex(num, 6).data()); - write1(num); - return true; - } - if (true) - { - math=arg[0]; - if (isop("tcall")) - { - unsigned int num = getnum(math); - if (num >= 16) asar_throw_error(2, error_type_block, error_id_invalid_tcall); - write1(((num<<4)|1)); - return true; - } - opv("asl", 0x0B, 0x0C); - opv("dec", 0x8B, 0x8C); - opv("inc", 0xAB, 0xAC); - opv("lsr", 0x4B, 0x4C); - opv("rol", 0x2B, 0x2C); - opv("ror", 0x6B, 0x6C); - op2("jmp", 0x5F); - op2("call", 0x3F); - op1("decw", 0x1A); - op1("incw", 0x3A); - op1("pcall", 0x4F); - opr("bpl", 0x10); - opr("bra", 0x2F); - opr("bmi", 0x30); - opr("bvc", 0x50); - opr("bvs", 0x70); - opr("bcc", 0x90); - opr("bcs", 0xB0); - opr("bne", 0xD0); - opr("beq", 0xF0); - } + eq("a") { + op0("asl", 0x1C); + op0("das", 0xBE); + op0("daa", 0xDF); + op0("dec", 0x9C); + op0("inc", 0xBC); + op0("lsr", 0x5C); + op0("pop", 0xAE); + op0("push", 0x2D); + op0("rol", 0x3C); + op0("ror", 0x7C); + op0("xcn", 0x9F); + } + eq("x") { + op0("dec", 0x1D); + op0("inc", 0x3D); + op0("pop", 0xCE); + op0("push", 0x4D); + } + eq("y") { + op0("dec", 0xDC); + op0("inc", 0xFC); + op0("pop", 0xEE); + op0("push", 0x6D); + } + eq("p") { + op0("pop", 0x8E); + op0("push", 0x0D); + } + if (isop("mul") && isam("ya")) w0(0xCF); + if (isop("jmp") && ismatch("(", "+x)")) w2(0x1F); + match("", "+x") { + op1("asl", 0x1B); + op1("dec", 0x9B); + op1("inc", 0xBB); + op1("lsr", 0x5B); + op1("rol", 0x3B); + op1("ror", 0x7B); + } + if (bitmatch(word[0], op, arg[0], math, bits)) { + if (assinglebitwithc(op, math, bits)) + return true; + else if (!stricmp(op, "set")) + write1((unsigned int)(0x02 | (bits << 5))); + else if (!stricmp(op, "clr")) + write1((unsigned int)(0x12 | (bits << 5))); + else + return false; + unsigned int num = getnum(math); + if (num >= 0x100) + asar_throw_error(2, error_type_block, + error_id_snes_address_out_of_bounds, + hex(num, 6).data()); + write1(num); + return true; + } + if (true) { + math = arg[0]; + if (isop("tcall")) { + unsigned int num = getnum(math); + if (num >= 16) + asar_throw_error(2, error_type_block, error_id_invalid_tcall); + write1(((num << 4) | 1)); + return true; + } + opv("asl", 0x0B, 0x0C); + opv("dec", 0x8B, 0x8C); + opv("inc", 0xAB, 0xAC); + opv("lsr", 0x4B, 0x4C); + opv("rol", 0x2B, 0x2C); + opv("ror", 0x6B, 0x6C); + op2("jmp", 0x5F); + op2("call", 0x3F); + op1("decw", 0x1A); + op1("incw", 0x3A); + op1("pcall", 0x4F); + opr("bpl", 0x10); + opr("bra", 0x2F); + opr("bmi", 0x30); + opr("bvc", 0x50); + opr("bvs", 0x70); + opr("bcc", 0x90); + opr("bcs", 0xB0); + opr("bne", 0xD0); + opr("beq", 0xF0); + } #undef isop #undef isam #undef eq @@ -249,154 +293,213 @@ bool asblock_spc700(char** word, int numwords) #undef opv #undef opr #undef match - return false; - } - if (numwordsinner==2) - { + return false; + } + if (numwordsinner == 2) { #define iscc(str1, str2) (!stricmp(arg[0], str1) && !stricmp(arg[1], str2)) -#define iscv(str1, left2, right2) (!stricmp(arg[0], str1) && matchandwrite(arg[1], left2, right2, s2)) -#define isvc(left1, right1, str2) (matchandwrite(arg[0], left1, right1, s1) && !stricmp(arg[1], str2)) -#define isvv(left1, right1, left2, right2) (matchandwrite(arg[0], left1, right1, s1) && matchandwrite(arg[1], left2, right2, s2)) +#define iscv(str1, left2, right2) \ + (!stricmp(arg[0], str1) && matchandwrite(arg[1], left2, right2, s2)) +#define isvc(left1, right1, str2) \ + (matchandwrite(arg[0], left1, right1, s1) && !stricmp(arg[1], str2)) +#define isvv(left1, right1, left2, right2) \ + (matchandwrite(arg[0], left1, right1, s1) && \ + matchandwrite(arg[1], left2, right2, s2)) #define cc(str1, str2) if (iscc(str1, str2)) #define cv(str1, left2, right2) if (iscv(str1, left2, right2)) #define vc(left1, right1, str2) if (isvc(left1, right1, str2)) #define vv(left1, right1, left2, right2) if (isvv(left1, right1, left2, right2)) -#define w0(opcode) do { write1((unsigned int)opcode); return true; } while(0) -#define w1(opcode, math) do { write1((unsigned int)opcode); unsigned int val=getnum(math); \ - if ((((val&0xFF00)&&(val&0x80000000)==0)||(((val&0xFF00)!=0xFF00)&&(val&0x80000000)))&&opLen!=1) asar_throw_warning(0, warning_id_spc700_assuming_8_bit); write1(val);return true; } while(0) -#define w2(opcode, math) do { write1((unsigned int)opcode); write2(getnum(math)); return true; } while(0) -#define wv(opcode1, opcode2, math) do { if ((opLen == 1) || (opLen == 0 && getlen(math)==1)) { write1((unsigned int)opcode1); write1(getnum(math)); } \ - else { write1((unsigned int)opcode2); write2(getnum(math)); } return true; } while(0) -#define w11(opcode, math1, math2) do { write1((unsigned int)opcode); write1(getnum(math1)); write1(getnum(math2)); return true; } while(0) -#define wr(opcode, math) do { int len=getlen(math); int num=(int)getnum(math); int pos=(len==1)?num:num-(snespos+2); \ - if (pass && foundlabel && (pos<-128 || pos>127)) asar_throw_error(2, error_type_block, error_id_relative_branch_out_of_bounds, dec(pos).data()); \ - write1((unsigned int)opcode); write1((unsigned int)pos); return true; } while(0) -#define w1r(opcode, math1, math2) do { int len=getlen(math2); int num=(int)getnum(math2); int pos=(len==1)?num:num-(snespos+3); \ - if (pass && foundlabel && (pos<-128 || pos>127)) asar_throw_error(2, error_type_block, error_id_relative_branch_out_of_bounds, dec(pos).data()); \ - write1((unsigned int)opcode); write1(getnum(math1)); write1((unsigned int)pos); return true; } while(0) - string s1; - string s2; - string op; - string math; - int bits; +#define w0(opcode) \ + do { \ + write1((unsigned int)opcode); \ + return true; \ + } while (0) +#define w1(opcode, math) \ + do { \ + write1((unsigned int)opcode); \ + unsigned int val = getnum(math); \ + if ((((val & 0xFF00) && (val & 0x80000000) == 0) || \ + (((val & 0xFF00) != 0xFF00) && (val & 0x80000000))) && \ + opLen != 1) \ + asar_throw_warning(0, warning_id_spc700_assuming_8_bit); \ + write1(val); \ + return true; \ + } while (0) +#define w2(opcode, math) \ + do { \ + write1((unsigned int)opcode); \ + write2(getnum(math)); \ + return true; \ + } while (0) +#define wv(opcode1, opcode2, math) \ + do { \ + if ((opLen == 1) || (opLen == 0 && getlen(math) == 1)) { \ + write1((unsigned int)opcode1); \ + write1(getnum(math)); \ + } else { \ + write1((unsigned int)opcode2); \ + write2(getnum(math)); \ + } \ + return true; \ + } while (0) +#define w11(opcode, math1, math2) \ + do { \ + write1((unsigned int)opcode); \ + write1(getnum(math1)); \ + write1(getnum(math2)); \ + return true; \ + } while (0) +#define wr(opcode, math) \ + do { \ + int len = getlen(math); \ + int num = (int)getnum(math); \ + int pos = (len == 1) ? num : num - (snespos + 2); \ + if (pass && foundlabel && (pos < -128 || pos > 127)) \ + asar_throw_error(2, error_type_block, \ + error_id_relative_branch_out_of_bounds, dec(pos).data()); \ + write1((unsigned int)opcode); \ + write1((unsigned int)pos); \ + return true; \ + } while (0) +#define w1r(opcode, math1, math2) \ + do { \ + int len = getlen(math2); \ + int num = (int)getnum(math2); \ + int pos = (len == 1) ? num : num - (snespos + 3); \ + if (pass && foundlabel && (pos < -128 || pos > 127)) \ + asar_throw_error(2, error_type_block, \ + error_id_relative_branch_out_of_bounds, dec(pos).data()); \ + write1((unsigned int)opcode); \ + write1(getnum(math1)); \ + write1((unsigned int)pos); \ + return true; \ + } while (0) + string s1; + string s2; + string op; + string math; + int bits; #define isop(test) (!stricmp(op, test)) - if (!stricmp(arg[0], "c") && bitmatch(word[0], op, arg[1], math, bits)) - { - if (assinglebitwithc(op, math, bits)) return true; - } - if (bitmatch(word[0], op, arg[0], s1, bits)) - { - if (isop("mov") && !stricmp(arg[1], "c")) - { - unsigned int num=getnum(s1); - if (num>=0x2000) asar_throw_error(2, error_type_block, error_id_snes_address_out_of_bounds, hex((unsigned int)num, 6).data()); - write1(0xCA); - write2(((unsigned int)bits<<13)|num); - return true; - } - if(0); - else if (isop("bbs")) write1((unsigned int)(0x03|(bits<<5))); - else if (isop("bbc")) write1((unsigned int)(0x13|(bits<<5))); - else return false; - unsigned int num=getnum(s1); - if (num>=0x100) asar_throw_error(2, error_type_block, error_id_snes_address_out_of_bounds, hex(num, 6).data()); - write1(num); - write1((getnum(arg[1])- (unsigned int)(snespos+1))); - return true; - } + if (!stricmp(arg[0], "c") && bitmatch(word[0], op, arg[1], math, bits)) { + if (assinglebitwithc(op, math, bits)) return true; + } + if (bitmatch(word[0], op, arg[0], s1, bits)) { + if (isop("mov") && !stricmp(arg[1], "c")) { + unsigned int num = getnum(s1); + if (num >= 0x2000) + asar_throw_error(2, error_type_block, + error_id_snes_address_out_of_bounds, + hex((unsigned int)num, 6).data()); + write1(0xCA); + write2(((unsigned int)bits << 13) | num); + return true; + } + if (0) + ; + else if (isop("bbs")) + write1((unsigned int)(0x03 | (bits << 5))); + else if (isop("bbc")) + write1((unsigned int)(0x13 | (bits << 5))); + else + return false; + unsigned int num = getnum(s1); + if (num >= 0x100) + asar_throw_error(2, error_type_block, + error_id_snes_address_out_of_bounds, + hex(num, 6).data()); + write1(num); + write1((getnum(arg[1]) - (unsigned int)(snespos + 1))); + return true; + } #undef isop - if (is("mov")) - { - if (iscc("(x)+", "a")) asar_throw_error(0, error_type_block, error_id_use_xplus); - cc("(x+)" , "a" ) w0(0xAF); - cc("(x)" , "a" ) w0(0xC6); - if (iscc("a", "(x)+")) asar_throw_error(0, error_type_block, error_id_use_xplus); - cc("a" , "(x+)" ) w0(0xBF); - cc("a" , "(x)" ) w0(0xE6); - cc("a" , "x" ) w0(0x7D); - cc("a" , "y" ) w0(0xDD); - cc("x" , "a" ) w0(0x5D); - cc("x" , "sp" ) w0(0x9D); - cc("y" , "a" ) w0(0xFD); - cc("sp" , "x" ) w0(0xBD); + if (is("mov")) { + if (iscc("(x)+", "a")) + asar_throw_error(0, error_type_block, error_id_use_xplus); + cc("(x+)", "a") w0(0xAF); + cc("(x)", "a") w0(0xC6); + if (iscc("a", "(x)+")) + asar_throw_error(0, error_type_block, error_id_use_xplus); + cc("a", "(x+)") w0(0xBF); + cc("a", "(x)") w0(0xE6); + cc("a", "x") w0(0x7D); + cc("a", "y") w0(0xDD); + cc("x", "a") w0(0x5D); + cc("x", "sp") w0(0x9D); + cc("y", "a") w0(0xFD); + cc("sp", "x") w0(0xBD); - vc("(","+x)", "a" ) w1(0xC7, s1); - vc("(",")+y", "a" ) w1(0xD7, s1); - vc("","+x" , "a" ) wv(0xD4, 0xD5, s1); - vc("","+y" , "a" ) w2(0xD6, s1); - vc("","" , "a" ) wv(0xC4, 0xC5, s1); - vc("","+x" , "y" ) w1(0xDB, s1); - vc("","+y" , "x" ) w1(0xD9, s1); - vc("","" , "x" ) wv(0xD8, 0xC9, s1); - vc("","" , "y" ) wv(0xCB, 0xCC, s1); + vc("(", "+x)", "a") w1(0xC7, s1); + vc("(", ")+y", "a") w1(0xD7, s1); + vc("", "+x", "a") wv(0xD4, 0xD5, s1); + vc("", "+y", "a") w2(0xD6, s1); + vc("", "", "a") wv(0xC4, 0xC5, s1); + vc("", "+x", "y") w1(0xDB, s1); + vc("", "+y", "x") w1(0xD9, s1); + vc("", "", "x") wv(0xD8, 0xC9, s1); + vc("", "", "y") wv(0xCB, 0xCC, s1); - cv("a" , "#","" ) w1(0xE8, s2); - cv("a" , "(","+x)") w1(0xE7, s2); - cv("a" , "(",")+y") w1(0xF7, s2); - cv("a" , "","+x" ) wv(0xF4, 0xF5, s2); - cv("a" , "","+y" ) w2(0xF6, s2); - cv("a" , "","" ) wv(0xE4, 0xE5, s2); - cv("x" , "#","" ) w1(0xCD, s2); - cv("x" , "","+y" ) w1(0xF9, s2); - cv("x" , "","" ) wv(0xF8, 0xE9, s2); - cv("y" , "#","" ) w1(0x8D, s2); - cv("y" , "","+x" ) w1(0xFB, s2); - cv("y" , "","" ) wv(0xEB, 0xEC, s2); + cv("a", "#", "") w1(0xE8, s2); + cv("a", "(", "+x)") w1(0xE7, s2); + cv("a", "(", ")+y") w1(0xF7, s2); + cv("a", "", "+x") wv(0xF4, 0xF5, s2); + cv("a", "", "+y") w2(0xF6, s2); + cv("a", "", "") wv(0xE4, 0xE5, s2); + cv("x", "#", "") w1(0xCD, s2); + cv("x", "", "+y") w1(0xF9, s2); + cv("x", "", "") wv(0xF8, 0xE9, s2); + cv("y", "#", "") w1(0x8D, s2); + cv("y", "", "+x") w1(0xFB, s2); + cv("y", "", "") wv(0xEB, 0xEC, s2); - vv("","" , "#","" ) w11(0x8F, s2, s1); - vv("","" , "","" ) w11(0xFA, s2, s1); - } - if (is("cmp")) - { - cv("x", "#","") w1(0xC8, s2); - cv("x", "","" ) wv(0x3E, 0x1E, s2); - cv("y", "#","") w1(0xAD, s2); - cv("y", "","" ) wv(0x7E, 0x5E, s2); - } - if (is("or") || is("and") || is("eor") || is("cmp") || is("adc") || is("sbc")) - { - int offset = 0; - if (is("or" )) offset=0x00; - if (is("and")) offset=0x20; - if (is("eor")) offset=0x40; - if (is("cmp")) offset=0x60; - if (is("adc")) offset=0x80; - if (is("sbc")) offset=0xA0; + vv("", "", "#", "") w11(0x8F, s2, s1); + vv("", "", "", "") w11(0xFA, s2, s1); + } + if (is("cmp")) { + cv("x", "#", "") w1(0xC8, s2); + cv("x", "", "") wv(0x3E, 0x1E, s2); + cv("y", "#", "") w1(0xAD, s2); + cv("y", "", "") wv(0x7E, 0x5E, s2); + } + if (is("or") || is("and") || is("eor") || is("cmp") || is("adc") || + is("sbc")) { + int offset = 0; + if (is("or")) offset = 0x00; + if (is("and")) offset = 0x20; + if (is("eor")) offset = 0x40; + if (is("cmp")) offset = 0x60; + if (is("adc")) offset = 0x80; + if (is("sbc")) offset = 0xA0; - cc("a" , "(x)" ) w0(offset+0x06); - cc("(x)", "(y)" ) w0(offset+0x19); + cc("a", "(x)") w0(offset + 0x06); + cc("(x)", "(y)") w0(offset + 0x19); - cv("a" , "#","" ) w1(offset+0x08, s2); - cv("a" , "(","+x)") w1(offset+0x07, s2); - cv("a" , "(",")+y") w1(offset+0x17, s2); - cv("a" , "","+x" ) wv(offset+0x14, offset+0x15, s2); - cv("a" , "","+y" ) w2(offset+0x16, s2); - cv("a" , "","" ) wv(offset+0x04, offset+0x05, s2); + cv("a", "#", "") w1(offset + 0x08, s2); + cv("a", "(", "+x)") w1(offset + 0x07, s2); + cv("a", "(", ")+y") w1(offset + 0x17, s2); + cv("a", "", "+x") wv(offset + 0x14, offset + 0x15, s2); + cv("a", "", "+y") w2(offset + 0x16, s2); + cv("a", "", "") wv(offset + 0x04, offset + 0x05, s2); - vv("","", "#","" ) w11(offset+0x18, s2, s1); - vv("","", "","" ) w11(offset+0x09, s2, s1); - } - vc("","", "a") - { - if (is("tset")) w2(0x0E, s1); - if (is("tclr")) w2(0x4E, s1); - } - if (is("div") && iscc("ya", "x")) w0(0x9E); - cv("ya", "","") - { - if (is("cmpw")) w1(0x5A, s2); - if (is("addw")) w1(0x7A, s2); - if (is("subw")) w1(0x9A, s2); - if (is("movw")) w1(0xBA, s2); - } - if (is("movw") && isvc("","", "ya")) w1(0xDA, s1); - if (is("cbne") && isvv("","+x", "","")) w1r(0xDE, s1, s2); - if (is("dbnz") && iscv("y", "","")) wr(0xFE, s2); - vv("","", "","") - { - if (is("dbnz")) w1r(0x6E, s1, s2); - if (is("cbne")) w1r(0x2E, s1, s2); - } + vv("", "", "#", "") w11(offset + 0x18, s2, s1); + vv("", "", "", "") w11(offset + 0x09, s2, s1); + } + vc("", "", "a") { + if (is("tset")) w2(0x0E, s1); + if (is("tclr")) w2(0x4E, s1); + } + if (is("div") && iscc("ya", "x")) w0(0x9E); + cv("ya", "", "") { + if (is("cmpw")) w1(0x5A, s2); + if (is("addw")) w1(0x7A, s2); + if (is("subw")) w1(0x9A, s2); + if (is("movw")) w1(0xBA, s2); + } + if (is("movw") && isvc("", "", "ya")) w1(0xDA, s1); + if (is("cbne") && isvv("", "+x", "", "")) w1r(0xDE, s1, s2); + if (is("dbnz") && iscv("y", "", "")) wr(0xFE, s2); + vv("", "", "", "") { + if (is("dbnz")) w1r(0x6E, s1, s2); + if (is("cbne")) w1r(0x2E, s1, s2); + } #undef iscc #undef iscv #undef isvc @@ -410,10 +513,10 @@ bool asblock_spc700(char** word, int numwords) #undef w2 #undef wv #undef w11 - return false; - } - return false; - } - else return false; - return true; + return false; + } + return false; + } else + return false; + return true; } diff --git a/src/asar/arch-superfx.cpp b/src/asar/arch-superfx.cpp index afc83072..31dc4245 100644 --- a/src/asar/arch-superfx.cpp +++ b/src/asar/arch-superfx.cpp @@ -1,330 +1,309 @@ #include "asar.h" -#include "assembleblock.h" #include "asar_math.h" +#include "assembleblock.h" #define write1 write1_pick -void asinit_superfx() -{ -} +void asinit_superfx() {} -void asend_superfx() -{ -} +void asend_superfx() {} -static void range(int min, int mid, int max) -{ - if (midmax) asar_throw_error(0, error_type_block, error_id_superfx_invalid_register, min, max); +static void range(int min, int mid, int max) { + if (mid < min || mid > max) + asar_throw_error(0, error_type_block, error_id_superfx_invalid_register, min, + max); } enum reg_t { - reg_parr, - reg_r, - reg_hash, + reg_parr, + reg_r, + reg_hash, }; -static bool getreg(const char * par, int * reg, reg_t type) -{ - int ret; - *reg=-1; - if (type==reg_parr && *par++!='(') return false; - if (type==reg_parr && to_lower(*par++)!='r') return false; - if (type==reg_r && to_lower(*par++)!='r') return false; - if (type==reg_hash && *par++!='#') return false; - if (!is_digit(par[0])) return false; - if (is_digit(par[1])) - { - if (par[0]!='1' || par[1]>'5') return false; - ret=par[1]-'0'+10; - par+=2; - } - else - { - ret=par[0]-'0'; - par+=1; - } - if (type==reg_parr && *par++!=')') return false; - if (*par) return false; - *reg=ret; - return true; +static bool getreg(const char* par, int* reg, reg_t type) { + int ret; + *reg = -1; + if (type == reg_parr && *par++ != '(') return false; + if (type == reg_parr && to_lower(*par++) != 'r') return false; + if (type == reg_r && to_lower(*par++) != 'r') return false; + if (type == reg_hash && *par++ != '#') return false; + if (!is_digit(par[0])) return false; + if (is_digit(par[1])) { + if (par[0] != '1' || par[1] > '5') return false; + ret = par[1] - '0' + 10; + par += 2; + } else { + ret = par[0] - '0'; + par += 1; + } + if (type == reg_parr && *par++ != ')') return false; + if (*par) return false; + *reg = ret; + return true; } -//for LMS and SMS short addressing forms, check range & evenness +// for LMS and SMS short addressing forms, check range & evenness static bool check_short_addr(int num) { - if (num % 2 > 0 || num < 0 || num > 0x1FE) { - asar_throw_error(0, error_type_block, error_id_superfx_invalid_short_address, hex((unsigned int)num).data()); - return false; - } - return true; + if (num % 2 > 0 || num < 0 || num > 0x1FE) { + asar_throw_error(0, error_type_block, error_id_superfx_invalid_short_address, + hex((unsigned int)num).data()); + return false; + } + return true; } -bool asblock_superfx(char** word, int numwords) -{ +bool asblock_superfx(char** word, int numwords) { #define is(test) (!stricmp(word[0], test)) - char * par= nullptr; - if (word[1]) par= duplicate_string(word[1]); - autoptr parptr=par; - if(0); - else if (assemblemapper(word, numwords)) return true; - else if (numwords==1) - { -#define op(from, to) if (is(from)) { write1(to); return true; } -#define op3d(from, to) if (is(from)) { write1(0x3D); write1(to); return true; } -#define op3e(from, to) if (is(from)) { write1(0x3E); write1(to); return true; } -#define op3f(from, to) if (is(from)) { write1(0x3F); write1(to); return true; } - op("STOP", 0x00); - op("NOP", 0x01); - op("CACHE", 0x02); - op("LSR", 0x03); - op("ROL", 0x04); - op("LOOP", 0x3C); - op("ALT1", 0x3D); - op("ALT2", 0x3E); - op("ALT3", 0x3F); - op("PLOT", 0x4C); - op("SWAP", 0x4D); - op("COLOR", 0x4E); - op("NOT", 0x4F); - op("MERGE", 0x70); - op("SBK", 0x90); - op("SEX", 0x95); - op("ASR", 0x96); - op("ROR", 0x97); - op("LOB", 0x9E); - op("FMULT", 0x9F); - op("HIB", 0xC0); - op("GETC", 0xDF); - op("GETB", 0xEF); - op3d("RPIX", 0x4C); - op3d("CMODE", 0x4E); - op3d("DIV2", 0x96); - op3d("LMULT", 0x9F); - op3d("GETBH", 0xEF); - op3e("RAMB", 0xDF); - op3e("GETBL", 0xEF); - op3f("ROMB", 0xDF); - op3f("GETBS", 0xEF); + char* par = nullptr; + if (word[1]) par = duplicate_string(word[1]); + autoptr parptr = par; + if (0) + ; + else if (assemblemapper(word, numwords)) + return true; + else if (numwords == 1) { +#define op(from, to) \ + if (is(from)) { \ + write1(to); \ + return true; \ + } +#define op3d(from, to) \ + if (is(from)) { \ + write1(0x3D); \ + write1(to); \ + return true; \ + } +#define op3e(from, to) \ + if (is(from)) { \ + write1(0x3E); \ + write1(to); \ + return true; \ + } +#define op3f(from, to) \ + if (is(from)) { \ + write1(0x3F); \ + write1(to); \ + return true; \ + } + op("STOP", 0x00); + op("NOP", 0x01); + op("CACHE", 0x02); + op("LSR", 0x03); + op("ROL", 0x04); + op("LOOP", 0x3C); + op("ALT1", 0x3D); + op("ALT2", 0x3E); + op("ALT3", 0x3F); + op("PLOT", 0x4C); + op("SWAP", 0x4D); + op("COLOR", 0x4E); + op("NOT", 0x4F); + op("MERGE", 0x70); + op("SBK", 0x90); + op("SEX", 0x95); + op("ASR", 0x96); + op("ROR", 0x97); + op("LOB", 0x9E); + op("FMULT", 0x9F); + op("HIB", 0xC0); + op("GETC", 0xDF); + op("GETB", 0xEF); + op3d("RPIX", 0x4C); + op3d("CMODE", 0x4E); + op3d("DIV2", 0x96); + op3d("LMULT", 0x9F); + op3d("GETBH", 0xEF); + op3e("RAMB", 0xDF); + op3e("GETBL", 0xEF); + op3f("ROMB", 0xDF); + op3f("GETBS", 0xEF); #undef op #undef op3d #undef op3e #undef op3f - return false; - } - else if (numwords==2) - { - string tmp=par; - int numwordsinner; - autoptr parcpy= duplicate_string(par); - autoptr arg=qpsplit(parcpy, ',', &numwordsinner); - bool ret=false; -#define ok() ret=true -#define op(op) if (is(op)) ok() -#define w3d(val) ,write1(0x3D) w(val) -#define w3e(val) ,write1(0x3E) w(val) -#define w3f(val) ,write1(0x3F) w(val) - if (numwordsinner ==1) - { -#define w(val) ,write1((unsigned int)(val+reg)) -#define reg_range(min, max) ,range(min, reg, max) - int reg; - if (getreg(par, ®, reg_r)) - { - op("TO") w(0x10); - op("WITH") w(0x20); - op("ADD") w(0x50); - op("SUB") w(0x60); - op("AND") reg_range(1, 15) w(0x70); - op("MULT") w(0x80); - op("JMP") reg_range(8, 13) w(0x90); - op("FROM") w(0xB0); - op("OR") reg_range(1, 15) w(0xC0); - op("INC") reg_range(0, 14) w(0xD0); - op("DEC") reg_range(0, 14) w(0xE0); + return false; + } else if (numwords == 2) { + string tmp = par; + int numwordsinner; + autoptr parcpy = duplicate_string(par); + autoptr arg = qpsplit(parcpy, ',', &numwordsinner); + bool ret = false; +#define ok() ret = true +#define op(op) \ + if (is(op)) ok() +#define w3d(val) , write1(0x3D) w(val) +#define w3e(val) , write1(0x3E) w(val) +#define w3f(val) , write1(0x3F) w(val) + if (numwordsinner == 1) { +#define w(val) , write1((unsigned int)(val + reg)) +#define reg_range(min, max) , range(min, reg, max) + int reg; + if (getreg(par, ®, reg_r)) { + op("TO") w(0x10); + op("WITH") w(0x20); + op("ADD") w(0x50); + op("SUB") w(0x60); + op("AND") reg_range(1, 15) w(0x70); + op("MULT") w(0x80); + op("JMP") reg_range(8, 13) w(0x90); + op("FROM") w(0xB0); + op("OR") reg_range(1, 15) w(0xC0); + op("INC") reg_range(0, 14) w(0xD0); + op("DEC") reg_range(0, 14) w(0xE0); - op("ADC") w3d(0x50); - op("SBC") w3d(0x60); - op("BIC") reg_range(1, 15) w3d(0x70); - op("UMULT") w3d(0x80); - op("LJMP") reg_range(8, 13) w3d(0x90); - op("XOR") reg_range(1, 15) w3d(0xC0); + op("ADC") w3d(0x50); + op("SBC") w3d(0x60); + op("BIC") reg_range(1, 15) w3d(0x70); + op("UMULT") w3d(0x80); + op("LJMP") reg_range(8, 13) w3d(0x90); + op("XOR") reg_range(1, 15) w3d(0xC0); - op("CMP") w3f(0x60); - } - if (getreg(par, ®, reg_hash)) - { - op("LINK") reg_range(1, 4) w(0x90); + op("CMP") w3f(0x60); + } + if (getreg(par, ®, reg_hash)) { + op("LINK") reg_range(1, 4) w(0x90); - op("ADD") w3e(0x50); - op("SUB") w3e(0x60); - op("AND") reg_range(1, 15) w3e(0x70); - op("MULT") w3e(0x80); - op("OR") reg_range(1, 15) w3e(0xC0); + op("ADD") w3e(0x50); + op("SUB") w3e(0x60); + op("AND") reg_range(1, 15) w3e(0x70); + op("MULT") w3e(0x80); + op("OR") reg_range(1, 15) w3e(0xC0); - op("ADC") w3f(0x50); - op("BIC") reg_range(1, 15) w3f(0x70); - op("UMULT") w3f(0x80); - op("XOR") reg_range(1, 15) w3f(0xC0); - } - if (getreg(par, ®, reg_parr)) - { - op("STW") reg_range(0, 11) w(0x30); - op("LDW") reg_range(0, 11) w(0x40); - op("STB") reg_range(0, 11) w3d(0x30); - op("LDB") reg_range(0, 11) w3d(0x40); - } + op("ADC") w3f(0x50); + op("BIC") reg_range(1, 15) w3f(0x70); + op("UMULT") w3f(0x80); + op("XOR") reg_range(1, 15) w3f(0xC0); + } + if (getreg(par, ®, reg_parr)) { + op("STW") reg_range(0, 11) w(0x30); + op("LDW") reg_range(0, 11) w(0x40); + op("STB") reg_range(0, 11) w3d(0x30); + op("LDB") reg_range(0, 11) w3d(0x40); + } #undef w #undef reg_range - int byte=-1; -#define br(name, val) if (is(name)) byte=val; - br("BRA", 0x05); - br("BGE", 0x06); - br("BLT", 0x07); - br("BNE", 0x08); - br("BEQ", 0x09); - br("BPL", 0x0A); - br("BMI", 0x0B); - br("BCC", 0x0C); - br("BCS", 0x0D); - br("BVC", 0x0E); - br("BVS", 0x0F); + int byte = -1; +#define br(name, val) \ + if (is(name)) byte = val; + br("BRA", 0x05); + br("BGE", 0x06); + br("BLT", 0x07); + br("BNE", 0x08); + br("BEQ", 0x09); + br("BPL", 0x0A); + br("BMI", 0x0B); + br("BCC", 0x0C); + br("BCS", 0x0D); + br("BVC", 0x0E); + br("BVS", 0x0F); #undef br - if (byte!=-1) - { - ret=true; - int len=getlen(par); - unsigned int num=getnum(par); - if (len==1) - { - write1((unsigned int)byte); write1(num); - } - else - { - int pos=(int)getnum(par)-((snespos&0xFFFFFF)+2); - write1((unsigned int)byte); write1((unsigned int)pos); - if (pass==2 && (pos<-128 || pos>127)) - { - asar_throw_error(2, error_type_block, error_id_relative_branch_out_of_bounds, dec(pos).data()); - } - } - } - } - if (numwordsinner==2) - { -#define w(val) ,write1((unsigned int)(val)) - int reg1; bool isreg1=getreg(arg[0], ®1, reg_r); - int reg2; bool isreg2=getreg(arg[1], ®2, reg_r); - if (isreg1) - { - if (isreg2) - { - op("MOVE") w(0x20+reg2) w(0x10+reg1); - op("MOVES") w(0x20+reg1) w(0xB0+reg2); - } - if (arg[1][0]=='#') - { - unsigned int num=getnum(arg[1]+1); - num&=0xFFFF; - op("IBT") w(0xA0+reg1) w(num); - op("IWT") w(0xF0+reg1) w(num) w(num>>8); - if (num<0x80 || num>=0xFF80) - { - op("MOVE") w(0xA0+reg1) w(num); - } - else - { - op("MOVE") w(0xF0+reg1) w(num) w(num>>8); - } - } - if (getreg(arg[1], ®2, reg_parr)) - { - if (reg1==0) - { - op("MOVEB") w(0x3D) w(0x40+reg2); - op("MOVEW") w(0x40+reg2); - } - else - { - op("MOVEB") w(0x10+reg1) w(0x3D) w(0x40+reg2); - op("MOVEW") w(0x10+reg1) w(0x40+reg2); - } - } - else if (arg[1][0]=='(') - { - char * endpar=strchr(arg[1], ')'); - if (!endpar || endpar[1]) return false; - unsigned int num=getnum(arg[1]); - op("LM") w(0x3D) w(0xF0+reg1) w(num) w(num>>8); + if (byte != -1) { + ret = true; + int len = getlen(par); + unsigned int num = getnum(par); + if (len == 1) { + write1((unsigned int)byte); + write1(num); + } else { + int pos = (int)getnum(par) - ((snespos & 0xFFFFFF) + 2); + write1((unsigned int)byte); + write1((unsigned int)pos); + if (pass == 2 && (pos < -128 || pos > 127)) { + asar_throw_error(2, error_type_block, + error_id_relative_branch_out_of_bounds, + dec(pos).data()); + } + } + } + } + if (numwordsinner == 2) { +#define w(val) , write1((unsigned int)(val)) + int reg1; + bool isreg1 = getreg(arg[0], ®1, reg_r); + int reg2; + bool isreg2 = getreg(arg[1], ®2, reg_r); + if (isreg1) { + if (isreg2) { + op("MOVE") w(0x20 + reg2) w(0x10 + reg1); + op("MOVES") w(0x20 + reg1) w(0xB0 + reg2); + } + if (arg[1][0] == '#') { + unsigned int num = getnum(arg[1] + 1); + num &= 0xFFFF; + op("IBT") w(0xA0 + reg1) w(num); + op("IWT") w(0xF0 + reg1) w(num) w(num >> 8); + if (num < 0x80 || num >= 0xFF80) { + op("MOVE") w(0xA0 + reg1) w(num); + } else { + op("MOVE") w(0xF0 + reg1) w(num) w(num >> 8); + } + } + if (getreg(arg[1], ®2, reg_parr)) { + if (reg1 == 0) { + op("MOVEB") w(0x3D) w(0x40 + reg2); + op("MOVEW") w(0x40 + reg2); + } else { + op("MOVEB") w(0x10 + reg1) w(0x3D) w(0x40 + reg2); + op("MOVEW") w(0x10 + reg1) w(0x40 + reg2); + } + } else if (arg[1][0] == '(') { + char* endpar = strchr(arg[1], ')'); + if (!endpar || endpar[1]) return false; + unsigned int num = getnum(arg[1]); + op("LM") w(0x3D) w(0xF0 + reg1) w(num) w(num >> 8); - if (is("LMS")) { - ok(); - if (check_short_addr((int)num)) - { - ok() w(0x3D) w(0xA0+reg1) w(num>>1); - } - } + if (is("LMS")) { + ok(); + if (check_short_addr((int)num)) { + ok() w(0x3D) w(0xA0 + reg1) w(num >> 1); + } + } - if (num&1 || num>=0x200) - { - op("MOVE") w(0x3D) w(0xF0+reg1) w(num) w(num>>8); - } - else - { - op("MOVE") w(0x3D) w(0xA0+reg1) w(num); - } - } - if (is("LEA")) - { - unsigned int num=getnum(arg[1]); - ok() w(0xF0+reg1) w(num) w(num>>8); - } - } - else if (isreg2) - { - if (getreg(arg[0], ®1, reg_parr)) - { - if (reg1==0) - { - op("MOVEB") w(0x3D) w(0x30+reg2); - op("MOVEW") w(0x30+reg2); - } - else - { - op("MOVEB") w(0xB0+reg1) w(0x3D) w(0x30+reg2); - op("MOVEW") w(0xB0+reg1) w(0x30+reg2); - } - } - else if (arg[0][0]=='(') - { - char * endpar=strchr(arg[0], ')'); - if (!endpar || endpar[1]) return false; - unsigned int num=getnum(arg[0]); - op("SM") w(0x3E) w(0xF0+reg2) w(num) w(num>>8); + if (num & 1 || num >= 0x200) { + op("MOVE") w(0x3D) w(0xF0 + reg1) w(num) w(num >> 8); + } else { + op("MOVE") w(0x3D) w(0xA0 + reg1) w(num); + } + } + if (is("LEA")) { + unsigned int num = getnum(arg[1]); + ok() w(0xF0 + reg1) w(num) w(num >> 8); + } + } else if (isreg2) { + if (getreg(arg[0], ®1, reg_parr)) { + if (reg1 == 0) { + op("MOVEB") w(0x3D) w(0x30 + reg2); + op("MOVEW") w(0x30 + reg2); + } else { + op("MOVEB") w(0xB0 + reg1) w(0x3D) w(0x30 + reg2); + op("MOVEW") w(0xB0 + reg1) w(0x30 + reg2); + } + } else if (arg[0][0] == '(') { + char* endpar = strchr(arg[0], ')'); + if (!endpar || endpar[1]) return false; + unsigned int num = getnum(arg[0]); + op("SM") w(0x3E) w(0xF0 + reg2) w(num) w(num >> 8); - if (is("SMS")) - { - ok(); - if (check_short_addr((int)num)) - { - ok() w(0x3E) w(0xA0+reg2) w(num>>1); - } - } + if (is("SMS")) { + ok(); + if (check_short_addr((int)num)) { + ok() w(0x3E) w(0xA0 + reg2) w(num >> 1); + } + } - if (num&1 || num>=0x200) - { - op("MOVE") w(0x3E) w(0xF0+reg2) w(num) w(num>>8); - } - else - { - op("MOVE") w(0x3E) w(0xA0+reg2) w(num); - } - } - } - } + if (num & 1 || num >= 0x200) { + op("MOVE") w(0x3E) w(0xF0 + reg2) w(num) w(num >> 8); + } else { + op("MOVE") w(0x3E) w(0xA0 + reg2) w(num); + } + } + } + } #undef ok #undef op #undef w3d #undef w3e #undef w3f - return ret; - } - return false; + return ret; + } + return false; } diff --git a/src/asar/asar.h b/src/asar/asar.h index 4cba8975..ab6d2130 100644 --- a/src/asar/asar.h +++ b/src/asar/asar.h @@ -1,4 +1,6 @@ -#if (defined(__sun__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__APPLE__)) && !defined(linux) +#if (defined(__sun__) || defined(__FreeBSD__) || defined(__NetBSD__) || \ + defined(__OpenBSD__) || defined(__APPLE__)) && \ + !defined(linux) #error Please use -Dlinux on non-Linux Unix-likes. #endif @@ -9,26 +11,26 @@ #pragma once #define Asar +#include + #include "assocarr.h" -#include "libstr.h" -#include "libsmw.h" #include "errors.h" -#include "warnings.h" +#include "libsmw.h" +#include "libstr.h" #include "virtualfile.h" -#include +#include "warnings.h" -extern unsigned const char * romdata_r; +extern unsigned const char* romdata_r; extern int romlen_r; -inline void verify_paren(autoptr &ptr) -{ - if(!ptr) asar_throw_error(0, error_type_block, error_id_mismatched_parentheses); +inline void verify_paren(autoptr& ptr) { + if (!ptr) asar_throw_error(0, error_type_block, error_id_mismatched_parentheses); } -int getlen(const char * str, bool optimizebankextraction=false); -bool is_hex_constant(const char * str); +int getlen(const char* str, bool optimizebankextraction = false); +bool is_hex_constant(const char* str); -bool validatedefinename(const char * name); +bool validatedefinename(const char* name); string create_symbols_file(string format, uint32_t romCrc); @@ -37,20 +39,21 @@ void parse_std_defines(const char* textfile); void reseteverything(); -void resolvedefines(string& out, const char * start); +void resolvedefines(string& out, const char* start); int get_version_int(); bool setmapper(); -void assemblefile(const char * filename); -void assembleline(const char * fname, int linenum, const char * line); +void assemblefile(const char* filename); +void assembleline(const char* fname, int linenum, const char* line); void do_line_logic(const char* line, const char* filename, int lineno); bool file_included_once(const char* file); -void get_current_line_details(string* location, string* details, bool exclude_block=false); +void get_current_line_details(string* location, string* details, + bool exclude_block = false); string get_callstack(); asar_error_id vfile_error_to_error_id(virtual_file_error vfile_error); @@ -64,20 +67,16 @@ size_t check_stack_left(); class recurseblock { public: - recurseblock() - { - recursioncount++; + recurseblock() { + recursioncount++; #if !defined(_WIN32) && defined(NO_USE_THREADS) - if(recursioncount > 500) + if (recursioncount > 500) #else - if(check_stack_left() < 32768 || recursioncount > 5000) + if (check_stack_left() < 32768 || recursioncount > 5000) #endif - asar_throw_error(pass, error_type_fatal, error_id_recursion_limit); - } - ~recurseblock() - { - recursioncount--; - } + asar_throw_error(pass, error_type_fatal, error_id_recursion_limit); + } + ~recurseblock() { recursioncount--; } }; extern const int asarver_maj; @@ -100,25 +99,25 @@ extern int optimizeforbank; extern int in_macro_def; -//this is a trick to namespace an enum to avoid name collision without too much verbosity -//could technically name the enum too but this is fine for now. +// this is a trick to namespace an enum to avoid name collision without too much +// verbosity could technically name the enum too but this is fine for now. namespace optimize_dp_flag { - enum : int { - NONE, //don't optimize - RAM, //bank 7E only (always uses dp base) - ALWAYS //bank 00-3F[|80] and 7E (always uses dp base) - }; +enum : int { + NONE, // don't optimize + RAM, // bank 7E only (always uses dp base) + ALWAYS // bank 00-3F[|80] and 7E (always uses dp base) +}; } extern int optimize_dp; extern int dp_base; namespace optimize_address_flag { - enum : int { - DEFAULT,//simply use optimizeforbank - RAM, //default+bank 7E only RAM address < $2000 - MIRRORS //ram+if optimizeforbank is 00-3F[|80] and address < $8000 - }; +enum : int { + DEFAULT, // simply use optimizeforbank + RAM, // default+bank 7E only RAM address < $2000 + MIRRORS // ram+if optimizeforbank is 00-3F[|80] and address < $8000 +}; } extern int optimize_address; @@ -135,37 +134,34 @@ extern assocarr builtindefines; namespace callstack_entry_type { - enum e : int { - FILE, - MACRO_CALL, - LINE, - BLOCK, - }; +enum e : int { + FILE, + MACRO_CALL, + LINE, + BLOCK, +}; } struct callstack_entry { - callstack_entry_type::e type; - string content; - int lineno; - - callstack_entry(callstack_entry_type::e type, const char* content, int lineno) - { - this->type = type; - this->content = content; - this->lineno = lineno; - } - - callstack_entry() - { - } + callstack_entry_type::e type; + string content; + int lineno; + + callstack_entry(callstack_entry_type::e type, const char* content, int lineno) { + this->type = type; + this->content = content; + this->lineno = lineno; + } + + callstack_entry() {} }; struct printable_callstack_entry { - string fullpath; - string prettypath; - int lineno; - string details; + string fullpath; + string prettypath; + int lineno; + string details; }; @@ -174,15 +170,11 @@ extern bool simple_callstacks; class callstack_push { public: - callstack_push(callstack_entry_type::e type, const char* content, int lineno=-1) - { - callstack.append(callstack_entry(type, content, lineno)); - } - - ~callstack_push() - { - callstack.remove(callstack.count-1); - } + callstack_push(callstack_entry_type::e type, const char* content, int lineno = -1) { + callstack.append(callstack_entry(type, content, lineno)); + } + + ~callstack_push() { callstack.remove(callstack.count - 1); } }; bool in_top_level_file(); @@ -190,14 +182,15 @@ const char* get_current_file_name(); int get_current_line(); const char* get_current_block(); -void get_full_printable_callstack(autoarray* out, int indentation, bool add_lines); +void get_full_printable_callstack(autoarray* out, + int indentation, bool add_lines); #if !defined(NO_USE_THREADS) && !defined(RUN_VIA_THREAD) -// RPG Hacker: This is currently disabled for debug builds, because it causes random crashes -// when used in combination with -fsanitize=address. -# if defined(_WIN32) && defined(NDEBUG) -# define RUN_VIA_FIBER -# else -# define RUN_VIA_THREAD -# endif +// RPG Hacker: This is currently disabled for debug builds, because it causes random +// crashes when used in combination with -fsanitize=address. +#if defined(_WIN32) && defined(NDEBUG) +#define RUN_VIA_FIBER +#else +#define RUN_VIA_THREAD +#endif #endif diff --git a/src/asar/asar_math.cpp b/src/asar/asar_math.cpp index 493ac9f8..61a01ff4 100644 --- a/src/asar/asar_math.cpp +++ b/src/asar/asar_math.cpp @@ -1,36 +1,39 @@ -//Don't try using this in your own project, it's got a lot of Asar-specific tweaks. Use mathlib.cpp instead. -#include "platform/file-helpers.h" +// Don't try using this in your own project, it's got a lot of Asar-specific tweaks. Use +// mathlib.cpp instead. +#include "asar_math.h" + +#include +#include +#include + #include "asar.h" -#include "virtualfile.h" #include "assembleblock.h" #include "macro.h" -#include "asar_math.h" +#include "platform/file-helpers.h" #include "table.h" #include "unicode.h" -#include -#include -#include +#include "virtualfile.h" -static const char * str; -//save before calling eval if needed after -static const char * current_user_function_name; +static const char* str; +// save before calling eval if needed after +static const char* current_user_function_name; static double getnumcore(); static double getnum(); static double eval(int depth); -//label (bool foundLabel) (bool forwardLabel) -//userfunction +// label (bool foundLabel) (bool forwardLabel) +// userfunction bool foundlabel; bool foundlabel_static; bool forwardlabel; struct cachedfile { - string filename; - virtual_file_handle filehandle; - size_t filesize; - bool used; + string filename; + virtual_file_handle filehandle; + size_t filesize; + bool used; }; #define numcachedfiles 16 @@ -40,968 +43,911 @@ static int cachedfileindex = 0; // Opens a file, trying to open it from cache first -static cachedfile * opencachedfile(string fname, bool should_error) -{ - cachedfile * cachedfilehandle = nullptr; - - const char* current_file = get_current_file_name(); - - // RPG Hacker: Only using a combined path here because that should - // hopefully result in a unique string for every file, whereas - // fname could be a relative path, which isn't guaranteed to be unique. - // Note that this does not affect how we open the file - this is - // handled by the filesystem and uses our include paths etc. - string combinedname = filesystem->create_absolute_path(dir(current_file), fname); - - for (int i = 0; i < numcachedfiles; i++) - { - if (cachedfiles[i].used && cachedfiles[i].filename == combinedname) - { - cachedfilehandle = &cachedfiles[i]; - break; - } - } - - if (cachedfilehandle == nullptr) - { - if (cachedfiles[cachedfileindex].used) - { - filesystem->close_file(cachedfiles[cachedfileindex].filehandle); - cachedfiles[cachedfileindex].filehandle = INVALID_VIRTUAL_FILE_HANDLE; - cachedfiles[cachedfileindex].used = false; - } - - cachedfilehandle = &cachedfiles[cachedfileindex]; - } - - if (cachedfilehandle != nullptr) - { - if (!cachedfilehandle->used) - { - cachedfilehandle->filehandle = filesystem->open_file(fname, current_file); - if (cachedfilehandle->filehandle != INVALID_VIRTUAL_FILE_HANDLE) - { - cachedfilehandle->used = true; - cachedfilehandle->filename = combinedname; - cachedfilehandle->filesize = filesystem->get_file_size(cachedfilehandle->filehandle); - cachedfileindex++; - // randomdude999: when we run out of cached files, just start overwriting ones from the start - if (cachedfileindex >= numcachedfiles) cachedfileindex = 0; - } - } - } - - if ((cachedfilehandle == nullptr || cachedfilehandle->filehandle == INVALID_VIRTUAL_FILE_HANDLE) && should_error) - { - asar_throw_error(2, error_type_block, vfile_error_to_error_id(asar_get_last_io_error()), fname.data()); - } - - return cachedfilehandle; -} - - -void closecachedfiles() -{ - for (int i = 0; i < numcachedfiles; i++) - { - if (cachedfiles[i].used) - { - if (cachedfiles[i].filehandle != INVALID_VIRTUAL_FILE_HANDLE && filesystem) - { - filesystem->close_file(cachedfiles[i].filehandle); - cachedfiles[i].filehandle = INVALID_VIRTUAL_FILE_HANDLE; - } - - cachedfiles[i].used = false; - } - } - - cachedfileindex = 0; -} - - -static int struct_size(const char *name) -{ - if(pass && !structs.exists(name)) asar_throw_error(2, error_type_block, error_id_struct_not_found, name); - else if(!structs.exists(name)) return 0; - return structs.find(name).struct_size; -} - -static int object_size(const char *name) -{ - if(pass && !structs.exists(name)) asar_throw_error(2, error_type_block, error_id_struct_not_found, name); - else if(!structs.exists(name)) return 0; - return structs.find(name).object_size; -} - -static int data_size(const char *name) -{ - unsigned int label; - unsigned int next_label = 0xFFFFFF; - if(!labels.exists(name)) asar_throw_error(2, error_type_block, error_id_label_not_found, name); - foundlabel = true; - snes_label label_data = labels.find(name); - foundlabel_static &= label_data.is_static; - label = label_data.pos & 0xFFFFFF; - labels.each([&next_label, label](const char *key, snes_label current_label){ - current_label.pos &= 0xFFFFFF; - if(label < current_label.pos && current_label.pos < next_label){ - next_label = current_label.pos; - } - }); - if(next_label == 0xFFFFFF) asar_throw_warning(2, warning_id_datasize_last_label, name); - if(next_label-label > 0xFFFF) asar_throw_warning(2, warning_id_datasize_exceeds_size, name); - return next_label-label; -} - - -string get_string_argument() -{ - while (*str==' ') str++; - if (*str=='"') - { - const char * strpos = str + 1; - while (*str!='"' && *str!='\0') str++; - str = strchr(str + 1, '"'); - string tempname(strpos , (int)(str - strpos)); - str++; - while (*str==' ') str++; //eat space - return tempname; - }//todo make this error a better one later - - asar_throw_error(2, error_type_block, error_id_string_literal_not_terminated); - return ""; //never actually called, but I don't feel like figuring out __attribute__ ((noreturn)) on MSVC -} - -//only returns alphanumeric (and _) starting with alpha or _ -string get_symbol_argument() -{ - while (*str==' ') str++; //is this proper? Dunno yet. - const char * strpos = str; - if(is_ualpha(*str)) str++; - while (is_ualnum(*str) || *str == '.') str++; - if(strpos == str){ - //error nothing was read, this is a placeholder error - asar_throw_error(2, error_type_block, error_id_string_literal_not_terminated); - } +static cachedfile* opencachedfile(string fname, bool should_error) { + cachedfile* cachedfilehandle = nullptr; + + const char* current_file = get_current_file_name(); + + // RPG Hacker: Only using a combined path here because that should + // hopefully result in a unique string for every file, whereas + // fname could be a relative path, which isn't guaranteed to be unique. + // Note that this does not affect how we open the file - this is + // handled by the filesystem and uses our include paths etc. + string combinedname = filesystem->create_absolute_path(dir(current_file), fname); + + for (int i = 0; i < numcachedfiles; i++) { + if (cachedfiles[i].used && cachedfiles[i].filename == combinedname) { + cachedfilehandle = &cachedfiles[i]; + break; + } + } + + if (cachedfilehandle == nullptr) { + if (cachedfiles[cachedfileindex].used) { + filesystem->close_file(cachedfiles[cachedfileindex].filehandle); + cachedfiles[cachedfileindex].filehandle = INVALID_VIRTUAL_FILE_HANDLE; + cachedfiles[cachedfileindex].used = false; + } + + cachedfilehandle = &cachedfiles[cachedfileindex]; + } + + if (cachedfilehandle != nullptr) { + if (!cachedfilehandle->used) { + cachedfilehandle->filehandle = filesystem->open_file(fname, current_file); + if (cachedfilehandle->filehandle != INVALID_VIRTUAL_FILE_HANDLE) { + cachedfilehandle->used = true; + cachedfilehandle->filename = combinedname; + cachedfilehandle->filesize = + filesystem->get_file_size(cachedfilehandle->filehandle); + cachedfileindex++; + // randomdude999: when we run out of cached files, just start + // overwriting ones from the start + if (cachedfileindex >= numcachedfiles) cachedfileindex = 0; + } + } + } + + if ((cachedfilehandle == nullptr || + cachedfilehandle->filehandle == INVALID_VIRTUAL_FILE_HANDLE) && + should_error) { + asar_throw_error(2, error_type_block, + vfile_error_to_error_id(asar_get_last_io_error()), + fname.data()); + } + + return cachedfilehandle; +} + + +void closecachedfiles() { + for (int i = 0; i < numcachedfiles; i++) { + if (cachedfiles[i].used) { + if (cachedfiles[i].filehandle != INVALID_VIRTUAL_FILE_HANDLE && + filesystem) { + filesystem->close_file(cachedfiles[i].filehandle); + cachedfiles[i].filehandle = INVALID_VIRTUAL_FILE_HANDLE; + } + + cachedfiles[i].used = false; + } + } + + cachedfileindex = 0; +} + + +static int struct_size(const char* name) { + if (pass && !structs.exists(name)) + asar_throw_error(2, error_type_block, error_id_struct_not_found, name); + else if (!structs.exists(name)) + return 0; + return structs.find(name).struct_size; +} + +static int object_size(const char* name) { + if (pass && !structs.exists(name)) + asar_throw_error(2, error_type_block, error_id_struct_not_found, name); + else if (!structs.exists(name)) + return 0; + return structs.find(name).object_size; +} + +static int data_size(const char* name) { + unsigned int label; + unsigned int next_label = 0xFFFFFF; + if (!labels.exists(name)) + asar_throw_error(2, error_type_block, error_id_label_not_found, name); + foundlabel = true; + snes_label label_data = labels.find(name); + foundlabel_static &= label_data.is_static; + label = label_data.pos & 0xFFFFFF; + labels.each([&next_label, label](const char* key, snes_label current_label) { + current_label.pos &= 0xFFFFFF; + if (label < current_label.pos && current_label.pos < next_label) { + next_label = current_label.pos; + } + }); + if (next_label == 0xFFFFFF) + asar_throw_warning(2, warning_id_datasize_last_label, name); + if (next_label - label > 0xFFFF) + asar_throw_warning(2, warning_id_datasize_exceeds_size, name); + return next_label - label; +} + + +string get_string_argument() { + while (*str == ' ') str++; + if (*str == '"') { + const char* strpos = str + 1; + while (*str != '"' && *str != '\0') str++; + str = strchr(str + 1, '"'); + string tempname(strpos, (int)(str - strpos)); + str++; + while (*str == ' ') str++; // eat space + return tempname; + } // todo make this error a better one later - string symbol = string(strpos, (int)(str - strpos)); - while (*str==' ') str++; //eat spaces - return symbol; + asar_throw_error(2, error_type_block, error_id_string_literal_not_terminated); + return ""; // never actually called, but I don't feel like figuring out + // __attribute__ ((noreturn)) on MSVC } -double get_double_argument() -{ - while (*str==' ') str++; - double result = eval(0); - while (*str==' ') str++; //eat spaces - return result; -} +// only returns alphanumeric (and _) starting with alpha or _ +string get_symbol_argument() { + while (*str == ' ') str++; // is this proper? Dunno yet. + const char* strpos = str; + if (is_ualpha(*str)) str++; + while (is_ualnum(*str) || *str == '.') str++; + if (strpos == str) { + // error nothing was read, this is a placeholder error + asar_throw_error(2, error_type_block, error_id_string_literal_not_terminated); + } -//will eat the comma -bool has_next_parameter() -{ - if (*str==',') - { - str++; - return true; - } - return false; + string symbol = string(strpos, (int)(str - strpos)); + while (*str == ' ') str++; // eat spaces + return symbol; } -void require_next_parameter() -{ - if (*str==',') - { - str++; - return; - } - asar_throw_error(2, error_type_block, error_id_require_parameter); +double get_double_argument() { + while (*str == ' ') str++; + double result = eval(0); + while (*str == ' ') str++; // eat spaces + return result; } -template double asar_unary_wrapper() -{ - return F()(get_double_argument()); +// will eat the comma +bool has_next_parameter() { + if (*str == ',') { + str++; + return true; + } + return false; } -template double asar_unary_wrapper() -{ - return F(get_double_argument()); +void require_next_parameter() { + if (*str == ',') { + str++; + return; + } + asar_throw_error(2, error_type_block, error_id_require_parameter); } -//possibly restrict type T in the future.... -//first a case for functors -template double asar_binary_wrapper() -{ - double first = get_double_argument(); - require_next_parameter(); - return F()(first, get_double_argument()); +template +double asar_unary_wrapper() { + return F()(get_double_argument()); } -//this could be DRY with if constexpr....oh well -template double asar_binary_wrapper() -{ - double first = get_double_argument(); - require_next_parameter(); - return F(first, get_double_argument()); + +template +double asar_unary_wrapper() { + return F(get_double_argument()); } -double asar_bank(double a) -{ - return (int)a >> 16; -} +// possibly restrict type T in the future.... +// first a case for functors +template +double asar_binary_wrapper() { + double first = get_double_argument(); + require_next_parameter(); + return F()(first, get_double_argument()); +} +// this could be DRY with if constexpr....oh well +template +double asar_binary_wrapper() { + double first = get_double_argument(); + require_next_parameter(); + return F(first, get_double_argument()); +} +double asar_bank(double a) { return (int)a >> 16; } -double asar_logical_nand(double a, double b) -{ - return !(a && b); -} +double asar_logical_nand(double a, double b) { return !(a && b); } -double asar_logical_nor(double a, double b) -{ - return !(a || b); -} +double asar_logical_nor(double a, double b) { return !(a || b); } -double asar_logical_xor(double a, double b) -{ - return !!a ^ !!b; -} - -double asar_max(double a, double b) -{ - return a > b ? a : b; -} - -double asar_min(double a, double b) -{ - return a < b ? a : b; -} - -double asar_clamp() -{ - double value = get_double_argument(); - require_next_parameter(); - double low = get_double_argument(); - require_next_parameter(); - double high = get_double_argument(); - - return asar_max(low, asar_min(high, value)); -} - -double asar_safediv() -{ - double dividend = get_double_argument(); - require_next_parameter(); - double divisor = get_double_argument(); - require_next_parameter(); - double default_value = get_double_argument(); - return divisor == 0.0 ? default_value : dividend / divisor; -} +double asar_logical_xor(double a, double b) { return !!a ^ !!b; } -double asar_select() -{ - double selector = get_double_argument(); - require_next_parameter(); - double a = get_double_argument(); - require_next_parameter(); - double b = get_double_argument(); +double asar_max(double a, double b) { return a > b ? a : b; } - return selector == 0.0 ? b : a; -} +double asar_min(double a, double b) { return a < b ? a : b; } -double asar_snestopc_wrapper() -{ - return snestopc(get_double_argument()); -} +double asar_clamp() { + double value = get_double_argument(); + require_next_parameter(); + double low = get_double_argument(); + require_next_parameter(); + double high = get_double_argument(); + + return asar_max(low, asar_min(high, value)); +} + +double asar_safediv() { + double dividend = get_double_argument(); + require_next_parameter(); + double divisor = get_double_argument(); + require_next_parameter(); + double default_value = get_double_argument(); -double asar_pctosnes_wrapper() -{ - return pctosnes(get_double_argument()); + return divisor == 0.0 ? default_value : dividend / divisor; } -double asar_realbase_wrapper() -{ - //need a better way to do this to make sure it is useful... - //foundlabel=true; //Going to consider this an implicit label because we don't really have a better system - return realsnespos; -} - -template double asar_read() -{ - int target = get_double_argument(); - int addr=snestopc_pick(target); - if(has_next_parameter()) - { - double default_value = get_double_argument(); - if (addr<0) return default_value; - else if (addr+count>romlen_r) return default_value; - } - else - { - if (addr<0) asar_throw_error(2, error_type_block, error_id_snes_address_doesnt_map_to_rom, (hex((unsigned int)target, 6) + " in read function").data()); - else if (addr+count>romlen_r) asar_throw_error(2, error_type_block, error_id_snes_address_out_of_bounds, (hex(target, 6) + " in read function").data()); - } - - unsigned int value = 0; - for(int i = 0; i < count; i++) - { - value |= romdata_r[addr+i] << (8 * i); - } - return value; -} - -template double asar_canread() -{ - int length = count; - if(!length) - { - length = get_double_argument(); - } - int addr=snestopc_pick(get_double_argument()); - if (addr<0 || addr+length-1>=romlen_r) return 0; - else return 1; -} - -template double asar_readfile() -{ - static_assert(count && count <= 4, "invalid count"); //1-4 inclusive - - string name = get_string_argument(); - require_next_parameter(); - size_t offset = get_double_argument(); - bool should_error = !has_next_parameter(); - cachedfile * fhandle = opencachedfile(name, should_error); - if(!should_error) - { - double default_value = get_double_argument(); - if (fhandle == nullptr || fhandle->filehandle == INVALID_VIRTUAL_FILE_HANDLE) return default_value; - if (offset < 0 || offset + count > fhandle->filesize) return default_value; - } - else - { - if (fhandle == nullptr || fhandle->filehandle == INVALID_VIRTUAL_FILE_HANDLE) asar_throw_error(2, error_type_block, vfile_error_to_error_id(asar_get_last_io_error()), name.data()); - if (offset < 0 || offset + count > fhandle->filesize) asar_throw_error(2, error_type_block, error_id_file_offset_out_of_bounds, dec(offset).data(), name.data()); - } - - unsigned char data[4] = { 0, 0, 0, 0 }; - filesystem->read_file(fhandle->filehandle, data, offset, count); - - unsigned int value = 0; - for(size_t i = 0; i < count; i++) - { - value |= data[i] << (8 * i); - } - - return value; -} - -template double asar_canreadfile() -{ - string name = get_string_argument(); - require_next_parameter(); - size_t offset = get_double_argument(); - size_t length = count; - if(!count) - { - require_next_parameter(); - length = get_double_argument(); - } - cachedfile * fhandle = opencachedfile(name, false); - if (fhandle == nullptr || fhandle->filehandle == INVALID_VIRTUAL_FILE_HANDLE) return 0; - if (offset < 0 || offset + length > fhandle->filesize) return 0; - return 1; -} - -// returns 0 if the file is OK, 1 if the file doesn't exist, 2 if it couldn't be opened for some other reason -static double asar_filestatus() -{ - cachedfile * fhandle = opencachedfile(get_string_argument(), false); - if (fhandle == nullptr || fhandle->filehandle == INVALID_VIRTUAL_FILE_HANDLE) - { - if (filesystem->get_last_error() == vfe_doesnt_exist) - { - return 1; - } - else - { - return 2; - } - } - return 0; +double asar_select() { + double selector = get_double_argument(); + require_next_parameter(); + double a = get_double_argument(); + require_next_parameter(); + double b = get_double_argument(); + + return selector == 0.0 ? b : a; +} + +double asar_snestopc_wrapper() { return snestopc(get_double_argument()); } + +double asar_pctosnes_wrapper() { return pctosnes(get_double_argument()); } + +double asar_realbase_wrapper() { + // need a better way to do this to make sure it is useful... + // foundlabel=true; //Going to consider this an implicit label because we don't + // really have a better system + return realsnespos; +} + +template +double asar_read() { + int target = get_double_argument(); + int addr = snestopc_pick(target); + if (has_next_parameter()) { + double default_value = get_double_argument(); + if (addr < 0) + return default_value; + else if (addr + count > romlen_r) + return default_value; + } else { + if (addr < 0) + asar_throw_error( + 2, error_type_block, error_id_snes_address_doesnt_map_to_rom, + (hex((unsigned int)target, 6) + " in read function").data()); + else if (addr + count > romlen_r) + asar_throw_error(2, error_type_block, error_id_snes_address_out_of_bounds, + (hex(target, 6) + " in read function").data()); + } + + unsigned int value = 0; + for (int i = 0; i < count; i++) { + value |= romdata_r[addr + i] << (8 * i); + } + return value; +} + +template +double asar_canread() { + int length = count; + if (!length) { + length = get_double_argument(); + } + int addr = snestopc_pick(get_double_argument()); + if (addr < 0 || addr + length - 1 >= romlen_r) + return 0; + else + return 1; +} + +template +double asar_readfile() { + static_assert(count && count <= 4, "invalid count"); // 1-4 inclusive + + string name = get_string_argument(); + require_next_parameter(); + size_t offset = get_double_argument(); + bool should_error = !has_next_parameter(); + cachedfile* fhandle = opencachedfile(name, should_error); + if (!should_error) { + double default_value = get_double_argument(); + if (fhandle == nullptr || fhandle->filehandle == INVALID_VIRTUAL_FILE_HANDLE) + return default_value; + if (offset < 0 || offset + count > fhandle->filesize) return default_value; + } else { + if (fhandle == nullptr || fhandle->filehandle == INVALID_VIRTUAL_FILE_HANDLE) + asar_throw_error(2, error_type_block, + vfile_error_to_error_id(asar_get_last_io_error()), + name.data()); + if (offset < 0 || offset + count > fhandle->filesize) + asar_throw_error(2, error_type_block, error_id_file_offset_out_of_bounds, + dec(offset).data(), name.data()); + } + + unsigned char data[4] = {0, 0, 0, 0}; + filesystem->read_file(fhandle->filehandle, data, offset, count); + + unsigned int value = 0; + for (size_t i = 0; i < count; i++) { + value |= data[i] << (8 * i); + } + + return value; +} + +template +double asar_canreadfile() { + string name = get_string_argument(); + require_next_parameter(); + size_t offset = get_double_argument(); + size_t length = count; + if (!count) { + require_next_parameter(); + length = get_double_argument(); + } + cachedfile* fhandle = opencachedfile(name, false); + if (fhandle == nullptr || fhandle->filehandle == INVALID_VIRTUAL_FILE_HANDLE) + return 0; + if (offset < 0 || offset + length > fhandle->filesize) return 0; + return 1; +} + +// returns 0 if the file is OK, 1 if the file doesn't exist, 2 if it couldn't be opened +// for some other reason +static double asar_filestatus() { + cachedfile* fhandle = opencachedfile(get_string_argument(), false); + if (fhandle == nullptr || fhandle->filehandle == INVALID_VIRTUAL_FILE_HANDLE) { + if (filesystem->get_last_error() == vfe_doesnt_exist) { + return 1; + } else { + return 2; + } + } + return 0; } // Returns the size of the specified file. -static double asar_filesize() -{ - string name = get_string_argument(); - cachedfile * fhandle = opencachedfile(name, false); - if (fhandle == nullptr || fhandle->filehandle == INVALID_VIRTUAL_FILE_HANDLE) asar_throw_error(2, error_type_block, vfile_error_to_error_id(asar_get_last_io_error()), name.data()); - return (double)fhandle->filesize; +static double asar_filesize() { + string name = get_string_argument(); + cachedfile* fhandle = opencachedfile(name, false); + if (fhandle == nullptr || fhandle->filehandle == INVALID_VIRTUAL_FILE_HANDLE) + asar_throw_error(2, error_type_block, + vfile_error_to_error_id(asar_get_last_io_error()), + name.data()); + return (double)fhandle->filesize; } // Checks whether the specified define is defined. -static double asar_isdefined() -{ - return defines.exists(get_string_argument()); -} +static double asar_isdefined() { return defines.exists(get_string_argument()); } // RPG Hacker: What exactly makes this function overly complicated, you ask? // Well, it converts a double to a string and then back to a double. // It was the quickest reliable solution I could find, though, so there's that. -static double asar_round() -{ - double number = get_double_argument(); - require_next_parameter(); - - // Hue hue hue... ass! - // OK, sorry, I apologize. - string asstring = ftostrvar(number, get_double_argument()); - - // Some hacky shenanigans with variables going on here - const char * strbackup = str; - str = asstring; - double asdouble = (double)getnum(); - str = strbackup; - - return asdouble; -} - -static double asar_structsize_wrapper() -{ - string symbol = get_symbol_argument(); - if(symbol == "..."){ - if(!inmacro) asar_throw_error(2, error_type_block, error_id_vararg_sizeof_nomacro); - if(numvarargs == -1) asar_throw_error(2, error_type_block, error_id_macro_not_varadic, "sizeof(...)"); - return numvarargs; - } - return (double)struct_size(symbol); -} - -static double asar_objectsize_wrapper() -{ - return (double)object_size(get_symbol_argument()); -} - -static double asar_datasize_wrapper() -{ - return (double)data_size(get_symbol_argument()); -} - -static double asar_stringsequal() -{ - string string1 = get_string_argument(); - require_next_parameter(); - return (strcmp(string1, get_string_argument()) == 0 ? 1.0 : 0.0); -} - -static double asar_stringsequalnocase() -{ - string string1 = get_string_argument(); - require_next_parameter(); - return (stricmp(string1, get_string_argument()) == 0 ? 1.0 : 0.0); -} - -static double asar_char() -{ - string string1 = get_string_argument(); - require_next_parameter(); - int offset = get_double_argument(); - if(offset >= string1.length()) asar_throw_error(2, error_type_block, error_id_oob, offset, string1.length()); - return string1[offset]; -} - -static double asar_strlen() -{ - return get_string_argument().length(); -} - -string copy_arg() -{ - if(*str == '"') - { - string t = "\""; - return (t += get_string_argument() + "\""); - } - - string result; - bool is_symbolic = true; - int parlevel=0; - int i = 0; - while(parlevel > 0 || (str[i] != ',' && str[i] != ')')) - { - is_symbolic &= is_ualnum(str[i]); - if(str[i] == '(') parlevel++; - else if(str[i] == ')') parlevel--; - i++; - } - result += string(str, i); - str += i; - - if(!is_symbolic) - { - const char *oldstr=str; - str = (const char *)result; - result = ftostr(eval(0)); - str = oldstr; - } - return result; -} - -assocarr builtin_functions = -{ - {"sqrt", asar_unary_wrapper}, - {"sin", asar_unary_wrapper}, - {"cos", asar_unary_wrapper}, - {"tan", asar_unary_wrapper}, - {"asin", asar_unary_wrapper}, - {"acos", asar_unary_wrapper}, - {"atan", asar_unary_wrapper}, - {"arcsin", asar_unary_wrapper}, - {"arccos", asar_unary_wrapper}, - {"arctan", asar_unary_wrapper}, - {"log", asar_unary_wrapper}, - {"log10", asar_unary_wrapper}, - {"log2", asar_unary_wrapper}, - - {"ceil", asar_unary_wrapper}, - {"floor", asar_unary_wrapper}, - - {"read1", asar_read<1>}, //This handles the safe and unsafe variant - {"read2", asar_read<2>}, - {"read3", asar_read<3>}, - {"read4", asar_read<4>}, - {"canread", asar_canread<0>}, - {"canread1", asar_canread<1>}, - {"canread2", asar_canread<2>}, - {"canread3", asar_canread<3>}, - {"canread4", asar_canread<4>}, - - {"readfile1", asar_readfile<1>}, - {"readfile2", asar_readfile<2>}, - {"readfile3", asar_readfile<3>}, - {"readfile4", asar_readfile<4>}, - {"canreadfile", asar_canreadfile<0>}, - {"canreadfile1", asar_canreadfile<1>}, - {"canreadfile2", asar_canreadfile<2>}, - {"canreadfile3", asar_canreadfile<3>}, - {"canreadfile4", asar_canreadfile<4>}, - - {"filesize", asar_filesize}, - {"getfilestatus", asar_filestatus}, - - {"defined", asar_isdefined}, - - {"snestopc", asar_snestopc_wrapper}, - {"pctosnes", asar_pctosnes_wrapper}, - {"realbase", asar_realbase_wrapper}, - - {"max", asar_binary_wrapper}, - {"min", asar_binary_wrapper}, - {"clamp", asar_clamp}, - - {"safediv", asar_safediv}, - - {"select", asar_select}, - {"bank", asar_unary_wrapper}, - {"not", asar_unary_wrapper>}, - {"equal", asar_binary_wrapper>}, - {"notequal", asar_binary_wrapper>}, - {"less", asar_binary_wrapper>}, - {"lessequal", asar_binary_wrapper>}, - {"greater", asar_binary_wrapper>}, - {"greaterequal", asar_binary_wrapper>}, - - {"and", asar_binary_wrapper>}, - {"or", asar_binary_wrapper>}, - {"nand", asar_binary_wrapper}, - {"nor", asar_binary_wrapper}, - {"xor", asar_binary_wrapper}, - - {"round", asar_round}, - - {"sizeof", asar_structsize_wrapper}, - {"objectsize", asar_objectsize_wrapper}, - {"datasize", asar_datasize_wrapper}, - - {"stringsequal", asar_stringsequal}, - {"stringsequalnocase", asar_stringsequalnocase}, - {"char", asar_char}, - {"stringlength", asar_strlen} -}; +static double asar_round() { + double number = get_double_argument(); + require_next_parameter(); + + // Hue hue hue... ass! + // OK, sorry, I apologize. + string asstring = ftostrvar(number, get_double_argument()); + + // Some hacky shenanigans with variables going on here + const char* strbackup = str; + str = asstring; + double asdouble = (double)getnum(); + str = strbackup; + + return asdouble; +} + +static double asar_structsize_wrapper() { + string symbol = get_symbol_argument(); + if (symbol == "...") { + if (!inmacro) + asar_throw_error(2, error_type_block, error_id_vararg_sizeof_nomacro); + if (numvarargs == -1) + asar_throw_error(2, error_type_block, error_id_macro_not_varadic, + "sizeof(...)"); + return numvarargs; + } + return (double)struct_size(symbol); +} + +static double asar_objectsize_wrapper() { + return (double)object_size(get_symbol_argument()); +} + +static double asar_datasize_wrapper() { + return (double)data_size(get_symbol_argument()); +} + +static double asar_stringsequal() { + string string1 = get_string_argument(); + require_next_parameter(); + return (strcmp(string1, get_string_argument()) == 0 ? 1.0 : 0.0); +} + +static double asar_stringsequalnocase() { + string string1 = get_string_argument(); + require_next_parameter(); + return (stricmp(string1, get_string_argument()) == 0 ? 1.0 : 0.0); +} + +static double asar_char() { + string string1 = get_string_argument(); + require_next_parameter(); + int offset = get_double_argument(); + if (offset >= string1.length()) + asar_throw_error(2, error_type_block, error_id_oob, offset, string1.length()); + return string1[offset]; +} + +static double asar_strlen() { return get_string_argument().length(); } + +string copy_arg() { + if (*str == '"') { + string t = "\""; + return (t += get_string_argument() + "\""); + } + + string result; + bool is_symbolic = true; + int parlevel = 0; + int i = 0; + while (parlevel > 0 || (str[i] != ',' && str[i] != ')')) { + is_symbolic &= is_ualnum(str[i]); + if (str[i] == '(') + parlevel++; + else if (str[i] == ')') + parlevel--; + i++; + } + result += string(str, i); + str += i; + + if (!is_symbolic) { + const char* oldstr = str; + str = (const char*)result; + result = ftostr(eval(0)); + str = oldstr; + } + return result; +} + +assocarr builtin_functions = { + {"sqrt", asar_unary_wrapper}, + {"sin", asar_unary_wrapper}, + {"cos", asar_unary_wrapper}, + {"tan", asar_unary_wrapper}, + {"asin", asar_unary_wrapper}, + {"acos", asar_unary_wrapper}, + {"atan", asar_unary_wrapper}, + {"arcsin", asar_unary_wrapper}, + {"arccos", asar_unary_wrapper}, + {"arctan", asar_unary_wrapper}, + {"log", asar_unary_wrapper}, + {"log10", asar_unary_wrapper}, + {"log2", asar_unary_wrapper}, + + {"ceil", asar_unary_wrapper}, + {"floor", asar_unary_wrapper}, + + {"read1", asar_read<1>}, // This handles the safe and unsafe variant + {"read2", asar_read<2>}, + {"read3", asar_read<3>}, + {"read4", asar_read<4>}, + {"canread", asar_canread<0>}, + {"canread1", asar_canread<1>}, + {"canread2", asar_canread<2>}, + {"canread3", asar_canread<3>}, + {"canread4", asar_canread<4>}, + + {"readfile1", asar_readfile<1>}, + {"readfile2", asar_readfile<2>}, + {"readfile3", asar_readfile<3>}, + {"readfile4", asar_readfile<4>}, + {"canreadfile", asar_canreadfile<0>}, + {"canreadfile1", asar_canreadfile<1>}, + {"canreadfile2", asar_canreadfile<2>}, + {"canreadfile3", asar_canreadfile<3>}, + {"canreadfile4", asar_canreadfile<4>}, + + {"filesize", asar_filesize}, + {"getfilestatus", asar_filestatus}, + + {"defined", asar_isdefined}, + + {"snestopc", asar_snestopc_wrapper}, + {"pctosnes", asar_pctosnes_wrapper}, + {"realbase", asar_realbase_wrapper}, + + {"max", asar_binary_wrapper}, + {"min", asar_binary_wrapper}, + {"clamp", asar_clamp}, + + {"safediv", asar_safediv}, + + {"select", asar_select}, + {"bank", asar_unary_wrapper}, + {"not", asar_unary_wrapper>}, + {"equal", asar_binary_wrapper>}, + {"notequal", asar_binary_wrapper>}, + {"less", asar_binary_wrapper>}, + {"lessequal", asar_binary_wrapper>}, + {"greater", asar_binary_wrapper>}, + {"greaterequal", asar_binary_wrapper>}, + + {"and", asar_binary_wrapper>}, + {"or", asar_binary_wrapper>}, + {"nand", asar_binary_wrapper}, + {"nor", asar_binary_wrapper}, + {"xor", asar_binary_wrapper}, + + {"round", asar_round}, + + {"sizeof", asar_structsize_wrapper}, + {"objectsize", asar_objectsize_wrapper}, + {"datasize", asar_datasize_wrapper}, + + {"stringsequal", asar_stringsequal}, + {"stringsequalnocase", asar_stringsequalnocase}, + {"char", asar_char}, + {"stringlength", asar_strlen}}; assocarr functions; struct funcdat { - autoptr name; - int numargs; - autoptr argbuf;//this one isn't used, it's just to free it up - autoptr arguments; - autoptr content; + autoptr name; + int numargs; + autoptr argbuf; // this one isn't used, it's just to free it up + autoptr arguments; + autoptr content; }; static assocarr user_functions; -static double asar_call_user_function() -{ - autoarray args; - funcdat &user_function = user_functions[current_user_function_name]; - string real_content; - - while (*str==' ') str++; - bool has_next = *str != ')'; - - for (int i=0;i args; + funcdat& user_function = user_functions[current_user_function_name]; + string real_content; + + while (*str == ' ') str++; + bool has_next = *str != ')'; + + for (int i = 0; i < user_function.numargs; i++) { + if (!has_next) { + asar_throw_error(2, error_type_block, error_id_expected_parameter, + current_user_function_name); + } + args[i] = copy_arg(); + has_next = has_next_parameter(); + } + + if (has_next) { + asar_throw_error(2, error_type_block, error_id_unexpected_parameter, + current_user_function_name); + } + + for (int i = 0; user_function.content[i]; i++) { + if (!is_ualpha(user_function.content[i])) { + real_content += user_function.content[i]; + continue; + } + bool found = false; + for (int j = 0; user_function.arguments[j]; j++) { + // this should *always* have a null term or another character after + bool potential_arg = + stribegin(user_function.content + i, user_function.arguments[j]); + int next_char = i + strlen(user_function.arguments[j]); + if (potential_arg && !is_ualnum(user_function.content[next_char])) { + real_content += args[j]; + i = next_char - 1; + found = true; + } + } + + if (!found) { + for (; is_ualnum(user_function.content[i]); i++) { + real_content += user_function.content[i]; + } + real_content += user_function.content[i]; + } + } + const char* oldstr = str; + str = (const char*)real_content; + double result = eval(0); + str = oldstr; + return result; +} + +void createuserfunc(const char* name, const char* arguments, const char* content) { + if (!confirmqpar(content)) + asar_throw_error(0, error_type_block, error_id_mismatched_parentheses); + if (functions.exists(name)) // functions holds both types. + { + asar_throw_error(0, error_type_block, error_id_function_redefined, name); + } + funcdat& user_function = user_functions[name]; + user_function.name = duplicate_string(name); + user_function.argbuf = duplicate_string(arguments); + user_function.arguments = + split(user_function.argbuf, ',', &(user_function.numargs)); + user_function.content = duplicate_string(content); + for (int i = 0; user_function.arguments[i]; i++) { + for (int j = 0; user_function.arguments[j]; j++) { + if (i != j && + !stricmp(user_function.arguments[i], user_function.arguments[j])) { + asar_throw_error(0, error_type_block, error_id_duplicate_param_name, + user_function.arguments[i], name); + } + } + if (!confirmname(user_function.arguments[i])) { + user_functions.remove(name); + asar_throw_error(0, error_type_block, error_id_invalid_param_name); + } + } + + functions[name] = asar_call_user_function; } inline const long hextable[] = { - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, - -1,-1, 0,1,2,3,4,5,6,7,8,9,-1,-1,-1,-1,-1,-1,-1,10,11,12,13,14,15,-1, - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, - -1,-1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 -}; - -static double getnumcore() -{ - if (*str=='$') - { - if (!is_xdigit(*++str)) asar_throw_error(2, error_type_block, error_id_invalid_hex_value); - uint64_t ret = 0; - while (hextable[0 + *str] >= 0) { - ret = (ret << 4) | hextable[0 + *str++]; - } - return ret; - } - if (is_ualpha(*str) || *str=='.' || *str=='?') - { - const char * start=str; - while (is_ualnum(*str) || *str == '.') str++; - int len=(int)(str-start); - while (*str==' ') str++; - if (*str=='(') - { - str++; - // RPG Hacker: This is only here to assure that all strings are still - // alive in memory when we call our functions further down - double result; - while (true) - { - while (*str==' ') str++; - string function_name = string(start, len); - if(functions.exists(function_name)) - { - current_user_function_name = function_name; - result = functions[function_name](); - } - else - { - str++; - break; - } - - if (*str==')') - { - str++; - return result; - } - asar_throw_error(2, error_type_block, error_id_malformed_function_call); - } - - asar_throw_error(2, error_type_block, error_id_function_not_found, start); - } - else - { - foundlabel=true; - - const char *old_start = start; - snes_label label_data = labelval(&start); - int i=(int)label_data.pos; - foundlabel_static &= label_data.is_static; - if(str < start) - { - if ((str = strchr(str, '['))) - { - string struct_name = substr(old_start, (int)(str - old_start)); - str++; - i += (int)(eval(0) * object_size(struct_name)); - } - } - - str=start; - if (i==-1) forwardlabel=true; - return (int)i&0xFFFFFF; - } - } - if (*str=='(') - { - str++; - double rval=eval(0); - if (*str != ')') asar_throw_error(2, error_type_block, error_id_mismatched_parentheses); - str++; - return rval; - } - if (*str=='%') - { - if (str[1] != '0' && str[1] != '1') asar_throw_error(2, error_type_block, error_id_invalid_binary_value); - return strtoull(str+1, const_cast(&str), 2); - } - if (*str=='\'') - { - if (!str[1]) asar_throw_error(2, error_type_block, error_id_invalid_character); - int orig_val; - str++; - str += utf8_val(&orig_val, str); - if (orig_val == -1) asar_throw_error(0, error_type_block, error_id_invalid_utf8); - if (*str != '\'') asar_throw_error(2, error_type_block, error_id_invalid_character); - int64_t rval=thetable.get_val(orig_val); - if (rval == -1) - { - // RPG Hacker: Should be fine to not check return value of codepoint_to_utf8() here, because - // our error cases above already made sure that orig_val contains valid data at this point. - string u8_str; - codepoint_to_utf8(&u8_str, orig_val); - asar_throw_error(2, error_type_block, error_id_undefined_char, u8_str.data()); - } - str++; - return rval; - } - if (is_digit(*str)) - { - const char* end = str; - while (is_digit(*end) || *end == '.') end++; - string number; - number.assign(str, (int)(end - str)); - str = end; - return atof(number); - } - asar_throw_error(2, error_type_block, error_id_invalid_number); - return 0.0; -} - -inline double sanitize(double val) -{ - if (val != val) asar_throw_error(2, error_type_block, error_id_nan); - return val; -} - -static double getnum() -{ - while (*str==' ') str++; - if(*str == '$') return sanitize(getnumcore()); //optimize for the common case -#define prefix(sym, func) if (*str == sym) { str+=1; double val=getnum(); return sanitize(func); } -#define prefix2(sym, sym2, func) if (*str == sym && *(str+1) == sym2) { str+=2; double val=getnum(); return sanitize(func); } - prefix('-', -val); - prefix('~', ~(int)val); - prefix2('<', ':', (int)val>>16); - prefix('+', val); + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, + -1, -1, -1, -1, -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 10, 11, 12, + 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}; + +static double getnumcore() { + if (*str == '$') { + if (!is_xdigit(*++str)) + asar_throw_error(2, error_type_block, error_id_invalid_hex_value); + uint64_t ret = 0; + while (hextable[0 + *str] >= 0) { + ret = (ret << 4) | hextable[0 + *str++]; + } + return ret; + } + if (is_ualpha(*str) || *str == '.' || *str == '?') { + const char* start = str; + while (is_ualnum(*str) || *str == '.') str++; + int len = (int)(str - start); + while (*str == ' ') str++; + if (*str == '(') { + str++; + // RPG Hacker: This is only here to assure that all strings are still + // alive in memory when we call our functions further down + double result; + while (true) { + while (*str == ' ') str++; + string function_name = string(start, len); + if (functions.exists(function_name)) { + current_user_function_name = function_name; + result = functions[function_name](); + } else { + str++; + break; + } + + if (*str == ')') { + str++; + return result; + } + asar_throw_error(2, error_type_block, error_id_malformed_function_call); + } + + asar_throw_error(2, error_type_block, error_id_function_not_found, start); + } else { + foundlabel = true; + + const char* old_start = start; + snes_label label_data = labelval(&start); + int i = (int)label_data.pos; + foundlabel_static &= label_data.is_static; + if (str < start) { + if ((str = strchr(str, '['))) { + string struct_name = substr(old_start, (int)(str - old_start)); + str++; + i += (int)(eval(0) * object_size(struct_name)); + } + } + + str = start; + if (i == -1) forwardlabel = true; + return (int)i & 0xFFFFFF; + } + } + if (*str == '(') { + str++; + double rval = eval(0); + if (*str != ')') + asar_throw_error(2, error_type_block, error_id_mismatched_parentheses); + str++; + return rval; + } + if (*str == '%') { + if (str[1] != '0' && str[1] != '1') + asar_throw_error(2, error_type_block, error_id_invalid_binary_value); + return strtoull(str + 1, const_cast(&str), 2); + } + if (*str == '\'') { + if (!str[1]) asar_throw_error(2, error_type_block, error_id_invalid_character); + int orig_val; + str++; + str += utf8_val(&orig_val, str); + if (orig_val == -1) + asar_throw_error(0, error_type_block, error_id_invalid_utf8); + if (*str != '\'') + asar_throw_error(2, error_type_block, error_id_invalid_character); + int64_t rval = thetable.get_val(orig_val); + if (rval == -1) { + // RPG Hacker: Should be fine to not check return value of + // codepoint_to_utf8() here, because our error cases above already made sure + // that orig_val contains valid data at this point. + string u8_str; + codepoint_to_utf8(&u8_str, orig_val); + asar_throw_error(2, error_type_block, error_id_undefined_char, + u8_str.data()); + } + str++; + return rval; + } + if (is_digit(*str)) { + const char* end = str; + while (is_digit(*end) || *end == '.') end++; + string number; + number.assign(str, (int)(end - str)); + str = end; + return atof(number); + } + asar_throw_error(2, error_type_block, error_id_invalid_number); + return 0.0; +} + +inline double sanitize(double val) { + if (val != val) asar_throw_error(2, error_type_block, error_id_nan); + return val; +} + +static double getnum() { + while (*str == ' ') str++; + if (*str == '$') return sanitize(getnumcore()); // optimize for the common case +#define prefix(sym, func) \ + if (*str == sym) { \ + str += 1; \ + double val = getnum(); \ + return sanitize(func); \ + } +#define prefix2(sym, sym2, func) \ + if (*str == sym && *(str + 1) == sym2) { \ + str += 2; \ + double val = getnum(); \ + return sanitize(func); \ + } + prefix('-', -val); + prefix('~', ~(int)val); + prefix2('<', ':', (int)val >> 16); + prefix('+', val); #undef prefix #undef prefix2 - return sanitize(getnumcore()); + return sanitize(getnumcore()); } -int64_t getnum(const char* instr) -{ - double num = math(instr); - if(num < (double)INT64_MIN) { - return INT64_MIN; - } else if(num > (double)INT64_MAX) { - return INT64_MAX; - } - return (int64_t)num; +int64_t getnum(const char* instr) { + double num = math(instr); + if (num < (double)INT64_MIN) { + return INT64_MIN; + } else if (num > (double)INT64_MAX) { + return INT64_MAX; + } + return (int64_t)num; } -// RPG Hacker: Same function as above, but doesn't truncate our number via int conversion -double getnumdouble(const char * instr) -{ - return math(instr); -} +// RPG Hacker: Same function as above, but doesn't truncate our number via int +// conversion +double getnumdouble(const char* instr) { return math(instr); } -static double oper_wrapped_throw(asar_error_id errid) -{ - asar_throw_error(2, error_type_block, errid); - return 0.0; +static double oper_wrapped_throw(asar_error_id errid) { + asar_throw_error(2, error_type_block, errid); + return 0.0; } -static double eval(int depth) -{ - const char* posneglabel = str; - string posnegname = posneglabelname(&posneglabel, false); +static double eval(int depth) { + const char* posneglabel = str; + string posnegname = posneglabelname(&posneglabel, false); - if (posnegname.length() > 0) - { - if (*posneglabel != '\0' && *posneglabel != ')') goto notposneglabel; + if (posnegname.length() > 0) { + if (*posneglabel != '\0' && *posneglabel != ')') goto notposneglabel; - str = posneglabel; + str = posneglabel; - foundlabel=true; - if (*(posneglabel-1) == '+') forwardlabel=true; - snes_label label_data = labelval(posnegname); - foundlabel_static &= label_data.is_static; - return label_data.pos & 0xFFFFFF; - } + foundlabel = true; + if (*(posneglabel - 1) == '+') forwardlabel = true; + snes_label label_data = labelval(posnegname); + foundlabel_static &= label_data.is_static; + return label_data.pos & 0xFFFFFF; + } notposneglabel: - recurseblock rec; - double left=getnum(); - double right; - while (*str==' ') str++; - while (*str && *str != ')' && *str != ','&& *str != ']') - { - while (*str==' ') str++; + recurseblock rec; + double left = getnum(); + double right; + while (*str == ' ') str++; + while (*str && *str != ')' && *str != ',' && *str != ']') { + while (*str == ' ') str++; #define oper(name, thisdepth, contents) \ - if (!strncmp(str, name, strlen(name))) \ - { \ - if (depth<=thisdepth) \ - { \ - str+=strlen(name); \ - right=eval(thisdepth+1); \ - } \ - else return left; \ - left=contents; \ - continue; \ - } - oper("**", 6, pow((double)left, (double)right)); - oper("*", 5, left*right); - oper("/", 5, right != 0.0 ? left / right : oper_wrapped_throw(error_id_division_by_zero)); - oper("%", 5, right != 0.0 ? fmod((double)left, (double)right) : oper_wrapped_throw(error_id_modulo_by_zero)); - oper("+", 4, left+right); - oper("-", 4, left-right); - oper("<<", 3, right >= 0.0 ? (int64_t)left<<(uint64_t)right : oper_wrapped_throw(error_id_negative_shift)); - oper(">>", 3, right >= 0.0 ? (int64_t)left>>(uint64_t)right : oper_wrapped_throw(error_id_negative_shift)); - - //these two needed checked early to avoid bitwise from eating a operator - oper("&&", 0, (int64_t)left&&(int64_t)right); - oper("||", 0, (int64_t)left||(int64_t)right); - oper("&", 2, (int64_t)left&(int64_t)right); - oper("|", 2, (int64_t)left|(int64_t)right); - oper("^", 2, (int64_t)left^(int64_t)right); - - oper(">=", 1, left>=right); - oper("<=", 1, left<=right); - oper(">", 1, left>right); - oper("<", 1, left= 0.0 ? (int64_t)left << (uint64_t)right + : oper_wrapped_throw(error_id_negative_shift)); + oper(">>", 3, + right >= 0.0 ? (int64_t)left >> (uint64_t)right + : oper_wrapped_throw(error_id_negative_shift)); + + // these two needed checked early to avoid bitwise from eating a operator + oper("&&", 0, (int64_t)left && (int64_t)right); + oper("||", 0, (int64_t)left || (int64_t)right); + oper("&", 2, (int64_t)left & (int64_t)right); + oper("|", 2, (int64_t)left | (int64_t)right); + oper("^", 2, (int64_t)left ^ (int64_t)right); + + oper(">=", 1, left >= right); + oper("<=", 1, left <= right); + oper(">", 1, left > right); + oper("<", 1, left < right); + oper("==", 1, left == right); + oper("!=", 1, left != right); + + asar_throw_error(2, error_type_block, error_id_unknown_operator); #undef oper - } - return left; -} - -//static autoptr freeme; -double math(const char * s) -{ - //free(freeme); - //freeme=NULL; - foundlabel=false; - foundlabel_static=true; - forwardlabel=false; - str = s; - double rval = eval(0); - if (*str) - { - if (*str == ',') asar_throw_error(2, error_type_block, error_id_invalid_input); - else asar_throw_error(2, error_type_block, error_id_mismatched_parentheses); - } - return rval; -} - -void initmathcore() -{ - functions.reset(); - builtin_functions.each([](const char* key, double (*val)()) { - functions[key] = val; - }); - user_functions.reset(); -} - -void deinitmathcore() -{ - //not needed + } + return left; +} + +// static autoptr freeme; +double math(const char* s) { + // free(freeme); + // freeme=NULL; + foundlabel = false; + foundlabel_static = true; + forwardlabel = false; + str = s; + double rval = eval(0); + if (*str) { + if (*str == ',') + asar_throw_error(2, error_type_block, error_id_invalid_input); + else + asar_throw_error(2, error_type_block, error_id_mismatched_parentheses); + } + return rval; +} + +void initmathcore() { + functions.reset(); + builtin_functions.each( + [](const char* key, double (*val)()) { functions[key] = val; }); + user_functions.reset(); +} + +void deinitmathcore() { + // not needed } diff --git a/src/asar/asar_math.h b/src/asar/asar_math.h index af10954c..ea15f34d 100644 --- a/src/asar/asar_math.h +++ b/src/asar/asar_math.h @@ -1,16 +1,18 @@ #pragma once +#include + void initmathcore(); void deinitmathcore(); -int64_t getnum(const char * str); -double getnumdouble(const char * str); +int64_t getnum(const char* str); +double getnumdouble(const char* str); -void createuserfunc(const char * name, const char * arguments, const char * content); +void createuserfunc(const char* name, const char* arguments, const char* content); void closecachedfiles(); -double math(const char * mystr); +double math(const char* mystr); extern bool foundlabel; extern bool foundlabel_static; diff --git a/src/asar/assembleblock.cpp b/src/asar/assembleblock.cpp index fed0b0ad..3e0381d8 100644 --- a/src/asar/assembleblock.cpp +++ b/src/asar/assembleblock.cpp @@ -1,17 +1,18 @@ +#include "assembleblock.h" + +#include + #include "addr2line.h" +#include "arch-shared.h" #include "asar.h" -#include "assembleblock.h" #include "asar_math.h" +#include "interface-shared.h" #include "macro.h" #include "platform/file-helpers.h" #include "table.h" #include "unicode.h" -#include - -#include "interface-shared.h" -#include "arch-shared.h" -int arch=arch_65816; +int arch = arch_65816; bool snespos_valid = false; int snespos; @@ -42,228 +43,199 @@ static bool disable_bank_cross_errors = false; static bool check_half_banks_crossed = false; int bytes; -static int freespaceuse=0; +static int freespaceuse = 0; static enum { - ratsmeta_ban, - ratsmeta_allow, - ratsmeta_used, -} ratsmetastate=ratsmeta_ban; - -enum spcblock_type{ - spcblock_nspc, - spcblock_custom -}; - -struct spcblock_data{ - unsigned int destination; - spcblock_type type; - string macro_name; - - unsigned int execute_address; - unsigned int size_address; - mapper_t old_mapper; -}spcblock; - -int snestopc_pick(int addr) -{ - return snestopc(addr); + ratsmeta_ban, + ratsmeta_allow, + ratsmeta_used, +} ratsmetastate = ratsmeta_ban; + +enum spcblock_type { spcblock_nspc, spcblock_custom }; + +struct spcblock_data { + unsigned int destination; + spcblock_type type; + string macro_name; + + unsigned int execute_address; + unsigned int size_address; + mapper_t old_mapper; +} spcblock; + +int snestopc_pick(int addr) { return snestopc(addr); } + +inline void verifysnespos() { + if (!snespos_valid) { + asar_throw_warning(0, warning_id_missing_org); + snespos = 0x008000; + realsnespos = 0x008000; + startpos = 0x008000; + realstartpos = 0x008000; + } } -inline void verifysnespos() -{ - if (!snespos_valid) - { - asar_throw_warning(0, warning_id_missing_org); - snespos=0x008000; - realsnespos=0x008000; - startpos=0x008000; - realstartpos=0x008000; - } +static int fixsnespos(int inaddr, int step) { + // randomdude999: turns out it wasn't very reliable at all. + /* // RPG Hacker: No idea how reliable this is. + // Might not work with some of the more exotic mappers. + return pctosnes(snestopc(inaddr) + step); */ + if (mapper == lorom) { + if ((inaddr & 0xFFFF) + step > 0xFFFF) { + // bank crossed + return inaddr + step + 0x8000; + } + return inaddr + step; + } else if (mapper == hirom) { + if ((inaddr & 0x400000) == 0) { + // system pages, need to account for low pages and stuff + if ((inaddr & 0xFFFF) + step > 0xFFFF) { + return inaddr + step + 0x8000; + } + } + return inaddr + step; + } else if (mapper == exlorom) { + // exlorom has no mirroring so this should work fine + return pctosnes(snestopc(inaddr) + step); + } else if (mapper == exhirom) { + // apparently exhirom is pretty similar to hirom after all + if ((inaddr & 0x400000) == 0) { + // system pages, need to account for low pages and stuff + if ((inaddr & 0xFFFF) + step > 0xFFFF) { + return inaddr + step + 0x8000; + } + } + return inaddr + step; + } else if (mapper == sa1rom) { + if ((inaddr & 0x400000) == 0) { + // lorom area + if ((inaddr & 0xFFFF) + step > 0xFFFF) { + return inaddr + step + 0x8000; + } + return inaddr + step; + } else { + // hirom area + return inaddr + step; + } + } else if (mapper == sfxrom) { + if ((inaddr & 0x400000) == 0) { + // lorom area + if ((inaddr & 0xFFFF) + step > 0xFFFF) { + return inaddr + step + 0x8000; + } + } else { + // hirom area + return inaddr + step; + } + } else if (mapper == bigsa1rom) { + // no mirrors here, so this should work + return pctosnes(snestopc(inaddr) + step); + } else if (mapper == norom) { + return inaddr + step; + } + return -1; } -static int fixsnespos(int inaddr, int step) -{ - // randomdude999: turns out it wasn't very reliable at all. - /* // RPG Hacker: No idea how reliable this is. - // Might not work with some of the more exotic mappers. - return pctosnes(snestopc(inaddr) + step); */ - if (mapper == lorom) { - if ((inaddr&0xFFFF)+step > 0xFFFF) { - // bank crossed - return inaddr+step+0x8000; - } - return inaddr+step; - } else if (mapper == hirom) { - if ((inaddr&0x400000) == 0) { - // system pages, need to account for low pages and stuff - if ((inaddr&0xFFFF)+step > 0xFFFF) { - return inaddr+step+0x8000; - } - } - return inaddr+step; - } else if (mapper == exlorom) { - // exlorom has no mirroring so this should work fine - return pctosnes(snestopc(inaddr)+step); - } else if (mapper == exhirom) { - // apparently exhirom is pretty similar to hirom after all - if ((inaddr&0x400000) == 0) { - // system pages, need to account for low pages and stuff - if ((inaddr&0xFFFF)+step > 0xFFFF) { - return inaddr+step+0x8000; - } - } - return inaddr+step; - } else if (mapper == sa1rom) { - if((inaddr&0x400000) == 0) { - // lorom area - if ((inaddr&0xFFFF)+step > 0xFFFF) { - return inaddr+step+0x8000; - } - return inaddr+step; - } else { - // hirom area - return inaddr+step; - } - } else if (mapper == sfxrom) { - if ((inaddr&0x400000) == 0) { - // lorom area - if ((inaddr&0xFFFF)+step > 0xFFFF) { - return inaddr+step+0x8000; - } - } else { - // hirom area - return inaddr+step; - } - } else if (mapper == bigsa1rom) { - // no mirrors here, so this should work - return pctosnes(snestopc(inaddr)+step); - } else if (mapper == norom) { - return inaddr+step; - } - return -1; +inline void step(int num) { + if (disable_bank_cross_errors) { + snespos = fixsnespos(snespos, num); + realsnespos = fixsnespos(realsnespos, num); + + // RPG Hacker: Not adjusting startpos here will eventually throw + // an error in checkbankcross() if we set warn bankcross on again. + // As far as I can tell, those are pretty much just used for + // checking bank crossing, anyways, so it's hopefully save to just + // adjust them here. + startpos = snespos; + realstartpos = realsnespos; + } else { + snespos += num; + realsnespos += num; + } + bytes += num; } -inline void step(int num) -{ - if (disable_bank_cross_errors) - { - snespos = fixsnespos(snespos, num); - realsnespos = fixsnespos(realsnespos, num); - - // RPG Hacker: Not adjusting startpos here will eventually throw - // an error in checkbankcross() if we set warn bankcross on again. - // As far as I can tell, those are pretty much just used for - // checking bank crossing, anyways, so it's hopefully save to just - // adjust them here. - startpos = snespos; - realstartpos = realsnespos; - } - else - { - snespos += num; - realsnespos += num; - } - bytes+=num; -} - -inline void write1_65816(unsigned int num) -{ - verifysnespos(); - if (pass==2) - { - int pcpos=snestopc(realsnespos&0xFFFFFF); - if (pcpos<0) - { - movinglabelspossible=true; - asar_throw_error(2, error_type_block, error_id_snes_address_doesnt_map_to_rom, hex((unsigned int)realsnespos, 6).data()); - } - writeromdata_byte(pcpos, (unsigned char)num); - if (pcpos>=romlen) romlen=pcpos+1; - } - step(1); - ratsmetastate=ratsmeta_ban; +inline void write1_65816(unsigned int num) { + verifysnespos(); + if (pass == 2) { + int pcpos = snestopc(realsnespos & 0xFFFFFF); + if (pcpos < 0) { + movinglabelspossible = true; + asar_throw_error(2, error_type_block, + error_id_snes_address_doesnt_map_to_rom, + hex((unsigned int)realsnespos, 6).data()); + } + writeromdata_byte(pcpos, (unsigned char)num); + if (pcpos >= romlen) romlen = pcpos + 1; + } + step(1); + ratsmetastate = ratsmeta_ban; } int recent_opcode_num = 0; -void write1_pick(unsigned int num) -{ - write1_65816(num); -} +void write1_pick(unsigned int num) { write1_65816(num); } -static bool asblock_pick(char** word, int numwords) -{ - recent_opcode_num = 1; +static bool asblock_pick(char** word, int numwords) { + recent_opcode_num = 1; - if (arch==arch_65816) return asblock_65816(word, numwords); - if (arch==arch_spc700) return asblock_spc700(word, numwords); - if (arch==arch_superfx) return asblock_superfx(word, numwords); - return true; + if (arch == arch_65816) return asblock_65816(word, numwords); + if (arch == arch_spc700) return asblock_spc700(word, numwords); + if (arch == arch_superfx) return asblock_superfx(word, numwords); + return true; } #define write1 write1_pick #define snestopc snestopc_pick -const char * safedequote(char * str) -{ - const char * tmp=dequote(str); - if (!tmp) asar_throw_error(0, error_type_block, error_id_garbage_near_quoted_string); - return tmp; +const char* safedequote(char* str) { + const char* tmp = dequote(str); + if (!tmp) + asar_throw_error(0, error_type_block, error_id_garbage_near_quoted_string); + return tmp; } extern char romtitle[30]; extern bool stdlib; -void write2(unsigned int num) -{ - write1(num); - write1(num/256); +void write2(unsigned int num) { + write1(num); + write1(num / 256); } -void write3(unsigned int num) -{ - write1(num); - write1(num/256); - write1(num/65536); +void write3(unsigned int num) { + write1(num); + write1(num / 256); + write1(num / 65536); } -void write4(unsigned int num) -{ - write1(num); - write1(num/256); - write1(num/65536); - write1(num/16777216); +void write4(unsigned int num) { + write1(num); + write1(num / 256); + write1(num / 65536); + write1(num / 16777216); } -//these are NOT used by the math parser - see math.cpp for that -int read2(int insnespos) -{ - int addr=snestopc(insnespos); - if (addr<0 || addr+2>romlen_r) return -1; - return - romdata_r[addr ] | - (romdata_r[addr+1]<< 8); +// these are NOT used by the math parser - see math.cpp for that +int read2(int insnespos) { + int addr = snestopc(insnespos); + if (addr < 0 || addr + 2 > romlen_r) return -1; + return romdata_r[addr] | (romdata_r[addr + 1] << 8); } -int read3(int insnespos) -{ - int addr=snestopc(insnespos); - if (addr<0 || addr+3>romlen_r) return -1; - return - romdata_r[addr ] | - (romdata_r[addr+1]<< 8)| - (romdata_r[addr+2]<<16); +int read3(int insnespos) { + int addr = snestopc(insnespos); + if (addr < 0 || addr + 3 > romlen_r) return -1; + return romdata_r[addr] | (romdata_r[addr + 1] << 8) | (romdata_r[addr + 2] << 16); } -int getlenfromchar(char c) -{ - c=(char)to_lower(c); - if (c=='b') return 1; - if (c=='w') return 2; - if (c=='l') return 3; - asar_throw_error(0, error_type_block, error_id_invalid_opcode_length); - return -1; +int getlenfromchar(char c) { + c = (char)to_lower(c); + if (c == 'b') return 1; + if (c == 'w') return 2; + if (c == 'l') return 3; + asar_throw_error(0, error_type_block, error_id_invalid_opcode_length); + return -1; } assocarr labels; @@ -276,257 +248,242 @@ autoarray* macroneglabels; autoarray sublabels; autoarray* macrosublabels; -// randomdude999: ns is still the string to prefix to all labels, it's calculated whenever namespace_list is changed +// randomdude999: ns is still the string to prefix to all labels, it's calculated +// whenever namespace_list is changed string ns; string ns_backup; autoarray namespace_list; -//bool fastrom=false; +// bool fastrom=false; autoarray includeonce; -bool confirmname(const char * name) -{ - if (!name[0]) return false; - if (is_digit(name[0])) return false; - for (int i=0;name[i];i++) - { - if (!is_ualnum(name[i])) return false; - } - return true; +bool confirmname(const char* name) { + if (!name[0]) return false; + if (is_digit(name[0])) return false; + for (int i = 0; name[i]; i++) { + if (!is_ualnum(name[i])) return false; + } + return true; } -string posneglabelname(const char ** input, bool define) -{ - const char* label = *input; - - string output; - - int depth = 0; - bool ismacro = false; - - if (label[0] == '?') - { - ismacro = true; - label++; - } - if (label[0] == '-' || label[0] == '+') - { - char first = label[0]; - for (depth = 0; label[0] && label[0] == first; depth++) label++; - - if (!ismacro) - { - if (first == '+') - { - *input = label; - output = STR":pos_" + dec(depth) + "_" + dec(poslabels[depth]); - if (define) poslabels[depth]++; - } - else - { - *input = label; - if (define) neglabels[depth]++; - output = STR":neg_" + dec(depth) + "_" + dec(neglabels[depth]); - } - } - else - { - if (macrorecursion == 0 || macroposlabels == nullptr || macroneglabels == nullptr) - { - if (!macrorecursion) asar_throw_error(0, error_type_block, error_id_macro_label_outside_of_macro); - } - else - { - if (first == '+') - { - *input = label; - output = STR":macro_" + dec(calledmacros) + "_pos_" + dec(depth) + "_" + dec((*macroposlabels)[depth]); - if (define) (*macroposlabels)[depth]++; - } - else - { - *input = label; - if (define) (*macroneglabels)[depth]++; - output = STR":macro_" + dec(calledmacros) + "_neg_" + dec(depth) + "_" + dec((*macroneglabels)[depth]); - } - } - } - } - - return output; +string posneglabelname(const char** input, bool define) { + const char* label = *input; + + string output; + + int depth = 0; + bool ismacro = false; + + if (label[0] == '?') { + ismacro = true; + label++; + } + if (label[0] == '-' || label[0] == '+') { + char first = label[0]; + for (depth = 0; label[0] && label[0] == first; depth++) label++; + + if (!ismacro) { + if (first == '+') { + *input = label; + output = STR ":pos_" + dec(depth) + "_" + dec(poslabels[depth]); + if (define) poslabels[depth]++; + } else { + *input = label; + if (define) neglabels[depth]++; + output = STR ":neg_" + dec(depth) + "_" + dec(neglabels[depth]); + } + } else { + if (macrorecursion == 0 || macroposlabels == nullptr || + macroneglabels == nullptr) { + if (!macrorecursion) + asar_throw_error(0, error_type_block, + error_id_macro_label_outside_of_macro); + } else { + if (first == '+') { + *input = label; + output = STR ":macro_" + dec(calledmacros) + "_pos_" + dec(depth) + + "_" + dec((*macroposlabels)[depth]); + if (define) (*macroposlabels)[depth]++; + } else { + *input = label; + if (define) (*macroneglabels)[depth]++; + output = STR ":macro_" + dec(calledmacros) + "_neg_" + dec(depth) + + "_" + dec((*macroneglabels)[depth]); + } + } + } + } + + return output; } -static string labelname(const char ** rawname, bool define=false) -{ +static string labelname(const char** rawname, bool define = false) { #define deref_rawname (*rawname) - autoarray* sublabellist = &sublabels; - - bool ismacro = (deref_rawname[0] == '?'); - bool issublabel = false; - - if (ismacro) - { - deref_rawname++; - sublabellist = macrosublabels; - } - - string name; - int i=-1; - - if (is_digit(*deref_rawname)) asar_throw_error(1, error_type_block, error_id_invalid_label_name); - if (*deref_rawname ==':') - { - deref_rawname++; - name=":"; - } - else if (!in_struct && !in_sub_struct) - { - for (i=0;(*deref_rawname =='.');i++) deref_rawname++; - if (!is_ualnum(*deref_rawname)) asar_throw_error(1, error_type_block, error_id_invalid_label_name); - if (i) - { - if (!sublabellist || !(*sublabellist)[i - 1]) asar_throw_error(1, error_type_block, error_id_label_missing_parent); - name+=STR(*sublabellist)[i-1]+"_"; - issublabel = true; - } - } - - if (ismacro && !issublabel) - { - // RPG Hacker: Don't add the prefix for sublabels, because they already inherit it from - // their parents' names. - if (!macrorecursion || macrosublabels == nullptr) asar_throw_error(1, error_type_block, error_id_macro_label_outside_of_macro); - name = STR":macro_" + dec(calledmacros) + "_" + name; - } - - - if (in_struct || in_sub_struct) - { - if(in_sub_struct) - { - name += struct_parent + "."; - } - name += struct_name; - name += '.'; - if(*deref_rawname != '.') asar_throw_error(1, error_type_block, error_id_invalid_label_name); //probably should be a better error. TODO!!! - deref_rawname++; - } - - if (!is_ualnum(*deref_rawname)) asar_throw_error(1, error_type_block, error_id_invalid_label_name); - - while (is_ualnum(*deref_rawname) || *deref_rawname == '.') - { - name+=*(deref_rawname++); - } - - if(!define && *deref_rawname == '[') - { - while (*deref_rawname && *deref_rawname != ']') deref_rawname++; - if(*deref_rawname != ']') asar_throw_error(1, error_type_block, error_id_invalid_label_missing_closer); - deref_rawname++; - if(*deref_rawname != '.') asar_throw_error(1, error_type_block, error_id_invalid_label_name); - } - - while (is_ualnum(*deref_rawname) || *deref_rawname == '.') - { - name+=*(deref_rawname++); - } - - if(*deref_rawname == '[') asar_throw_error(2, error_type_block, error_id_invalid_subscript); - - if (define && i>=0) - { - (*sublabellist).reset(i); - (*sublabellist)[i]=name; - } - return name; + autoarray* sublabellist = &sublabels; + + bool ismacro = (deref_rawname[0] == '?'); + bool issublabel = false; + + if (ismacro) { + deref_rawname++; + sublabellist = macrosublabels; + } + + string name; + int i = -1; + + if (is_digit(*deref_rawname)) + asar_throw_error(1, error_type_block, error_id_invalid_label_name); + if (*deref_rawname == ':') { + deref_rawname++; + name = ":"; + } else if (!in_struct && !in_sub_struct) { + for (i = 0; (*deref_rawname == '.'); i++) deref_rawname++; + if (!is_ualnum(*deref_rawname)) + asar_throw_error(1, error_type_block, error_id_invalid_label_name); + if (i) { + if (!sublabellist || !(*sublabellist)[i - 1]) + asar_throw_error(1, error_type_block, error_id_label_missing_parent); + name += STR(*sublabellist)[i - 1] + "_"; + issublabel = true; + } + } + + if (ismacro && !issublabel) { + // RPG Hacker: Don't add the prefix for sublabels, because they already inherit + // it from their parents' names. + if (!macrorecursion || macrosublabels == nullptr) + asar_throw_error(1, error_type_block, + error_id_macro_label_outside_of_macro); + name = STR ":macro_" + dec(calledmacros) + "_" + name; + } + + + if (in_struct || in_sub_struct) { + if (in_sub_struct) { + name += struct_parent + "."; + } + name += struct_name; + name += '.'; + if (*deref_rawname != '.') + asar_throw_error(1, error_type_block, + error_id_invalid_label_name); // probably should be a + // better error. TODO!!! + deref_rawname++; + } + + if (!is_ualnum(*deref_rawname)) + asar_throw_error(1, error_type_block, error_id_invalid_label_name); + + while (is_ualnum(*deref_rawname) || *deref_rawname == '.') { + name += *(deref_rawname++); + } + + if (!define && *deref_rawname == '[') { + while (*deref_rawname && *deref_rawname != ']') deref_rawname++; + if (*deref_rawname != ']') + asar_throw_error(1, error_type_block, + error_id_invalid_label_missing_closer); + deref_rawname++; + if (*deref_rawname != '.') + asar_throw_error(1, error_type_block, error_id_invalid_label_name); + } + + while (is_ualnum(*deref_rawname) || *deref_rawname == '.') { + name += *(deref_rawname++); + } + + if (*deref_rawname == '[') + asar_throw_error(2, error_type_block, error_id_invalid_subscript); + + if (define && i >= 0) { + (*sublabellist).reset(i); + (*sublabellist)[i] = name; + } + return name; #undef deref_rawname } -inline bool labelvalcore(const char ** rawname, snes_label * rval, bool define, bool shouldthrow) -{ - string name=labelname(rawname, define); - if (ns && labels.exists(ns+name)) {*rval = labels.find(ns+name);} - else if (labels.exists(name)) {*rval = labels.find(name);} - else - { - if (shouldthrow && pass) - { - asar_throw_error(2, error_type_block, error_id_label_not_found, name.data()); - } - rval->pos = (unsigned int)-1; - rval->is_static = false; - return false; - } - return true; +inline bool labelvalcore(const char** rawname, snes_label* rval, bool define, + bool shouldthrow) { + string name = labelname(rawname, define); + if (ns && labels.exists(ns + name)) { + *rval = labels.find(ns + name); + } else if (labels.exists(name)) { + *rval = labels.find(name); + } else { + if (shouldthrow && pass) { + asar_throw_error(2, error_type_block, error_id_label_not_found, + name.data()); + } + rval->pos = (unsigned int)-1; + rval->is_static = false; + return false; + } + return true; } -snes_label labelval(const char ** rawname, bool define) -{ - snes_label rval; - labelvalcore(rawname, &rval, define, true); - return rval; +snes_label labelval(const char** rawname, bool define) { + snes_label rval; + labelvalcore(rawname, &rval, define, true); + return rval; } -snes_label labelval(string name, bool define) -{ - const char * rawname=name; - snes_label rval; - labelvalcore(&rawname, &rval, define, true); - return rval; +snes_label labelval(string name, bool define) { + const char* rawname = name; + snes_label rval; + labelvalcore(&rawname, &rval, define, true); + return rval; } -bool labelval(const char ** rawname, snes_label * rval, bool define) -{ - return labelvalcore(rawname, rval, define, false); +bool labelval(const char** rawname, snes_label* rval, bool define) { + return labelvalcore(rawname, rval, define, false); } -bool labelval(string name, snes_label * rval, bool define) -{ - const char * str=name; - return labelvalcore(&str, rval, define, false); +bool labelval(string name, snes_label* rval, bool define) { + const char* str = name; + return labelvalcore(&str, rval, define, false); } -static void setlabel(string name, int loc=-1, bool is_static=false) -{ - if (loc==-1) - { - verifysnespos(); - loc=snespos; - } - - snes_label label_data; - label_data.pos = (unsigned int)loc; - label_data.is_static = is_static; - - unsigned int labelpos; - if (pass==0) - { - if (labels.exists(name)) - { - movinglabelspossible=true; - asar_throw_error(0, error_type_block, error_id_label_redefined, name.data()); - } - labels.create(name) = label_data; - } - else if (pass==1) - { - labels.create(name) = label_data; - } - else if (pass==2) - { - //all label locations are known at this point, add a sanity check - if (!labels.exists(name)) asar_throw_error(2, error_type_block, error_id_label_on_third_pass); - labelpos = labels.find(name).pos; - if ((int)labelpos != loc && !movinglabelspossible) - { - if((unsigned int)loc>>16 != labelpos>>16) asar_throw_error(2, error_type_block, error_id_label_ambiguous, name.raw()); - else if(labelpos == (dp_base + 0xFFu)) asar_throw_error(2, error_type_block, error_id_label_ambiguous, name.raw()); - else if(errored) return; - else asar_throw_error(2, error_type_block, error_id_label_moving); - } - } +static void setlabel(string name, int loc = -1, bool is_static = false) { + if (loc == -1) { + verifysnespos(); + loc = snespos; + } + + snes_label label_data; + label_data.pos = (unsigned int)loc; + label_data.is_static = is_static; + + unsigned int labelpos; + if (pass == 0) { + if (labels.exists(name)) { + movinglabelspossible = true; + asar_throw_error(0, error_type_block, error_id_label_redefined, + name.data()); + } + labels.create(name) = label_data; + } else if (pass == 1) { + labels.create(name) = label_data; + } else if (pass == 2) { + // all label locations are known at this point, add a sanity check + if (!labels.exists(name)) + asar_throw_error(2, error_type_block, error_id_label_on_third_pass); + labelpos = labels.find(name).pos; + if ((int)labelpos != loc && !movinglabelspossible) { + if ((unsigned int)loc >> 16 != labelpos >> 16) + asar_throw_error(2, error_type_block, error_id_label_ambiguous, + name.raw()); + else if (labelpos == (dp_base + 0xFFu)) + asar_throw_error(2, error_type_block, error_id_label_ambiguous, + name.raw()); + else if (errored) + return; + else + asar_throw_error(2, error_type_block, error_id_label_moving); + } + } } @@ -548,24 +505,21 @@ static int freespaceorglen[256]; static bool freespacestatic[256]; static unsigned char freespacebyte[256]; -static void cleartable() -{ - thetable = table(); -} +static void cleartable() { thetable = table(); } struct pushable { - int arch; - int snespos; - int snesstart; - int snesposreal; - int snesstartreal; - int freeid; - int freeex; - int freest; - int arch1; - int arch2; - int arch3; - int arch4; + int arch; + int snespos; + int snesstart; + int snesposreal; + int snesstartreal; + int freeid; + int freeex; + int freest; + int arch1; + int arch2; + int arch3; + int arch4; }; static autoarray pushpc; static int pushpcnum; @@ -574,9 +528,9 @@ static autoarray basestack; static int basestacknum; struct ns_pushable { - string ns; - autoarray namespace_list; - bool nested_namespaces; + string ns; + autoarray namespace_list; + bool nested_namespaces; }; static autoarray pushns; @@ -588,1689 +542,1687 @@ static unsigned char padbyte[12]; static bool nested_namespaces = false; -static int getfreespaceid() -{ - static const int max_num_freespaces = 125; - if (freespaceidnext > max_num_freespaces) asar_throw_error(pass, error_type_fatal, error_id_freespace_limit_reached, max_num_freespaces); - return freespaceidnext++; +static int getfreespaceid() { + static const int max_num_freespaces = 125; + if (freespaceidnext > max_num_freespaces) + asar_throw_error(pass, error_type_fatal, error_id_freespace_limit_reached, + max_num_freespaces); + return freespaceidnext++; } -void checkbankcross() -{ - if (!snespos_valid) return; - if (disable_bank_cross_errors) return; - unsigned int mask = 0x7FFF0000 | (check_half_banks_crossed ? 0x8000 : 0); - if (((snespos^ startpos) & mask) && (((snespos - 1) ^ startpos) & mask)) - { - asar_throw_error(pass, error_type_fatal, error_id_bank_border_crossed, snespos); - } - else if (((realsnespos^realstartpos) & mask) && (((realsnespos - 1) ^ realstartpos) & mask)) - { - asar_throw_error(pass, error_type_fatal, error_id_bank_border_crossed, realsnespos); - } +void checkbankcross() { + if (!snespos_valid) return; + if (disable_bank_cross_errors) return; + unsigned int mask = 0x7FFF0000 | (check_half_banks_crossed ? 0x8000 : 0); + if (((snespos ^ startpos) & mask) && (((snespos - 1) ^ startpos) & mask)) { + asar_throw_error(pass, error_type_fatal, error_id_bank_border_crossed, snespos); + } else if (((realsnespos ^ realstartpos) & mask) && + (((realsnespos - 1) ^ realstartpos) & mask)) { + asar_throw_error(pass, error_type_fatal, error_id_bank_border_crossed, + realsnespos); + } } -static void freespaceend() -{ - if ((snespos&0x7F000000) && ((unsigned int)snespos&0x80000000)==0) - { - freespacelen[freespaceid]=snespos-freespacestart+freespaceextra; - snespos=(int)0xFFFFFFFF; - snespos_valid = false; - } - freespaceextra=0; +static void freespaceend() { + if ((snespos & 0x7F000000) && ((unsigned int)snespos & 0x80000000) == 0) { + freespacelen[freespaceid] = snespos - freespacestart + freespaceextra; + snespos = (int)0xFFFFFFFF; + snespos_valid = false; + } + freespaceextra = 0; } int numopcodes; -static void adddefine(const string & key, string & value) -{ - if (!defines.exists(key)) defines.create(key) = value; +static void adddefine(const string& key, string& value) { + if (!defines.exists(key)) defines.create(key) = value; } -void initstuff() -{ - if (pass==0) - { - for (int i=0;i<256;i++) - { - freespaceleak[i]=true; - freespaceorgpos[i]=-2; - freespaceorglen[i]=-1; - freespacebyte[i] = 0x00; - } - movinglabelspossible = false; - } - arch=arch_65816; - mapper=lorom; - mapper_set = false; - calledmacros = 0; - reallycalledmacros = 0; - macrorecursion = 0; - defines.reset(); - builtindefines.each(adddefine); - clidefines.each(adddefine); - ns=""; - namespace_list.reset(); - sublabels.reset(); - poslabels.reset(); - neglabels.reset(); - macroposlabels = nullptr; - macroneglabels = nullptr; - macrosublabels = nullptr; - cleartable(); - pushpc.reset(); - pushpcnum=0; - pushns.reset(); - pushnsnum = 0; - bytes=0; - freespaceuse=0; - memset(fillbyte, 0, sizeof(fillbyte)); - memset(padbyte, 0, sizeof(padbyte)); - snespos_valid = false; - snespos=(int)0xFFFFFFFF; - realsnespos= (int)0xFFFFFFFF; - startpos= (int)0xFFFFFFFF; - realstartpos= (int)0xFFFFFFFF; - //fastrom=false; - freespaceidnext=1; - freespaceid=1; - freespaceextra=0; - numopcodes=0; - incsrcdepth = 0; - - optimizeforbank = -1; - optimize_dp = optimize_dp_flag::NONE; - dp_base = 0; - optimize_address = optimize_address_flag::DEFAULT; - - in_struct = false; - in_sub_struct = false; - in_spcblock = false; - - if (arch==arch_65816) asinit_65816(); - if (arch==arch_spc700) asinit_spc700(); - if (arch==arch_superfx) asinit_superfx(); - - disable_bank_cross_errors = false; - check_half_banks_crossed = false; - nested_namespaces = false; - - includeonce.reset(); - - extern AddressToLineMapping addressToLineMapping; - addressToLineMapping.reset(); - - push_warnings(false); - - initmathcore(); - - callstack.reset(); +void initstuff() { + if (pass == 0) { + for (int i = 0; i < 256; i++) { + freespaceleak[i] = true; + freespaceorgpos[i] = -2; + freespaceorglen[i] = -1; + freespacebyte[i] = 0x00; + } + movinglabelspossible = false; + } + arch = arch_65816; + mapper = lorom; + mapper_set = false; + calledmacros = 0; + reallycalledmacros = 0; + macrorecursion = 0; + defines.reset(); + builtindefines.each(adddefine); + clidefines.each(adddefine); + ns = ""; + namespace_list.reset(); + sublabels.reset(); + poslabels.reset(); + neglabels.reset(); + macroposlabels = nullptr; + macroneglabels = nullptr; + macrosublabels = nullptr; + cleartable(); + pushpc.reset(); + pushpcnum = 0; + pushns.reset(); + pushnsnum = 0; + bytes = 0; + freespaceuse = 0; + memset(fillbyte, 0, sizeof(fillbyte)); + memset(padbyte, 0, sizeof(padbyte)); + snespos_valid = false; + snespos = (int)0xFFFFFFFF; + realsnespos = (int)0xFFFFFFFF; + startpos = (int)0xFFFFFFFF; + realstartpos = (int)0xFFFFFFFF; + // fastrom=false; + freespaceidnext = 1; + freespaceid = 1; + freespaceextra = 0; + numopcodes = 0; + incsrcdepth = 0; + + optimizeforbank = -1; + optimize_dp = optimize_dp_flag::NONE; + dp_base = 0; + optimize_address = optimize_address_flag::DEFAULT; + + in_struct = false; + in_sub_struct = false; + in_spcblock = false; + + if (arch == arch_65816) asinit_65816(); + if (arch == arch_spc700) asinit_spc700(); + if (arch == arch_superfx) asinit_superfx(); + + disable_bank_cross_errors = false; + check_half_banks_crossed = false; + nested_namespaces = false; + + includeonce.reset(); + + extern AddressToLineMapping addressToLineMapping; + addressToLineMapping.reset(); + + push_warnings(false); + + initmathcore(); + + callstack.reset(); } -//void nerf(const string& left, string& right){puts(S left+" = "+right);} +// void nerf(const string& left, string& right){puts(S left+" = "+right);} -void finishpass() -{ - verify_warnings(); - pull_warnings(false); +void finishpass() { + verify_warnings(); + pull_warnings(false); -//defines.traverse(nerf); - if(in_spcblock) asar_throw_error(0, error_type_block, error_id_missing_endspcblock); - if (in_struct || in_sub_struct) asar_throw_error(pass, error_type_null, error_id_struct_without_endstruct); - else if (pushpcnum && pass == 0) asar_throw_error(pass, error_type_null, error_id_pushpc_without_pullpc); - else if (pushnsnum && pass == 0) asar_throw_error(pass, error_type_null, error_id_pushns_without_pullns); - freespaceend(); - if (arch==arch_65816) asend_65816(); - if (arch==arch_spc700) asend_spc700(); - if (arch==arch_superfx) asend_superfx(); + // defines.traverse(nerf); + if (in_spcblock) + asar_throw_error(0, error_type_block, error_id_missing_endspcblock); + if (in_struct || in_sub_struct) + asar_throw_error(pass, error_type_null, error_id_struct_without_endstruct); + else if (pushpcnum && pass == 0) + asar_throw_error(pass, error_type_null, error_id_pushpc_without_pullpc); + else if (pushnsnum && pass == 0) + asar_throw_error(pass, error_type_null, error_id_pushns_without_pullns); + freespaceend(); + if (arch == arch_65816) asend_65816(); + if (arch == arch_spc700) asend_spc700(); + if (arch == arch_superfx) asend_superfx(); - deinitmathcore(); + deinitmathcore(); } -static bool addlabel(const char * label, int pos=-1, bool global_label = false) -{ - if (!label[0] || label[0]==':') return false;//colons are reserved for special labels - - const char* posneglabel = label; - string posnegname = posneglabelname(&posneglabel, true); - - if (posnegname.length() > 0) - { - if (global_label) return false; - if (*posneglabel != '\0' && *posneglabel != ':') asar_throw_error(0, error_type_block, error_id_broken_label_definition); - setlabel(posnegname, pos); - return true; - } - if (label[strlen(label)-1]==':' || label[0]=='.' || label[0]=='?' || label[0] == '#') - { - if (!label[1]) return false; - if(global_label && (in_struct || in_sub_struct || label[0]=='?')) return false; - - bool define = true; - - if (label[0] == '#') - { - define = false; - label++; - } - - // RPG Hacker: Also checking label[1] now, since it might be a macro sublabel. - // Also, apparently this here doesn't account for main labels. I guess because - // we don't even get here in the first place if they don't include a colon? - bool requirecolon = (label[0] != '.' && label[1] != '.') && (in_struct || in_sub_struct); - string name=labelname(&label, define); - if (label[0]==':') label++; - else if (requirecolon) asar_throw_error(0, error_type_block, error_id_broken_label_definition); - else if (global_label) return false; - if (label[0]) asar_throw_error(0, error_type_block, error_id_broken_label_definition); - if (ns && !global_label) name=ns+name; - setlabel(name, pos, ((in_struct || in_sub_struct) && static_struct)); - return true; - } - return false; +static bool addlabel(const char* label, int pos = -1, bool global_label = false) { + if (!label[0] || label[0] == ':') + return false; // colons are reserved for special labels + + const char* posneglabel = label; + string posnegname = posneglabelname(&posneglabel, true); + + if (posnegname.length() > 0) { + if (global_label) return false; + if (*posneglabel != '\0' && *posneglabel != ':') + asar_throw_error(0, error_type_block, error_id_broken_label_definition); + setlabel(posnegname, pos); + return true; + } + if (label[strlen(label) - 1] == ':' || label[0] == '.' || label[0] == '?' || + label[0] == '#') { + if (!label[1]) return false; + if (global_label && (in_struct || in_sub_struct || label[0] == '?')) + return false; + + bool define = true; + + if (label[0] == '#') { + define = false; + label++; + } + + // RPG Hacker: Also checking label[1] now, since it might be a macro sublabel. + // Also, apparently this here doesn't account for main labels. I guess because + // we don't even get here in the first place if they don't include a colon? + bool requirecolon = + (label[0] != '.' && label[1] != '.') && (in_struct || in_sub_struct); + string name = labelname(&label, define); + if (label[0] == ':') + label++; + else if (requirecolon) + asar_throw_error(0, error_type_block, error_id_broken_label_definition); + else if (global_label) + return false; + if (label[0]) + asar_throw_error(0, error_type_block, error_id_broken_label_definition); + if (ns && !global_label) name = ns + name; + setlabel(name, pos, ((in_struct || in_sub_struct) && static_struct)); + return true; + } + return false; } static autoarray elsestatus; -int numtrue=0;//if 1 -> increase both -int numif = 0; //if 0 or inside if 0 -> increase only numif +int numtrue = 0; // if 1 -> increase both +int numif = 0; // if 0 or inside if 0 -> increase only numif autoarray whilestatus; -static void push_pc() -{ - pushpc[pushpcnum].arch=arch; - pushpc[pushpcnum].snespos=snespos; - pushpc[pushpcnum].snesstart=startpos; - pushpc[pushpcnum].snesposreal=realsnespos; - pushpc[pushpcnum].snesstartreal=realstartpos; - pushpc[pushpcnum].freeid=freespaceid; - pushpc[pushpcnum].freeex=freespaceextra; - pushpc[pushpcnum].freest=freespacestart; - pushpcnum++; +static void push_pc() { + pushpc[pushpcnum].arch = arch; + pushpc[pushpcnum].snespos = snespos; + pushpc[pushpcnum].snesstart = startpos; + pushpc[pushpcnum].snesposreal = realsnespos; + pushpc[pushpcnum].snesstartreal = realstartpos; + pushpc[pushpcnum].freeid = freespaceid; + pushpc[pushpcnum].freeex = freespaceextra; + pushpc[pushpcnum].freest = freespacestart; + pushpcnum++; } -static void pop_pc() -{ - pushpcnum--; - snespos=pushpc[pushpcnum].snespos; - startpos=pushpc[pushpcnum].snesstart; - realsnespos=pushpc[pushpcnum].snesposreal; - realstartpos=pushpc[pushpcnum].snesstartreal; - freespaceid=pushpc[pushpcnum].freeid; - freespaceextra=pushpc[pushpcnum].freeex; - freespacestart=pushpc[pushpcnum].freest; +static void pop_pc() { + pushpcnum--; + snespos = pushpc[pushpcnum].snespos; + startpos = pushpc[pushpcnum].snesstart; + realsnespos = pushpc[pushpcnum].snesposreal; + realstartpos = pushpc[pushpcnum].snesstartreal; + freespaceid = pushpc[pushpcnum].freeid; + freespaceextra = pushpc[pushpcnum].freeex; + freespacestart = pushpc[pushpcnum].freest; } -string handle_print(char* input) -{ - string out; - autoptr pars = qpsplit(input, ','); - verify_paren(pars); - for (int i = 0; pars[i]; i++) - { - if (0); - else if (pars[i][0] == '"') out += safedequote(pars[i]); - else if (!stricmp(pars[i], "bytes")) out += dec(bytes); - else if (!stricmp(pars[i], "freespaceuse")) out += dec(freespaceuse); - else if (!stricmp(pars[i], "pc")) out += hex((unsigned int)(snespos & 0xFFFFFF), 6); - else if (!strncasecmp(pars[i], "bin(", strlen("bin(")) || - !strncasecmp(pars[i], "dec(", strlen("dec(")) || - !strncasecmp(pars[i], "hex(", strlen("hex(")) || - !strncasecmp(pars[i], "double(", strlen("double("))) - { - char * arg1pos = strchr(pars[i], '(') + 1; - char * endpos = strchr(arg1pos, '\0'); - while (*endpos == ' ' || *endpos == '\0') endpos--; - if (*endpos != ')') asar_throw_error(0, error_type_block, error_id_invalid_print_function_syntax); - string paramstr = string(arg1pos, (int)(endpos - arg1pos)); - - int numargs; - autoptr params = qpsplit(paramstr.temp_raw(), ',', &numargs); - verify_paren(params); - if (numargs > 2) asar_throw_error(0, error_type_block, error_id_wrong_num_parameters); - int precision = 0; - bool hasprec = numargs == 2; - if (hasprec) - { - precision = getnum(params[1]); - if (precision < 0) precision = 0; - if (precision > 64) precision = 64; - } - *(arg1pos - 1) = '\0'; // allows more convenient comparsion functions - if (!stricmp(pars[i], "bin")) - { - // sadly printf doesn't have binary, so let's roll our own - int64_t value = getnum(params[0]); - char buffer[65]; - if (value < 0) { - out += '-'; - value = -value; - // decrement precision because we've output one char already - precision -= 1; - if (precision<0) precision = 0; - } - for (int j = 0; j < 64; j++) { - buffer[63 - j] = '0' + ((value & (1ull << j)) >> j); - } - buffer[64] = 0; - int startidx = 0; - while (startidx < 64 - precision && buffer[startidx] == '0') startidx++; - if (startidx == 64) startidx--; // always want to print at least one digit - out += buffer + startidx; - } - else if (!stricmp(pars[i], "dec")) - { - int64_t value = getnum(params[0]); - char buffer[65]; - snprintf(buffer, 65, "%0*" PRId64, precision, value); - out += buffer; - } - else if (!stricmp(pars[i], "hex")) - { - int64_t value = getnum(params[0]); - char buffer[65]; - snprintf(buffer, 65, "%0*" PRIX64, precision, value); - out += buffer; - } - else if (!stricmp(pars[i], "double")) - { - if (!hasprec) precision = 5; - out += ftostrvar(getnumdouble(params[0]), precision); - } - } - else asar_throw_error(2, error_type_block, error_id_unknown_variable); - } - return out; +string handle_print(char* input) { + string out; + autoptr pars = qpsplit(input, ','); + verify_paren(pars); + for (int i = 0; pars[i]; i++) { + if (0) + ; + else if (pars[i][0] == '"') + out += safedequote(pars[i]); + else if (!stricmp(pars[i], "bytes")) + out += dec(bytes); + else if (!stricmp(pars[i], "freespaceuse")) + out += dec(freespaceuse); + else if (!stricmp(pars[i], "pc")) + out += hex((unsigned int)(snespos & 0xFFFFFF), 6); + else if (!strncasecmp(pars[i], "bin(", strlen("bin(")) || + !strncasecmp(pars[i], "dec(", strlen("dec(")) || + !strncasecmp(pars[i], "hex(", strlen("hex(")) || + !strncasecmp(pars[i], "double(", strlen("double("))) { + char* arg1pos = strchr(pars[i], '(') + 1; + char* endpos = strchr(arg1pos, '\0'); + while (*endpos == ' ' || *endpos == '\0') endpos--; + if (*endpos != ')') + asar_throw_error(0, error_type_block, + error_id_invalid_print_function_syntax); + string paramstr = string(arg1pos, (int)(endpos - arg1pos)); + + int numargs; + autoptr params = qpsplit(paramstr.temp_raw(), ',', &numargs); + verify_paren(params); + if (numargs > 2) + asar_throw_error(0, error_type_block, error_id_wrong_num_parameters); + int precision = 0; + bool hasprec = numargs == 2; + if (hasprec) { + precision = getnum(params[1]); + if (precision < 0) precision = 0; + if (precision > 64) precision = 64; + } + *(arg1pos - 1) = '\0'; // allows more convenient comparsion functions + if (!stricmp(pars[i], "bin")) { + // sadly printf doesn't have binary, so let's roll our own + int64_t value = getnum(params[0]); + char buffer[65]; + if (value < 0) { + out += '-'; + value = -value; + // decrement precision because we've output one char already + precision -= 1; + if (precision < 0) precision = 0; + } + for (int j = 0; j < 64; j++) { + buffer[63 - j] = '0' + ((value & (1ull << j)) >> j); + } + buffer[64] = 0; + int startidx = 0; + while (startidx < 64 - precision && buffer[startidx] == '0') startidx++; + if (startidx == 64) + startidx--; // always want to print at least one digit + out += buffer + startidx; + } else if (!stricmp(pars[i], "dec")) { + int64_t value = getnum(params[0]); + char buffer[65]; + snprintf(buffer, 65, "%0*" PRId64, precision, value); + out += buffer; + } else if (!stricmp(pars[i], "hex")) { + int64_t value = getnum(params[0]); + char buffer[65]; + snprintf(buffer, 65, "%0*" PRIX64, precision, value); + out += buffer; + } else if (!stricmp(pars[i], "double")) { + if (!hasprec) precision = 5; + out += ftostrvar(getnumdouble(params[0]), precision); + } + } else + asar_throw_error(2, error_type_block, error_id_unknown_variable); + } + return out; } -void assembleblock(const char * block) -{ +void assembleblock(const char* block) { #define is(test) (!stricmpwithlower(word[0], test)) -#define is0(test) (numwords==1 && !stricmpwithlower(word[0], test)) -#define is1(test) (numwords==2 && !stricmpwithlower(word[0], test)) -#define is2(test) (numwords==3 && !stricmpwithlower(word[0], test)) -#define is3(test) (numwords==4 && !stricmpwithlower(word[0], test)) +#define is0(test) (numwords == 1 && !stricmpwithlower(word[0], test)) +#define is1(test) (numwords == 2 && !stricmpwithlower(word[0], test)) +#define is2(test) (numwords == 3 && !stricmpwithlower(word[0], test)) +#define is3(test) (numwords == 4 && !stricmpwithlower(word[0], test)) #define par word[1] - callstack_push cs_push(callstack_entry_type::BLOCK, block); - - int numwords; - string splitblock = block; - char ** word = qsplit(splitblock.temp_raw(), ' ', &numwords); - autoptr scope_word = word; - // when writing out the data for the addrToLine mapping, - // we want to write out the snespos we had before writing opcodes - int addrToLinePos = realsnespos & 0xFFFFFF; - - while (numif==numtrue && word[0] && (!word[1] || strcmp(word[1], "=")) && addlabel(word[0])) - { - word++; - numwords--; - } - if (!word[0] || !word[0][0]) return; - if (is("if") || is("elseif") || is("assert") || is("while")) - { - string errmsg; - whiletracker wstatus; - wstatus.startline = get_current_line(); - wstatus.iswhile = is("while"); - wstatus.cond = false; - whiletracker& addedwstatus = (whilestatus[numif] = wstatus); - if (is("assert")) - { - autoptr tokens = qpsplit(word[numwords - 1], ','); - verify_paren(tokens); - if (tokens[0] != NULL && tokens[1] != NULL) - { - string rawerrmsg; - size_t pos = 1; - while (tokens[pos]) - { - rawerrmsg += tokens[pos]; - if (tokens[pos + 1] != NULL) - { - rawerrmsg += ","; - } - pos++; - } - - errmsg = handle_print(rawerrmsg.raw()); - } - } - - //handle nested if statements - if (numtrue!=numif && !(is("elseif") && numtrue+1==numif)) - { - if ((is("if") || is("while"))) numif++; - return; - } - if ((is("if") || is("while"))) numif++; - - for(int i = 1; i < numwords - 1; i++) - { - word[i][strlen(word[i])] = ' '; - } - numwords = 2; - bool cond = getnum(word[1]); - if (foundlabel && !foundlabel_static && !is("assert")) asar_throw_error(1, error_type_block, error_id_label_in_conditional, word[0]); - if (is("if") || is("while")) - { - if(0); - else if (cond) - { - numtrue++; - elsestatus[numif]=true; - } - else if (!cond) - { - elsestatus[numif]=false; - } - addedwstatus.cond = cond; - } - else if (is("elseif")) - { - if (!numif) asar_throw_error(1, error_type_block, error_id_misplaced_elseif); - if (whilestatus[numif - 1].iswhile) asar_throw_error(1, error_type_block, error_id_elseif_in_while); - if (numif==numtrue) numtrue--; - if (cond && !elsestatus[numif]) - { - numtrue++; - elsestatus[numif]=true; - } - } - // otherwise, must be assert command - else if (pass == 2 && !cond) - { - if (errmsg) asar_throw_error(2, error_type_block, error_id_assertion_failed, (string(": ") + errmsg).data()); - else asar_throw_error(2, error_type_block, error_id_assertion_failed, "."); - } - } - else if (is0("endif") || is0("endwhile")) - { - if (!numif || (whilestatus[numif - 1].iswhile && is("endif"))) asar_throw_error(1, error_type_block, error_id_misplaced_endif); - if (numif==numtrue) numtrue--; - numif--; - } - else if (is0("else")) - { - if (!numif) asar_throw_error(1, error_type_block, error_id_misplaced_else); - if (whilestatus[numif - 1].iswhile) asar_throw_error(1, error_type_block, error_id_else_in_while_loop); - else if (numif==numtrue) numtrue--; - else if (numif==numtrue+1 && !elsestatus[numif]) - { - numtrue++; - elsestatus[numif]=true; - } - } - else if (numif!=numtrue) return; - else if (asblock_pick(word, numwords)) - { - if (pass == 2) - { - // RPG Hacker: This makes a pretty big assumption to calculate the size of opcodes. - // However, this should currently only really be used for pseudo opcodes, where it should always be good enough. - if (recent_opcode_num > 0) - { - int opcode_size = ((0xFFFFFF & realsnespos) - addrToLinePos) / recent_opcode_num; - for (int i = 0; i < recent_opcode_num; ++i) - { - extern AddressToLineMapping addressToLineMapping; - addressToLineMapping.includeMapping(get_current_file_name(), get_current_line() + 1, addrToLinePos + (i * opcode_size)); - } - } - } - numopcodes += recent_opcode_num; - } - else if (is1("db") || is1("dw") || is1("dl") || is1("dd")) - { - autoptr pars=qpsplit(par, ','); - verify_paren(pars); - - void (*do_write)(unsigned int); - char first = to_lower(word[0][1]); - if (first == 'b') do_write = &write1; - else if (first == 'w') do_write = &write2; - else if (first == 'l') do_write = &write3; - else do_write = &write4; - - for (int i=0;pars[i];i++) - { - if (pars[i][0]=='"') - { - char * str=const_cast(safedequote(pars[i])); - int codepoint = 0u; - str += utf8_val(&codepoint, str); - while ( codepoint != 0 && codepoint != -1 ) - { - do_write(thetable.get_val(codepoint)); - str += utf8_val(&codepoint, str); - } - if (codepoint == -1) asar_throw_error(0, error_type_block, error_id_invalid_utf8); - } - else - { - do_write((pass==2)?getnum(pars[i]):0); - } - } - } - else if(word[0][0]=='%') - { - callmacro(strchr(block, '%')+1); - } - else if (is1("undef")) - { - string def = safedequote(par); - - if (defines.exists(def)) - { - defines.remove(def); - } - else - { - asar_throw_error(0, error_type_block, error_id_define_not_found, def.data()); - } - } - else if (is0("error")) - { - asar_throw_error(0, error_type_block, error_id_error_command, "."); - } - else if (is0("warn")) - { - asar_throw_warning(2, warning_id_warn_command, "."); - } - else if (is1("error")) - { - string out = handle_print(par); - // RPG Hacker: This used to be on pass 0, which had its merits (you don't want to miss a potentially critical - // user-generated error, just because a bazillion other errors are thrown in passes before it). However, I - // don't see how to support print functions with this without moving it to pass 2. Suggestions are welcome. - asar_throw_error(2, error_type_block, error_id_error_command, (string(": ") + out).data()); - } - else if (is1("warn")) - { - string out = handle_print(par); - asar_throw_warning(2, warning_id_warn_command, (string(": ") + out).data()); - } - else if (is1("warnings")) - { - if (stricmp(word[1], "push") == 0) - { - push_warnings(); - } - else if (stricmp(word[1], "pull") == 0) - { - pull_warnings(); - } - else - { - asar_throw_error(0, error_type_block, error_id_broken_command, "warnings", "Unknown parameter"); - } - } - else if (is2("warnings")) - { - if (stricmp(word[1], "enable") == 0) - { - asar_warning_id warnid = parse_warning_id_from_string(word[2]); - - if (warnid != warning_id_end) - { - set_warning_enabled(warnid, true); - } - else - { - asar_throw_error(0, error_type_block, error_id_invalid_warning_id, word[2], "warnings enable"); - } - } - else if (stricmp(word[1], "disable") == 0) - { - asar_warning_id warnid = parse_warning_id_from_string(word[2]); - - if (warnid != warning_id_end) - { - set_warning_enabled(warnid, false); - } - else - { - asar_throw_error(0, error_type_block, error_id_invalid_warning_id, word[2], "warnings disable"); - } - } - else - { - asar_throw_error(0, error_type_block, error_id_broken_command, "warnings", "Unknown parameter"); - } - } - else if(is1("global")) - { - if (!addlabel(word[1], -1, true)) - { - asar_throw_error(1, error_type_block, error_id_invalid_global_label, word[1]); - } - } - else if (is2("check")) - { - if (stricmp(word[1], "title") == 0) - { - // RPG Hacker: Removed trimming for now - I think requiring an exact match is probably - // better here (not sure, though, it's worth discussing) - string expected_title = safedequote(word[2]); - // randomdude999: this also removes leading spaces because itrim gets stuck in an endless - // loop when multi is true and one argument is empty - // in hirom the rom needs to be an entire bank for it to have a title, other modes only need 0x8000 bytes - if (romlen < ((mapper == hirom || mapper == exhirom) ? 0x10000 : 0x8000)) // too short - { - if (!ignoretitleerrors) // title errors shouldn't be ignored - asar_throw_error(0, error_type_block, error_id_rom_too_short, expected_title.data()); - else // title errors should be ignored, throw a warning anyways - asar_throw_warning(0, warning_id_rom_too_short, expected_title.data()); - } - else { - string actual_title; - string actual_display_title; - for (int i = 0;i < 21;i++) - { - unsigned char c = romdata[snestopc(0x00FFC0 + i)]; - actual_title += (char)c; - // the replacements are from interface-cli.cpp - if (c == 7) c = 14; - if (c == 8) c = 27; - if (c == 9) c = 26; - if (c == '\r') c = 17; - if (c == '\n') c = 25; - if (c == '\0') c = 155; - actual_display_title += (char)c; - } - if (strncmp(expected_title, actual_title, 21) != 0) - { - if (!ignoretitleerrors) // title errors shouldn't be ignored - asar_throw_error(0, error_type_block, error_id_rom_title_incorrect, expected_title.data(), actual_display_title.data()); - else // title errors should be ignored, throw a warning anyways - asar_throw_warning(0, warning_id_rom_title_incorrect, expected_title.data(), actual_display_title.data()); - } - } - } - else if (stricmp(word[1], "bankcross") == 0) - { - if (0); - else if (!stricmp(word[2], "off")) - { - disable_bank_cross_errors = true; - } - else if (!stricmp(word[2], "half")) - { - disable_bank_cross_errors = false; - check_half_banks_crossed = true; - } - else if (!stricmp(word[2], "full")) - { - disable_bank_cross_errors = false; - check_half_banks_crossed = false; - } - else asar_throw_error(0, error_type_block, error_id_invalid_check); - - } - else - { - asar_throw_error(0, error_type_block, error_id_invalid_check); - } - } - else if (is0("asar") || is1("asar")) - { - if (!asarverallowed) asar_throw_error(0, error_type_block, error_id_start_of_file); - if (!par) return; - int dots=0; - int dig=0; - for (int i=0;par[i];i++) - { - if (par[i]=='.') - { - if (!dig) asar_throw_error(0, error_type_block, error_id_invalid_version_number); - dig=0; - dots++; - } - else if (is_digit(par[i])) dig++; - else asar_throw_error(0, error_type_block, error_id_invalid_version_number); - } - if (!dig || !dots || dots>2) asar_throw_error(0, error_type_block, error_id_invalid_version_number); - autoptr vers=split(par, '.'); - int vermaj=atoi(vers[0]); - if (vermaj > asarver_maj) asar_throw_error(pass, error_type_fatal, error_id_asar_too_old); - if (vermaj2) error(0, "This version of Asar is too old for this patch."); - int verminbug=atoi(vers[1]); - int tmpver=asarver_bug; - if (tmpver>9) tmpver=9; - if (asarver_min*10+tmpverasarver_min) asar_throw_error(pass, error_type_fatal, error_id_asar_too_old); - int verbug=atoi(vers[2]); - if (vermin==asarver_min && verbug>asarver_bug) asar_throw_error(pass, error_type_fatal, error_id_asar_too_old); - } - } - else if (is0("include") || is1("includefrom")) - { - if (!asarverallowed) asar_throw_error(0, error_type_block, error_id_start_of_file); - if (in_top_level_file()) - { - if (par) asar_throw_error(pass, error_type_fatal, error_id_cant_be_main_file, (string(" The main file is '") + par + "'.").data()); - else asar_throw_error(pass, error_type_fatal, error_id_cant_be_main_file, ""); - } - } - else if (is0("includeonce")) - { - const char* current_file = get_current_file_name(); - if (!file_included_once(current_file)) - { - includeonce.append(current_file); - } - } - else if (numwords==3 && !stricmp(word[1], "=")) - { - if(word[0][0] == '\'' && word[0][1]) - { - int codepoint; - const char* char_start = word[0]+1; - const char* after = char_start + utf8_val(&codepoint, char_start); - if (codepoint == -1) asar_throw_error(0, error_type_block, error_id_invalid_utf8); - if(after[0] == '\'' && after[1] == '\0') { - thetable.set_val(codepoint, getnum(word[2])); - if (foundlabel && !foundlabel_static) asar_throw_error(0, error_type_block, error_id_no_labels_here); - return; - } // todo: error checking here - } - // randomdude999: int cast b/c i'm too lazy to also mess with making setlabel() - // unsigned, besides it wouldn't matter anyways. - int num=(int)getnum(word[2]); - if (foundlabel && !foundlabel_static) asar_throw_error(0, error_type_block, error_id_label_cross_assignment); - - const char* newlabelname = word[0]; - bool ismacro = false; - - if (newlabelname[0] == '?') - { - ismacro = true; - newlabelname++; - } - - if (ismacro && macrorecursion == 0) - { - asar_throw_error(0, error_type_block, error_id_macro_label_outside_of_macro); - } - - if (!confirmname(newlabelname)) asar_throw_error(0, error_type_block, error_id_invalid_label_name); - - string completename; - - if (ismacro) - { - completename += STR":macro_" + dec(calledmacros) + "_"; - } - - completename += newlabelname; - - setlabel(ns + completename, num, true); - } - else if (assemblemapper(word, numwords)) {} - else if (is1("org")) - { - if(in_spcblock) asar_throw_error(0, error_type_block, error_id_feature_unavaliable_in_spcblock); - freespaceend(); - unsigned int num=getnum(par); - if (forwardlabel) asar_throw_error(0, error_type_block, error_id_org_label_forward); - if (num&~0xFFFFFF) asar_throw_error(1, error_type_block, error_id_snes_address_out_of_bounds, hex(num, 6).data()); - if ((mapper==lorom || mapper==exlorom) && (num&0x408000)==0x400000 && (num&0x700000)!=0x700000) asar_throw_warning(0, warning_id_set_middle_byte); - //if (fastrom) num|=0x800000; - snespos=(int)num; - realsnespos=(int)num; - startpos=(int)num; - realstartpos=(int)num; - snespos_valid = true; - } -#define ret_error(errid) { asar_throw_error(0, error_type_block, errid); return; } -#define ret_error_params(errid, ...) { asar_throw_error(0, error_type_block, errid, __VA_ARGS__); return; } - else if (is("struct")) - { - //verifysnespos(); - if (in_struct || in_sub_struct) ret_error(error_id_nested_struct); - if (numwords < 2) ret_error(error_id_missing_struct_params); - if (numwords > 4) ret_error(error_id_too_many_struct_params); - if (!confirmname(word[1])) ret_error(error_id_invalid_struct_name); - - if (structs.exists(word[1]) && pass == 0) ret_error_params(error_id_struct_redefined, word[1]); - - static_struct = false; - old_snespos = snespos; - old_startpos = startpos; - old_optimizeforbank = optimizeforbank; - old_snespos_valid = snespos_valid; - unsigned int base = 0; - if (numwords == 3) - { - static_struct = true; - base = getnum(word[2]); - - if (foundlabel && !foundlabel_static) static_struct = false; - } - - bool old_in_struct = in_struct; - bool old_in_sub_struct = in_sub_struct; - in_struct = numwords == 2 || numwords == 3; - in_sub_struct = numwords == 4; - -#define ret_error_cleanup(errid) { in_struct = old_in_struct; in_sub_struct = old_in_sub_struct; asar_throw_error(0, error_type_block, errid); return; } -#define ret_error_params_cleanup(errid, ...) { in_struct = old_in_struct; in_sub_struct = old_in_sub_struct; asar_throw_error(0, error_type_block, errid, __VA_ARGS__); return; } - - if (numwords == 3) - { - if (base&~0xFFFFFF) ret_error_params_cleanup(error_id_snes_address_out_of_bounds, hex((unsigned int)base, 6).data()); - snespos = (int)base; - startpos = (int)base; - } - else if (numwords == 4) - { - if (strcasecmp(word[2], "extends")) ret_error_cleanup(error_id_missing_extends); - if (!confirmname(word[3])) ret_error_cleanup(error_id_struct_invalid_parent_name); - string tmp_struct_parent = word[3]; - - if (!structs.exists(tmp_struct_parent)) ret_error_params_cleanup(error_id_struct_not_found, tmp_struct_parent.data()); - snes_struct structure = structs.find(tmp_struct_parent); - - static_struct = structure.is_static; - struct_parent = tmp_struct_parent; - snespos = structure.base_end; - startpos = structure.base_end; - } - - push_pc(); - - optimizeforbank = -1; - - struct_name = word[1]; - struct_base = snespos; - realsnespos = 0; - realstartpos = 0; - snespos_valid = true; + callstack_push cs_push(callstack_entry_type::BLOCK, block); + + int numwords; + string splitblock = block; + char** word = qsplit(splitblock.temp_raw(), ' ', &numwords); + autoptr scope_word = word; + // when writing out the data for the addrToLine mapping, + // we want to write out the snespos we had before writing opcodes + int addrToLinePos = realsnespos & 0xFFFFFF; + + while (numif == numtrue && word[0] && (!word[1] || strcmp(word[1], "=")) && + addlabel(word[0])) { + word++; + numwords--; + } + if (!word[0] || !word[0][0]) return; + if (is("if") || is("elseif") || is("assert") || is("while")) { + string errmsg; + whiletracker wstatus; + wstatus.startline = get_current_line(); + wstatus.iswhile = is("while"); + wstatus.cond = false; + whiletracker& addedwstatus = (whilestatus[numif] = wstatus); + if (is("assert")) { + autoptr tokens = qpsplit(word[numwords - 1], ','); + verify_paren(tokens); + if (tokens[0] != NULL && tokens[1] != NULL) { + string rawerrmsg; + size_t pos = 1; + while (tokens[pos]) { + rawerrmsg += tokens[pos]; + if (tokens[pos + 1] != NULL) { + rawerrmsg += ","; + } + pos++; + } + + errmsg = handle_print(rawerrmsg.raw()); + } + } + + // handle nested if statements + if (numtrue != numif && !(is("elseif") && numtrue + 1 == numif)) { + if ((is("if") || is("while"))) numif++; + return; + } + if ((is("if") || is("while"))) numif++; + + for (int i = 1; i < numwords - 1; i++) { + word[i][strlen(word[i])] = ' '; + } + numwords = 2; + bool cond = getnum(word[1]); + if (foundlabel && !foundlabel_static && !is("assert")) + asar_throw_error(1, error_type_block, error_id_label_in_conditional, + word[0]); + if (is("if") || is("while")) { + if (0) + ; + else if (cond) { + numtrue++; + elsestatus[numif] = true; + } else if (!cond) { + elsestatus[numif] = false; + } + addedwstatus.cond = cond; + } else if (is("elseif")) { + if (!numif) + asar_throw_error(1, error_type_block, error_id_misplaced_elseif); + if (whilestatus[numif - 1].iswhile) + asar_throw_error(1, error_type_block, error_id_elseif_in_while); + if (numif == numtrue) numtrue--; + if (cond && !elsestatus[numif]) { + numtrue++; + elsestatus[numif] = true; + } + } + // otherwise, must be assert command + else if (pass == 2 && !cond) { + if (errmsg) + asar_throw_error(2, error_type_block, error_id_assertion_failed, + (string(": ") + errmsg).data()); + else + asar_throw_error(2, error_type_block, error_id_assertion_failed, "."); + } + } else if (is0("endif") || is0("endwhile")) { + if (!numif || (whilestatus[numif - 1].iswhile && is("endif"))) + asar_throw_error(1, error_type_block, error_id_misplaced_endif); + if (numif == numtrue) numtrue--; + numif--; + } else if (is0("else")) { + if (!numif) asar_throw_error(1, error_type_block, error_id_misplaced_else); + if (whilestatus[numif - 1].iswhile) + asar_throw_error(1, error_type_block, error_id_else_in_while_loop); + else if (numif == numtrue) + numtrue--; + else if (numif == numtrue + 1 && !elsestatus[numif]) { + numtrue++; + elsestatus[numif] = true; + } + } else if (numif != numtrue) + return; + else if (asblock_pick(word, numwords)) { + if (pass == 2) { + // RPG Hacker: This makes a pretty big assumption to calculate the size of + // opcodes. However, this should currently only really be used for pseudo + // opcodes, where it should always be good enough. + if (recent_opcode_num > 0) { + int opcode_size = + ((0xFFFFFF & realsnespos) - addrToLinePos) / recent_opcode_num; + for (int i = 0; i < recent_opcode_num; ++i) { + extern AddressToLineMapping addressToLineMapping; + addressToLineMapping.includeMapping( + get_current_file_name(), get_current_line() + 1, + addrToLinePos + (i * opcode_size)); + } + } + } + numopcodes += recent_opcode_num; + } else if (is1("db") || is1("dw") || is1("dl") || is1("dd")) { + autoptr pars = qpsplit(par, ','); + verify_paren(pars); + + void (*do_write)(unsigned int); + char first = to_lower(word[0][1]); + if (first == 'b') + do_write = &write1; + else if (first == 'w') + do_write = &write2; + else if (first == 'l') + do_write = &write3; + else + do_write = &write4; + + for (int i = 0; pars[i]; i++) { + if (pars[i][0] == '"') { + char* str = const_cast(safedequote(pars[i])); + int codepoint = 0u; + str += utf8_val(&codepoint, str); + while (codepoint != 0 && codepoint != -1) { + do_write(thetable.get_val(codepoint)); + str += utf8_val(&codepoint, str); + } + if (codepoint == -1) + asar_throw_error(0, error_type_block, error_id_invalid_utf8); + } else { + do_write((pass == 2) ? getnum(pars[i]) : 0); + } + } + } else if (word[0][0] == '%') { + callmacro(strchr(block, '%') + 1); + } else if (is1("undef")) { + string def = safedequote(par); + + if (defines.exists(def)) { + defines.remove(def); + } else { + asar_throw_error(0, error_type_block, error_id_define_not_found, + def.data()); + } + } else if (is0("error")) { + asar_throw_error(0, error_type_block, error_id_error_command, "."); + } else if (is0("warn")) { + asar_throw_warning(2, warning_id_warn_command, "."); + } else if (is1("error")) { + string out = handle_print(par); + // RPG Hacker: This used to be on pass 0, which had its merits (you don't want + // to miss a potentially critical user-generated error, just because a bazillion + // other errors are thrown in passes before it). However, I don't see how to + // support print functions with this without moving it to pass 2. Suggestions + // are welcome. + asar_throw_error(2, error_type_block, error_id_error_command, + (string(": ") + out).data()); + } else if (is1("warn")) { + string out = handle_print(par); + asar_throw_warning(2, warning_id_warn_command, (string(": ") + out).data()); + } else if (is1("warnings")) { + if (stricmp(word[1], "push") == 0) { + push_warnings(); + } else if (stricmp(word[1], "pull") == 0) { + pull_warnings(); + } else { + asar_throw_error(0, error_type_block, error_id_broken_command, "warnings", + "Unknown parameter"); + } + } else if (is2("warnings")) { + if (stricmp(word[1], "enable") == 0) { + asar_warning_id warnid = parse_warning_id_from_string(word[2]); + + if (warnid != warning_id_end) { + set_warning_enabled(warnid, true); + } else { + asar_throw_error(0, error_type_block, error_id_invalid_warning_id, + word[2], "warnings enable"); + } + } else if (stricmp(word[1], "disable") == 0) { + asar_warning_id warnid = parse_warning_id_from_string(word[2]); + + if (warnid != warning_id_end) { + set_warning_enabled(warnid, false); + } else { + asar_throw_error(0, error_type_block, error_id_invalid_warning_id, + word[2], "warnings disable"); + } + } else { + asar_throw_error(0, error_type_block, error_id_broken_command, "warnings", + "Unknown parameter"); + } + } else if (is1("global")) { + if (!addlabel(word[1], -1, true)) { + asar_throw_error(1, error_type_block, error_id_invalid_global_label, + word[1]); + } + } else if (is2("check")) { + if (stricmp(word[1], "title") == 0) { + // RPG Hacker: Removed trimming for now - I think requiring an exact match + // is probably better here (not sure, though, it's worth discussing) + string expected_title = safedequote(word[2]); + // randomdude999: this also removes leading spaces because itrim gets stuck + // in an endless loop when multi is true and one argument is empty in hirom + // the rom needs to be an entire bank for it to have a title, other modes + // only need 0x8000 bytes + if (romlen < ((mapper == hirom || mapper == exhirom) + ? 0x10000 + : 0x8000)) // too short + { + if (!ignoretitleerrors) // title errors shouldn't be ignored + asar_throw_error(0, error_type_block, error_id_rom_too_short, + expected_title.data()); + else // title errors should be ignored, throw a warning anyways + asar_throw_warning(0, warning_id_rom_too_short, + expected_title.data()); + } else { + string actual_title; + string actual_display_title; + for (int i = 0; i < 21; i++) { + unsigned char c = romdata[snestopc(0x00FFC0 + i)]; + actual_title += (char)c; + // the replacements are from interface-cli.cpp + if (c == 7) c = 14; + if (c == 8) c = 27; + if (c == 9) c = 26; + if (c == '\r') c = 17; + if (c == '\n') c = 25; + if (c == '\0') c = 155; + actual_display_title += (char)c; + } + if (strncmp(expected_title, actual_title, 21) != 0) { + if (!ignoretitleerrors) // title errors shouldn't be ignored + asar_throw_error( + 0, error_type_block, error_id_rom_title_incorrect, + expected_title.data(), actual_display_title.data()); + else // title errors should be ignored, throw a warning anyways + asar_throw_warning(0, warning_id_rom_title_incorrect, + expected_title.data(), + actual_display_title.data()); + } + } + } else if (stricmp(word[1], "bankcross") == 0) { + if (0) + ; + else if (!stricmp(word[2], "off")) { + disable_bank_cross_errors = true; + } else if (!stricmp(word[2], "half")) { + disable_bank_cross_errors = false; + check_half_banks_crossed = true; + } else if (!stricmp(word[2], "full")) { + disable_bank_cross_errors = false; + check_half_banks_crossed = false; + } else + asar_throw_error(0, error_type_block, error_id_invalid_check); + + } else { + asar_throw_error(0, error_type_block, error_id_invalid_check); + } + } else if (is0("asar") || is1("asar")) { + if (!asarverallowed) + asar_throw_error(0, error_type_block, error_id_start_of_file); + if (!par) return; + int dots = 0; + int dig = 0; + for (int i = 0; par[i]; i++) { + if (par[i] == '.') { + if (!dig) + asar_throw_error(0, error_type_block, + error_id_invalid_version_number); + dig = 0; + dots++; + } else if (is_digit(par[i])) + dig++; + else + asar_throw_error(0, error_type_block, error_id_invalid_version_number); + } + if (!dig || !dots || dots > 2) + asar_throw_error(0, error_type_block, error_id_invalid_version_number); + autoptr vers = split(par, '.'); + int vermaj = atoi(vers[0]); + if (vermaj > asarver_maj) + asar_throw_error(pass, error_type_fatal, error_id_asar_too_old); + if (vermaj < asarver_maj) return; + if (dots == 1) { + if (strlen(vers[1]) != 2) + asar_throw_error(0, error_type_block, error_id_invalid_version_number); + // if (asarver_min<10 && asarver_bug<10 && strlen(vers[1])>2) error(0, "This + // version of Asar is too old for this patch."); + int verminbug = atoi(vers[1]); + int tmpver = asarver_bug; + if (tmpver > 9) tmpver = 9; + if (asarver_min * 10 + tmpver < verminbug) + asar_throw_error(pass, error_type_fatal, error_id_asar_too_old); + } else { + int vermin = atoi(vers[1]); + if (vermin > asarver_min) + asar_throw_error(pass, error_type_fatal, error_id_asar_too_old); + int verbug = atoi(vers[2]); + if (vermin == asarver_min && verbug > asarver_bug) + asar_throw_error(pass, error_type_fatal, error_id_asar_too_old); + } + } else if (is0("include") || is1("includefrom")) { + if (!asarverallowed) + asar_throw_error(0, error_type_block, error_id_start_of_file); + if (in_top_level_file()) { + if (par) + asar_throw_error(pass, error_type_fatal, error_id_cant_be_main_file, + (string(" The main file is '") + par + "'.").data()); + else + asar_throw_error(pass, error_type_fatal, error_id_cant_be_main_file, + ""); + } + } else if (is0("includeonce")) { + const char* current_file = get_current_file_name(); + if (!file_included_once(current_file)) { + includeonce.append(current_file); + } + } else if (numwords == 3 && !stricmp(word[1], "=")) { + if (word[0][0] == '\'' && word[0][1]) { + int codepoint; + const char* char_start = word[0] + 1; + const char* after = char_start + utf8_val(&codepoint, char_start); + if (codepoint == -1) + asar_throw_error(0, error_type_block, error_id_invalid_utf8); + if (after[0] == '\'' && after[1] == '\0') { + thetable.set_val(codepoint, getnum(word[2])); + if (foundlabel && !foundlabel_static) + asar_throw_error(0, error_type_block, error_id_no_labels_here); + return; + } // todo: error checking here + } + // randomdude999: int cast b/c i'm too lazy to also mess with making setlabel() + // unsigned, besides it wouldn't matter anyways. + int num = (int)getnum(word[2]); + if (foundlabel && !foundlabel_static) + asar_throw_error(0, error_type_block, error_id_label_cross_assignment); + + const char* newlabelname = word[0]; + bool ismacro = false; + + if (newlabelname[0] == '?') { + ismacro = true; + newlabelname++; + } + + if (ismacro && macrorecursion == 0) { + asar_throw_error(0, error_type_block, + error_id_macro_label_outside_of_macro); + } + + if (!confirmname(newlabelname)) + asar_throw_error(0, error_type_block, error_id_invalid_label_name); + + string completename; + + if (ismacro) { + completename += STR ":macro_" + dec(calledmacros) + "_"; + } + + completename += newlabelname; + + setlabel(ns + completename, num, true); + } else if (assemblemapper(word, numwords)) { + } else if (is1("org")) { + if (in_spcblock) + asar_throw_error(0, error_type_block, + error_id_feature_unavaliable_in_spcblock); + freespaceend(); + unsigned int num = getnum(par); + if (forwardlabel) + asar_throw_error(0, error_type_block, error_id_org_label_forward); + if (num & ~0xFFFFFF) + asar_throw_error(1, error_type_block, error_id_snes_address_out_of_bounds, + hex(num, 6).data()); + if ((mapper == lorom || mapper == exlorom) && (num & 0x408000) == 0x400000 && + (num & 0x700000) != 0x700000) + asar_throw_warning(0, warning_id_set_middle_byte); + // if (fastrom) num|=0x800000; + snespos = (int)num; + realsnespos = (int)num; + startpos = (int)num; + realstartpos = (int)num; + snespos_valid = true; + } +#define ret_error(errid) \ + { \ + asar_throw_error(0, error_type_block, errid); \ + return; \ + } +#define ret_error_params(errid, ...) \ + { \ + asar_throw_error(0, error_type_block, errid, __VA_ARGS__); \ + return; \ + } + else if (is("struct")) { + // verifysnespos(); + if (in_struct || in_sub_struct) ret_error(error_id_nested_struct); + if (numwords < 2) ret_error(error_id_missing_struct_params); + if (numwords > 4) ret_error(error_id_too_many_struct_params); + if (!confirmname(word[1])) ret_error(error_id_invalid_struct_name); + + if (structs.exists(word[1]) && pass == 0) + ret_error_params(error_id_struct_redefined, word[1]); + + static_struct = false; + old_snespos = snespos; + old_startpos = startpos; + old_optimizeforbank = optimizeforbank; + old_snespos_valid = snespos_valid; + unsigned int base = 0; + if (numwords == 3) { + static_struct = true; + base = getnum(word[2]); + + if (foundlabel && !foundlabel_static) static_struct = false; + } + + bool old_in_struct = in_struct; + bool old_in_sub_struct = in_sub_struct; + in_struct = numwords == 2 || numwords == 3; + in_sub_struct = numwords == 4; + +#define ret_error_cleanup(errid) \ + { \ + in_struct = old_in_struct; \ + in_sub_struct = old_in_sub_struct; \ + asar_throw_error(0, error_type_block, errid); \ + return; \ + } +#define ret_error_params_cleanup(errid, ...) \ + { \ + in_struct = old_in_struct; \ + in_sub_struct = old_in_sub_struct; \ + asar_throw_error(0, error_type_block, errid, __VA_ARGS__); \ + return; \ + } + + if (numwords == 3) { + if (base & ~0xFFFFFF) + ret_error_params_cleanup(error_id_snes_address_out_of_bounds, + hex((unsigned int)base, 6).data()); + snespos = (int)base; + startpos = (int)base; + } else if (numwords == 4) { + if (strcasecmp(word[2], "extends")) + ret_error_cleanup(error_id_missing_extends); + if (!confirmname(word[3])) + ret_error_cleanup(error_id_struct_invalid_parent_name); + string tmp_struct_parent = word[3]; + + if (!structs.exists(tmp_struct_parent)) + ret_error_params_cleanup(error_id_struct_not_found, + tmp_struct_parent.data()); + snes_struct structure = structs.find(tmp_struct_parent); + + static_struct = structure.is_static; + struct_parent = tmp_struct_parent; + snespos = structure.base_end; + startpos = structure.base_end; + } + + push_pc(); + + optimizeforbank = -1; + + struct_name = word[1]; + struct_base = snespos; + realsnespos = 0; + realstartpos = 0; + snespos_valid = true; #undef ret_error_cleanup #undef ret_error_params_cleanup - } - else if (is("endstruct")) - { - if (numwords != 1 && numwords != 3) ret_error(error_id_invalid_endstruct_count); - if (numwords == 3 && strcasecmp(word[1], "align")) ret_error(error_id_expected_align); - if (!in_struct && !in_sub_struct) ret_error(error_id_endstruct_without_struct); - - int alignment = numwords == 3 ? (int)getnum(word[2]) : 1; - if (alignment < 1) ret_error(error_id_alignment_too_small); - - snes_struct structure; - structure.base_end = snespos; - structure.struct_size = alignment * ((snespos - struct_base + alignment - 1) / alignment); - structure.object_size = structure.struct_size; - structure.is_static = static_struct; - - if (in_struct) - { - structs.create(struct_name) = structure; - } - else if (in_sub_struct) - { - snes_struct parent; - parent = structs.find(struct_parent); - - if (parent.object_size < parent.struct_size + structure.struct_size) { - parent.object_size = parent.struct_size + structure.struct_size; - } - - structs.create(struct_parent + "." + struct_name) = structure; - structs.create(struct_parent) = parent; - } - - pop_pc(); - in_struct = false; - in_sub_struct = false; - snespos = old_snespos; - startpos = old_startpos; - optimizeforbank = old_optimizeforbank; - snespos_valid = old_snespos_valid; - static_struct = false; - } + } else if (is("endstruct")) { + if (numwords != 1 && numwords != 3) ret_error(error_id_invalid_endstruct_count); + if (numwords == 3 && strcasecmp(word[1], "align")) + ret_error(error_id_expected_align); + if (!in_struct && !in_sub_struct) ret_error(error_id_endstruct_without_struct); + + int alignment = numwords == 3 ? (int)getnum(word[2]) : 1; + if (alignment < 1) ret_error(error_id_alignment_too_small); + + snes_struct structure; + structure.base_end = snespos; + structure.struct_size = + alignment * ((snespos - struct_base + alignment - 1) / alignment); + structure.object_size = structure.struct_size; + structure.is_static = static_struct; + + if (in_struct) { + structs.create(struct_name) = structure; + } else if (in_sub_struct) { + snes_struct parent; + parent = structs.find(struct_parent); + + if (parent.object_size < parent.struct_size + structure.struct_size) { + parent.object_size = parent.struct_size + structure.struct_size; + } + + structs.create(struct_parent + "." + struct_name) = structure; + structs.create(struct_parent) = parent; + } + + pop_pc(); + in_struct = false; + in_sub_struct = false; + snespos = old_snespos; + startpos = old_startpos; + optimizeforbank = old_optimizeforbank; + snespos_valid = old_snespos_valid; + static_struct = false; + } #undef ret_error - else if(is("spcblock")) - { - //banned features when active: org, freespace(and variants), arch, mapper,namespace,pushns - if(arch != arch_spc700) asar_throw_error(0, error_type_block, error_id_spcblock_bad_arch); - if(in_struct || in_sub_struct) asar_throw_error(0, error_type_block, error_id_spcblock_inside_struct); - if(numwords < 2) asar_throw_error(0, error_type_block, error_id_spcblock_too_few_args); - if(numwords > 4) asar_throw_error(0, error_type_block, error_id_spcblock_too_many_args); - - spcblock.destination = getnum(par); - spcblock.type = spcblock_nspc; - spcblock.macro_name = ""; - - if (spcblock.destination&~0xFFFF) asar_throw_error(0, error_type_block, error_id_snes_address_out_of_bounds, hex(spcblock.destination, 6).data()); - - if(numwords == 3) - { - if(!stricmp(word[2], "nspc")) spcblock.type = spcblock_nspc; - else if(!stricmp(word[2], "custom")) asar_throw_error(0, error_type_block, error_id_custom_spcblock_missing_macro); - else asar_throw_error(0, error_type_block, error_id_unknown_spcblock_type); - } - else if(numwords == 4) - { - if(!stricmp(word[2], "custom")) spcblock.type = spcblock_custom; - else asar_throw_error(0, error_type_block, error_id_extra_spcblock_arg_for_type); - - if(macros.exists(word[3])) - { - macrodata *macro = macros.find(word[3]); - if(!macro->variadic) asar_throw_error(0, error_type_block, error_id_spcblock_macro_must_be_varadic); - if(macro->numargs != 3) asar_throw_error(0, error_type_block, error_id_spcblock_macro_invalid_static_args); - spcblock.macro_name = word[3]; - } - else asar_throw_error(0, error_type_block, error_id_spcblock_macro_doesnt_exist); - } - - switch(spcblock.type) - { - case spcblock_nspc: - spcblock.size_address=realsnespos; - write2(0x0000); - write2(spcblock.destination); - snespos=(int)spcblock.destination; - startpos=(int)spcblock.destination; - spcblock.execute_address = -1u; - break; - case spcblock_custom: - //this is a todo that probably won't be ready for 1.9 - //mostly so we can leverage some cleanups we make in 2.0 for practicality - asar_throw_error(0, error_type_block, error_id_spcblock_custom_types_incomplete); - push_pc(); - spcblock.old_mapper = mapper; - mapper = norom; - break; - default: - asar_throw_error(0, error_type_fatal, error_id_internal_error, "invalid spcblock type"); - } - - ns_backup = ns; - ns = STR":SPCBLOCK:_" + ns_backup; - in_spcblock = true; - } - else if(is0("endspcblock")) - { - if(!in_spcblock) asar_throw_error(0, error_type_block, error_id_endspcblock_without_spcblock); - - switch(spcblock.type) - { - case spcblock_nspc: - if (pass==2) - { - int pcpos=snestopc(spcblock.size_address&0xFFFFFF); - if (pcpos<0) asar_throw_error(2, error_type_block, error_id_snes_address_doesnt_map_to_rom, hex((unsigned int)realsnespos, 6).data()); - int num=snespos-startpos; - writeromdata_byte(pcpos, (unsigned char)num); - writeromdata_byte(pcpos+1, (unsigned char)(num >> 8)); - } - if(spcblock.execute_address != -1u) - { - write2(0x0000); - write2((unsigned int)spcblock.execute_address); - } - break; - case spcblock_custom: - mapper = spcblock.old_mapper; - pop_pc(); - break; - default: - asar_throw_error(0, error_type_fatal, error_id_internal_error, "invalid spcblock type"); - } - ns = ns_backup; - in_spcblock = false; - } - else if (is1("startpos")) - { - if(!in_spcblock) asar_throw_error(0, error_type_block, error_id_startpos_without_spcblock); - spcblock.execute_address=getnum(par); - } - else if (is1("base")) - { - if (!stricmp(par, "off")) - { - snespos=realsnespos; - startpos=realstartpos; - snespos_valid = realsnespos >= 0; - return; - } - unsigned int num=getnum(par); - if (forwardlabel) asar_throw_error(0, error_type_block, error_id_base_label_invalid); - if (num&~0xFFFFFF) asar_throw_error(1, error_type_block, error_id_snes_address_out_of_bounds, hex((unsigned int)num).data()); - snespos=(int)num; - startpos=(int)num; - optimizeforbank=-1; - snespos_valid = realsnespos >= 0; - } - else if (is1("dpbase")) - { - unsigned int num=(int)getnum(par); - if (forwardlabel) asar_throw_error(0, error_type_block, error_id_base_label_invalid); - if (num&~0xFF00) asar_throw_error(1, error_type_block, error_id_bad_dp_base, hex((unsigned int)num, 6).data()); - dp_base = (int)num; - } - else if (is2("optimize")) - { - if (!stricmp(par, "dp")) - { - if (!stricmp(word[2], "none")) - { - optimize_dp = optimize_dp_flag::NONE; - return; - } - if (!stricmp(word[2], "ram")) - { - optimize_dp = optimize_dp_flag::RAM; - return; - } - if (!stricmp(word[2], "always")) - { - optimize_dp = optimize_dp_flag::ALWAYS; - return; - } - asar_throw_error(1, error_type_block, error_id_bad_dp_optimize, word[2]); - } - if (!stricmp(par, "address")) - { - if (!stricmp(word[2], "default")) - { - optimize_address = optimize_address_flag::DEFAULT; - return; - } - if (!stricmp(word[2], "ram")) - { - optimize_address = optimize_address_flag::RAM; - return; - } - if (!stricmp(word[2], "mirrors")) - { - optimize_address = optimize_address_flag::MIRRORS; - return; - } - asar_throw_error(1, error_type_block, error_id_bad_address_optimize, word[2]); - } - asar_throw_error(1, error_type_block, error_id_bad_optimize, par); - } - else if (is1("bank")) - { - if (!stricmp(par, "auto")) - { - optimizeforbank=-1; - return; - } - if (!stricmp(par, "noassume")) - { - optimizeforbank=0x100; - return; - } - unsigned int num=getnum(par); - //if (forwardlabel) error(0, "bank Label is not valid"); - //if (foundlabel) num>>=16; - if (num&~0x0000FF) asar_throw_error(1, error_type_block, error_id_snes_address_out_of_bounds, hex((unsigned int)num, 6).data()); - optimizeforbank=(int)num; - } - else if (is("freespace") || is("freecode") || is("freedata")) - { - if(in_spcblock) asar_throw_error(0, error_type_block, error_id_feature_unavaliable_in_spcblock); - string parstr; - if (numwords==1) parstr="\n";//crappy hack: impossible character to cut out extra commas - else if (numwords==2) parstr=word[1]; - else asar_throw_error(0, error_type_block, error_id_invalid_freespace_request); - if (is("freecode")) parstr=STR"ram,"+parstr; - if (is("freedata")) parstr=STR"noram,"+parstr; - autoptr pars=split(parstr.temp_raw(), ','); - unsigned char fsbyte = 0x00; - int useram=-1; - bool fixedpos=false; - bool align=false; - bool leakwarn=true; - for (int i=0;pars[i];i++) - { - if (pars[i][0]=='\n') {} - else if (!stricmp(pars[i], "ram")) - { - if (useram!=-1) asar_throw_error(0, error_type_block, error_id_invalid_freespace_request); - useram=1; - } - else if (!stricmp(pars[i], "noram")) - { - if (useram!=-1) asar_throw_error(0, error_type_block, error_id_invalid_freespace_request); - useram=0; - } - else if (!stricmp(pars[i], "static")) - { - if (fixedpos) asar_throw_error(0, error_type_block, error_id_invalid_freespace_request); - fixedpos=true; - } - else if (!stricmp(pars[i], "align")) - { - if (align) asar_throw_error(0, error_type_block, error_id_invalid_freespace_request); - align=true; - } - else if (!stricmp(pars[i], "cleaned")) - { - if (!leakwarn) asar_throw_error(0, error_type_block, error_id_invalid_freespace_request); - leakwarn=false; - } - else - { - fsbyte = (unsigned char)getnum(pars[i]); - } - } - if (useram==-1) asar_throw_error(0, error_type_block, error_id_invalid_freespace_request); - if (mapper == norom) asar_throw_error(0, error_type_block, error_id_no_freespace_norom); - freespaceend(); - freespaceid=getfreespaceid(); - freespacebyte[freespaceid] = fsbyte; - if (pass==0) snespos=(freespaceid<<24)|0x8000; - if (pass==1) - { - if (fixedpos && freespaceorgpos[freespaceid]<0) - { - freespacepos[freespaceid]=0x008000; - freespaceleak[freespaceid]=false;//mute some other errors - asar_throw_error(1, error_type_block, error_id_static_freespace_autoclean); - } - if (fixedpos && freespaceorgpos[freespaceid]) freespacepos[freespaceid]=snespos=(freespaceid<<24)|freespaceorgpos[freespaceid]; - else freespacepos[freespaceid]=snespos=(freespaceid<<24)|getsnesfreespace(freespacelen[freespaceid], (useram != 0), true, true, align, freespacebyte[freespaceid]); - } - if (pass==2) - { - if (fixedpos && freespaceorgpos[freespaceid]==-1) return;//to kill some errors - snespos=(freespaceid<<24)|freespacepos[freespaceid]; - resizerats(snespos&0xFFFFFF, freespacelen[freespaceid]); - if (freespaceleak[freespaceid] && leakwarn) asar_throw_warning(2, warning_id_freespace_leaked); - if (fixedpos && freespaceorgpos[freespaceid]>0 && freespacelen[freespaceid]>freespaceorglen[freespaceid]) - asar_throw_error(2, error_type_block, error_id_static_freespace_growing); - freespaceuse+=8+freespacelen[freespaceid]; - } - freespacestatic[freespaceid]=fixedpos; - if (snespos < 0 && mapper == sa1rom) asar_throw_error(pass, error_type_fatal, error_id_no_freespace_in_mapped_banks, dec(freespacelen[freespaceid]).data()); - if (snespos < 0) asar_throw_error(pass, error_type_fatal, error_id_no_freespace, dec(freespacelen[freespaceid]).data()); - bytes+=8; - freespacestart=snespos; - startpos=snespos; - realstartpos=snespos; - realsnespos=snespos; - optimizeforbank=-1; - ratsmetastate=ratsmeta_allow; - snespos_valid = true; - } - else if (is1("prot")) - { - if(in_spcblock) asar_throw_error(0, error_type_block, error_id_feature_unavaliable_in_spcblock); - if (!ratsmetastate) asar_throw_error(2, error_type_block, error_id_prot_not_at_freespace_start); - if (ratsmetastate==ratsmeta_used) step(-5); - int num; - autoptr pars=qpsplit(par, ',', &num); - verify_paren(pars); - write1('P'); - write1('R'); - write1('O'); - write1('T'); - if (num * 3 > 255) asar_throw_error(0, error_type_block, error_id_prot_too_many_entries); - write1((unsigned int)(num*3)); - for (int i=0;i>24]=false; - } - write1('S'); - write1('T'); - write1('O'); - write1('P'); - write1(0); - ratsmetastate=ratsmeta_used; - } - else if (is1("autoclean") || is2("autoclean")) - { - if(in_spcblock) asar_throw_error(0, error_type_block, error_id_feature_unavaliable_in_spcblock); - if (numwords==3) - { - if ((unsigned int)snespos & 0xFF000000) asar_throw_error(0, error_type_block, error_id_autoclean_in_freespace); - const char * labeltest = word[2]; - string testlabel = labeltest; - int num=(int)labelval(&labeltest).pos; - if (*labeltest) asar_throw_error(0, error_type_block, error_id_label_not_found, testlabel.data()); - unsigned char targetid=(unsigned char)(num>>24); - if (pass==1) freespaceleak[targetid]=false; - num&=0xFFFFFF; - if (strlen(par)>3 && !stricmp(par+3, ".l")) par[3]=0; - if (!stricmp(par, "JSL") || !stricmp(par, "JML")) - { - int orgpos=read3(snespos+1); - int ratsloc=freespaceorgpos[targetid]; - int firstbyte = ((!stricmp(par, "JSL")) ? 0x22 : 0x5C); - int pcpos=snestopc(snespos); - if (/*pass==1 && */pcpos>=0 && pcpos=num || start<0) - { - asar_throw_error(2, error_type_block, error_id_autoclean_label_at_freespace_end); - } - - extern AddressToLineMapping addressToLineMapping; - addressToLineMapping.includeMapping(get_current_file_name(), get_current_line() + 1, addrToLinePos); - } - //freespaceorglen[targetid]=read2(ratsloc-4)+1; - freespaceorgpos[targetid]=ratsloc; - } - else if (!stricmp(par, "dl")) - { - int orgpos=read3(snespos); - int ratsloc=freespaceorgpos[targetid]; - int start=ratsstart(num); - if (pass==1 && num>=0) - { - ratsloc=ratsstart(orgpos)+8; - if (!freespacestatic[targetid]) removerats(orgpos, freespacebyte[targetid]); - } - else if (!ratsloc) ratsloc=0; - if ((start==num || start<0) && pass==2) - asar_throw_error(2, error_type_block, error_id_autoclean_label_at_freespace_end); - write3((unsigned int)num); - freespaceorgpos[targetid]=ratsloc; - freespaceorglen[targetid]=read2(ratsloc-4)+1; - } - else asar_throw_error(0, error_type_block, error_id_broken_autoclean); - } - else if (pass==0) removerats((int)getnum(word[1]), 0x00); - } - else if (is0("pushpc")) - { - verifysnespos(); - pushpc[pushpcnum].arch=arch; - pushpc[pushpcnum].snespos=snespos; - pushpc[pushpcnum].snesstart=startpos; - pushpc[pushpcnum].snesposreal=realsnespos; - pushpc[pushpcnum].snesstartreal=realstartpos; - pushpc[pushpcnum].freeid=freespaceid; - pushpc[pushpcnum].freeex=freespaceextra; - pushpc[pushpcnum].freest=freespacestart; - pushpcnum++; - snespos=(int)0xFFFFFFFF; - startpos= (int)0xFFFFFFFF; - realsnespos= (int)0xFFFFFFFF; - realstartpos= (int)0xFFFFFFFF; - snespos_valid = false; - } - else if (is0("pullpc")) - { - if (!pushpcnum) asar_throw_error(0, error_type_block, error_id_pullpc_without_pushpc); - pushpcnum--; - freespaceend(); - if (arch != pushpc[pushpcnum].arch) asar_throw_error(0, error_type_block, error_id_pullpc_different_arch); - snespos=pushpc[pushpcnum].snespos; - startpos=pushpc[pushpcnum].snesstart; - realsnespos=pushpc[pushpcnum].snesposreal; - realstartpos=pushpc[pushpcnum].snesstartreal; - freespaceid=pushpc[pushpcnum].freeid; - freespaceextra=pushpc[pushpcnum].freeex; - freespacestart=pushpc[pushpcnum].freest; - snespos_valid = true; - } - else if (is0("pushbase")) - { - basestack[basestacknum] = snespos; - basestacknum++; - } - else if (is0("pullbase")) - { - if (!basestacknum) asar_throw_error(0, error_type_block, error_id_pullbase_without_pushbase); - basestacknum--; - snespos = basestack[basestacknum]; - startpos = basestack[basestacknum]; - - if (snespos != realstartpos) - { - optimizeforbank = -1; - } - } - else if (is0("pushns")) - { - if(in_spcblock) asar_throw_error(0, error_type_block, error_id_feature_unavaliable_in_spcblock); - pushns[pushnsnum].ns = ns; - for(int i = 0; i < namespace_list.count; i++) - { - pushns[pushnsnum].namespace_list.append(namespace_list[i]); - } - pushns[pushnsnum].nested_namespaces = nested_namespaces; - pushnsnum++; - - namespace_list.reset(); - ns = ""; - nested_namespaces = false; - } - else if (is0("pullns")) - { - if(in_spcblock) asar_throw_error(0, error_type_block, error_id_feature_unavaliable_in_spcblock); - if (!pushnsnum) asar_throw_error(0, error_type_block, error_id_pullns_without_pushns); - pushnsnum--; - ns = pushns[pushnsnum].ns; - nested_namespaces = pushns[pushnsnum].nested_namespaces; - namespace_list.reset(); - for(int i = 0; i < pushns[pushnsnum].namespace_list.count; i++) - { - namespace_list.append(pushns[pushnsnum].namespace_list[i]); - } - } - else if (is1("namespace") || is2("namespace")) - { - if(in_spcblock) asar_throw_error(0, error_type_block, error_id_feature_unavaliable_in_spcblock); - bool leave = false; - if (par) - { - if (!stricmp(par, "off")) - { - if (word[2]) asar_throw_error(0, error_type_block, error_id_invalid_namespace_use); - leave = true; - } - else if (!stricmp(par, "nested")) - { - if (!word[2]) asar_throw_error(0, error_type_block, error_id_invalid_namespace_use); - else if (!stricmp(word[2], "on")) nested_namespaces = true; - else if (!stricmp(word[2], "off")) nested_namespaces = false; - } - else - { - if (word[2]) asar_throw_error(0, error_type_block, error_id_invalid_namespace_use); - const char * tmpstr= safedequote(par); - if (!confirmname(tmpstr)) asar_throw_error(0, error_type_block, error_id_invalid_namespace_name); - if (!nested_namespaces) - { - namespace_list.reset(); - } - namespace_list.append(tmpstr); - } - } - else - { - leave = true; - } - - if (leave) - { - if (nested_namespaces) - { - namespace_list.remove(namespace_list.count - 1); - } - else - { - namespace_list.reset(); - } - } - - // recompute ns - ns = ""; - for (int i = 0; i < namespace_list.count; i++) - { - ns += namespace_list[i]; - ns += "_"; - } - } - else if (is1("warnpc")) - { - unsigned int maxpos=getnum(par); - if ((unsigned int)snespos & 0xFF000000) asar_throw_error(0, error_type_block, error_id_warnpc_in_freespace); - if ((unsigned int)maxpos & 0xFF000000) asar_throw_error(0, error_type_block, error_id_warnpc_broken_param); - if ((unsigned int)snespos > maxpos) asar_throw_error(0, error_type_block, error_id_warnpc_failed, hex((unsigned int)snespos).data(), hex((unsigned int)maxpos, 6).data()); - } + else if (is("spcblock")) { + // banned features when active: org, freespace(and variants), arch, + // mapper,namespace,pushns + if (arch != arch_spc700) + asar_throw_error(0, error_type_block, error_id_spcblock_bad_arch); + if (in_struct || in_sub_struct) + asar_throw_error(0, error_type_block, error_id_spcblock_inside_struct); + if (numwords < 2) + asar_throw_error(0, error_type_block, error_id_spcblock_too_few_args); + if (numwords > 4) + asar_throw_error(0, error_type_block, error_id_spcblock_too_many_args); + + spcblock.destination = getnum(par); + spcblock.type = spcblock_nspc; + spcblock.macro_name = ""; + + if (spcblock.destination & ~0xFFFF) + asar_throw_error(0, error_type_block, error_id_snes_address_out_of_bounds, + hex(spcblock.destination, 6).data()); + + if (numwords == 3) { + if (!stricmp(word[2], "nspc")) + spcblock.type = spcblock_nspc; + else if (!stricmp(word[2], "custom")) + asar_throw_error(0, error_type_block, + error_id_custom_spcblock_missing_macro); + else + asar_throw_error(0, error_type_block, error_id_unknown_spcblock_type); + } else if (numwords == 4) { + if (!stricmp(word[2], "custom")) + spcblock.type = spcblock_custom; + else + asar_throw_error(0, error_type_block, + error_id_extra_spcblock_arg_for_type); + + if (macros.exists(word[3])) { + macrodata* macro = macros.find(word[3]); + if (!macro->variadic) + asar_throw_error(0, error_type_block, + error_id_spcblock_macro_must_be_varadic); + if (macro->numargs != 3) + asar_throw_error(0, error_type_block, + error_id_spcblock_macro_invalid_static_args); + spcblock.macro_name = word[3]; + } else + asar_throw_error(0, error_type_block, + error_id_spcblock_macro_doesnt_exist); + } + + switch (spcblock.type) { + case spcblock_nspc: + spcblock.size_address = realsnespos; + write2(0x0000); + write2(spcblock.destination); + snespos = (int)spcblock.destination; + startpos = (int)spcblock.destination; + spcblock.execute_address = -1u; + break; + case spcblock_custom: + // this is a todo that probably won't be ready for 1.9 + // mostly so we can leverage some cleanups we make in 2.0 for + // practicality + asar_throw_error(0, error_type_block, + error_id_spcblock_custom_types_incomplete); + push_pc(); + spcblock.old_mapper = mapper; + mapper = norom; + break; + default: + asar_throw_error(0, error_type_fatal, error_id_internal_error, + "invalid spcblock type"); + } + + ns_backup = ns; + ns = STR ":SPCBLOCK:_" + ns_backup; + in_spcblock = true; + } else if (is0("endspcblock")) { + if (!in_spcblock) + asar_throw_error(0, error_type_block, + error_id_endspcblock_without_spcblock); + + switch (spcblock.type) { + case spcblock_nspc: + if (pass == 2) { + int pcpos = snestopc(spcblock.size_address & 0xFFFFFF); + if (pcpos < 0) + asar_throw_error(2, error_type_block, + error_id_snes_address_doesnt_map_to_rom, + hex((unsigned int)realsnespos, 6).data()); + int num = snespos - startpos; + writeromdata_byte(pcpos, (unsigned char)num); + writeromdata_byte(pcpos + 1, (unsigned char)(num >> 8)); + } + if (spcblock.execute_address != -1u) { + write2(0x0000); + write2((unsigned int)spcblock.execute_address); + } + break; + case spcblock_custom: + mapper = spcblock.old_mapper; + pop_pc(); + break; + default: + asar_throw_error(0, error_type_fatal, error_id_internal_error, + "invalid spcblock type"); + } + ns = ns_backup; + in_spcblock = false; + } else if (is1("startpos")) { + if (!in_spcblock) + asar_throw_error(0, error_type_block, error_id_startpos_without_spcblock); + spcblock.execute_address = getnum(par); + } else if (is1("base")) { + if (!stricmp(par, "off")) { + snespos = realsnespos; + startpos = realstartpos; + snespos_valid = realsnespos >= 0; + return; + } + unsigned int num = getnum(par); + if (forwardlabel) + asar_throw_error(0, error_type_block, error_id_base_label_invalid); + if (num & ~0xFFFFFF) + asar_throw_error(1, error_type_block, error_id_snes_address_out_of_bounds, + hex((unsigned int)num).data()); + snespos = (int)num; + startpos = (int)num; + optimizeforbank = -1; + snespos_valid = realsnespos >= 0; + } else if (is1("dpbase")) { + unsigned int num = (int)getnum(par); + if (forwardlabel) + asar_throw_error(0, error_type_block, error_id_base_label_invalid); + if (num & ~0xFF00) + asar_throw_error(1, error_type_block, error_id_bad_dp_base, + hex((unsigned int)num, 6).data()); + dp_base = (int)num; + } else if (is2("optimize")) { + if (!stricmp(par, "dp")) { + if (!stricmp(word[2], "none")) { + optimize_dp = optimize_dp_flag::NONE; + return; + } + if (!stricmp(word[2], "ram")) { + optimize_dp = optimize_dp_flag::RAM; + return; + } + if (!stricmp(word[2], "always")) { + optimize_dp = optimize_dp_flag::ALWAYS; + return; + } + asar_throw_error(1, error_type_block, error_id_bad_dp_optimize, word[2]); + } + if (!stricmp(par, "address")) { + if (!stricmp(word[2], "default")) { + optimize_address = optimize_address_flag::DEFAULT; + return; + } + if (!stricmp(word[2], "ram")) { + optimize_address = optimize_address_flag::RAM; + return; + } + if (!stricmp(word[2], "mirrors")) { + optimize_address = optimize_address_flag::MIRRORS; + return; + } + asar_throw_error(1, error_type_block, error_id_bad_address_optimize, + word[2]); + } + asar_throw_error(1, error_type_block, error_id_bad_optimize, par); + } else if (is1("bank")) { + if (!stricmp(par, "auto")) { + optimizeforbank = -1; + return; + } + if (!stricmp(par, "noassume")) { + optimizeforbank = 0x100; + return; + } + unsigned int num = getnum(par); + // if (forwardlabel) error(0, "bank Label is not valid"); + // if (foundlabel) num>>=16; + if (num & ~0x0000FF) + asar_throw_error(1, error_type_block, error_id_snes_address_out_of_bounds, + hex((unsigned int)num, 6).data()); + optimizeforbank = (int)num; + } else if (is("freespace") || is("freecode") || is("freedata")) { + if (in_spcblock) + asar_throw_error(0, error_type_block, + error_id_feature_unavaliable_in_spcblock); + string parstr; + if (numwords == 1) + parstr = "\n"; // crappy hack: impossible character to cut out extra commas + else if (numwords == 2) + parstr = word[1]; + else + asar_throw_error(0, error_type_block, error_id_invalid_freespace_request); + if (is("freecode")) parstr = STR "ram," + parstr; + if (is("freedata")) parstr = STR "noram," + parstr; + autoptr pars = split(parstr.temp_raw(), ','); + unsigned char fsbyte = 0x00; + int useram = -1; + bool fixedpos = false; + bool align = false; + bool leakwarn = true; + for (int i = 0; pars[i]; i++) { + if (pars[i][0] == '\n') { + } else if (!stricmp(pars[i], "ram")) { + if (useram != -1) + asar_throw_error(0, error_type_block, + error_id_invalid_freespace_request); + useram = 1; + } else if (!stricmp(pars[i], "noram")) { + if (useram != -1) + asar_throw_error(0, error_type_block, + error_id_invalid_freespace_request); + useram = 0; + } else if (!stricmp(pars[i], "static")) { + if (fixedpos) + asar_throw_error(0, error_type_block, + error_id_invalid_freespace_request); + fixedpos = true; + } else if (!stricmp(pars[i], "align")) { + if (align) + asar_throw_error(0, error_type_block, + error_id_invalid_freespace_request); + align = true; + } else if (!stricmp(pars[i], "cleaned")) { + if (!leakwarn) + asar_throw_error(0, error_type_block, + error_id_invalid_freespace_request); + leakwarn = false; + } else { + fsbyte = (unsigned char)getnum(pars[i]); + } + } + if (useram == -1) + asar_throw_error(0, error_type_block, error_id_invalid_freespace_request); + if (mapper == norom) + asar_throw_error(0, error_type_block, error_id_no_freespace_norom); + freespaceend(); + freespaceid = getfreespaceid(); + freespacebyte[freespaceid] = fsbyte; + if (pass == 0) snespos = (freespaceid << 24) | 0x8000; + if (pass == 1) { + if (fixedpos && freespaceorgpos[freespaceid] < 0) { + freespacepos[freespaceid] = 0x008000; + freespaceleak[freespaceid] = false; // mute some other errors + asar_throw_error(1, error_type_block, + error_id_static_freespace_autoclean); + } + if (fixedpos && freespaceorgpos[freespaceid]) + freespacepos[freespaceid] = snespos = + (freespaceid << 24) | freespaceorgpos[freespaceid]; + else + freespacepos[freespaceid] = snespos = + (freespaceid << 24) | + getsnesfreespace(freespacelen[freespaceid], (useram != 0), true, + true, align, freespacebyte[freespaceid]); + } + if (pass == 2) { + if (fixedpos && freespaceorgpos[freespaceid] == -1) + return; // to kill some errors + snespos = (freespaceid << 24) | freespacepos[freespaceid]; + resizerats(snespos & 0xFFFFFF, freespacelen[freespaceid]); + if (freespaceleak[freespaceid] && leakwarn) + asar_throw_warning(2, warning_id_freespace_leaked); + if (fixedpos && freespaceorgpos[freespaceid] > 0 && + freespacelen[freespaceid] > freespaceorglen[freespaceid]) + asar_throw_error(2, error_type_block, + error_id_static_freespace_growing); + freespaceuse += 8 + freespacelen[freespaceid]; + } + freespacestatic[freespaceid] = fixedpos; + if (snespos < 0 && mapper == sa1rom) + asar_throw_error(pass, error_type_fatal, + error_id_no_freespace_in_mapped_banks, + dec(freespacelen[freespaceid]).data()); + if (snespos < 0) + asar_throw_error(pass, error_type_fatal, error_id_no_freespace, + dec(freespacelen[freespaceid]).data()); + bytes += 8; + freespacestart = snespos; + startpos = snespos; + realstartpos = snespos; + realsnespos = snespos; + optimizeforbank = -1; + ratsmetastate = ratsmeta_allow; + snespos_valid = true; + } else if (is1("prot")) { + if (in_spcblock) + asar_throw_error(0, error_type_block, + error_id_feature_unavaliable_in_spcblock); + if (!ratsmetastate) + asar_throw_error(2, error_type_block, error_id_prot_not_at_freespace_start); + if (ratsmetastate == ratsmeta_used) step(-5); + int num; + autoptr pars = qpsplit(par, ',', &num); + verify_paren(pars); + write1('P'); + write1('R'); + write1('O'); + write1('T'); + if (num * 3 > 255) + asar_throw_error(0, error_type_block, error_id_prot_too_many_entries); + write1((unsigned int)(num * 3)); + for (int i = 0; i < num; i++) { + // int num=getnum(pars[i]); + const char* labeltest = pars[i]; + string testlabel = labeltest; + int labelnum = (int)labelval(&labeltest).pos; + if (*labeltest) + asar_throw_error(0, error_type_block, error_id_label_not_found, + testlabel.data()); + write3((unsigned int)labelnum); + if (pass == 1) freespaceleak[labelnum >> 24] = false; + } + write1('S'); + write1('T'); + write1('O'); + write1('P'); + write1(0); + ratsmetastate = ratsmeta_used; + } else if (is1("autoclean") || is2("autoclean")) { + if (in_spcblock) + asar_throw_error(0, error_type_block, + error_id_feature_unavaliable_in_spcblock); + if (numwords == 3) { + if ((unsigned int)snespos & 0xFF000000) + asar_throw_error(0, error_type_block, error_id_autoclean_in_freespace); + const char* labeltest = word[2]; + string testlabel = labeltest; + int num = (int)labelval(&labeltest).pos; + if (*labeltest) + asar_throw_error(0, error_type_block, error_id_label_not_found, + testlabel.data()); + unsigned char targetid = (unsigned char)(num >> 24); + if (pass == 1) freespaceleak[targetid] = false; + num &= 0xFFFFFF; + if (strlen(par) > 3 && !stricmp(par + 3, ".l")) par[3] = 0; + if (!stricmp(par, "JSL") || !stricmp(par, "JML")) { + int orgpos = read3(snespos + 1); + int ratsloc = freespaceorgpos[targetid]; + int firstbyte = ((!stricmp(par, "JSL")) ? 0x22 : 0x5C); + int pcpos = snestopc(snespos); + if (/*pass==1 && */ pcpos >= 0 && pcpos < romlen_r && + romdata_r[pcpos] == firstbyte) { + ratsloc = ratsstart(orgpos) + 8; + freespaceorglen[targetid] = read2(ratsloc - 4) + 1; + if (!freespacestatic[targetid] && pass == 1) + removerats(orgpos, freespacebyte[targetid]); + } else if (ratsloc < 0) + ratsloc = 0; + write1((unsigned int)firstbyte); + write3((unsigned int)num); + if (pass == 2) { + int start = ratsstart(num); + if (start >= num || start < 0) { + asar_throw_error(2, error_type_block, + error_id_autoclean_label_at_freespace_end); + } + + extern AddressToLineMapping addressToLineMapping; + addressToLineMapping.includeMapping(get_current_file_name(), + get_current_line() + 1, + addrToLinePos); + } + // freespaceorglen[targetid]=read2(ratsloc-4)+1; + freespaceorgpos[targetid] = ratsloc; + } else if (!stricmp(par, "dl")) { + int orgpos = read3(snespos); + int ratsloc = freespaceorgpos[targetid]; + int start = ratsstart(num); + if (pass == 1 && num >= 0) { + ratsloc = ratsstart(orgpos) + 8; + if (!freespacestatic[targetid]) + removerats(orgpos, freespacebyte[targetid]); + } else if (!ratsloc) + ratsloc = 0; + if ((start == num || start < 0) && pass == 2) + asar_throw_error(2, error_type_block, + error_id_autoclean_label_at_freespace_end); + write3((unsigned int)num); + freespaceorgpos[targetid] = ratsloc; + freespaceorglen[targetid] = read2(ratsloc - 4) + 1; + } else + asar_throw_error(0, error_type_block, error_id_broken_autoclean); + } else if (pass == 0) + removerats((int)getnum(word[1]), 0x00); + } else if (is0("pushpc")) { + verifysnespos(); + pushpc[pushpcnum].arch = arch; + pushpc[pushpcnum].snespos = snespos; + pushpc[pushpcnum].snesstart = startpos; + pushpc[pushpcnum].snesposreal = realsnespos; + pushpc[pushpcnum].snesstartreal = realstartpos; + pushpc[pushpcnum].freeid = freespaceid; + pushpc[pushpcnum].freeex = freespaceextra; + pushpc[pushpcnum].freest = freespacestart; + pushpcnum++; + snespos = (int)0xFFFFFFFF; + startpos = (int)0xFFFFFFFF; + realsnespos = (int)0xFFFFFFFF; + realstartpos = (int)0xFFFFFFFF; + snespos_valid = false; + } else if (is0("pullpc")) { + if (!pushpcnum) + asar_throw_error(0, error_type_block, error_id_pullpc_without_pushpc); + pushpcnum--; + freespaceend(); + if (arch != pushpc[pushpcnum].arch) + asar_throw_error(0, error_type_block, error_id_pullpc_different_arch); + snespos = pushpc[pushpcnum].snespos; + startpos = pushpc[pushpcnum].snesstart; + realsnespos = pushpc[pushpcnum].snesposreal; + realstartpos = pushpc[pushpcnum].snesstartreal; + freespaceid = pushpc[pushpcnum].freeid; + freespaceextra = pushpc[pushpcnum].freeex; + freespacestart = pushpc[pushpcnum].freest; + snespos_valid = true; + } else if (is0("pushbase")) { + basestack[basestacknum] = snespos; + basestacknum++; + } else if (is0("pullbase")) { + if (!basestacknum) + asar_throw_error(0, error_type_block, error_id_pullbase_without_pushbase); + basestacknum--; + snespos = basestack[basestacknum]; + startpos = basestack[basestacknum]; + + if (snespos != realstartpos) { + optimizeforbank = -1; + } + } else if (is0("pushns")) { + if (in_spcblock) + asar_throw_error(0, error_type_block, + error_id_feature_unavaliable_in_spcblock); + pushns[pushnsnum].ns = ns; + for (int i = 0; i < namespace_list.count; i++) { + pushns[pushnsnum].namespace_list.append(namespace_list[i]); + } + pushns[pushnsnum].nested_namespaces = nested_namespaces; + pushnsnum++; + + namespace_list.reset(); + ns = ""; + nested_namespaces = false; + } else if (is0("pullns")) { + if (in_spcblock) + asar_throw_error(0, error_type_block, + error_id_feature_unavaliable_in_spcblock); + if (!pushnsnum) + asar_throw_error(0, error_type_block, error_id_pullns_without_pushns); + pushnsnum--; + ns = pushns[pushnsnum].ns; + nested_namespaces = pushns[pushnsnum].nested_namespaces; + namespace_list.reset(); + for (int i = 0; i < pushns[pushnsnum].namespace_list.count; i++) { + namespace_list.append(pushns[pushnsnum].namespace_list[i]); + } + } else if (is1("namespace") || is2("namespace")) { + if (in_spcblock) + asar_throw_error(0, error_type_block, + error_id_feature_unavaliable_in_spcblock); + bool leave = false; + if (par) { + if (!stricmp(par, "off")) { + if (word[2]) + asar_throw_error(0, error_type_block, + error_id_invalid_namespace_use); + leave = true; + } else if (!stricmp(par, "nested")) { + if (!word[2]) + asar_throw_error(0, error_type_block, + error_id_invalid_namespace_use); + else if (!stricmp(word[2], "on")) + nested_namespaces = true; + else if (!stricmp(word[2], "off")) + nested_namespaces = false; + } else { + if (word[2]) + asar_throw_error(0, error_type_block, + error_id_invalid_namespace_use); + const char* tmpstr = safedequote(par); + if (!confirmname(tmpstr)) + asar_throw_error(0, error_type_block, + error_id_invalid_namespace_name); + if (!nested_namespaces) { + namespace_list.reset(); + } + namespace_list.append(tmpstr); + } + } else { + leave = true; + } + + if (leave) { + if (nested_namespaces) { + namespace_list.remove(namespace_list.count - 1); + } else { + namespace_list.reset(); + } + } + + // recompute ns + ns = ""; + for (int i = 0; i < namespace_list.count; i++) { + ns += namespace_list[i]; + ns += "_"; + } + } else if (is1("warnpc")) { + unsigned int maxpos = getnum(par); + if ((unsigned int)snespos & 0xFF000000) + asar_throw_error(0, error_type_block, error_id_warnpc_in_freespace); + if ((unsigned int)maxpos & 0xFF000000) + asar_throw_error(0, error_type_block, error_id_warnpc_broken_param); + if ((unsigned int)snespos > maxpos) + asar_throw_error(0, error_type_block, error_id_warnpc_failed, + hex((unsigned int)snespos).data(), + hex((unsigned int)maxpos, 6).data()); + } #ifdef SANDBOX - else if (is("incsrc") || is("incbin") || is("table")) - { - asar_throw_error(0, error_type_block, error_id_command_disabled); - } + else if (is("incsrc") || is("incbin") || is("table")) { + asar_throw_error(0, error_type_block, error_id_command_disabled); + } #endif - else if (is1("incsrc")) - { - const char* current_file = get_current_file_name(); - string name; - // RPG Hacker: Should this also throw on absolute paths? - // E.g., on something starting with C:/ or whatever. - if (strchr(par, '\\')) - { - asar_throw_error(0, error_type_block, error_id_platform_paths); - } - name=safedequote(par); - assemblefile(name); - } - else if (is1("incbin") || is3("incbin")) - { - if (numwords == 4 && strcmp(word[2], "->")) asar_throw_error(0, error_type_block, error_id_broken_incbin); - int len; - int start=0; - int end=0; - if (strqchr(par, ':')) - { - char * lengths=strqchr(par, ':'); - *lengths=0; - lengths++; - if (strchr(lengths, '"')) asar_throw_error(0, error_type_block, error_id_broken_incbin); - if(*lengths=='(') { - char* tmp = strqpchr(lengths, '-'); - if(!tmp || (*(tmp-1)!=')')) asar_throw_error(0, error_type_block, error_id_broken_incbin); - start = (int)getnum(string(lengths+1, tmp-1-lengths-1)); - if (foundlabel && !foundlabel_static) asar_throw_error(0, error_type_block, error_id_no_labels_here); - lengths = tmp; - } else { - start=(int)strtoul(lengths, &lengths, 16); - } - if (*lengths!='-') asar_throw_error(0, error_type_block, error_id_broken_incbin); - lengths++; - if(*lengths=='(') { - char* tmp = strchr(lengths, '\0'); - if(*(tmp-1)!=')') asar_throw_error(0, error_type_block, error_id_broken_incbin); - end = (int)getnum(string(lengths+1, tmp-1-lengths-1)); - if (foundlabel && !foundlabel_static) asar_throw_error(0, error_type_block, error_id_no_labels_here); - // no need to check end-of-string here - } else { - end=(int)strtoul(lengths, &lengths, 16); - if (*lengths) asar_throw_error(0, error_type_block, error_id_broken_incbin); - } - } - const char* current_file = get_current_file_name(); - string name; - // RPG Hacker: Should this also throw on absolute paths? - // E.g., on something starting with C:/ or whatever. - if (strchr(par, '\\')) - { - asar_throw_error(0, error_type_block, error_id_platform_paths); - } - name = safedequote(par); - char * data;//I couldn't find a way to get this into an autoptr - if (!readfile(name, current_file, &data, &len)) asar_throw_error(0, error_type_block, vfile_error_to_error_id(asar_get_last_io_error()), name.data()); - autoptr datacopy=data; - if (!end) end=len; - if(start < 0) asar_throw_error(0, error_type_block, error_id_file_offset_out_of_bounds, dec(start).data(), name.data()); - if (end < start || end > len || end < 0) asar_throw_error(0, error_type_block, error_id_file_offset_out_of_bounds, dec(end).data(), name.data()); - if (numwords==4) - { - if (!confirmname(word[3])) - { - int pos=(int)getnum(word[3]); - if (foundlabel && !foundlabel_static) asar_throw_error(0, error_type_block, error_id_no_labels_here); - int offset=snestopc(pos); - if (offset + end - start > 0xFFFFFF) asar_throw_error(0, error_type_block, error_id_16mb_rom_limit); - if (offset+end-start>romlen) romlen=offset+end-start; - if (pass==2) writeromdata(offset, data+start, end-start); - } - else - { - int pos; - if (pass==0) - { - if (end - start > 65536) asar_throw_error(0, error_type_block, error_id_incbin_64kb_limit); - pos=getpcfreespace(end-start, false, true, false); - if (pos < 0) asar_throw_error(0, error_type_block, error_id_no_freespace, dec(end - start).data()); - int foundfreespaceid=getfreespaceid(); - freespacepos[foundfreespaceid]=pctosnes(pos)|(/*fastrom?0x800000:*/0x000000)|(foundfreespaceid <<24); - setlabel(word[3], freespacepos[foundfreespaceid]); - writeromdata_bytes(pos, 0xFF, end-start); - } - if (pass==1) - { - getfreespaceid();//nothing to do here, but we want to tick the counter - } - if (pass==2) - { - int foundfreespaceid =getfreespaceid(); - if (freespaceleak[foundfreespaceid]) asar_throw_warning(2, warning_id_freespace_leaked); - writeromdata(snestopc(freespacepos[foundfreespaceid]&0xFFFFFF), data+start, end-start); - freespaceuse+=8+end-start; - } - } - } - else - { - for (int i=start;i 2 && stricmp(word[1], "align")) asar_throw_error(0, error_type_block, error_id_unknown_command); - if(numwords == 5 && stricmp(word[3], "offset")) asar_throw_error(0, error_type_block, error_id_unknown_command); - int amount; - if(numwords > 2) - { - int alignment = getnum(word[2]); - if(foundlabel && !foundlabel_static) asar_throw_error(0, error_type_block, error_id_no_labels_here); - int offset = 0; - if(numwords==5) - { - offset = getnum(word[4]); - if(foundlabel && !foundlabel_static) asar_throw_error(0, error_type_block, error_id_no_labels_here); - } - if(alignment > 0x800000) asar_throw_error(0, error_type_block, error_id_alignment_too_big); - if(alignment < 1) asar_throw_error(0, error_type_block, error_id_alignment_too_small); - if(alignment & (alignment-1)) asar_throw_error(0, error_type_block, error_id_invalid_alignment); - // i just guessed this formula but it seems to work - amount = (alignment - ((snespos - offset) & (alignment-1))) & (alignment-1); - } - else - { - amount = (int)getnum(par); - if (foundlabel && !foundlabel_static) asar_throw_error(0, error_type_block, error_id_no_labels_here); - } - if(is("skip")) step(amount); - else for(int i=0; i < amount; i++) write1(fillbyte[i%12]); - - } - else if (is0("cleartable")) - { - cleartable(); - } - else if (is0("pushtable")) - { - tablestack.append(thetable); - } - else if (is0("pulltable")) - { - if (tablestack.count <= 0) asar_throw_error(0, error_type_block, error_id_pulltable_without_table); - thetable=tablestack[tablestack.count-1]; - tablestack.remove(tablestack.count-1); - } - else if (is3("function")) - { - if (stricmp(word[2], "=")) asar_throw_error(0, error_type_block, error_id_broken_function_declaration); - if (!confirmqpar(word[1])) asar_throw_error(0, error_type_block, error_id_broken_function_declaration); - string line=word[1]; - line.qnormalize(); - char * startpar=strqchr(line.data(), '('); - if (!startpar) asar_throw_error(0, error_type_block, error_id_broken_function_declaration); - *startpar=0; - startpar++; - if (!confirmname(line)) asar_throw_error(0, error_type_block, error_id_invalid_function_name); - char * endpar=strqchr(startpar, ')'); - //confirmqpar requires that all parentheses are matched, and a starting one exists, therefore it is harmless to not check for nulls - if (endpar[1]) asar_throw_error(0, error_type_block, error_id_broken_function_declaration); - *endpar=0; - createuserfunc(line, startpar, word[3]); - } - else if (is1("print")) - { - string out = handle_print(par); - if (pass==2) print(out); - } - else if (is1("reset")) - { - if(0); - else if (!stricmp(par, "bytes")) bytes=0; - else if (!stricmp(par, "freespaceuse")) freespaceuse=0; - else asar_throw_error(2, error_type_block, error_id_unknown_variable); - } - else if (is1("padbyte") || is1("padword") || is1("padlong") || is1("paddword")) - { - int len = 0; - if (is("padbyte")) len=1; - if (is("padword")) len=2; - if (is("padlong")) len=3; - if (is("paddword")) len=4; - unsigned int val=getnum(par); - if (foundlabel && !foundlabel_static) asar_throw_error(0, error_type_block, error_id_no_labels_here); - for (int i=0;i<12;i+=len) - { - unsigned int tmpval=val; - for (int j=0;j>=8; - } - } - } - else if (is1("pad")) - { - if ((unsigned int)realsnespos & 0xFF000000) asar_throw_error(0, error_type_block, error_id_pad_in_freespace); - int num=(int)getnum(par); - if ((unsigned int)num & 0xFF000000) asar_throw_error(0, error_type_block, error_id_snes_address_doesnt_map_to_rom, hex((unsigned int)num, 6).data()); - if (num>realsnespos) - { - int end=snestopc(num); - int start=snestopc(realsnespos); - int len=end-start; - for (int i=0;i>=8; - } - } - } - else if (is1("arch")) - { - if(in_spcblock) asar_throw_error(0, error_type_block, error_id_feature_unavaliable_in_spcblock); - if (!stricmp(par, "65816")) { arch=arch_65816; return; } - if (!stricmp(par, "spc700")) { arch=arch_spc700; return; } - if (!stricmp(par, "superfx")) { arch=arch_superfx; return; } - } - else if (is0("{") || is0("}")) {} - else - { - asar_throw_error(1, error_type_block, error_id_unknown_command); - } - + else if (is1("incsrc")) { + const char* current_file = get_current_file_name(); + string name; + // RPG Hacker: Should this also throw on absolute paths? + // E.g., on something starting with C:/ or whatever. + if (strchr(par, '\\')) { + asar_throw_error(0, error_type_block, error_id_platform_paths); + } + name = safedequote(par); + assemblefile(name); + } else if (is1("incbin") || is3("incbin")) { + if (numwords == 4 && strcmp(word[2], "->")) + asar_throw_error(0, error_type_block, error_id_broken_incbin); + int len; + int start = 0; + int end = 0; + if (strqchr(par, ':')) { + char* lengths = strqchr(par, ':'); + *lengths = 0; + lengths++; + if (strchr(lengths, '"')) + asar_throw_error(0, error_type_block, error_id_broken_incbin); + if (*lengths == '(') { + char* tmp = strqpchr(lengths, '-'); + if (!tmp || (*(tmp - 1) != ')')) + asar_throw_error(0, error_type_block, error_id_broken_incbin); + start = (int)getnum(string(lengths + 1, tmp - 1 - lengths - 1)); + if (foundlabel && !foundlabel_static) + asar_throw_error(0, error_type_block, error_id_no_labels_here); + lengths = tmp; + } else { + start = (int)strtoul(lengths, &lengths, 16); + } + if (*lengths != '-') + asar_throw_error(0, error_type_block, error_id_broken_incbin); + lengths++; + if (*lengths == '(') { + char* tmp = strchr(lengths, '\0'); + if (*(tmp - 1) != ')') + asar_throw_error(0, error_type_block, error_id_broken_incbin); + end = (int)getnum(string(lengths + 1, tmp - 1 - lengths - 1)); + if (foundlabel && !foundlabel_static) + asar_throw_error(0, error_type_block, error_id_no_labels_here); + // no need to check end-of-string here + } else { + end = (int)strtoul(lengths, &lengths, 16); + if (*lengths) + asar_throw_error(0, error_type_block, error_id_broken_incbin); + } + } + const char* current_file = get_current_file_name(); + string name; + // RPG Hacker: Should this also throw on absolute paths? + // E.g., on something starting with C:/ or whatever. + if (strchr(par, '\\')) { + asar_throw_error(0, error_type_block, error_id_platform_paths); + } + name = safedequote(par); + char* data; // I couldn't find a way to get this into an autoptr + if (!readfile(name, current_file, &data, &len)) + asar_throw_error(0, error_type_block, + vfile_error_to_error_id(asar_get_last_io_error()), + name.data()); + autoptr datacopy = data; + if (!end) end = len; + if (start < 0) + asar_throw_error(0, error_type_block, error_id_file_offset_out_of_bounds, + dec(start).data(), name.data()); + if (end < start || end > len || end < 0) + asar_throw_error(0, error_type_block, error_id_file_offset_out_of_bounds, + dec(end).data(), name.data()); + if (numwords == 4) { + if (!confirmname(word[3])) { + int pos = (int)getnum(word[3]); + if (foundlabel && !foundlabel_static) + asar_throw_error(0, error_type_block, error_id_no_labels_here); + int offset = snestopc(pos); + if (offset + end - start > 0xFFFFFF) + asar_throw_error(0, error_type_block, error_id_16mb_rom_limit); + if (offset + end - start > romlen) romlen = offset + end - start; + if (pass == 2) writeromdata(offset, data + start, end - start); + } else { + int pos; + if (pass == 0) { + if (end - start > 65536) + asar_throw_error(0, error_type_block, + error_id_incbin_64kb_limit); + pos = getpcfreespace(end - start, false, true, false); + if (pos < 0) + asar_throw_error(0, error_type_block, error_id_no_freespace, + dec(end - start).data()); + int foundfreespaceid = getfreespaceid(); + freespacepos[foundfreespaceid] = pctosnes(pos) | + (/*fastrom?0x800000:*/ 0x000000) | + (foundfreespaceid << 24); + setlabel(word[3], freespacepos[foundfreespaceid]); + writeromdata_bytes(pos, 0xFF, end - start); + } + if (pass == 1) { + getfreespaceid(); // nothing to do here, but we want to tick the + // counter + } + if (pass == 2) { + int foundfreespaceid = getfreespaceid(); + if (freespaceleak[foundfreespaceid]) + asar_throw_warning(2, warning_id_freespace_leaked); + writeromdata(snestopc(freespacepos[foundfreespaceid] & 0xFFFFFF), + data + start, end - start); + freespaceuse += 8 + end - start; + } + } + } else { + for (int i = start; i < end; i++) write1((unsigned int)data[i]); + } + } else if (is("skip") || is("fill")) { + if (numwords != 2 && numwords != 3 && numwords != 5) + asar_throw_error(0, error_type_block, error_id_unknown_command); + if (numwords > 2 && stricmp(word[1], "align")) + asar_throw_error(0, error_type_block, error_id_unknown_command); + if (numwords == 5 && stricmp(word[3], "offset")) + asar_throw_error(0, error_type_block, error_id_unknown_command); + int amount; + if (numwords > 2) { + int alignment = getnum(word[2]); + if (foundlabel && !foundlabel_static) + asar_throw_error(0, error_type_block, error_id_no_labels_here); + int offset = 0; + if (numwords == 5) { + offset = getnum(word[4]); + if (foundlabel && !foundlabel_static) + asar_throw_error(0, error_type_block, error_id_no_labels_here); + } + if (alignment > 0x800000) + asar_throw_error(0, error_type_block, error_id_alignment_too_big); + if (alignment < 1) + asar_throw_error(0, error_type_block, error_id_alignment_too_small); + if (alignment & (alignment - 1)) + asar_throw_error(0, error_type_block, error_id_invalid_alignment); + // i just guessed this formula but it seems to work + amount = (alignment - ((snespos - offset) & (alignment - 1))) & + (alignment - 1); + } else { + amount = (int)getnum(par); + if (foundlabel && !foundlabel_static) + asar_throw_error(0, error_type_block, error_id_no_labels_here); + } + if (is("skip")) + step(amount); + else + for (int i = 0; i < amount; i++) write1(fillbyte[i % 12]); + + } else if (is0("cleartable")) { + cleartable(); + } else if (is0("pushtable")) { + tablestack.append(thetable); + } else if (is0("pulltable")) { + if (tablestack.count <= 0) + asar_throw_error(0, error_type_block, error_id_pulltable_without_table); + thetable = tablestack[tablestack.count - 1]; + tablestack.remove(tablestack.count - 1); + } else if (is3("function")) { + if (stricmp(word[2], "=")) + asar_throw_error(0, error_type_block, error_id_broken_function_declaration); + if (!confirmqpar(word[1])) + asar_throw_error(0, error_type_block, error_id_broken_function_declaration); + string line = word[1]; + line.qnormalize(); + char* startpar = strqchr(line.data(), '('); + if (!startpar) + asar_throw_error(0, error_type_block, error_id_broken_function_declaration); + *startpar = 0; + startpar++; + if (!confirmname(line)) + asar_throw_error(0, error_type_block, error_id_invalid_function_name); + char* endpar = strqchr(startpar, ')'); + // confirmqpar requires that all parentheses are matched, and a starting one + // exists, therefore it is harmless to not check for nulls + if (endpar[1]) + asar_throw_error(0, error_type_block, error_id_broken_function_declaration); + *endpar = 0; + createuserfunc(line, startpar, word[3]); + } else if (is1("print")) { + string out = handle_print(par); + if (pass == 2) print(out); + } else if (is1("reset")) { + if (0) + ; + else if (!stricmp(par, "bytes")) + bytes = 0; + else if (!stricmp(par, "freespaceuse")) + freespaceuse = 0; + else + asar_throw_error(2, error_type_block, error_id_unknown_variable); + } else if (is1("padbyte") || is1("padword") || is1("padlong") || is1("paddword")) { + int len = 0; + if (is("padbyte")) len = 1; + if (is("padword")) len = 2; + if (is("padlong")) len = 3; + if (is("paddword")) len = 4; + unsigned int val = getnum(par); + if (foundlabel && !foundlabel_static) + asar_throw_error(0, error_type_block, error_id_no_labels_here); + for (int i = 0; i < 12; i += len) { + unsigned int tmpval = val; + for (int j = 0; j < len; j++) { + padbyte[i + j] = (unsigned char)tmpval; + tmpval >>= 8; + } + } + } else if (is1("pad")) { + if ((unsigned int)realsnespos & 0xFF000000) + asar_throw_error(0, error_type_block, error_id_pad_in_freespace); + int num = (int)getnum(par); + if ((unsigned int)num & 0xFF000000) + asar_throw_error(0, error_type_block, + error_id_snes_address_doesnt_map_to_rom, + hex((unsigned int)num, 6).data()); + if (num > realsnespos) { + int end = snestopc(num); + int start = snestopc(realsnespos); + int len = end - start; + for (int i = 0; i < len; i++) write1(padbyte[i % 12]); + } + } else if (is1("fillbyte") || is1("fillword") || is1("filllong") || + is1("filldword")) { + int len = 0; + if (is("fillbyte")) len = 1; + if (is("fillword")) len = 2; + if (is("filllong")) len = 3; + if (is("filldword")) len = 4; + unsigned int val = getnum(par); + if (foundlabel && !foundlabel_static) + asar_throw_error(0, error_type_block, error_id_no_labels_here); + for (int i = 0; i < 12; i += len) { + unsigned int tmpval = val; + for (int j = 0; j < len; j++) { + fillbyte[i + j] = (unsigned char)tmpval; + tmpval >>= 8; + } + } + } else if (is1("arch")) { + if (in_spcblock) + asar_throw_error(0, error_type_block, + error_id_feature_unavaliable_in_spcblock); + if (!stricmp(par, "65816")) { + arch = arch_65816; + return; + } + if (!stricmp(par, "spc700")) { + arch = arch_spc700; + return; + } + if (!stricmp(par, "superfx")) { + arch = arch_superfx; + return; + } + } else if (is0("{") || is0("}")) { + } else { + asar_throw_error(1, error_type_block, error_id_unknown_command); + } } -bool assemblemapper(char** word, int numwords) -{ - auto previous_mapper = mapper; - if(0); - else if (is0("lorom")) - { - //this also makes xkas set snespos to $008000 for some reason - mapper=lorom; - } - else if (is0("hirom")) - { - //xkas makes this point to $C00000 - mapper=hirom; - } - else if (is0("exlorom")) - { - mapper = exlorom; - } - else if (is0("exhirom")) - { - mapper=exhirom; - } - else if (is0("sfxrom")) - { - mapper=sfxrom; - //fastrom=false; - } - else if (is0("norom")) - { - //$000000 would be the best snespos for this, but I don't care - mapper=norom; - //fastrom=false; - if(!force_checksum_fix) - checksum_fix_enabled = false;//we don't know where the header is, so don't set the checksum - } - else if (is0("fullsa1rom")) - { - mapper=bigsa1rom; - //fastrom=false; - } - else if (is("sa1rom")) - { - //fastrom=false; - if (par) - { - if (word[2]) asar_throw_error(0, error_type_block, error_id_invalid_mapper); - if (!is_digit(par[0]) || par[1]!=',' || - !is_digit(par[2]) || par[3]!=',' || - !is_digit(par[4]) || par[5]!=',' || - !is_digit(par[6]) || par[7]) asar_throw_error(0, error_type_block, error_id_invalid_mapper); - int len; - autoptr pars=qpsplit(par, ',', &len); - verify_paren(pars); - if (len!=4) asar_throw_error(0, error_type_block, error_id_invalid_mapper); - sa1banks[0]=(par[0]-'0')<<20; - sa1banks[1]=(par[2]-'0')<<20; - sa1banks[4]=(par[4]-'0')<<20; - sa1banks[5]=(par[6]-'0')<<20; - } - else - { - sa1banks[0]=0<<20; - sa1banks[1]=1<<20; - sa1banks[4]=2<<20; - sa1banks[5]=3<<20; - } - mapper=sa1rom; - } - else return false; - - if(in_spcblock) asar_throw_error(0, error_type_block, error_id_feature_unavaliable_in_spcblock); - if(!mapper_set){ - mapper_set = true; - }else if(previous_mapper != mapper){ - asar_throw_warning(1, warning_id_mapper_already_set); - } - return true; +bool assemblemapper(char** word, int numwords) { + auto previous_mapper = mapper; + if (0) + ; + else if (is0("lorom")) { + // this also makes xkas set snespos to $008000 for some reason + mapper = lorom; + } else if (is0("hirom")) { + // xkas makes this point to $C00000 + mapper = hirom; + } else if (is0("exlorom")) { + mapper = exlorom; + } else if (is0("exhirom")) { + mapper = exhirom; + } else if (is0("sfxrom")) { + mapper = sfxrom; + // fastrom=false; + } else if (is0("norom")) { + //$000000 would be the best snespos for this, but I don't care + mapper = norom; + // fastrom=false; + if (!force_checksum_fix) + checksum_fix_enabled = false; // we don't know where the header is, so + // don't set the checksum + } else if (is0("fullsa1rom")) { + mapper = bigsa1rom; + // fastrom=false; + } else if (is("sa1rom")) { + // fastrom=false; + if (par) { + if (word[2]) asar_throw_error(0, error_type_block, error_id_invalid_mapper); + if (!is_digit(par[0]) || par[1] != ',' || !is_digit(par[2]) || + par[3] != ',' || !is_digit(par[4]) || par[5] != ',' || + !is_digit(par[6]) || par[7]) + asar_throw_error(0, error_type_block, error_id_invalid_mapper); + int len; + autoptr pars = qpsplit(par, ',', &len); + verify_paren(pars); + if (len != 4) + asar_throw_error(0, error_type_block, error_id_invalid_mapper); + sa1banks[0] = (par[0] - '0') << 20; + sa1banks[1] = (par[2] - '0') << 20; + sa1banks[4] = (par[4] - '0') << 20; + sa1banks[5] = (par[6] - '0') << 20; + } else { + sa1banks[0] = 0 << 20; + sa1banks[1] = 1 << 20; + sa1banks[4] = 2 << 20; + sa1banks[5] = 3 << 20; + } + mapper = sa1rom; + } else + return false; + + if (in_spcblock) + asar_throw_error(0, error_type_block, error_id_feature_unavaliable_in_spcblock); + if (!mapper_set) { + mapper_set = true; + } else if (previous_mapper != mapper) { + asar_throw_warning(1, warning_id_mapper_already_set); + } + return true; } diff --git a/src/asar/assembleblock.h b/src/asar/assembleblock.h index 10f52fd9..112f3d80 100644 --- a/src/asar/assembleblock.h +++ b/src/asar/assembleblock.h @@ -1,37 +1,40 @@ #pragma once +#include "assocarr.h" +#include "autoarray.h" +#include "libstr.h" + enum { arch_65816, arch_spc700, arch_superfx }; extern int arch; bool assemblemapper(char** word, int numwords); struct snes_struct { - string parent; - int base_end; - int struct_size; - int object_size; - bool is_static; + string parent; + int base_end; + int struct_size; + int object_size; + bool is_static; }; extern assocarr structs; struct snes_label { - unsigned int pos; - bool is_static; - - snes_label() - { - pos = 0; - is_static = false; - } + unsigned int pos; + bool is_static; + + snes_label() { + pos = 0; + is_static = false; + } }; -// RPG Hacker: Really the only purpose of this struct is to support pushtable and pulltable -// Also don't know where else to put this, so putting it in this header +// RPG Hacker: Really the only purpose of this struct is to support pushtable and +// pulltable Also don't know where else to put this, so putting it in this header /*struct chartabledata { - unsigned int table[256]; + unsigned int table[256]; }; extern chartabledata table; @@ -39,15 +42,15 @@ unsigned int get_table_val(int inp); void set_table_val(int inp, unsigned int out);*/ struct whiletracker { - bool iswhile; - int startline; - bool cond; + bool iswhile; + int startline; + bool cond; }; extern autoarray whilestatus; -bool confirmname(const char * name); -string posneglabelname(const char ** input, bool define); +bool confirmname(const char* name); +string posneglabelname(const char** input, bool define); void write1_pick(unsigned int num); void write2(unsigned int num); @@ -58,19 +61,19 @@ int snestopc_pick(int addr); int getlenfromchar(char c); -snes_label labelval(const char ** rawname, bool define = false); +snes_label labelval(const char** rawname, bool define = false); snes_label labelval(string name, bool define = false); -bool labelval(const char ** rawname, snes_label * rval, bool define = false); -bool labelval(string name, snes_label * rval, bool define = false); +bool labelval(const char** rawname, snes_label* rval, bool define = false); +bool labelval(string name, snes_label* rval, bool define = false); -const char * safedequote(char * str); +const char* safedequote(char* str); void checkbankcross(); void initstuff(); void finishpass(); -void assembleblock(const char * block); +void assembleblock(const char* block); extern int snespos; extern int realsnespos; diff --git a/src/asar/assocarr.h b/src/asar/assocarr.h index 97090ce4..10e1b8fc 100644 --- a/src/asar/assocarr.h +++ b/src/asar/assocarr.h @@ -1,326 +1,312 @@ -//API for assocarr myarr: -//myarr.exists("index") +// API for assocarr myarr: +// myarr.exists("index") // Returns a boolean telling whether the entry exists. // Complexity: O(log n). -//myarr.find("index") -// Returns a mytype& corresponding to the relevant entry. If it doesn't exist, behaviour is undefined. -// Complexity: O(log n). -//myarr.create("index") -// Returns a mytype& corresponding to the relevant entry. If it doesn't exist, it's created. -// Complexity: Worst case O(n), reduced to amortized O(log n) if the element should be at the end (strict weak ordering), or O(log n) if the element -// exists. -//myarr.remove("index") -// Removes the element corresponding to the relevant entry. If it doesn't exist, this structure is left untouched. -// Complexity: Worst case O(n), reduced to amortized O(log n) if the element is at the end. -//myarr.reset() -// Clears the data structure, calling all destructors. It is safe to call this function on an empty structure. -// Complexity: O(n). -//myarr.move("from", "to") -// Moves an element from index "from" to "to". If the source doesn't exist, behaviour is undefined. If the target exists, no action is performed. -// Complexity: O(n). -//myarr["index"] -// Similar to myarr.create("index"), but if the returned entry isn't changed by the next call to any function in the same structure, it's removed. -// if (myarr["index"]) is safe if type can cast to a bool, and myarr["index"]=4 is also valid if the assignment is valid for a type&. However, it is -// not safe to use assocarr > myarr; if (myarr["3"]["4"]), since the inner assocarr won't know that it's supposed to be garbage collected, -// and the outer one sees that the inner one changed, so you get pollution and memory waste. However, if (myarr["3"].exists("4")) is safe. -// Complexity: Same as myarr.create(). -//myarr.each(func) -// Calls func() for each entry in the structure. func must match the prototype void func(const char * index, mytype& val), but can be a lambda. No -// non-const function may be called on this structure from inside func(), but it is safe to call const functions of the structure. The function -// calls are in the same order as the indexes, in a strict weak ordering. -// Complexity: O(n). -//Space usage: O(n). -//C++ version: C++98 or C++03, not sure which. -//Serializer support: Yes, if mytype is serializable. +// myarr.find("index") +// Returns a mytype& corresponding to the relevant entry. If it doesn't exist, +// behaviour is undefined. Complexity: O(log n). +// myarr.create("index") +// Returns a mytype& corresponding to the relevant entry. If it doesn't exist, it's +// created. Complexity: Worst case O(n), reduced to amortized O(log n) if the element +// should be at the end (strict weak ordering), or O(log n) if the element exists. +// myarr.remove("index") +// Removes the element corresponding to the relevant entry. If it doesn't exist, this +// structure is left untouched. Complexity: Worst case O(n), reduced to amortized O(log +// n) if the element is at the end. +// myarr.reset() +// Clears the data structure, calling all destructors. It is safe to call this function +// on an empty structure. Complexity: O(n). +// myarr.move("from", "to") +// Moves an element from index "from" to "to". If the source doesn't exist, behaviour +// is undefined. If the target exists, no action is performed. Complexity: O(n). +// myarr["index"] +// Similar to myarr.create("index"), but if the returned entry isn't changed by the +// next call to any function in the same structure, it's removed. if (myarr["index"]) +// is safe if type can cast to a bool, and myarr["index"]=4 is also valid if the +// assignment is valid for a type&. However, it is not safe to use +// assocarr > myarr; if (myarr["3"]["4"]), since the inner assocarr won't +// know that it's supposed to be garbage collected, and the outer one sees that the +// inner one changed, so you get pollution and memory waste. However, if +// (myarr["3"].exists("4")) is safe. Complexity: Same as myarr.create(). +// myarr.each(func) +// Calls func() for each entry in the structure. func must match the prototype void +// func(const char * index, mytype& val), but can be a lambda. No non-const function +// may be called on this structure from inside func(), but it is safe to call const +// functions of the structure. The function calls are in the same order as the indexes, +// in a strict weak ordering. Complexity: O(n). +// Space usage: O(n). +// C++ version: C++98 or C++03, not sure which. +// Serializer support: Yes, if mytype is serializable. //"Undefined behaviour" means "segfault" in most cases. #pragma once #include + +#include "libmisc.h" //bitround #include "std-includes.h" -#include "libmisc.h"//bitround -//just a helper for initializer list -template struct pair{ - T1 first; - T2 second; +// just a helper for initializer list +template +struct pair { + T1 first; + T2 second; }; -template class assocarr { +template +class assocarr { public: -int num; + int num; private: + const char** indexes; + right** ptr; + int bufferlen; -const char ** indexes; -right ** ptr; -int bufferlen; - -char lastone[sizeof(right)]; -int lastid; + char lastone[sizeof(right)]; + int lastid; -void collectgarbage(const char * ifnotmatch=nullptr) -{ - if (lastid==-1) return; - if (ifnotmatch && !strcmp(indexes[lastid], ifnotmatch)) return; - if (!memcmp(ptr[lastid], lastone, sizeof(right))) - { - int tmpid=lastid; - lastid=-1; - remove(indexes[tmpid]); - } - lastid=-1; -} + void collectgarbage(const char* ifnotmatch = nullptr) { + if (lastid == -1) return; + if (ifnotmatch && !strcmp(indexes[lastid], ifnotmatch)) return; + if (!memcmp(ptr[lastid], lastone, sizeof(right))) { + int tmpid = lastid; + lastid = -1; + remove(indexes[tmpid]); + } + lastid = -1; + } -right& rawadd(const char * index, bool collect) -{ - collectgarbage(index); - int loc=0; - int skip= (int)bitround((unsigned int)num); - while (skip) - { - int dir; - if (loc>=num) dir=1; - else dir=strcmp(indexes[loc], index); - if (!dir) return ptr[loc][0]; - skip/=2; - if (dir>0) loc-=skip; - else loc+=skip; - if (loc<0) - { - loc=0; - break; - } - } - if (loc= num) + dir = 1; + else + dir = strcmp(indexes[loc], index); + if (!dir) return ptr[loc][0]; + skip /= 2; + if (dir > 0) + loc -= skip; + else + loc += skip; + if (loc < 0) { + loc = 0; + break; + } + } + if (loc < num && strcmp(indexes[loc], index) < 0) loc++; + if (num == bufferlen) { + if (!num) + bufferlen = 1; + else + bufferlen *= 2; + ptr = (right**)realloc(ptr, sizeof(right*) * (size_t)bufferlen); + indexes = (const char**)realloc(indexes, + sizeof(const char*) * (size_t)bufferlen); + } + num++; + memmove(indexes + loc + 1, indexes + loc, + sizeof(const char*) * (size_t)(num - loc - 1)); + memmove(ptr + loc + 1, ptr + loc, sizeof(right*) * (size_t)(num - loc - 1)); + indexes[loc] = duplicate_string(index); + ptr[loc] = (right*)malloc(sizeof(right)); + memset(ptr[loc], 0, sizeof(right)); + new (ptr[loc]) right; + if (collect) { + lastid = loc; + memcpy(lastone, ptr[loc], sizeof(right)); + } + return ptr[loc][0]; + } -int find_i(const char * index) const -{ - int loc=0; - int skip=(int)bitround((unsigned int)num); - while (skip) - { - int dir; - if (loc>=num) dir=1; - else dir=strcmp(indexes[loc], index); - if (!dir) return loc; - skip/=2; - if (dir>0) loc-=skip; - else loc+=skip; - if (loc<0) return -1; - } - return -1; -} + int find_i(const char* index) const { + int loc = 0; + int skip = (int)bitround((unsigned int)num); + while (skip) { + int dir; + if (loc >= num) + dir = 1; + else + dir = strcmp(indexes[loc], index); + if (!dir) return loc; + skip /= 2; + if (dir > 0) + loc -= skip; + else + loc += skip; + if (loc < 0) return -1; + } + return -1; + } public: + bool exists(const char* index) const { return find_i(index) >= 0; } -bool exists(const char * index) const -{ - return find_i(index)>=0; -} + right& find(const char* index) const { return ptr[find_i(index)][0]; } -right& find(const char * index) const -{ - return ptr[find_i(index)][0]; -} + right& create(const char* index) { return rawadd(index, false); } -right& create(const char * index) -{ - return rawadd(index, false); -} + void remove(const char* index) { + collectgarbage(); + int loc = 0; + int skip = (int)bitround((unsigned int)num); + while (skip) { + int dir; + if (loc >= num) + dir = 1; + else + dir = strcmp(indexes[loc], index); + if (!dir) { + free((void*)indexes[loc]); + ptr[loc]->~right(); + free(ptr[loc]); + memmove(indexes + loc, indexes + loc + 1, + sizeof(const char*) * (size_t)(num - loc - 1)); + memmove(ptr + loc, ptr + loc + 1, + sizeof(right*) * (size_t)(num - loc - 1)); + num--; + if (num == bufferlen / 2) { + bufferlen /= 2; + ptr = (right**)realloc(ptr, sizeof(right*) * (size_t)bufferlen); + indexes = (const char**)realloc( + indexes, sizeof(const char*) * (size_t)bufferlen); + } + return; + } + skip /= 2; + if (dir > 0) + loc -= skip; + else + loc += skip; + if (loc < 0) return; + } + } -void remove(const char * index) -{ - collectgarbage(); - int loc=0; - int skip= (int)bitround((unsigned int)num); - while (skip) - { - int dir; - if (loc>=num) dir=1; - else dir=strcmp(indexes[loc], index); - if (!dir) - { - free((void*)indexes[loc]); - ptr[loc]->~right(); - free(ptr[loc]); - memmove(indexes+loc, indexes+loc+1, sizeof(const char *)*(size_t)(num-loc-1)); - memmove(ptr+loc, ptr+loc+1, sizeof(right*)*(size_t)(num-loc-1)); - num--; - if (num==bufferlen/2) - { - bufferlen/=2; - ptr=(right**)realloc(ptr, sizeof(right*)*(size_t)bufferlen); - indexes=(const char**)realloc(indexes, sizeof(const char *)*(size_t)bufferlen); - } - return; - } - skip/=2; - if (dir>0) loc-=skip; - else loc+=skip; - if (loc<0) return; - } -} + void move(const char* from, const char* to) { + collectgarbage(); + int frompos = find_i(from); + int topos = 0; + int skip = bitround(num); + while (skip) { + int dir; + if (topos >= num) + dir = 1; + else + dir = strcmp(indexes[topos], to); + if (!dir) return; + skip /= 2; + if (dir > 0) + topos -= skip; + else + topos += skip; + if (topos < 0) { + topos = 0; + break; + } + } + if (topos < num && strcmp(indexes[topos], to) < 0) topos++; + right* tmp = ptr[frompos]; + if (topos == frompos || topos == frompos + 1) { + free((void*)indexes[frompos]); + indexes[frompos] = duplicate_string(to); + } else if (topos > frompos) { + free((void*)indexes[frompos]); + memmove(indexes + frompos, indexes + frompos + 1, + sizeof(const char*) * (topos - frompos - 1)); + memmove(ptr + frompos, ptr + frompos + 1, + sizeof(right*) * (topos - frompos - 1)); + ptr[topos - 1] = tmp; + indexes[topos - 1] = + duplicate_string(to); // I wonder what the fuck I'm doing. + } else { + free((void*)indexes[frompos]); + memmove(indexes + topos + 1, indexes + topos, + sizeof(const char*) * (frompos - topos)); + memmove(ptr + topos + 1, ptr + topos, sizeof(right*) * (frompos - topos)); + ptr[topos] = tmp; + indexes[topos] = duplicate_string(to); + } + } -void move(const char * from, const char * to) -{ - collectgarbage(); - int frompos=find_i(from); - int topos=0; - int skip=bitround(num); - while (skip) - { - int dir; - if (topos>=num) dir=1; - else dir=strcmp(indexes[topos], to); - if (!dir) return; - skip/=2; - if (dir>0) topos-=skip; - else topos+=skip; - if (topos<0) - { - topos=0; - break; - } - } - if (toposfrompos) - { - free((void*)indexes[frompos]); - memmove(indexes+frompos, indexes+frompos+1, sizeof(const char *)*(topos-frompos-1)); - memmove(ptr+frompos, ptr+frompos+1, sizeof(right*)*(topos-frompos-1)); - ptr[topos-1]=tmp; - indexes[topos-1]= duplicate_string(to);//I wonder what the fuck I'm doing. - } - else - { - free((void*)indexes[frompos]); - memmove(indexes+topos+1, indexes+topos, sizeof(const char *)*(frompos-topos)); - memmove(ptr+topos+1, ptr+topos, sizeof(right*)*(frompos-topos)); - ptr[topos]=tmp; - indexes[topos]= duplicate_string(to); - } -} + void reset() { + for (int i = 0; i < num; i++) { + free((void*)indexes[i]); + ptr[i]->~right(); + free(ptr[i]); + } + free(indexes); + free(ptr); + indexes = nullptr; + ptr = nullptr; + num = 0; + bufferlen = 0; + lastid = -1; + } -void reset() -{ - for (int i=0;i~right(); - free(ptr[i]); - } - free(indexes); - free(ptr); - indexes=nullptr; - ptr= nullptr; - num=0; - bufferlen=0; - lastid=-1; -} + assocarr() { + indexes = nullptr; + ptr = nullptr; + num = 0; + bufferlen = 0; + lastid = -1; + } -assocarr() -{ - indexes= nullptr; - ptr= nullptr; - num=0; - bufferlen=0; - lastid=-1; -} + assocarr(std::initializer_list> list) { + indexes = nullptr; + ptr = nullptr; + num = 0; + bufferlen = 0; + lastid = -1; -assocarr(std::initializer_list> list) -{ - indexes= nullptr; - ptr= nullptr; - num=0; - bufferlen=0; - lastid=-1; - - for(auto &item : list){ - rawadd(item.first, true) = item.second; - } -} + for (auto& item : list) { + rawadd(item.first, true) = item.second; + } + } -~assocarr() -{ - reset(); -} + ~assocarr() { reset(); } -right& operator[](const char * index) -{ - return rawadd(index, true); -} + right& operator[](const char* index) { return rawadd(index, true); } -//void(*func)(const char * key, right& value) -template void each(t func) -{ - collectgarbage(); - for (int i=0;i + void each(t func) { + collectgarbage(); + for (int i = 0; i < num; i++) { + func(indexes[i], ptr[i][0]); + } + } -//void debug(){puts("");for(int i=0;i class autoptr { - T ptr; +// Note: T must be a pointer type, or stuff will screw up. To make a pointer last longer +// than this object, assign nullptr to it and it won't free the old one. +template +class autoptr { + T ptr; + public: - operator T() const - { - return ptr; - } - - autoptr& operator=(T ptr_) - { - ptr = ptr_; - return *this; - } - - autoptr() - { - ptr = nullptr; - } - - autoptr(T ptr_) - { - ptr = ptr_; - } - - autoptr(const autoptr& ptr_) - { - ptr = ptr_.ptr; - } - - ~autoptr() - { - if (ptr) free((void*)ptr); - } + operator T() const { return ptr; } + + autoptr& operator=(T ptr_) { + ptr = ptr_; + return *this; + } + + autoptr() { ptr = nullptr; } + + autoptr(T ptr_) { ptr = ptr_; } + + autoptr(const autoptr& ptr_) { ptr = ptr_.ptr; } + + ~autoptr() { + if (ptr) free((void*)ptr); + } }; -template class autoarray { +template +class autoarray { public: - int count; + int count; private: - T* ptr; - int bufferlen; - - T dummy; - static const int default_size = 128; - - const T& getconst(int id) const - { - if (id < 0) return dummy; - if (id >= count) return dummy; - return ptr[id]; - } - - T& get(int id) - { - if (id < 0) return dummy; - if (id >= bufferlen - 4) - { - resize(id); - } - if (id >= count) - { - for (int i = count;i <= id;i++) new(ptr + i) T(); - count = id + 1; - } - return ptr[id]; - } - - void resize(int size) - { - int oldlen = count; - while (bufferlen <= size + 4) bufferlen *= 2; - T *old = ptr; - ptr = (T*)malloc(sizeof(T)*(size_t)bufferlen); - for(int i = 0; i < oldlen; i++){ - new(ptr + i) T(); - ptr[i] = static_cast(old[i]); - } - free(old); - memset(ptr + oldlen, 0, (size_t)(bufferlen - oldlen) * sizeof(T)); - } + T* ptr; + int bufferlen; + + T dummy; + static const int default_size = 128; + + const T& getconst(int id) const { + if (id < 0) return dummy; + if (id >= count) return dummy; + return ptr[id]; + } + + T& get(int id) { + if (id < 0) return dummy; + if (id >= bufferlen - 4) { + resize(id); + } + if (id >= count) { + for (int i = count; i <= id; i++) new (ptr + i) T(); + count = id + 1; + } + return ptr[id]; + } + + void resize(int size) { + int oldlen = count; + while (bufferlen <= size + 4) bufferlen *= 2; + T* old = ptr; + ptr = (T*)malloc(sizeof(T) * (size_t)bufferlen); + for (int i = 0; i < oldlen; i++) { + new (ptr + i) T(); + ptr[i] = static_cast(old[i]); + } + free(old); + memset(ptr + oldlen, 0, (size_t)(bufferlen - oldlen) * sizeof(T)); + } public: - - void reset(int keep = 0) - { - if (keep >= count) return; - for (int i = keep;i < count;i++) ptr[i].~T(); - memset(ptr + keep, 0, (size_t)(count - keep) * sizeof(T)); - if (keep < bufferlen / 2) - { - while (keep < bufferlen / 2 && bufferlen>8) bufferlen /= 2; - T *old = ptr; - ptr = (T*)malloc(sizeof(T)*(size_t)bufferlen); - for(int i = 0; i < keep; i++){ - new(ptr + i) T(); - ptr[i] = static_cast(old[i]); - } - free(old); - - } - count = keep; - } - - T& operator[](int id) - { - return get(id); - } - - const T& operator[](int id) const - { - return getconst(id); - } - - operator T*() - { - return ptr; - } - - operator const T*() const - { - return ptr; - } - - T& append(const T& item) - { - return (get(count) = item); - } - - //insert is not safe for non pod types!!! - void insert(int pos) - { - if (pos<0 || pos>count) return; - if (count >= bufferlen - 4) - { - resize(count); - } - memmove(ptr + pos + 1, ptr + pos, sizeof(T)*(count - pos)); - memset(ptr + pos, 0, sizeof(T)); - new(ptr + pos) T(); - count++; - } - - void insert(int pos, const T& item) - { - if (pos<0 || pos>count) return; - if (count >= bufferlen - 4) - { - resize(count); - } - memmove(ptr + pos + 1, ptr + pos, sizeof(T)*(size_t)(count - pos)); - memset(ptr + pos, 0, sizeof(T)); - new(ptr + pos) T(); - ptr[pos] = item; - count++; - } - - void remove(int id) - { - if (id < 0 || id >= count) return; - count--; - ptr[id].~T(); - for(int i = id; i < count; i++){ - ptr[i] = static_cast(ptr[i+1]); - } - } - - autoarray() - { - ptr = (T*)malloc(sizeof(T) * default_size); - memset(ptr, 0, default_size*sizeof(T)); - bufferlen = default_size; - count = 0; - } - - ~autoarray() - { - for (int i = 0;i < count;i++) ptr[i].~T(); - free(ptr); - } + void reset(int keep = 0) { + if (keep >= count) return; + for (int i = keep; i < count; i++) ptr[i].~T(); + memset(ptr + keep, 0, (size_t)(count - keep) * sizeof(T)); + if (keep < bufferlen / 2) { + while (keep < bufferlen / 2 && bufferlen > 8) bufferlen /= 2; + T* old = ptr; + ptr = (T*)malloc(sizeof(T) * (size_t)bufferlen); + for (int i = 0; i < keep; i++) { + new (ptr + i) T(); + ptr[i] = static_cast(old[i]); + } + free(old); + } + count = keep; + } + + T& operator[](int id) { return get(id); } + + const T& operator[](int id) const { return getconst(id); } + + operator T*() { return ptr; } + + operator const T*() const { return ptr; } + + T& append(const T& item) { return (get(count) = item); } + + // insert is not safe for non pod types!!! + void insert(int pos) { + if (pos < 0 || pos > count) return; + if (count >= bufferlen - 4) { + resize(count); + } + memmove(ptr + pos + 1, ptr + pos, sizeof(T) * (count - pos)); + memset(ptr + pos, 0, sizeof(T)); + new (ptr + pos) T(); + count++; + } + + void insert(int pos, const T& item) { + if (pos < 0 || pos > count) return; + if (count >= bufferlen - 4) { + resize(count); + } + memmove(ptr + pos + 1, ptr + pos, sizeof(T) * (size_t)(count - pos)); + memset(ptr + pos, 0, sizeof(T)); + new (ptr + pos) T(); + ptr[pos] = item; + count++; + } + + void remove(int id) { + if (id < 0 || id >= count) return; + count--; + ptr[id].~T(); + for (int i = id; i < count; i++) { + ptr[i] = static_cast(ptr[i + 1]); + } + } + + autoarray() { + ptr = (T*)malloc(sizeof(T) * default_size); + memset(ptr, 0, default_size * sizeof(T)); + bufferlen = default_size; + count = 0; + } + + ~autoarray() { + for (int i = 0; i < count; i++) ptr[i].~T(); + free(ptr); + } #ifdef SERIALIZER - void serialize(serializer& s) - { - if (s.serializing) s(count); - else - { - int i; - s(i); - get(i - 1); - } - for (int i = 0;i < count;i++) s(ptr[i]); - } + void serialize(serializer& s) { + if (s.serializing) + s(count); + else { + int i; + s(i); + get(i - 1); + } + for (int i = 0; i < count; i++) s(ptr[i]); + } #endif #define SERIALIZER_BANNED }; diff --git a/src/asar/crc32.cpp b/src/asar/crc32.cpp index cbaa3a63..049e892e 100644 --- a/src/asar/crc32.cpp +++ b/src/asar/crc32.cpp @@ -1,6 +1,6 @@ /* -* Generic crc32 helper function -*/ + * Generic crc32 helper function + */ #include "crc32.h" @@ -8,114 +8,100 @@ borrowed the tables directly, and made some minor changes to the crc32-function (including changing the interface). //ylo */ - /* ============================================================= */ - /* COPYRIGHT (C) 1986 Gary S. Brown. You may use this program, or */ - /* code or tables extracted from it, as desired without restriction. */ - /* */ - /* First, the polynomial itself and its table of feedback terms. The */ - /* polynomial is */ - /* X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0 */ - /* */ - /* Note that we take it "backwards" and put the highest-order term in */ - /* the lowest-order bit. The X^32 term is "implied"; the LSB is the */ - /* X^31 term, etc. The X^0 term (usually shown as "+1") results in */ - /* the MSB being 1. */ - /* */ - /* Note that the usual hardware shift register implementation, which */ - /* is what we're using (we're merely optimizing it by doing eight-bit */ - /* chunks at a time) shifts bits into the lowest-order term. In our */ - /* implementation, that means shifting towards the right. Why do we */ - /* do it this way? Because the calculated CRC must be transmitted in */ - /* order from highest-order term to lowest-order term. UARTs transmit */ - /* characters in order from LSB to MSB. By storing the CRC this way, */ - /* we hand it to the UART in the order low-byte to high-byte; the UART */ - /* sends each low-bit to hight-bit; and the result is transmission bit */ - /* by bit from highest- to lowest-order term without requiring any bit */ - /* shuffling on our part. Reception works similarly. */ - /* */ - /* The feedback terms table consists of 256, 32-bit entries. Notes: */ - /* */ - /* The table can be generated at runtime if desired; code to do so */ - /* is shown later. It might not be obvious, but the feedback */ - /* terms simply represent the results of eight shift/xor opera- */ - /* tions for all combinations of data and CRC register values. */ - /* */ - /* The values must be right-shifted by eight bits by the "updcrc" */ - /* logic; the shift must be unsigned (bring in zeroes). On some */ - /* hardware you could probably optimize the shift in assembler by */ - /* using byte-swap instructions. */ - /* polynomial $edb88320 */ - /* */ - /* -------------------------------------------------------------------- */ +/* ============================================================= */ +/* COPYRIGHT (C) 1986 Gary S. Brown. You may use this program, or */ +/* code or tables extracted from it, as desired without restriction. */ +/* */ +/* First, the polynomial itself and its table of feedback terms. The */ +/* polynomial is */ +/* X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0 */ +/* */ +/* Note that we take it "backwards" and put the highest-order term in */ +/* the lowest-order bit. The X^32 term is "implied"; the LSB is the */ +/* X^31 term, etc. The X^0 term (usually shown as "+1") results in */ +/* the MSB being 1. */ +/* */ +/* Note that the usual hardware shift register implementation, which */ +/* is what we're using (we're merely optimizing it by doing eight-bit */ +/* chunks at a time) shifts bits into the lowest-order term. In our */ +/* implementation, that means shifting towards the right. Why do we */ +/* do it this way? Because the calculated CRC must be transmitted in */ +/* order from highest-order term to lowest-order term. UARTs transmit */ +/* characters in order from LSB to MSB. By storing the CRC this way, */ +/* we hand it to the UART in the order low-byte to high-byte; the UART */ +/* sends each low-bit to hight-bit; and the result is transmission bit */ +/* by bit from highest- to lowest-order term without requiring any bit */ +/* shuffling on our part. Reception works similarly. */ +/* */ +/* The feedback terms table consists of 256, 32-bit entries. Notes: */ +/* */ +/* The table can be generated at runtime if desired; code to do so */ +/* is shown later. It might not be obvious, but the feedback */ +/* terms simply represent the results of eight shift/xor opera- */ +/* tions for all combinations of data and CRC register values. */ +/* */ +/* The values must be right-shifted by eight bits by the "updcrc" */ +/* logic; the shift must be unsigned (bring in zeroes). On some */ +/* hardware you could probably optimize the shift in assembler by */ +/* using byte-swap instructions. */ +/* polynomial $edb88320 */ +/* */ +/* -------------------------------------------------------------------- */ static uint32_t crc32_tab[] = { - 0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L, - 0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L, - 0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L, - 0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL, - 0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L, - 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L, - 0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L, - 0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL, - 0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L, - 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL, - 0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L, - 0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L, - 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L, - 0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL, - 0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL, - 0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L, - 0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL, - 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L, - 0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L, - 0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L, - 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL, - 0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L, - 0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L, - 0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL, - 0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L, - 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L, - 0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L, - 0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L, - 0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L, - 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL, - 0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL, - 0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L, - 0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L, - 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL, - 0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL, - 0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L, - 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL, - 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L, - 0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL, - 0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L, - 0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL, - 0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L, - 0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L, - 0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL, - 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L, - 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L, - 0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L, - 0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L, - 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L, - 0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L, - 0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL, - 0x2d02ef8dL -}; + 0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L, 0x706af48fL, + 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L, 0xe0d5e91eL, 0x97d2d988L, + 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L, 0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, + 0xf3b97148L, 0x84be41deL, 0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, + 0x136c9856L, 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L, + 0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L, 0xa2677172L, + 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL, 0x35b5a8faL, 0x42b2986cL, + 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L, 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, + 0x26d930acL, 0x51de003aL, 0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, + 0xcfba9599L, 0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L, + 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L, 0x01db7106L, + 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL, 0x9fbfe4a5L, 0xe8b8d433L, + 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL, 0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, + 0x91646c97L, 0xe6635c01L, 0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, + 0x6c0695edL, 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L, + 0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L, 0xfbd44c65L, + 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L, 0x4adfa541L, 0x3dd895d7L, + 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL, 0x346ed9fcL, 0xad678846L, 0xda60b8d0L, + 0x44042d73L, 0x33031de5L, 0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, + 0xbe0b1010L, 0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL, + 0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L, 0x2eb40d81L, + 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L, 0x03b6e20cL, 0x74b1d29aL, + 0xead54739L, 0x9dd277afL, 0x04db2615L, 0x73dc1683L, 0xe3630b12L, 0x94643b84L, + 0x0d6d6a3eL, 0x7a6a5aa8L, 0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, + 0xf00f9344L, 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL, + 0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL, 0x67dd4accL, + 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L, 0xd6d6a3e8L, 0xa1d1937eL, + 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L, 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, + 0xd80d2bdaL, 0xaf0a1b4cL, 0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, + 0x316e8eefL, 0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L, + 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL, 0xb2bd0b28L, + 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L, 0x2cd99e8bL, 0x5bdeae1dL, + 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL, 0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, + 0x72076785L, 0x05005713L, 0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, + 0x92d28e9bL, 0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L, + 0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L, 0x18b74777L, + 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL, 0x8f659effL, 0xf862ae69L, + 0x616bffd3L, 0x166ccf45L, 0xa00ae278L, 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, + 0xa7672661L, 0xd06016f7L, 0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, + 0x40df0b66L, 0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L, + 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L, 0xcdd70693L, + 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L, 0x5d681b02L, 0x2a6f2b94L, + 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL, 0x2d02ef8dL}; /* Return a 32-bit CRC of the contents of the buffer. */ -uint32_t crc32(const uint8_t *s, unsigned int len) -{ - unsigned int i; - uint32_t crc32val; +uint32_t crc32(const uint8_t* s, unsigned int len) { + unsigned int i; + uint32_t crc32val; - crc32val = (uint32_t)~0; - for (i = 0; i < len; i ++) - { - crc32val = - crc32_tab[(crc32val ^ s[i]) & 0xff] ^ - (crc32val >> 8); - } - return ~crc32val; + crc32val = (uint32_t)~0; + for (i = 0; i < len; i++) { + crc32val = crc32_tab[(crc32val ^ s[i]) & 0xff] ^ (crc32val >> 8); + } + return ~crc32val; } diff --git a/src/asar/crc32.h b/src/asar/crc32.h index 95d89fb5..9c919962 100644 --- a/src/asar/crc32.h +++ b/src/asar/crc32.h @@ -1,9 +1,9 @@ /* -* Generic crc32 helper function -*/ + * Generic crc32 helper function + */ #pragma once #include -extern uint32_t crc32(const uint8_t *s, unsigned int len); +extern uint32_t crc32(const uint8_t* s, unsigned int len); diff --git a/src/asar/errors.cpp b/src/asar/errors.cpp index 432e7a2a..b5f5d17a 100644 --- a/src/asar/errors.cpp +++ b/src/asar/errors.cpp @@ -1,379 +1,448 @@ -#include "asar.h" #include #include +#include "asar.h" #include "interface-shared.h" static int asar_num_errors = 0; -struct asar_error_mapping -{ - asar_error_id errid; - const char* name; - const char* message; - - asar_error_mapping(asar_error_id inerrid, const char* iname, const char* inmessage) - { - ++asar_num_errors; - - errid = inerrid; - name = iname; - message = inmessage; - - // RPG Hacker: Sanity check. This makes sure that the order - // of entries in asar_errors matches with the order of - // constants in asar_error_id. This is important because - // we access asar_errors as an array. - // Would love to do this via static_assert(), but can't - // think of a way to do so. - assert(errid - error_id_start == asar_num_errors); - } +struct asar_error_mapping { + asar_error_id errid; + const char* name; + const char* message; + + asar_error_mapping(asar_error_id inerrid, const char* iname, + const char* inmessage) { + ++asar_num_errors; + + errid = inerrid; + name = iname; + message = inmessage; + + // RPG Hacker: Sanity check. This makes sure that the order + // of entries in asar_errors matches with the order of + // constants in asar_error_id. This is important because + // we access asar_errors as an array. + // Would love to do this via static_assert(), but can't + // think of a way to do so. + assert(errid - error_id_start == asar_num_errors); + } }; // Keep in sync with asar_error_id. // Both, enum mapping and order, must match. -#define ERR(name) error_id_ ## name, "E" #name -static asar_error_mapping asar_errors[] = -{ - { ERR(limit_reached), "Over %d errors detected. Aborting." }, - { ERR(werror), "One or more warnings was detected with werror on." }, - - { ERR(16mb_rom_limit), "Can't create ROMs larger than 16MB." }, - { ERR(buffer_too_small), "The given buffer is too small to contain the resulting ROM." }, - { ERR(params_null), "params passed to asar_patch_ex() is null." }, - { ERR(params_invalid_size), "Size of params passed to asar_patch_ex() is invalid." }, - { ERR(cmdl_define_invalid), "Invalid define name in %s: '%s'." }, - { ERR(cmdl_define_override), "%s '%s' overrides a previous define. Did you specify the same define twice?" }, - { ERR(create_rom_failed), "Couldn't create ROM." }, - { ERR(open_rom_failed), "Couldn't open ROM." }, - { ERR(open_rom_not_smw_extension), "Doesn't look like an SMW ROM. (Maybe its extension is wrong?)" }, - { ERR(open_rom_not_smw_header), "Doesn't look like an SMW ROM. (Maybe it's headered?)" }, - { ERR(stddefines_no_identifier), "stddefines.txt contains a line with a value, but no identifier." }, - { ERR(stddefine_after_closing_quote), "Broken std defines. (Something after closing quote)" }, - - { ERR(failed_to_open_file), "Failed to open file '%s'." }, - { ERR(file_not_found), "File '%s' wasn't found." }, - { ERR(readfile_1_to_4_bytes), "Can only read chunks of 1 to 4 bytes." }, - { ERR(canreadfile_0_bytes), "Number of bytes to check must be > 0." }, - { ERR(file_offset_out_of_bounds), "File offset %s out of bounds for file '%s'." }, - { ERR(rep_at_file_end), "rep at the end of a file" }, - - { ERR(mismatched_parentheses), "Mismatched parentheses." }, - { ERR(invalid_hex_value), "Invalid hex value." }, - { ERR(invalid_binary_value), "Invalid binary value." }, - { ERR(invalid_character), "Invalid character." }, - { ERR(string_literal_not_terminated), "String literal not terminated." }, - { ERR(malformed_function_call), "Malformed function call." }, - { ERR(invalid_number), "Invalid number." }, - { ERR(garbage_near_quoted_string), "Garbage near quoted string." }, - { ERR(mismatched_quotes), "Mismatched quotes." }, - - { ERR(rom_too_short), "ROM is too short to have a title. (Expected '%s')" }, - { ERR(rom_title_incorrect), "ROM title is incorrect. Expected '%s', got '%s'." }, - - { ERR(bank_border_crossed), "A bank border was crossed, SNES address $%06X." }, - - { ERR(start_of_file), "This command may only be used at the start of a file." }, - { ERR(xkas_asar_conflict), "Using @xkas and @asar in the same patch is not supported." }, - { ERR(invalid_version_number), "Invalid version number." }, - { ERR(asar_too_old), "This version of Asar is too old for this patch." }, - - { ERR(relative_branch_out_of_bounds), "Relative branch out of bounds. (Distance is %s)." }, - { ERR(snes_address_doesnt_map_to_rom), "SNES address %s doesn't map to ROM." }, - { ERR(snes_address_out_of_bounds), "SNES address %s out of bounds." }, - { ERR(invalid_tcall), "Invalid tcall." }, - { ERR(use_xplus), "Use (x+) instead." }, - { ERR(opcode_length_too_long), "Opcode length is too long." }, - { ERR(superfx_invalid_short_address), "Invalid short address parameter: $%s. (Must be even number and $0000-$01FE)" }, - { ERR(superfx_invalid_register), "Invalid register for opcode; valid range is %d-%d." }, - { ERR(invalid_opcode_length), "Invalid opcode length specification." }, - { ERR(invalid_mapper), "Invalid mapper." }, - - { ERR(nan), "NaN encountered." }, - { ERR(division_by_zero), "Division by zero." }, - { ERR(modulo_by_zero), "Modulo by zero." }, - { ERR(unknown_operator), "Unknown operator." }, - { ERR(invalid_input), "Invalid input." }, - - { ERR(invalid_function_name), "Invalid function name." }, - { ERR(function_not_found), "Function '%s' wasn't found." }, - { ERR(function_redefined), "Function '%s' redefined." }, - { ERR(broken_function_declaration), "Broken function declaration." }, - { ERR(wrong_num_parameters), "Wrong number of parameters to function." }, - { ERR(invalid_param_name), "Invalid parameter name." }, - { ERR(math_invalid_type), "Wrong type for parameter %s, expected %s." }, - - { ERR(invalid_label_name), "Invalid label name." }, - { ERR(label_not_found), "Label '%s' wasn't found." }, - { ERR(label_redefined), "Label '%s' redefined." }, - { ERR(broken_label_definition), "Broken label definition." }, - { ERR(label_cross_assignment), "Setting labels to other non-static labels is not valid." }, - { ERR(macro_label_outside_of_macro), "Macro label outside of a macro." }, - { ERR(label_on_third_pass), "Internal error: A label was created on the third pass. Please create an issue on the official GitHub repository and attach a patch which reproduces the error." }, - { ERR(label_moving), "Internal error: A label is moving around. Please create an issue on the official GitHub repository and attach a patch which reproduces the error." }, - - { ERR(invalid_namespace_name), "Invalid namespace name." }, - { ERR(invalid_namespace_use), "Invalid use of namespace command." }, - - { ERR(invalid_struct_name), "Invalid struct name." }, - { ERR(struct_not_found), "Struct '%s' wasn't found." }, - { ERR(struct_redefined), "Struct '%s' redefined." }, - { ERR(struct_invalid_parent_name), "Invalid parent name." }, - { ERR(invalid_label_missing_closer), "Invalid label name, missing array closer." }, - { ERR(multiple_subscript_operators), "Multiple subscript operators is invalid." }, - { ERR(invalid_subscript), "Invalid array subscript after first scope resolution." }, - { ERR(label_missing_parent), "This label has no parent." }, - { ERR(array_invalid_inside_structs), "Array syntax invalid inside structs." }, - { ERR(struct_without_endstruct), "struct without matching endstruct." }, - { ERR(nested_struct), "Can not nest structs." }, - { ERR(missing_struct_params), "Missing struct parameters." }, - { ERR(too_many_struct_params), "Too many struct parameters." }, - { ERR(missing_extends), "Missing extends keyword." }, - { ERR(invalid_endstruct_count), "Invalid endstruct parameter count." }, - { ERR(expected_align), "Expected align parameter." }, - { ERR(endstruct_without_struct), "endstruct can only be used in combination with struct." }, - { ERR(alignment_too_small), "Alignment must be >= 1." }, - - { ERR(invalid_define_name), "Invalid define name." }, - { ERR(define_not_found), "Define '%s' wasn't found." }, - { ERR(broken_define_declaration), "Broken define declaration." }, - { ERR(overriding_builtin_define), "Trying to set define '%s', which is the name of a built-in define and thus can't be modified." }, - { ERR(define_label_math), "!Define #= Label is not allowed with non-static labels." }, - { ERR(mismatched_braces), "Mismatched braces." }, - - { ERR(invalid_macro_name), "Invalid macro name." }, - { ERR(macro_not_found), "Macro '%s' wasn't found." }, - { ERR(macro_redefined), "Macro '%s' redefined." }, - { ERR(broken_macro_declaration), "Broken macro declaration." }, - { ERR(invalid_macro_param_name), "Invalid macro parameter name." }, - { ERR(macro_param_not_found), "Macro parameter '%s' wasn't found.%s" }, - { ERR(macro_param_redefined), "Macro parameter '%s' redefined" }, - { ERR(broken_macro_usage), "Broken macro usage." }, - { ERR(macro_wrong_num_params), "Wrong number of parameters to macro." }, - { ERR(broken_macro_contents), "Broken macro contents." }, - { ERR(rep_at_macro_end), "rep or if at the end of a macro." }, - { ERR(nested_macro_definition), "Nested macro definition: Trying to define a macro inside '%s', which is not supported." }, - { ERR(misplaced_endmacro), "Misplaced endmacro." }, - { ERR(unclosed_macro), "Unclosed macro: '%s'." }, - - { ERR(label_in_conditional), "Non-static label in %s command." }, - { ERR(broken_conditional), "Broken %s command." }, - { ERR(invalid_condition), "Invalid condition." }, - { ERR(misplaced_elseif), "Misplaced elseif." }, - { ERR(elseif_in_while), "Can't use elseif in a while loop." }, - { ERR(elseif_in_singleline), "Can't use elseif on single-line statements." }, - { ERR(misplaced_endif), "Misplaced endif." }, - { ERR(misplaced_else), "Misplaced else." }, - { ERR(else_in_while_loop), "Can't use else in a while loop." }, - { ERR(unclosed_if), "Unclosed if statement." }, - - { ERR(unknown_command), "Unknown command." }, - { ERR(command_disabled), "This command is disabled." }, - - { ERR(broken_incbin), "Broken incbin command." }, - { ERR(incbin_64kb_limit), "Can't include more than 64 kilobytes at once." }, - { ERR(recursion_limit), "Recursion limit reached." }, - { ERR(command_in_non_root_file), "This command may only be used in the root file." }, - { ERR(cant_be_main_file), "This file may not be used as the main file.%s" }, - { ERR(no_labels_here), "Can't use non-static labels here." }, - - { ERR(invalid_freespace_request), "Invalid freespace request." }, - { ERR(no_banks_with_ram_mirrors), "No banks contain the RAM mirrors in hirom or exhirom." }, - { ERR(no_freespace_norom), "Can't find freespace in norom." }, - { ERR(static_freespace_autoclean), "A static freespace must be targeted by at least one autoclean." }, - { ERR(static_freespace_growing), "A static freespace may not grow." }, - { ERR(no_freespace_in_mapped_banks), "No freespace found in the mapped banks. (Requested size: %s)" }, - { ERR(no_freespace), "No freespace found. (Requested size: %s)" }, - { ERR(freespace_limit_reached), "A patch may not contain more than %d freespaces." }, - - { ERR(prot_not_at_freespace_start), "PROT must be used at the start of a freespace block." }, - { ERR(prot_too_many_entries), "Too many entries to PROT." }, - - { ERR(autoclean_in_freespace), "autoclean used in freespace." }, - { ERR(autoclean_label_at_freespace_end), "Don't autoclean a label at the end of a freespace block, you'll remove some stuff you're not supposed to remove." }, - { ERR(broken_autoclean), "Broken autoclean command." }, - - { ERR(pulltable_without_table), "Using pulltable when there is no table on the stack yet." }, - { ERR(invalid_table_file), "Invalid table file. Invalid entry on line: %i" }, - - { ERR(pad_in_freespace), "pad does not make sense in a freespaced code." }, - - { ERR(org_label_invalid), "org Label is not valid." }, - { ERR(org_label_forward), "org Label is only valid for labels earlier in the patch." }, - - { ERR(skip_label_invalid), "skip Label is not valid." }, - - { ERR(spc700_inline_no_base), "base is not implemented for architecture spc700-inline." }, - { ERR(base_label_invalid), "base Label is not valid." }, - - { ERR(rep_label), "rep Label is not valid." }, - - { ERR(pushpc_without_pullpc), "pushpc without matching pullpc." }, - { ERR(pullpc_without_pushpc), "pullpc without matching pushpc." }, - { ERR(pullpc_different_arch), "pullpc in another architecture than the pushpc." }, - { ERR(pullbase_without_pushbase), "pullbase without matching pushbase." }, - - { ERR(invalid_math), "Invalid math command." }, - { ERR(invalid_warn), "Invalid warn command." }, - { ERR(invalid_check), "Invalid check command." }, - - { ERR(warnpc_in_freespace), "warnpc used in freespace." }, - { ERR(warnpc_broken_param), "Broken warnpc parameter." }, - { ERR(warnpc_failed), "warnpc failed: Current position (%s) is after end position (%s)." }, - { ERR(warnpc_failed_equal), "warnpc failed: Current position (%s) is equal to end position (%s)." }, - - { ERR(assertion_failed), "Assertion failed%s" }, - - { ERR(error_command), "error command%s" }, - - { ERR(invalid_print_function_syntax), "Invalid printable string syntax." }, - { ERR(unknown_variable), "Unknown variable." }, - - { ERR(invalid_warning_id), "Warning '%s' (passed to %s) doesn't exist." }, - - { ERR(pushwarnings_without_pullwarnings), "warnings push without matching warnings pull." }, - { ERR(pullwarnings_without_pushwarnings), "warnings pull without matching warnings push." }, - - { ERR(failed_to_open_file_access_denied), "Failed to open file '%s'. Access denied." }, - { ERR(failed_to_open_not_regular_file), "Failed to open file '%s'. Not a regular file (did you try to use a directory?)" }, - - { ERR(bad_dp_base), "The dp base should be page aligned (i.e. a multiple of 256)"}, - { ERR(bad_dp_optimize), "Bad dp optimize value %s, expected: [none, ram, always]"}, - { ERR(bad_address_optimize), "Bad dp optimize value %s, expected: [default, ram, mirrors]"}, - { ERR(bad_optimize), "Bad optimize value %s, expected: [dp, address]"}, - - { ERR(require_parameter), "Missing required function parameter"}, - { ERR(expected_parameter), "Not enough parameters in calling of function %s"}, - { ERR(unexpected_parameter), "Too many parameters in calling of function %s"}, - { ERR(duplicate_param_name), "Duplicated parameter name: %s in creation of function %s"}, - - { ERR(invalid_alignment), "Invalid alignment. Expected a power of 2." }, - { ERR(alignment_too_big), "Requested alignment too large." }, - - { ERR(negative_shift), "Bitshift amount is negative." }, - - { ERR(macro_not_varadic), "Invalid use of %s, active macro is not variadic." }, - { ERR(vararg_sizeof_nomacro), "Invalid use of sizeof(...), no active macro." }, - { ERR(macro_wrong_min_params), "Variadic macro call with too few parameters" }, - { ERR(vararg_out_of_bounds), "Variadic macro parameter requested is out of bounds." }, - { ERR(vararg_must_be_last), "Variadic macro parameter must be the last parameter." }, - { ERR(invalid_global_label), "Global label definition contains an invalid label [%s]."}, - - { ERR(spc700_addr_out_of_range), "Address %s out of range for instruction, valid range is 0000-1FFF" }, - { ERR(label_ambiguous), "Label (%s) location is ambiguous due to straddling optimization border." }, - - { ERR(feature_unavaliable_in_spcblock), "This feature may not be used while an spcblock is active" }, - { ERR(endspcblock_without_spcblock), "Use of endspcblock without matching spcblock" }, - { ERR(missing_endspcblock), "Use of endspcblock without matching spcblock" }, - { ERR(spcblock_bad_arch), "spcblock only valid inside spc700 arch" }, - { ERR(spcblock_inside_struct), "Can not start an spcblock while a struct is still open" }, - { ERR(spcblock_too_few_args), "Too few args passed to spcblock" }, - { ERR(spcblock_too_many_args), "Too many args passed to spcblock" }, - { ERR(unknown_spcblock_type), "Unknown spcblock format" }, - { ERR(custom_spcblock_missing_macro), "Custom spcblock types must refer to a valid macro" }, - { ERR(spcblock_macro_doesnt_exist), "Macro specified to custom spcblock was not found"}, - { ERR(extra_spcblock_arg_for_type), "Only custom spcblock type takes a fourth argument" }, - { ERR(spcblock_macro_must_be_varadic), "Custom spcblock macros must be variadic" }, - { ERR(spcblock_macro_invalid_static_args), "Custom spcblock macros must have three static arguments" }, - { ERR(spcblock_custom_types_incomplete), "Custom spcblock types are not yet supported. One day." }, - { ERR(startpos_without_spcblock), "The startpos command is only valid in spcblocks" }, - { ERR(internal_error), "An internal asar error occured (%s). Send help." }, - - { ERR(pushns_without_pullns), "pushns without matching pullns." }, - { ERR(pullns_without_pushns), "pullns without matching pushns." }, - - { ERR(label_forward), "The use of forward labels is not allowed in this context." }, - { ERR(undefined_char), "'%s' is not defined in the character table" }, - - { ERR(invalid_utf8), "Invalid text encoding detected. Asar expects UTF-8-encoded text. Please re-save this file in a text editor of choice using UTF-8 encoding." }, - { ERR(cmdl_utf16_to_utf8_failed), "UTF-16 to UTF-8 string conversion failed: %s." }, - - { ERR(broken_command), "Broken %s command. %s" }, - { ERR(phantom_error), "A phantom error occurred. This is an Asar bug, please report it: https://github.com/RPGHacker/asar/issues" }, - - { ERR(oob), "Operation out of bounds: Requested index %d for object of size %d" }, - - { ERR(unclosed_vararg), "Variadic macro parameter wasn't closed properly." }, - { ERR(invalid_vararg), "Trying to use variadic macro parameter syntax to resolve a non variadic argument." }, - - { ERR(macro_param_outside_macro), "Reference to macro parameter outside of macro" }, - { ERR(invalid_depth_resolve), "Invalid %s resolution depth: Trying to backwards-resolve a %s using %i '^', but current scope only supports up to %i '^'." }, - - { ERR(platform_paths), "Platform-specific paths aren'supported. Please use platform-independent paths (use / instead of \\)." }, +#define ERR(name) error_id_##name, "E" #name +static asar_error_mapping asar_errors[] = { + {ERR(limit_reached), "Over %d errors detected. Aborting."}, + {ERR(werror), "One or more warnings was detected with werror on."}, + + {ERR(16mb_rom_limit), "Can't create ROMs larger than 16MB."}, + {ERR(buffer_too_small), + "The given buffer is too small to contain the resulting ROM."}, + {ERR(params_null), "params passed to asar_patch_ex() is null."}, + {ERR(params_invalid_size), + "Size of params passed to asar_patch_ex() is invalid."}, + {ERR(cmdl_define_invalid), "Invalid define name in %s: '%s'."}, + {ERR(cmdl_define_override), + "%s '%s' overrides a previous define. Did you specify the same define twice?"}, + {ERR(create_rom_failed), "Couldn't create ROM."}, + {ERR(open_rom_failed), "Couldn't open ROM."}, + {ERR(open_rom_not_smw_extension), + "Doesn't look like an SMW ROM. (Maybe its extension is wrong?)"}, + {ERR(open_rom_not_smw_header), + "Doesn't look like an SMW ROM. (Maybe it's headered?)"}, + {ERR(stddefines_no_identifier), + "stddefines.txt contains a line with a value, but no identifier."}, + {ERR(stddefine_after_closing_quote), + "Broken std defines. (Something after closing quote)"}, + + {ERR(failed_to_open_file), "Failed to open file '%s'."}, + {ERR(file_not_found), "File '%s' wasn't found."}, + {ERR(readfile_1_to_4_bytes), "Can only read chunks of 1 to 4 bytes."}, + {ERR(canreadfile_0_bytes), "Number of bytes to check must be > 0."}, + {ERR(file_offset_out_of_bounds), "File offset %s out of bounds for file '%s'."}, + {ERR(rep_at_file_end), "rep at the end of a file"}, + + {ERR(mismatched_parentheses), "Mismatched parentheses."}, + {ERR(invalid_hex_value), "Invalid hex value."}, + {ERR(invalid_binary_value), "Invalid binary value."}, + {ERR(invalid_character), "Invalid character."}, + {ERR(string_literal_not_terminated), "String literal not terminated."}, + {ERR(malformed_function_call), "Malformed function call."}, + {ERR(invalid_number), "Invalid number."}, + {ERR(garbage_near_quoted_string), "Garbage near quoted string."}, + {ERR(mismatched_quotes), "Mismatched quotes."}, + + {ERR(rom_too_short), "ROM is too short to have a title. (Expected '%s')"}, + {ERR(rom_title_incorrect), "ROM title is incorrect. Expected '%s', got '%s'."}, + + {ERR(bank_border_crossed), "A bank border was crossed, SNES address $%06X."}, + + {ERR(start_of_file), "This command may only be used at the start of a file."}, + {ERR(xkas_asar_conflict), + "Using @xkas and @asar in the same patch is not supported."}, + {ERR(invalid_version_number), "Invalid version number."}, + {ERR(asar_too_old), "This version of Asar is too old for this patch."}, + + {ERR(relative_branch_out_of_bounds), + "Relative branch out of bounds. (Distance is %s)."}, + {ERR(snes_address_doesnt_map_to_rom), "SNES address %s doesn't map to ROM."}, + {ERR(snes_address_out_of_bounds), "SNES address %s out of bounds."}, + {ERR(invalid_tcall), "Invalid tcall."}, + {ERR(use_xplus), "Use (x+) instead."}, + {ERR(opcode_length_too_long), "Opcode length is too long."}, + {ERR(superfx_invalid_short_address), + "Invalid short address parameter: $%s. (Must be even number and $0000-$01FE)"}, + {ERR(superfx_invalid_register), + "Invalid register for opcode; valid range is %d-%d."}, + {ERR(invalid_opcode_length), "Invalid opcode length specification."}, + {ERR(invalid_mapper), "Invalid mapper."}, + + {ERR(nan), "NaN encountered."}, + {ERR(division_by_zero), "Division by zero."}, + {ERR(modulo_by_zero), "Modulo by zero."}, + {ERR(unknown_operator), "Unknown operator."}, + {ERR(invalid_input), "Invalid input."}, + + {ERR(invalid_function_name), "Invalid function name."}, + {ERR(function_not_found), "Function '%s' wasn't found."}, + {ERR(function_redefined), "Function '%s' redefined."}, + {ERR(broken_function_declaration), "Broken function declaration."}, + {ERR(wrong_num_parameters), "Wrong number of parameters to function."}, + {ERR(invalid_param_name), "Invalid parameter name."}, + {ERR(math_invalid_type), "Wrong type for parameter %s, expected %s."}, + + {ERR(invalid_label_name), "Invalid label name."}, + {ERR(label_not_found), "Label '%s' wasn't found."}, + {ERR(label_redefined), "Label '%s' redefined."}, + {ERR(broken_label_definition), "Broken label definition."}, + {ERR(label_cross_assignment), + "Setting labels to other non-static labels is not valid."}, + {ERR(macro_label_outside_of_macro), "Macro label outside of a macro."}, + {ERR(label_on_third_pass), + "Internal error: A label was created on the third pass. Please create an " + "issue on the official GitHub repository and attach a patch which reproduces " + "the error."}, + {ERR(label_moving), + "Internal error: A label is moving around. Please create an issue on the " + "official GitHub repository and attach a patch which reproduces the error."}, + + {ERR(invalid_namespace_name), "Invalid namespace name."}, + {ERR(invalid_namespace_use), "Invalid use of namespace command."}, + + {ERR(invalid_struct_name), "Invalid struct name."}, + {ERR(struct_not_found), "Struct '%s' wasn't found."}, + {ERR(struct_redefined), "Struct '%s' redefined."}, + {ERR(struct_invalid_parent_name), "Invalid parent name."}, + {ERR(invalid_label_missing_closer), + "Invalid label name, missing array closer."}, + {ERR(multiple_subscript_operators), "Multiple subscript operators is invalid."}, + {ERR(invalid_subscript), + "Invalid array subscript after first scope resolution."}, + {ERR(label_missing_parent), "This label has no parent."}, + {ERR(array_invalid_inside_structs), "Array syntax invalid inside structs."}, + {ERR(struct_without_endstruct), "struct without matching endstruct."}, + {ERR(nested_struct), "Can not nest structs."}, + {ERR(missing_struct_params), "Missing struct parameters."}, + {ERR(too_many_struct_params), "Too many struct parameters."}, + {ERR(missing_extends), "Missing extends keyword."}, + {ERR(invalid_endstruct_count), "Invalid endstruct parameter count."}, + {ERR(expected_align), "Expected align parameter."}, + {ERR(endstruct_without_struct), + "endstruct can only be used in combination with struct."}, + {ERR(alignment_too_small), "Alignment must be >= 1."}, + + {ERR(invalid_define_name), "Invalid define name."}, + {ERR(define_not_found), "Define '%s' wasn't found."}, + {ERR(broken_define_declaration), "Broken define declaration."}, + {ERR(overriding_builtin_define), + "Trying to set define '%s', which is the name of a built-in define and thus " + "can't be modified."}, + {ERR(define_label_math), + "!Define #= Label is not allowed with non-static labels."}, + {ERR(mismatched_braces), "Mismatched braces."}, + + {ERR(invalid_macro_name), "Invalid macro name."}, + {ERR(macro_not_found), "Macro '%s' wasn't found."}, + {ERR(macro_redefined), "Macro '%s' redefined."}, + {ERR(broken_macro_declaration), "Broken macro declaration."}, + {ERR(invalid_macro_param_name), "Invalid macro parameter name."}, + {ERR(macro_param_not_found), "Macro parameter '%s' wasn't found.%s"}, + {ERR(macro_param_redefined), "Macro parameter '%s' redefined"}, + {ERR(broken_macro_usage), "Broken macro usage."}, + {ERR(macro_wrong_num_params), "Wrong number of parameters to macro."}, + {ERR(broken_macro_contents), "Broken macro contents."}, + {ERR(rep_at_macro_end), "rep or if at the end of a macro."}, + {ERR(nested_macro_definition), + "Nested macro definition: Trying to define a macro inside '%s', which is not " + "supported."}, + {ERR(misplaced_endmacro), "Misplaced endmacro."}, + {ERR(unclosed_macro), "Unclosed macro: '%s'."}, + + {ERR(label_in_conditional), "Non-static label in %s command."}, + {ERR(broken_conditional), "Broken %s command."}, + {ERR(invalid_condition), "Invalid condition."}, + {ERR(misplaced_elseif), "Misplaced elseif."}, + {ERR(elseif_in_while), "Can't use elseif in a while loop."}, + {ERR(elseif_in_singleline), "Can't use elseif on single-line statements."}, + {ERR(misplaced_endif), "Misplaced endif."}, + {ERR(misplaced_else), "Misplaced else."}, + {ERR(else_in_while_loop), "Can't use else in a while loop."}, + {ERR(unclosed_if), "Unclosed if statement."}, + + {ERR(unknown_command), "Unknown command."}, + {ERR(command_disabled), "This command is disabled."}, + + {ERR(broken_incbin), "Broken incbin command."}, + {ERR(incbin_64kb_limit), "Can't include more than 64 kilobytes at once."}, + {ERR(recursion_limit), "Recursion limit reached."}, + {ERR(command_in_non_root_file), + "This command may only be used in the root file."}, + {ERR(cant_be_main_file), "This file may not be used as the main file.%s"}, + {ERR(no_labels_here), "Can't use non-static labels here."}, + + {ERR(invalid_freespace_request), "Invalid freespace request."}, + {ERR(no_banks_with_ram_mirrors), + "No banks contain the RAM mirrors in hirom or exhirom."}, + {ERR(no_freespace_norom), "Can't find freespace in norom."}, + {ERR(static_freespace_autoclean), + "A static freespace must be targeted by at least one autoclean."}, + {ERR(static_freespace_growing), "A static freespace may not grow."}, + {ERR(no_freespace_in_mapped_banks), + "No freespace found in the mapped banks. (Requested size: %s)"}, + {ERR(no_freespace), "No freespace found. (Requested size: %s)"}, + {ERR(freespace_limit_reached), + "A patch may not contain more than %d freespaces."}, + + {ERR(prot_not_at_freespace_start), + "PROT must be used at the start of a freespace block."}, + {ERR(prot_too_many_entries), "Too many entries to PROT."}, + + {ERR(autoclean_in_freespace), "autoclean used in freespace."}, + {ERR(autoclean_label_at_freespace_end), + "Don't autoclean a label at the end of a freespace block, you'll remove some " + "stuff you're not supposed to remove."}, + {ERR(broken_autoclean), "Broken autoclean command."}, + + {ERR(pulltable_without_table), + "Using pulltable when there is no table on the stack yet."}, + {ERR(invalid_table_file), "Invalid table file. Invalid entry on line: %i"}, + + {ERR(pad_in_freespace), "pad does not make sense in a freespaced code."}, + + {ERR(org_label_invalid), "org Label is not valid."}, + {ERR(org_label_forward), + "org Label is only valid for labels earlier in the patch."}, + + {ERR(skip_label_invalid), "skip Label is not valid."}, + + {ERR(spc700_inline_no_base), + "base is not implemented for architecture spc700-inline."}, + {ERR(base_label_invalid), "base Label is not valid."}, + + {ERR(rep_label), "rep Label is not valid."}, + + {ERR(pushpc_without_pullpc), "pushpc without matching pullpc."}, + {ERR(pullpc_without_pushpc), "pullpc without matching pushpc."}, + {ERR(pullpc_different_arch), "pullpc in another architecture than the pushpc."}, + {ERR(pullbase_without_pushbase), "pullbase without matching pushbase."}, + + {ERR(invalid_math), "Invalid math command."}, + {ERR(invalid_warn), "Invalid warn command."}, + {ERR(invalid_check), "Invalid check command."}, + + {ERR(warnpc_in_freespace), "warnpc used in freespace."}, + {ERR(warnpc_broken_param), "Broken warnpc parameter."}, + {ERR(warnpc_failed), + "warnpc failed: Current position (%s) is after end position (%s)."}, + {ERR(warnpc_failed_equal), + "warnpc failed: Current position (%s) is equal to end position (%s)."}, + + {ERR(assertion_failed), "Assertion failed%s"}, + + {ERR(error_command), "error command%s"}, + + {ERR(invalid_print_function_syntax), "Invalid printable string syntax."}, + {ERR(unknown_variable), "Unknown variable."}, + + {ERR(invalid_warning_id), "Warning '%s' (passed to %s) doesn't exist."}, + + {ERR(pushwarnings_without_pullwarnings), + "warnings push without matching warnings pull."}, + {ERR(pullwarnings_without_pushwarnings), + "warnings pull without matching warnings push."}, + + {ERR(failed_to_open_file_access_denied), + "Failed to open file '%s'. Access denied."}, + {ERR(failed_to_open_not_regular_file), + "Failed to open file '%s'. Not a regular file (did you try to use a " + "directory?)"}, + + {ERR(bad_dp_base), + "The dp base should be page aligned (i.e. a multiple of 256)"}, + {ERR(bad_dp_optimize), + "Bad dp optimize value %s, expected: [none, ram, always]"}, + {ERR(bad_address_optimize), + "Bad dp optimize value %s, expected: [default, ram, mirrors]"}, + {ERR(bad_optimize), "Bad optimize value %s, expected: [dp, address]"}, + + {ERR(require_parameter), "Missing required function parameter"}, + {ERR(expected_parameter), "Not enough parameters in calling of function %s"}, + {ERR(unexpected_parameter), "Too many parameters in calling of function %s"}, + {ERR(duplicate_param_name), + "Duplicated parameter name: %s in creation of function %s"}, + + {ERR(invalid_alignment), "Invalid alignment. Expected a power of 2."}, + {ERR(alignment_too_big), "Requested alignment too large."}, + + {ERR(negative_shift), "Bitshift amount is negative."}, + + {ERR(macro_not_varadic), "Invalid use of %s, active macro is not variadic."}, + {ERR(vararg_sizeof_nomacro), "Invalid use of sizeof(...), no active macro."}, + {ERR(macro_wrong_min_params), "Variadic macro call with too few parameters"}, + {ERR(vararg_out_of_bounds), + "Variadic macro parameter requested is out of bounds."}, + {ERR(vararg_must_be_last), + "Variadic macro parameter must be the last parameter."}, + {ERR(invalid_global_label), + "Global label definition contains an invalid label [%s]."}, + + {ERR(spc700_addr_out_of_range), + "Address %s out of range for instruction, valid range is 0000-1FFF"}, + {ERR(label_ambiguous), + "Label (%s) location is ambiguous due to straddling optimization border."}, + + {ERR(feature_unavaliable_in_spcblock), + "This feature may not be used while an spcblock is active"}, + {ERR(endspcblock_without_spcblock), + "Use of endspcblock without matching spcblock"}, + {ERR(missing_endspcblock), "Use of endspcblock without matching spcblock"}, + {ERR(spcblock_bad_arch), "spcblock only valid inside spc700 arch"}, + {ERR(spcblock_inside_struct), + "Can not start an spcblock while a struct is still open"}, + {ERR(spcblock_too_few_args), "Too few args passed to spcblock"}, + {ERR(spcblock_too_many_args), "Too many args passed to spcblock"}, + {ERR(unknown_spcblock_type), "Unknown spcblock format"}, + {ERR(custom_spcblock_missing_macro), + "Custom spcblock types must refer to a valid macro"}, + {ERR(spcblock_macro_doesnt_exist), + "Macro specified to custom spcblock was not found"}, + {ERR(extra_spcblock_arg_for_type), + "Only custom spcblock type takes a fourth argument"}, + {ERR(spcblock_macro_must_be_varadic), + "Custom spcblock macros must be variadic"}, + {ERR(spcblock_macro_invalid_static_args), + "Custom spcblock macros must have three static arguments"}, + {ERR(spcblock_custom_types_incomplete), + "Custom spcblock types are not yet supported. One day."}, + {ERR(startpos_without_spcblock), + "The startpos command is only valid in spcblocks"}, + {ERR(internal_error), "An internal asar error occured (%s). Send help."}, + + {ERR(pushns_without_pullns), "pushns without matching pullns."}, + {ERR(pullns_without_pushns), "pullns without matching pushns."}, + + {ERR(label_forward), + "The use of forward labels is not allowed in this context."}, + {ERR(undefined_char), "'%s' is not defined in the character table"}, + + {ERR(invalid_utf8), + "Invalid text encoding detected. Asar expects UTF-8-encoded text. Please " + "re-save this file in a text editor of choice using UTF-8 encoding."}, + {ERR(cmdl_utf16_to_utf8_failed), + "UTF-16 to UTF-8 string conversion failed: %s."}, + + {ERR(broken_command), "Broken %s command. %s"}, + {ERR(phantom_error), + "A phantom error occurred. This is an Asar bug, please report it: " + "https://github.com/RPGHacker/asar/issues"}, + + {ERR(oob), "Operation out of bounds: Requested index %d for object of size %d"}, + + {ERR(unclosed_vararg), "Variadic macro parameter wasn't closed properly."}, + {ERR(invalid_vararg), + "Trying to use variadic macro parameter syntax to resolve a non variadic " + "argument."}, + + {ERR(macro_param_outside_macro), + "Reference to macro parameter outside of macro"}, + {ERR(invalid_depth_resolve), + "Invalid %s resolution depth: Trying to backwards-resolve a %s using %i '^', " + "but current scope only supports up to %i '^'."}, + + {ERR(platform_paths), + "Platform-specific paths aren'supported. Please use platform-independent " + "paths (use / instead of \\)."}, }; // RPG Hacker: Sanity check. This makes sure that the element count of asar_error // matches with the number of constants in asar_error_id. This is important, because // we are going to access asar_warnings as an array. -static_assert(sizeof(asar_errors) / sizeof(asar_errors[0]) == error_id_count, "asar_errors and asar_error_id are not in sync"); - -template -void asar_error_template(asar_error_id errid, int whichpass, const char* message) -{ - try - { - error_interface((int)errid, whichpass, message); - t err; - throw err; - } - catch (errnull&) {} +static_assert(sizeof(asar_errors) / sizeof(asar_errors[0]) == error_id_count, + "asar_errors and asar_error_id are not in sync"); + +template +void asar_error_template(asar_error_id errid, int whichpass, const char* message) { + try { + error_interface((int)errid, whichpass, message); + t err; + throw err; + } catch (errnull&) { + } } #if !defined(__clang__) -void(*shutupgcc1)(asar_error_id, int, const char*) = asar_error_template; -void(*shutupgcc2)(asar_error_id, int, const char*) = asar_error_template; -void(*shutupgcc3)(asar_error_id, int, const char*) = asar_error_template; -void(*shutupgcc4)(asar_error_id, int, const char*) = asar_error_template; +void (*shutupgcc1)(asar_error_id, int, const char*) = asar_error_template; +void (*shutupgcc2)(asar_error_id, int, const char*) = asar_error_template; +void (*shutupgcc3)(asar_error_id, int, const char*) = asar_error_template; +void (*shutupgcc4)(asar_error_id, int, const char*) = asar_error_template; #endif -void asar_throw_error(int whichpass, asar_error_type type, asar_error_id errid, ...) -{ - assert(errid > error_id_start && errid < error_id_end); +void asar_throw_error(int whichpass, asar_error_type type, asar_error_id errid, ...) { + assert(errid > error_id_start && errid < error_id_end); - const asar_error_mapping& error = asar_errors[errid - error_id_start - 1]; + const asar_error_mapping& error = asar_errors[errid - error_id_start - 1]; - char error_buffer[1024]; - va_list args; - va_start(args, errid); + char error_buffer[1024]; + va_list args; + va_start(args, errid); #if defined(__clang__) -# pragma clang diagnostic push - // "format string is not a literal". - // The pointer we're passing here should always point to a string literal, - // thus, I think, we can safely ignore this here. -# pragma clang diagnostic ignored "-Wformat-nonliteral" +#pragma clang diagnostic push + // "format string is not a literal". + // The pointer we're passing here should always point to a string literal, + // thus, I think, we can safely ignore this here. +#pragma clang diagnostic ignored "-Wformat-nonliteral" #endif - vsnprintf(error_buffer, sizeof(error_buffer), error.message, args); + vsnprintf(error_buffer, sizeof(error_buffer), error.message, args); #if defined(__clang__) -# pragma clang diagnostic pop +#pragma clang diagnostic pop #endif - va_end(args); - - switch (type) - { - case error_type_null: - asar_error_template(errid, whichpass, error_buffer); - break; - case error_type_block: - asar_error_template(errid, whichpass, error_buffer); - break; - case error_type_line: - asar_error_template(errid, whichpass, error_buffer); - break; - case error_type_fatal: - default: - asar_error_template(errid, whichpass, error_buffer); - break; - } + va_end(args); + + switch (type) { + case error_type_null: + asar_error_template(errid, whichpass, error_buffer); + break; + case error_type_block: + asar_error_template(errid, whichpass, error_buffer); + break; + case error_type_line: + asar_error_template(errid, whichpass, error_buffer); + break; + case error_type_fatal: + default: + asar_error_template(errid, whichpass, error_buffer); + break; + } } -const char* get_error_name(asar_error_id errid) -{ - assert(errid > error_id_start && errid < error_id_end); +const char* get_error_name(asar_error_id errid) { + assert(errid > error_id_start && errid < error_id_end); - const asar_error_mapping& error = asar_errors[errid - error_id_start - 1]; + const asar_error_mapping& error = asar_errors[errid - error_id_start - 1]; - return error.name; + return error.name; } diff --git a/src/asar/errors.h b/src/asar/errors.h index 7fa05b34..baaee9e4 100644 --- a/src/asar/errors.h +++ b/src/asar/errors.h @@ -1,291 +1,289 @@ #pragma once -#define ASAR_ERROR_RANGE_START 5000 +#define ASAR_ERROR_RANGE_START 5000 // NOTE: Don't reorder these. That would change their ID. // If you need to remove one, stub it out. // If you need to add one, add it at the end (before error_id_end). // Keep in sync with asar_errors. -enum asar_error_id : int -{ - error_id_start = ASAR_ERROR_RANGE_START, - - error_id_limit_reached, - error_id_werror, - - error_id_16mb_rom_limit, - error_id_buffer_too_small, - error_id_params_null, - error_id_params_invalid_size, - error_id_cmdl_define_invalid, - error_id_cmdl_define_override, - error_id_create_rom_failed, - error_id_open_rom_failed, - error_id_open_rom_not_smw_extension, - error_id_open_rom_not_smw_header, - error_id_stddefines_no_identifier, - error_id_stddefine_after_closing_quote, - - error_id_failed_to_open_file, - error_id_file_not_found, - error_id_readfile_1_to_4_bytes, - error_id_canreadfile_0_bytes, - error_id_file_offset_out_of_bounds, - error_id_rep_at_file_end, - - error_id_mismatched_parentheses, - error_id_invalid_hex_value, - error_id_invalid_binary_value, - error_id_invalid_character, - error_id_string_literal_not_terminated, - error_id_malformed_function_call, - error_id_invalid_number, - error_id_garbage_near_quoted_string, - error_id_mismatched_quotes, - - error_id_rom_too_short, - error_id_rom_title_incorrect, - - error_id_bank_border_crossed, - - error_id_start_of_file, - error_id_xkas_asar_conflict, - error_id_invalid_version_number, - error_id_asar_too_old, - - error_id_relative_branch_out_of_bounds, - error_id_snes_address_doesnt_map_to_rom, - error_id_snes_address_out_of_bounds, - error_id_invalid_tcall, - error_id_use_xplus, - error_id_opcode_length_too_long, - error_id_superfx_invalid_short_address, - error_id_superfx_invalid_register, - error_id_invalid_opcode_length, - error_id_invalid_mapper, - - error_id_nan, - error_id_division_by_zero, - error_id_modulo_by_zero, - error_id_unknown_operator, - error_id_invalid_input, - - error_id_invalid_function_name, - error_id_function_not_found, - error_id_function_redefined, - error_id_broken_function_declaration, - error_id_wrong_num_parameters, - error_id_invalid_param_name, - error_id_math_invalid_type, - - error_id_invalid_label_name, - error_id_label_not_found, - error_id_label_redefined, - error_id_broken_label_definition, - error_id_label_cross_assignment, - error_id_macro_label_outside_of_macro, - error_id_label_on_third_pass, - error_id_label_moving, - - error_id_invalid_namespace_name, - error_id_invalid_namespace_use, - - error_id_invalid_struct_name, - error_id_struct_not_found, - error_id_struct_redefined, - error_id_struct_invalid_parent_name, - error_id_invalid_label_missing_closer, - error_id_multiple_subscript_operators, - error_id_invalid_subscript, - error_id_label_missing_parent, - error_id_array_invalid_inside_structs, - error_id_struct_without_endstruct, - error_id_nested_struct, - error_id_missing_struct_params, - error_id_too_many_struct_params, - error_id_missing_extends, - error_id_invalid_endstruct_count, - error_id_expected_align, - error_id_endstruct_without_struct, - error_id_alignment_too_small, - - error_id_invalid_define_name, - error_id_define_not_found, - error_id_broken_define_declaration, - error_id_overriding_builtin_define, - error_id_define_label_math, - error_id_mismatched_braces, - - error_id_invalid_macro_name, - error_id_macro_not_found, - error_id_macro_redefined, - error_id_broken_macro_declaration, - error_id_invalid_macro_param_name, - error_id_macro_param_not_found, - error_id_macro_param_redefined, - error_id_broken_macro_usage, - error_id_macro_wrong_num_params, - error_id_broken_macro_contents, - error_id_rep_at_macro_end, - error_id_nested_macro_definition, - error_id_misplaced_endmacro, - error_id_unclosed_macro, - - error_id_label_in_conditional, - error_id_broken_conditional, - error_id_invalid_condition, - error_id_misplaced_elseif, - error_id_elseif_in_while, - error_id_elseif_in_singleline, - error_id_misplaced_endif, - error_id_misplaced_else, - error_id_else_in_while_loop, - error_id_unclosed_if, - - error_id_unknown_command, - error_id_command_disabled, - - error_id_broken_incbin, - error_id_incbin_64kb_limit, - error_id_recursion_limit, - error_id_command_in_non_root_file, - error_id_cant_be_main_file, - error_id_no_labels_here, - - error_id_invalid_freespace_request, - error_id_no_banks_with_ram_mirrors, - error_id_no_freespace_norom, - error_id_static_freespace_autoclean, - error_id_static_freespace_growing, - error_id_no_freespace_in_mapped_banks, - error_id_no_freespace, - error_id_freespace_limit_reached, - - error_id_prot_not_at_freespace_start, - error_id_prot_too_many_entries, - - error_id_autoclean_in_freespace, - error_id_autoclean_label_at_freespace_end, - error_id_broken_autoclean, - - error_id_pulltable_without_table, - error_id_invalid_table_file, - - error_id_pad_in_freespace, - - error_id_org_label_invalid, - error_id_org_label_forward, - - error_id_skip_label_invalid, - - error_id_spc700_inline_no_base, - error_id_base_label_invalid, - - error_id_rep_label, - - error_id_pushpc_without_pullpc, - error_id_pullpc_without_pushpc, - error_id_pullpc_different_arch, - error_id_pullbase_without_pushbase, - - error_id_invalid_math, - error_id_invalid_warn, - error_id_invalid_check, - - error_id_warnpc_in_freespace, - error_id_warnpc_broken_param, - error_id_warnpc_failed, - error_id_warnpc_failed_equal, - - error_id_assertion_failed, - - error_id_error_command, - - error_id_invalid_print_function_syntax, - error_id_unknown_variable, - - error_id_invalid_warning_id, - - error_id_pushwarnings_without_pullwarnings, - error_id_pullwarnings_without_pushwarnings, - - error_id_failed_to_open_file_access_denied, - error_id_failed_to_open_not_regular_file, - - error_id_bad_dp_base, - error_id_bad_dp_optimize, - error_id_bad_address_optimize, - error_id_bad_optimize, - - error_id_require_parameter, - error_id_expected_parameter, - error_id_unexpected_parameter, - error_id_duplicate_param_name, - - error_id_invalid_alignment, - error_id_alignment_too_big, - - error_id_negative_shift, - - error_id_macro_not_varadic, - error_id_vararg_sizeof_nomacro, - error_id_macro_wrong_min_params, - error_id_vararg_out_of_bounds, - error_id_vararg_must_be_last, - - error_id_invalid_global_label, - - error_id_spc700_addr_out_of_range, - error_id_label_ambiguous, +enum asar_error_id : int { + error_id_start = ASAR_ERROR_RANGE_START, + + error_id_limit_reached, + error_id_werror, + + error_id_16mb_rom_limit, + error_id_buffer_too_small, + error_id_params_null, + error_id_params_invalid_size, + error_id_cmdl_define_invalid, + error_id_cmdl_define_override, + error_id_create_rom_failed, + error_id_open_rom_failed, + error_id_open_rom_not_smw_extension, + error_id_open_rom_not_smw_header, + error_id_stddefines_no_identifier, + error_id_stddefine_after_closing_quote, + + error_id_failed_to_open_file, + error_id_file_not_found, + error_id_readfile_1_to_4_bytes, + error_id_canreadfile_0_bytes, + error_id_file_offset_out_of_bounds, + error_id_rep_at_file_end, + + error_id_mismatched_parentheses, + error_id_invalid_hex_value, + error_id_invalid_binary_value, + error_id_invalid_character, + error_id_string_literal_not_terminated, + error_id_malformed_function_call, + error_id_invalid_number, + error_id_garbage_near_quoted_string, + error_id_mismatched_quotes, + + error_id_rom_too_short, + error_id_rom_title_incorrect, + + error_id_bank_border_crossed, + + error_id_start_of_file, + error_id_xkas_asar_conflict, + error_id_invalid_version_number, + error_id_asar_too_old, + + error_id_relative_branch_out_of_bounds, + error_id_snes_address_doesnt_map_to_rom, + error_id_snes_address_out_of_bounds, + error_id_invalid_tcall, + error_id_use_xplus, + error_id_opcode_length_too_long, + error_id_superfx_invalid_short_address, + error_id_superfx_invalid_register, + error_id_invalid_opcode_length, + error_id_invalid_mapper, + + error_id_nan, + error_id_division_by_zero, + error_id_modulo_by_zero, + error_id_unknown_operator, + error_id_invalid_input, + + error_id_invalid_function_name, + error_id_function_not_found, + error_id_function_redefined, + error_id_broken_function_declaration, + error_id_wrong_num_parameters, + error_id_invalid_param_name, + error_id_math_invalid_type, + + error_id_invalid_label_name, + error_id_label_not_found, + error_id_label_redefined, + error_id_broken_label_definition, + error_id_label_cross_assignment, + error_id_macro_label_outside_of_macro, + error_id_label_on_third_pass, + error_id_label_moving, + + error_id_invalid_namespace_name, + error_id_invalid_namespace_use, + + error_id_invalid_struct_name, + error_id_struct_not_found, + error_id_struct_redefined, + error_id_struct_invalid_parent_name, + error_id_invalid_label_missing_closer, + error_id_multiple_subscript_operators, + error_id_invalid_subscript, + error_id_label_missing_parent, + error_id_array_invalid_inside_structs, + error_id_struct_without_endstruct, + error_id_nested_struct, + error_id_missing_struct_params, + error_id_too_many_struct_params, + error_id_missing_extends, + error_id_invalid_endstruct_count, + error_id_expected_align, + error_id_endstruct_without_struct, + error_id_alignment_too_small, + + error_id_invalid_define_name, + error_id_define_not_found, + error_id_broken_define_declaration, + error_id_overriding_builtin_define, + error_id_define_label_math, + error_id_mismatched_braces, + + error_id_invalid_macro_name, + error_id_macro_not_found, + error_id_macro_redefined, + error_id_broken_macro_declaration, + error_id_invalid_macro_param_name, + error_id_macro_param_not_found, + error_id_macro_param_redefined, + error_id_broken_macro_usage, + error_id_macro_wrong_num_params, + error_id_broken_macro_contents, + error_id_rep_at_macro_end, + error_id_nested_macro_definition, + error_id_misplaced_endmacro, + error_id_unclosed_macro, + + error_id_label_in_conditional, + error_id_broken_conditional, + error_id_invalid_condition, + error_id_misplaced_elseif, + error_id_elseif_in_while, + error_id_elseif_in_singleline, + error_id_misplaced_endif, + error_id_misplaced_else, + error_id_else_in_while_loop, + error_id_unclosed_if, + + error_id_unknown_command, + error_id_command_disabled, + + error_id_broken_incbin, + error_id_incbin_64kb_limit, + error_id_recursion_limit, + error_id_command_in_non_root_file, + error_id_cant_be_main_file, + error_id_no_labels_here, + + error_id_invalid_freespace_request, + error_id_no_banks_with_ram_mirrors, + error_id_no_freespace_norom, + error_id_static_freespace_autoclean, + error_id_static_freespace_growing, + error_id_no_freespace_in_mapped_banks, + error_id_no_freespace, + error_id_freespace_limit_reached, + + error_id_prot_not_at_freespace_start, + error_id_prot_too_many_entries, + + error_id_autoclean_in_freespace, + error_id_autoclean_label_at_freespace_end, + error_id_broken_autoclean, + + error_id_pulltable_without_table, + error_id_invalid_table_file, + + error_id_pad_in_freespace, + + error_id_org_label_invalid, + error_id_org_label_forward, + + error_id_skip_label_invalid, + + error_id_spc700_inline_no_base, + error_id_base_label_invalid, + + error_id_rep_label, + + error_id_pushpc_without_pullpc, + error_id_pullpc_without_pushpc, + error_id_pullpc_different_arch, + error_id_pullbase_without_pushbase, + + error_id_invalid_math, + error_id_invalid_warn, + error_id_invalid_check, + + error_id_warnpc_in_freespace, + error_id_warnpc_broken_param, + error_id_warnpc_failed, + error_id_warnpc_failed_equal, + + error_id_assertion_failed, + + error_id_error_command, + + error_id_invalid_print_function_syntax, + error_id_unknown_variable, + + error_id_invalid_warning_id, + + error_id_pushwarnings_without_pullwarnings, + error_id_pullwarnings_without_pushwarnings, + + error_id_failed_to_open_file_access_denied, + error_id_failed_to_open_not_regular_file, + + error_id_bad_dp_base, + error_id_bad_dp_optimize, + error_id_bad_address_optimize, + error_id_bad_optimize, + + error_id_require_parameter, + error_id_expected_parameter, + error_id_unexpected_parameter, + error_id_duplicate_param_name, + + error_id_invalid_alignment, + error_id_alignment_too_big, + + error_id_negative_shift, + + error_id_macro_not_varadic, + error_id_vararg_sizeof_nomacro, + error_id_macro_wrong_min_params, + error_id_vararg_out_of_bounds, + error_id_vararg_must_be_last, - error_id_feature_unavaliable_in_spcblock, - error_id_endspcblock_without_spcblock, - error_id_missing_endspcblock, - error_id_spcblock_bad_arch, - error_id_spcblock_inside_struct, - error_id_spcblock_too_few_args, - error_id_spcblock_too_many_args, - error_id_unknown_spcblock_type, - error_id_custom_spcblock_missing_macro, - error_id_spcblock_macro_doesnt_exist, - error_id_extra_spcblock_arg_for_type, - error_id_spcblock_macro_must_be_varadic, - error_id_spcblock_macro_invalid_static_args, - error_id_spcblock_custom_types_incomplete, - error_id_startpos_without_spcblock, - error_id_internal_error, + error_id_invalid_global_label, - error_id_pushns_without_pullns, - error_id_pullns_without_pushns, + error_id_spc700_addr_out_of_range, + error_id_label_ambiguous, - error_id_label_forward, - error_id_undefined_char, - error_id_invalid_utf8, - error_id_cmdl_utf16_to_utf8_failed, + error_id_feature_unavaliable_in_spcblock, + error_id_endspcblock_without_spcblock, + error_id_missing_endspcblock, + error_id_spcblock_bad_arch, + error_id_spcblock_inside_struct, + error_id_spcblock_too_few_args, + error_id_spcblock_too_many_args, + error_id_unknown_spcblock_type, + error_id_custom_spcblock_missing_macro, + error_id_spcblock_macro_doesnt_exist, + error_id_extra_spcblock_arg_for_type, + error_id_spcblock_macro_must_be_varadic, + error_id_spcblock_macro_invalid_static_args, + error_id_spcblock_custom_types_incomplete, + error_id_startpos_without_spcblock, + error_id_internal_error, - error_id_broken_command, - error_id_phantom_error, + error_id_pushns_without_pullns, + error_id_pullns_without_pushns, - error_id_oob, - - error_id_unclosed_vararg, - error_id_invalid_vararg, + error_id_label_forward, + error_id_undefined_char, + error_id_invalid_utf8, + error_id_cmdl_utf16_to_utf8_failed, - error_id_macro_param_outside_macro, - error_id_invalid_depth_resolve, + error_id_broken_command, + error_id_phantom_error, - error_id_platform_paths, + error_id_oob, - error_id_end, - error_id_count = error_id_end - error_id_start - 1, + error_id_unclosed_vararg, + error_id_invalid_vararg, + + error_id_macro_param_outside_macro, + error_id_invalid_depth_resolve, + + error_id_platform_paths, + + error_id_end, + error_id_count = error_id_end - error_id_start - 1, }; -enum asar_error_type -{ - error_type_fatal, - error_type_line, - error_type_block, - error_type_null +enum asar_error_type { + error_type_fatal, + error_type_line, + error_type_block, + error_type_null }; struct errfatal {}; diff --git a/src/asar/interface-cli.cpp b/src/asar/interface-cli.cpp index 80bb6a31..9bb366b9 100644 --- a/src/asar/interface-cli.cpp +++ b/src/asar/interface-cli.cpp @@ -1,58 +1,57 @@ #include "asar.h" +#include "asar_math.h" +#include "assembleblock.h" +#include "interface-shared.h" #include "libcon.h" #include "platform/file-helpers.h" -#include "virtualfile.h" -#include "interface-shared.h" -#include "assembleblock.h" -#include "asar_math.h" -#include "unicode.h" #include "platform/thread-helpers.h" +#include "unicode.h" +#include "virtualfile.h" #if defined(windows) -# define NOMINMAX -# include -# include -# include +#define NOMINMAX +#include +#include +#include #endif #ifdef TIMELIMIT -# if defined(linux) -# include -# include -# elif defined(_WIN32) -//WARNING: The Windows equivalent of SIGXCPU, job limits, is very poorly suited for short-running -// tasks like this; it's only checked approximately every seven seconds on the machine I tested on, -// and it kills the process instantly once this happens. (Additionally, due to an implementation -// quirk, it'll bug up if you ask for anything above about seven minutes, so don't do that.) -# define NOMINMAX -# include -# else -# error Time limits not configured for this OS. -# endif +#if defined(linux) +#include +#include +#elif defined(_WIN32) +// WARNING: The Windows equivalent of SIGXCPU, job limits, is very poorly suited for +// short-running +// tasks like this; it's only checked approximately every seven seconds on the machine I +// tested on, and it kills the process instantly once this happens. (Additionally, due +// to an implementation quirk, it'll bug up if you ask for anything above about seven +// minutes, so don't do that.) +#define NOMINMAX +#include +#else +#error Time limits not configured for this OS. +#endif #endif #if defined(linux) -# include +#include #elif defined(_WIN32) -# include -# include +#include +#include #endif extern const char asarver[]; -void print(const char * str) -{ - puts(str); -} +void print(const char* str) { puts(str); } -static FILE * errloc=stderr; -static int errnum=0; +static FILE* errloc = stderr; +static int errnum = 0; namespace ansi_text_color { - enum e : int { - BRIGHT_RED, - BRIGHT_YELLOW, - }; +enum e : int { + BRIGHT_RED, + BRIGHT_YELLOW, +}; } #if defined(_WIN32) @@ -60,586 +59,581 @@ static bool has_windows_screen_info = false; static DWORD windows_screen_attributes = 0u; #endif -void set_text_color(FILE* output_loc, string* in_out_str, ansi_text_color::e color) -{ +void set_text_color(FILE* output_loc, string* in_out_str, ansi_text_color::e color) { #if defined(linux) - if (isatty(fileno(output_loc))) - { - switch (color) - { - case ansi_text_color::BRIGHT_RED: - *in_out_str = STR "\u001b[31;1m" + *in_out_str; - break; - case ansi_text_color::BRIGHT_YELLOW: - *in_out_str = STR "\u001b[33;1m" + *in_out_str; - break; - } - } + if (isatty(fileno(output_loc))) { + switch (color) { + case ansi_text_color::BRIGHT_RED: + *in_out_str = STR "\u001b[31;1m" + *in_out_str; + break; + case ansi_text_color::BRIGHT_YELLOW: + *in_out_str = STR "\u001b[33;1m" + *in_out_str; + break; + } + } #elif defined(_WIN32) - // Currently using SetConsoleTextAttribute() approach over an ASCII escape character - // approach because it makes the output text easier to parse. Unfortunately, this - // also currently makes this a Windows-only solution. - CONSOLE_SCREEN_BUFFER_INFO screenInfo; - HANDLE win_handle = (HANDLE)_get_osfhandle(fileno(output_loc)); - if (GetConsoleScreenBufferInfo(win_handle, &screenInfo) == TRUE) - { - DWORD color = 0u; - switch (color) - { - case ansi_text_color::BRIGHT_RED: - color = FOREGROUND_RED; - break; - case ansi_text_color::BRIGHT_YELLOW: - color = FOREGROUND_RED | FOREGROUND_GREEN; - break; - } - - windows_screen_attributes = screenInfo.wAttributes; - has_windows_screen_info = true; - SetConsoleTextAttribute(win_handle, (windows_screen_attributes & 0x00F0) | FOREGROUND_INTENSITY | color); - } + // Currently using SetConsoleTextAttribute() approach over an ASCII escape character + // approach because it makes the output text easier to parse. Unfortunately, this + // also currently makes this a Windows-only solution. + CONSOLE_SCREEN_BUFFER_INFO screenInfo; + HANDLE win_handle = (HANDLE)_get_osfhandle(fileno(output_loc)); + if (GetConsoleScreenBufferInfo(win_handle, &screenInfo) == TRUE) { + DWORD color = 0u; + switch (color) { + case ansi_text_color::BRIGHT_RED: + color = FOREGROUND_RED; + break; + case ansi_text_color::BRIGHT_YELLOW: + color = FOREGROUND_RED | FOREGROUND_GREEN; + break; + } + + windows_screen_attributes = screenInfo.wAttributes; + has_windows_screen_info = true; + SetConsoleTextAttribute(win_handle, (windows_screen_attributes & 0x00F0) | + FOREGROUND_INTENSITY | color); + } #endif } -void reset_text_color(FILE* output_loc, string* in_out_str) -{ +void reset_text_color(FILE* output_loc, string* in_out_str) { #if defined(linux) - if (isatty(fileno(output_loc))) - { - *in_out_str = STR "\u001b[0m" + *in_out_str; - } + if (isatty(fileno(output_loc))) { + *in_out_str = STR "\u001b[0m" + *in_out_str; + } #elif defined(_WIN32) - if (has_windows_screen_info) - { - HANDLE win_handle = (HANDLE)_get_osfhandle(fileno(output_loc)); - SetConsoleTextAttribute(win_handle, windows_screen_attributes); - } + if (has_windows_screen_info) { + HANDLE win_handle = (HANDLE)_get_osfhandle(fileno(output_loc)); + SetConsoleTextAttribute(win_handle, windows_screen_attributes); + } #endif } -void error_interface(int errid, int whichpass, const char * e_) -{ - errored = true; - if (pass == whichpass) - { - errnum++; - const char* current_block = get_current_block(); - // don't show current block if the error came from an error command or limit reached - bool show_block = (current_block && (errid != error_id_error_command && errid != error_id_limit_reached)); - bool show_stack = (errid != error_id_limit_reached); - string location; - string details; - get_current_line_details(&location, &details, !show_block); - string error_string = (show_stack ? location+": " : STR "") + "error: (" + get_error_name((asar_error_id)errid) + "): " + e_; - string details_string = (show_stack ? details + get_callstack() : "") + "\n"; - set_text_color(errloc, &error_string, ansi_text_color::BRIGHT_RED); - fputs(error_string, errloc); - reset_text_color(errloc, &details_string); - fputs(details_string, errloc); - static const int max_num_errors = 20; - if (errnum == max_num_errors + 1) asar_throw_error(pass, error_type_fatal, error_id_limit_reached, max_num_errors); - } +void error_interface(int errid, int whichpass, const char* e_) { + errored = true; + if (pass == whichpass) { + errnum++; + const char* current_block = get_current_block(); + // don't show current block if the error came from an error command or limit + // reached + bool show_block = (current_block && (errid != error_id_error_command && + errid != error_id_limit_reached)); + bool show_stack = (errid != error_id_limit_reached); + string location; + string details; + get_current_line_details(&location, &details, !show_block); + string error_string = (show_stack ? location + ": " : STR "") + "error: (" + + get_error_name((asar_error_id)errid) + "): " + e_; + string details_string = (show_stack ? details + get_callstack() : "") + "\n"; + set_text_color(errloc, &error_string, ansi_text_color::BRIGHT_RED); + fputs(error_string, errloc); + reset_text_color(errloc, &details_string); + fputs(details_string, errloc); + static const int max_num_errors = 20; + if (errnum == max_num_errors + 1) + asar_throw_error(pass, error_type_fatal, error_id_limit_reached, + max_num_errors); + } } -static bool werror=false; -static bool warned=false; - -void warn(int errid, const char * e_) -{ - const char* current_block = get_current_block(); - // don't show current block if the warning came from a warn command - bool show_block = (current_block && (errid != warning_id_warn_command)); - string location; - string details; - get_current_line_details(&location, &details, !show_block); - string warning_string = location+": warning: (" + get_warning_name((asar_warning_id)errid) + "): " + e_; - string details_string = details + get_callstack() + "\n"; - set_text_color(errloc, &warning_string, ansi_text_color::BRIGHT_YELLOW); - fputs(warning_string, errloc); - reset_text_color(errloc, &details_string); - fputs(details_string, errloc); - warned=true; +static bool werror = false; +static bool warned = false; + +void warn(int errid, const char* e_) { + const char* current_block = get_current_block(); + // don't show current block if the warning came from a warn command + bool show_block = (current_block && (errid != warning_id_warn_command)); + string location; + string details; + get_current_line_details(&location, &details, !show_block); + string warning_string = location + ": warning: (" + + get_warning_name((asar_warning_id)errid) + "): " + e_; + string details_string = details + get_callstack() + "\n"; + set_text_color(errloc, &warning_string, ansi_text_color::BRIGHT_YELLOW); + fputs(warning_string, errloc); + reset_text_color(errloc, &details_string); + fputs(details_string, errloc); + warned = true; } #ifdef TIMELIMIT #if defined(linux) -void onsigxcpu(int ignored) -{ - error(pass, "Time limit exceeded."); - exit(1); +void onsigxcpu(int ignored) { + error(pass, "Time limit exceeded."); + exit(1); } #elif defined(_WIN32) -//null +// null #endif #endif -int main(int argc, const char * argv[]) -{ +int main(int argc, const char* argv[]) { #ifdef TIMELIMIT #if defined(linux) - rlimit lim; - lim.rlim_cur=TIMELIMIT; - lim.rlim_max=RLIM_INFINITY; - setrlimit(RLIMIT_CPU, &lim); - signal(SIGXCPU, onsigxcpu); + rlimit lim; + lim.rlim_cur = TIMELIMIT; + lim.rlim_max = RLIM_INFINITY; + setrlimit(RLIMIT_CPU, &lim); + signal(SIGXCPU, onsigxcpu); #elif defined(_WIN32) - HANDLE hjob=CreateJobObject(NULL, nullptr); - AssignProcessToJobObject(hjob, GetCurrentProcess()); - JOBOBJECT_BASIC_LIMIT_INFORMATION jbli; - jbli.LimitFlags=JOB_OBJECT_LIMIT_PROCESS_TIME; - jbli.PerProcessUserTimeLimit.LowPart=10*1000*1000*TIMELIMIT; - jbli.PerProcessUserTimeLimit.HighPart=0; - SetInformationJobObject(hjob, JobObjectBasicLimitInformation, &jbli, sizeof(jbli)); + HANDLE hjob = CreateJobObject(NULL, nullptr); + AssignProcessToJobObject(hjob, GetCurrentProcess()); + JOBOBJECT_BASIC_LIMIT_INFORMATION jbli; + jbli.LimitFlags = JOB_OBJECT_LIMIT_PROCESS_TIME; + jbli.PerProcessUserTimeLimit.LowPart = 10 * 1000 * 1000 * TIMELIMIT; + jbli.PerProcessUserTimeLimit.HighPart = 0; + SetInformationJobObject(hjob, JobObjectBasicLimitInformation, &jbli, sizeof(jbli)); #endif #endif -#define pause(sev) do { if (pause>=pause_##sev) libcon_pause(); } while(0) +#define pause(sev) \ + do { \ + if (pause >= pause_##sev) libcon_pause(); \ + } while (0) - enum { - pause_no, - pause_err, - pause_warn, - pause_yes, - } pause=pause_no; + enum { + pause_no, + pause_err, + pause_warn, + pause_yes, + } pause = pause_no; - enum cmdlparam - { - cmdlparam_none, + enum cmdlparam { + cmdlparam_none, - cmdlparam_addincludepath, - cmdlparam_adddefine, + cmdlparam_addincludepath, + cmdlparam_adddefine, - cmdlparam_count - }; + cmdlparam_count + }; #if defined(windows) - // RPG Hacker: MinGW compatibility hack. -# if !defined(_O_U16TEXT) -# define _O_U16TEXT 0x20000 -# endif + // RPG Hacker: MinGW compatibility hack. +#if !defined(_O_U16TEXT) +#define _O_U16TEXT 0x20000 +#endif - _setmode(_fileno(stdin), _O_U16TEXT); - // RPG Hacker: These would currently break Asar, because we're using narrow print functions everywhere. - //_setmode(_fileno(stdout), _O_U16TEXT); - //_setmode(_fileno(stderr), _O_U16TEXT); + _setmode(_fileno(stdin), _O_U16TEXT); + // RPG Hacker: These would currently break Asar, because we're using narrow print + // functions everywhere. + //_setmode(_fileno(stdout), _O_U16TEXT); + //_setmode(_fileno(stderr), _O_U16TEXT); - SetConsoleOutputCP(CP_UTF8); - SetConsoleCP(CP_UTF8); + SetConsoleOutputCP(CP_UTF8); + SetConsoleCP(CP_UTF8); - // RPG Hacker: Full Unicode support on Windows requires using a wchar_t command line. - // This means we have to convert our arguments from UTF-16 to UTF-8 here. - LPWSTR * argv_w = CommandLineToArgvW(GetCommandLineW(), &argc); + // RPG Hacker: Full Unicode support on Windows requires using a wchar_t command + // line. This means we have to convert our arguments from UTF-16 to UTF-8 here. + LPWSTR* argv_w = CommandLineToArgvW(GetCommandLineW(), &argc); - autoarray u8_argv_arr; - autoarray raw_argv_arr; + autoarray u8_argv_arr; + autoarray raw_argv_arr; - for (int i = 0; i < argc; ++i) - { - if (!utf16_to_utf8(&u8_argv_arr[i], argv_w[i])) - { - asar_throw_error(pass, error_type_null, error_id_cmdl_utf16_to_utf8_failed, "Command line arguments on Windows must be valid UTF-16."); - pause(err); - return 1; - } + for (int i = 0; i < argc; ++i) { + if (!utf16_to_utf8(&u8_argv_arr[i], argv_w[i])) { + asar_throw_error(pass, error_type_null, error_id_cmdl_utf16_to_utf8_failed, + "Command line arguments on Windows must be valid UTF-16."); + pause(err); + return 1; + } - raw_argv_arr[i] = u8_argv_arr[i]; - } + raw_argv_arr[i] = u8_argv_arr[i]; + } - argv = (const char**)raw_argv_arr; + argv = (const char**)raw_argv_arr; #endif - try - { - romdata_r = nullptr; - string version=STR"Asar "+dec(asarver_maj)+"."+dec(asarver_min)+((asarver_bug>=10 || asarver_min>=10)?".":"")+ - dec(asarver_bug)+(asarver_beta?"pre":"")+", originally developed by Alcaro, maintained by Asar devs.\n"+ - "Source code: https://github.com/RPGHacker/asar\n"; - const char * myname=argv[0]; - if (strrchr(myname, '/')) myname=strrchr(myname, '/')+1; - //char * dot=strrchr(myname, '.'); - //if (dot) *dot='\0'; - //if (dot) *dot='.'; - libcon_init(argc, argv, - "[options] asm_file [rom_file]\n\n" - "Supported options:\n\n" - " --version \n" - " Display version information.\n\n" - " -v, --verbose \n" - " Enable verbose mode.\n\n" - " --symbols=\n" - " Specifies the format of the symbols output file. (Default is none for no symbols file)\n\n" - " --symbols-path=\n" - " Override the default path to the symbols output file. The default is the ROM's base name with an\n" - " extension of '.sym'.\n\n" - " --no-title-check\n" - " Disable verifying ROM title. (Note that irresponsible use will likely corrupt your ROM)\n\n" - " --pause-mode=\n" - " Specify when Asar should pause the application. (Never, on error, on warning or always)\n\n" - " --fix-checksum=\n" - " Override Asar's checksum generation, allowing you to manually enable/disable generating a checksum\n\n" - " -I \n" - " --include \n" - " Add an include search path to Asar.\n\n" - " -D[=] \n" - " --define [=]\n" - " Add a define (optionally with a value) to Asar.\n\n" - " -werror \n" - " Treat warnings as errors.\n\n" - " -w \n" - " Enable a specific warning.\n\n" - " -wno \n" - " Disable a specific warning.\n\n" - " --full-call-stack\n" - " Enables detailed call stack information for warnings and errors.\n\n" - ); - ignoretitleerrors=false; - string par; - bool verbose=libcon_interactive; - string symbols=""; - string symfilename=""; - - autoarray includepaths; - autoarray includepath_cstrs; - - while ((par=libcon_option())) - { - cmdlparam postprocess_param = cmdlparam_none; - const char* postprocess_arg = nullptr; - -#define checkstartmatch(arg, stringliteral) (!strncmp(arg, stringliteral, strlen(stringliteral))) - - if (par=="--no-title-check") ignoretitleerrors=true; - else if (par == "-v" || par=="--verbose") verbose=true; - else if (checkstartmatch(par, "--symbols=")) - { - if (par == "--symbols=none") symbols = ""; - else if (par=="--symbols=wla") symbols="wla"; - else if (par=="--symbols=nocash") symbols="nocash"; - else libcon_badusage(); - } - else if (checkstartmatch(par, "--symbols-path=")) { - symfilename=((const char*)par) + strlen("--symbols-path="); - } - else if (par=="--version") - { - puts(version); - return 0; - } - else if (checkstartmatch(par, "--pause-mode=")) - { - if (par=="--pause-mode=never") pause=pause_no; - else if (par=="--pause-mode=on-error") pause=pause_err; - else if (par=="--pause-mode=on-warning") pause=pause_warn; - else if (par=="--pause-mode=always") pause=pause_yes; - else libcon_badusage(); - } - else if(checkstartmatch(par, "--fix-checksum=")) { - if(par=="--fix-checksum=on") { - force_checksum_fix = true; - checksum_fix_enabled = true; - } else if(par=="--fix-checksum=off") { - force_checksum_fix = true; - checksum_fix_enabled = false; - } else libcon_badusage(); - } - else if (checkstartmatch(par, "-I")) - { - postprocess_param = cmdlparam_addincludepath; - postprocess_arg = ((const char*)par) + strlen("-I"); - } - else if (checkstartmatch(par, "-D")) - { - postprocess_param = cmdlparam_adddefine; - postprocess_arg = ((const char*)par) + strlen("-D"); - } - else if (par == "--include") - { - postprocess_arg = libcon_option_value(); - if (postprocess_arg != nullptr) - { - postprocess_param = cmdlparam_addincludepath; - } - } - else if (par == "--define") - { - postprocess_arg = libcon_option_value(); - if (postprocess_arg != nullptr) - { - postprocess_param = cmdlparam_adddefine; - } - } - else if (checkstartmatch(par, "-w")) - { - const char* w_param = ((const char*)par) + strlen("-w"); - - if (checkstartmatch(w_param, "error")) - { - werror = true; - } - else if (checkstartmatch(w_param, "no")) - { - const char* name_start = w_param + strlen("no"); - asar_warning_id warnid = parse_warning_id_from_string(name_start); - - if (warnid != warning_id_end) - { - set_warning_enabled(warnid, false); - } - else - { - asar_throw_error(pass, error_type_null, error_id_invalid_warning_id, name_start, "-wno"); - } - } - else - { - asar_warning_id warnid = parse_warning_id_from_string(w_param); - - if (warnid != warning_id_end) - { - set_warning_enabled(warnid, true); - } - else - { - asar_throw_error(pass, error_type_null, error_id_invalid_warning_id, w_param, "-w"); - } - } - - } - else if (par=="--full-call-stack") simple_callstacks=false; - else libcon_badusage(); - - if (postprocess_param == cmdlparam_addincludepath) - { - includepaths.append(postprocess_arg); - } - else if (postprocess_param == cmdlparam_adddefine) - { - if (strchr(postprocess_arg, '=') != nullptr) - { - // argument contains value, not only name - const char* eq_loc = strchr(postprocess_arg, '='); - string name = string(postprocess_arg, (int)(eq_loc - postprocess_arg)); - strip_whitespace(name); - name.strip_prefix('!'); // remove leading ! if present - - if (!validatedefinename(name)) asar_throw_error(pass, error_type_null, error_id_cmdl_define_invalid, "command line defines", name.data()); - - if (clidefines.exists(name)) { - asar_throw_error(pass, error_type_null, error_id_cmdl_define_override, "Command line define", name.data()); - pause(err); - return 1; - } - clidefines.create(name) = eq_loc + 1; - } - else - { - // argument doesn't have a value, only name - string name = postprocess_arg; - strip_whitespace(name); - name.strip_prefix('!'); // remove leading ! if present - - if (!validatedefinename(name)) asar_throw_error(pass, error_type_null, error_id_cmdl_define_invalid, "command line defines", name.data()); - - if (clidefines.exists(name)) { - asar_throw_error(pass, error_type_null, error_id_cmdl_define_override, "Command line define", name.data()); - pause(err); - return 1; - } - clidefines.create(name) = ""; - } - } - } - if (verbose) - { - puts(version); - } - string asmname=libcon_require_filename("Enter patch name:"); - string romname=libcon_optional_filename("Enter ROM name:", nullptr); - //char * outname=libcon_optional_filename("Enter output ROM name:", nullptr); - libcon_end(); - if (!strchr(asmname, '.') && !file_exists(asmname)) asmname+=".asm"; - if (!romname) - { - string romnametmp = get_base_name(asmname); - if (file_exists(romnametmp+".sfc")) romname=romnametmp+".sfc"; - else if (file_exists(romnametmp+".smc")) romname=romnametmp+".smc"; - else romname=romnametmp+".sfc"; - } - else if (!strchr(romname, '.') && !file_exists(romname)) - { - if (file_exists(romname+".sfc")) romname+=".sfc"; - else if (file_exists(romname+".smc")) romname+=".smc"; - } - if (!file_exists(romname)) - { - FileHandleType f = open_file(romname, FileOpenMode_Write); - if (f == InvalidFileHandle) - { - asar_throw_error(pass, error_type_fatal, error_id_create_rom_failed); - } - close_file(f); - } - if (!openrom(romname, false)) - { - asar_throw_error(pass, error_type_null, openromerror); - pause(err); - return 1; - } - //check if the ROM title and checksum looks sane - if (romlen>=32768 && !ignoretitleerrors) - { - bool validtitle=setmapper(); - if (!validtitle) - { - string title; - for (int i=0;i<21;i++) - { - unsigned char c=romdata[snestopc(0x00FFC0+i)]; - if (c==7) c=14; - if (c==8) c=27;//to not generate more hard-to-print characters than needed - if (c==9) c=26;//random characters are picked in accordance with the charset Windows-1252, but they should be garbage in all charsets - if (c=='\r') c=17; - if (c=='\n') c=25; - if (c=='\0') c=155; - title+=(char)c; - } - if (libcon_interactive) - { - if (!libcon_question_bool(STR"Warning: The ROM title appears to be \""+title+"\", which looks like garbage. " - "Is this your ROM title? (Note that improperly answering \"yes\" will crash your ROM.)", false)) - { - puts("Assembling aborted. snespurify should be able to fix your ROM."); - return 1; - } - } - else - { - puts(STR"Error: The ROM title appears to be \""+title+"\", which looks like garbage. " - "If this is the ROM title, add --no-title-check to the command line options. If the ROM title is something else, use snespurify on your ROM."); - pause(err); - return 1; - } - } - } - romdata_r=(unsigned char*)malloc((size_t)romlen); - romlen_r=romlen; - memcpy((void*)romdata_r, romdata, (size_t)romlen);//recently allocated, dead - - string stdincludespath = STR dir(argv[0]) + "stdincludes.txt"; - parse_std_includes(stdincludespath, includepaths); - - for (int i = 0; i < includepaths.count; ++i) - { - includepath_cstrs.append((const char*)includepaths[i]); - } - - size_t includepath_count = (size_t)includepath_cstrs.count; - virtual_filesystem new_filesystem; - new_filesystem.initialize(&includepath_cstrs[0], includepath_count); - filesystem = &new_filesystem; - - string stddefinespath = STR dir(argv[0]) + "stddefines.txt"; - parse_std_defines(stddefinespath); - - auto execute_patch = [&]() - { - try { - for (pass=0;pass<3;pass++) - { - //pass 1: find which bank all labels are in, for label optimizations - // freespaces are listed as above 0xFFFFFF, to find if it's in the ROM or if it's dynamic - //pass 2: find where exactly all labels are - //pass 3: assemble it all - initstuff(); - assemblefile(asmname); - // RPG Hacker: Necessary, because finishpass() can throws warning and errors. - callstack_push cs_push(callstack_entry_type::FILE, filesystem->create_absolute_path(nullptr, asmname)); - finishpass(); - } - return true; - } catch(errfatal&) { - return false; - } - }; + try { + romdata_r = nullptr; + string version = + STR "Asar " + dec(asarver_maj) + "." + dec(asarver_min) + + ((asarver_bug >= 10 || asarver_min >= 10) ? "." : "") + + dec(asarver_bug) + (asarver_beta ? "pre" : "") + + ", originally developed by Alcaro, maintained by Asar devs.\n" + + "Source code: https://github.com/RPGHacker/asar\n"; + const char* myname = argv[0]; + if (strrchr(myname, '/')) myname = strrchr(myname, '/') + 1; + // char * dot=strrchr(myname, '.'); + // if (dot) *dot='\0'; + // if (dot) *dot='.'; + libcon_init( + argc, argv, + "[options] asm_file [rom_file]\n\n" + "Supported options:\n\n" + " --version \n" + " Display version information.\n\n" + " -v, --verbose \n" + " Enable verbose mode.\n\n" + " --symbols=\n" + " Specifies the format of the symbols output file. " + "(Default is none for no symbols file)\n\n" + " --symbols-path=\n" + " Override the default path to the symbols output " + "file. The default is the ROM's base name with an\n" + " extension of '.sym'.\n\n" + " --no-title-check\n" + " Disable verifying ROM title. (Note that " + "irresponsible use will likely corrupt your ROM)\n\n" + " --pause-mode=\n" + " Specify when Asar should pause the application. " + "(Never, on error, on warning or always)\n\n" + " --fix-checksum=\n" + " Override Asar's checksum generation, allowing you " + "to manually enable/disable generating a checksum\n\n" + " -I \n" + " --include \n" + " Add an include search path to Asar.\n\n" + " -D[=] \n" + " --define [=]\n" + " Add a define (optionally with a value) to Asar.\n\n" + " -werror \n" + " Treat warnings as errors.\n\n" + " -w \n" + " Enable a specific warning.\n\n" + " -wno \n" + " Disable a specific warning.\n\n" + " --full-call-stack\n" + " Enables detailed call stack information for " + "warnings and errors.\n\n"); + ignoretitleerrors = false; + string par; + bool verbose = libcon_interactive; + string symbols = ""; + string symfilename = ""; + + autoarray includepaths; + autoarray includepath_cstrs; + + while ((par = libcon_option())) { + cmdlparam postprocess_param = cmdlparam_none; + const char* postprocess_arg = nullptr; + +#define checkstartmatch(arg, stringliteral) \ + (!strncmp(arg, stringliteral, strlen(stringliteral))) + + if (par == "--no-title-check") + ignoretitleerrors = true; + else if (par == "-v" || par == "--verbose") + verbose = true; + else if (checkstartmatch(par, "--symbols=")) { + if (par == "--symbols=none") + symbols = ""; + else if (par == "--symbols=wla") + symbols = "wla"; + else if (par == "--symbols=nocash") + symbols = "nocash"; + else + libcon_badusage(); + } else if (checkstartmatch(par, "--symbols-path=")) { + symfilename = ((const char*)par) + strlen("--symbols-path="); + } else if (par == "--version") { + puts(version); + return 0; + } else if (checkstartmatch(par, "--pause-mode=")) { + if (par == "--pause-mode=never") + pause = pause_no; + else if (par == "--pause-mode=on-error") + pause = pause_err; + else if (par == "--pause-mode=on-warning") + pause = pause_warn; + else if (par == "--pause-mode=always") + pause = pause_yes; + else + libcon_badusage(); + } else if (checkstartmatch(par, "--fix-checksum=")) { + if (par == "--fix-checksum=on") { + force_checksum_fix = true; + checksum_fix_enabled = true; + } else if (par == "--fix-checksum=off") { + force_checksum_fix = true; + checksum_fix_enabled = false; + } else + libcon_badusage(); + } else if (checkstartmatch(par, "-I")) { + postprocess_param = cmdlparam_addincludepath; + postprocess_arg = ((const char*)par) + strlen("-I"); + } else if (checkstartmatch(par, "-D")) { + postprocess_param = cmdlparam_adddefine; + postprocess_arg = ((const char*)par) + strlen("-D"); + } else if (par == "--include") { + postprocess_arg = libcon_option_value(); + if (postprocess_arg != nullptr) { + postprocess_param = cmdlparam_addincludepath; + } + } else if (par == "--define") { + postprocess_arg = libcon_option_value(); + if (postprocess_arg != nullptr) { + postprocess_param = cmdlparam_adddefine; + } + } else if (checkstartmatch(par, "-w")) { + const char* w_param = ((const char*)par) + strlen("-w"); + + if (checkstartmatch(w_param, "error")) { + werror = true; + } else if (checkstartmatch(w_param, "no")) { + const char* name_start = w_param + strlen("no"); + asar_warning_id warnid = parse_warning_id_from_string(name_start); + + if (warnid != warning_id_end) { + set_warning_enabled(warnid, false); + } else { + asar_throw_error(pass, error_type_null, + error_id_invalid_warning_id, name_start, + "-wno"); + } + } else { + asar_warning_id warnid = parse_warning_id_from_string(w_param); + + if (warnid != warning_id_end) { + set_warning_enabled(warnid, true); + } else { + asar_throw_error(pass, error_type_null, + error_id_invalid_warning_id, w_param, "-w"); + } + } + + } else if (par == "--full-call-stack") + simple_callstacks = false; + else + libcon_badusage(); + + if (postprocess_param == cmdlparam_addincludepath) { + includepaths.append(postprocess_arg); + } else if (postprocess_param == cmdlparam_adddefine) { + if (strchr(postprocess_arg, '=') != nullptr) { + // argument contains value, not only name + const char* eq_loc = strchr(postprocess_arg, '='); + string name = + string(postprocess_arg, (int)(eq_loc - postprocess_arg)); + strip_whitespace(name); + name.strip_prefix('!'); // remove leading ! if present + + if (!validatedefinename(name)) + asar_throw_error(pass, error_type_null, + error_id_cmdl_define_invalid, + "command line defines", name.data()); + + if (clidefines.exists(name)) { + asar_throw_error(pass, error_type_null, + error_id_cmdl_define_override, + "Command line define", name.data()); + pause(err); + return 1; + } + clidefines.create(name) = eq_loc + 1; + } else { + // argument doesn't have a value, only name + string name = postprocess_arg; + strip_whitespace(name); + name.strip_prefix('!'); // remove leading ! if present + + if (!validatedefinename(name)) + asar_throw_error(pass, error_type_null, + error_id_cmdl_define_invalid, + "command line defines", name.data()); + + if (clidefines.exists(name)) { + asar_throw_error(pass, error_type_null, + error_id_cmdl_define_override, + "Command line define", name.data()); + pause(err); + return 1; + } + clidefines.create(name) = ""; + } + } + } + if (verbose) { + puts(version); + } + string asmname = libcon_require_filename("Enter patch name:"); + string romname = libcon_optional_filename("Enter ROM name:", nullptr); + // char * outname=libcon_optional_filename("Enter output ROM name:", nullptr); + libcon_end(); + if (!strchr(asmname, '.') && !file_exists(asmname)) asmname += ".asm"; + if (!romname) { + string romnametmp = get_base_name(asmname); + if (file_exists(romnametmp + ".sfc")) + romname = romnametmp + ".sfc"; + else if (file_exists(romnametmp + ".smc")) + romname = romnametmp + ".smc"; + else + romname = romnametmp + ".sfc"; + } else if (!strchr(romname, '.') && !file_exists(romname)) { + if (file_exists(romname + ".sfc")) + romname += ".sfc"; + else if (file_exists(romname + ".smc")) + romname += ".smc"; + } + if (!file_exists(romname)) { + FileHandleType f = open_file(romname, FileOpenMode_Write); + if (f == InvalidFileHandle) { + asar_throw_error(pass, error_type_fatal, error_id_create_rom_failed); + } + close_file(f); + } + if (!openrom(romname, false)) { + asar_throw_error(pass, error_type_null, openromerror); + pause(err); + return 1; + } + // check if the ROM title and checksum looks sane + if (romlen >= 32768 && !ignoretitleerrors) { + bool validtitle = setmapper(); + if (!validtitle) { + string title; + for (int i = 0; i < 21; i++) { + unsigned char c = romdata[snestopc(0x00FFC0 + i)]; + if (c == 7) c = 14; + if (c == 8) + c = 27; // to not generate more hard-to-print characters than + // needed + if (c == 9) + c = 26; // random characters are picked in accordance with the + // charset Windows-1252, but they should be garbage in + // all charsets + if (c == '\r') c = 17; + if (c == '\n') c = 25; + if (c == '\0') c = 155; + title += (char)c; + } + if (libcon_interactive) { + if (!libcon_question_bool( + STR "Warning: The ROM title appears to be \"" + title + + "\", which looks like garbage. " + "Is this your ROM title? (Note that improperly " + "answering \"yes\" will crash your ROM.)", + false)) { + puts("Assembling aborted. snespurify should be able to fix " + "your ROM."); + return 1; + } + } else { + puts(STR "Error: The ROM title appears to be \"" + title + + "\", which looks like garbage. " + "If this is the ROM title, add --no-title-check to the " + "command line options. If the ROM title is something else, " + "use snespurify on your ROM."); + pause(err); + return 1; + } + } + } + romdata_r = (unsigned char*)malloc((size_t)romlen); + romlen_r = romlen; + memcpy((void*)romdata_r, romdata, (size_t)romlen); // recently allocated, dead + + string stdincludespath = STR dir(argv[0]) + "stdincludes.txt"; + parse_std_includes(stdincludespath, includepaths); + + for (int i = 0; i < includepaths.count; ++i) { + includepath_cstrs.append((const char*)includepaths[i]); + } + + size_t includepath_count = (size_t)includepath_cstrs.count; + virtual_filesystem new_filesystem; + new_filesystem.initialize(&includepath_cstrs[0], includepath_count); + filesystem = &new_filesystem; + + string stddefinespath = STR dir(argv[0]) + "stddefines.txt"; + parse_std_defines(stddefinespath); + + auto execute_patch = [&]() { + try { + for (pass = 0; pass < 3; pass++) { + // pass 1: find which bank all labels are in, for label + // optimizations + // freespaces are listed as above 0xFFFFFF, to find if it's in the + // ROM or if it's dynamic + // pass 2: find where exactly all labels are + // pass 3: assemble it all + initstuff(); + assemblefile(asmname); + // RPG Hacker: Necessary, because finishpass() can throws warning + // and errors. + callstack_push cs_push( + callstack_entry_type::FILE, + filesystem->create_absolute_path(nullptr, asmname)); + finishpass(); + } + return true; + } catch (errfatal&) { + return false; + } + }; #if defined(RUN_VIA_FIBER) - run_as_fiber(execute_patch); + run_as_fiber(execute_patch); #elif defined(RUN_VIA_THREAD) - run_as_thread(execute_patch); + run_as_thread(execute_patch); #else - execute_patch(); + execute_patch(); #endif - new_filesystem.destroy(); - filesystem = nullptr; - - if (werror && warned) asar_throw_error(pass, error_type_null, error_id_werror); - if (checksum_fix_enabled) fixchecksum(); - //if (pcpos>romlen) romlen=pcpos; - if (errored) - { - if (errnum==0) - asar_throw_error(pass, error_type_null, error_id_phantom_error); - puts("Errors were detected while assembling the patch. Assembling aborted. Your ROM has not been modified."); - closerom(false); - reseteverything(); - pause(err); - return 1; - } - if (warned) - { - if (libcon_interactive) - { - if (!libcon_question_bool("One or more warnings were detected while assembling the patch. " - "Do you want insert the patch anyways? (Default: yes)", true)) - { - puts("ROM left unchanged."); - closerom(false); - reseteverything(); - return 1; - } - } - else - { - if (verbose) puts("Assembling completed, but one or more warnings were detected."); - pause(warn); - } - } - else - { - if (verbose) puts("Assembling completed without problems."); - pause(yes); - } - unsigned int romCrc = closerom(); - if (symbols) - { - if (!symfilename) symfilename = get_base_name(romname)+".sym"; - string contents = create_symbols_file(symbols, romCrc).convert_line_endings_to_native(); - - FileHandleType symfile = open_file(symfilename, FileOpenMode_Write); - - if (symfile == InvalidFileHandle) - { - puts(STR"Failed to create symbols file: \""+symfilename+"\"."); - pause(err); - return 1; - } - else - { - write_file(symfile, (const char*)contents, (uint32_t)contents.length()); - close_file(symfile); - } - } - reseteverything(); - } - catch(errfatal&) - { - puts("A fatal error was detected while assembling the patch. Assembling aborted. Your ROM has not been modified."); - closerom(false); - reseteverything(); - pause(err); - return 1; - } - return 0; + new_filesystem.destroy(); + filesystem = nullptr; + + if (werror && warned) asar_throw_error(pass, error_type_null, error_id_werror); + if (checksum_fix_enabled) fixchecksum(); + // if (pcpos>romlen) romlen=pcpos; + if (errored) { + if (errnum == 0) + asar_throw_error(pass, error_type_null, error_id_phantom_error); + puts("Errors were detected while assembling the patch. Assembling aborted. " + "Your ROM has not been modified."); + closerom(false); + reseteverything(); + pause(err); + return 1; + } + if (warned) { + if (libcon_interactive) { + if (!libcon_question_bool( + "One or more warnings were detected while assembling the " + "patch. " + "Do you want insert the patch anyways? (Default: yes)", + true)) { + puts("ROM left unchanged."); + closerom(false); + reseteverything(); + return 1; + } + } else { + if (verbose) + puts("Assembling completed, but one or more warnings were " + "detected."); + pause(warn); + } + } else { + if (verbose) puts("Assembling completed without problems."); + pause(yes); + } + unsigned int romCrc = closerom(); + if (symbols) { + if (!symfilename) symfilename = get_base_name(romname) + ".sym"; + string contents = create_symbols_file(symbols, romCrc) + .convert_line_endings_to_native(); + + FileHandleType symfile = open_file(symfilename, FileOpenMode_Write); + + if (symfile == InvalidFileHandle) { + puts(STR "Failed to create symbols file: \"" + symfilename + "\"."); + pause(err); + return 1; + } else { + write_file(symfile, (const char*)contents, (uint32_t)contents.length()); + close_file(symfile); + } + } + reseteverything(); + } catch (errfatal&) { + puts("A fatal error was detected while assembling the patch. Assembling " + "aborted. Your ROM has not been modified."); + closerom(false); + reseteverything(); + pause(err); + return 1; + } + return 0; } diff --git a/src/asar/interface-lib.cpp b/src/asar/interface-lib.cpp index 39e4f593..593b2261 100644 --- a/src/asar/interface-lib.cpp +++ b/src/asar/interface-lib.cpp @@ -1,11 +1,11 @@ #include "asar.h" +#include "asar_math.h" +#include "assembleblock.h" #include "crc32.h" -#include "virtualfile.h" -#include "platform/file-helpers.h" #include "interface-shared.h" -#include "assembleblock.h" -#include "asar_math.h" +#include "platform/file-helpers.h" #include "platform/thread-helpers.h" +#include "virtualfile.h" #if defined(CPPCLI) #define EXPORT extern "C" @@ -16,47 +16,48 @@ #define EXPORT extern "C" #endif #else -#define EXPORT extern "C" __attribute__ ((visibility ("default"))) +#define EXPORT extern "C" __attribute__((visibility("default"))) #endif -static autoarray prints; +static autoarray prints; static string symbolsfile; static int numprint; static uint32_t romCrc; #define APIVERSION 400 -// note: somewhat fragile, assumes that every patchparams struct inherits from exactly the previous one +// note: somewhat fragile, assumes that every patchparams struct inherits from exactly +// the previous one /* $EXPORTSTRUCT_PP$ */ struct patchparams_base { - // The size of this struct. Set to (int)sizeof(patchparams). - int structsize; + // The size of this struct. Set to (int)sizeof(patchparams). + int structsize; }; /* $EXPORTSTRUCT$ */ struct stackentry { - const char * fullpath; - const char * prettypath; - int lineno; - const char * details; + const char* fullpath; + const char* prettypath; + int lineno; + const char* details; }; /* $EXPORTSTRUCT$ */ struct errordata { - const char * fullerrdata; - const char * rawerrdata; - const char * block; - const char * filename; - int line; - const struct stackentry * callstack; - int callstacksize; - const char * errname; + const char* fullerrdata; + const char* rawerrdata; + const char* block; + const char* filename; + int line; + const struct stackentry* callstack; + int callstacksize; + const char* errname; }; -static autoarray errors; +static autoarray errors; static int numerror; static autoarray warnings; @@ -65,306 +66,296 @@ static int numwarn; /* $EXPORTSTRUCT$ */ struct labeldata { - const char * name; - int location; + const char* name; + int location; }; /* $EXPORTSTRUCT$ */ struct definedata { - const char * name; - const char * contents; + const char* name; + const char* contents; }; /* $EXPORTSTRUCT$ */ struct warnsetting { - const char * warnid; - bool enabled; + const char* warnid; + bool enabled; }; /* $EXPORTSTRUCT$ */ struct memoryfile { - const char* path; - const void* buffer; - size_t length; + const char* path; + const void* buffer; + size_t length; }; -void print(const char * str) -{ - prints[numprint++]= duplicate_string(str); -} - -static void fillerror(errordata& myerr, const char* errname, const char * type, const char * str, bool show_block) -{ - const char* current_filename = get_current_file_name(); - if(current_filename) myerr.filename= duplicate_string(current_filename); - else myerr.filename = duplicate_string(""); - myerr.line=get_current_line(); - const char* current_block = get_current_block(); - if (current_block) myerr.block= duplicate_string(current_block); - else myerr.block= duplicate_string(""); - myerr.rawerrdata= duplicate_string(str); - string location; - string details; - get_current_line_details(&location, &details); - myerr.fullerrdata= duplicate_string(location+": "+type+str+details+get_callstack()); - myerr.errname = duplicate_string(errname); - - autoarray printable_stack; - get_full_printable_callstack(&printable_stack, 0, false); - - myerr.callstacksize = printable_stack.count; - myerr.callstack = static_cast(malloc(sizeof(stackentry) * myerr.callstacksize)); - - for (int i = 0; i < myerr.callstacksize; ++i) - { - stackentry& entry = const_cast(myerr.callstack[i]); - - entry.fullpath = duplicate_string(printable_stack[i].fullpath); - entry.prettypath = duplicate_string(printable_stack[i].prettypath); - entry.lineno = printable_stack[i].lineno; - entry.details = duplicate_string(printable_stack[i].details); - } +void print(const char* str) { prints[numprint++] = duplicate_string(str); } + +static void fillerror(errordata& myerr, const char* errname, const char* type, + const char* str, bool show_block) { + const char* current_filename = get_current_file_name(); + if (current_filename) + myerr.filename = duplicate_string(current_filename); + else + myerr.filename = duplicate_string(""); + myerr.line = get_current_line(); + const char* current_block = get_current_block(); + if (current_block) + myerr.block = duplicate_string(current_block); + else + myerr.block = duplicate_string(""); + myerr.rawerrdata = duplicate_string(str); + string location; + string details; + get_current_line_details(&location, &details); + myerr.fullerrdata = + duplicate_string(location + ": " + type + str + details + get_callstack()); + myerr.errname = duplicate_string(errname); + + autoarray printable_stack; + get_full_printable_callstack(&printable_stack, 0, false); + + myerr.callstacksize = printable_stack.count; + myerr.callstack = + static_cast(malloc(sizeof(stackentry) * myerr.callstacksize)); + + for (int i = 0; i < myerr.callstacksize; ++i) { + stackentry& entry = const_cast(myerr.callstack[i]); + + entry.fullpath = duplicate_string(printable_stack[i].fullpath); + entry.prettypath = duplicate_string(printable_stack[i].prettypath); + entry.lineno = printable_stack[i].lineno; + entry.details = duplicate_string(printable_stack[i].details); + } } -static bool ismath=false; +static bool ismath = false; static string matherror; -void error_interface(int errid, int whichpass, const char * e_) -{ - errored = true; - if (ismath) matherror = e_; - else if (pass == whichpass) { - // don't show current block if the error came from an error command - bool show_block = (errid != error_id_error_command); - fillerror(errors[numerror++], get_error_name((asar_error_id)errid), STR "error: (" + get_error_name((asar_error_id)errid) + "): ", e_, show_block); - } - else {}//ignore anything else +void error_interface(int errid, int whichpass, const char* e_) { + errored = true; + if (ismath) + matherror = e_; + else if (pass == whichpass) { + // don't show current block if the error came from an error command + bool show_block = (errid != error_id_error_command); + fillerror(errors[numerror++], get_error_name((asar_error_id)errid), + STR "error: (" + get_error_name((asar_error_id)errid) + "): ", e_, + show_block); + } else { + } // ignore anything else } -void warn(int errid, const char * str) -{ - // don't show current block if the warning came from a warn command - bool show_block = (errid != warning_id_warn_command); - fillerror(warnings[numwarn++], get_warning_name((asar_warning_id)errid), STR "warning: (" + get_warning_name((asar_warning_id)errid) + "): ", str, show_block); +void warn(int errid, const char* str) { + // don't show current block if the warning came from a warn command + bool show_block = (errid != warning_id_warn_command); + fillerror(warnings[numwarn++], get_warning_name((asar_warning_id)errid), + STR "warning: (" + get_warning_name((asar_warning_id)errid) + "): ", str, + show_block); } static autoarray ddata; -static int definesinddata=0; - - -static void resetdllstuff() -{ -#define free_and_null(x) free((void*)x); x = nullptr - for (int i=0;i(errors[i].callstack[j]); - free_and_null(entry.fullpath); - free_and_null(entry.prettypath); - free_and_null(entry.details); - } - free_and_null(errors[i].callstack); - } - errors.reset(); - numerror=0; - - for (int i=0;i(warnings[i].callstack[j]); - free_and_null(entry.fullpath); - free_and_null(entry.prettypath); - free_and_null(entry.details); - } - free_and_null(warnings[i].callstack); - } - warnings.reset(); - numwarn=0; - - for (int i=0;i(errors[i].callstack[j]); + free_and_null(entry.fullpath); + free_and_null(entry.prettypath); + free_and_null(entry.details); + } + free_and_null(errors[i].callstack); + } + errors.reset(); + numerror = 0; + + for (int i = 0; i < numwarn; i++) { + free_and_null(warnings[i].filename); + free_and_null(warnings[i].rawerrdata); + free_and_null(warnings[i].fullerrdata); + free_and_null(warnings[i].block); + free_and_null(warnings[i].errname); + + for (int j = 0; j < warnings[i].callstacksize; ++j) { + stackentry& entry = const_cast(warnings[i].callstack[j]); + free_and_null(entry.fullpath); + free_and_null(entry.prettypath); + free_and_null(entry.details); + } + free_and_null(warnings[i].callstack); + } + warnings.reset(); + numwarn = 0; + + for (int i = 0; i < definesinddata; i++) { + free_and_null(ddata[i].name); + free_and_null(ddata[i].contents); + } + ddata.reset(); + definesinddata = 0; #undef free_and_null - romCrc = 0; - clidefines.reset(); - reset_warnings_to_default(); + romCrc = 0; + clidefines.reset(); + reset_warnings_to_default(); - reseteverything(); + reseteverything(); } -#define maxromsize (16*1024*1024) +#define maxromsize (16 * 1024 * 1024) static autoarray ldata; static int labelsinldata = 0; static bool expectsNewAPI = false; -static void addlabel(const string & name, const snes_label & label_data) -{ - labeldata label; - label.name = strdup(name); - label.location = (int)(label_data.pos & 0xFFFFFF); - ldata[labelsinldata++] = label; +static void addlabel(const string& name, const snes_label& label_data) { + labeldata label; + label.name = strdup(name); + label.location = (int)(label_data.pos & 0xFFFFFF); + ldata[labelsinldata++] = label; } /* $EXPORTSTRUCT_PP$ */ -struct patchparams_v200 : public patchparams_base -{ - // Same parameters as asar_patch() - const char * patchloc; - char * romdata; - int buflen; - int * romlen; - - // Include paths to use when searching files. - const char** includepaths; - int numincludepaths; - - // A list of additional defines to make available to the patch. - const struct definedata* additional_defines; - int additional_define_count; - - // Path to a text file to parse standard include search paths from. - // Set to NULL to not use any standard includes search paths. - const char* stdincludesfile; - - // Path to a text file to parse standard defines from. - // Set to NULL to not use any standard defines. - const char* stddefinesfile; - - // A list of warnings to enable or disable. - // Specify warnings in the format "WXXXX" where XXXX = warning ID. - const struct warnsetting * warning_settings; - int warning_setting_count; - - // List of memory files to create on the virtual filesystem. - const struct memoryfile * memory_files; - int memory_file_count; - - // Set override_checksum_gen to true and generate_checksum to true/false - // to force generating/not generating a checksum. - bool override_checksum_gen; - bool generate_checksum; - - // Set this to true for generated error and warning texts to always - // contain their full call stack. - bool full_call_stack; +struct patchparams_v200 : public patchparams_base { + // Same parameters as asar_patch() + const char* patchloc; + char* romdata; + int buflen; + int* romlen; + + // Include paths to use when searching files. + const char** includepaths; + int numincludepaths; + + // A list of additional defines to make available to the patch. + const struct definedata* additional_defines; + int additional_define_count; + + // Path to a text file to parse standard include search paths from. + // Set to NULL to not use any standard includes search paths. + const char* stdincludesfile; + + // Path to a text file to parse standard defines from. + // Set to NULL to not use any standard defines. + const char* stddefinesfile; + + // A list of warnings to enable or disable. + // Specify warnings in the format "WXXXX" where XXXX = warning ID. + const struct warnsetting* warning_settings; + int warning_setting_count; + + // List of memory files to create on the virtual filesystem. + const struct memoryfile* memory_files; + int memory_file_count; + + // Set override_checksum_gen to true and generate_checksum to true/false + // to force generating/not generating a checksum. + bool override_checksum_gen; + bool generate_checksum; + + // Set this to true for generated error and warning texts to always + // contain their full call stack. + bool full_call_stack; }; /* $EXPORTSTRUCT_PP$ */ -struct patchparams : public patchparams_v200 -{ - -}; - -static void asar_patch_begin(char * romdata_, int buflen, int * romlen_) -{ - if (buflen != maxromsize) - { - romdata_r = (unsigned char*)malloc(maxromsize); - memcpy(const_cast(romdata_r)/*we just allocated this, it's safe to violate its const*/, romdata_, (size_t)*romlen_); - } - else romdata_r = (unsigned char*)romdata_; - romdata = (unsigned char*)malloc(maxromsize); - // RPG Hacker: Without this memset, freespace commands can (and probably will) fail. - memset((void*)romdata, 0, maxromsize); - memcpy(const_cast(romdata), romdata_, (size_t)*romlen_); - resetdllstuff(); - romlen = *romlen_; - romlen_r = *romlen_; +struct patchparams : public patchparams_v200 {}; + +static void asar_patch_begin(char* romdata_, int buflen, int* romlen_) { + if (buflen != maxromsize) { + romdata_r = (unsigned char*)malloc(maxromsize); + memcpy(const_cast(romdata_r) /*we just allocated this, it's safe + to violate its const*/ + , + romdata_, (size_t)*romlen_); + } else + romdata_r = (unsigned char*)romdata_; + romdata = (unsigned char*)malloc(maxromsize); + // RPG Hacker: Without this memset, freespace commands can (and probably will) fail. + memset((void*)romdata, 0, maxromsize); + memcpy(const_cast(romdata), romdata_, (size_t)*romlen_); + resetdllstuff(); + romlen = *romlen_; + romlen_r = *romlen_; } -static void asar_patch_main(const char * patchloc) -{ - if (!path_is_absolute(patchloc)) asar_throw_warning(pass, warning_id_relative_path_used, "patch file"); - - try - { - for (pass = 0;pass < 3;pass++) - { - initstuff(); - assemblefile(patchloc); - // RPG Hacker: Necessary, because finishpass() can throws warning and errors. - callstack_push cs_push(callstack_entry_type::FILE, filesystem->create_absolute_path(nullptr, patchloc)); - finishpass(); - } - } - catch (errfatal&) {} +static void asar_patch_main(const char* patchloc) { + if (!path_is_absolute(patchloc)) + asar_throw_warning(pass, warning_id_relative_path_used, "patch file"); + + try { + for (pass = 0; pass < 3; pass++) { + initstuff(); + assemblefile(patchloc); + // RPG Hacker: Necessary, because finishpass() can throws warning and + // errors. + callstack_push cs_push(callstack_entry_type::FILE, + filesystem->create_absolute_path(nullptr, patchloc)); + finishpass(); + } + } catch (errfatal&) { + } } -static bool asar_patch_end(char * romdata_, int buflen, int * romlen_) -{ - if (checksum_fix_enabled) fixchecksum(); - if (romdata_ != (const char*)romdata_r) free(const_cast(romdata_r)); - if (buflen < romlen) asar_throw_error(pass, error_type_null, error_id_buffer_too_small); - if (errored) - { - if (numerror==0) - asar_throw_error(pass, error_type_null, error_id_phantom_error); - free(const_cast(romdata)); - return false; - } - if (*romlen_ != buflen) - { - *romlen_ = romlen; - } - romCrc = crc32((const uint8_t*)romdata, (size_t)romlen); - memcpy(romdata_, romdata, (size_t)romlen); - free(const_cast(romdata)); - return true; +static bool asar_patch_end(char* romdata_, int buflen, int* romlen_) { + if (checksum_fix_enabled) fixchecksum(); + if (romdata_ != (const char*)romdata_r) free(const_cast(romdata_r)); + if (buflen < romlen) + asar_throw_error(pass, error_type_null, error_id_buffer_too_small); + if (errored) { + if (numerror == 0) + asar_throw_error(pass, error_type_null, error_id_phantom_error); + free(const_cast(romdata)); + return false; + } + if (*romlen_ != buflen) { + *romlen_ = romlen; + } + romCrc = crc32((const uint8_t*)romdata, (size_t)romlen); + memcpy(romdata_, romdata, (size_t)romlen); + free(const_cast(romdata)); + return true; } #if defined(__clang__) -# pragma clang diagnostic push -# pragma clang diagnostic ignored "-Wmissing-prototypes" +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wmissing-prototypes" #endif // this and asar_close are hardcoded in each api -EXPORT bool asar_init() -{ - if (!expectsNewAPI) return false; - return true; +EXPORT bool asar_init() { + if (!expectsNewAPI) return false; + return true; } /* $EXPORT$ * Returns the version, in the format major*10000+minor*100+bugfix*1. This * means that 1.2.34 would be returned as 10234. */ -EXPORT int asar_version() -{ - return get_version_int(); -} +EXPORT int asar_version() { return get_version_int(); } /* $EXPORT$ * Returns the API version, format major*100+minor. Minor is incremented on @@ -375,27 +366,22 @@ EXPORT int asar_version() * Calling this one also sets a flag that makes asar_init not instantly return * false; this is so programs expecting an older API won't do anything unexpected. */ -EXPORT int asar_apiversion() -{ - expectsNewAPI=true; - return APIVERSION; +EXPORT int asar_apiversion() { + expectsNewAPI = true; + return APIVERSION; } /* $EXPORT$ * Clears out all errors, warnings and printed statements, and clears the file * cache. Not really useful, since asar_patch() already does this. */ -EXPORT bool asar_reset() -{ - resetdllstuff(); - pass=0; - return true; +EXPORT bool asar_reset() { + resetdllstuff(); + pass = 0; + return true; } -EXPORT void asar_close() -{ - resetdllstuff(); -} +EXPORT void asar_close() { resetdllstuff(); } /* $EXPORT$ * Applies a patch. The first argument is a filename (so Asar knows where to @@ -408,118 +394,126 @@ EXPORT void asar_close() * be left unchanged. * See the documentation of struct patchparams for more information. */ -EXPORT bool asar_patch(const struct patchparams_base *params) -{ - auto execute_patch = [&]() { - if (params == nullptr) - { - asar_throw_error(pass, error_type_null, error_id_params_null); - } - - if (params->structsize != sizeof(patchparams_v200)) - { - asar_throw_error(pass, error_type_null, error_id_params_invalid_size); - } - - patchparams paramscurrent; - memset(¶mscurrent, 0, sizeof(paramscurrent)); - memcpy(¶mscurrent, params, (size_t)params->structsize); - - - asar_patch_begin(paramscurrent.romdata, paramscurrent.buflen, paramscurrent.romlen); - - simple_callstacks = !paramscurrent.full_call_stack; - - autoarray includepaths; - autoarray includepath_cstrs; - - for (int i = 0; i < paramscurrent.numincludepaths; ++i) - { - if (!path_is_absolute(paramscurrent.includepaths[i])) asar_throw_warning(pass, warning_id_relative_path_used, "include search"); - string& newpath = includepaths.append(paramscurrent.includepaths[i]); - includepath_cstrs.append((const char*)newpath); - } - - if (paramscurrent.stdincludesfile != nullptr) { - if (!path_is_absolute(paramscurrent.stdincludesfile)) asar_throw_warning(pass, warning_id_relative_path_used, "std includes file"); - string stdincludespath = paramscurrent.stdincludesfile; - parse_std_includes(stdincludespath, includepaths); - } - - for (int i = 0; i < includepaths.count; ++i) - { - includepath_cstrs.append((const char*)includepaths[i]); - } - - size_t includepath_count = (size_t)includepath_cstrs.count; - virtual_filesystem new_filesystem; - new_filesystem.initialize(&includepath_cstrs[0], includepath_count); - filesystem = &new_filesystem; - - for(int i = 0; i < paramscurrent.memory_file_count; ++i) { - memoryfile f = paramscurrent.memory_files[i]; - filesystem->add_memory_file(f.path, f.buffer, f.length); - } - - clidefines.reset(); - for (int i = 0; i < paramscurrent.additional_define_count; ++i) - { - string name = (paramscurrent.additional_defines[i].name != nullptr ? paramscurrent.additional_defines[i].name : ""); - strip_whitespace(name); - name.strip_prefix('!'); // remove leading ! if present - if (!validatedefinename(name)) asar_throw_error(pass, error_type_null, error_id_cmdl_define_invalid, "asar_patch_ex() additional defines", name.data()); - if (clidefines.exists(name)) { - asar_throw_error(pass, error_type_null, error_id_cmdl_define_override, "asar_patch_ex() additional define", name.data()); - return false; - } - string contents = (paramscurrent.additional_defines[i].contents != nullptr ? paramscurrent.additional_defines[i].contents : ""); - clidefines.create(name) = contents; - } - - if (paramscurrent.stddefinesfile != nullptr) { - if (!path_is_absolute(paramscurrent.stddefinesfile)) asar_throw_warning(pass, warning_id_relative_path_used, "std defines file"); - string stddefinespath = paramscurrent.stddefinesfile; - parse_std_defines(stddefinespath); - } else { - parse_std_defines(nullptr); // needed to populate builtin defines - } - - for (int i = 0; i < paramscurrent.warning_setting_count; ++i) - { - asar_warning_id warnid = parse_warning_id_from_string(paramscurrent.warning_settings[i].warnid); - - if (warnid != warning_id_end) - { - set_warning_enabled(warnid, paramscurrent.warning_settings[i].enabled); - } - else - { - asar_throw_error(pass, error_type_null, error_id_invalid_warning_id, paramscurrent.warning_settings[i].warnid, "asar_patch_ex() warning_settings"); - } - } - - if(paramscurrent.override_checksum_gen) { - checksum_fix_enabled = paramscurrent.generate_checksum; - force_checksum_fix = true; - } - - asar_patch_main(paramscurrent.patchloc); - - // RPG Hacker: Required before the destroy() below, - // otherwise it will leak memory. - closecachedfiles(); - - new_filesystem.destroy(); - filesystem = nullptr; - - return asar_patch_end(paramscurrent.romdata, paramscurrent.buflen, paramscurrent.romlen); -}; +EXPORT bool asar_patch(const struct patchparams_base* params) { + auto execute_patch = [&]() { + if (params == nullptr) { + asar_throw_error(pass, error_type_null, error_id_params_null); + } + + if (params->structsize != sizeof(patchparams_v200)) { + asar_throw_error(pass, error_type_null, error_id_params_invalid_size); + } + + patchparams paramscurrent; + memset(¶mscurrent, 0, sizeof(paramscurrent)); + memcpy(¶mscurrent, params, (size_t)params->structsize); + + + asar_patch_begin(paramscurrent.romdata, paramscurrent.buflen, + paramscurrent.romlen); + + simple_callstacks = !paramscurrent.full_call_stack; + + autoarray includepaths; + autoarray includepath_cstrs; + + for (int i = 0; i < paramscurrent.numincludepaths; ++i) { + if (!path_is_absolute(paramscurrent.includepaths[i])) + asar_throw_warning(pass, warning_id_relative_path_used, + "include search"); + string& newpath = includepaths.append(paramscurrent.includepaths[i]); + includepath_cstrs.append((const char*)newpath); + } + + if (paramscurrent.stdincludesfile != nullptr) { + if (!path_is_absolute(paramscurrent.stdincludesfile)) + asar_throw_warning(pass, warning_id_relative_path_used, + "std includes file"); + string stdincludespath = paramscurrent.stdincludesfile; + parse_std_includes(stdincludespath, includepaths); + } + + for (int i = 0; i < includepaths.count; ++i) { + includepath_cstrs.append((const char*)includepaths[i]); + } + + size_t includepath_count = (size_t)includepath_cstrs.count; + virtual_filesystem new_filesystem; + new_filesystem.initialize(&includepath_cstrs[0], includepath_count); + filesystem = &new_filesystem; + + for (int i = 0; i < paramscurrent.memory_file_count; ++i) { + memoryfile f = paramscurrent.memory_files[i]; + filesystem->add_memory_file(f.path, f.buffer, f.length); + } + + clidefines.reset(); + for (int i = 0; i < paramscurrent.additional_define_count; ++i) { + string name = (paramscurrent.additional_defines[i].name != nullptr + ? paramscurrent.additional_defines[i].name + : ""); + strip_whitespace(name); + name.strip_prefix('!'); // remove leading ! if present + if (!validatedefinename(name)) + asar_throw_error(pass, error_type_null, error_id_cmdl_define_invalid, + "asar_patch_ex() additional defines", name.data()); + if (clidefines.exists(name)) { + asar_throw_error(pass, error_type_null, error_id_cmdl_define_override, + "asar_patch_ex() additional define", name.data()); + return false; + } + string contents = (paramscurrent.additional_defines[i].contents != nullptr + ? paramscurrent.additional_defines[i].contents + : ""); + clidefines.create(name) = contents; + } + + if (paramscurrent.stddefinesfile != nullptr) { + if (!path_is_absolute(paramscurrent.stddefinesfile)) + asar_throw_warning(pass, warning_id_relative_path_used, + "std defines file"); + string stddefinespath = paramscurrent.stddefinesfile; + parse_std_defines(stddefinespath); + } else { + parse_std_defines(nullptr); // needed to populate builtin defines + } + + for (int i = 0; i < paramscurrent.warning_setting_count; ++i) { + asar_warning_id warnid = parse_warning_id_from_string( + paramscurrent.warning_settings[i].warnid); + + if (warnid != warning_id_end) { + set_warning_enabled(warnid, paramscurrent.warning_settings[i].enabled); + } else { + asar_throw_error(pass, error_type_null, error_id_invalid_warning_id, + paramscurrent.warning_settings[i].warnid, + "asar_patch_ex() warning_settings"); + } + } + + if (paramscurrent.override_checksum_gen) { + checksum_fix_enabled = paramscurrent.generate_checksum; + force_checksum_fix = true; + } + + asar_patch_main(paramscurrent.patchloc); + + // RPG Hacker: Required before the destroy() below, + // otherwise it will leak memory. + closecachedfiles(); + + new_filesystem.destroy(); + filesystem = nullptr; + + return asar_patch_end(paramscurrent.romdata, paramscurrent.buflen, + paramscurrent.romlen); + }; #if defined(RUN_VIA_FIBER) - return run_as_fiber(execute_patch); + return run_as_fiber(execute_patch); #elif defined(RUN_VIA_THREAD) - return run_as_thread(execute_patch); + return run_as_thread(execute_patch); #else - return execute_patch(); + return execute_patch(); #endif } @@ -529,10 +523,7 @@ EXPORT bool asar_patch(const struct patchparams_base *params) * errors; however, it is safe to give smaller buffers if you don't expect any * ROMs larger than 4MB or something. */ -EXPORT int asar_maxromsize() -{ - return maxromsize; -} +EXPORT int asar_maxromsize() { return maxromsize; } /* $EXPORT$ * Get a list of all errors. @@ -540,98 +531,89 @@ EXPORT int asar_maxromsize() * called again, or until asar_patch, asar_reset or asar_close is called, * whichever comes first. Copy the contents if you need it for a longer time. */ -EXPORT const struct errordata * asar_geterrors(int * count) -{ - *count=numerror; - return errors; +EXPORT const struct errordata* asar_geterrors(int* count) { + *count = numerror; + return errors; } /* $EXPORT$ * Get a list of all warnings. */ -EXPORT const struct errordata * asar_getwarnings(int * count) -{ - *count=numwarn; - return warnings; +EXPORT const struct errordata* asar_getwarnings(int* count) { + *count = numwarn; + return warnings; } /* $EXPORT$ * Get a list of all printed data. */ -EXPORT const char * const * asar_getprints(int * count) -{ - *count=numprint; - return prints; +EXPORT const char* const* asar_getprints(int* count) { + *count = numprint; + return prints; } /* $EXPORT$ * Get a list of all labels. */ -EXPORT const struct labeldata * asar_getalllabels(int * count) -{ - for (int i=0;i -#include // for size_t +#include // for size_t -//These structures are returned from various functions. +// These structures are returned from various functions. struct stackentry { - const char * fullpath; - const char * prettypath; - int lineno; - const char * details; + const char* fullpath; + const char* prettypath; + int lineno; + const char* details; }; struct errordata { - const char * fullerrdata; - const char * rawerrdata; - const char * block; - const char * filename; - int line; - const struct stackentry * callstack; - int callstacksize; - const char * errname; + const char* fullerrdata; + const char* rawerrdata; + const char* block; + const char* filename; + int line; + const struct stackentry* callstack; + int callstacksize; + const char* errname; }; struct labeldata { - const char * name; - int location; + const char* name; + int location; }; struct definedata { - const char * name; - const char * contents; + const char* name; + const char* contents; }; struct warnsetting { - const char * warnid; - bool enabled; + const char* warnid; + bool enabled; }; struct memoryfile { - const char* path; - const void* buffer; - size_t length; + const char* path; + const void* buffer; + size_t length; }; enum mappertype { - invalid_mapper, - lorom, - hirom, - sa1rom, - bigsa1rom, - sfxrom, - exlorom, - exhirom, - norom + invalid_mapper, + lorom, + hirom, + sa1rom, + bigsa1rom, + sfxrom, + exlorom, + exhirom, + norom }; struct writtenblockdata { - int pcoffset; - int snesoffset; - int numbytes; + int pcoffset; + int snesoffset; + int numbytes; }; struct patchparams { - // The size of this struct. Set to (int)sizeof(patchparams). - int structsize; - - // Same parameters as asar_patch() - const char * patchloc; - char * romdata; - int buflen; - int * romlen; - - // Include paths to use when searching files. - const char** includepaths; - int numincludepaths; - - // A list of additional defines to make available to the patch. - const struct definedata* additional_defines; - int additional_define_count; - - // Path to a text file to parse standard include search paths from. - // Set to NULL to not use any standard includes search paths. - const char* stdincludesfile; - - // Path to a text file to parse standard defines from. - // Set to NULL to not use any standard defines. - const char* stddefinesfile; - - // A list of warnings to enable or disable. - // Specify warnings in the format "WXXXX" where XXXX = warning ID. - const struct warnsetting * warning_settings; - int warning_setting_count; - - // List of memory files to create on the virtual filesystem. - const struct memoryfile * memory_files; - int memory_file_count; - - // Set override_checksum_gen to true and generate_checksum to true/false - // to force generating/not generating a checksum. - bool override_checksum_gen; - bool generate_checksum; - - // Set this to true for generated error and warning texts to always - // contain their full call stack. - bool full_call_stack; + // The size of this struct. Set to (int)sizeof(patchparams). + int structsize; + + // Same parameters as asar_patch() + const char* patchloc; + char* romdata; + int buflen; + int* romlen; + + // Include paths to use when searching files. + const char** includepaths; + int numincludepaths; + + // A list of additional defines to make available to the patch. + const struct definedata* additional_defines; + int additional_define_count; + + // Path to a text file to parse standard include search paths from. + // Set to NULL to not use any standard includes search paths. + const char* stdincludesfile; + + // Path to a text file to parse standard defines from. + // Set to NULL to not use any standard defines. + const char* stddefinesfile; + + // A list of warnings to enable or disable. + // Specify warnings in the format "WXXXX" where XXXX = warning ID. + const struct warnsetting* warning_settings; + int warning_setting_count; + + // List of memory files to create on the virtual filesystem. + const struct memoryfile* memory_files; + int memory_file_count; + + // Set override_checksum_gen to true and generate_checksum to true/false + // to force generating/not generating a checksum. + bool override_checksum_gen; + bool generate_checksum; + + // Set this to true for generated error and warning texts to always + // contain their full call stack. + bool full_call_stack; }; #ifdef __cplusplus @@ -144,7 +144,7 @@ bool asar_reset(void); * be left unchanged. * See the documentation of struct patchparams for more information. */ -bool asar_patch(const struct patchparams *params); +bool asar_patch(const struct patchparams* params); /* Returns the maximum possible size of the output ROM from asar_patch(). * Giving this size to buflen guarantees you will not get any buffer too small @@ -158,35 +158,35 @@ int asar_maxromsize(void); * called again, or until asar_patch, asar_reset or asar_close is called, * whichever comes first. Copy the contents if you need it for a longer time. */ -const struct errordata * asar_geterrors(int * count); +const struct errordata* asar_geterrors(int* count); /* Get a list of all warnings. */ -const struct errordata * asar_getwarnings(int * count); +const struct errordata* asar_getwarnings(int* count); /* Get a list of all printed data. */ -const char * const * asar_getprints(int * count); +const char* const* asar_getprints(int* count); /* Get a list of all labels. */ -const struct labeldata * asar_getalllabels(int * count); +const struct labeldata* asar_getalllabels(int* count); /* Get the ROM location of one label. -1 means "not found". */ -int asar_getlabelval(const char * name); +int asar_getlabelval(const char* name); /* Get the value of a define. */ -const char * asar_getdefine(const char * name); +const char* asar_getdefine(const char* name); /* Parses all defines in the parameter. Note that it may emit errors. */ -const char * asar_resolvedefines(const char * data); +const char* asar_resolvedefines(const char* data); /* Gets the values and names of all defines. */ -const struct definedata * asar_getalldefines(int * count); +const struct definedata* asar_getalldefines(int* count); /* Parses a string containing math. It automatically assumes global scope (no * namespaces), and has access to all functions and labels from the last call @@ -194,12 +194,12 @@ const struct definedata * asar_getalldefines(int * count); * if it failed (non-NULL, contains a descriptive string). It does not affect * asar_geterrors. */ -double asar_math(const char * math_, const char ** error); +double asar_math(const char* math_, const char** error); /* Get a list of all the blocks written to the ROM by calls such as * asar_patch(). */ -const struct writtenblockdata * asar_getwrittenblocks(int * count); +const struct writtenblockdata* asar_getwrittenblocks(int* count); /* Get the mapper currently used by Asar. */ @@ -207,7 +207,7 @@ enum mappertype asar_getmapper(void); /* Generates the contents of a symbols file for in a specific format. */ -const char * asar_getsymbolsfile(const char* type); +const char* asar_getsymbolsfile(const char* type); #ifdef __cplusplus } diff --git a/src/asar/interface-shared.h b/src/asar/interface-shared.h index b5fb27e9..185b4559 100644 --- a/src/asar/interface-shared.h +++ b/src/asar/interface-shared.h @@ -1,10 +1,10 @@ #pragma once // This function should only be called inside assembleblock.cpp. -void print(const char * str); +void print(const char* str); // This function should only be called inside errors.cpp. -void error_interface(int errid, int whichpass, const char * e_); +void error_interface(int errid, int whichpass, const char* e_); // This function should only be called inside warnings.cpp. -void warn(int errid, const char * e); +void warn(int errid, const char* e); diff --git a/src/asar/libcon.cpp b/src/asar/libcon.cpp index 6437d6dc..bedcb870 100644 --- a/src/asar/libcon.cpp +++ b/src/asar/libcon.cpp @@ -1,174 +1,161 @@ #include "libcon.h" + +#include + #include "libstr.h" #include "unicode.h" -#include -static const char * progname; -static const char ** args; +static const char* progname; +static const char** args; static int argsleft; bool libcon_interactive; -static const char * usage; - -static volatile bool confirmclose=true; -void libcon_pause() -{ - if (confirmclose) - { - confirmclose=false; +static const char* usage; + +static volatile bool confirmclose = true; +void libcon_pause() { + if (confirmclose) { + confirmclose = false; #if defined(_WIN32) - system("pause"); + system("pause"); #else - printf("Press Enter to continue"); - getchar(); + printf("Press Enter to continue"); + getchar(); #endif - confirmclose=true; - } + confirmclose = true; + } } -void libcon_badusage() -{ - printf("usage: %s %s", progname, usage); - exit(1); +void libcon_badusage() { + printf("usage: %s %s", progname, usage); + exit(1); } -static const char * getarg(bool tellusage, const char * defval= nullptr) -{ - if (!argsleft) - { - if (tellusage) libcon_badusage(); - return defval; - } - args++; - argsleft--; - return args[0]; +static const char* getarg(bool tellusage, const char* defval = nullptr) { + if (!argsleft) { + if (tellusage) libcon_badusage(); + return defval; + } + args++; + argsleft--; + return args[0]; } -void u8_fgets(char* buffer, int buffer_size, FILE* handle) -{ +void u8_fgets(char* buffer, int buffer_size, FILE* handle) { #if defined(windows) - // RPG Hacker: Using buffer_size * 2 here to account for potential surrogate pairs. - // The idea is that our buffer here should be able to at least hold the same amount - // of characters as the old ANSI version would have supported. - int num_chars = buffer_size * 2; - wchar_t* w_buf = (wchar_t*)malloc(num_chars * sizeof(wchar_t)); - (void)fgetws(w_buf, num_chars, stdin); - string u8_str; - if (utf16_to_utf8(&u8_str, w_buf)) - { - strncpy(buffer, u8_str, buffer_size); - buffer[buffer_size-1] = '\0'; - } - free(w_buf); + // RPG Hacker: Using buffer_size * 2 here to account for potential surrogate pairs. + // The idea is that our buffer here should be able to at least hold the same amount + // of characters as the old ANSI version would have supported. + int num_chars = buffer_size * 2; + wchar_t* w_buf = (wchar_t*)malloc(num_chars * sizeof(wchar_t)); + (void)fgetws(w_buf, num_chars, stdin); + string u8_str; + if (utf16_to_utf8(&u8_str, w_buf)) { + strncpy(buffer, u8_str, buffer_size); + buffer[buffer_size - 1] = '\0'; + } + free(w_buf); #else - (void)fgets(buffer, buffer_size, handle); + (void)fgets(buffer, buffer_size, handle); #endif } -static const char * requirestrfromuser(const char * question, bool filename) -{ - confirmclose=false; - char * rval=(char*)malloc(256); - *rval=0; - while (!strchr(rval, '\n') || *rval=='\n') - { - *rval=0; - printf("%s ", question); - u8_fgets(rval, 250, stdin); - } - *strchr(rval, '\n')=0; - confirmclose=true; +static const char* requirestrfromuser(const char* question, bool filename) { + confirmclose = false; + char* rval = (char*)malloc(256); + *rval = 0; + while (!strchr(rval, '\n') || *rval == '\n') { + *rval = 0; + printf("%s ", question); + u8_fgets(rval, 250, stdin); + } + *strchr(rval, '\n') = 0; + confirmclose = true; #ifdef _WIN32 - if (filename && rval[0]=='"' && rval[2]==':') - { - char * rvalend=strchr(rval, '\0'); - if (rvalend[-1]=='"') rvalend[-1]='\0'; - return rval+1; - } + if (filename && rval[0] == '"' && rval[2] == ':') { + char* rvalend = strchr(rval, '\0'); + if (rvalend[-1] == '"') rvalend[-1] = '\0'; + return rval + 1; + } #endif - return rval; + return rval; } -static const char * requeststrfromuser(const char * question, bool filename, const char * defval) -{ - confirmclose=false; - char * rval=(char*)malloc(256); - *rval=0; - printf("%s ", question); - u8_fgets(rval, 250, stdin); - char *eol = strchr(rval, '\n'); - if(!eol) - { - printf("Unexpected end of input"); - exit(-1); - } - *eol = 0; - confirmclose=true; - if (!*rval) return defval; +static const char* requeststrfromuser(const char* question, bool filename, + const char* defval) { + confirmclose = false; + char* rval = (char*)malloc(256); + *rval = 0; + printf("%s ", question); + u8_fgets(rval, 250, stdin); + char* eol = strchr(rval, '\n'); + if (!eol) { + printf("Unexpected end of input"); + exit(-1); + } + *eol = 0; + confirmclose = true; + if (!*rval) return defval; #ifdef _WIN32 - if (filename && rval[0]=='"' && rval[2]==':') - { - char * rvalend=strchr(rval, '\0'); - if (rvalend[-1]=='"') rvalend[-1]='\0'; - return rval+1; - } + if (filename && rval[0] == '"' && rval[2] == ':') { + char* rvalend = strchr(rval, '\0'); + if (rvalend[-1] == '"') rvalend[-1] = '\0'; + return rval + 1; + } #endif - return rval; + return rval; } -void libcon_init(int argc, const char ** argv, const char * usage_) -{ - progname=argv[0]; - args=argv; - argsleft=argc-1; - usage=usage_; - libcon_interactive=(!argsleft); +void libcon_init(int argc, const char** argv, const char* usage_) { + progname = argv[0]; + args = argv; + argsleft = argc - 1; + usage = usage_; + libcon_interactive = (!argsleft); #if defined(_WIN32) - if (libcon_interactive) atexit(libcon_pause); + if (libcon_interactive) atexit(libcon_pause); #endif } -const char * libcon_require_filename(const char * desc) -{ - if (libcon_interactive) return requirestrfromuser(desc, true); - else return getarg(true); +const char* libcon_require_filename(const char* desc) { + if (libcon_interactive) + return requirestrfromuser(desc, true); + else + return getarg(true); } -const char * libcon_optional(const char * desc, const char * defval) -{ - if (libcon_interactive) return requeststrfromuser(desc, false, defval); - else return getarg(false, defval); +const char* libcon_optional(const char* desc, const char* defval) { + if (libcon_interactive) + return requeststrfromuser(desc, false, defval); + else + return getarg(false, defval); } -const char * libcon_optional_filename(const char * desc, const char * defval) -{ - if (libcon_interactive) return requeststrfromuser(desc, true, defval); - else return getarg(false, defval); +const char* libcon_optional_filename(const char* desc, const char* defval) { + if (libcon_interactive) + return requeststrfromuser(desc, true, defval); + else + return getarg(false, defval); } -const char * libcon_option() -{ - if (!libcon_interactive && argsleft && args[1][0]=='-') return getarg(false); - return nullptr; +const char* libcon_option() { + if (!libcon_interactive && argsleft && args[1][0] == '-') return getarg(false); + return nullptr; } -const char * libcon_option_value() -{ - if (!libcon_interactive) return getarg(false); - return nullptr; +const char* libcon_option_value() { + if (!libcon_interactive) return getarg(false); + return nullptr; } -bool libcon_question_bool(const char * desc, bool defval) -{ - if (!libcon_interactive) return defval; - while (true) - { - const char * answer=requeststrfromuser(desc, false, defval?"y":"n"); - if (!stricmp(answer, "y") || !stricmp(answer, "yes")) return true; - if (!stricmp(answer, "n") || !stricmp(answer, "no")) return false; - } +bool libcon_question_bool(const char* desc, bool defval) { + if (!libcon_interactive) return defval; + while (true) { + const char* answer = requeststrfromuser(desc, false, defval ? "y" : "n"); + if (!stricmp(answer, "y") || !stricmp(answer, "yes")) return true; + if (!stricmp(answer, "n") || !stricmp(answer, "no")) return false; + } } -void libcon_end() -{ - if (!libcon_interactive && argsleft) libcon_badusage(); +void libcon_end() { + if (!libcon_interactive && argsleft) libcon_badusage(); } diff --git a/src/asar/libcon.h b/src/asar/libcon.h index c1ebdb68..7a1d0ba3 100644 --- a/src/asar/libcon.h +++ b/src/asar/libcon.h @@ -1,11 +1,11 @@ #pragma once -void libcon_init(int argc, const char * argv[], const char * usage); -const char * libcon_require_filename(const char * desc); -const char * libcon_optional(const char * desc, const char * defval); -const char * libcon_optional_filename(const char * desc, const char * defval); -const char * libcon_option(); -const char * libcon_option_value(); -bool libcon_question_bool(const char * desc, bool defval); +void libcon_init(int argc, const char* argv[], const char* usage); +const char* libcon_require_filename(const char* desc); +const char* libcon_optional(const char* desc, const char* defval); +const char* libcon_optional_filename(const char* desc, const char* defval); +const char* libcon_option(); +const char* libcon_option_value(); +bool libcon_question_bool(const char* desc, bool defval); void libcon_end(); void libcon_badusage(); void libcon_pause(); diff --git a/src/asar/libmisc.h b/src/asar/libmisc.h index 537486ed..31e02300 100644 --- a/src/asar/libmisc.h +++ b/src/asar/libmisc.h @@ -1,17 +1,13 @@ #pragma once -inline int min(int a, int b) -{ - return a > b ? b : a; -} +inline int min(int a, int b) { return a > b ? b : a; } -inline unsigned bitround(unsigned v) -{ - v--; - v |= v >> 1; - v |= v >> 2; - v |= v >> 4; - v |= v >> 8; - v |= v >> 16; - v++; - return v; +inline unsigned bitround(unsigned v) { + v--; + v |= v >> 1; + v |= v >> 2; + v |= v >> 4; + v |= v >> 8; + v |= v >> 16; + v++; + return v; } diff --git a/src/asar/libsmw.cpp b/src/asar/libsmw.cpp index be1f0b11..e4f32836 100644 --- a/src/asar/libsmw.cpp +++ b/src/asar/libsmw.cpp @@ -1,11 +1,12 @@ #include "asar.h" #include "crc32.h" - #include "platform/file-helpers.h" -mapper_t mapper=lorom; -int sa1banks[8]={0<<20, 1<<20, -1, -1, 2<<20, 3<<20, -1, -1}; -const unsigned char * romdata= nullptr; // NOTE: Changed into const to prevent direct write access - use writeromdata() functions below +mapper_t mapper = lorom; +int sa1banks[8] = {0 << 20, 1 << 20, -1, -1, 2 << 20, 3 << 20, -1, -1}; +const unsigned char* romdata = + nullptr; // NOTE: Changed into const to prevent direct write access - use + // writeromdata() functions below int romlen; static bool header; static FileHandleType thisfile = InvalidFileHandle; @@ -16,471 +17,468 @@ autoarray writtenblocks; // RPG Hacker: Uses binary search to find the insert position of our ROM write #ifdef ASAR_SHARED -static int findromwritepos(int snesoffset, int searchstartpos, int searchendpos) -{ - if (searchendpos == searchstartpos) - { - return searchstartpos; - } +static int findromwritepos(int snesoffset, int searchstartpos, int searchendpos) { + if (searchendpos == searchstartpos) { + return searchstartpos; + } - int centerpos = searchstartpos + ((searchendpos - searchstartpos) / 2); + int centerpos = searchstartpos + ((searchendpos - searchstartpos) / 2); - if (writtenblocks[centerpos].snesoffset >= snesoffset) - { - return findromwritepos(snesoffset, searchstartpos, centerpos); - } + if (writtenblocks[centerpos].snesoffset >= snesoffset) { + return findromwritepos(snesoffset, searchstartpos, centerpos); + } - return findromwritepos(snesoffset, centerpos + 1, searchendpos); + return findromwritepos(snesoffset, centerpos + 1, searchendpos); } -static void addromwriteforbank(int snesoffset, int numbytes) -{ - int currentbank = (snesoffset & 0xFF0000); +static void addromwriteforbank(int snesoffset, int numbytes) { + int currentbank = (snesoffset & 0xFF0000); - int insertpos = findromwritepos(snesoffset, 0, writtenblocks.count); + int insertpos = findromwritepos(snesoffset, 0, writtenblocks.count); - if (insertpos > 0 && (writtenblocks[insertpos - 1].snesoffset & 0xFF0000) == currentbank - && writtenblocks[insertpos - 1].snesoffset + writtenblocks[insertpos - 1].numbytes >= snesoffset) - { - // Merge if we overlap with a preceding block - int firstend = writtenblocks[insertpos - 1].snesoffset + writtenblocks[insertpos - 1].numbytes; - int secondend = snesoffset + numbytes; + if (insertpos > 0 && + (writtenblocks[insertpos - 1].snesoffset & 0xFF0000) == currentbank && + writtenblocks[insertpos - 1].snesoffset + + writtenblocks[insertpos - 1].numbytes >= + snesoffset) { + // Merge if we overlap with a preceding block + int firstend = writtenblocks[insertpos - 1].snesoffset + + writtenblocks[insertpos - 1].numbytes; + int secondend = snesoffset + numbytes; - int newend = (firstend > secondend ? firstend : secondend); + int newend = (firstend > secondend ? firstend : secondend); - numbytes = newend - writtenblocks[insertpos - 1].snesoffset; - snesoffset = writtenblocks[insertpos - 1].snesoffset; + numbytes = newend - writtenblocks[insertpos - 1].snesoffset; + snesoffset = writtenblocks[insertpos - 1].snesoffset; - writtenblocks.remove(insertpos - 1); - insertpos -= 1; - } + writtenblocks.remove(insertpos - 1); + insertpos -= 1; + } - while (insertpos < writtenblocks.count && (writtenblocks[insertpos].snesoffset & 0xFF0000) == currentbank - && snesoffset + numbytes >= writtenblocks[insertpos].snesoffset) - { - // Merge if we overlap with a succeeding block - int firstend = snesoffset + numbytes; - int secondend = writtenblocks[insertpos].snesoffset + writtenblocks[insertpos].numbytes; + while (insertpos < writtenblocks.count && + (writtenblocks[insertpos].snesoffset & 0xFF0000) == currentbank && + snesoffset + numbytes >= writtenblocks[insertpos].snesoffset) { + // Merge if we overlap with a succeeding block + int firstend = snesoffset + numbytes; + int secondend = + writtenblocks[insertpos].snesoffset + writtenblocks[insertpos].numbytes; - int newend = (firstend > secondend ? firstend : secondend); + int newend = (firstend > secondend ? firstend : secondend); - numbytes = newend - snesoffset; + numbytes = newend - snesoffset; - writtenblocks.remove(insertpos); - } + writtenblocks.remove(insertpos); + } - // Insert ROM write - writtenblockdata blockdata; - blockdata.snesoffset = snesoffset; - blockdata.pcoffset = snestopc(snesoffset); - blockdata.numbytes = numbytes; + // Insert ROM write + writtenblockdata blockdata; + blockdata.snesoffset = snesoffset; + blockdata.pcoffset = snestopc(snesoffset); + blockdata.numbytes = numbytes; - writtenblocks.insert(insertpos, blockdata); + writtenblocks.insert(insertpos, blockdata); } -static void addromwrite(int pcoffset, int numbytes) -{ - int snesaddr = pctosnes(pcoffset); - int bytesleft = numbytes; +static void addromwrite(int pcoffset, int numbytes) { + int snesaddr = pctosnes(pcoffset); + int bytesleft = numbytes; - // RPG Hacker: Some kind of witchcraft which I actually hope works as intended - // Basically, the purpose of this is to sort all ROM writes into banks for the sake of cleanness + // RPG Hacker: Some kind of witchcraft which I actually hope works as intended + // Basically, the purpose of this is to sort all ROM writes into banks for the sake + // of cleanness - while (((snesaddr >> 16) & 0xFF) != (((snesaddr + bytesleft) >> 16) & 0xFF)) - { - int bytesinbank = ((snesaddr + 0x10000) & 0xFF0000) - snesaddr; + while (((snesaddr >> 16) & 0xFF) != (((snesaddr + bytesleft) >> 16) & 0xFF)) { + int bytesinbank = ((snesaddr + 0x10000) & 0xFF0000) - snesaddr; - addromwriteforbank(snesaddr, bytesinbank); + addromwriteforbank(snesaddr, bytesinbank); - pcoffset += bytesinbank; - snesaddr = pctosnes(pcoffset); - bytesleft -= bytesinbank; - } + pcoffset += bytesinbank; + snesaddr = pctosnes(pcoffset); + bytesleft -= bytesinbank; + } - addromwriteforbank(snesaddr, bytesleft); + addromwriteforbank(snesaddr, bytesleft); } #endif -void writeromdata(int pcoffset, const void * indata, int numbytes) -{ - memcpy(const_cast(romdata) + pcoffset, indata, (size_t)numbytes); - #ifdef ASAR_SHARED - addromwrite(pcoffset, numbytes); - #endif +void writeromdata(int pcoffset, const void* indata, int numbytes) { + memcpy(const_cast(romdata) + pcoffset, indata, (size_t)numbytes); +#ifdef ASAR_SHARED + addromwrite(pcoffset, numbytes); +#endif } -void writeromdata_byte(int pcoffset, unsigned char indata) -{ - memcpy(const_cast(romdata) + pcoffset, &indata, 1); - #ifdef ASAR_SHARED - addromwrite(pcoffset, 1); - #endif +void writeromdata_byte(int pcoffset, unsigned char indata) { + memcpy(const_cast(romdata) + pcoffset, &indata, 1); +#ifdef ASAR_SHARED + addromwrite(pcoffset, 1); +#endif } -void writeromdata_bytes(int pcoffset, unsigned char indata, int numbytes) -{ - memset(const_cast(romdata) + pcoffset, indata, (size_t)numbytes); - #ifdef ASAR_SHARED - addromwrite(pcoffset, numbytes); - #endif +void writeromdata_bytes(int pcoffset, unsigned char indata, int numbytes) { + memset(const_cast(romdata) + pcoffset, indata, (size_t)numbytes); +#ifdef ASAR_SHARED + addromwrite(pcoffset, numbytes); +#endif } -int ratsstart(int snesaddr) -{ - int pcaddr=snestopc(snesaddr); - if (pcaddr<0x7FFF8) return -1; - const unsigned char * start=romdata+pcaddr-0x10000; - for (int i=0x10000;i>=0;i--) - { - if (!strncmp((const char*)start+i, "STAR", 4) && - (start[i+4]^start[i+6])==0xFF && (start[i+5]^start[i+7])==0xFF) - { - if ((start[i+4]|(start[i+5]<<8))>0x10000-i-8-1) return pctosnes((int)(start-romdata+i)); - return -1; - } - } - return -1; +int ratsstart(int snesaddr) { + int pcaddr = snestopc(snesaddr); + if (pcaddr < 0x7FFF8) return -1; + const unsigned char* start = romdata + pcaddr - 0x10000; + for (int i = 0x10000; i >= 0; i--) { + if (!strncmp((const char*)start + i, "STAR", 4) && + (start[i + 4] ^ start[i + 6]) == 0xFF && + (start[i + 5] ^ start[i + 7]) == 0xFF) { + if ((start[i + 4] | (start[i + 5] << 8)) > 0x10000 - i - 8 - 1) + return pctosnes((int)(start - romdata + i)); + return -1; + } + } + return -1; } -void resizerats(int snesaddr, int newlen) -{ - int pos=snestopc(ratsstart(snesaddr)); - if (pos<0) return; - if (newlen!=0) newlen--; - writeromdata_byte(pos+4, (unsigned char)(newlen&0xFF)); - writeromdata_byte(pos+5, (unsigned char)((newlen>>8)&0xFF)); - writeromdata_byte(pos+6, (unsigned char)((newlen&0xFF)^0xFF)); - writeromdata_byte(pos+7, (unsigned char)(((newlen>>8)&0xFF)^0xFF)); +void resizerats(int snesaddr, int newlen) { + int pos = snestopc(ratsstart(snesaddr)); + if (pos < 0) return; + if (newlen != 0) newlen--; + writeromdata_byte(pos + 4, (unsigned char)(newlen & 0xFF)); + writeromdata_byte(pos + 5, (unsigned char)((newlen >> 8) & 0xFF)); + writeromdata_byte(pos + 6, (unsigned char)((newlen & 0xFF) ^ 0xFF)); + writeromdata_byte(pos + 7, (unsigned char)(((newlen >> 8) & 0xFF) ^ 0xFF)); } -static void handleprot(int loc, char * name, int len, const unsigned char * contents) -{ - (void)loc; // RPG Hacker: Silence "unused argument" warning. - - if (!strncmp(name, "PROT", 4)) - { - memcpy(name, "NULL", 4);//to block recursion, in case someone is an idiot - if (len%3) return; - len/=3; - for (int i=0;i=0;i--) writeromdata_byte(addr+i, clean_byte); +void removerats(int snesaddr, unsigned char clean_byte) { + int addr = ratsstart(snesaddr); + if (addr < 0) return; + // randomdude999: don't forget bank borders + WalkMetadata(pctosnes(snestopc(addr) + 8), handleprot); + addr = snestopc(addr); + for (int i = (romdata[addr + 4] | (romdata[addr + 5] << 8)) + 8; i >= 0; i--) + writeromdata_byte(addr + i, clean_byte); } -static inline int trypcfreespace(int start, int end, int size, int banksize, int minalign, unsigned char freespacebyte) -{ - while (start+size<=end) - { - if ( - ((start+8)&~banksize)!=((start+size-1)&~banksize&0xFFFFFF)//if the contents won't fit in this bank... - && - (start&banksize&0xFFFFF8)!=(banksize&0xFFFFF8)//and the RATS tag can't fit in the bank either... - ) - { - start&=~banksize&0xFFFFFF;//round it down to the start of the bank, - start|=banksize&0xFFFFF8;//then round it up to the end minus the RATS tag... - continue; - } - if (minalign) - { - start&=~minalign&0xFFFFFF; - start|=minalign&0xFFFFF8; - } - if (!strncmp((const char*)romdata+start, "STAR", 4) && - (romdata[start+4]^romdata[start+6])==0xFF && (romdata[start+5]^romdata[start+7])==0xFF) - { - start+=(romdata[start+4]|(romdata[start+5]<<8))+1+8; - continue; - } - bool bad=false; - for (int i=0;i>8)&0xFF)); - writeromdata_byte(start+6, (unsigned char)((size&0xFF)^0xFF)); - writeromdata_byte(start+7, (unsigned char)(((size>>8)&0xFF)^0xFF)); - return start+8; - } - return -1; +static inline int trypcfreespace(int start, int end, int size, int banksize, + int minalign, unsigned char freespacebyte) { + while (start + size <= end) { + if (((start + 8) & ~banksize) != + ((start + size - 1) & ~banksize & + 0xFFFFFF) // if the contents won't fit in this bank... + && (start & banksize & 0xFFFFF8) != + (banksize & + 0xFFFFF8) // and the RATS tag can't fit in the bank either... + ) { + start &= ~banksize & 0xFFFFFF; // round it down to the start of the bank, + start |= banksize & + 0xFFFFF8; // then round it up to the end minus the RATS tag... + continue; + } + if (minalign) { + start &= ~minalign & 0xFFFFFF; + start |= minalign & 0xFFFFF8; + } + if (!strncmp((const char*)romdata + start, "STAR", 4) && + (romdata[start + 4] ^ romdata[start + 6]) == 0xFF && + (romdata[start + 5] ^ romdata[start + 7]) == 0xFF) { + start += (romdata[start + 4] | (romdata[start + 5] << 8)) + 1 + 8; + continue; + } + bool bad = false; + for (int i = 0; i < size; i++) { + if (romdata[start + i] != freespacebyte) { + // TheBiob: fix freedata align freezing. + if ((start & minalign) == 0x7FF8 && i < 8) i = 8; + start += i; + if (!i) + start++; // this could check for a rats tag instead, but somehow I + // think this will give better performance. + bad = true; + break; + } + } + if (bad) continue; + size -= 8; + if (size) size--; // rats tags eat one byte more than specified for some reason + writeromdata_byte(start + 0, 'S'); + writeromdata_byte(start + 1, 'T'); + writeromdata_byte(start + 2, 'A'); + writeromdata_byte(start + 3, 'R'); + writeromdata_byte(start + 4, (unsigned char)(size & 0xFF)); + writeromdata_byte(start + 5, (unsigned char)((size >> 8) & 0xFF)); + writeromdata_byte(start + 6, (unsigned char)((size & 0xFF) ^ 0xFF)); + writeromdata_byte(start + 7, (unsigned char)(((size >> 8) & 0xFF) ^ 0xFF)); + return start + 8; + } + return -1; } -//This function finds a block of freespace. -1 means "no freespace found", anything else is a PC address. -//isforcode=false tells it to favor banks 40+, true tells it to avoid them entirely. -//It automatically adds a RATS tag. - -int getpcfreespace(int size, bool isforcode, bool autoexpand, bool respectbankborders, bool align, unsigned char freespacebyte) -{ - if (!size) return 0x1234;//in case someone protects zero bytes for some dumb reason. - //You can write zero bytes to anywhere, so I'll just return something that removerats will ignore. - if (size>0x10000) return -1; - size+=8; - if (mapper==lorom) - { - if (size>0x8008 && respectbankborders) return -1; - rebootlorom: - if (romlen>0x200000 && !isforcode) - { - int pos=trypcfreespace(0x200000-8, (romlen<0x400000)?romlen:0x400000, size, - respectbankborders?0x7FFF:0xFFFFFF, align?0x7FFF:(respectbankborders || size<32768)?0:0x7FFF, freespacebyte); - if (pos>=0) return pos; - } - int pos=trypcfreespace(0x80000, (romlen<0x200000)?romlen:0x200000, size, - respectbankborders?0x7FFF:0xFFFFFF, align?0x7FFF:(respectbankborders || size<32768)?0:0x7FFF, freespacebyte); - if (pos>=0) return pos; - if (autoexpand) - { - if(0); - else if (romlen==0x080000) - { - romlen=0x100000; - writeromdata_byte(snestopc(0x00FFD7), 0x0A); - } - else if (romlen==0x100000) - { - romlen=0x200000; - writeromdata_byte(snestopc(0x00FFD7), 0x0B); - } - else if (isforcode) return -1;//no point creating freespace that can't be used - else if (romlen==0x200000 || romlen==0x300000) - { - romlen=0x400000; - writeromdata_byte(snestopc(0x00FFD7), 0x0C); - } - else return -1; - autoexpand=false; - goto rebootlorom; - } - } - if (mapper==hirom) - { - if (isforcode) - { - for(int i = 0x8000; i < min(romlen, 0x400000); i += 0xFFFF){ - int space = trypcfreespace(i, min(i+0x7FFF, romlen), size, 0x7FFF, align?0xFFFF:0, freespacebyte); - if(space != -1) return space; - } - return -1; - } - return trypcfreespace(0, romlen, size, 0xFFFF, align?0xFFFF:0, freespacebyte); - } - if (mapper==exlorom) - { - // RPG Hacker: Not really 100% sure what to do here, but I suppose this simplified code will do - // and we won't need all the complicated stuff from LoROM above? - if (isforcode) - { - trypcfreespace(0, min(romlen, 0x200000), size, 0x7FFF, align?0x7FFF:0, freespacebyte); - } - return trypcfreespace(0, romlen, size, 0x7FFF, align ? 0x7FFF : 0, freespacebyte); - } - if (mapper==exhirom) - { - if (isforcode) - { - for(int i = 0x8000; i < romlen && i < 0x400000; i += 0xFFFF){ - int space = trypcfreespace(i, min(i+0x7FFF, romlen), size, 0x7FFF, align?0xFFFF:0, freespacebyte); - if(space != -1) return space; - } - return -1; - } - return trypcfreespace(0, romlen, size, 0xFFFF, align?0xFFFF:0, freespacebyte); - } - if (mapper==sfxrom) - { - if (!isforcode) return -1; - // try not to overwrite smw stuff - return trypcfreespace(0x80000, romlen, size, 0x7FFF, align?0x7FFF:0, freespacebyte); - } - if (mapper==sa1rom) - { - rebootsa1rom: - int nextbank=-1; - for (int i=0;i<8;i++) - { - if (i&2) continue; - if (sa1banks[i]+0x100000>romlen) - { - if (sa1banks[i]<=romlen && sa1banks[i]+0x100000>romlen) nextbank=sa1banks[i]; - continue; - } - int pos=trypcfreespace(sa1banks[i]?sa1banks[i]:0x80000, sa1banks[i]+0x100000, size, 0x7FFF, align?0x7FFF:0, freespacebyte); - if (pos>=0) return pos; - } - if (autoexpand && nextbank>=0) - { - unsigned char x7FD7[]={0, 0x0A, 0x0B, 0x0C, 0x0C, 0x0D, 0x0D, 0x0D, 0x0D}; - romlen=nextbank+0x100000; - writeromdata_byte(0x7FD7, x7FD7[romlen>>20]); - autoexpand=false; - goto rebootsa1rom; - } - } - if (mapper==bigsa1rom) - { - if(!isforcode && romlen > 0x400000) - { - int pos=trypcfreespace(0x400000, romlen, size, 0xFFFF, align?0xFFFF:0, freespacebyte); - if(pos>=0) return pos; - } - int pos=trypcfreespace(0x080000, romlen, size, 0x7FFF, align?0x7FFF:0, freespacebyte); - if(pos>=0) return pos; - } - return -1; +// This function finds a block of freespace. -1 means "no freespace found", anything +// else is a PC address. isforcode=false tells it to favor banks 40+, true tells it to +// avoid them entirely. It automatically adds a RATS tag. + +int getpcfreespace(int size, bool isforcode, bool autoexpand, bool respectbankborders, + bool align, unsigned char freespacebyte) { + if (!size) + return 0x1234; // in case someone protects zero bytes for some dumb reason. + // You can write zero bytes to anywhere, so I'll just return something that + // removerats will ignore. + if (size > 0x10000) return -1; + size += 8; + if (mapper == lorom) { + if (size > 0x8008 && respectbankborders) return -1; + rebootlorom: + if (romlen > 0x200000 && !isforcode) { + int pos = trypcfreespace(0x200000 - 8, + (romlen < 0x400000) ? romlen : 0x400000, size, + respectbankborders ? 0x7FFF : 0xFFFFFF, + align ? 0x7FFF + : (respectbankborders || size < 32768) ? 0 + : 0x7FFF, + freespacebyte); + if (pos >= 0) return pos; + } + int pos = trypcfreespace(0x80000, (romlen < 0x200000) ? romlen : 0x200000, size, + respectbankborders ? 0x7FFF : 0xFFFFFF, + align ? 0x7FFF + : (respectbankborders || size < 32768) ? 0 + : 0x7FFF, + freespacebyte); + if (pos >= 0) return pos; + if (autoexpand) { + if (0) + ; + else if (romlen == 0x080000) { + romlen = 0x100000; + writeromdata_byte(snestopc(0x00FFD7), 0x0A); + } else if (romlen == 0x100000) { + romlen = 0x200000; + writeromdata_byte(snestopc(0x00FFD7), 0x0B); + } else if (isforcode) + return -1; // no point creating freespace that can't be used + else if (romlen == 0x200000 || romlen == 0x300000) { + romlen = 0x400000; + writeromdata_byte(snestopc(0x00FFD7), 0x0C); + } else + return -1; + autoexpand = false; + goto rebootlorom; + } + } + if (mapper == hirom) { + if (isforcode) { + for (int i = 0x8000; i < min(romlen, 0x400000); i += 0xFFFF) { + int space = trypcfreespace(i, min(i + 0x7FFF, romlen), size, 0x7FFF, + align ? 0xFFFF : 0, freespacebyte); + if (space != -1) return space; + } + return -1; + } + return trypcfreespace(0, romlen, size, 0xFFFF, align ? 0xFFFF : 0, + freespacebyte); + } + if (mapper == exlorom) { + // RPG Hacker: Not really 100% sure what to do here, but I suppose this + // simplified code will do and we won't need all the complicated stuff from + // LoROM above? + if (isforcode) { + trypcfreespace(0, min(romlen, 0x200000), size, 0x7FFF, align ? 0x7FFF : 0, + freespacebyte); + } + return trypcfreespace(0, romlen, size, 0x7FFF, align ? 0x7FFF : 0, + freespacebyte); + } + if (mapper == exhirom) { + if (isforcode) { + for (int i = 0x8000; i < romlen && i < 0x400000; i += 0xFFFF) { + int space = trypcfreespace(i, min(i + 0x7FFF, romlen), size, 0x7FFF, + align ? 0xFFFF : 0, freespacebyte); + if (space != -1) return space; + } + return -1; + } + return trypcfreespace(0, romlen, size, 0xFFFF, align ? 0xFFFF : 0, + freespacebyte); + } + if (mapper == sfxrom) { + if (!isforcode) return -1; + // try not to overwrite smw stuff + return trypcfreespace(0x80000, romlen, size, 0x7FFF, align ? 0x7FFF : 0, + freespacebyte); + } + if (mapper == sa1rom) { + rebootsa1rom: + int nextbank = -1; + for (int i = 0; i < 8; i++) { + if (i & 2) continue; + if (sa1banks[i] + 0x100000 > romlen) { + if (sa1banks[i] <= romlen && sa1banks[i] + 0x100000 > romlen) + nextbank = sa1banks[i]; + continue; + } + int pos = trypcfreespace(sa1banks[i] ? sa1banks[i] : 0x80000, + sa1banks[i] + 0x100000, size, 0x7FFF, + align ? 0x7FFF : 0, freespacebyte); + if (pos >= 0) return pos; + } + if (autoexpand && nextbank >= 0) { + unsigned char x7FD7[] = {0, 0x0A, 0x0B, 0x0C, 0x0C, 0x0D, 0x0D, 0x0D, 0x0D}; + romlen = nextbank + 0x100000; + writeromdata_byte(0x7FD7, x7FD7[romlen >> 20]); + autoexpand = false; + goto rebootsa1rom; + } + } + if (mapper == bigsa1rom) { + if (!isforcode && romlen > 0x400000) { + int pos = trypcfreespace(0x400000, romlen, size, 0xFFFF, align ? 0xFFFF : 0, + freespacebyte); + if (pos >= 0) return pos; + } + int pos = trypcfreespace(0x080000, romlen, size, 0x7FFF, align ? 0x7FFF : 0, + freespacebyte); + if (pos >= 0) return pos; + } + return -1; } -void WalkMetadata(int loc, void(*func)(int loc, char * name, int len, const unsigned char * contents)) -{ - int pcoff=snestopc(loc); - if (strncmp((const char*)romdata+pcoff-8, "STAR", 4)) return; - const unsigned char * metadata=romdata+pcoff; - while (is_upper(metadata[0]) && is_upper(metadata[1]) && is_upper(metadata[2]) && is_upper(metadata[3])) - { - if (!strncmp((const char*)metadata, "STOP", 4)) - { - metadata=romdata+pcoff; - while (is_upper(metadata[0]) && is_upper(metadata[1]) && is_upper(metadata[2]) && is_upper(metadata[3])) - { - if (!strncmp((const char*)metadata, "STOP", 4)) - { - break; - } - func(pctosnes((int)(metadata-romdata)), (char*)const_cast(metadata), metadata[4], metadata+5); - metadata+=5+metadata[4]; - } - break; - } - metadata+=5+metadata[4]; - } +void WalkMetadata(int loc, void (*func)(int loc, char* name, int len, + const unsigned char* contents)) { + int pcoff = snestopc(loc); + if (strncmp((const char*)romdata + pcoff - 8, "STAR", 4)) return; + const unsigned char* metadata = romdata + pcoff; + while (is_upper(metadata[0]) && is_upper(metadata[1]) && is_upper(metadata[2]) && + is_upper(metadata[3])) { + if (!strncmp((const char*)metadata, "STOP", 4)) { + metadata = romdata + pcoff; + while (is_upper(metadata[0]) && is_upper(metadata[1]) && + is_upper(metadata[2]) && is_upper(metadata[3])) { + if (!strncmp((const char*)metadata, "STOP", 4)) { + break; + } + func(pctosnes((int)(metadata - romdata)), + (char*)const_cast(metadata), metadata[4], + metadata + 5); + metadata += 5 + metadata[4]; + } + break; + } + metadata += 5 + metadata[4]; + } } -int getsnesfreespace(int size, bool isforcode, bool autoexpand, bool respectbankborders, bool align, unsigned char freespacebyte) -{ - return pctosnes(getpcfreespace(size, isforcode, autoexpand, respectbankborders, align, freespacebyte)); +int getsnesfreespace(int size, bool isforcode, bool autoexpand, bool respectbankborders, + bool align, unsigned char freespacebyte) { + return pctosnes(getpcfreespace(size, isforcode, autoexpand, respectbankborders, + align, freespacebyte)); } -bool openrom(const char * filename, bool confirm) -{ - closerom(); - thisfile = open_file(filename, FileOpenMode_ReadWrite); - if (thisfile == InvalidFileHandle) - { - openromerror = error_id_open_rom_failed; - return false; - } - header=false; - if (strlen(filename)>4) - { - const char * fnameend=strchr(filename, '\0')-4; - header=(!stricmp(fnameend, ".smc")); - } - romlen=(int)get_file_size(thisfile)-(header*512); - if (romlen<0) romlen=0; - set_file_pos(thisfile, header*512); - romdata=(unsigned char*)malloc(sizeof(unsigned char)*16*1024*1024); - int truelen=(int)read_file(thisfile, (void*)romdata, (uint32_t)romlen); - if (truelen!=romlen) - { - openromerror = error_id_open_rom_failed; - free(const_cast(romdata)); - return false; - } - memset(const_cast(romdata)+romlen, 0x00, (size_t)(16*1024*1024-romlen)); - if (confirm && snestopc(0x00FFC0)+21<(int)romlen && strncmp((const char*)romdata+snestopc(0x00FFC0), "SUPER MARIOWORLD ", 21)) - { - closerom(false); - openromerror = header ? error_id_open_rom_not_smw_extension : error_id_open_rom_not_smw_header; - return false; - } - return true; +bool openrom(const char* filename, bool confirm) { + closerom(); + thisfile = open_file(filename, FileOpenMode_ReadWrite); + if (thisfile == InvalidFileHandle) { + openromerror = error_id_open_rom_failed; + return false; + } + header = false; + if (strlen(filename) > 4) { + const char* fnameend = strchr(filename, '\0') - 4; + header = (!stricmp(fnameend, ".smc")); + } + romlen = (int)get_file_size(thisfile) - (header * 512); + if (romlen < 0) romlen = 0; + set_file_pos(thisfile, header * 512); + romdata = (unsigned char*)malloc(sizeof(unsigned char) * 16 * 1024 * 1024); + int truelen = (int)read_file(thisfile, (void*)romdata, (uint32_t)romlen); + if (truelen != romlen) { + openromerror = error_id_open_rom_failed; + free(const_cast(romdata)); + return false; + } + memset(const_cast(romdata) + romlen, 0x00, + (size_t)(16 * 1024 * 1024 - romlen)); + if (confirm && snestopc(0x00FFC0) + 21 < (int)romlen && + strncmp((const char*)romdata + snestopc(0x00FFC0), "SUPER MARIOWORLD ", + 21)) { + closerom(false); + openromerror = header ? error_id_open_rom_not_smw_extension + : error_id_open_rom_not_smw_header; + return false; + } + return true; } -uint32_t closerom(bool save) -{ - uint32_t romCrc = 0; - if (thisfile != InvalidFileHandle && save && romlen) - { - set_file_pos(thisfile, header*512); - write_file(thisfile, romdata, (uint32_t)romlen); - - // do a quick re-read of the header, and include that in the crc32 calculation if necessary - { - uint8_t* filedata = (uint8_t*)malloc(sizeof(uint8_t) * (romlen + header * 512)); - if (header) - { - set_file_pos(thisfile, 0u); - read_file(thisfile, filedata, 512); - } - memcpy(filedata + (header * 512), romdata, sizeof(uint8_t) * (size_t)romlen); - romCrc = crc32(filedata, (unsigned int)(romlen + header * 512)); - free(filedata); - } - } - if (thisfile != InvalidFileHandle) close_file(thisfile); - if (romdata) free(const_cast(romdata)); - thisfile= InvalidFileHandle; - romdata= nullptr; - romlen=0; - return romCrc; +uint32_t closerom(bool save) { + uint32_t romCrc = 0; + if (thisfile != InvalidFileHandle && save && romlen) { + set_file_pos(thisfile, header * 512); + write_file(thisfile, romdata, (uint32_t)romlen); + + // do a quick re-read of the header, and include that in the crc32 calculation + // if necessary + { + uint8_t* filedata = + (uint8_t*)malloc(sizeof(uint8_t) * (romlen + header * 512)); + if (header) { + set_file_pos(thisfile, 0u); + read_file(thisfile, filedata, 512); + } + memcpy(filedata + (header * 512), romdata, + sizeof(uint8_t) * (size_t)romlen); + romCrc = crc32(filedata, (unsigned int)(romlen + header * 512)); + free(filedata); + } + } + if (thisfile != InvalidFileHandle) close_file(thisfile); + if (romdata) free(const_cast(romdata)); + thisfile = InvalidFileHandle; + romdata = nullptr; + romlen = 0; + return romCrc; } -static unsigned int getchecksum() -{ - unsigned int checksum=0; - if((romlen & (romlen-1)) == 0) - { - // romlen is a power of 2, just add up all the bytes - for (int i=0;i> 1; - int secondpart = romlen - firstpart; - int repeatcount = firstpart / secondpart; - unsigned int secondpart_sum = 0; - for(int i = 0; i < firstpart; i++) checksum += romdata[i]; - for(int i = firstpart; i < romlen; i++) secondpart_sum += romdata[i]; - checksum += secondpart_sum * repeatcount; - } - return checksum&0xFFFF; +static unsigned int getchecksum() { + unsigned int checksum = 0; + if ((romlen & (romlen - 1)) == 0) { + // romlen is a power of 2, just add up all the bytes + for (int i = 0; i < romlen; i++) checksum += romdata[i]; + } else { + // assume romlen is the sum of 2 powers of 2 - i haven't seen any real rom that + // isn't, and if you make such a rom, fixing its checksum is your problem. + int firstpart = bitround(romlen) >> 1; + int secondpart = romlen - firstpart; + int repeatcount = firstpart / secondpart; + unsigned int secondpart_sum = 0; + for (int i = 0; i < firstpart; i++) checksum += romdata[i]; + for (int i = firstpart; i < romlen; i++) secondpart_sum += romdata[i]; + checksum += secondpart_sum * repeatcount; + } + return checksum & 0xFFFF; } -void fixchecksum() -{ - // randomdude999: clear out checksum bytes before recalculating checksum, this should make it correct on roms that don't have a checksum yet - writeromdata(snestopc(0x00FFDC), "\xFF\xFF\0\0", 4); - int checksum=(int)getchecksum(); - writeromdata_byte(snestopc(0x00FFDE), (unsigned char)(checksum&255)); - writeromdata_byte(snestopc(0x00FFDF), (unsigned char)((checksum>>8)&255)); - writeromdata_byte(snestopc(0x00FFDC), (unsigned char)((checksum&255)^255)); - writeromdata_byte(snestopc(0x00FFDD), (unsigned char)(((checksum>>8)&255)^255)); +void fixchecksum() { + // randomdude999: clear out checksum bytes before recalculating checksum, this + // should make it correct on roms that don't have a checksum yet + writeromdata(snestopc(0x00FFDC), "\xFF\xFF\0\0", 4); + int checksum = (int)getchecksum(); + writeromdata_byte(snestopc(0x00FFDE), (unsigned char)(checksum & 255)); + writeromdata_byte(snestopc(0x00FFDF), (unsigned char)((checksum >> 8) & 255)); + writeromdata_byte(snestopc(0x00FFDC), (unsigned char)((checksum & 255) ^ 255)); + writeromdata_byte(snestopc(0x00FFDD), + (unsigned char)(((checksum >> 8) & 255) ^ 255)); } diff --git a/src/asar/libsmw.h b/src/asar/libsmw.h index feaa6917..b3e40d9d 100644 --- a/src/asar/libsmw.h +++ b/src/asar/libsmw.h @@ -1,201 +1,188 @@ #pragma once -#include "errors.h" -#include "autoarray.h" #include -extern const unsigned char * romdata; +#include "autoarray.h" +#include "errors.h" + +extern const unsigned char* romdata; extern int romlen; extern asar_error_id openromerror; -bool openrom(const char * filename, bool confirm=true); +bool openrom(const char* filename, bool confirm = true); uint32_t closerom(bool save = true); enum mapper_t { - invalid_mapper, - lorom, - hirom, - sa1rom, - bigsa1rom, - sfxrom, - exlorom, - exhirom, - norom + invalid_mapper, + lorom, + hirom, + sa1rom, + bigsa1rom, + sfxrom, + exlorom, + exhirom, + norom } extern mapper; -extern int sa1banks[8];//only 0, 1, 4, 5 are used +extern int sa1banks[8]; // only 0, 1, 4, 5 are used -void writeromdata(int pcoffset, const void * indata, int numbytes); +void writeromdata(int pcoffset, const void* indata, int numbytes); void writeromdata_byte(int pcoffset, unsigned char indata); void writeromdata_bytes(int pcoffset, unsigned char indata, int numbytes); struct writtenblockdata { - int pcoffset; - int snesoffset; - int numbytes; + int pcoffset; + int snesoffset; + int numbytes; }; extern autoarray writtenblocks; -inline int snestopc(int addr) -{ - if (addr<0 || addr>0xFFFFFF) return -1;//not 24bit - if (mapper==lorom) - { - // randomdude999: The low pages ($0000-$7FFF) of banks 70-7D are used - // for SRAM, the high pages are available for ROM data though - if ((addr&0xFE0000)==0x7E0000 ||//wram - (addr&0x408000)==0x000000 ||//hardware regs, ram mirrors, other strange junk - (addr&0x708000)==0x700000)//sram (low parts of banks 70-7D) - return -1; - addr=((addr&0x7F0000)>>1|(addr&0x7FFF)); - return addr; - } - if (mapper==hirom) - { - if ((addr&0xFE0000)==0x7E0000 ||//wram - (addr&0x408000)==0x000000)//hardware regs, ram mirrors, other strange junk - return -1; - return addr&0x3FFFFF; - } - if (mapper==exlorom) - { - if ((addr&0xF00000)==0x700000 ||//wram, sram - (addr&0x408000)==0x000000)//area that shouldn't be used in lorom - return -1; - if (addr&0x800000) - { - addr=((addr&0x7F0000)>>1|(addr&0x7FFF)); - } - else - { - addr=((addr&0x7F0000)>>1|(addr&0x7FFF))+0x400000; - } - return addr; - } - if (mapper==exhirom) - { - if ((addr&0xFE0000)==0x7E0000 ||//wram - (addr&0x408000)==0x000000)//hardware regs, ram mirrors, other strange junk - return -1; - if ((addr&0x800000)==0x000000) return (addr&0x3FFFFF)|0x400000; - return addr&0x3FFFFF; - } - if (mapper==sfxrom) - { - // Asar emulates GSU1, because apparently emulators don't support the extra ROM data from GSU2 - if ((addr&0x600000)==0x600000 ||//wram, sram, open bus - (addr&0x408000)==0x000000 ||//hardware regs, ram mirrors, rom mirrors, other strange junk - (addr&0x800000)==0x800000)//fastrom isn't valid either in superfx - return -1; - if (addr&0x400000) return addr&0x3FFFFF; - else return (addr&0x7F0000)>>1|(addr&0x7FFF); - } - if (mapper==sa1rom) - { - if ((addr&0x408000)==0x008000) - { - return sa1banks[(addr&0xE00000)>>21]|((addr&0x1F0000)>>1)|(addr&0x007FFF); - } - if ((addr&0xC00000)==0xC00000) +inline int snestopc(int addr) { + if (addr < 0 || addr > 0xFFFFFF) return -1; // not 24bit + if (mapper == lorom) { + // randomdude999: The low pages ($0000-$7FFF) of banks 70-7D are used + // for SRAM, the high pages are available for ROM data though + if ((addr & 0xFE0000) == 0x7E0000 || // wram + (addr & 0x408000) == + 0x000000 || // hardware regs, ram mirrors, other strange junk + (addr & 0x708000) == 0x700000) // sram (low parts of banks 70-7D) + return -1; + addr = ((addr & 0x7F0000) >> 1 | (addr & 0x7FFF)); + return addr; + } + if (mapper == hirom) { + if ((addr & 0xFE0000) == 0x7E0000 || // wram + (addr & 0x408000) == + 0x000000) // hardware regs, ram mirrors, other strange junk + return -1; + return addr & 0x3FFFFF; + } + if (mapper == exlorom) { + if ((addr & 0xF00000) == 0x700000 || // wram, sram + (addr & 0x408000) == 0x000000) // area that shouldn't be used in lorom + return -1; + if (addr & 0x800000) { + addr = ((addr & 0x7F0000) >> 1 | (addr & 0x7FFF)); + } else { + addr = ((addr & 0x7F0000) >> 1 | (addr & 0x7FFF)) + 0x400000; + } + return addr; + } + if (mapper == exhirom) { + if ((addr & 0xFE0000) == 0x7E0000 || // wram + (addr & 0x408000) == + 0x000000) // hardware regs, ram mirrors, other strange junk + return -1; + if ((addr & 0x800000) == 0x000000) return (addr & 0x3FFFFF) | 0x400000; + return addr & 0x3FFFFF; + } + if (mapper == sfxrom) { + // Asar emulates GSU1, because apparently emulators don't support the extra ROM + // data from GSU2 + if ((addr & 0x600000) == 0x600000 || // wram, sram, open bus + (addr & 0x408000) == 0x000000 || // hardware regs, ram mirrors, rom + // mirrors, other strange junk + (addr & 0x800000) == 0x800000) // fastrom isn't valid either in superfx + return -1; + if (addr & 0x400000) + return addr & 0x3FFFFF; + else + return (addr & 0x7F0000) >> 1 | (addr & 0x7FFF); + } + if (mapper == sa1rom) { + if ((addr & 0x408000) == 0x008000) { + return sa1banks[(addr & 0xE00000) >> 21] | ((addr & 0x1F0000) >> 1) | + (addr & 0x007FFF); + } + if ((addr & 0xC00000) == 0xC00000) { + return sa1banks[((addr & 0x100000) >> 20) | ((addr & 0x200000) >> 19)] | + (addr & 0x0FFFFF); + } + return -1; + } + if (mapper == bigsa1rom) { + if ((addr & 0xC00000) == 0xC00000) // hirom { - return sa1banks[((addr&0x100000)>>20)|((addr&0x200000)>>19)]|(addr&0x0FFFFF); + return (addr & 0x3FFFFF) | 0x400000; } - return -1; - } - if (mapper==bigsa1rom) - { - if ((addr&0xC00000)==0xC00000)//hirom - { - return (addr&0x3FFFFF)|0x400000; - } - if ((addr&0xC00000)==0x000000 || (addr&0xC00000)==0x800000)//lorom - { - if ((addr&0x008000)==0x000000) return -1; - return (addr&0x800000)>>2 | (addr&0x3F0000)>>1 | (addr&0x7FFF); - } - return -1; - } - if (mapper==norom) - { - return addr; - } - return -1; + if ((addr & 0xC00000) == 0x000000 || (addr & 0xC00000) == 0x800000) // lorom + { + if ((addr & 0x008000) == 0x000000) return -1; + return (addr & 0x800000) >> 2 | (addr & 0x3F0000) >> 1 | (addr & 0x7FFF); + } + return -1; + } + if (mapper == norom) { + return addr; + } + return -1; } -inline int pctosnes(int addr) -{ - if (addr<0) return -1; - if (mapper==lorom) - { - if (addr>=0x400000) return -1; - addr=((addr<<1)&0x7F0000)|(addr&0x7FFF)|0x8000; - return addr|0x800000; - } - if (mapper==hirom) - { - if (addr>=0x400000) return -1; - return addr|0xC00000; - } - if (mapper == exlorom) - { - if (addr>=0x800000) return -1; - if (addr&0x400000) - { - addr-=0x400000; - addr=((addr<<1)&0x7F0000)|(addr&0x7FFF)|0x8000; - return addr; - } - else - { - addr=((addr<<1)&0x7F0000)|(addr&0x7FFF)|0x8000; - return addr|0x800000; - } - } - if (mapper == exhirom) - { - if (addr>=0x800000) return -1; - if (addr&0x400000) return addr; - return addr|0xC00000; - } - if (mapper==sa1rom) - { - for (int i=0;i<8;i++) - { - if (sa1banks[i]==(addr&0x700000)){ return 0x008000|(i<<21)|((addr&0x0F8000)<<1)|(addr&0x7FFF);} - } - return -1; - } - if (mapper==bigsa1rom) - { - if (addr>=0x800000) return -1; - if ((addr&0x400000)==0x400000) - { - return addr|0xC00000; - } - if ((addr&0x600000)==0x000000) - { - return ((addr<<1)&0x3F0000)|0x8000|(addr&0x7FFF); - } - if ((addr&0x600000)==0x200000) - { - return 0x800000|((addr<<1)&0x3F0000)|0x8000|(addr&0x7FFF); - } - return -1; - } - if (mapper==sfxrom) - { - if (addr>=0x200000) return -1; - return ((addr<<1)&0x7F0000)|(addr&0x7FFF)|0x8000; - } - if (mapper==norom) - { - return addr; - } - return -1; +inline int pctosnes(int addr) { + if (addr < 0) return -1; + if (mapper == lorom) { + if (addr >= 0x400000) return -1; + addr = ((addr << 1) & 0x7F0000) | (addr & 0x7FFF) | 0x8000; + return addr | 0x800000; + } + if (mapper == hirom) { + if (addr >= 0x400000) return -1; + return addr | 0xC00000; + } + if (mapper == exlorom) { + if (addr >= 0x800000) return -1; + if (addr & 0x400000) { + addr -= 0x400000; + addr = ((addr << 1) & 0x7F0000) | (addr & 0x7FFF) | 0x8000; + return addr; + } else { + addr = ((addr << 1) & 0x7F0000) | (addr & 0x7FFF) | 0x8000; + return addr | 0x800000; + } + } + if (mapper == exhirom) { + if (addr >= 0x800000) return -1; + if (addr & 0x400000) return addr; + return addr | 0xC00000; + } + if (mapper == sa1rom) { + for (int i = 0; i < 8; i++) { + if (sa1banks[i] == (addr & 0x700000)) { + return 0x008000 | (i << 21) | ((addr & 0x0F8000) << 1) | + (addr & 0x7FFF); + } + } + return -1; + } + if (mapper == bigsa1rom) { + if (addr >= 0x800000) return -1; + if ((addr & 0x400000) == 0x400000) { + return addr | 0xC00000; + } + if ((addr & 0x600000) == 0x000000) { + return ((addr << 1) & 0x3F0000) | 0x8000 | (addr & 0x7FFF); + } + if ((addr & 0x600000) == 0x200000) { + return 0x800000 | ((addr << 1) & 0x3F0000) | 0x8000 | (addr & 0x7FFF); + } + return -1; + } + if (mapper == sfxrom) { + if (addr >= 0x200000) return -1; + return ((addr << 1) & 0x7F0000) | (addr & 0x7FFF) | 0x8000; + } + if (mapper == norom) { + return addr; + } + return -1; } -int getpcfreespace(int size, bool isforcode, bool autoexpand=true, bool respectbankborders=true, bool align=false, unsigned char freespacebyte=0x00); -int getsnesfreespace(int size, bool isforcode, bool autoexpand=true, bool respectbankborders=true, bool align=false, unsigned char freespacebyte=0x00); +int getpcfreespace(int size, bool isforcode, bool autoexpand = true, + bool respectbankborders = true, bool align = false, + unsigned char freespacebyte = 0x00); +int getsnesfreespace(int size, bool isforcode, bool autoexpand = true, + bool respectbankborders = true, bool align = false, + unsigned char freespacebyte = 0x00); void resizerats(int snesaddr, int newlen); void removerats(int snesaddr, unsigned char clean_byte); @@ -203,4 +190,12 @@ int ratsstart(int pcaddr); void fixchecksum(); -void WalkMetadata(int loc, void(*func)(int loc, char * name, int len, const unsigned char * contents));//This one calls func() for each metadata block in the RATS tag whose contents (metadata) start at loc in the ROM. Do not replace name with an invalid metadata name, and note that name is not null terminated. +void WalkMetadata( + int loc, + void (*func)(int loc, char* name, int len, + const unsigned char* + contents)); // This one calls func() for each metadata + // block in the RATS tag whose contents + // (metadata) start at loc in the ROM. Do not + // replace name with an invalid metadata name, + // and note that name is not null terminated. diff --git a/src/asar/libstr.cpp b/src/asar/libstr.cpp index ffc7ebdd..8772642f 100644 --- a/src/asar/libstr.cpp +++ b/src/asar/libstr.cpp @@ -1,481 +1,443 @@ #include "asar.h" -#include "virtualfile.h" -#include "unicode.h" - #include "platform/file-helpers.h" +#include "unicode.h" +#include "virtualfile.h" -#define typed_malloc(type, count) (type*)malloc(sizeof(type)*(count)) -#define typed_realloc(type, ptr, count) (type*)realloc(ptr, sizeof(type)*(count)) +#define typed_malloc(type, count) (type*)malloc(sizeof(type) * (count)) +#define typed_realloc(type, ptr, count) (type*)realloc(ptr, sizeof(type) * (count)) // Detects if str starts with a UTF-8 byte order mark. -// If so, throws a warning, then returns the number of bytes we should skip ahead in the string. -size_t check_bom(const char* str) -{ - // RPG Hacker: We could also check for BoMs of incompatible encodings here (like UTF-16) - // and throw errors, but not sure if that's worth adding. Asar never supported any wide - // encodings to begin with, so it's unreasonable to assume that any UTF-16 patches currently - // exist for it. As for future patches, those should be caught by the "must be UTF-8" checks - // I have already implemented further below. - // I think UTF-8 + BoM is the only case that could lead to confusion if we didn't handle it, - // so that's why I have added this. - if (str[0u] == '\xEF' && str[1u] == '\xBB' && str[2u] == '\xBF') - { - asar_throw_warning(0, warning_id_byte_order_mark_utf8); - return 3u; - } - - return 0u; +// If so, throws a warning, then returns the number of bytes we should skip ahead in the +// string. +size_t check_bom(const char* str) { + // RPG Hacker: We could also check for BoMs of incompatible encodings here (like + // UTF-16) and throw errors, but not sure if that's worth adding. Asar never + // supported any wide encodings to begin with, so it's unreasonable to assume that + // any UTF-16 patches currently exist for it. As for future patches, those should be + // caught by the "must be UTF-8" checks I have already implemented further below. I + // think UTF-8 + BoM is the only case that could lead to confusion if we didn't + // handle it, so that's why I have added this. + if (str[0u] == '\xEF' && str[1u] == '\xBB' && str[2u] == '\xBF') { + asar_throw_warning(0, warning_id_byte_order_mark_utf8); + return 3u; + } + + return 0u; } -char * readfile(const char * fname, const char * basepath) -{ - virtual_file_handle myfile = filesystem->open_file(fname, basepath); - if (myfile == INVALID_VIRTUAL_FILE_HANDLE) return nullptr; - size_t datalen = filesystem->get_file_size(myfile); - char * data= typed_malloc(char, datalen+1); - data[filesystem->read_file(myfile, data, 0u, datalen)] = 0; - filesystem->close_file(myfile); - - if (!is_valid_utf8(data)) - { - free(data); - asar_throw_error(0, error_type_block, error_id_invalid_utf8); - } - if(check_bom(data)){ - data[0] = ' '; - data[1] = ' '; - data[2] = ' '; - } - return data; +char* readfile(const char* fname, const char* basepath) { + virtual_file_handle myfile = filesystem->open_file(fname, basepath); + if (myfile == INVALID_VIRTUAL_FILE_HANDLE) return nullptr; + size_t datalen = filesystem->get_file_size(myfile); + char* data = typed_malloc(char, datalen + 1); + data[filesystem->read_file(myfile, data, 0u, datalen)] = 0; + filesystem->close_file(myfile); + + if (!is_valid_utf8(data)) { + free(data); + asar_throw_error(0, error_type_block, error_id_invalid_utf8); + } + if (check_bom(data)) { + data[0] = ' '; + data[1] = ' '; + data[2] = ' '; + } + return data; } // RPG Hacker: like readfile(), but doesn't use virtual file system // and instead read our file directly. -char * readfilenative(const char * fname) -{ - FileHandleType myfile = open_file(fname, FileOpenMode_Read); - if (myfile == InvalidFileHandle) return nullptr; - size_t datalen = (size_t)get_file_size(myfile); - char * data = typed_malloc(char, datalen + 1); - data[read_file(myfile, data, datalen)] = 0; - close_file(myfile); - - if (!is_valid_utf8(data)) asar_throw_error(0, error_type_block, error_id_invalid_utf8); - if(check_bom(data)){ - data[0] = ' '; - data[1] = ' '; - data[2] = ' '; - } - return data; +char* readfilenative(const char* fname) { + FileHandleType myfile = open_file(fname, FileOpenMode_Read); + if (myfile == InvalidFileHandle) return nullptr; + size_t datalen = (size_t)get_file_size(myfile); + char* data = typed_malloc(char, datalen + 1); + data[read_file(myfile, data, datalen)] = 0; + close_file(myfile); + + if (!is_valid_utf8(data)) + asar_throw_error(0, error_type_block, error_id_invalid_utf8); + if (check_bom(data)) { + data[0] = ' '; + data[1] = ' '; + data[2] = ' '; + } + return data; } -bool readfile(const char * fname, const char * basepath, char ** data, int * len) -{ - virtual_file_handle myfile = filesystem->open_file(fname, basepath); - if (!myfile) return false; - size_t datalen = filesystem->get_file_size(myfile); - *data= typed_malloc(char, datalen); - *len = (int)filesystem->read_file(myfile, *data, 0, datalen); - filesystem->close_file(myfile); - return true; +bool readfile(const char* fname, const char* basepath, char** data, int* len) { + virtual_file_handle myfile = filesystem->open_file(fname, basepath); + if (!myfile) return false; + size_t datalen = filesystem->get_file_size(myfile); + *data = typed_malloc(char, datalen); + *len = (int)filesystem->read_file(myfile, *data, 0, datalen); + filesystem->close_file(myfile); + return true; } -#define isq(n) (((0x2227 ^ (0x0101 * (n))) - 0x0101UL) & ~(0x2227 ^ (0x0101 * (n))) & 0x8080UL) -#define isqp(n) (((0x22272829 ^ (0x01010101 * (n))) - 0x01010101UL) & ~(0x22272829 ^ (0x01010101 * (n))) & 0x80808080UL) +#define isq(n) \ + (((0x2227 ^ (0x0101 * (n))) - 0x0101UL) & ~(0x2227 ^ (0x0101 * (n))) & 0x8080UL) +#define isqp(n) \ + (((0x22272829 ^ (0x01010101 * (n))) - 0x01010101UL) & \ + ~(0x22272829 ^ (0x01010101 * (n))) & 0x80808080UL) // RPG Hacker: Only index this with ASCII characters. // Anything else doesn't make sense, anyways. const bool qparlut[128] = { - 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }; -//this will leave the last char found as the one pointed at -inline bool skip_quote(char *&str) -{ - - if(*str == '"') str = strchr(str + 1, '"'); - else if(*str == '\'') - { - int codepoint; - str += utf8_val(&codepoint, str + 1) + 1; - if(*str != '\'') return false; - } - return str; +// this will leave the last char found as the one pointed at +inline bool skip_quote(char*& str) { + if (*str == '"') + str = strchr(str + 1, '"'); + else if (*str == '\'') { + int codepoint; + str += utf8_val(&codepoint, str + 1) + 1; + if (*str != '\'') return false; + } + return str; } -//eat 1 char or quote/par -inline bool skip_par(char *&str) -{ - int par = 0; - if(*str != '\'' && *str != '"' && *str != '(' && *str != ')') - { - str++; - return true; - } - while(true) - { - char *t = str; - if(*str == '"') t = strchr(t + 1, '"'); - else if(*str == '\'') - { - int codepoint; - t += utf8_val(&codepoint, t + 1) + 1; - if(*t != '\'') return false; - } - else if(*t == '(') - { - par++; - } - else if(*t == ')') - { - par--; - if(par < 0) return false; - } - - str = t + 1; - if(!*str || !par) return par == 0 ? true : false; - } +// eat 1 char or quote/par +inline bool skip_par(char*& str) { + int par = 0; + if (*str != '\'' && *str != '"' && *str != '(' && *str != ')') { + str++; + return true; + } + while (true) { + char* t = str; + if (*str == '"') + t = strchr(t + 1, '"'); + else if (*str == '\'') { + int codepoint; + t += utf8_val(&codepoint, t + 1) + 1; + if (*t != '\'') return false; + } else if (*t == '(') { + par++; + } else if (*t == ')') { + par--; + if (par < 0) return false; + } + + str = t + 1; + if (!*str || !par) return par == 0 ? true : false; + } } -//instr should not be duplicate chars. Instr should also not be 1 char -string& string::qreplace(const char * instr, const char * outstr) -{ - string& thisstring =*this; - if (!strstr(thisstring, instr)) return thisstring; - int inlen = strlen(instr); - string out; - for (int i=0;thisstring[i];) - { - if (!strncmp((const char*)thisstring +i, instr, inlen)) - { - out+=outstr; - i+=inlen; - } - // randomdude999: prevent appending the null terminator to the output - else if(!isq(thisstring[i])) out+= thisstring[i++]; - else - { - char *start = raw() + i; - char *end = start; - if(!skip_quote(end)) return thisstring; - out.append(raw(), i, end - start + i + 1); - i += end - start + 1; - - } - } - thisstring =out; - return thisstring; +// instr should not be duplicate chars. Instr should also not be 1 char +string& string::qreplace(const char* instr, const char* outstr) { + string& thisstring = *this; + if (!strstr(thisstring, instr)) return thisstring; + int inlen = strlen(instr); + string out; + for (int i = 0; thisstring[i];) { + if (!strncmp((const char*)thisstring + i, instr, inlen)) { + out += outstr; + i += inlen; + } + // randomdude999: prevent appending the null terminator to the output + else if (!isq(thisstring[i])) + out += thisstring[i++]; + else { + char* start = raw() + i; + char* end = start; + if (!skip_quote(end)) return thisstring; + out.append(raw(), i, end - start + i + 1); + i += end - start + 1; + } + } + thisstring = out; + return thisstring; } -string& string::qnormalize() -{ - string& thisstring =*this; - string out; - char *startstr = thisstring.raw(); - char *str = startstr; - while(str = strpbrk(str, "'\" \t,\r")) - { - if(is_space(*str)) - { - if(str[0] == ' ' && !is_space(str[1])) - { - str++; - continue; - } - out.append(startstr, 0, str - startstr); - out += ' '; - while(is_space(*str)) str++; - startstr = str; - }else if(*str == ',') - { - str++; - if(is_space(*str)) - { - out.append(startstr, 0, str - startstr); - while(is_space(*str)) str++; - startstr = str; - } - } - else - { - str = strchr(str + 1, *str); //confirm quotes has already been run, so this should be okay - if(!str) return thisstring; - str++; - } - } - if(startstr != thisstring.raw()) - { - out.append(startstr, 0, strlen(startstr)); //the remaining - - thisstring = out; - } - return thisstring; +string& string::qnormalize() { + string& thisstring = *this; + string out; + char* startstr = thisstring.raw(); + char* str = startstr; + while (str = strpbrk(str, "'\" \t,\r")) { + if (is_space(*str)) { + if (str[0] == ' ' && !is_space(str[1])) { + str++; + continue; + } + out.append(startstr, 0, str - startstr); + out += ' '; + while (is_space(*str)) str++; + startstr = str; + } else if (*str == ',') { + str++; + if (is_space(*str)) { + out.append(startstr, 0, str - startstr); + while (is_space(*str)) str++; + startstr = str; + } + } else { + str = strchr(str + 1, *str); // confirm quotes has already been run, so + // this should be okay + if (!str) return thisstring; + str++; + } + } + if (startstr != thisstring.raw()) { + out.append(startstr, 0, strlen(startstr)); // the remaining + + thisstring = out; + } + return thisstring; } -bool confirmquotes(const char * str) -{ - while(*str) - { - char *dquote = strchr((char *)str, '"'); - char *squote = strchr((char *)str, '\''); - if(dquote || squote) - { - if(dquote && (dquote < squote || !squote)) - { - dquote = strchr(dquote+1, '"'); - if(dquote) str = dquote+1; - else return false; - } - else - { - int codepoint; - squote += utf8_val(&codepoint, squote + 1) + 1; - if(*squote == '\'') str = squote+1; - else return false; - } - } - else - { - return true; - } - } - return true; +bool confirmquotes(const char* str) { + while (*str) { + char* dquote = strchr((char*)str, '"'); + char* squote = strchr((char*)str, '\''); + if (dquote || squote) { + if (dquote && (dquote < squote || !squote)) { + dquote = strchr(dquote + 1, '"'); + if (dquote) + str = dquote + 1; + else + return false; + } else { + int codepoint; + squote += utf8_val(&codepoint, squote + 1) + 1; + if (*squote == '\'') + str = squote + 1; + else + return false; + } + } else { + return true; + } + } + return true; } -bool confirmqpar(const char * str) -{ - //todo fully optimize - int par = 0; - while((unsigned char)*str >= 128 || !qparlut[*str]) str++; - while(*str) - { - if(*str == '"') - { - str = strchr(str + 1, '"'); - if(!str++) return false; - } - else if(*str == '\'') - { - int codepoint; - str += utf8_val(&codepoint, str + 1) + 1; - if(*str == '\'') str++; - else return false; - } - else - { - par += 1 - ((*str++ - '(') << 1); - if(par < 0) return false; - } - while((unsigned char)*str >= 128 || !qparlut[*str]) str++; - } - return !par; +bool confirmqpar(const char* str) { + // todo fully optimize + int par = 0; + while ((unsigned char)*str >= 128 || !qparlut[*str]) str++; + while (*str) { + if (*str == '"') { + str = strchr(str + 1, '"'); + if (!str++) return false; + } else if (*str == '\'') { + int codepoint; + str += utf8_val(&codepoint, str + 1) + 1; + if (*str == '\'') + str++; + else + return false; + } else { + par += 1 - ((*str++ - '(') << 1); + if (par < 0) return false; + } + while ((unsigned char)*str >= 128 || !qparlut[*str]) str++; + } + return !par; } -char ** split(char * str, char key, int * len) -{ - char *thisentry=strchr(str, key); - if (!thisentry) - { - char ** out= typed_malloc(char*, 2); - out[0]=str; - out[1]=nullptr; - if (len) *len=1; - return out; - } - int count=15; //makes the default alloc 8 elements, sounds fair. - char ** outdata= typed_malloc(char*, (size_t)count+1); - - int newcount=0; - outdata[newcount++]=str; - do{ - *thisentry = 0; - thisentry++; - outdata[newcount++]=thisentry; - if(newcount >= count) - { - count *= 2; - outdata = typed_realloc(char *, outdata, count); - } - }while((thisentry = strchr(thisentry, key))); - - outdata[newcount]= nullptr; - if (len) *len=newcount; - return outdata; +char** split(char* str, char key, int* len) { + char* thisentry = strchr(str, key); + if (!thisentry) { + char** out = typed_malloc(char*, 2); + out[0] = str; + out[1] = nullptr; + if (len) *len = 1; + return out; + } + int count = 15; // makes the default alloc 8 elements, sounds fair. + char** outdata = typed_malloc(char*, (size_t)count + 1); + + int newcount = 0; + outdata[newcount++] = str; + do { + *thisentry = 0; + thisentry++; + outdata[newcount++] = thisentry; + if (newcount >= count) { + count *= 2; + outdata = typed_realloc(char*, outdata, count); + } + } while ((thisentry = strchr(thisentry, key))); + + outdata[newcount] = nullptr; + if (len) *len = newcount; + return outdata; } -char ** qsplit(char * str, char key, int * len) -{ - if (!strchr(str, '"') && !strchr(str, '\'')) return split(str, key, len); - - int count=15; - char ** outdata= typed_malloc(char*, (size_t)count+1); - int newcount=0; - char * thisentry=str; - outdata[newcount++]=thisentry; - while (*thisentry) /*todo fix*/ - { - if (*thisentry == key) - { - *thisentry=0; - thisentry++; - outdata[newcount++]=thisentry; - if(newcount >= count) - { - count *= 2; - outdata = typed_realloc(char *, outdata, count); - } - } - else if(skip_quote(thisentry)) thisentry++; - else return nullptr; - } - outdata[newcount]= nullptr; - if (len) *len=newcount; - return outdata; +char** qsplit(char* str, char key, int* len) { + if (!strchr(str, '"') && !strchr(str, '\'')) return split(str, key, len); + + int count = 15; + char** outdata = typed_malloc(char*, (size_t)count + 1); + int newcount = 0; + char* thisentry = str; + outdata[newcount++] = thisentry; + while (*thisentry) /*todo fix*/ + { + if (*thisentry == key) { + *thisentry = 0; + thisentry++; + outdata[newcount++] = thisentry; + if (newcount >= count) { + count *= 2; + outdata = typed_realloc(char*, outdata, count); + } + } else if (skip_quote(thisentry)) + thisentry++; + else + return nullptr; + } + outdata[newcount] = nullptr; + if (len) *len = newcount; + return outdata; } -char ** qsplitstr(char * str, const char * key, int * len) -{ - //check if the str is found first - if (!strstr(str, key)) - { - char ** out= typed_malloc(char*, 2); - out[0]=str; - out[1]=nullptr; - if (len) *len=1; - return out; - } - - int keylen=(int)strlen(key); - int count=15; - char ** outdata= typed_malloc(char*, (size_t)count+1); - int newcount=0; - char * thisentry=str; - outdata[newcount++]=thisentry; - while (*thisentry) /*todo fix*/ - { - if (!strncmp(thisentry, key, (size_t)keylen)) - { - *thisentry=0; - thisentry+=keylen; - outdata[newcount++]=thisentry; - if(newcount >= count) - { - count *= 2; - outdata = typed_realloc(char *, outdata, count); - } - } - else if(skip_quote(thisentry)) thisentry++; - else return nullptr; - } - outdata[newcount]= nullptr; - if (len) *len=newcount; - return outdata; +char** qsplitstr(char* str, const char* key, int* len) { + // check if the str is found first + if (!strstr(str, key)) { + char** out = typed_malloc(char*, 2); + out[0] = str; + out[1] = nullptr; + if (len) *len = 1; + return out; + } + + int keylen = (int)strlen(key); + int count = 15; + char** outdata = typed_malloc(char*, (size_t)count + 1); + int newcount = 0; + char* thisentry = str; + outdata[newcount++] = thisentry; + while (*thisentry) /*todo fix*/ + { + if (!strncmp(thisentry, key, (size_t)keylen)) { + *thisentry = 0; + thisentry += keylen; + outdata[newcount++] = thisentry; + if (newcount >= count) { + count *= 2; + outdata = typed_realloc(char*, outdata, count); + } + } else if (skip_quote(thisentry)) + thisentry++; + else + return nullptr; + } + outdata[newcount] = nullptr; + if (len) *len = newcount; + return outdata; } -//this function is most commonly called in cases where additional chars are very likely -char ** qpsplit(char * str, char key, int * len) -{ - if (!strchr(str, '(') && !strchr(str, ')')) return qsplit(str, key, len); - int count=7; - char ** outdata= typed_malloc(char*, (size_t)count+1); - - int newcount=0; - char * thisentry=str; - outdata[newcount++]=thisentry; - while (*thisentry) - { - //skippar(*thisentry, thisentry++, return nullptr;) - if (*thisentry == key) - { - *thisentry=0; - thisentry++; - outdata[newcount++]=thisentry; - if(newcount >= count) - { - count *= 2; - outdata = typed_realloc(char *, outdata, count); - } - } - else if(!skip_par(thisentry)) return nullptr; - } - outdata[newcount]= nullptr; - if (len) *len=newcount; - return outdata; +// this function is most commonly called in cases where additional chars are very likely +char** qpsplit(char* str, char key, int* len) { + if (!strchr(str, '(') && !strchr(str, ')')) return qsplit(str, key, len); + int count = 7; + char** outdata = typed_malloc(char*, (size_t)count + 1); + + int newcount = 0; + char* thisentry = str; + outdata[newcount++] = thisentry; + while (*thisentry) { + // skippar(*thisentry, thisentry++, return nullptr;) + if (*thisentry == key) { + *thisentry = 0; + thisentry++; + outdata[newcount++] = thisentry; + if (newcount >= count) { + count *= 2; + outdata = typed_realloc(char*, outdata, count); + } + } else if (!skip_par(thisentry)) + return nullptr; + } + outdata[newcount] = nullptr; + if (len) *len = newcount; + return outdata; } -string &itrim(string &input, const char * left, const char * right) -{ - bool nukeright=true; - int totallen=input.length(); - int rightlen=(int)strlen(right); - if (rightlen && rightlen<=totallen) - { - const char * rightend=right+rightlen; - const char * strend=input.data()+totallen; - while (right!=rightend) - { - rightend--; - strend--; - if (to_lower(*strend)!=to_lower(*rightend)) nukeright=false; - } - if (nukeright) - { - totallen-=rightlen; - input.truncate(totallen); - } - } - bool nukeleft=true; - int leftlen = strlen(left); - if(leftlen == 1 && input.data()[0] == left[0]) - { - return input = string(input.data()+1, (input.length()-1)); - } - else - { - for (int i = 0; i < leftlen; i++) - { - if (to_lower(input.data()[i])!=to_lower(left[i])) nukeleft=false; - } - if (nukeleft) input = string(input.data()+leftlen, (input.length()-leftlen)); - } - return input; +string& itrim(string& input, const char* left, const char* right) { + bool nukeright = true; + int totallen = input.length(); + int rightlen = (int)strlen(right); + if (rightlen && rightlen <= totallen) { + const char* rightend = right + rightlen; + const char* strend = input.data() + totallen; + while (right != rightend) { + rightend--; + strend--; + if (to_lower(*strend) != to_lower(*rightend)) nukeright = false; + } + if (nukeright) { + totallen -= rightlen; + input.truncate(totallen); + } + } + bool nukeleft = true; + int leftlen = strlen(left); + if (leftlen == 1 && input.data()[0] == left[0]) { + return input = string(input.data() + 1, (input.length() - 1)); + } else { + for (int i = 0; i < leftlen; i++) { + if (to_lower(input.data()[i]) != to_lower(left[i])) nukeleft = false; + } + if (nukeleft) + input = string(input.data() + leftlen, (input.length() - leftlen)); + } + return input; } -char* strqpchr(char* str, char key) -{ - while (*str) - { - if (*str == key) return const_cast(str); - else if(!skip_par(str)) return nullptr; - } - return nullptr; +char* strqpchr(char* str, char key) { + while (*str) { + if (*str == key) + return const_cast(str); + else if (!skip_par(str)) + return nullptr; + } + return nullptr; } extern const uint8_t char_props[256] = { - //x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 xA xB xC xD xE xF - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x80,0x00,0x00,0x80,0x00,0x00, // 0x - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // 1x - 0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // 2x !"#$%&'()*+,-./ - 0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x00,0x00,0x00,0x00,0x00,0x00, // 3x 0123456789:;<=>? - 0x00,0x23,0x23,0x23,0x23,0x23,0x23,0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22, // 4x @ABCDEFGHIJKLMNO - 0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x00,0x00,0x00,0x00,0x08, // 5x PQRSTUVWXYZ[\]^_ - 0x00,0x25,0x25,0x25,0x25,0x25,0x25,0x24,0x24,0x24,0x24,0x24,0x24,0x24,0x24,0x24, // 6x `abcdefghijklmno - 0x24,0x24,0x24,0x24,0x24,0x24,0x24,0x24,0x24,0x24,0x24,0x00,0x00,0x00,0x00,0x00, // 7x pqrstuvwxyz{|}~ - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // 8x - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // 9x - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // Ax - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // Bx - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // Cx - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // Dx - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // Ex - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // Fx + // x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 xA xB xC xD xE xF + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0x00, 0x00, // 0x + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 1x + 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 2x !"#$%&'()*+,-./ + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 3x 0123456789:;<=>? + 0x00, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x22, + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, // 4x @ABCDEFGHIJKLMNO + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, + 0x22, 0x22, 0x22, 0x00, 0x00, 0x00, 0x00, 0x08, // 5x PQRSTUVWXYZ[\]^_ + 0x00, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x24, + 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, // 6x `abcdefghijklmno + 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, + 0x24, 0x24, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, // 7x pqrstuvwxyz{|}~ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 8x + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 9x + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Ax + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Bx + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Cx + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Dx + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Ex + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Fx }; diff --git a/src/asar/libstr.h b/src/asar/libstr.h index 5fc5add1..b7d13288 100644 --- a/src/asar/libstr.h +++ b/src/asar/libstr.h @@ -1,14 +1,17 @@ #pragma once -#include "std-includes.h" -#include "libmisc.h" #include -//ty alcaro + +#include "libmisc.h" +#include "std-includes.h" +// ty alcaro extern const unsigned char char_props[256]; -static inline int to_lower(unsigned char c) { return c|(char_props[c]&0x20); } -static inline int to_upper(unsigned char c) { return c&~(char_props[c]&0x20); } +static inline int to_lower(unsigned char c) { return c | (char_props[c] & 0x20); } +static inline int to_upper(unsigned char c) { return c & ~(char_props[c] & 0x20); } -inline bool is_space(unsigned char c) { return char_props[c] & 0x80; } // C standard says \f \v are space, but this one disagrees +inline bool is_space(unsigned char c) { + return char_props[c] & 0x80; +} // C standard says \f \v are space, but this one disagrees inline bool is_digit(unsigned char c) { return char_props[c] & 0x40; } inline bool is_alpha(unsigned char c) { return char_props[c] & 0x20; } inline bool is_lower(unsigned char c) { return char_props[c] & 0x04; } @@ -18,553 +21,471 @@ inline bool is_ualpha(unsigned char c) { return char_props[c] & 0x28; } inline bool is_ualnum(unsigned char c) { return char_props[c] & 0x68; } inline bool is_xdigit(unsigned char c) { return char_props[c] & 0x01; } -inline char *copy(const char *source, int copy_length, char *dest) -{ - memcpy(dest, source, copy_length*sizeof(char)); - return dest; +inline char* copy(const char* source, int copy_length, char* dest) { + memcpy(dest, source, copy_length * sizeof(char)); + return dest; } class string { public: -const char *data() const -{ - return cached_data; -} - -char *temp_raw() const //things to cleanup and take a look at -{ - return cached_data; -} - -char *raw() const -{ - return cached_data; -} - -int length() const -{ - return is_inlined() ? inlined.len : allocated.len; -} - -void set_length(int length) -{ - if(length > max_inline_length_){ - inlined.len = (unsigned char)-1; - allocated.len = length; - }else{ - inlined.len = length; - } -} - -void truncate(int newlen) -{ - resize(newlen); -} - -void assign(const char * newstr) -{ - if (!newstr) newstr = ""; - assign(newstr, strlen(newstr)); -} - -void assign(const string &newstr) -{ - assign(newstr, newstr.length()); -} - -void assign(const char * newstr, int end) -{ - resize(end); - copy(newstr, length(), cached_data); -} - - -string& operator=(const char * newstr) -{ - assign(newstr); - return *this; -} - -string& operator=(const string &newstr) -{ - assign(newstr); - return *this; -} - -string& append(const string& other, int start, int end) -{ - int current_end = length(); - resize(length() + end - start); - copy(other.cached_data + start, end - start, cached_data + current_end); - return *this; -} - -string& append(const char *other, int start, int end) -{ - int current_end = length(); - resize(length() + end - start); - copy(other + start, end - start, cached_data + current_end); - return *this; -} - -string& operator+=(const string& other) -{ - int current_end = length(); - resize(length() + other.length()); - copy(other.cached_data, other.length(), cached_data + current_end); - return *this; -} - -string& operator+=(const char *other) -{ - int current_end = length(); - int otherlen=(int)strlen(other); - resize(length() + otherlen); - copy(other, otherlen, cached_data + current_end); - return *this; -} - -string& operator+=(char c) -{ - resize(length() + 1); - cached_data[length() - 1] = c; - return *this; -} - -string operator+(char right) const -{ - string ret=*this; - ret+=right; - return ret; -} - -string operator+(const char * right) const -{ - string ret=*this; - ret+=right; - return ret; -} - -bool operator==(const char * right) const -{ - return !strcmp(data(), right); -} - -bool operator==(const string& right) const -{ - return !strcmp(data(), right.data()); -} - -bool operator!=(const char * right) const -{ - return (strcmp(data(), right) != 0); -} - -bool operator!=(const string& right) const -{ - return (strcmp(data(), right.data()) != 0); -} - -operator const char*() const -{ - return data(); -} - -explicit operator bool() const -{ - return length(); -} - -string() -{ - //todo reduce I know this isn't all needed - allocated.bufferlen = 0; - allocated.str = 0; - allocated.len = 0; - inlined.len = 0; - cached_data = inlined.str; - next_resize = max_inline_length_+1; - -} -string(const char * newstr) : string() -{ - assign(newstr); -} -string(const char * newstr, int newlen) : string() -{ - assign(newstr, newlen); -} -string(const string& old) : string() -{ - assign(old.data()); -} - -string(string &&move) : string() -{ - *this = move; -} - -string& operator=(string&& move) -{ - if(!is_inlined()) free(allocated.str); - if(!move.is_inlined()){ - allocated.str = move.allocated.str; - allocated.bufferlen = move.allocated.bufferlen; - set_length(move.allocated.len); - - move.inlined.len = 0; - move.inlined.str[0] = 0; - cached_data = allocated.str; - next_resize = move.next_resize; - - }else{ - inlined.len = 0; - cached_data = inlined.str; - next_resize = max_inline_length_+1; - assign(move); - } - return *this; -} - -~string() -{ - if(!is_inlined()){ - free(allocated.str); - } -} - -//maybe these should return refs to chain. but also good not to encourage chaining -void strip_prefix(char c) -{ - if(cached_data[0] == c){ - *this = string(cached_data + 1, length() - 1); - } -} - -void strip_suffix(char c) -{ - if(cached_data[length() - 1] == c){ - truncate(length() - 1); - } -} - -string& qreplace(const char * instr, const char * outstr); -string& qnormalize(); - -// RPG Hacker: My hack shmeck to get around no longer supporting text mode. -// Symbol files are currently the only thing that use text mode, anyways, and I don't even know -// if the emulators that read them care about line endings. -string& convert_line_endings_to_native() -{ + const char* data() const { return cached_data; } + + char* temp_raw() const // things to cleanup and take a look at + { + return cached_data; + } + + char* raw() const { return cached_data; } + + int length() const { return is_inlined() ? inlined.len : allocated.len; } + + void set_length(int length) { + if (length > max_inline_length_) { + inlined.len = (unsigned char)-1; + allocated.len = length; + } else { + inlined.len = length; + } + } + + void truncate(int newlen) { resize(newlen); } + + void assign(const char* newstr) { + if (!newstr) newstr = ""; + assign(newstr, strlen(newstr)); + } + + void assign(const string& newstr) { assign(newstr, newstr.length()); } + + void assign(const char* newstr, int end) { + resize(end); + copy(newstr, length(), cached_data); + } + + + string& operator=(const char* newstr) { + assign(newstr); + return *this; + } + + string& operator=(const string& newstr) { + assign(newstr); + return *this; + } + + string& append(const string& other, int start, int end) { + int current_end = length(); + resize(length() + end - start); + copy(other.cached_data + start, end - start, cached_data + current_end); + return *this; + } + + string& append(const char* other, int start, int end) { + int current_end = length(); + resize(length() + end - start); + copy(other + start, end - start, cached_data + current_end); + return *this; + } + + string& operator+=(const string& other) { + int current_end = length(); + resize(length() + other.length()); + copy(other.cached_data, other.length(), cached_data + current_end); + return *this; + } + + string& operator+=(const char* other) { + int current_end = length(); + int otherlen = (int)strlen(other); + resize(length() + otherlen); + copy(other, otherlen, cached_data + current_end); + return *this; + } + + string& operator+=(char c) { + resize(length() + 1); + cached_data[length() - 1] = c; + return *this; + } + + string operator+(char right) const { + string ret = *this; + ret += right; + return ret; + } + + string operator+(const char* right) const { + string ret = *this; + ret += right; + return ret; + } + + bool operator==(const char* right) const { return !strcmp(data(), right); } + + bool operator==(const string& right) const { return !strcmp(data(), right.data()); } + + bool operator!=(const char* right) const { return (strcmp(data(), right) != 0); } + + bool operator!=(const string& right) const { + return (strcmp(data(), right.data()) != 0); + } + + operator const char*() const { return data(); } + + explicit operator bool() const { return length(); } + + string() { + // todo reduce I know this isn't all needed + allocated.bufferlen = 0; + allocated.str = 0; + allocated.len = 0; + inlined.len = 0; + cached_data = inlined.str; + next_resize = max_inline_length_ + 1; + } + string(const char* newstr) : string() { assign(newstr); } + string(const char* newstr, int newlen) : string() { assign(newstr, newlen); } + string(const string& old) : string() { assign(old.data()); } + + string(string&& move) : string() { *this = move; } + + string& operator=(string&& move) { + if (!is_inlined()) free(allocated.str); + if (!move.is_inlined()) { + allocated.str = move.allocated.str; + allocated.bufferlen = move.allocated.bufferlen; + set_length(move.allocated.len); + + move.inlined.len = 0; + move.inlined.str[0] = 0; + cached_data = allocated.str; + next_resize = move.next_resize; + + } else { + inlined.len = 0; + cached_data = inlined.str; + next_resize = max_inline_length_ + 1; + assign(move); + } + return *this; + } + + ~string() { + if (!is_inlined()) { + free(allocated.str); + } + } + + // maybe these should return refs to chain. but also good not to encourage chaining + void strip_prefix(char c) { + if (cached_data[0] == c) { + *this = string(cached_data + 1, length() - 1); + } + } + + void strip_suffix(char c) { + if (cached_data[length() - 1] == c) { + truncate(length() - 1); + } + } + + string& qreplace(const char* instr, const char* outstr); + string& qnormalize(); + + // RPG Hacker: My hack shmeck to get around no longer supporting text mode. + // Symbol files are currently the only thing that use text mode, anyways, and I + // don't even know if the emulators that read them care about line endings. + string& convert_line_endings_to_native() { #if defined(windows) - // RPG Hacker: This is quite stinky, but doing the replacement directly will lead to a dead-lock. - // \x08 = backspace should never appear inside a string, so I'm abusing it here. - return qreplace("\n", "\x08").qreplace("\x08", "\r\n"); + // RPG Hacker: This is quite stinky, but doing the replacement directly will + // lead to a dead-lock. \x08 = backspace should never appear inside a string, so + // I'm abusing it here. + return qreplace("\n", "\x08").qreplace("\x08", "\r\n"); #else - return *this; + return *this; #endif -} + } #ifdef SERIALIZER -void serialize(serializer & s) -{ - s(str, allocated.bufferlen); - set_length(strlen(str)); -} + void serialize(serializer& s) { + s(str, allocated.bufferlen); + set_length(strlen(str)); + } #endif #define SERIALIZER_BANNED private: -static const int scale_factor = 4; //scale sso -static const int max_inline_length_ = ((sizeof(char *) + sizeof(int) * 2) * scale_factor) - 2; -char *cached_data; -int next_resize; -struct si{ - char str[max_inline_length_ + 1]; - unsigned char len; -}; - -struct sa{ - char *str; - int len; - int bufferlen ; -}; -union{ - si inlined; - sa allocated; -}; - - -void resize(int new_length) -{ - const char *old_data = data(); - if(new_length >= next_resize || (!is_inlined() && new_length <= max_inline_length_)) { - if(new_length > max_inline_length_ && (is_inlined() || allocated.bufferlen <= new_length)){ //SSO or big to big - int new_size = bitround(new_length + 1); - if(old_data == inlined.str){ - allocated.str = copy(old_data, min(length(), new_length), (char *)malloc(new_size)); - }else{ - allocated.str = (char *)realloc(allocated.str, new_size); - old_data = inlined.str; //this will prevent freeing a dead realloc ptr - } - allocated.bufferlen = new_size; - cached_data = allocated.str; - next_resize = allocated.bufferlen; - }else if(length() > max_inline_length_ && new_length <= max_inline_length_){ //big to SSO - copy(old_data, new_length, inlined.str); - cached_data = inlined.str; - next_resize = max_inline_length_+1; - } - if(old_data != inlined.str && old_data != data()){ - free((char *)old_data); - } - } - set_length(new_length); - - raw()[new_length] = 0; //always ensure null terminator -} - -bool is_inlined() const -{ - return inlined.len != (unsigned char)-1; -} + static const int scale_factor = 4; // scale sso + static const int max_inline_length_ = + ((sizeof(char*) + sizeof(int) * 2) * scale_factor) - 2; + char* cached_data; + int next_resize; + struct si { + char str[max_inline_length_ + 1]; + unsigned char len; + }; + + struct sa { + char* str; + int len; + int bufferlen; + }; + union { + si inlined; + sa allocated; + }; + + + void resize(int new_length) { + const char* old_data = data(); + if (new_length >= next_resize || + (!is_inlined() && new_length <= max_inline_length_)) { + if (new_length > max_inline_length_ && + (is_inlined() || + allocated.bufferlen <= new_length)) { // SSO or big to big + int new_size = bitround(new_length + 1); + if (old_data == inlined.str) { + allocated.str = copy(old_data, min(length(), new_length), + (char*)malloc(new_size)); + } else { + allocated.str = (char*)realloc(allocated.str, new_size); + old_data = inlined.str; // this will prevent freeing a dead realloc + // ptr + } + allocated.bufferlen = new_size; + cached_data = allocated.str; + next_resize = allocated.bufferlen; + } else if (length() > max_inline_length_ && + new_length <= max_inline_length_) { // big to SSO + copy(old_data, new_length, inlined.str); + cached_data = inlined.str; + next_resize = max_inline_length_ + 1; + } + if (old_data != inlined.str && old_data != data()) { + free((char*)old_data); + } + } + set_length(new_length); + + raw()[new_length] = 0; // always ensure null terminator + } + + bool is_inlined() const { return inlined.len != (unsigned char)-1; } }; #define STR (string) -char * readfile(const char * fname, const char * basepath); -char * readfilenative(const char * fname); -bool readfile(const char * fname, const char * basepath, char ** data, int * len);//if you want an uchar*, cast it -char ** split(char * str, char key, int * len= nullptr); -char ** qsplit(char * str, char key, int * len= nullptr); -char ** qpsplit(char * str, char key, int * len= nullptr); -char ** qsplitstr(char * str, const char * key, int * len= nullptr); -bool confirmquotes(const char * str); -bool confirmqpar(const char * str); +char* readfile(const char* fname, const char* basepath); +char* readfilenative(const char* fname); +bool readfile(const char* fname, const char* basepath, char** data, + int* len); // if you want an uchar*, cast it +char** split(char* str, char key, int* len = nullptr); +char** qsplit(char* str, char key, int* len = nullptr); +char** qpsplit(char* str, char key, int* len = nullptr); +char** qsplitstr(char* str, const char* key, int* len = nullptr); +bool confirmquotes(const char* str); +bool confirmqpar(const char* str); char* strqpchr(char* str, char key); -inline string hex(unsigned int value) -{ - char buffer[64]; - if(0); - else if (value<=0x000000FF) sprintf(buffer, "%.2X", value); - else if (value<=0x0000FFFF) sprintf(buffer, "%.4X", value); - else if (value<=0x00FFFFFF) sprintf(buffer, "%.6X", value); - else sprintf(buffer, "%.8X", value); - return buffer; -} - -inline string hex(unsigned int value, int width) -{ - char buffer[64]; - sprintf(buffer, "%.*X", width, value); - return buffer; -} - -inline string dec(int value) -{ - char buffer[64]; - sprintf(buffer, "%i", value); - return buffer; -} - -inline string ftostr(double value) -{ - // randomdude999: With 100 digits of precision, the buffer needs to be approx. 311+100, - // but let's be safe here https://stackoverflow.com/questions/7235456 - char rval[512]; - // RPG Hacker: Ridiculously high precision, I know, but we're working with doubles - // here and can afford it, so no need to waste any precision - sprintf(rval, "%.100f", value); - if (strchr(rval, '.'))//nuke useless zeroes - { - char * end=strrchr(rval, '\0')-1; - while (*end=='0') - { - *end='\0'; - end--; - } - if (*end=='.') *end='\0'; - } - return rval; +inline string hex(unsigned int value) { + char buffer[64]; + if (0) + ; + else if (value <= 0x000000FF) + sprintf(buffer, "%.2X", value); + else if (value <= 0x0000FFFF) + sprintf(buffer, "%.4X", value); + else if (value <= 0x00FFFFFF) + sprintf(buffer, "%.6X", value); + else + sprintf(buffer, "%.8X", value); + return buffer; +} + +inline string hex(unsigned int value, int width) { + char buffer[64]; + sprintf(buffer, "%.*X", width, value); + return buffer; +} + +inline string dec(int value) { + char buffer[64]; + sprintf(buffer, "%i", value); + return buffer; +} + +inline string ftostr(double value) { + // randomdude999: With 100 digits of precision, the buffer needs to be approx. + // 311+100, but let's be safe here https://stackoverflow.com/questions/7235456 + char rval[512]; + // RPG Hacker: Ridiculously high precision, I know, but we're working with doubles + // here and can afford it, so no need to waste any precision + sprintf(rval, "%.100f", value); + if (strchr(rval, '.')) // nuke useless zeroes + { + char* end = strrchr(rval, '\0') - 1; + while (*end == '0') { + *end = '\0'; + end--; + } + if (*end == '.') *end = '\0'; + } + return rval; } // Same as above, but with variable precision -inline string ftostrvar(double value, int precision) -{ - int clampedprecision = precision; - if (clampedprecision < 0) clampedprecision = 0; - if (clampedprecision > 100) clampedprecision = 100; - - // see above - char rval[512]; - sprintf(rval, "%.*f", clampedprecision, (double)value); - if (strchr(rval, '.'))//nuke useless zeroes - { - char * end = strrchr(rval, '\0') - 1; - while (*end == '0') - { - *end = '\0'; - end--; - } - if (*end == '.') *end = '\0'; - } - return rval; -} - -inline bool stribegin(const char * str, const char * key) -{ - for (int i=0;key[i];i++) - { - if (to_lower(str[i])!=to_lower(key[i])) return false; - } - return true; -} - -inline bool striend(const char * str, const char * key) -{ - const char * keyend=strrchr(key, '\0'); - const char * strend=strrchr(str, '\0'); - if(keyend-key > strend-str) return false; - - while (key!=keyend) - { - keyend--; - strend--; - if (to_lower(*strend)!=to_lower(*keyend)) return false; - } - return true; +inline string ftostrvar(double value, int precision) { + int clampedprecision = precision; + if (clampedprecision < 0) clampedprecision = 0; + if (clampedprecision > 100) clampedprecision = 100; + + // see above + char rval[512]; + sprintf(rval, "%.*f", clampedprecision, (double)value); + if (strchr(rval, '.')) // nuke useless zeroes + { + char* end = strrchr(rval, '\0') - 1; + while (*end == '0') { + *end = '\0'; + end--; + } + if (*end == '.') *end = '\0'; + } + return rval; +} + +inline bool stribegin(const char* str, const char* key) { + for (int i = 0; key[i]; i++) { + if (to_lower(str[i]) != to_lower(key[i])) return false; + } + return true; +} + +inline bool striend(const char* str, const char* key) { + const char* keyend = strrchr(key, '\0'); + const char* strend = strrchr(str, '\0'); + if (keyend - key > strend - str) return false; + + while (key != keyend) { + keyend--; + strend--; + if (to_lower(*strend) != to_lower(*keyend)) return false; + } + return true; +} + +inline bool stricmpwithupper(const char* word1, const char* word2) { + while (*word2) { + if (to_upper(*word1++) != *word2++) return true; + } + return *word1; +} + +inline bool stricmpwithlower(const char* word1, const char* word2) { + while (*word2) { + if (to_lower(*word1++) != *word2++) return true; + } + return *word1; +} + +// function: return the string without quotes around it, if any exists +// if they don't exist, return it unaltered +// it is not guaranteed to return str +// it is not guaranteed to not edit str +// the input must be freed even though it's garbage, the output must not +inline const char* dequote(char* str) { + if (*str != '"') return str; + char* end = strrchr(str, '"'); + if (end) { + *end = 0; + char* quote = str + 1; + while ((quote = strstr(quote, "\"\""))) + memmove(quote, quote + 1, strlen(quote)); + return str + 1; + } + return nullptr; +} + +inline char* strqchr(const char* str, char key) { + while (*str != '\0') { + if (*str == key) { + return const_cast(str); + } else if (*str == '"' || *str == '\'') { + // Special case hack for ''', which is currently our official way of + // handling the ' character. Even though it really stinks. + if (str[0] == '\'' && str[1] == '\'' && str[2] == '\'') { + str += 2; + } else { + char delimiter = *str; + + do { + str++; + + // If we want to support backslash escapes, we'll have to add that + // right here. + } while (*str != delimiter && *str != '\0'); + + // This feels like a superfluous check, but I can't really find a clean + // way to avoid it. + if (*str == '\0') { + return nullptr; + } + } + } + + str++; + } + + return nullptr; +} + +inline string substr(const char* str, int len) { return string(str, len); } + +// todo make these members +string& strip_prefix(string& str, char c); +string& strip_suffix(string& str, char c); + + +inline char* strip_whitespace(char* str) { + while (is_space(*str)) str++; + for (int i = strlen(str) - 1; i >= 0; i--) { + if (!is_space(str[i])) { + str[i + 1] = 0; + return str; + } + } + return str; } - -inline bool stricmpwithupper(const char *word1, const char *word2) -{ - while(*word2) - { - if(to_upper(*word1++) != *word2++) return true; - } - return *word1; -} - -inline bool stricmpwithlower(const char *word1, const char *word2) -{ - while(*word2) - { - if(to_lower(*word1++) != *word2++) return true; - } - return *word1; -} - -//function: return the string without quotes around it, if any exists -//if they don't exist, return it unaltered -//it is not guaranteed to return str -//it is not guaranteed to not edit str -//the input must be freed even though it's garbage, the output must not -inline const char * dequote(char * str) -{ - if (*str!='"') return str; - char *end = strrchr(str, '"'); - if (end) - { - *end = 0; - char *quote = str+1; - while((quote = strstr(quote, "\"\""))) memmove(quote, quote+1, strlen(quote)); - return str + 1; - } - return nullptr; -} - -inline char * strqchr(const char * str, char key) -{ - while (*str != '\0') - { - if (*str == key) { return const_cast(str); } - else if (*str == '"' || *str == '\'') - { - // Special case hack for ''', which is currently our official way of handling the ' character. - // Even though it really stinks. - if (str[0] == '\'' && str[1] == '\'' && str[2] == '\'') { str += 2; } - else - { - char delimiter = *str; - - do - { - str++; - - // If we want to support backslash escapes, we'll have to add that right here. - } while (*str != delimiter && *str != '\0'); - - // This feels like a superfluous check, but I can't really find a clean way to avoid it. - if (*str == '\0') { return nullptr; } - } - } - - str++; - } - - return nullptr; +inline void strip_whitespace(string& str) { + str = string(strip_whitespace(str.temp_raw())); } -inline string substr(const char * str, int len) -{ - return string(str, len); -} - -//todo make these members -string &strip_prefix(string &str, char c); -string &strip_suffix(string &str, char c); - - -inline char *strip_whitespace(char *str) -{ - while(is_space(*str)) str++; - for(int i = strlen(str) - 1; i >= 0; i--) - { - if(!is_space(str[i])) - { - str[i + 1] = 0; - return str; - } - } - return str; -} -inline void strip_whitespace(string &str) -{ - str = string(strip_whitespace(str.temp_raw())); -} - -string &itrim(string &str, const char * left, const char * right); - -inline string &lower(string &old) -{ - int length = old.length(); - for (int i=0;i -inline int getconnectedlines(stringarraytype& lines, int startline, string& out) -{ - int count = 0; - - for (int i = startline; lines[i]; i++) - { - // The line should already be stripped of any comments at this point - int linestartpos = (int)strlen(lines[i]); - - if(linestartpos && lines[i][linestartpos - 1] == '\\') - { - count++; - out += string(lines[i], linestartpos - 1); - continue; - } - else - { - out += string(lines[i], linestartpos); - return count; - } - } - - return count; +template +inline int getconnectedlines(stringarraytype& lines, int startline, string& out) { + int count = 0; + + for (int i = startline; lines[i]; i++) { + // The line should already be stripped of any comments at this point + int linestartpos = (int)strlen(lines[i]); + + if (linestartpos && lines[i][linestartpos - 1] == '\\') { + count++; + out += string(lines[i], linestartpos - 1); + continue; + } else { + out += string(lines[i], linestartpos); + return count; + } + } + + return count; } diff --git a/src/asar/macro.cpp b/src/asar/macro.cpp index b2b764ea..42b801fe 100644 --- a/src/asar/macro.cpp +++ b/src/asar/macro.cpp @@ -1,12 +1,13 @@ -#include "asar.h" -#include "assembleblock.h" #include "macro.h" + +#include "asar.h" #include "asar_math.h" +#include "assembleblock.h" #include "warnings.h" assocarr macros; string defining_macro_name; -static macrodata * thisone; +static macrodata* thisone; static int numlines; int calledmacros; @@ -19,394 +20,413 @@ macrodata* current_macro; const char* const* current_macro_args; int current_macro_numargs; -void startmacro(const char * line_) -{ - thisone= nullptr; - if (!confirmqpar(line_)) asar_throw_error(0, error_type_block, error_id_broken_macro_declaration); - string line=line_; - line.qnormalize(); - char * startpar=(char *)strchr(line.data(), '('); - if (!startpar) asar_throw_error(0, error_type_block, error_id_broken_macro_declaration); - *startpar=0; - startpar++; - if (!confirmname(line)) asar_throw_error(0, error_type_block, error_id_invalid_macro_name); - defining_macro_name=line; - char * endpar=startpar+strlen(startpar)-1; - //confirmqpar requires that all parentheses are matched, and a starting one exists, therefore it is harmless to not check for nullptrs - if (*endpar != ')') asar_throw_error(0, error_type_block, error_id_broken_macro_declaration); - *endpar=0; - for (int i=0;startpar[i];i++) - { - char c=startpar[i]; - if (!is_ualnum(c)&& c!=','&& c!='.'&& c!=' ') asar_throw_error(0, error_type_block, error_id_broken_macro_declaration); - if (c==',' && is_digit(startpar[i+1])) asar_throw_error(0, error_type_block, error_id_broken_macro_declaration); - } - if (*startpar==',' || is_digit(*startpar) || strstr(startpar, ",,") || endpar[-1]==',') asar_throw_error(0, error_type_block, error_id_broken_macro_declaration); - if (macros.exists(defining_macro_name)) asar_throw_error(0, error_type_block, error_id_macro_redefined, defining_macro_name.data()); - thisone=(macrodata*)malloc(sizeof(macrodata)); - new(thisone) macrodata; - if (*startpar) - { - char **arguments = split(duplicate_string(startpar), ',', &thisone->numargs); - thisone->arguments_buffer = arguments[0]; - for (int i=0;arguments[i];i++) - { - arguments[i] = strip_whitespace(arguments[i]); - } - thisone->arguments=(const char* const*)arguments; - } - else - { - const char ** noargs=(const char**)malloc(sizeof(const char**)); - *noargs=nullptr; - thisone->arguments=noargs; - thisone->arguments_buffer = nullptr; - thisone->numargs=0; - } - thisone->variadic = false; - thisone->fname= duplicate_string(get_current_file_name()); - thisone->startline=get_current_line(); - thisone->parent_macro=current_macro; - thisone->parent_macro_num_varargs=0; - // RPG Hacker: -1 to take the ... into account, which is also being counted. - if (thisone->parent_macro != nullptr) thisone->parent_macro_num_varargs = current_macro_numargs-(current_macro->numargs-1); - for (int i=0;thisone->arguments[i];i++) - { - if(!strcmp(thisone->arguments[i], "...") && !thisone->arguments[i+1]) thisone->variadic = true; - else if(!strcmp(thisone->arguments[i], "...")) asar_throw_error(0, error_type_block, error_id_vararg_must_be_last); - else if(strchr(thisone->arguments[i], '.')) asar_throw_error(0, error_type_block, error_id_invalid_macro_param_name); - else if (!confirmname(thisone->arguments[i])) asar_throw_error(0, error_type_block, error_id_invalid_macro_param_name); - for (int j=i+1;thisone->arguments[j];j++) - { - if (!strcmp(thisone->arguments[i], thisone->arguments[j])) asar_throw_error(0, error_type_block, error_id_macro_param_redefined, thisone->arguments[i]); - } - } - numlines=0; +void startmacro(const char* line_) { + thisone = nullptr; + if (!confirmqpar(line_)) + asar_throw_error(0, error_type_block, error_id_broken_macro_declaration); + string line = line_; + line.qnormalize(); + char* startpar = (char*)strchr(line.data(), '('); + if (!startpar) + asar_throw_error(0, error_type_block, error_id_broken_macro_declaration); + *startpar = 0; + startpar++; + if (!confirmname(line)) + asar_throw_error(0, error_type_block, error_id_invalid_macro_name); + defining_macro_name = line; + char* endpar = startpar + strlen(startpar) - 1; + // confirmqpar requires that all parentheses are matched, and a starting one exists, + // therefore it is harmless to not check for nullptrs + if (*endpar != ')') + asar_throw_error(0, error_type_block, error_id_broken_macro_declaration); + *endpar = 0; + for (int i = 0; startpar[i]; i++) { + char c = startpar[i]; + if (!is_ualnum(c) && c != ',' && c != '.' && c != ' ') + asar_throw_error(0, error_type_block, error_id_broken_macro_declaration); + if (c == ',' && is_digit(startpar[i + 1])) + asar_throw_error(0, error_type_block, error_id_broken_macro_declaration); + } + if (*startpar == ',' || is_digit(*startpar) || strstr(startpar, ",,") || + endpar[-1] == ',') + asar_throw_error(0, error_type_block, error_id_broken_macro_declaration); + if (macros.exists(defining_macro_name)) + asar_throw_error(0, error_type_block, error_id_macro_redefined, + defining_macro_name.data()); + thisone = (macrodata*)malloc(sizeof(macrodata)); + new (thisone) macrodata; + if (*startpar) { + char** arguments = split(duplicate_string(startpar), ',', &thisone->numargs); + thisone->arguments_buffer = arguments[0]; + for (int i = 0; arguments[i]; i++) { + arguments[i] = strip_whitespace(arguments[i]); + } + thisone->arguments = (const char* const*)arguments; + } else { + const char** noargs = (const char**)malloc(sizeof(const char**)); + *noargs = nullptr; + thisone->arguments = noargs; + thisone->arguments_buffer = nullptr; + thisone->numargs = 0; + } + thisone->variadic = false; + thisone->fname = duplicate_string(get_current_file_name()); + thisone->startline = get_current_line(); + thisone->parent_macro = current_macro; + thisone->parent_macro_num_varargs = 0; + // RPG Hacker: -1 to take the ... into account, which is also being counted. + if (thisone->parent_macro != nullptr) + thisone->parent_macro_num_varargs = + current_macro_numargs - (current_macro->numargs - 1); + for (int i = 0; thisone->arguments[i]; i++) { + if (!strcmp(thisone->arguments[i], "...") && !thisone->arguments[i + 1]) + thisone->variadic = true; + else if (!strcmp(thisone->arguments[i], "...")) + asar_throw_error(0, error_type_block, error_id_vararg_must_be_last); + else if (strchr(thisone->arguments[i], '.')) + asar_throw_error(0, error_type_block, error_id_invalid_macro_param_name); + else if (!confirmname(thisone->arguments[i])) + asar_throw_error(0, error_type_block, error_id_invalid_macro_param_name); + for (int j = i + 1; thisone->arguments[j]; j++) { + if (!strcmp(thisone->arguments[i], thisone->arguments[j])) + asar_throw_error(0, error_type_block, error_id_macro_param_redefined, + thisone->arguments[i]); + } + } + numlines = 0; } -void tomacro(const char * line) -{ - if (!thisone) return; - thisone->lines[numlines++]=line; +void tomacro(const char* line) { + if (!thisone) return; + thisone->lines[numlines++] = line; } -void endmacro(bool insert) -{ - if (!thisone) return; - thisone->numlines=numlines; - if (insert) macros.create(defining_macro_name) = thisone; - else - { - freemacro(thisone); - thisone=nullptr; - } +void endmacro(bool insert) { + if (!thisone) return; + thisone->numlines = numlines; + if (insert) + macros.create(defining_macro_name) = thisone; + else { + freemacro(thisone); + thisone = nullptr; + } } #define cfree(x) free((void*)x) -void freemacro(macrodata* & macro) -{ - macro->lines.~autoarray(); - cfree(macro->fname); - cfree(macro->arguments_buffer); - cfree(macro->arguments); - cfree(macro); +void freemacro(macrodata*& macro) { + macro->lines.~autoarray(); + cfree(macro->fname); + cfree(macro->arguments_buffer); + cfree(macro->arguments); + cfree(macro); } #undef cfree -void callmacro(const char * data) -{ - int prev_numvarargs = numvarargs; - macrodata * thismacro; - if (!confirmqpar(data)) asar_throw_error(0, error_type_block, error_id_broken_macro_usage); - string line=data; - line.qnormalize(); - char * startpar=(char *)strchr(line.data(), '('); - if (!startpar) asar_throw_error(0, error_type_block, error_id_broken_macro_usage); - *startpar=0; - startpar++; - if (!confirmname(line)) asar_throw_error(0, error_type_block, error_id_broken_macro_usage); - if (!macros.exists(line)) asar_throw_error(0, error_type_block, error_id_macro_not_found, line.data()); - thismacro = macros.find(line); - char * endpar=startpar+strlen(startpar)-1; - //confirmqpar requires that all parentheses are matched, and a starting one exists, therefore it is harmless to not check for nullptrs - if (*endpar != ')') asar_throw_error(0, error_type_block, error_id_broken_macro_declaration); - *endpar=0; - autoptr args; - int numargs=0; - if (*startpar) args=(const char* const*)qpsplit(startpar, ',', &numargs); - if (numargs != thismacro->numargs && !thismacro->variadic) asar_throw_error(1, error_type_block, error_id_macro_wrong_num_params); - // RPG Hacker: -1, because the ... is also counted as an argument, yet we want it to be entirely optional. - if (numargs < thismacro->numargs - 1 && thismacro->variadic) asar_throw_error(1, error_type_block, error_id_macro_wrong_min_params); - - macrorecursion++; - inmacro=true; - int old_calledmacros = calledmacros; - calledmacros = reallycalledmacros++; - int startif=numif; - - for (int i = 0; i < numargs; ++i) - { - // RPG Hacker: These casts make me feel very nasty. - (*reinterpret_cast*>(&args))[i] = safedequote(strip_whitespace((char*)args[i])); - } - - // RPG Hacker: -1 to take the ... into account, which is also being counted. - if(thismacro->variadic) numvarargs = numargs-(thismacro->numargs-1); - else numvarargs = -1; - - autoarray* oldmacroposlabels = macroposlabels; - autoarray* oldmacroneglabels = macroneglabels; - autoarray* oldmacrosublabels = macrosublabels; - - autoarray newmacroposlabels; - autoarray newmacroneglabels; - autoarray newmacrosublabels; - - macroposlabels = &newmacroposlabels; - macroneglabels = &newmacroneglabels; - macrosublabels = &newmacrosublabels; - - macrodata* old_macro = current_macro; - const char* const* old_macro_args = current_macro_args; - int old_numargs = current_macro_numargs; - current_macro = thismacro; - current_macro_args = args; - current_macro_numargs = numargs; - - callstack_push cs_push(callstack_entry_type::MACRO_CALL, data); - - { - callstack_push cs_push(callstack_entry_type::FILE, thismacro->fname); - - for (int i=0;inumlines;i++) - { - int prevnumif = numif; - - do_line_logic(thismacro->lines[i], thismacro->fname, thismacro->startline+i+1); - - if (numif != prevnumif && whilestatus[numif].iswhile && whilestatus[numif].cond) - // RPG Hacker: -1 to compensate for the i++, and another -1 - // because ->lines doesn't include the macro header. - i = whilestatus[numif].startline - thismacro->startline - 2; - } - } - - macroposlabels = oldmacroposlabels; - macroneglabels = oldmacroneglabels; - macrosublabels = oldmacrosublabels; - - current_macro = old_macro; - current_macro_args = old_macro_args; - current_macro_numargs = old_numargs; - - macrorecursion--; - inmacro = macrorecursion; - numvarargs = prev_numvarargs; - calledmacros = old_calledmacros; - if (numif!=startif) - { - numif=startif; - numtrue=startif; - asar_throw_error(0, error_type_block, error_id_unclosed_if); - } +void callmacro(const char* data) { + int prev_numvarargs = numvarargs; + macrodata* thismacro; + if (!confirmqpar(data)) + asar_throw_error(0, error_type_block, error_id_broken_macro_usage); + string line = data; + line.qnormalize(); + char* startpar = (char*)strchr(line.data(), '('); + if (!startpar) asar_throw_error(0, error_type_block, error_id_broken_macro_usage); + *startpar = 0; + startpar++; + if (!confirmname(line)) + asar_throw_error(0, error_type_block, error_id_broken_macro_usage); + if (!macros.exists(line)) + asar_throw_error(0, error_type_block, error_id_macro_not_found, line.data()); + thismacro = macros.find(line); + char* endpar = startpar + strlen(startpar) - 1; + // confirmqpar requires that all parentheses are matched, and a starting one exists, + // therefore it is harmless to not check for nullptrs + if (*endpar != ')') + asar_throw_error(0, error_type_block, error_id_broken_macro_declaration); + *endpar = 0; + autoptr args; + int numargs = 0; + if (*startpar) args = (const char* const*)qpsplit(startpar, ',', &numargs); + if (numargs != thismacro->numargs && !thismacro->variadic) + asar_throw_error(1, error_type_block, error_id_macro_wrong_num_params); + // RPG Hacker: -1, because the ... is also counted as an argument, yet we want it to + // be entirely optional. + if (numargs < thismacro->numargs - 1 && thismacro->variadic) + asar_throw_error(1, error_type_block, error_id_macro_wrong_min_params); + + macrorecursion++; + inmacro = true; + int old_calledmacros = calledmacros; + calledmacros = reallycalledmacros++; + int startif = numif; + + for (int i = 0; i < numargs; ++i) { + // RPG Hacker: These casts make me feel very nasty. + (*reinterpret_cast*>(&args))[i] = + safedequote(strip_whitespace((char*)args[i])); + } + + // RPG Hacker: -1 to take the ... into account, which is also being counted. + if (thismacro->variadic) + numvarargs = numargs - (thismacro->numargs - 1); + else + numvarargs = -1; + + autoarray* oldmacroposlabels = macroposlabels; + autoarray* oldmacroneglabels = macroneglabels; + autoarray* oldmacrosublabels = macrosublabels; + + autoarray newmacroposlabels; + autoarray newmacroneglabels; + autoarray newmacrosublabels; + + macroposlabels = &newmacroposlabels; + macroneglabels = &newmacroneglabels; + macrosublabels = &newmacrosublabels; + + macrodata* old_macro = current_macro; + const char* const* old_macro_args = current_macro_args; + int old_numargs = current_macro_numargs; + current_macro = thismacro; + current_macro_args = args; + current_macro_numargs = numargs; + + callstack_push cs_push(callstack_entry_type::MACRO_CALL, data); + + { + callstack_push cs_push(callstack_entry_type::FILE, thismacro->fname); + + for (int i = 0; i < thismacro->numlines; i++) { + int prevnumif = numif; + + do_line_logic(thismacro->lines[i], thismacro->fname, + thismacro->startline + i + 1); + + if (numif != prevnumif && whilestatus[numif].iswhile && + whilestatus[numif].cond) + // RPG Hacker: -1 to compensate for the i++, and another -1 + // because ->lines doesn't include the macro header. + i = whilestatus[numif].startline - thismacro->startline - 2; + } + } + + macroposlabels = oldmacroposlabels; + macroneglabels = oldmacroneglabels; + macrosublabels = oldmacrosublabels; + + current_macro = old_macro; + current_macro_args = old_macro_args; + current_macro_numargs = old_numargs; + + macrorecursion--; + inmacro = macrorecursion; + numvarargs = prev_numvarargs; + calledmacros = old_calledmacros; + if (numif != startif) { + numif = startif; + numtrue = startif; + asar_throw_error(0, error_type_block, error_id_unclosed_if); + } } -string generate_macro_arg_string(const char* named_arg, int depth) -{ - string ret="<"; - for (int i = 0; i < depth;++i) - { - ret += '^'; - } - ret += named_arg; - ret += ">"; - return ret; +string generate_macro_arg_string(const char* named_arg, int depth) { + string ret = "<"; + for (int i = 0; i < depth; ++i) { + ret += '^'; + } + ret += named_arg; + ret += ">"; + return ret; } -string generate_macro_arg_string(int var_arg, int depth) -{ - string ret="<"; - for (int i = 0; i < depth;++i) - { - ret += '^'; - } - ret += dec(var_arg); - ret += ">"; - return ret; +string generate_macro_arg_string(int var_arg, int depth) { + string ret = "<"; + for (int i = 0; i < depth; ++i) { + ret += '^'; + } + ret += dec(var_arg); + ret += ">"; + return ret; } -string generate_macro_hint_string(const char* named_arg, const macrodata* thismacro, int desired_depth, int current_depth=0) -{ - // RPG Hacker: This only work when the incorrectly used parameter - // is inside the macro that is currently being defined. Not great, - // but still better than nothing. - if (current_depth == 0 && thisone != nullptr) - { - for (int j=0;thisone->arguments[j];j++) - { - if (!strcmp(named_arg, thisone->arguments[j])) - { - string ret=" Did you mean: '"; - ret += generate_macro_arg_string(thisone->arguments[j], 0); - ret += "'?"; - return ret; - } - } - } - - // RPG Hacker: Technically, we could skip a level here and go straight - // to the parent, but maybe at some point we'll want to expand this to - // also look for similar args in the current level, so I'll leave it - // like this, just in case. - if (thismacro != nullptr) - { - for (int j=0;thismacro->arguments[j];j++) - { - if (!strcmp(named_arg, thismacro->arguments[j])) - { - string ret=" Did you mean: '"; - ret += generate_macro_arg_string(thismacro->arguments[j], desired_depth+current_depth); - ret += "'?"; - return ret; - } - } - return generate_macro_hint_string(named_arg, thismacro->parent_macro, desired_depth, current_depth+1); - } - - return ""; +string generate_macro_hint_string(const char* named_arg, const macrodata* thismacro, + int desired_depth, int current_depth = 0) { + // RPG Hacker: This only work when the incorrectly used parameter + // is inside the macro that is currently being defined. Not great, + // but still better than nothing. + if (current_depth == 0 && thisone != nullptr) { + for (int j = 0; thisone->arguments[j]; j++) { + if (!strcmp(named_arg, thisone->arguments[j])) { + string ret = " Did you mean: '"; + ret += generate_macro_arg_string(thisone->arguments[j], 0); + ret += "'?"; + return ret; + } + } + } + + // RPG Hacker: Technically, we could skip a level here and go straight + // to the parent, but maybe at some point we'll want to expand this to + // also look for similar args in the current level, so I'll leave it + // like this, just in case. + if (thismacro != nullptr) { + for (int j = 0; thismacro->arguments[j]; j++) { + if (!strcmp(named_arg, thismacro->arguments[j])) { + string ret = " Did you mean: '"; + ret += generate_macro_arg_string(thismacro->arguments[j], + desired_depth + current_depth); + ret += "'?"; + return ret; + } + } + return generate_macro_hint_string(named_arg, thismacro->parent_macro, + desired_depth, current_depth + 1); + } + + return ""; } -string generate_macro_hint_string(int var_arg, const macrodata* thismacro, int desired_depth, int current_depth=0) -{ - if (thismacro != nullptr) - { - if (thismacro->parent_macro_num_varargs > var_arg) - { - string ret=" Did you mean: '"; - ret += generate_macro_arg_string(var_arg, desired_depth+current_depth+1); - ret += "'?"; - return ret; - } - return generate_macro_hint_string(var_arg, thismacro->parent_macro, desired_depth, current_depth+1); - } - - return ""; +string generate_macro_hint_string(int var_arg, const macrodata* thismacro, + int desired_depth, int current_depth = 0) { + if (thismacro != nullptr) { + if (thismacro->parent_macro_num_varargs > var_arg) { + string ret = " Did you mean: '"; + ret += generate_macro_arg_string(var_arg, + desired_depth + current_depth + 1); + ret += "'?"; + return ret; + } + return generate_macro_hint_string(var_arg, thismacro->parent_macro, + desired_depth, current_depth + 1); + } + + return ""; } string replace_macro_args(const char* line) { - string out; - for (const char * in=line;*in;) - { - if (*in=='<' && in[1]=='<' && in[2] != ':') - { - if (in[2] == '^') - { - out+="<"; - in+=1; - } - else - { - out+="<<"; - in+=2; - } - } - else if (*in=='<') - { - const char * end=in+1; - // RPG Hacker: Added checking for space here, because this code would consider - // if a < b && a > c - // a macro arg expansion. In practice, this is still a sloppy solution and is - // likely to fail in some edge case I can't think of right now. Should parse - // this in a much more robust way at some point... - if (*end==' ') - { - out += *(in++); - continue; - } - - while (*end && *end!='>'&& *end!='<' && *(end+1)!=':') end++; //allow for conditionals and <: - if (*end!='>') - { - out+=*(in++); - continue; - } - - int depth = 0; - for (const char* depth_str = in+1; *depth_str=='^'; depth_str++) - { - depth++; - } - - if (depth != in_macro_def) - { - string temp(in, end-in+1); - out+=temp; - in=end+1; - if (depth > in_macro_def) - { - if (in_macro_def > 0) asar_throw_error(0, error_type_line, error_id_invalid_depth_resolve, "macro parameter", "macro parameter", depth, in_macro_def-1); - else asar_throw_error(0, error_type_block, error_id_macro_param_outside_macro); - } - continue; - } - - if (depth > 0 && !inmacro) asar_throw_error(0, error_type_line, error_id_invalid_depth_resolve, "macro parameter", "macro parameter", depth, in_macro_def-1); - in += depth+1; - - bool is_variadic_arg = false; - if (in[0] == '.' && in[1] == '.' && in[2] == '.' && in[3] == '[') - { - if (end[-1] != ']') - asar_throw_error(0, error_type_block, error_id_unclosed_vararg); - - is_variadic_arg = true; - in += 4; - end--; - } - - if(!inmacro) asar_throw_error(0, error_type_block, error_id_macro_param_outside_macro); - if(is_variadic_arg && !current_macro->variadic) asar_throw_error(0, error_type_block, error_id_macro_not_varadic, "<...[math]>"); - //*end=0; - string param; - string temp(in, end-in); - resolvedefines(param, temp); - in = param.data(); - bool valid_named_param = confirmname(in); - if (!is_variadic_arg) - { - if (!valid_named_param) asar_throw_error(0, error_type_block, error_id_invalid_macro_param_name); - bool found=false; - for (int j=0;current_macro->arguments[j];j++) - { - if (!strcmp(in, current_macro->arguments[j])) - { - found=true; - out+=current_macro_args[j]; - break; - } - } - if (!found) - { - asar_throw_error(0, error_type_block, error_id_macro_param_not_found, generate_macro_arg_string(in, depth).raw(), generate_macro_hint_string(in, current_macro, depth).raw()); - } - } - else - { - snes_label ret; - if(valid_named_param && !labelval(in, &ret, false)) asar_throw_error(0, error_type_block, error_id_invalid_vararg, in); - int arg_num = getnum(in); - - if(forwardlabel) asar_throw_error(0, error_type_block, error_id_label_forward); - - if (arg_num < 0) asar_throw_error(1, error_type_block, error_id_vararg_out_of_bounds, generate_macro_arg_string(arg_num, depth).raw(), ""); - if (arg_num > current_macro_numargs-current_macro->numargs) asar_throw_error(1, error_type_block, error_id_vararg_out_of_bounds, generate_macro_arg_string(arg_num, depth).raw(), generate_macro_hint_string(arg_num, current_macro, depth).raw()); - out+=current_macro_args[arg_num+current_macro->numargs-1]; - } - in=end+1; - if (is_variadic_arg) in++; - } - else out+=*(in++); - } - return out; + string out; + for (const char* in = line; *in;) { + if (*in == '<' && in[1] == '<' && in[2] != ':') { + if (in[2] == '^') { + out += "<"; + in += 1; + } else { + out += "<<"; + in += 2; + } + } else if (*in == '<') { + const char* end = in + 1; + // RPG Hacker: Added checking for space here, because this code would + // consider if a < b && a > c a macro arg expansion. In practice, this is + // still a sloppy solution and is likely to fail in some edge case I can't + // think of right now. Should parse this in a much more robust way at some + // point... + if (*end == ' ') { + out += *(in++); + continue; + } + + while (*end && *end != '>' && *end != '<' && *(end + 1) != ':') + end++; // allow for conditionals and <: + if (*end != '>') { + out += *(in++); + continue; + } + + int depth = 0; + for (const char* depth_str = in + 1; *depth_str == '^'; depth_str++) { + depth++; + } + + if (depth != in_macro_def) { + string temp(in, end - in + 1); + out += temp; + in = end + 1; + if (depth > in_macro_def) { + if (in_macro_def > 0) + asar_throw_error(0, error_type_line, + error_id_invalid_depth_resolve, + "macro parameter", "macro parameter", depth, + in_macro_def - 1); + else + asar_throw_error(0, error_type_block, + error_id_macro_param_outside_macro); + } + continue; + } + + if (depth > 0 && !inmacro) + asar_throw_error(0, error_type_line, error_id_invalid_depth_resolve, + "macro parameter", "macro parameter", depth, + in_macro_def - 1); + in += depth + 1; + + bool is_variadic_arg = false; + if (in[0] == '.' && in[1] == '.' && in[2] == '.' && in[3] == '[') { + if (end[-1] != ']') + asar_throw_error(0, error_type_block, error_id_unclosed_vararg); + + is_variadic_arg = true; + in += 4; + end--; + } + + if (!inmacro) + asar_throw_error(0, error_type_block, + error_id_macro_param_outside_macro); + if (is_variadic_arg && !current_macro->variadic) + asar_throw_error(0, error_type_block, error_id_macro_not_varadic, + "<...[math]>"); + //*end=0; + string param; + string temp(in, end - in); + resolvedefines(param, temp); + in = param.data(); + bool valid_named_param = confirmname(in); + if (!is_variadic_arg) { + if (!valid_named_param) + asar_throw_error(0, error_type_block, + error_id_invalid_macro_param_name); + bool found = false; + for (int j = 0; current_macro->arguments[j]; j++) { + if (!strcmp(in, current_macro->arguments[j])) { + found = true; + out += current_macro_args[j]; + break; + } + } + if (!found) { + asar_throw_error( + 0, error_type_block, error_id_macro_param_not_found, + generate_macro_arg_string(in, depth).raw(), + generate_macro_hint_string(in, current_macro, depth).raw()); + } + } else { + snes_label ret; + if (valid_named_param && !labelval(in, &ret, false)) + asar_throw_error(0, error_type_block, error_id_invalid_vararg, in); + int arg_num = getnum(in); + + if (forwardlabel) + asar_throw_error(0, error_type_block, error_id_label_forward); + + if (arg_num < 0) + asar_throw_error(1, error_type_block, error_id_vararg_out_of_bounds, + generate_macro_arg_string(arg_num, depth).raw(), + ""); + if (arg_num > current_macro_numargs - current_macro->numargs) + asar_throw_error( + 1, error_type_block, error_id_vararg_out_of_bounds, + generate_macro_arg_string(arg_num, depth).raw(), + generate_macro_hint_string(arg_num, current_macro, depth) + .raw()); + out += current_macro_args[arg_num + current_macro->numargs - 1]; + } + in = end + 1; + if (is_variadic_arg) in++; + } else + out += *(in++); + } + return out; } \ No newline at end of file diff --git a/src/asar/macro.h b/src/asar/macro.h index 59a1a78d..9fdfabd7 100644 --- a/src/asar/macro.h +++ b/src/asar/macro.h @@ -1,9 +1,13 @@ #pragma once -void startmacro(const char * line); -void tomacro(const char * line); +#include "assocarr.h" +#include "autoarray.h" +#include "libstr.h" + +void startmacro(const char* line); +void tomacro(const char* line); void endmacro(bool insert); -void callmacro(const char * data); +void callmacro(const char* data); string replace_macro_args(const char* line); extern int macrorecursion; @@ -14,21 +18,20 @@ extern int numvarargs; extern string defining_macro_name; -struct macrodata -{ - autoarray lines; - int numlines; - int startline; - const char * fname; - const char * const* arguments; - const char *arguments_buffer; - int numargs; - bool variadic; - const macrodata* parent_macro; - int parent_macro_num_varargs; +struct macrodata { + autoarray lines; + int numlines; + int startline; + const char* fname; + const char* const* arguments; + const char* arguments_buffer; + int numargs; + bool variadic; + const macrodata* parent_macro; + int parent_macro_num_varargs; }; -void freemacro(macrodata* & macro); +void freemacro(macrodata*& macro); extern assocarr macros; extern macrodata* current_macro; diff --git a/src/asar/main.cpp b/src/asar/main.cpp index 9bf67c13..1429586d 100644 --- a/src/asar/main.cpp +++ b/src/asar/main.cpp @@ -1,97 +1,107 @@ // "because satanism is best defeated by summoning a bigger satan" // ~Alcaro, 2019 (discussing Asar) +#include + #include "addr2line.h" #include "asar.h" -#include "virtualfile.h" -#include "platform/file-helpers.h" -#include "assembleblock.h" #include "asar_math.h" +#include "assembleblock.h" #include "macro.h" -#include -// randomdude999: remember to also update the .rc files (in res/windows/) when changing this. -// Couldn't find a way to automate this without shoving the version somewhere in the CMake files -const int asarver_maj=2; -const int asarver_min=0; -const int asarver_bug=0; -const bool asarver_beta=true; +#include "platform/file-helpers.h" +#include "virtualfile.h" +// randomdude999: remember to also update the .rc files (in res/windows/) when changing +// this. Couldn't find a way to automate this without shoving the version somewhere in +// the CMake files +const int asarver_maj = 2; +const int asarver_min = 0; +const int asarver_bug = 0; +const bool asarver_beta = true; #ifdef _I_RELEASE -extern char blockbetareleases[(!asarver_beta)?1:-1]; +extern char blockbetareleases[(!asarver_beta) ? 1 : -1]; #endif #ifdef _I_DEBUG -extern char blockreleasedebug[(asarver_beta)?1:-1]; +extern char blockreleasedebug[(asarver_beta) ? 1 : -1]; #endif -unsigned const char * romdata_r; +unsigned const char* romdata_r; int romlen_r; int pass; -int optimizeforbank=-1; +int optimizeforbank = -1; int optimize_dp = optimize_dp_flag::NONE; int dp_base = 0; int optimize_address = optimize_address_flag::DEFAULT; autoarray callstack; -bool errored=false; -bool ignoretitleerrors=false; +bool errored = false; +bool ignoretitleerrors = false; -volatile int recursioncount=0; +volatile int recursioncount = 0; virtual_filesystem* filesystem = nullptr; AddressToLineMapping addressToLineMapping; -int get_version_int() -{ - return asarver_maj * 10000 + asarver_min * 100 + asarver_bug; -} - -bool setmapper() -{ - int maxscore=-99999; - mapper_t bestmap=lorom; - mapper_t maps[]={lorom, hirom, exlorom, exhirom}; - for (size_t mapid=0;mapid=128) highbits++; - else if (is_upper(c)) score+=3; - else if (c==' ') score+=2; - else if (is_digit(c)) score+=1; - else if (is_lower(c)) score+=1; - else if (c=='-') score+=1; - else if (!c) foundnull=true; - else score-=3; - } - if (highbits>0 && highbits<=14) score-=21;//high bits set on some, but not all, bytes = unlikely to be a ROM - if ((romdata[snestopc(0x00FFDE)]^romdata[snestopc(0x00FFDC)])!=0xFF || - (romdata[snestopc(0x00FFDF)]^romdata[snestopc(0x00FFDD)])!=0xFF) score=-99999;//checksum doesn't match up to 0xFFFF? Not a ROM. - //too lazy to check the real checksum - if (score>maxscore) - { - maxscore=score; - bestmap=mapper; - } - } - mapper=bestmap; - - //detect oddball mappers - int mapperbyte=romdata[snestopc(0x00FFD5)]; - int romtypebyte=romdata[snestopc(0x00FFD6)]; - if (mapper==lorom) - { - if (mapperbyte==0x23 && (romtypebyte==0x32 || romtypebyte==0x34 || romtypebyte==0x35)) mapper=sa1rom; - } - return (maxscore>=0); +int get_version_int() { return asarver_maj * 10000 + asarver_min * 100 + asarver_bug; } + +bool setmapper() { + int maxscore = -99999; + mapper_t bestmap = lorom; + mapper_t maps[] = {lorom, hirom, exlorom, exhirom}; + for (size_t mapid = 0; mapid < sizeof(maps) / sizeof(maps[0]); mapid++) { + mapper = maps[mapid]; + int score = 0; + int highbits = 0; + bool foundnull = false; + for (int i = 0; i < 21; i++) { + unsigned char c = romdata[snestopc(0x00FFC0 + i)]; + if (foundnull && c) + score -= 4; // according to some documents, NUL terminated names are + // possible - but they shouldn't appear in the middle of + // the name + if (c >= 128) + highbits++; + else if (is_upper(c)) + score += 3; + else if (c == ' ') + score += 2; + else if (is_digit(c)) + score += 1; + else if (is_lower(c)) + score += 1; + else if (c == '-') + score += 1; + else if (!c) + foundnull = true; + else + score -= 3; + } + if (highbits > 0 && highbits <= 14) + score -= 21; // high bits set on some, but not all, bytes = unlikely to be + // a ROM + if ((romdata[snestopc(0x00FFDE)] ^ romdata[snestopc(0x00FFDC)]) != 0xFF || + (romdata[snestopc(0x00FFDF)] ^ romdata[snestopc(0x00FFDD)]) != 0xFF) + score = -99999; // checksum doesn't match up to 0xFFFF? Not a ROM. + // too lazy to check the real checksum + if (score > maxscore) { + maxscore = score; + bestmap = mapper; + } + } + mapper = bestmap; + + // detect oddball mappers + int mapperbyte = romdata[snestopc(0x00FFD5)]; + int romtypebyte = romdata[snestopc(0x00FFD6)]; + if (mapper == lorom) { + if (mapperbyte == 0x23 && + (romtypebyte == 0x32 || romtypebyte == 0x34 || romtypebyte == 0x35)) + mapper = sa1rom; + } + return (maxscore >= 0); } @@ -101,410 +111,377 @@ bool simple_callstacks = true; // Shortens target_path to a relative path, but only if it resides // within base_path or a child directory of it. -string shorten_to_relative_path(const char* base_path, const char* target_path) -{ - if (stribegin(target_path, base_path)) target_path += strlen(base_path); - return target_path; +string shorten_to_relative_path(const char* base_path, const char* target_path) { + if (stribegin(target_path, base_path)) target_path += strlen(base_path); + return target_path; } -string get_top_level_directory() -{ - string top_level_file_dir; - for (int i = 0; i < callstack.count; ++i) - { - if (callstack[i].type == callstack_entry_type::FILE) - { - top_level_file_dir = dir(callstack[i].content); - break; - } - } - return top_level_file_dir; +string get_top_level_directory() { + string top_level_file_dir; + for (int i = 0; i < callstack.count; ++i) { + if (callstack[i].type == callstack_entry_type::FILE) { + top_level_file_dir = dir(callstack[i].content); + break; + } + } + return top_level_file_dir; } -string generate_call_details_string(const char* current_block, const char* current_call, int indentation, bool add_lines) -{ - string e; - if (current_block != nullptr || current_call != nullptr) - { - string indent; - if (add_lines) indent += "|"; - for (; indentation > 0; --indentation) indent += " "; - - if (current_block != nullptr) e += STR "\n"+indent+"in block: ["+current_block+"]"; - if (current_call != nullptr) e += STR "\n"+indent+"in macro call: [%"+current_call+"]"; - } - return e; +string generate_call_details_string(const char* current_block, const char* current_call, + int indentation, bool add_lines) { + string e; + if (current_block != nullptr || current_call != nullptr) { + string indent; + if (add_lines) indent += "|"; + for (; indentation > 0; --indentation) indent += " "; + + if (current_block != nullptr) + e += STR "\n" + indent + "in block: [" + current_block + "]"; + if (current_call != nullptr) + e += STR "\n" + indent + "in macro call: [%" + current_call + "]"; + } + return e; } -string get_pretty_filename(const char* current_file) -{ - // RPG Hacker: One could make an argument that we shouldn't shorten paths - // here, since some IDEs support jumping to files by double-clicking their - // paths. However, AFAIK, no IDE supports this for Asar yet, and if it's - // ever desired, we could just make it a command line option. Until then, - // I think it's more important to optimize for pretty command line display. - return shorten_to_relative_path(get_top_level_directory(), current_file); +string get_pretty_filename(const char* current_file) { + // RPG Hacker: One could make an argument that we shouldn't shorten paths + // here, since some IDEs support jumping to files by double-clicking their + // paths. However, AFAIK, no IDE supports this for Asar yet, and if it's + // ever desired, we could just make it a command line option. Until then, + // I think it's more important to optimize for pretty command line display. + return shorten_to_relative_path(get_top_level_directory(), current_file); } -string generate_filename_and_line(const char* current_file, int current_line_no) -{ - return STR current_file - + (current_line_no>=0?STR ":"+dec(current_line_no+1):""); +string generate_filename_and_line(const char* current_file, int current_line_no) { + return STR current_file + + (current_line_no >= 0 ? STR ":" + dec(current_line_no + 1) : ""); } -string format_stack_line(const printable_callstack_entry& entry, int stack_frame_index) -{ - string indent = "\n| "; - indent += dec(stack_frame_index); - indent += ": "; - // RPG Hacker: We'll probably never have a call stack in the - // hundreds even, so this very specific, lazy solution suffices. - if (stack_frame_index < 100) indent += " "; - if (stack_frame_index < 10) indent += " "; - return indent - + generate_filename_and_line(entry.prettypath, entry.lineno) - + entry.details; +string format_stack_line(const printable_callstack_entry& entry, + int stack_frame_index) { + string indent = "\n| "; + indent += dec(stack_frame_index); + indent += ": "; + // RPG Hacker: We'll probably never have a call stack in the + // hundreds even, so this very specific, lazy solution suffices. + if (stack_frame_index < 100) indent += " "; + if (stack_frame_index < 10) indent += " "; + return indent + generate_filename_and_line(entry.prettypath, entry.lineno) + + entry.details; } -void push_stack_line(autoarray* out, const char* current_file, const char* current_block, const char* current_call, int current_line_no, int indentation, bool add_lines) -{ - printable_callstack_entry new_entry; - new_entry.fullpath = current_file; - new_entry.prettypath = get_pretty_filename(current_file); - new_entry.lineno = current_line_no; - new_entry.details = strip_whitespace(generate_call_details_string(current_block, current_call, indentation, add_lines).raw()); - out->append(new_entry); +void push_stack_line(autoarray* out, + const char* current_file, const char* current_block, + const char* current_call, int current_line_no, int indentation, + bool add_lines) { + printable_callstack_entry new_entry; + new_entry.fullpath = current_file; + new_entry.prettypath = get_pretty_filename(current_file); + new_entry.lineno = current_line_no; + new_entry.details = + strip_whitespace(generate_call_details_string(current_block, current_call, + indentation, add_lines) + .raw()); + out->append(new_entry); } -void get_current_line_details(string* location, string* details, bool exclude_block) -{ - const char* current_file = nullptr; - const char* current_block = nullptr; - const char* current_call = nullptr; - int current_line_no = -1; - for (int i = callstack.count-1; i >= 0 ; --i) - { - switch (callstack[i].type) - { - case callstack_entry_type::FILE: - current_file = callstack[i].content; - if (exclude_block) current_block = nullptr; - *location = generate_filename_and_line(get_pretty_filename(current_file), current_line_no); - *details = generate_call_details_string(current_block, current_call, 4, false); - return; - case callstack_entry_type::MACRO_CALL: - if (current_call == nullptr) current_call = callstack[i].content; - break; - case callstack_entry_type::LINE: - if (current_block == nullptr && current_call == nullptr) current_block = callstack[i].content; - if (current_line_no == -1) current_line_no = callstack[i].lineno; - break; - case callstack_entry_type::BLOCK: - if (current_block == nullptr) current_block = callstack[i].content; - break; - } - } - *location = ""; - *details = ""; +void get_current_line_details(string* location, string* details, bool exclude_block) { + const char* current_file = nullptr; + const char* current_block = nullptr; + const char* current_call = nullptr; + int current_line_no = -1; + for (int i = callstack.count - 1; i >= 0; --i) { + switch (callstack[i].type) { + case callstack_entry_type::FILE: + current_file = callstack[i].content; + if (exclude_block) current_block = nullptr; + *location = generate_filename_and_line( + get_pretty_filename(current_file), current_line_no); + *details = generate_call_details_string(current_block, current_call, 4, + false); + return; + case callstack_entry_type::MACRO_CALL: + if (current_call == nullptr) current_call = callstack[i].content; + break; + case callstack_entry_type::LINE: + if (current_block == nullptr && current_call == nullptr) + current_block = callstack[i].content; + if (current_line_no == -1) current_line_no = callstack[i].lineno; + break; + case callstack_entry_type::BLOCK: + if (current_block == nullptr) current_block = callstack[i].content; + break; + } + } + *location = ""; + *details = ""; } -void get_full_printable_callstack(autoarray* out, int indentation, bool add_lines) -{ - out->reset(); - const char* current_file = nullptr; - const char* current_block = nullptr; - const char* current_call = nullptr; - int current_line_no = -1; - for (int i = 0; i < callstack.count; ++i) - { - switch (callstack[i].type) - { - case callstack_entry_type::FILE: - if (current_file != nullptr) - { - push_stack_line(out, current_file, current_block, current_call, current_line_no, indentation, add_lines); - } - current_file = callstack[i].content; - current_block = nullptr; - current_call = nullptr; - current_line_no = -1; - break; - case callstack_entry_type::MACRO_CALL: - current_block = nullptr; - current_call = callstack[i].content; - break; - case callstack_entry_type::LINE: - current_line_no = callstack[i].lineno; - current_block = callstack[i].content; - break; - case callstack_entry_type::BLOCK: - current_block = callstack[i].content; - break; - } - } +void get_full_printable_callstack(autoarray* out, + int indentation, bool add_lines) { + out->reset(); + const char* current_file = nullptr; + const char* current_block = nullptr; + const char* current_call = nullptr; + int current_line_no = -1; + for (int i = 0; i < callstack.count; ++i) { + switch (callstack[i].type) { + case callstack_entry_type::FILE: + if (current_file != nullptr) { + push_stack_line(out, current_file, current_block, current_call, + current_line_no, indentation, add_lines); + } + current_file = callstack[i].content; + current_block = nullptr; + current_call = nullptr; + current_line_no = -1; + break; + case callstack_entry_type::MACRO_CALL: + current_block = nullptr; + current_call = callstack[i].content; + break; + case callstack_entry_type::LINE: + current_line_no = callstack[i].lineno; + current_block = callstack[i].content; + break; + case callstack_entry_type::BLOCK: + current_block = callstack[i].content; + break; + } + } } -string get_full_callstack() -{ - autoarray printable_stack; - get_full_printable_callstack(&printable_stack, 12, true); - - string e; - if (printable_stack.count > 0) - { - e += "\nFull call stack:"; - for (int i = printable_stack.count-1; i >= 0; --i) - { - e += format_stack_line(printable_stack[i], i); - } - } - return e; +string get_full_callstack() { + autoarray printable_stack; + get_full_printable_callstack(&printable_stack, 12, true); + + string e; + if (printable_stack.count > 0) { + e += "\nFull call stack:"; + for (int i = printable_stack.count - 1; i >= 0; --i) { + e += format_stack_line(printable_stack[i], i); + } + } + return e; } // RPG Hacker: This function essetially replicates classic Asar behavior // of only printing a single macro call below the current level. -string get_simple_callstack() -{ - int i; - const char* current_call = nullptr; - for (i = callstack.count-1; i >= 0 ; --i) - { - if (callstack[i].type == callstack_entry_type::MACRO_CALL) - { - current_call = callstack[i].content; - break; - } - } - - const char* current_file = nullptr; - int current_line_no = -1; - if (current_call != nullptr) - { - bool stop = false; - for (int j = i-1; j >= 0 ; --j) - { - switch (callstack[j].type) - { - case callstack_entry_type::FILE: - if (current_file != nullptr) - { - stop = true; - break; - } - current_file = callstack[j].content; - break; - case callstack_entry_type::MACRO_CALL: - stop = true; - break; - case callstack_entry_type::LINE: - if (current_line_no == -1) current_line_no = callstack[j].lineno; - break; - case callstack_entry_type::BLOCK: - break; - } - - if (current_file != nullptr && current_line_no != -1) stop = true; - - if (stop) break; - } - } - - string e; - if (current_call != nullptr && current_file != nullptr) - { - e += STR "\n called from: " + generate_filename_and_line(get_pretty_filename(current_file), current_line_no) - + ": [%" + current_call + "]"; - } - return e; +string get_simple_callstack() { + int i; + const char* current_call = nullptr; + for (i = callstack.count - 1; i >= 0; --i) { + if (callstack[i].type == callstack_entry_type::MACRO_CALL) { + current_call = callstack[i].content; + break; + } + } + + const char* current_file = nullptr; + int current_line_no = -1; + if (current_call != nullptr) { + bool stop = false; + for (int j = i - 1; j >= 0; --j) { + switch (callstack[j].type) { + case callstack_entry_type::FILE: + if (current_file != nullptr) { + stop = true; + break; + } + current_file = callstack[j].content; + break; + case callstack_entry_type::MACRO_CALL: + stop = true; + break; + case callstack_entry_type::LINE: + if (current_line_no == -1) current_line_no = callstack[j].lineno; + break; + case callstack_entry_type::BLOCK: + break; + } + + if (current_file != nullptr && current_line_no != -1) stop = true; + + if (stop) break; + } + } + + string e; + if (current_call != nullptr && current_file != nullptr) { + e += STR "\n called from: " + + generate_filename_and_line(get_pretty_filename(current_file), + current_line_no) + + ": [%" + current_call + "]"; + } + return e; } -string get_callstack() -{ - if (simple_callstacks) - return get_simple_callstack(); - else - return get_full_callstack(); +string get_callstack() { + if (simple_callstacks) + return get_simple_callstack(); + else + return get_full_callstack(); } -asar_error_id vfile_error_to_error_id(virtual_file_error vfile_error) -{ - switch (vfile_error) - { - case vfe_doesnt_exist: - return error_id_file_not_found; - case vfe_access_denied: - return error_id_failed_to_open_file_access_denied; - case vfe_not_regular_file: - return error_id_failed_to_open_not_regular_file; - case vfe_unknown: - case vfe_none: - case vfe_num_errors: - return error_id_failed_to_open_file; - } - - return error_id_failed_to_open_file; +asar_error_id vfile_error_to_error_id(virtual_file_error vfile_error) { + switch (vfile_error) { + case vfe_doesnt_exist: + return error_id_file_not_found; + case vfe_access_denied: + return error_id_failed_to_open_file_access_denied; + case vfe_not_regular_file: + return error_id_failed_to_open_not_regular_file; + case vfe_unknown: + case vfe_none: + case vfe_num_errors: + return error_id_failed_to_open_file; + } + + return error_id_failed_to_open_file; } -virtual_file_error asar_get_last_io_error() -{ - if (filesystem != nullptr) - { - return filesystem->get_last_error(); - } +virtual_file_error asar_get_last_io_error() { + if (filesystem != nullptr) { + return filesystem->get_last_error(); + } - return vfe_unknown; + return vfe_unknown; } static bool freespaced; -static int getlenforlabel(int insnespos, int thislabel, bool exists) -{ - unsigned int bank = thislabel>>16; - unsigned int word = thislabel&0xFFFF; - unsigned int relaxed_bank = optimizeforbank < 0 ? 0 : optimizeforbank; - if (!exists) - { - if (!freespaced) freespaceextra++; - freespaced=true; - return 2; - } - else if((optimize_dp == optimize_dp_flag::RAM) && bank == 0x7E && (word-dp_base < 0x100)) - { - return 1; - } - else if(optimize_dp == optimize_dp_flag::ALWAYS && (bank == 0x7E || !(bank & 0x40)) && (word-dp_base < 0x100)) - { - return 1; - } - else if (optimize_address == optimize_address_flag::RAM && bank == 0x7E && word < 0x2000) - { - return 2; - } - else if (optimize_address == optimize_address_flag::MIRRORS && (bank == relaxed_bank || (!(bank & 0x40) && !(relaxed_bank & 0x40))) && word < 0x2000) - { - return 2; - } - else if (optimize_address == optimize_address_flag::MIRRORS && !(bank & 0x40) && !(relaxed_bank & 0x40) && word < 0x8000) - { - return 2; - } - else if (optimizeforbank>=0) - { - if ((unsigned int)thislabel&0xFF000000) return 3; - else if (bank==(unsigned int)optimizeforbank) return 2; - else return 3; - } - else if ((unsigned int)(thislabel|insnespos)&0xFF000000) - { - if ((unsigned int)(thislabel^insnespos)&0xFF000000) return 3; - else return 2; - } - else if ((thislabel^insnespos)&0xFF0000){ return 3; } - else { return 2;} +static int getlenforlabel(int insnespos, int thislabel, bool exists) { + unsigned int bank = thislabel >> 16; + unsigned int word = thislabel & 0xFFFF; + unsigned int relaxed_bank = optimizeforbank < 0 ? 0 : optimizeforbank; + if (!exists) { + if (!freespaced) freespaceextra++; + freespaced = true; + return 2; + } else if ((optimize_dp == optimize_dp_flag::RAM) && bank == 0x7E && + (word - dp_base < 0x100)) { + return 1; + } else if (optimize_dp == optimize_dp_flag::ALWAYS && + (bank == 0x7E || !(bank & 0x40)) && (word - dp_base < 0x100)) { + return 1; + } else if (optimize_address == optimize_address_flag::RAM && bank == 0x7E && + word < 0x2000) { + return 2; + } else if (optimize_address == optimize_address_flag::MIRRORS && + (bank == relaxed_bank || (!(bank & 0x40) && !(relaxed_bank & 0x40))) && + word < 0x2000) { + return 2; + } else if (optimize_address == optimize_address_flag::MIRRORS && !(bank & 0x40) && + !(relaxed_bank & 0x40) && word < 0x8000) { + return 2; + } else if (optimizeforbank >= 0) { + if ((unsigned int)thislabel & 0xFF000000) + return 3; + else if (bank == (unsigned int)optimizeforbank) + return 2; + else + return 3; + } else if ((unsigned int)(thislabel | insnespos) & 0xFF000000) { + if ((unsigned int)(thislabel ^ insnespos) & 0xFF000000) + return 3; + else + return 2; + } else if ((thislabel ^ insnespos) & 0xFF0000) { + return 3; + } else { + return 2; + } } -bool is_hex_constant(const char* str){ - if (*str=='$') - { - str++; - while(is_xdigit(*str)) { - str++; - } - if(*str=='\0'){ - return true; - } - } - return false; +bool is_hex_constant(const char* str) { + if (*str == '$') { + str++; + while (is_xdigit(*str)) { + str++; + } + if (*str == '\0') { + return true; + } + } + return false; } -int getlen(const char * orgstr, bool optimizebankextraction) -{ - const char * str=orgstr; - freespaced=false; - - const char* posneglabel = str; - string posnegname = posneglabelname(&posneglabel, false); - - if (posnegname.length() > 0) - { - if (*posneglabel != '\0') goto notposneglabel; - - if (!pass) return 2; - snes_label label_data; - // RPG Hacker: Umm... what kind of magic constant is this? - label_data.pos = 31415926; - bool found = labelval(posnegname, &label_data); - return getlenforlabel(snespos, (int)label_data.pos, found); - } +int getlen(const char* orgstr, bool optimizebankextraction) { + const char* str = orgstr; + freespaced = false; + + const char* posneglabel = str; + string posnegname = posneglabelname(&posneglabel, false); + + if (posnegname.length() > 0) { + if (*posneglabel != '\0') goto notposneglabel; + + if (!pass) return 2; + snes_label label_data; + // RPG Hacker: Umm... what kind of magic constant is this? + label_data.pos = 31415926; + bool found = labelval(posnegname, &label_data); + return getlenforlabel(snespos, (int)label_data.pos, found); + } notposneglabel: - int len=0; - while (*str) - { - int thislen=0; - bool maybebankextraction=(str==orgstr); - if (*str=='$') - { - str++; - int i; - for (i=0;is_xdigit(str[i]);i++); - //if (i&1) warn(S dec(i)+"-digit hex value");//blocked in getnum instead - thislen=(i+1)/2; - str+=i; - } - else if (*str=='%') - { - str++; - int i; - for (i=0;str[i]=='0' || str[i]=='1';i++); - //if (i&7) warn(S dec(i)+"-digit binary value"); - thislen=(i+7)/8; - str+=i; - } - else if (str[0]=='\'' && str[2]=='\'') - { - thislen=1; - str+=3; - } - else if (is_digit(*str)) - { - int val=strtol(str, const_cast(&str), 10); - if (val>=0) thislen=1; - if (val>=256) thislen=2; - if (val>=65536) thislen=3; - } - else if (is_ualpha(*str) || *str=='.' || *str=='?') - { - snes_label thislabel; - bool exists=labelval(&str, &thislabel); - thislen=getlenforlabel(snespos, (int)thislabel.pos, exists); - } - else str++; - if (optimizebankextraction && maybebankextraction && - (!strcmp(str, ">>16") || !strcmp(str, "/65536") || !strcmp(str, "/$10000"))) - return 1; - if (thislen>len) len=thislen; - } - return len; + int len = 0; + while (*str) { + int thislen = 0; + bool maybebankextraction = (str == orgstr); + if (*str == '$') { + str++; + int i; + for (i = 0; is_xdigit(str[i]); i++) + ; + // if (i&1) warn(S dec(i)+"-digit hex value");//blocked in getnum instead + thislen = (i + 1) / 2; + str += i; + } else if (*str == '%') { + str++; + int i; + for (i = 0; str[i] == '0' || str[i] == '1'; i++) + ; + // if (i&7) warn(S dec(i)+"-digit binary value"); + thislen = (i + 7) / 8; + str += i; + } else if (str[0] == '\'' && str[2] == '\'') { + thislen = 1; + str += 3; + } else if (is_digit(*str)) { + int val = strtol(str, const_cast(&str), 10); + if (val >= 0) thislen = 1; + if (val >= 256) thislen = 2; + if (val >= 65536) thislen = 3; + } else if (is_ualpha(*str) || *str == '.' || *str == '?') { + snes_label thislabel; + bool exists = labelval(&str, &thislabel); + thislen = getlenforlabel(snespos, (int)thislabel.pos, exists); + } else + str++; + if (optimizebankextraction && maybebankextraction && + (!strcmp(str, ">>16") || !strcmp(str, "/65536") || !strcmp(str, "/$10000"))) + return 1; + if (thislen > len) len = thislen; + } + return len; } struct strcompare { - bool operator() (const char * lhs, const char * rhs) const - { - return strcmp(lhs, rhs)<0; - } + bool operator()(const char* lhs, const char* rhs) const { + return strcmp(lhs, rhs) < 0; + } }; struct stricompare { - bool operator() (const char * lhs, const char * rhs) const - { - return stricmp(lhs, rhs)<0; - } + bool operator()(const char* lhs, const char* rhs) const { + return stricmp(lhs, rhs) < 0; + } }; struct sourcefile { - char *data; - char** contents; - int numlines; + char* data; + char** contents; + int numlines; }; static assocarr filecontents; @@ -513,705 +490,674 @@ assocarr defines; assocarr clidefines; assocarr builtindefines; -bool validatedefinename(const char * name) -{ - if (!name[0]) return false; - for (int i = 0;name[i];i++) - { - if (!is_ualnum(name[i])) return false; - } +bool validatedefinename(const char* name) { + if (!name[0]) return false; + for (int i = 0; name[i]; i++) { + if (!is_ualnum(name[i])) return false; + } - return true; + return true; } -void resolvedefines(string& out, const char * start) -{ - recurseblock rec; - const char * here=start; - if (!strchr(here, '!')) - { - out += here; - return; - } - while (*here) - { - if (here[0] == '\\' && here[1] == '\\') - { - // allow using \\ as escape sequence - out += "\\"; - here += 2; - } - else if (here[0] == '\\' && here[1] == '!') - { - // allow using \! to escape ! - out+="!"; - here += 2; - } - else if (*here=='!') - { - bool first=(here==start || (here>=start+4 && here[-1]==' ' && here[-2]==':' && here[-3]==' '));//check if it's the start of a block - string defname; - here++; - - int depth = 0; - for (const char* depth_str = here; *depth_str=='^'; depth_str++) - { - depth++; - } - here += depth; - - if (depth != in_macro_def) - { - out += '!'; - for (int i=0; i < depth; ++i) out += '^'; - if (depth > in_macro_def) asar_throw_error(0, error_type_line, error_id_invalid_depth_resolve, "define", "define", depth, in_macro_def); - continue; - } - - if (*here=='{') - { - here++; - string unprocessedname; - int braces=1; - while (true) - { - if (*here=='{') braces++; - if (*here=='}') braces--; - if (!*here) asar_throw_error(0, error_type_line, error_id_mismatched_braces); - if (!braces) break; - unprocessedname+=*here++; - } - here++; - resolvedefines(defname, unprocessedname); - if (!validatedefinename(defname)) asar_throw_error(0, error_type_line, error_id_invalid_define_name); - } - else - { - while (is_ualnum(*here)) defname+=*here++; - } - - if (first) - { - enum { - null, - append, - expand, - domath, - setifnotset, - } mode; - if(0); - else if (stribegin(here, " = ")) { here+=3; mode=null; } - else if (stribegin(here, " += ")) { here+=4; mode=append; } - else if (stribegin(here, " := ")) { here+=4; mode=expand; } - else if (stribegin(here, " #= ")) { here+=4; mode=domath; } - else if (stribegin(here, " ?= ")) { here+=4; mode=setifnotset; } - else goto notdefineset; - string val; - if (*here=='"') - { - here++; - while (true) - { - if (*here=='"') - { - if (!here[1] || here[1]==' ') break; - else if (here[1]=='"') here++; - else asar_throw_error(0, error_type_line, error_id_broken_define_declaration); - } - val+=*here++; - } - here++; - } - else - { - while (*here && *here!=' ') val+=*here++; - } - //if (strqchr(val.data(), ';')) *strqchr(val.data(), ';')=0; - if (*here && !stribegin(here, " : ")) asar_throw_error(0, error_type_line, error_id_broken_define_declaration); - // RPG Hacker: Is it really a good idea to normalize - // the content of defines? That kinda violates their - // functionality as a string replacement mechanism. - val.qnormalize(); - - // RPG Hacker: throw an error if we're trying to overwrite built-in defines. - if (builtindefines.exists(defname)) - { - asar_throw_error(0, error_type_line, error_id_overriding_builtin_define, defname.data()); - } - - switch (mode) - { - case null: - { - defines.create(defname) = val; - break; - } - case append: - { - if (!defines.exists(defname)) asar_throw_error(0, error_type_line, error_id_define_not_found, defname.data()); - string oldval = defines.find(defname); - val=oldval+val; - defines.create(defname) = val; - break; - } - case expand: - { - string newval; - resolvedefines(newval, val); - defines.create(defname) = newval; - break; - } - case domath: - { - string newval; - resolvedefines(newval, val); - double num= getnumdouble(newval); - if (foundlabel && !foundlabel_static) asar_throw_error(0, error_type_line, error_id_define_label_math); - defines.create(defname) = ftostr(num); - break; - } - case setifnotset: - { - if (!defines.exists(defname)) defines.create(defname) = val; - break; - } - } - } - else - { - notdefineset: - if (!defname) out+="!"; - else - { - if (!defines.exists(defname)) asar_throw_error(0, error_type_line, error_id_define_not_found, defname.data()); - else { - string thisone = defines.find(defname); - resolvedefines(out, thisone); - } - } - } - } - else out+=*here++; - } - if (!confirmquotes(out)) { asar_throw_error(0, error_type_null, error_id_mismatched_quotes); out = ""; } +void resolvedefines(string& out, const char* start) { + recurseblock rec; + const char* here = start; + if (!strchr(here, '!')) { + out += here; + return; + } + while (*here) { + if (here[0] == '\\' && here[1] == '\\') { + // allow using \\ as escape sequence + out += "\\"; + here += 2; + } else if (here[0] == '\\' && here[1] == '!') { + // allow using \! to escape ! + out += "!"; + here += 2; + } else if (*here == '!') { + bool first = (here == start || + (here >= start + 4 && here[-1] == ' ' && here[-2] == ':' && + here[-3] == ' ')); // check if it's the start of a block + string defname; + here++; + + int depth = 0; + for (const char* depth_str = here; *depth_str == '^'; depth_str++) { + depth++; + } + here += depth; + + if (depth != in_macro_def) { + out += '!'; + for (int i = 0; i < depth; ++i) out += '^'; + if (depth > in_macro_def) + asar_throw_error(0, error_type_line, error_id_invalid_depth_resolve, + "define", "define", depth, in_macro_def); + continue; + } + + if (*here == '{') { + here++; + string unprocessedname; + int braces = 1; + while (true) { + if (*here == '{') braces++; + if (*here == '}') braces--; + if (!*here) + asar_throw_error(0, error_type_line, + error_id_mismatched_braces); + if (!braces) break; + unprocessedname += *here++; + } + here++; + resolvedefines(defname, unprocessedname); + if (!validatedefinename(defname)) + asar_throw_error(0, error_type_line, error_id_invalid_define_name); + } else { + while (is_ualnum(*here)) defname += *here++; + } + + if (first) { + enum { + null, + append, + expand, + domath, + setifnotset, + } mode; + if (0) + ; + else if (stribegin(here, " = ")) { + here += 3; + mode = null; + } else if (stribegin(here, " += ")) { + here += 4; + mode = append; + } else if (stribegin(here, " := ")) { + here += 4; + mode = expand; + } else if (stribegin(here, " #= ")) { + here += 4; + mode = domath; + } else if (stribegin(here, " ?= ")) { + here += 4; + mode = setifnotset; + } else + goto notdefineset; + string val; + if (*here == '"') { + here++; + while (true) { + if (*here == '"') { + if (!here[1] || here[1] == ' ') + break; + else if (here[1] == '"') + here++; + else + asar_throw_error(0, error_type_line, + error_id_broken_define_declaration); + } + val += *here++; + } + here++; + } else { + while (*here && *here != ' ') val += *here++; + } + // if (strqchr(val.data(), ';')) *strqchr(val.data(), ';')=0; + if (*here && !stribegin(here, " : ")) + asar_throw_error(0, error_type_line, + error_id_broken_define_declaration); + // RPG Hacker: Is it really a good idea to normalize + // the content of defines? That kinda violates their + // functionality as a string replacement mechanism. + val.qnormalize(); + + // RPG Hacker: throw an error if we're trying to overwrite built-in + // defines. + if (builtindefines.exists(defname)) { + asar_throw_error(0, error_type_line, + error_id_overriding_builtin_define, + defname.data()); + } + + switch (mode) { + case null: { + defines.create(defname) = val; + break; + } + case append: { + if (!defines.exists(defname)) + asar_throw_error(0, error_type_line, + error_id_define_not_found, defname.data()); + string oldval = defines.find(defname); + val = oldval + val; + defines.create(defname) = val; + break; + } + case expand: { + string newval; + resolvedefines(newval, val); + defines.create(defname) = newval; + break; + } + case domath: { + string newval; + resolvedefines(newval, val); + double num = getnumdouble(newval); + if (foundlabel && !foundlabel_static) + asar_throw_error(0, error_type_line, + error_id_define_label_math); + defines.create(defname) = ftostr(num); + break; + } + case setifnotset: { + if (!defines.exists(defname)) defines.create(defname) = val; + break; + } + } + } else { + notdefineset: + if (!defname) + out += "!"; + else { + if (!defines.exists(defname)) + asar_throw_error(0, error_type_line, error_id_define_not_found, + defname.data()); + else { + string thisone = defines.find(defname); + resolvedefines(out, thisone); + } + } + } + } else + out += *here++; + } + if (!confirmquotes(out)) { + asar_throw_error(0, error_type_null, error_id_mismatched_quotes); + out = ""; + } } bool moreonline; bool asarverallowed = false; -void assembleline(const char * fname, int linenum, const char * line) -{ - recurseblock rec; - bool moreonlinetmp=moreonline; - // randomdude999: redundant, assemblefile already converted the path to absolute - //string absolutepath = filesystem->create_absolute_path("", fname); - string absolutepath = fname; - try - { - string out=line; - out.qreplace(": :", ": :"); - autoptr blocks=qsplitstr(out.temp_raw(), " : "); - moreonline=true; - for (int block=0;moreonline;block++) - { - moreonline=(blocks[block+1] != nullptr); - try - { - string stripped_block = strip_whitespace(blocks[block]); - - callstack_push cs_push(callstack_entry_type::BLOCK, stripped_block); - - assembleblock(stripped_block); - checkbankcross(); - } - catch (errblock&) {} - if (blocks[block][0]!='\0') asarverallowed=false; - } - } - catch (errline&) {} - moreonline=moreonlinetmp; +void assembleline(const char* fname, int linenum, const char* line) { + recurseblock rec; + bool moreonlinetmp = moreonline; + // randomdude999: redundant, assemblefile already converted the path to absolute + // string absolutepath = filesystem->create_absolute_path("", fname); + string absolutepath = fname; + try { + string out = line; + out.qreplace(": :", ": :"); + autoptr blocks = qsplitstr(out.temp_raw(), " : "); + moreonline = true; + for (int block = 0; moreonline; block++) { + moreonline = (blocks[block + 1] != nullptr); + try { + string stripped_block = strip_whitespace(blocks[block]); + + callstack_push cs_push(callstack_entry_type::BLOCK, stripped_block); + + assembleblock(stripped_block); + checkbankcross(); + } catch (errblock&) { + } + if (blocks[block][0] != '\0') asarverallowed = false; + } + } catch (errline&) { + } + moreonline = moreonlinetmp; } -int incsrcdepth=0; +int incsrcdepth = 0; // Returns true if a file is protected via // an "includeonce". -bool file_included_once(const char* file) -{ - for (int i = 0; i < includeonce.count; ++i) - { - if (includeonce[i] == file) - { - return true; - } - } - - return false; +bool file_included_once(const char* file) { + for (int i = 0; i < includeonce.count; ++i) { + if (includeonce[i] == file) { + return true; + } + } + + return false; } autoarray macro_defs; -int in_macro_def=0; - -void assemblefile(const char * filename) -{ - incsrcdepth++; - string absolutepath = filesystem->create_absolute_path(get_current_file_name(), filename); - - if (file_included_once(absolutepath)) - { - return; - } - - callstack_push cs_push(callstack_entry_type::FILE, absolutepath); - - sourcefile file; - file.contents = nullptr; - file.numlines = 0; - int startif=numif; - if (!filecontents.exists(absolutepath)) - { - char * temp = readfile(absolutepath, ""); - if (!temp) - { - asar_throw_error(0, error_type_null, vfile_error_to_error_id(asar_get_last_io_error()), filename); - - return; - } - sourcefile& newfile = filecontents.create(absolutepath); - newfile.contents =split(temp, '\n'); - newfile.data = temp; - for (int i=0;newfile.contents[i];i++) - { - newfile.numlines++; - char * line= newfile.contents[i]; - char * comment = strqchr(line, ';'); - if(comment) *comment = 0; - if (!confirmquotes(line)) { callstack_push cs_push(callstack_entry_type::LINE, line, i); asar_throw_error(0, error_type_null, error_id_mismatched_quotes); line[0] = '\0'; } - newfile.contents[i] = strip_whitespace(line); - } - for(int i=0;newfile.contents[i];i++) - { - char* line = newfile.contents[i]; - if(!*line) continue; - for (int j=1;line[strlen(line) - 1] == ',' && newfile.contents[i+j];j++) - { - // not using strcat because the source and dest overlap here - char* otherline = newfile.contents[i+j]; - char* line_end = line + strlen(line); - while(*otherline) *line_end++ = *otherline++; - *line_end = '\0'; - static char nullstr[]=""; - newfile.contents[i+j]=nullstr; - } - } - file = newfile; - } else { // filecontents.exists(absolutepath) - file = filecontents.find(absolutepath); - } - asarverallowed=true; - for (int i=0;file.contents[i] && i(file.contents, i, connectedline); - - int prevnumif = numif; - - do_line_logic(connectedline, absolutepath, i); - i += skiplines; - - if (numif != prevnumif && whilestatus[numif].iswhile && whilestatus[numif].cond) - i = whilestatus[numif].startline - 1; - } - while (in_macro_def > 0) - { - asar_throw_error(0, error_type_null, error_id_unclosed_macro, macro_defs[in_macro_def-1].data()); - if (!pass && in_macro_def == 1) endmacro(false); - in_macro_def--; - macro_defs.remove(in_macro_def); - } - if (numif!=startif) - { - numif=startif; - numtrue=startif; - asar_throw_error(0, error_type_null, error_id_unclosed_if); - } - incsrcdepth--; +int in_macro_def = 0; + +void assemblefile(const char* filename) { + incsrcdepth++; + string absolutepath = + filesystem->create_absolute_path(get_current_file_name(), filename); + + if (file_included_once(absolutepath)) { + return; + } + + callstack_push cs_push(callstack_entry_type::FILE, absolutepath); + + sourcefile file; + file.contents = nullptr; + file.numlines = 0; + int startif = numif; + if (!filecontents.exists(absolutepath)) { + char* temp = readfile(absolutepath, ""); + if (!temp) { + asar_throw_error(0, error_type_null, + vfile_error_to_error_id(asar_get_last_io_error()), + filename); + + return; + } + sourcefile& newfile = filecontents.create(absolutepath); + newfile.contents = split(temp, '\n'); + newfile.data = temp; + for (int i = 0; newfile.contents[i]; i++) { + newfile.numlines++; + char* line = newfile.contents[i]; + char* comment = strqchr(line, ';'); + if (comment) *comment = 0; + if (!confirmquotes(line)) { + callstack_push cs_push(callstack_entry_type::LINE, line, i); + asar_throw_error(0, error_type_null, error_id_mismatched_quotes); + line[0] = '\0'; + } + newfile.contents[i] = strip_whitespace(line); + } + for (int i = 0; newfile.contents[i]; i++) { + char* line = newfile.contents[i]; + if (!*line) continue; + for (int j = 1; line[strlen(line) - 1] == ',' && newfile.contents[i + j]; + j++) { + // not using strcat because the source and dest overlap here + char* otherline = newfile.contents[i + j]; + char* line_end = line + strlen(line); + while (*otherline) *line_end++ = *otherline++; + *line_end = '\0'; + static char nullstr[] = ""; + newfile.contents[i + j] = nullstr; + } + } + file = newfile; + } else { // filecontents.exists(absolutepath) + file = filecontents.find(absolutepath); + } + asarverallowed = true; + for (int i = 0; file.contents[i] && i < file.numlines; i++) { + string connectedline; + int skiplines = getconnectedlines(file.contents, i, connectedline); + + int prevnumif = numif; + + do_line_logic(connectedline, absolutepath, i); + i += skiplines; + + if (numif != prevnumif && whilestatus[numif].iswhile && whilestatus[numif].cond) + i = whilestatus[numif].startline - 1; + } + while (in_macro_def > 0) { + asar_throw_error(0, error_type_null, error_id_unclosed_macro, + macro_defs[in_macro_def - 1].data()); + if (!pass && in_macro_def == 1) endmacro(false); + in_macro_def--; + macro_defs.remove(in_macro_def); + } + if (numif != startif) { + numif = startif; + numtrue = startif; + asar_throw_error(0, error_type_null, error_id_unclosed_if); + } + incsrcdepth--; } // RPG Hacker: At some point, this should probably be merged // into assembleline(), since the two names just cause // confusion otherwise. -void do_line_logic(const char* line, const char* filename, int lineno) -{ - try - { - string current_line; - if (numif==numtrue || (numtrue+1==numif && stribegin(line, "elseif "))) - { - callstack_push cs_push(callstack_entry_type::LINE, line, lineno); - string tmp=replace_macro_args(line); - tmp.qnormalize(); - resolvedefines(current_line, tmp); - } - else current_line=line; - - callstack_push cs_push(callstack_entry_type::LINE, current_line, lineno); - - if (stribegin(current_line, "macro ") && numif==numtrue) - { - // RPG Hacker: Slight redundancy here with code that is - // also in startmacro(). Could improve this for Asar 2.0. - string macro_name = current_line.data()+6; - char * startpar=strqchr(macro_name.data(), '('); - if (startpar) *startpar=0; - macro_defs.append(macro_name); - - // RPG Hacker: I think it would make more logical sense - // to have this ++ after the if, but hat breaks compatibility - // with at least one test, and it generally leads to more - // errors being output after a broken macro declaration. - in_macro_def++; - if (!pass) - { - if (in_macro_def == 1) startmacro(current_line.data()+6); - else tomacro(current_line); - } - } - else if (!stricmp(current_line, "endmacro") && numif==numtrue) - { - if (in_macro_def == 0) asar_throw_error(0, error_type_line, error_id_misplaced_endmacro); - else - { - in_macro_def--; - macro_defs.remove(in_macro_def); - if (!pass) - { - if (in_macro_def == 0) endmacro(true); - else tomacro(current_line); - } - } - } - else if (in_macro_def > 0) - { - if (!pass) tomacro(current_line); - } - else - { - assembleline(filename, lineno, current_line); - } - } - catch (errline&) {} +void do_line_logic(const char* line, const char* filename, int lineno) { + try { + string current_line; + if (numif == numtrue || (numtrue + 1 == numif && stribegin(line, "elseif "))) { + callstack_push cs_push(callstack_entry_type::LINE, line, lineno); + string tmp = replace_macro_args(line); + tmp.qnormalize(); + resolvedefines(current_line, tmp); + } else + current_line = line; + + callstack_push cs_push(callstack_entry_type::LINE, current_line, lineno); + + if (stribegin(current_line, "macro ") && numif == numtrue) { + // RPG Hacker: Slight redundancy here with code that is + // also in startmacro(). Could improve this for Asar 2.0. + string macro_name = current_line.data() + 6; + char* startpar = strqchr(macro_name.data(), '('); + if (startpar) *startpar = 0; + macro_defs.append(macro_name); + + // RPG Hacker: I think it would make more logical sense + // to have this ++ after the if, but hat breaks compatibility + // with at least one test, and it generally leads to more + // errors being output after a broken macro declaration. + in_macro_def++; + if (!pass) { + if (in_macro_def == 1) + startmacro(current_line.data() + 6); + else + tomacro(current_line); + } + } else if (!stricmp(current_line, "endmacro") && numif == numtrue) { + if (in_macro_def == 0) + asar_throw_error(0, error_type_line, error_id_misplaced_endmacro); + else { + in_macro_def--; + macro_defs.remove(in_macro_def); + if (!pass) { + if (in_macro_def == 0) + endmacro(true); + else + tomacro(current_line); + } + } + } else if (in_macro_def > 0) { + if (!pass) tomacro(current_line); + } else { + assembleline(filename, lineno, current_line); + } + } catch (errline&) { + } } -void parse_std_includes(const char* textfile, autoarray& outarray) -{ - char* content = readfilenative(textfile); - - if (content != nullptr) - { - char* pos = content; - - while (pos[0] != '\0') - { - string stdinclude; - - do - { - if (pos[0] != '\r' && pos[0] != '\n') - { - stdinclude += pos[0]; - } - pos++; - } while (pos[0] != '\0' && pos[0] != '\n'); - - strip_whitespace(stdinclude); - - if (stdinclude != "") - { - if (!path_is_absolute(stdinclude)) - { - stdinclude = dir(textfile) + stdinclude; - } - outarray.append(normalize_path(stdinclude)); - } - } - - free(content); - } +void parse_std_includes(const char* textfile, autoarray& outarray) { + char* content = readfilenative(textfile); + + if (content != nullptr) { + char* pos = content; + + while (pos[0] != '\0') { + string stdinclude; + + do { + if (pos[0] != '\r' && pos[0] != '\n') { + stdinclude += pos[0]; + } + pos++; + } while (pos[0] != '\0' && pos[0] != '\n'); + + strip_whitespace(stdinclude); + + if (stdinclude != "") { + if (!path_is_absolute(stdinclude)) { + stdinclude = dir(textfile) + stdinclude; + } + outarray.append(normalize_path(stdinclude)); + } + } + + free(content); + } } -void parse_std_defines(const char* textfile) -{ - - // RPG Hacker: add built-in defines. - // (They're not really standard defines, but I was lazy and this was - // one convenient place for doing it). - builtindefines.create("assembler") = "asar"; - builtindefines.create("assembler_ver") = dec(get_version_int()); - builtindefines.create("assembler_time") = dec(time(nullptr)); - - if(textfile == nullptr) return; - - char* content = readfilenative(textfile); - - if (content != nullptr) - { - char* pos = content; - while (*pos != 0) { - string define_name; - string define_val; - - while (*pos != '=' && *pos != '\n') { - define_name += *pos; - pos++; - } - if (*pos != 0 && *pos != '\n') pos++; // skip = - while (*pos != 0 && *pos != '\n') { - define_val += *pos; - pos++; - } - if (*pos != 0) - pos++; // skip \n - // clean define_name - strip_whitespace(define_name); - define_name.strip_prefix('!'); // remove leading ! if present - - if (define_name == "") - { - if (define_val == "") - { - continue; - } - - asar_throw_error(pass, error_type_null, error_id_stddefines_no_identifier); - } - - if (!validatedefinename(define_name)) asar_throw_error(pass, error_type_null, error_id_cmdl_define_invalid, "stddefines.txt", define_name.data()); - - // clean define_val - const char* defval = define_val.data(); - string cleaned_defval; - - if (*defval == 0) { - // no value - if (clidefines.exists(define_name)) asar_throw_error(pass, error_type_null, error_id_cmdl_define_override, "Std define", define_name.data()); - clidefines.create(define_name) = ""; - continue; - } - - while (*defval == ' ' || *defval == '\t') defval++; // skip whitespace in beginning - if (*defval == '"') { - defval++; // skip opening quote - while (*defval != '"' && *defval != 0) - cleaned_defval += *defval++; - - if (*defval == 0) { - asar_throw_error(pass, error_type_null, error_id_mismatched_quotes); - } - defval++; // skip closing quote - while (*defval == ' ' || *defval == '\t') defval++; // skip whitespace - if (*defval != 0 && *defval != '\n') - asar_throw_error(pass, error_type_null, error_id_stddefine_after_closing_quote); - - if (clidefines.exists(define_name)) asar_throw_error(pass, error_type_null, error_id_cmdl_define_override, "Std define", define_name.data()); - clidefines.create(define_name) = cleaned_defval; - continue; - } - else - { - // slightly hacky way to remove trailing whitespace - const char* defval_end = strchr(defval, '\n'); // slightly hacky way to get end of string or newline - if (!defval_end) defval_end = strchr(defval, 0); - defval_end--; - while (*defval_end == ' ' || *defval_end == '\t') defval_end--; - cleaned_defval = string(defval, (int)(defval_end - defval + 1)); - - if (clidefines.exists(define_name)) asar_throw_error(pass, error_type_null, error_id_cmdl_define_override, "Std define", define_name.data()); - clidefines.create(define_name) = cleaned_defval; - continue; - } - - } - free(content); - } +void parse_std_defines(const char* textfile) { + // RPG Hacker: add built-in defines. + // (They're not really standard defines, but I was lazy and this was + // one convenient place for doing it). + builtindefines.create("assembler") = "asar"; + builtindefines.create("assembler_ver") = dec(get_version_int()); + builtindefines.create("assembler_time") = dec(time(nullptr)); + + if (textfile == nullptr) return; + + char* content = readfilenative(textfile); + + if (content != nullptr) { + char* pos = content; + while (*pos != 0) { + string define_name; + string define_val; + + while (*pos != '=' && *pos != '\n') { + define_name += *pos; + pos++; + } + if (*pos != 0 && *pos != '\n') pos++; // skip = + while (*pos != 0 && *pos != '\n') { + define_val += *pos; + pos++; + } + if (*pos != 0) pos++; // skip \n + // clean define_name + strip_whitespace(define_name); + define_name.strip_prefix('!'); // remove leading ! if present + + if (define_name == "") { + if (define_val == "") { + continue; + } + + asar_throw_error(pass, error_type_null, + error_id_stddefines_no_identifier); + } + + if (!validatedefinename(define_name)) + asar_throw_error(pass, error_type_null, error_id_cmdl_define_invalid, + "stddefines.txt", define_name.data()); + + // clean define_val + const char* defval = define_val.data(); + string cleaned_defval; + + if (*defval == 0) { + // no value + if (clidefines.exists(define_name)) + asar_throw_error(pass, error_type_null, + error_id_cmdl_define_override, "Std define", + define_name.data()); + clidefines.create(define_name) = ""; + continue; + } + + while (*defval == ' ' || *defval == '\t') + defval++; // skip whitespace in beginning + if (*defval == '"') { + defval++; // skip opening quote + while (*defval != '"' && *defval != 0) cleaned_defval += *defval++; + + if (*defval == 0) { + asar_throw_error(pass, error_type_null, error_id_mismatched_quotes); + } + defval++; // skip closing quote + while (*defval == ' ' || *defval == '\t') defval++; // skip whitespace + if (*defval != 0 && *defval != '\n') + asar_throw_error(pass, error_type_null, + error_id_stddefine_after_closing_quote); + + if (clidefines.exists(define_name)) + asar_throw_error(pass, error_type_null, + error_id_cmdl_define_override, "Std define", + define_name.data()); + clidefines.create(define_name) = cleaned_defval; + continue; + } else { + // slightly hacky way to remove trailing whitespace + const char* defval_end = strchr( + defval, + '\n'); // slightly hacky way to get end of string or newline + if (!defval_end) defval_end = strchr(defval, 0); + defval_end--; + while (*defval_end == ' ' || *defval_end == '\t') defval_end--; + cleaned_defval = string(defval, (int)(defval_end - defval + 1)); + + if (clidefines.exists(define_name)) + asar_throw_error(pass, error_type_null, + error_id_cmdl_define_override, "Std define", + define_name.data()); + clidefines.create(define_name) = cleaned_defval; + continue; + } + } + free(content); + } } bool checksum_fix_enabled = true; bool force_checksum_fix = false; #define cfree(x) free((void*)x) -static void clearmacro(const string & key, macrodata* & macro) -{ - (void)key; - freemacro(macro); +static void clearmacro(const string& key, macrodata*& macro) { + (void)key; + freemacro(macro); } -static void clearfile(const string & key, sourcefile& filecontent) -{ - (void)key; - cfree(filecontent.data); - cfree(filecontent.contents); +static void clearfile(const string& key, sourcefile& filecontent) { + (void)key; + cfree(filecontent.data); + cfree(filecontent.contents); } #undef cfree -static void adddefine(const string & key, string & value) -{ - if (!defines.exists(key)) defines.create(key) = value; +static void adddefine(const string& key, string& value) { + if (!defines.exists(key)) defines.create(key) = value; } static string symbolfile; -static void printsymbol_wla(const string& key, snes_label& label) -{ - string line = hex((label.pos & 0xFF0000)>>16, 2)+":"+hex(label.pos & 0xFFFF, 4)+" "+key+"\n"; - symbolfile += line; +static void printsymbol_wla(const string& key, snes_label& label) { + string line = hex((label.pos & 0xFF0000) >> 16, 2) + ":" + + hex(label.pos & 0xFFFF, 4) + " " + key + "\n"; + symbolfile += line; } -static void printsymbol_nocash(const string& key, snes_label& label) -{ - string line = hex(label.pos & 0xFFFFFF, 8)+" "+key+"\n"; - symbolfile += line; +static void printsymbol_nocash(const string& key, snes_label& label) { + string line = hex(label.pos & 0xFFFFFF, 8) + " " + key + "\n"; + symbolfile += line; } -string create_symbols_file(string format, uint32_t romCrc){ - format = lower(format); - symbolfile = ""; - if(format == "wla") - { - symbolfile = "; wla symbolic information file\n"; - symbolfile += "; generated by asar\n"; - - symbolfile += "\n[labels]\n"; - labels.each(printsymbol_wla); - - symbolfile += "\n[source files]\n"; - const autoarray& addrToLineFileList = addressToLineMapping.getFileList(); - for (int i = 0; i < addrToLineFileList.count; ++i) - { - char addrToFileListStr[256]; - snprintf(addrToFileListStr, 256, "%.4x %.8x %s\n", - i, - addrToLineFileList[i].fileCrc, - addrToLineFileList[i].filename.data() - ); - symbolfile += addrToFileListStr; - } - - symbolfile += "\n[rom checksum]\n"; - { - char romCrcStr[32]; - snprintf(romCrcStr, 32, "%.8x\n", - romCrc - ); - symbolfile += romCrcStr; - } - - symbolfile += "\n[addr-to-line mapping]\n"; - const autoarray& addrToLineInfo = addressToLineMapping.getAddrToLineInfo(); - for (int i = 0; i < addrToLineInfo.count; ++i) - { - char addrToLineStr[32]; - snprintf(addrToLineStr, 32, "%.2x:%.4x %.4x:%.8x\n", - (addrToLineInfo[i].addr & 0xFF0000) >> 16, - addrToLineInfo[i].addr & 0xFFFF, - addrToLineInfo[i].fileIdx & 0xFFFF, - addrToLineInfo[i].line & 0xFFFFFFFF - ); - symbolfile += addrToLineStr; - } - - } - else if (format == "nocash") - { - symbolfile = ";no$sns symbolic information file\n"; - symbolfile += ";generated by asar\n"; - symbolfile += "\n"; - labels.each(printsymbol_nocash); - } - return symbolfile; +string create_symbols_file(string format, uint32_t romCrc) { + format = lower(format); + symbolfile = ""; + if (format == "wla") { + symbolfile = "; wla symbolic information file\n"; + symbolfile += "; generated by asar\n"; + + symbolfile += "\n[labels]\n"; + labels.each(printsymbol_wla); + + symbolfile += "\n[source files]\n"; + const autoarray& addrToLineFileList = + addressToLineMapping.getFileList(); + for (int i = 0; i < addrToLineFileList.count; ++i) { + char addrToFileListStr[256]; + snprintf(addrToFileListStr, 256, "%.4x %.8x %s\n", i, + addrToLineFileList[i].fileCrc, + addrToLineFileList[i].filename.data()); + symbolfile += addrToFileListStr; + } + + symbolfile += "\n[rom checksum]\n"; + { + char romCrcStr[32]; + snprintf(romCrcStr, 32, "%.8x\n", romCrc); + symbolfile += romCrcStr; + } + + symbolfile += "\n[addr-to-line mapping]\n"; + const autoarray& addrToLineInfo = + addressToLineMapping.getAddrToLineInfo(); + for (int i = 0; i < addrToLineInfo.count; ++i) { + char addrToLineStr[32]; + snprintf(addrToLineStr, 32, "%.2x:%.4x %.4x:%.8x\n", + (addrToLineInfo[i].addr & 0xFF0000) >> 16, + addrToLineInfo[i].addr & 0xFFFF, + addrToLineInfo[i].fileIdx & 0xFFFF, + addrToLineInfo[i].line & 0xFFFFFFFF); + symbolfile += addrToLineStr; + } + + } else if (format == "nocash") { + symbolfile = ";no$sns symbolic information file\n"; + symbolfile += ";generated by asar\n"; + symbolfile += "\n"; + labels.each(printsymbol_nocash); + } + return symbolfile; } -bool in_top_level_file() -{ - int num_files = 0; - for (int i = callstack.count-1; i >= 0; --i) - { - if (callstack[i].type == callstack_entry_type::FILE) - { - num_files++; - if (num_files > 1) break; - } - } - return (num_files <= 1); +bool in_top_level_file() { + int num_files = 0; + for (int i = callstack.count - 1; i >= 0; --i) { + if (callstack[i].type == callstack_entry_type::FILE) { + num_files++; + if (num_files > 1) break; + } + } + return (num_files <= 1); } -const char* get_current_file_name() -{ - for (int i = callstack.count-1; i >= 0; --i) - { - if (callstack[i].type == callstack_entry_type::FILE) - return callstack[i].content.raw(); - } - return nullptr; +const char* get_current_file_name() { + for (int i = callstack.count - 1; i >= 0; --i) { + if (callstack[i].type == callstack_entry_type::FILE) + return callstack[i].content.raw(); + } + return nullptr; } -int get_current_line() -{ - for (int i = callstack.count-1; i >= 0; --i) - { - if (callstack[i].type == callstack_entry_type::LINE) return callstack[i].lineno; - } - return -1; +int get_current_line() { + for (int i = callstack.count - 1; i >= 0; --i) { + if (callstack[i].type == callstack_entry_type::LINE) return callstack[i].lineno; + } + return -1; } -const char* get_current_block() -{ - for (int i = callstack.count-1; i >= 0; --i) - { - if (callstack[i].type == callstack_entry_type::LINE || callstack[i].type == callstack_entry_type::BLOCK) return callstack[i].content.raw(); - } - return nullptr; +const char* get_current_block() { + for (int i = callstack.count - 1; i >= 0; --i) { + if (callstack[i].type == callstack_entry_type::LINE || + callstack[i].type == callstack_entry_type::BLOCK) + return callstack[i].content.raw(); + } + return nullptr; } -void reseteverything() -{ - string str; - labels.reset(); - defines.reset(); - builtindefines.each(adddefine); - clidefines.each(adddefine); - structs.reset(); - - macros.each(clearmacro); - macros.reset(); - - filecontents.each(clearfile); - filecontents.reset(); - - writtenblocks.reset(); - - optimizeforbank=-1; - optimize_dp = optimize_dp_flag::NONE; - dp_base = 0; - optimize_address = optimize_address_flag::DEFAULT; - - closecachedfiles(); - - incsrcdepth=0; - errored = false; - checksum_fix_enabled = true; - force_checksum_fix = false; - - in_macro_def = 0; - - #ifndef ASAR_SHARED - free(const_cast(romdata_r)); - #endif - - callstack.reset(); - simple_callstacks = true; +void reseteverything() { + string str; + labels.reset(); + defines.reset(); + builtindefines.each(adddefine); + clidefines.each(adddefine); + structs.reset(); + + macros.each(clearmacro); + macros.reset(); + + filecontents.each(clearfile); + filecontents.reset(); + + writtenblocks.reset(); + + optimizeforbank = -1; + optimize_dp = optimize_dp_flag::NONE; + dp_base = 0; + optimize_address = optimize_address_flag::DEFAULT; + + closecachedfiles(); + + incsrcdepth = 0; + errored = false; + checksum_fix_enabled = true; + force_checksum_fix = false; + + in_macro_def = 0; + +#ifndef ASAR_SHARED + free(const_cast(romdata_r)); +#endif + + callstack.reset(); + simple_callstacks = true; #undef free } diff --git a/src/asar/platform/file-helpers.cpp b/src/asar/platform/file-helpers.cpp index ad937cb3..cba45003 100644 --- a/src/asar/platform/file-helpers.cpp +++ b/src/asar/platform/file-helpers.cpp @@ -2,8 +2,8 @@ // This function is based in part on nall, which is under the following licence. -// This modified version is licenced under the LGPL version 3 or later. See the LICENSE file -// for details. +// This modified version is licenced under the LGPL version 3 or later. See the LICENSE +// file for details. // // Copyright (c) 2006-2015 byuu // @@ -20,32 +20,28 @@ // PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER // TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR // PERFORMANCE OF THIS SOFTWARE. -string dir(char const *name) -{ - for (signed i = (int)strlen(name); i >= 0; i--) - { - if (name[i] == '/' || name[i] == '\\') - { - return string(name, i+1); - } - } - return ""; +string dir(char const* name) { + for (signed i = (int)strlen(name); i >= 0; i--) { + if (name[i] == '/' || name[i] == '\\') { + return string(name, i + 1); + } + } + return ""; } -string create_combined_path(const char* path1, const char* path2) -{ - string combined = path1; +string create_combined_path(const char* path1, const char* path2) { + string combined = path1; - int length = combined.length(); + int length = combined.length(); - if (combined.length() > 0 && path1[length - 1] != '\\' && path1[length - 1] != '/') - { - combined += get_native_path_separator(); - } + if (combined.length() > 0 && path1[length - 1] != '\\' && + path1[length - 1] != '/') { + combined += get_native_path_separator(); + } - combined += path2; + combined += path2; - return normalize_path(combined); + return normalize_path(combined); } @@ -76,162 +72,137 @@ string create_combined_path(const char* path1, const char* path2) // patch/main.asm // (However, a path like that is invalid, anyways, so // this hopefully doesn't matter). -string normalize_path(const char* path) -{ - string normalized = path; - - - // RPG Hacker: Replace all \ in path by / - // (Windows should work with / in paths just fine). - // Note: will likely break network paths on Windows, - // which still expect a prefix of \\, but does anyone - // seriously even use them with Asar? - for (int i = 0; i < normalized.length(); ++i) - { - if (normalized[i] == '\\') - { - normalized.temp_raw()[i]= '/'; - } - } - - // RPG Hacker: Collapse path by removing all unnecessary - // . or .. path components. - // As a little hack, just overwrite all the stuff we're - // going to remove with 0x01 and remove it later. Probably - // easier than to always shorten the path immediately. - // Also using \x01 instead of \0 so that the path is still - // easily readable in a debugger (since it normally just - // assumes a string to end at \0). I don't think \x01 can - // be used in valid paths, so this should be okay. - char* previous_dir_start_pos = nullptr; - char* current_dir_start_pos = normalized.temp_raw(); - while (*current_dir_start_pos == '/') - { - ++current_dir_start_pos; - } - char* current_dir_end_pos = current_dir_start_pos; - - - while (*current_dir_start_pos != '\0') - { - while (*current_dir_end_pos != '/' && *current_dir_end_pos != '\0') - { - ++current_dir_end_pos; - } - - size_t length = (size_t)(current_dir_end_pos - current_dir_start_pos); - if (length > 0 && strncmp(current_dir_start_pos, ".", length) == 0) - { - // Found a . path component - remove it. - while (current_dir_start_pos != current_dir_end_pos) - { - *current_dir_start_pos = '\x01'; - ++current_dir_start_pos; - } - - if (*current_dir_start_pos == '/') - { - *current_dir_start_pos = '\x01'; - ++current_dir_end_pos; - } - } - else if (length > 0 && strncmp(current_dir_start_pos, "..", length) == 0) - { - // Found a .. path component - if we have any known - // folder before it, remove them both. - if (previous_dir_start_pos != nullptr) - { - // previous_dir_start_pos and current_dir_start_pos are immediately - // set to something else shortly after, so it should be - // okay to move them directly here and not use a - // temporary variable. - while (*previous_dir_start_pos != '/' && *previous_dir_start_pos != '\0') - { - *previous_dir_start_pos = '\x01'; - ++previous_dir_start_pos; - } - - if (*previous_dir_start_pos == '/') - { - *previous_dir_start_pos = '\x01'; - } - - while (current_dir_start_pos != current_dir_end_pos) - { - *current_dir_start_pos = '\x01'; - ++current_dir_start_pos; - } - - if (*current_dir_start_pos == '/') - { - *current_dir_start_pos = '\x01'; - ++current_dir_end_pos; - } - } - - previous_dir_start_pos = nullptr; - } - else - { - if (*current_dir_end_pos == '/') - { - ++current_dir_end_pos; - } - - if (length > 0) - { - previous_dir_start_pos = current_dir_start_pos; - } - } - - current_dir_start_pos = current_dir_end_pos; - } - - - // Now construct our new string by copying everything but the \x01 into it. - string copy; - - for (int i = 0; i < normalized.length(); ++i) - { - if (normalized[i] != '\x01') - { - copy += normalized[i]; - } - } - - normalized = copy; - - - // RPG Hacker: On Windows, file paths are case-insensitive. - // It isn't really good style to mix different cases, especially - // when referring to the same file, but it's theoretically possible - // to do so, so we need to handle it and not have Asar fail in this case. - // The easiest way to handle it is to convert the entire path to lowercase. +string normalize_path(const char* path) { + string normalized = path; + + + // RPG Hacker: Replace all \ in path by / + // (Windows should work with / in paths just fine). + // Note: will likely break network paths on Windows, + // which still expect a prefix of \\, but does anyone + // seriously even use them with Asar? + for (int i = 0; i < normalized.length(); ++i) { + if (normalized[i] == '\\') { + normalized.temp_raw()[i] = '/'; + } + } + + // RPG Hacker: Collapse path by removing all unnecessary + // . or .. path components. + // As a little hack, just overwrite all the stuff we're + // going to remove with 0x01 and remove it later. Probably + // easier than to always shorten the path immediately. + // Also using \x01 instead of \0 so that the path is still + // easily readable in a debugger (since it normally just + // assumes a string to end at \0). I don't think \x01 can + // be used in valid paths, so this should be okay. + char* previous_dir_start_pos = nullptr; + char* current_dir_start_pos = normalized.temp_raw(); + while (*current_dir_start_pos == '/') { + ++current_dir_start_pos; + } + char* current_dir_end_pos = current_dir_start_pos; + + + while (*current_dir_start_pos != '\0') { + while (*current_dir_end_pos != '/' && *current_dir_end_pos != '\0') { + ++current_dir_end_pos; + } + + size_t length = (size_t)(current_dir_end_pos - current_dir_start_pos); + if (length > 0 && strncmp(current_dir_start_pos, ".", length) == 0) { + // Found a . path component - remove it. + while (current_dir_start_pos != current_dir_end_pos) { + *current_dir_start_pos = '\x01'; + ++current_dir_start_pos; + } + + if (*current_dir_start_pos == '/') { + *current_dir_start_pos = '\x01'; + ++current_dir_end_pos; + } + } else if (length > 0 && strncmp(current_dir_start_pos, "..", length) == 0) { + // Found a .. path component - if we have any known + // folder before it, remove them both. + if (previous_dir_start_pos != nullptr) { + // previous_dir_start_pos and current_dir_start_pos are immediately + // set to something else shortly after, so it should be + // okay to move them directly here and not use a + // temporary variable. + while (*previous_dir_start_pos != '/' && + *previous_dir_start_pos != '\0') { + *previous_dir_start_pos = '\x01'; + ++previous_dir_start_pos; + } + + if (*previous_dir_start_pos == '/') { + *previous_dir_start_pos = '\x01'; + } + + while (current_dir_start_pos != current_dir_end_pos) { + *current_dir_start_pos = '\x01'; + ++current_dir_start_pos; + } + + if (*current_dir_start_pos == '/') { + *current_dir_start_pos = '\x01'; + ++current_dir_end_pos; + } + } + + previous_dir_start_pos = nullptr; + } else { + if (*current_dir_end_pos == '/') { + ++current_dir_end_pos; + } + + if (length > 0) { + previous_dir_start_pos = current_dir_start_pos; + } + } + + current_dir_start_pos = current_dir_end_pos; + } + + + // Now construct our new string by copying everything but the \x01 into it. + string copy; + + for (int i = 0; i < normalized.length(); ++i) { + if (normalized[i] != '\x01') { + copy += normalized[i]; + } + } + + normalized = copy; + + + // RPG Hacker: On Windows, file paths are case-insensitive. + // It isn't really good style to mix different cases, especially + // when referring to the same file, but it's theoretically possible + // to do so, so we need to handle it and not have Asar fail in this case. + // The easiest way to handle it is to convert the entire path to lowercase. #if defined(windows) - normalized = lower(normalized); + normalized = lower(normalized); #endif - // TODO: Maybe resolve symbolic links here? - // Not sure if it's a good idea and if it's needed in the first place. - // In theory, it could lead to some problems with files that exist - // only in memory. - return normalized; + // TODO: Maybe resolve symbolic links here? + // Not sure if it's a good idea and if it's needed in the first place. + // In theory, it could lead to some problems with files that exist + // only in memory. + return normalized; } -string get_base_name(char const *name) -{ - for(int i = (int)strlen(name); i >= 0; --i) - { - if(name[i] == '/' || name[i] == '\\') - { - //file has no extension - break; - } - if(name[i] == '.') - { - return string(name, i); - } - } - return string(name); +string get_base_name(char const* name) { + for (int i = (int)strlen(name); i >= 0; --i) { + if (name[i] == '/' || name[i] == '\\') { + // file has no extension + break; + } + if (name[i] == '.') { + return string(name, i); + } + } + return string(name); } diff --git a/src/asar/platform/file-helpers.h b/src/asar/platform/file-helpers.h index 9fc1f28c..fe8bf93a 100644 --- a/src/asar/platform/file-helpers.h +++ b/src/asar/platform/file-helpers.h @@ -1,53 +1,52 @@ #pragma once -#include "libstr.h" - #include +#include "libstr.h" + // Functions in this file expect UTF-8 file paths. #if defined(windows) - // RPG Hacker: These should be HANDLE rather than void*, but I don't feel like - // including Windows.h in here, just for these typedefs. - typedef void* FileHandleType; - const FileHandleType InvalidFileHandle = (void*)(size_t)-1; +// RPG Hacker: These should be HANDLE rather than void*, but I don't feel like +// including Windows.h in here, just for these typedefs. +typedef void* FileHandleType; +const FileHandleType InvalidFileHandle = (void*)(size_t)-1; #elif defined(linux) - typedef FILE* FileHandleType; - const FileHandleType InvalidFileHandle = nullptr; +typedef FILE* FileHandleType; +const FileHandleType InvalidFileHandle = nullptr; #else - typedef FILE* FileHandleType; - const FileHandleType InvalidFileHandle = nullptr; +typedef FILE* FileHandleType; +const FileHandleType InvalidFileHandle = nullptr; #endif // RPG Hacker: Read/write is currently only used for handling the ROM. // I could have made this a bit-mask, but since we only have these three modes, // I think this is the nicer-looking solution. -enum FileOpenMode -{ - FileOpenMode_Read, - FileOpenMode_Write, - FileOpenMode_ReadWrite, +enum FileOpenMode { + FileOpenMode_Read, + FileOpenMode_Write, + FileOpenMode_ReadWrite, }; -enum FileOpenError -{ - FileOpenError_None, - FileOpenError_NotFound, - FileOpenError_AccessDenied, - FileOpenError_Unknown, +enum FileOpenError { + FileOpenError_None, + FileOpenError_NotFound, + FileOpenError_AccessDenied, + FileOpenError_Unknown, }; -bool file_exists(const char * filename); +bool file_exists(const char* filename); bool path_is_absolute(const char* path); char get_native_path_separator(); bool check_is_regular_file(const char* path); -string dir(char const *name); +string dir(char const* name); string create_combined_path(const char* path1, const char* path2); string normalize_path(const char* path); -string get_base_name(char const *name); +string get_base_name(char const* name); -FileHandleType open_file(const char* path, FileOpenMode mode, FileOpenError* error = nullptr); +FileHandleType open_file(const char* path, FileOpenMode mode, + FileOpenError* error = nullptr); void close_file(FileHandleType handle); uint64_t get_file_size(FileHandleType handle); void set_file_pos(FileHandleType handle, uint64_t pos); diff --git a/src/asar/platform/generic/file-helpers-generic.cpp b/src/asar/platform/generic/file-helpers-generic.cpp index a4214bc8..c90463ef 100644 --- a/src/asar/platform/generic/file-helpers-generic.cpp +++ b/src/asar/platform/generic/file-helpers-generic.cpp @@ -1,120 +1,101 @@ -#include "platform/file-helpers.h" #include -bool file_exists(const char * filename) -{ - FILE * f = fopen(filename, "rb"); - if (f) fclose(f); - return f; -} +#include "platform/file-helpers.h" -bool path_is_absolute(const char* path) -{ - return ('/' == path[0]); +bool file_exists(const char* filename) { + FILE* f = fopen(filename, "rb"); + if (f) fclose(f); + return f; } -char get_native_path_separator() -{ - return '/'; -} +bool path_is_absolute(const char* path) { return ('/' == path[0]); } + +char get_native_path_separator() { return '/'; } -bool check_is_regular_file(const char* path) -{ - // no really universal way to check this - return true; +bool check_is_regular_file(const char* path) { + // no really universal way to check this + return true; } -FileHandleType open_file(const char* path, FileOpenMode mode, FileOpenError* error) -{ - FILE* out_handle = NULL; - const char* open_mode; - - switch (mode) - { - case FileOpenMode_ReadWrite: - open_mode = "r+b"; - break; - case FileOpenMode_Read: - open_mode = "rb"; - break; - case FileOpenMode_Write: - open_mode = "wb"; - break; - } - - out_handle = fopen(path, open_mode); - - if (error != NULL) - { - if (out_handle != NULL) - { - *error = FileOpenError_None; - } - else - { - int fopen_error = errno; - - switch (fopen_error) - { - case ENOENT: - *error = FileOpenError_NotFound; - break; - case EACCES: - *error = FileOpenError_AccessDenied; - break; - default: - *error = FileOpenError_Unknown; - break; - } - } - } - - return out_handle; +FileHandleType open_file(const char* path, FileOpenMode mode, FileOpenError* error) { + FILE* out_handle = NULL; + const char* open_mode; + + switch (mode) { + case FileOpenMode_ReadWrite: + open_mode = "r+b"; + break; + case FileOpenMode_Read: + open_mode = "rb"; + break; + case FileOpenMode_Write: + open_mode = "wb"; + break; + } + + out_handle = fopen(path, open_mode); + + if (error != NULL) { + if (out_handle != NULL) { + *error = FileOpenError_None; + } else { + int fopen_error = errno; + + switch (fopen_error) { + case ENOENT: + *error = FileOpenError_NotFound; + break; + case EACCES: + *error = FileOpenError_AccessDenied; + break; + default: + *error = FileOpenError_Unknown; + break; + } + } + } + + return out_handle; } -void close_file(FileHandleType handle) -{ - if (handle == InvalidFileHandle) return; +void close_file(FileHandleType handle) { + if (handle == InvalidFileHandle) return; - fclose(handle); + fclose(handle); } -uint64_t get_file_size(FileHandleType handle) -{ - if (handle == InvalidFileHandle) return 0u; +uint64_t get_file_size(FileHandleType handle) { + if (handle == InvalidFileHandle) return 0u; - long int f_pos = ftell(handle); - fseek(handle, 0, SEEK_END); - long int f_size = ftell(handle); - fseek(handle, f_pos, SEEK_SET); + long int f_pos = ftell(handle); + fseek(handle, 0, SEEK_END); + long int f_size = ftell(handle); + fseek(handle, f_pos, SEEK_SET); - return (uint64_t)f_size; + return (uint64_t)f_size; } -void set_file_pos(FileHandleType handle, uint64_t pos) -{ - if (handle == InvalidFileHandle) return; +void set_file_pos(FileHandleType handle, uint64_t pos) { + if (handle == InvalidFileHandle) return; - // TODO: Some error handling would be wise here. + // TODO: Some error handling would be wise here. - fseek(handle, pos, SEEK_SET); + fseek(handle, pos, SEEK_SET); } -uint32_t read_file(FileHandleType handle, void* buffer, uint32_t num_bytes) -{ - if (handle == InvalidFileHandle) return 0u; +uint32_t read_file(FileHandleType handle, void* buffer, uint32_t num_bytes) { + if (handle == InvalidFileHandle) return 0u; - // TODO: Some error handling would be wise here. + // TODO: Some error handling would be wise here. - return (uint32_t)fread(buffer, 1, num_bytes, handle); + return (uint32_t)fread(buffer, 1, num_bytes, handle); } -uint32_t write_file(FileHandleType handle, const void* buffer, uint32_t num_bytes) -{ - if (handle == InvalidFileHandle) return 0u; +uint32_t write_file(FileHandleType handle, const void* buffer, uint32_t num_bytes) { + if (handle == InvalidFileHandle) return 0u; - // TODO: Some error handling would be wise here. + // TODO: Some error handling would be wise here. - return (uint32_t)fwrite(buffer, 1, num_bytes, handle); + return (uint32_t)fwrite(buffer, 1, num_bytes, handle); } diff --git a/src/asar/platform/generic/thread-helpers-pthread.h b/src/asar/platform/generic/thread-helpers-pthread.h index 5db856bf..777a1a47 100644 --- a/src/asar/platform/generic/thread-helpers-pthread.h +++ b/src/asar/platform/generic/thread-helpers-pthread.h @@ -1,73 +1,74 @@ #pragma once -#include #include +#include struct function_pointer_wrapper /*have this struct at global level*/ { - static void* (*thread_callback)(void*); - static void* execute_thread(void* parameter) { return thread_callback(parameter); } + static void* (*thread_callback)(void*); + static void* execute_thread(void* parameter) { return thread_callback(parameter); } }; void* (*function_pointer_wrapper::thread_callback)(void*) = nullptr; template bool run_as_thread(functor&& callback) { - struct thread_wrapper { - functor& callback; - bool result; - - void* execute() { - result = callback(); - if (result) - pthread_exit(NULL); - else - pthread_exit(reinterpret_cast((uintptr_t)-1)); - } - - } wrapper{ callback, false }; - - function_pointer_wrapper::thread_callback = [](void* parameter) { - return reinterpret_cast(parameter)->execute(); - }; - - pthread_attr_t settings; - pthread_t thread_id; - int ret; - - ret = pthread_attr_init(&settings); - if (ret == -1){ - return callback(); - } - - ret = pthread_attr_setstacksize(&settings, 16 * 1024 * 1024); - if (ret == -1){ - return callback(); - } - - ret = pthread_create(&thread_id, &settings, &function_pointer_wrapper::execute_thread, &wrapper); - if (ret == -1){ - return callback(); - } - - void* thread_ret; - pthread_join(thread_id, &thread_ret); - - return wrapper.result; + struct thread_wrapper { + functor& callback; + bool result; + + void* execute() { + result = callback(); + if (result) + pthread_exit(NULL); + else + pthread_exit(reinterpret_cast((uintptr_t)-1)); + } + + } wrapper{callback, false}; + + function_pointer_wrapper::thread_callback = [](void* parameter) { + return reinterpret_cast(parameter)->execute(); + }; + + pthread_attr_t settings; + pthread_t thread_id; + int ret; + + ret = pthread_attr_init(&settings); + if (ret == -1) { + return callback(); + } + + ret = pthread_attr_setstacksize(&settings, 16 * 1024 * 1024); + if (ret == -1) { + return callback(); + } + + ret = pthread_create(&thread_id, &settings, + &function_pointer_wrapper::execute_thread, &wrapper); + if (ret == -1) { + return callback(); + } + + void* thread_ret; + pthread_join(thread_id, &thread_ret); + + return wrapper.result; } #ifndef NO_USE_THREADS size_t check_stack_left() { - pthread_attr_t attrs; - void *stack_start; - size_t stack_size; + pthread_attr_t attrs; + void* stack_start; + size_t stack_size; - pthread_getattr_np(pthread_self(), &attrs); - pthread_attr_getstack(&attrs, &stack_start, &stack_size); - pthread_attr_destroy(&attrs); + pthread_getattr_np(pthread_self(), &attrs); + pthread_attr_getstack(&attrs, &stack_start, &stack_size); + pthread_attr_destroy(&attrs); - // using a random local as a rough estimate for current top-of-stack - size_t stack_left = (char*)&stack_size - (char*)stack_start; - return stack_left; + // using a random local as a rough estimate for current top-of-stack + size_t stack_left = (char*)&stack_size - (char*)stack_start; + return stack_left; } #endif diff --git a/src/asar/platform/linux/file-helpers-linux.cpp b/src/asar/platform/linux/file-helpers-linux.cpp index f0700776..e5e0f6b1 100644 --- a/src/asar/platform/linux/file-helpers-linux.cpp +++ b/src/asar/platform/linux/file-helpers-linux.cpp @@ -1,126 +1,105 @@ -#include "platform/file-helpers.h" -#include #include +#include -bool file_exists(const char * filename) -{ - struct stat st; - return (!stat(filename, &st)); -} +#include "platform/file-helpers.h" -bool path_is_absolute(const char* path) -{ - return ('/' == path[0]); +bool file_exists(const char* filename) { + struct stat st; + return (!stat(filename, &st)); } -char get_native_path_separator() -{ - return '/'; -} +bool path_is_absolute(const char* path) { return ('/' == path[0]); } + +char get_native_path_separator() { return '/'; } -bool check_is_regular_file(const char* path) -{ - struct stat finfo; - if (stat(path, &finfo) == 0) - { - // either regular file or symlink - if (finfo.st_mode & (S_IFREG | S_IFLNK)) - return true; - } - return false; +bool check_is_regular_file(const char* path) { + struct stat finfo; + if (stat(path, &finfo) == 0) { + // either regular file or symlink + if (finfo.st_mode & (S_IFREG | S_IFLNK)) return true; + } + return false; } -FileHandleType open_file(const char* path, FileOpenMode mode, FileOpenError* error) -{ - FILE* out_handle = NULL; - const char* open_mode; - - switch (mode) - { - case FileOpenMode_ReadWrite: - open_mode = "r+b"; - break; - case FileOpenMode_Read: - open_mode = "rb"; - break; - case FileOpenMode_Write: - open_mode = "wb"; - break; - } - - out_handle = fopen(path, open_mode); - - if (error != NULL) - { - if (out_handle != NULL) - { - *error = FileOpenError_None; - } - else - { - int fopen_error = errno; - - switch (fopen_error) - { - case ENOENT: - *error = FileOpenError_NotFound; - break; - case EACCES: - *error = FileOpenError_AccessDenied; - break; - default: - *error = FileOpenError_Unknown; - break; - } - } - } - - return out_handle; +FileHandleType open_file(const char* path, FileOpenMode mode, FileOpenError* error) { + FILE* out_handle = NULL; + const char* open_mode; + + switch (mode) { + case FileOpenMode_ReadWrite: + open_mode = "r+b"; + break; + case FileOpenMode_Read: + open_mode = "rb"; + break; + case FileOpenMode_Write: + open_mode = "wb"; + break; + } + + out_handle = fopen(path, open_mode); + + if (error != NULL) { + if (out_handle != NULL) { + *error = FileOpenError_None; + } else { + int fopen_error = errno; + + switch (fopen_error) { + case ENOENT: + *error = FileOpenError_NotFound; + break; + case EACCES: + *error = FileOpenError_AccessDenied; + break; + default: + *error = FileOpenError_Unknown; + break; + } + } + } + + return out_handle; } -void close_file(FileHandleType handle) -{ - if (handle == InvalidFileHandle) return; +void close_file(FileHandleType handle) { + if (handle == InvalidFileHandle) return; - fclose(handle); + fclose(handle); } -uint64_t get_file_size(FileHandleType handle) -{ - if (handle == InvalidFileHandle) return 0u; +uint64_t get_file_size(FileHandleType handle) { + if (handle == InvalidFileHandle) return 0u; - long int f_pos = ftell(handle); - fseek(handle, 0, SEEK_END); - long int f_size = ftell(handle); - fseek(handle, f_pos, SEEK_SET); + long int f_pos = ftell(handle); + fseek(handle, 0, SEEK_END); + long int f_size = ftell(handle); + fseek(handle, f_pos, SEEK_SET); - return (uint64_t)f_size; + return (uint64_t)f_size; } -void set_file_pos(FileHandleType handle, uint64_t pos) -{ - if (handle == InvalidFileHandle) return; +void set_file_pos(FileHandleType handle, uint64_t pos) { + if (handle == InvalidFileHandle) return; - // TODO: Some error handling would be wise here. + // TODO: Some error handling would be wise here. - fseek(handle, pos, SEEK_SET); + fseek(handle, pos, SEEK_SET); } -uint32_t read_file(FileHandleType handle, void* buffer, uint32_t num_bytes) -{ - if (handle == InvalidFileHandle) return 0u; +uint32_t read_file(FileHandleType handle, void* buffer, uint32_t num_bytes) { + if (handle == InvalidFileHandle) return 0u; - // TODO: Some error handling would be wise here. + // TODO: Some error handling would be wise here. - return (uint32_t)fread(buffer, 1, num_bytes, handle); + return (uint32_t)fread(buffer, 1, num_bytes, handle); } -uint32_t write_file(FileHandleType handle, const void* buffer, uint32_t num_bytes) -{ - if (handle == InvalidFileHandle) return 0u; +uint32_t write_file(FileHandleType handle, const void* buffer, uint32_t num_bytes) { + if (handle == InvalidFileHandle) return 0u; - // TODO: Some error handling would be wise here. + // TODO: Some error handling would be wise here. - return (uint32_t)fwrite(buffer, 1, num_bytes, handle); + return (uint32_t)fwrite(buffer, 1, num_bytes, handle); } diff --git a/src/asar/platform/thread-helpers.h b/src/asar/platform/thread-helpers.h index 32905ca1..73b24ba8 100644 --- a/src/asar/platform/thread-helpers.h +++ b/src/asar/platform/thread-helpers.h @@ -1,7 +1,7 @@ #pragma once #if defined(windows) -# include "windows/thread-helpers-win32.h" +#include "windows/thread-helpers-win32.h" #else -# include "generic/thread-helpers-pthread.h" +#include "generic/thread-helpers-pthread.h" #endif diff --git a/src/asar/platform/windows/file-helpers-win32.cpp b/src/asar/platform/windows/file-helpers-win32.cpp index fa870d71..98f91df9 100644 --- a/src/asar/platform/windows/file-helpers-win32.cpp +++ b/src/asar/platform/windows/file-helpers-win32.cpp @@ -3,167 +3,151 @@ //#include "platform/file-helpers.h" #if defined(_MSC_VER) -# pragma warning(push) -# pragma warning(disable : 4668) -# pragma warning(disable : 4987) +#pragma warning(push) +#pragma warning(disable : 4668) +#pragma warning(disable : 4987) #endif #define NOMINMAX -#include #include +#include #if defined(_MSC_VER) -# pragma warning(pop) +#pragma warning(pop) #endif #include -#include "../file-helpers.h" #include "../../unicode.h" +#include "../file-helpers.h" -bool file_exists(const char * filename) -{ - std::wstring u16_str; - if (!utf8_to_utf16(&u16_str, filename)) return false; - return (GetFileAttributesW(u16_str.c_str()) != INVALID_FILE_ATTRIBUTES); +bool file_exists(const char* filename) { + std::wstring u16_str; + if (!utf8_to_utf16(&u16_str, filename)) return false; + return (GetFileAttributesW(u16_str.c_str()) != INVALID_FILE_ATTRIBUTES); } -bool path_is_absolute(const char* path) -{ - return ((isalpha(path[0]) && - (':' == path[1]) && (('\\' == path[2]) || ('/' == path[2]))) /* drive letter + root, e.g. C:\dir or C:/dir */ - || ('\\' == path[0]) || ('/' == path[0])); /* just root, e.g. \dir or /dir */ +bool path_is_absolute(const char* path) { + return ((isalpha(path[0]) && (':' == path[1]) && + (('\\' == path[2]) || + ('/' == path[2]))) /* drive letter + root, e.g. C:\dir or C:/dir */ + || ('\\' == path[0]) || + ('/' == path[0])); /* just root, e.g. \dir or /dir */ } -char get_native_path_separator() -{ - return '\\'; -} +char get_native_path_separator() { return '\\'; } -bool check_is_regular_file(const char* path) -{ - std::wstring u16_str; - if (!utf8_to_utf16(&u16_str, path)) return false; - return !(GetFileAttributesW(u16_str.c_str()) & FILE_ATTRIBUTE_DIRECTORY); +bool check_is_regular_file(const char* path) { + std::wstring u16_str; + if (!utf8_to_utf16(&u16_str, path)) return false; + return !(GetFileAttributesW(u16_str.c_str()) & FILE_ATTRIBUTE_DIRECTORY); } -FileHandleType open_file(const char* path, FileOpenMode mode, FileOpenError* error) -{ - HANDLE out_handle = NULL; - DWORD access_mode = 0; - DWORD share_mode = 0; - DWORD disposition = 0; - - switch (mode) - { - case FileOpenMode_ReadWrite: - access_mode = GENERIC_READ | GENERIC_WRITE; - disposition = OPEN_EXISTING; - break; - case FileOpenMode_Read: - access_mode = GENERIC_READ; - // This should be fine, right? - share_mode = FILE_SHARE_READ; - disposition = OPEN_EXISTING; - break; - case FileOpenMode_Write: - access_mode = GENERIC_WRITE; - disposition = CREATE_ALWAYS; - break; - } - - std::wstring u16_str; - if (!utf8_to_utf16(&u16_str, path)) - { - // RPG Hacker: I treat encoding error as "file not found", which I guess is what - // Windows would do, anyways. - *error = FileOpenError_NotFound; - return INVALID_HANDLE_VALUE; - } - - out_handle = CreateFileW(u16_str.c_str(), access_mode, share_mode, NULL, disposition, FILE_ATTRIBUTE_NORMAL, NULL); - - if (error != NULL) - { - if (out_handle != INVALID_HANDLE_VALUE) - { - *error = FileOpenError_None; - } - else - { - DWORD win_error = GetLastError(); - - switch (win_error) - { - case ERROR_FILE_NOT_FOUND: - *error = FileOpenError_NotFound; - break; - case ERROR_ACCESS_DENIED: - *error = FileOpenError_AccessDenied; - break; - default: - *error = FileOpenError_Unknown; - break; - } - } - } - - return out_handle; +FileHandleType open_file(const char* path, FileOpenMode mode, FileOpenError* error) { + HANDLE out_handle = NULL; + DWORD access_mode = 0; + DWORD share_mode = 0; + DWORD disposition = 0; + + switch (mode) { + case FileOpenMode_ReadWrite: + access_mode = GENERIC_READ | GENERIC_WRITE; + disposition = OPEN_EXISTING; + break; + case FileOpenMode_Read: + access_mode = GENERIC_READ; + // This should be fine, right? + share_mode = FILE_SHARE_READ; + disposition = OPEN_EXISTING; + break; + case FileOpenMode_Write: + access_mode = GENERIC_WRITE; + disposition = CREATE_ALWAYS; + break; + } + + std::wstring u16_str; + if (!utf8_to_utf16(&u16_str, path)) { + // RPG Hacker: I treat encoding error as "file not found", which I guess is what + // Windows would do, anyways. + *error = FileOpenError_NotFound; + return INVALID_HANDLE_VALUE; + } + + out_handle = CreateFileW(u16_str.c_str(), access_mode, share_mode, NULL, + disposition, FILE_ATTRIBUTE_NORMAL, NULL); + + if (error != NULL) { + if (out_handle != INVALID_HANDLE_VALUE) { + *error = FileOpenError_None; + } else { + DWORD win_error = GetLastError(); + + switch (win_error) { + case ERROR_FILE_NOT_FOUND: + *error = FileOpenError_NotFound; + break; + case ERROR_ACCESS_DENIED: + *error = FileOpenError_AccessDenied; + break; + default: + *error = FileOpenError_Unknown; + break; + } + } + } + + return out_handle; } -void close_file(FileHandleType handle) -{ - if (handle == InvalidFileHandle) return; +void close_file(FileHandleType handle) { + if (handle == InvalidFileHandle) return; - CloseHandle(handle); + CloseHandle(handle); } -uint64_t get_file_size(FileHandleType handle) -{ - if (handle == InvalidFileHandle) return 0u; +uint64_t get_file_size(FileHandleType handle) { + if (handle == InvalidFileHandle) return 0u; - LARGE_INTEGER f_size; + LARGE_INTEGER f_size; - if (!GetFileSizeEx(handle, &f_size)) return 0u; + if (!GetFileSizeEx(handle, &f_size)) return 0u; - return (uint64_t)f_size.QuadPart; + return (uint64_t)f_size.QuadPart; } -void set_file_pos(FileHandleType handle, uint64_t pos) -{ - if (handle == InvalidFileHandle) return; +void set_file_pos(FileHandleType handle, uint64_t pos) { + if (handle == InvalidFileHandle) return; - // TODO: Some error handling would be wise here. + // TODO: Some error handling would be wise here. - LARGE_INTEGER new_pos; - new_pos.QuadPart = (LONGLONG)pos; + LARGE_INTEGER new_pos; + new_pos.QuadPart = (LONGLONG)pos; - SetFilePointerEx(handle, new_pos, NULL, FILE_BEGIN); + SetFilePointerEx(handle, new_pos, NULL, FILE_BEGIN); } -uint32_t read_file(FileHandleType handle, void* buffer, uint32_t num_bytes) -{ - if (handle == InvalidFileHandle) return 0u; +uint32_t read_file(FileHandleType handle, void* buffer, uint32_t num_bytes) { + if (handle == InvalidFileHandle) return 0u; - DWORD bytes_read = 0u; + DWORD bytes_read = 0u; - // TODO: Some error handling would be wise here. + // TODO: Some error handling would be wise here. - ReadFile(handle, buffer, (DWORD)num_bytes, &bytes_read, NULL); + ReadFile(handle, buffer, (DWORD)num_bytes, &bytes_read, NULL); - return (uint32_t)bytes_read; + return (uint32_t)bytes_read; } -uint32_t write_file(FileHandleType handle, const void* buffer, uint32_t num_bytes) -{ - if (handle == InvalidFileHandle) return 0u; +uint32_t write_file(FileHandleType handle, const void* buffer, uint32_t num_bytes) { + if (handle == InvalidFileHandle) return 0u; - DWORD bytes_written = 0u; + DWORD bytes_written = 0u; - // TODO: Some error handling would be wise here. + // TODO: Some error handling would be wise here. - WriteFile(handle, buffer, (DWORD)num_bytes, &bytes_written, NULL); + WriteFile(handle, buffer, (DWORD)num_bytes, &bytes_written, NULL); - return (uint32_t)bytes_written; + return (uint32_t)bytes_written; } diff --git a/src/asar/platform/windows/thread-helpers-win32.h b/src/asar/platform/windows/thread-helpers-win32.h index 388f5c00..2cfa5307 100644 --- a/src/asar/platform/windows/thread-helpers-win32.h +++ b/src/asar/platform/windows/thread-helpers-win32.h @@ -3,91 +3,100 @@ #if defined(_WIN32) #include + +// windows.h must be called first #include struct function_pointer_wrapper /*have this struct at global level*/ { - static void (*fiber_callback)(void *); - static void __stdcall execute_fiber(void* parameter) { return fiber_callback(parameter); } - - static unsigned long (*thread_callback)(void*); - static unsigned long __stdcall execute_thread(void* parameter) { return thread_callback(parameter); } + static void (*fiber_callback)(void*); + static void __stdcall execute_fiber(void* parameter) { + return fiber_callback(parameter); + } + + static unsigned long (*thread_callback)(void*); + static unsigned long __stdcall execute_thread(void* parameter) { + return thread_callback(parameter); + } }; void (*function_pointer_wrapper::fiber_callback)(void*) = nullptr; unsigned long (*function_pointer_wrapper::thread_callback)(void*) = nullptr; -template -bool run_as_fiber(functor &&callback) { - struct fiber_wrapper { - functor &callback; - void *original; - bool result; - - void execute() { - result = callback(); - SwitchToFiber(original); - } - - } wrapper{callback, nullptr, false}; - - function_pointer_wrapper::fiber_callback = [](void *parameter){ - reinterpret_cast(parameter)->execute(); - }; - auto fiber = CreateFiberEx(16*1024*1024, 16*1024*1024, 0, &function_pointer_wrapper::execute_fiber, &wrapper); - - if (!fiber) { - return callback(); - } - - void* main_thread = ConvertThreadToFiber(nullptr); - if (!main_thread && GetLastError() != ERROR_ALREADY_FIBER) { - return callback(); - } - wrapper.original = GetCurrentFiber(); - SwitchToFiber(fiber); - DeleteFiber(fiber); - if (main_thread) { - ConvertFiberToThread(); - } - return wrapper.result; +template +bool run_as_fiber(functor&& callback) { + struct fiber_wrapper { + functor& callback; + void* original; + bool result; + + void execute() { + result = callback(); + SwitchToFiber(original); + } + + } wrapper{callback, nullptr, false}; + + function_pointer_wrapper::fiber_callback = [](void* parameter) { + reinterpret_cast(parameter)->execute(); + }; + auto fiber = CreateFiberEx(16 * 1024 * 1024, 16 * 1024 * 1024, 0, + &function_pointer_wrapper::execute_fiber, &wrapper); + + if (!fiber) { + return callback(); + } + + void* main_thread = ConvertThreadToFiber(nullptr); + if (!main_thread && GetLastError() != ERROR_ALREADY_FIBER) { + return callback(); + } + wrapper.original = GetCurrentFiber(); + SwitchToFiber(fiber); + DeleteFiber(fiber); + if (main_thread) { + ConvertFiberToThread(); + } + return wrapper.result; } template bool run_as_thread(functor&& callback) { - struct thread_wrapper { - functor& callback; - bool result; + struct thread_wrapper { + functor& callback; + bool result; - unsigned long execute() { - result = callback(); - return (result ? 0 : -1); - } + unsigned long execute() { + result = callback(); + return (result ? 0 : -1); + } - } wrapper{ callback, false }; + } wrapper{callback, false}; - function_pointer_wrapper::thread_callback = [](void* parameter) { - return reinterpret_cast(parameter)->execute(); - }; + function_pointer_wrapper::thread_callback = [](void* parameter) { + return reinterpret_cast(parameter)->execute(); + }; - auto thread = CreateThread(NULL, 16 * 1024 * 1024, &function_pointer_wrapper::execute_thread, &wrapper, 0, NULL); + auto thread = + CreateThread(NULL, 16 * 1024 * 1024, + &function_pointer_wrapper::execute_thread, &wrapper, 0, NULL); - if (!thread) { - return callback(); - } + if (!thread) { + return callback(); + } - WaitForSingleObject(thread, INFINITE); + WaitForSingleObject(thread, INFINITE); - CloseHandle(thread); + CloseHandle(thread); - return wrapper.result; + return wrapper.result; } size_t check_stack_left() { - ULONG_PTR stack_low, stack_high; - GetCurrentThreadStackLimits(&stack_low, &stack_high); - size_t stack_left = (char*)&stack_low - (char*)stack_low; - return stack_left; + ULONG_PTR stack_low, stack_high; + GetCurrentThreadStackLimits(&stack_low, &stack_high); + size_t stack_left = (char*)&stack_low - (char*)stack_low; + return stack_left; } #endif diff --git a/src/asar/std-includes.h b/src/asar/std-includes.h index df8c4597..b2d4b5a7 100644 --- a/src/asar/std-includes.h +++ b/src/asar/std-includes.h @@ -6,28 +6,28 @@ // that we can easily disable certain warnings via pragmas. #if defined(_MSC_VER) -# pragma warning(push) -# pragma warning(disable : 4514) -# pragma warning(disable : 4577) -# pragma warning(disable : 4668) -# pragma warning(disable : 4987) +#pragma warning(push) +#pragma warning(disable : 4514) +#pragma warning(disable : 4577) +#pragma warning(disable : 4668) +#pragma warning(disable : 4987) #endif -#include //placement new -#include //malloc, realloc, free -#include //strcmp, memmove -#include #include #include +#include +#include //malloc, realloc, free +#include //strcmp, memmove + #include +#include //placement new -inline char * duplicate_string(const char * str) -{ - char * a = (char*)malloc(sizeof(char)*(strlen(str) + 1)); - strcpy(a, str); - return a; +inline char* duplicate_string(const char* str) { + char* a = (char*)malloc(sizeof(char) * (strlen(str) + 1)); + strcpy(a, str); + return a; } #if defined(_MSC_VER) -# pragma warning(pop) +#pragma warning(pop) #endif diff --git a/src/asar/table.cpp b/src/asar/table.cpp index 4bef088d..5ab3ab03 100644 --- a/src/asar/table.cpp +++ b/src/asar/table.cpp @@ -1,82 +1,79 @@ -#include +#include "table.h" + #include +#include #include -#include "table.h" table::table() { - memset(data, 0, sizeof(data)); - utf8_mode = true; + memset(data, 0, sizeof(data)); + utf8_mode = true; } void table::clear() { - for(int i=0; i<256; i++) { - if(data[i] != nullptr) { - for(int j=0; j<256; j++) { - if(data[i][j] != nullptr) free(data[i][j]); - } - free(data[i]); - } - } - memset(data, 0, sizeof(data)); + for (int i = 0; i < 256; i++) { + if (data[i] != nullptr) { + for (int j = 0; j < 256; j++) { + if (data[i][j] != nullptr) free(data[i][j]); + } + free(data[i]); + } + } + memset(data, 0, sizeof(data)); } void table::copy_from(const table& from) { - memcpy(data, from.data, sizeof(data)); - utf8_mode = from.utf8_mode; - // copy over all allocated pages - for(int i=0; i<256; i++) { - if(data[i] != nullptr) { - table_page** newp = (table_page**)calloc(256,sizeof(void*)); - memcpy(newp, data[i], 256*sizeof(void*)); - data[i] = newp; - for(int j=0; j<256; j++) { - if(data[i][j] != nullptr) { - table_page* newp = (table_page*)calloc(1,sizeof(table_page)); - memcpy(newp, data[i][j], sizeof(table_page)); - data[i][j] = newp; - } - } - } - } + memcpy(data, from.data, sizeof(data)); + utf8_mode = from.utf8_mode; + // copy over all allocated pages + for (int i = 0; i < 256; i++) { + if (data[i] != nullptr) { + table_page** newp = (table_page**)calloc(256, sizeof(void*)); + memcpy(newp, data[i], 256 * sizeof(void*)); + data[i] = newp; + for (int j = 0; j < 256; j++) { + if (data[i][j] != nullptr) { + table_page* newp = (table_page*)calloc(1, sizeof(table_page)); + memcpy(newp, data[i][j], sizeof(table_page)); + data[i][j] = newp; + } + } + } + } } table& table::operator=(const table& from) { - clear(); - copy_from(from); - return *this; + clear(); + copy_from(from); + return *this; } -table::table(const table& from) { - copy_from(from); -} +table::table(const table& from) { copy_from(from); } -table::~table() { - clear(); -} +table::~table() { clear(); } void table::set_val(int off, uint32_t val) { - if(data[off >> 16] == nullptr) { - data[off >> 16] = (table_page**)calloc(256,sizeof(void*)); - } - table_page** thisbank = data[off >> 16]; - if(thisbank[(off >> 8) & 255] == nullptr) { - thisbank[(off >> 8) & 255] = (table_page*)calloc(1,sizeof(table_page)); - } - table_page* thispage = thisbank[(off >> 8) & 255]; - int idx = (off & 255) / 32; - int bit = off % 32; - thispage->defined[idx] |= 1<chars[off & 255] = val; + if (data[off >> 16] == nullptr) { + data[off >> 16] = (table_page**)calloc(256, sizeof(void*)); + } + table_page** thisbank = data[off >> 16]; + if (thisbank[(off >> 8) & 255] == nullptr) { + thisbank[(off >> 8) & 255] = (table_page*)calloc(1, sizeof(table_page)); + } + table_page* thispage = thisbank[(off >> 8) & 255]; + int idx = (off & 255) / 32; + int bit = off % 32; + thispage->defined[idx] |= 1 << bit; + thispage->chars[off & 255] = val; } int64_t table::get_val(int off) { - int64_t def = utf8_mode ? off : -1; - table_page** thisbank = data[off >> 16]; - if(thisbank == nullptr) return def; - table_page* thispage = thisbank[(off >> 8) & 255]; - if(thispage == nullptr) return def; - int idx = (off & 255) / 32; - int bit = off % 32; - if(((thispage->defined[idx] >> bit) & 1) == 0) return def; - return thispage->chars[off & 255]; + int64_t def = utf8_mode ? off : -1; + table_page** thisbank = data[off >> 16]; + if (thisbank == nullptr) return def; + table_page* thispage = thisbank[(off >> 8) & 255]; + if (thispage == nullptr) return def; + int idx = (off & 255) / 32; + int bit = off % 32; + if (((thispage->defined[idx] >> bit) & 1) == 0) return def; + return thispage->chars[off & 255]; } diff --git a/src/asar/table.h b/src/asar/table.h index 7f206616..1f16ead7 100644 --- a/src/asar/table.h +++ b/src/asar/table.h @@ -1,28 +1,31 @@ - // data structures for the "table" command +#include + class table_page { public: - uint32_t chars[256]; - // bit mask of defined entries - uint32_t defined[8]; + uint32_t chars[256]; + // bit mask of defined entries + uint32_t defined[8]; }; class table { public: - table(); - table(const table& from); - table& operator=(const table& from); - void set_val(int off, uint32_t val); - // returns either the 32-bit unsigned value or -1 if that codepoint isn't in the table - int64_t get_val(int off); - ~table(); - // if set, each undefined char goes to its unicode codepoint - bool utf8_mode; + table(); + table(const table& from); + table& operator=(const table& from); + void set_val(int off, uint32_t val); + // returns either the 32-bit unsigned value or -1 if that codepoint isn't in the + // table + int64_t get_val(int off); + ~table(); + // if set, each undefined char goes to its unicode codepoint + bool utf8_mode; + private: - table_page** data[256]; - void clear(); - void copy_from(const table& from); + table_page** data[256]; + void clear(); + void copy_from(const table& from); }; extern table thetable; diff --git a/src/asar/unicode.cpp b/src/asar/unicode.cpp index 9c59ffe1..60e42ca9 100644 --- a/src/asar/unicode.cpp +++ b/src/asar/unicode.cpp @@ -1,173 +1,156 @@ #include "unicode.h" size_t utf8_val(int* codepoint, const char* inp) { - unsigned char c = *inp++; - int val; - if (c < 0x80) { - // plain ascii - *codepoint = c; - return 1u; - } - // RPG Hacker: Byte sequences starting with 0xC0 or 0xC1 are invalid. - // So are byte sequences starting with anything >= 0xF5. - // And anything below 0xC0 indicates a follow-up byte and should never be at the start of a sequence. - else if (c > 0xC1 && c < 0xF5) { - // 1, 2 or 3 continuation bytes - int cont_byte_count = (c >= 0xF0) ? 3 : (c >= 0xE0) ? 2 : 1; - // bit hack to extract the significant bits from the start byte - val = (c & ((1 << (6 - cont_byte_count)) - 1)); - for (int i = 0; i < cont_byte_count; i++) { - unsigned char next = *inp++; - if ((next & 0xC0) != 0x80) { - *codepoint = -1; - return 0u; - } - val = (val << 6) | (next & 0x3F); - } - if (// too many cont.bytes - (*inp & 0xC0) == 0x80 || - - // invalid codepoints - val > 0x10FFFF || - - // check overlong encodings - (cont_byte_count == 3 && val < 0x1000) || - (cont_byte_count == 2 && val < 0x800) || - (cont_byte_count == 1 && val < 0x80) || - - // UTF16 surrogates - (val >= 0xD800 && val <= 0xDFFF) - ) { - *codepoint = -1; - return 0u; - }; - *codepoint = val; - return 1u + cont_byte_count; - } - - // if none of the above, this couldn't possibly be a valid encoding - *codepoint = -1; - return 0u; + unsigned char c = *inp++; + int val; + if (c < 0x80) { + // plain ascii + *codepoint = c; + return 1u; + } + // RPG Hacker: Byte sequences starting with 0xC0 or 0xC1 are invalid. + // So are byte sequences starting with anything >= 0xF5. + // And anything below 0xC0 indicates a follow-up byte and should never be at the + // start of a sequence. + else if (c > 0xC1 && c < 0xF5) { + // 1, 2 or 3 continuation bytes + int cont_byte_count = (c >= 0xF0) ? 3 : (c >= 0xE0) ? 2 : 1; + // bit hack to extract the significant bits from the start byte + val = (c & ((1 << (6 - cont_byte_count)) - 1)); + for (int i = 0; i < cont_byte_count; i++) { + unsigned char next = *inp++; + if ((next & 0xC0) != 0x80) { + *codepoint = -1; + return 0u; + } + val = (val << 6) | (next & 0x3F); + } + if ( // too many cont.bytes + (*inp & 0xC0) == 0x80 || + + // invalid codepoints + val > 0x10FFFF || + + // check overlong encodings + (cont_byte_count == 3 && val < 0x1000) || + (cont_byte_count == 2 && val < 0x800) || + (cont_byte_count == 1 && val < 0x80) || + + // UTF16 surrogates + (val >= 0xD800 && val <= 0xDFFF)) { + *codepoint = -1; + return 0u; + }; + *codepoint = val; + return 1u + cont_byte_count; + } + + // if none of the above, this couldn't possibly be a valid encoding + *codepoint = -1; + return 0u; } bool codepoint_to_utf8(string* out, unsigned int codepoint) { - *out = ""; - if (codepoint < 0x80) { - *out += (unsigned char)codepoint; - } - else if (codepoint < 0x800) { - *out += (unsigned char)(0xc0 | (codepoint >> 6)); - *out += (unsigned char)(0x80 | (codepoint & 0x3f)); - } - else if (codepoint < 0x10000) { - *out += (unsigned char)(0xe0 | (codepoint >> 12)); - *out += (unsigned char)(0x80 | ((codepoint >> 6) & 0x3f)); - *out += (unsigned char)(0x80 | (codepoint & 0x3f)); - } - else if (codepoint < 0x110000) { - *out += (unsigned char)(0xf0 | (codepoint >> 18)); - *out += (unsigned char)(0x80 | ((codepoint >> 12) & 0x3f)); - *out += (unsigned char)(0x80 | ((codepoint >> 6) & 0x3f)); - *out += (unsigned char)(0x80 | (codepoint & 0x3f)); - } - else return false; - - return true; + *out = ""; + if (codepoint < 0x80) { + *out += (unsigned char)codepoint; + } else if (codepoint < 0x800) { + *out += (unsigned char)(0xc0 | (codepoint >> 6)); + *out += (unsigned char)(0x80 | (codepoint & 0x3f)); + } else if (codepoint < 0x10000) { + *out += (unsigned char)(0xe0 | (codepoint >> 12)); + *out += (unsigned char)(0x80 | ((codepoint >> 6) & 0x3f)); + *out += (unsigned char)(0x80 | (codepoint & 0x3f)); + } else if (codepoint < 0x110000) { + *out += (unsigned char)(0xf0 | (codepoint >> 18)); + *out += (unsigned char)(0x80 | ((codepoint >> 12) & 0x3f)); + *out += (unsigned char)(0x80 | ((codepoint >> 6) & 0x3f)); + *out += (unsigned char)(0x80 | (codepoint & 0x3f)); + } else + return false; + + return true; } bool is_valid_utf8(const char* inp) { - while (*inp != '\0') { - int codepoint; - inp += utf8_val(&codepoint, inp); + while (*inp != '\0') { + int codepoint; + inp += utf8_val(&codepoint, inp); - if (codepoint == -1) return false; - } + if (codepoint == -1) return false; + } - return true; + return true; } -size_t utf16_val(int* codepoint, const wchar_t* inp) -{ - wchar_t first_word = *inp; - - if (first_word <= 0xD800 || first_word >= 0xDFFF) - { - // Single word - *codepoint = first_word; - return 1u; - } - else if (first_word >= 0xD800 && first_word <= 0xDBFF) - { - // Start of a surrogate pair - wchar_t second_word = *(inp + 1); - - if (second_word >= 0xDC00 && second_word <= 0xDFFF) - { - *codepoint = 0x10000 - + ((int)(first_word - 0xD800) << 10u) - + ((int)(second_word - 0xDC00)); - return 2u; - } - } - - // Everything not covered above is considered invalid. - *codepoint = -1; - return 0u; +size_t utf16_val(int* codepoint, const wchar_t* inp) { + wchar_t first_word = *inp; + + if (first_word <= 0xD800 || first_word >= 0xDFFF) { + // Single word + *codepoint = first_word; + return 1u; + } else if (first_word >= 0xD800 && first_word <= 0xDBFF) { + // Start of a surrogate pair + wchar_t second_word = *(inp + 1); + + if (second_word >= 0xDC00 && second_word <= 0xDFFF) { + *codepoint = 0x10000 + ((int)(first_word - 0xD800) << 10u) + + ((int)(second_word - 0xDC00)); + return 2u; + } + } + + // Everything not covered above is considered invalid. + *codepoint = -1; + return 0u; } -bool codepoint_to_utf16(std::wstring* out, unsigned int codepoint) -{ - if (codepoint >= 0x10000 && codepoint <= 0x10FFFF) - { - wchar_t high = (wchar_t)(((codepoint - 0x10000) >> 10) + 0xD800); - wchar_t low = (wchar_t)(((codepoint - 0x10000) & 0b1111111111) + 0xDC00); - - *out = std::wstring() + high + low; - return true; - } - else if (codepoint <= 0xD800 || codepoint >= 0xDFFF) - { - *out = std::wstring() + (wchar_t)codepoint; - return true; - } - - // Everything not covered above should be considered invalid. - return false; +bool codepoint_to_utf16(std::wstring* out, unsigned int codepoint) { + if (codepoint >= 0x10000 && codepoint <= 0x10FFFF) { + wchar_t high = (wchar_t)(((codepoint - 0x10000) >> 10) + 0xD800); + wchar_t low = (wchar_t)(((codepoint - 0x10000) & 0b1111111111) + 0xDC00); + + *out = std::wstring() + high + low; + return true; + } else if (codepoint <= 0xD800 || codepoint >= 0xDFFF) { + *out = std::wstring() + (wchar_t)codepoint; + return true; + } + + // Everything not covered above should be considered invalid. + return false; } -bool utf16_to_utf8(string* result, const wchar_t* u16_str) -{ - *result = ""; +bool utf16_to_utf8(string* result, const wchar_t* u16_str) { + *result = ""; - int codepoint; - do - { - u16_str += utf16_val(&codepoint, u16_str); + int codepoint; + do { + u16_str += utf16_val(&codepoint, u16_str); - string next; - if (codepoint == -1 || !codepoint_to_utf8(&next, codepoint)) return false; + string next; + if (codepoint == -1 || !codepoint_to_utf8(&next, codepoint)) return false; - *result += next; - } while (codepoint != 0); + *result += next; + } while (codepoint != 0); - return true; + return true; } -bool utf8_to_utf16(std::wstring* result, const char* u8_str) -{ - *result = L""; +bool utf8_to_utf16(std::wstring* result, const char* u8_str) { + *result = L""; - int codepoint; - do - { - u8_str += utf8_val(&codepoint, u8_str); + int codepoint; + do { + u8_str += utf8_val(&codepoint, u8_str); - std::wstring next; - if (codepoint == -1 || !codepoint_to_utf16(&next, codepoint)) return false; + std::wstring next; + if (codepoint == -1 || !codepoint_to_utf16(&next, codepoint)) return false; - *result += next; - } while (codepoint != 0); + *result += next; + } while (codepoint != 0); - return true; + return true; } diff --git a/src/asar/unicode.h b/src/asar/unicode.h index c9e2096c..794fffad 100644 --- a/src/asar/unicode.h +++ b/src/asar/unicode.h @@ -1,10 +1,10 @@ #if !defined(ASAR_UNICODE_H) #define ASAR_UNICODE_H -#include "libstr.h" - #include +#include "libstr.h" + // Writes the code point at *inp into codepoint (-1 on invalid UTF-8). // Returns the number of bytes consumed for the code point (0 for invalid UTF-8). size_t utf8_val(int* codepoint, const char* inp); @@ -17,8 +17,9 @@ bool is_valid_utf8(const char* inp); // RPG Hacker: UTF-16 functions below expect wchar_t to be at least 16-bit wide. -// Not that this should be a problem, since we shouldn't need those for anything aside from Windows. -// char16_t would be more reliable, but would mean always having to cast to wchar_t* for Windows APIs. +// Not that this should be a problem, since we shouldn't need those for anything aside +// from Windows. char16_t would be more reliable, but would mean always having to cast +// to wchar_t* for Windows APIs. // Same as utf8_val(), but for UTF-16. size_t utf16_val(int* codepoint, const wchar_t* inp); diff --git a/src/asar/virtualfile.cpp b/src/asar/virtualfile.cpp index 71120c8b..c175468b 100644 --- a/src/asar/virtualfile.cpp +++ b/src/asar/virtualfile.cpp @@ -1,326 +1,264 @@ -#include #include "virtualfile.h" + +#include + #include "platform/file-helpers.h" #include "warnings.h" -class virtual_file -{ +class virtual_file { public: - virtual ~virtual_file() - { - } + virtual ~virtual_file() {} - virtual void close() = 0; + virtual void close() = 0; - virtual size_t read(void* out_buffer, size_t pos, size_t num_bytes) = 0; + virtual size_t read(void* out_buffer, size_t pos, size_t num_bytes) = 0; - virtual size_t get_size() = 0; + virtual size_t get_size() = 0; }; -class memory_file : public virtual_file -{ +class memory_file : public virtual_file { public: - memory_file(const void* data, size_t length) - : m_data(data), m_length(length) - { - } + memory_file(const void* data, size_t length) : m_data(data), m_length(length) {} - virtual ~memory_file() - { - close(); - } + virtual ~memory_file() { close(); } - virtual void close() - { - } + virtual void close() {} - virtual size_t read(void* out_buffer, size_t pos, size_t num_bytes) - { - if(pos > m_length) return 0; + virtual size_t read(void* out_buffer, size_t pos, size_t num_bytes) { + if (pos > m_length) return 0; - int diff = (int)(pos + num_bytes) - (int)m_length; - num_bytes -= diff < 0 ? 0 : (unsigned int)diff; + int diff = (int)(pos + num_bytes) - (int)m_length; + num_bytes -= diff < 0 ? 0 : (unsigned int)diff; - memcpy(out_buffer, (const char*)m_data + pos, num_bytes); - return num_bytes; - } + memcpy(out_buffer, (const char*)m_data + pos, num_bytes); + return num_bytes; + } - virtual size_t get_size() - { - return m_length; - } + virtual size_t get_size() { return m_length; } private: - const void* m_data; - size_t m_length; + const void* m_data; + size_t m_length; }; -class physical_file : public virtual_file -{ +class physical_file : public virtual_file { public: - physical_file() - : m_file_handle(InvalidFileHandle) - { - } - - virtual ~physical_file() - { - close(); - } - - virtual_file_error open(const string& path) - { - if (path != "") - { - // randomdude999: checking this before file regularity to improve error messages - if(!file_exists((const char*)path)) return vfe_doesnt_exist; - if(!check_is_regular_file((const char*)path)) return vfe_not_regular_file; - - FileOpenError error = FileOpenError_None; - - m_file_handle = open_file((const char*)path, FileOpenMode_Read, &error); - - if (m_file_handle == InvalidFileHandle) - { - if (error == FileOpenError_NotFound) - { - return vfe_doesnt_exist; - } - else if (error == FileOpenError_AccessDenied) - { - return vfe_access_denied; - } - else - { - return vfe_unknown; - } - } - - return vfe_none; - } - - return vfe_doesnt_exist; - } - - virtual void close() - { - if (m_file_handle != InvalidFileHandle) - { - close_file(m_file_handle); - m_file_handle = InvalidFileHandle; - } - } - - virtual size_t read(void* out_buffer, size_t pos, size_t num_bytes) - { - set_file_pos(m_file_handle, (uint64_t)pos); - return (size_t)read_file(m_file_handle, out_buffer, (uint32_t)num_bytes); - } - - virtual size_t get_size() - { - return (size_t)get_file_size(m_file_handle); - } + physical_file() : m_file_handle(InvalidFileHandle) {} + + virtual ~physical_file() { close(); } + + virtual_file_error open(const string& path) { + if (path != "") { + // randomdude999: checking this before file regularity to improve error + // messages + if (!file_exists((const char*)path)) return vfe_doesnt_exist; + if (!check_is_regular_file((const char*)path)) return vfe_not_regular_file; + + FileOpenError error = FileOpenError_None; + + m_file_handle = open_file((const char*)path, FileOpenMode_Read, &error); + + if (m_file_handle == InvalidFileHandle) { + if (error == FileOpenError_NotFound) { + return vfe_doesnt_exist; + } else if (error == FileOpenError_AccessDenied) { + return vfe_access_denied; + } else { + return vfe_unknown; + } + } + + return vfe_none; + } + + return vfe_doesnt_exist; + } + + virtual void close() { + if (m_file_handle != InvalidFileHandle) { + close_file(m_file_handle); + m_file_handle = InvalidFileHandle; + } + } + + virtual size_t read(void* out_buffer, size_t pos, size_t num_bytes) { + set_file_pos(m_file_handle, (uint64_t)pos); + return (size_t)read_file(m_file_handle, out_buffer, (uint32_t)num_bytes); + } + + virtual size_t get_size() { return (size_t)get_file_size(m_file_handle); } private: - friend class virtual_filesystem; + friend class virtual_filesystem; - FileHandleType m_file_handle; + FileHandleType m_file_handle; }; +void virtual_filesystem::initialize(const char** include_paths, + size_t num_include_paths) { + m_include_paths.reset(); -void virtual_filesystem::initialize(const char** include_paths, size_t num_include_paths) -{ - m_include_paths.reset(); + for (size_t i = 0; i < num_include_paths; ++i) { + m_include_paths[(int)i] = include_paths[i]; + } - for (size_t i = 0; i < num_include_paths; ++i) - { - m_include_paths[(int)i] = include_paths[i]; - } - - m_last_error = vfe_none; - m_memory_files.reset(); + m_last_error = vfe_none; + m_memory_files.reset(); } -void virtual_filesystem::destroy() -{ - m_include_paths.reset(); -} +void virtual_filesystem::destroy() { m_include_paths.reset(); } -virtual_file_handle virtual_filesystem::open_file(const char* path, const char* base_path) -{ - m_last_error = vfe_none; - - string absolutepath = create_absolute_path(base_path, path); - - virtual_file_type vft = get_file_type_from_path(absolutepath); - - if (vft != vft_memory_file) - { - asar_throw_warning(0, warning_id_check_memory_file, path, (int)warning_id_check_memory_file); - } - - switch (vft) - { - case vft_physical_file: - { - physical_file* new_file = new physical_file; - - if (new_file == nullptr) - { - m_last_error = vfe_unknown; - return INVALID_VIRTUAL_FILE_HANDLE; - } - - virtual_file_error error = new_file->open(absolutepath); - - if (error != vfe_none) - { - delete new_file; - m_last_error = error; - return INVALID_VIRTUAL_FILE_HANDLE; - } - - return static_cast(new_file); - } - - case vft_memory_file: - { - if(m_memory_files.exists(absolutepath)) { - memory_buffer mem_buf = m_memory_files.find(absolutepath); - memory_file* new_file = new memory_file(mem_buf.data, mem_buf.length); - return static_cast(new_file); - } else { - m_last_error = vfe_doesnt_exist; - return INVALID_VIRTUAL_FILE_HANDLE; - } - } - - default: - // We should not get here - m_last_error = vfe_unknown; - return INVALID_VIRTUAL_FILE_HANDLE; - } -} +virtual_file_handle virtual_filesystem::open_file(const char* path, + const char* base_path) { + m_last_error = vfe_none; + + string absolutepath = create_absolute_path(base_path, path); + + virtual_file_type vft = get_file_type_from_path(absolutepath); + + if (vft != vft_memory_file) { + asar_throw_warning(0, warning_id_check_memory_file, path, + (int)warning_id_check_memory_file); + } + + switch (vft) { + case vft_physical_file: { + physical_file* new_file = new physical_file; -void virtual_filesystem::close_file(virtual_file_handle file_handle) -{ - if (file_handle != INVALID_VIRTUAL_FILE_HANDLE) - { - virtual_file* file = static_cast(file_handle); + if (new_file == nullptr) { + m_last_error = vfe_unknown; + return INVALID_VIRTUAL_FILE_HANDLE; + } - file->close(); + virtual_file_error error = new_file->open(absolutepath); - delete file; - } + if (error != vfe_none) { + delete new_file; + m_last_error = error; + return INVALID_VIRTUAL_FILE_HANDLE; + } + + return static_cast(new_file); + } + + case vft_memory_file: { + if (m_memory_files.exists(absolutepath)) { + memory_buffer mem_buf = m_memory_files.find(absolutepath); + memory_file* new_file = new memory_file(mem_buf.data, mem_buf.length); + return static_cast(new_file); + } else { + m_last_error = vfe_doesnt_exist; + return INVALID_VIRTUAL_FILE_HANDLE; + } + } + + default: + // We should not get here + m_last_error = vfe_unknown; + return INVALID_VIRTUAL_FILE_HANDLE; + } } +void virtual_filesystem::close_file(virtual_file_handle file_handle) { + if (file_handle != INVALID_VIRTUAL_FILE_HANDLE) { + virtual_file* file = static_cast(file_handle); + + file->close(); + + delete file; + } +} -size_t virtual_filesystem::read_file(virtual_file_handle file_handle, void* out_buffer, size_t pos, size_t num_bytes) -{ - if (file_handle != INVALID_VIRTUAL_FILE_HANDLE) - { - virtual_file* file = static_cast(file_handle); +size_t virtual_filesystem::read_file(virtual_file_handle file_handle, void* out_buffer, + size_t pos, size_t num_bytes) { + if (file_handle != INVALID_VIRTUAL_FILE_HANDLE) { + virtual_file* file = static_cast(file_handle); - return file->read(out_buffer, pos, num_bytes); - } + return file->read(out_buffer, pos, num_bytes); + } - return 0u; + return 0u; } -size_t virtual_filesystem::get_file_size(virtual_file_handle file_handle) -{ - if (file_handle != INVALID_VIRTUAL_FILE_HANDLE) - { - virtual_file* file = static_cast(file_handle); +size_t virtual_filesystem::get_file_size(virtual_file_handle file_handle) { + if (file_handle != INVALID_VIRTUAL_FILE_HANDLE) { + virtual_file* file = static_cast(file_handle); - return file->get_size(); - } + return file->get_size(); + } - return 0u; + return 0u; } -virtual_filesystem::virtual_file_type virtual_filesystem::get_file_type_from_path(const char* path) -{ - if(m_memory_files.exists(path)) { - return vft_memory_file; - } else { - return vft_physical_file; - } +virtual_filesystem::virtual_file_type virtual_filesystem::get_file_type_from_path( + const char* path) { + if (m_memory_files.exists(path)) { + return vft_memory_file; + } else { + return vft_physical_file; + } } -void virtual_filesystem::add_memory_file(const char* name, const void* buffer, size_t length) { - memory_buffer mem_buf = { buffer, length }; - string normalized_path = normalize_path(name); - m_memory_files.remove(normalized_path); - m_memory_files.create(normalized_path) = mem_buf; +void virtual_filesystem::add_memory_file(const char* name, const void* buffer, + size_t length) { + memory_buffer mem_buf = {buffer, length}; + string normalized_path = normalize_path(name); + m_memory_files.remove(normalized_path); + m_memory_files.create(normalized_path) = mem_buf; } -bool virtual_filesystem::is_path_absolute(const char* path) -{ - return path_is_absolute(path); +bool virtual_filesystem::is_path_absolute(const char* path) { + return path_is_absolute(path); } -string virtual_filesystem::create_absolute_path(const char* base, const char* target) -{ - if (is_path_absolute(target) || base == nullptr || base[0] == '\0') - { - return normalize_path(target); - } - - string path_to_use = ""; - string test_path = ""; - - test_path = normalize_path(target); - - // First check if path is absolute - if (path_is_absolute(test_path)) - { - if (m_memory_files.exists(test_path) || file_exists(test_path)) - { - path_to_use = test_path; - } - } - else - { - // Now check if path exists relative to the base path - if (base != nullptr) - { - test_path = create_combined_path(dir(base), target); - } - - if (test_path != "" && (m_memory_files.exists(test_path) || file_exists(test_path))) - { - path_to_use = test_path; - } - else - { - // Finally check if path exists relative to any include path - bool found = false; - for (int i = 0; i < m_include_paths.count; ++i) - { - test_path = create_combined_path(m_include_paths[i], target); - - if (m_memory_files.exists(test_path) || file_exists(test_path)) - { - found = true; - path_to_use = test_path; - break; - } - } - - if (!found) - { - // Reset our path so that we don't return an empty one - // (that will do some weird shit and fuck up error messages) - path_to_use = target; - } - } - } - - return path_to_use; +string virtual_filesystem::create_absolute_path(const char* base, const char* target) { + if (is_path_absolute(target) || base == nullptr || base[0] == '\0') { + return normalize_path(target); + } + + string path_to_use = ""; + string test_path = ""; + + test_path = normalize_path(target); + + // First check if path is absolute + if (path_is_absolute(test_path)) { + if (m_memory_files.exists(test_path) || file_exists(test_path)) { + path_to_use = test_path; + } + } else { + // Now check if path exists relative to the base path + if (base != nullptr) { + test_path = create_combined_path(dir(base), target); + } + + if (test_path != "" && + (m_memory_files.exists(test_path) || file_exists(test_path))) { + path_to_use = test_path; + } else { + // Finally check if path exists relative to any include path + bool found = false; + for (int i = 0; i < m_include_paths.count; ++i) { + test_path = create_combined_path(m_include_paths[i], target); + + if (m_memory_files.exists(test_path) || file_exists(test_path)) { + found = true; + path_to_use = test_path; + break; + } + } + + if (!found) { + // Reset our path so that we don't return an empty one + // (that will do some weird shit and fuck up error messages) + path_to_use = target; + } + } + } + + return path_to_use; } diff --git a/src/asar/virtualfile.h b/src/asar/virtualfile.h index 3351c5ce..f11a213c 100644 --- a/src/asar/virtualfile.h +++ b/src/asar/virtualfile.h @@ -1,8 +1,8 @@ #if !defined(ASAR_VIRTUALFILE_H) #define ASAR_VIRTUALFILE_H -#include "autoarray.h" #include "assocarr.h" +#include "autoarray.h" #include "libstr.h" // RPG Hacker: A virtual file system which can work with physical files @@ -11,60 +11,51 @@ typedef void* virtual_file_handle; static const virtual_file_handle INVALID_VIRTUAL_FILE_HANDLE = nullptr; -enum virtual_file_error -{ - vfe_none, +enum virtual_file_error { + vfe_none, - vfe_doesnt_exist, - vfe_access_denied, - vfe_not_regular_file, - vfe_unknown, + vfe_doesnt_exist, + vfe_access_denied, + vfe_not_regular_file, + vfe_unknown, - vfe_num_errors + vfe_num_errors }; -struct memory_buffer -{ +struct memory_buffer { const void* data; size_t length; }; -class virtual_filesystem -{ +class virtual_filesystem { public: - void initialize(const char** include_paths, size_t num_include_paths); - void destroy(); + void initialize(const char** include_paths, size_t num_include_paths); + void destroy(); - virtual_file_handle open_file(const char* path, const char* base_path); - void close_file(virtual_file_handle file_handle); + virtual_file_handle open_file(const char* path, const char* base_path); + void close_file(virtual_file_handle file_handle); - size_t read_file(virtual_file_handle file_handle, void* out_buffer, size_t pos, size_t num_bytes); + size_t read_file(virtual_file_handle file_handle, void* out_buffer, size_t pos, + size_t num_bytes); - size_t get_file_size(virtual_file_handle file_handle); + size_t get_file_size(virtual_file_handle file_handle); - bool is_path_absolute(const char* path); + bool is_path_absolute(const char* path); - string create_absolute_path(const char* base, const char* target); + string create_absolute_path(const char* base, const char* target); - void add_memory_file(const char* name, const void* buffer, size_t length); + void add_memory_file(const char* name, const void* buffer, size_t length); - inline virtual_file_error get_last_error() - { - return m_last_error; - } + inline virtual_file_error get_last_error() { return m_last_error; } private: - enum virtual_file_type - { - vft_physical_file, - vft_memory_file - }; + enum virtual_file_type { vft_physical_file, vft_memory_file }; - virtual_file_type get_file_type_from_path(const char* path); + virtual_file_type get_file_type_from_path(const char* path); - assocarr m_memory_files; - autoarray m_include_paths; - virtual_file_error m_last_error; + assocarr m_memory_files; + autoarray m_include_paths; + virtual_file_error m_last_error; }; #endif diff --git a/src/asar/warnings.cpp b/src/asar/warnings.cpp index b08c4c28..e80bd983 100644 --- a/src/asar/warnings.cpp +++ b/src/asar/warnings.cpp @@ -1,254 +1,256 @@ #include "warnings.h" -#include "asar.h" #include #include +#include "asar.h" #include "interface-shared.h" static int asar_num_warnings = 0; -struct asar_warning_mapping -{ - asar_warning_id warnid; - const char* name; - const char* message; - bool enabled; - bool enabled_default; - - asar_warning_mapping(asar_warning_id inwarnid, const char *iname, const char* inmessage, bool inenabled = true) - { - ++asar_num_warnings; - - warnid = inwarnid; - name = iname; - message = inmessage; - enabled = inenabled; - enabled_default = inenabled; - - // RPG Hacker: Sanity check. This makes sure that the order - // of entries in asar_warnings matches with the order of - // constants in asar_warning_id. This is important because - // we access asar_warnings as an array. - // Would love to do this via static_assert(), but can't - // think of a way to do so. - assert(warnid - warning_id_start == asar_num_warnings); - } +struct asar_warning_mapping { + asar_warning_id warnid; + const char* name; + const char* message; + bool enabled; + bool enabled_default; + + asar_warning_mapping(asar_warning_id inwarnid, const char* iname, + const char* inmessage, bool inenabled = true) { + ++asar_num_warnings; + + warnid = inwarnid; + name = iname; + message = inmessage; + enabled = inenabled; + enabled_default = inenabled; + + // RPG Hacker: Sanity check. This makes sure that the order + // of entries in asar_warnings matches with the order of + // constants in asar_warning_id. This is important because + // we access asar_warnings as an array. + // Would love to do this via static_assert(), but can't + // think of a way to do so. + assert(warnid - warning_id_start == asar_num_warnings); + } }; // Keep in sync with asar_warning_id. // Both, enum mapping and order, must match. -#define WRN(name) warning_id_ ## name, "W" #name -static asar_warning_mapping asar_warnings[] = -{ - { WRN(relative_path_used), "Relative %s path passed to asar_patch_ex() - please use absolute paths only to prevent undefined behavior!" }, - - { WRN(rom_too_short), "ROM is too short to have a title. (Expected '%s')" }, - { WRN(rom_title_incorrect), "ROM title is incorrect. Expected '%s', got '%s'." }, - - { WRN(65816_yy_x_does_not_exist), "($yy),x does not exist, assuming $yy,x." }, - { WRN(65816_xx_y_assume_16_bit), "%s $xx,y is not valid with 8-bit parameters, assuming 16-bit." }, - { WRN(spc700_assuming_8_bit), "This opcode does not exist with 16-bit parameters, assuming 8-bit." }, - - { WRN(cross_platform_path), "This patch may not assemble cleanly on all platforms. Please use / instead." }, - - { WRN(missing_org), "Missing org or freespace command." }, - { WRN(set_middle_byte), "It would be wise to set the 008000 bit of this address." }, - - { WRN(unrecognized_special_command), "Unrecognized special command - your version of Asar might be outdated." }, - - { WRN(freespace_leaked), "This freespace appears to be leaked." }, - - { WRN(warn_command), "warn command%s" }, - - { WRN(implicitly_sized_immediate), "Implicitly sized immediate.", false }, - - { WRN(xkas_deprecated), "xkas support is being deprecated and will be removed in a future version of Asar. Please use an older version of Asar (<=1.50) if you need it." }, - { WRN(xkas_eat_parentheses), "xkas compatibility warning: Unlike xkas, Asar does not eat parentheses after defines." }, - { WRN(xkas_label_access), "xkas compatibility warning: Label access is always 24bit in emulation mode, but may be 16bit in native mode." }, - { WRN(xkas_warnpc_relaxed), "xkas conversion warning : warnpc is relaxed one byte in Asar." }, - { WRN(xkas_style_conditional), "xkas-style conditional compilation detected. Please use the if command instead." }, - { WRN(xkas_patch), "If you want to assemble an xkas patch, add ;@xkas at the top or you may run into a couple of problems." }, - { WRN(xkas_incsrc_relative), "xkas compatibility warning: incsrc and incbin look for files relative to the patch in Asar, but xkas looks relative to the assembler." }, - { WRN(convert_to_asar), "Convert the patch to native Asar format instead of making an Asar-only xkas patch." }, - - { WRN(fixed_deprecated), "the 'fixed' parameter on freespace/freecode/freedata is deprecated - please use 'static' instead." }, - - { WRN(autoclear_deprecated), "'autoclear' is deprecated - please use 'autoclean' instead." }, - - { WRN(check_memory_file), "Accessing file '%s' which is not in memory while W%d is enabled.", false }, - - { WRN(if_not_condition_deprecated), "'if !condition' is deprecated - please use 'if not(condition)' instead." }, - - { WRN(function_redefined), "Function '%s' redefined." }, - - { WRN(datasize_last_label), "Datasize used on last detected label '%s'." }, - { WRN(datasize_exceeds_size), "Datasize exceeds 0xFFFF for label '%s'." }, - - { WRN(mapper_already_set), "A mapper has already been selected." }, - { WRN(feature_deprecated), "DEPRECATION NOTIFICATION: Feature \"%s\" is deprecated and will be REMOVED in the future. Please update your code to conform to newer styles. Suggested work around: %s." }, - - { WRN(byte_order_mark_utf8), "UTF-8 byte order mark detected and skipped." }, +#define WRN(name) warning_id_##name, "W" #name +static asar_warning_mapping asar_warnings[] = { + {WRN(relative_path_used), + "Relative %s path passed to asar_patch_ex() - please use absolute paths only " + "to prevent undefined behavior!"}, + + {WRN(rom_too_short), "ROM is too short to have a title. (Expected '%s')"}, + {WRN(rom_title_incorrect), "ROM title is incorrect. Expected '%s', got '%s'."}, + + {WRN(65816_yy_x_does_not_exist), "($yy),x does not exist, assuming $yy,x."}, + {WRN(65816_xx_y_assume_16_bit), + "%s $xx,y is not valid with 8-bit parameters, assuming 16-bit."}, + {WRN(spc700_assuming_8_bit), + "This opcode does not exist with 16-bit parameters, assuming 8-bit."}, + + {WRN(cross_platform_path), + "This patch may not assemble cleanly on all platforms. Please use / instead."}, + + {WRN(missing_org), "Missing org or freespace command."}, + {WRN(set_middle_byte), + "It would be wise to set the 008000 bit of this address."}, + + {WRN(unrecognized_special_command), + "Unrecognized special command - your version of Asar might be outdated."}, + + {WRN(freespace_leaked), "This freespace appears to be leaked."}, + + {WRN(warn_command), "warn command%s"}, + + {WRN(implicitly_sized_immediate), "Implicitly sized immediate.", false}, + + {WRN(xkas_deprecated), + "xkas support is being deprecated and will be removed in a future version of " + "Asar. Please use an older version of Asar (<=1.50) if you need it."}, + {WRN(xkas_eat_parentheses), + "xkas compatibility warning: Unlike xkas, Asar does not eat parentheses after " + "defines."}, + {WRN(xkas_label_access), + "xkas compatibility warning: Label access is always 24bit in emulation mode, " + "but may be 16bit in native mode."}, + {WRN(xkas_warnpc_relaxed), + "xkas conversion warning : warnpc is relaxed one byte in Asar."}, + {WRN(xkas_style_conditional), + "xkas-style conditional compilation detected. Please use the if command " + "instead."}, + {WRN(xkas_patch), + "If you want to assemble an xkas patch, add ;@xkas at the top or you may run " + "into a couple of problems."}, + {WRN(xkas_incsrc_relative), + "xkas compatibility warning: incsrc and incbin look for files relative to the " + "patch in Asar, but xkas looks relative to the assembler."}, + {WRN(convert_to_asar), + "Convert the patch to native Asar format instead of making an Asar-only xkas " + "patch."}, + + {WRN(fixed_deprecated), + "the 'fixed' parameter on freespace/freecode/freedata is deprecated - please " + "use 'static' instead."}, + + {WRN(autoclear_deprecated), + "'autoclear' is deprecated - please use 'autoclean' instead."}, + + {WRN(check_memory_file), + "Accessing file '%s' which is not in memory while W%d is enabled.", false}, + + {WRN(if_not_condition_deprecated), + "'if !condition' is deprecated - please use 'if not(condition)' instead."}, + + {WRN(function_redefined), "Function '%s' redefined."}, + + {WRN(datasize_last_label), "Datasize used on last detected label '%s'."}, + {WRN(datasize_exceeds_size), "Datasize exceeds 0xFFFF for label '%s'."}, + + {WRN(mapper_already_set), "A mapper has already been selected."}, + {WRN(feature_deprecated), + "DEPRECATION NOTIFICATION: Feature \"%s\" is deprecated and will be REMOVED " + "in the future. Please update your code to conform to newer styles. Suggested " + "work around: %s."}, + + {WRN(byte_order_mark_utf8), "UTF-8 byte order mark detected and skipped."}, }; // RPG Hacker: Sanity check. This makes sure that the element count of asar_warnings // matches with the number of constants in asar_warning_id. This is important, because // we are going to access asar_warnings as an array. -static_assert(sizeof(asar_warnings) / sizeof(asar_warnings[0]) == warning_id_count, "asar_warnings and asar_warning_id are not in sync"); +static_assert(sizeof(asar_warnings) / sizeof(asar_warnings[0]) == warning_id_count, + "asar_warnings and asar_warning_id are not in sync"); -void asar_throw_warning(int whichpass, asar_warning_id warnid, ...) -{ - if (pass == whichpass) - { - assert(warnid > warning_id_start && warnid < warning_id_end); +void asar_throw_warning(int whichpass, asar_warning_id warnid, ...) { + if (pass == whichpass) { + assert(warnid > warning_id_start && warnid < warning_id_end); - const asar_warning_mapping& warning = asar_warnings[warnid - warning_id_start - 1]; + const asar_warning_mapping& warning = + asar_warnings[warnid - warning_id_start - 1]; - if (warning.enabled) - { - char warning_buffer[1024]; - va_list args; - va_start(args, warnid); + if (warning.enabled) { + char warning_buffer[1024]; + va_list args; + va_start(args, warnid); #if defined(__clang__) -# pragma clang diagnostic push +#pragma clang diagnostic push // "format string is not a literal". // The pointer we're passing here should always point to a string literal, // thus, I think, we can safely ignore this here. -# pragma clang diagnostic ignored "-Wformat-nonliteral" +#pragma clang diagnostic ignored "-Wformat-nonliteral" #endif - vsnprintf(warning_buffer, sizeof(warning_buffer), warning.message, args); + vsnprintf(warning_buffer, sizeof(warning_buffer), warning.message, args); #if defined(__clang__) -# pragma clang diagnostic pop +#pragma clang diagnostic pop #endif - va_end(args); - warn((int)warnid, warning_buffer); - } - } + va_end(args); + warn((int)warnid, warning_buffer); + } + } } -const char* get_warning_name(asar_warning_id warnid) -{ - assert(warnid > warning_id_start && warnid < warning_id_end); +const char* get_warning_name(asar_warning_id warnid) { + assert(warnid > warning_id_start && warnid < warning_id_end); - const asar_warning_mapping& warning = asar_warnings[warnid - warning_id_start - 1]; + const asar_warning_mapping& warning = asar_warnings[warnid - warning_id_start - 1]; - return warning.name; + return warning.name; } +void set_warning_enabled(asar_warning_id warnid, bool enabled) { + assert(warnid > warning_id_start && warnid < warning_id_end); -void set_warning_enabled(asar_warning_id warnid, bool enabled) -{ - assert(warnid > warning_id_start && warnid < warning_id_end); - - asar_warning_mapping& warning = asar_warnings[warnid - warning_id_start - 1]; + asar_warning_mapping& warning = asar_warnings[warnid - warning_id_start - 1]; - warning.enabled = enabled; + warning.enabled = enabled; } -asar_warning_id parse_warning_id_from_string(const char* string) -{ - const char* pos = string; - - if (pos == nullptr) - { - return warning_id_end; - } - - - if (pos[0] == 'w' || pos[0] == 'W') - { - ++pos; - } - for(int i = 0; i < warning_id_end-warning_id_start-1; i++) - { - if(!stricmpwithlower(pos, asar_warnings[i].name+1)) - { - return asar_warnings[i].warnid; - } - } - - return warning_id_end; +asar_warning_id parse_warning_id_from_string(const char* string) { + const char* pos = string; + + if (pos == nullptr) { + return warning_id_end; + } + + + if (pos[0] == 'w' || pos[0] == 'W') { + ++pos; + } + for (int i = 0; i < warning_id_end - warning_id_start - 1; i++) { + if (!stricmpwithlower(pos, asar_warnings[i].name + 1)) { + return asar_warnings[i].warnid; + } + } + + return warning_id_end; } -void reset_warnings_to_default() -{ - for (int i = (int)(warning_id_start + 1); i < (int)warning_id_end; ++i) - { - asar_warning_mapping& warning = asar_warnings[i - (int)warning_id_start - 1]; +void reset_warnings_to_default() { + for (int i = (int)(warning_id_start + 1); i < (int)warning_id_end; ++i) { + asar_warning_mapping& warning = asar_warnings[i - (int)warning_id_start - 1]; - warning.enabled = warning.enabled_default; - } + warning.enabled = warning.enabled_default; + } } -struct warnings_state -{ - bool enabled[warning_id_count]; +struct warnings_state { + bool enabled[warning_id_count]; }; static autoarray warnings_state_stack; static warnings_state main_warnings_state; -void push_warnings(bool warnings_command) -{ - warnings_state current_state; - - for (int i = 0; i < (int)warning_id_count; ++i) - { - current_state.enabled[i] = asar_warnings[i].enabled; - } - - if (warnings_command) - { - warnings_state_stack.append(current_state); - } - else - { - main_warnings_state = current_state; - } +void push_warnings(bool warnings_command) { + warnings_state current_state; + + for (int i = 0; i < (int)warning_id_count; ++i) { + current_state.enabled[i] = asar_warnings[i].enabled; + } + + if (warnings_command) { + warnings_state_stack.append(current_state); + } else { + main_warnings_state = current_state; + } } -void pull_warnings(bool warnings_command) -{ - if (warnings_state_stack.count > 0 || !warnings_command) - { - warnings_state prev_state; - - if (warnings_command) - { - prev_state = warnings_state_stack[warnings_state_stack.count - 1]; - } - else - { - prev_state = main_warnings_state; - } - - for (int i = 0; i < (int)warning_id_count; ++i) - { - asar_warnings[i].enabled = prev_state.enabled[i]; - } - - if (warnings_command) - { - warnings_state_stack.remove(warnings_state_stack.count - 1); - } - } - else - { - asar_throw_error(0, error_type_block, error_id_pullwarnings_without_pushwarnings); - } +void pull_warnings(bool warnings_command) { + if (warnings_state_stack.count > 0 || !warnings_command) { + warnings_state prev_state; + + if (warnings_command) { + prev_state = warnings_state_stack[warnings_state_stack.count - 1]; + } else { + prev_state = main_warnings_state; + } + + for (int i = 0; i < (int)warning_id_count; ++i) { + asar_warnings[i].enabled = prev_state.enabled[i]; + } + + if (warnings_command) { + warnings_state_stack.remove(warnings_state_stack.count - 1); + } + } else { + asar_throw_error(0, error_type_block, + error_id_pullwarnings_without_pushwarnings); + } } -void verify_warnings() -{ - if (warnings_state_stack.count > 0) - { - asar_throw_error(0, error_type_null, error_id_pushwarnings_without_pullwarnings); +void verify_warnings() { + if (warnings_state_stack.count > 0) { + asar_throw_error(0, error_type_null, + error_id_pushwarnings_without_pullwarnings); - warnings_state_stack.reset(); - } + warnings_state_stack.reset(); + } } diff --git a/src/asar/warnings.h b/src/asar/warnings.h index 5835cbcd..c95429e6 100644 --- a/src/asar/warnings.h +++ b/src/asar/warnings.h @@ -1,66 +1,65 @@ #pragma once -#define ASAR_WARNING_RANGE_START 1000 +#define ASAR_WARNING_RANGE_START 1000 // NOTE: Don't reorder these. That would change their ID. // If you need to remove one, stub it out. // If you need to add one, add it at the end (before warning_id_end). // Keep in sync with asar_warnings. -enum asar_warning_id : int -{ - warning_id_start = ASAR_WARNING_RANGE_START, +enum asar_warning_id : int { + warning_id_start = ASAR_WARNING_RANGE_START, - warning_id_relative_path_used, + warning_id_relative_path_used, - warning_id_rom_too_short, - warning_id_rom_title_incorrect, + warning_id_rom_too_short, + warning_id_rom_title_incorrect, - warning_id_65816_yy_x_does_not_exist, - warning_id_65816_xx_y_assume_16_bit, - warning_id_spc700_assuming_8_bit, + warning_id_65816_yy_x_does_not_exist, + warning_id_65816_xx_y_assume_16_bit, + warning_id_spc700_assuming_8_bit, - warning_id_cross_platform_path, + warning_id_cross_platform_path, - warning_id_missing_org, - warning_id_set_middle_byte, + warning_id_missing_org, + warning_id_set_middle_byte, - warning_id_unrecognized_special_command, + warning_id_unrecognized_special_command, - warning_id_freespace_leaked, + warning_id_freespace_leaked, - warning_id_warn_command, + warning_id_warn_command, - warning_id_implicitly_sized_immediate, + warning_id_implicitly_sized_immediate, - warning_id_xkas_deprecated, - warning_id_xkas_eat_parentheses, - warning_id_xkas_label_access, - warning_id_xkas_warnpc_relaxed, - warning_id_xkas_style_conditional, - warning_id_xkas_patch, - warning_id_xkas_incsrc_relative, - warning_id_convert_to_asar, + warning_id_xkas_deprecated, + warning_id_xkas_eat_parentheses, + warning_id_xkas_label_access, + warning_id_xkas_warnpc_relaxed, + warning_id_xkas_style_conditional, + warning_id_xkas_patch, + warning_id_xkas_incsrc_relative, + warning_id_convert_to_asar, - warning_id_fixed_deprecated, + warning_id_fixed_deprecated, - warning_id_autoclear_deprecated, + warning_id_autoclear_deprecated, - warning_id_check_memory_file, + warning_id_check_memory_file, - warning_id_if_not_condition_deprecated, + warning_id_if_not_condition_deprecated, - warning_id_function_redefined, - - warning_id_datasize_last_label, - warning_id_datasize_exceeds_size, - - warning_id_mapper_already_set, - warning_id_feature_deprecated, + warning_id_function_redefined, - warning_id_byte_order_mark_utf8, + warning_id_datasize_last_label, + warning_id_datasize_exceeds_size, - warning_id_end, - warning_id_count = warning_id_end - warning_id_start - 1 + warning_id_mapper_already_set, + warning_id_feature_deprecated, + + warning_id_byte_order_mark_utf8, + + warning_id_end, + warning_id_count = warning_id_end - warning_id_start - 1 }; void asar_throw_warning(int whichpass, asar_warning_id warnid, ...);