// == AUTOMATICALLY GENERATED by armips_gen.py, do not edit directly ==
// armips assembler v0.11 (commit 9029577f6448c78dadcffbc876d2b8cbcfc6da9b)
// https://github.com/kingcom/armips
// To simplify compilation, all files have been concatenated into one.
// MIPS only, other architectures not included.

/*
The MIT License (MIT)

Copyright (c) 2009-2020 Kingcom

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/

#define ARMIPS_USE_STD_FILESYSTEM

#pragma region "File: Util/FileSystem.h"

#ifdef ARMIPS_USE_STD_FILESYSTEM

#include <filesystem>
#include <fstream>

namespace fs
{
	using namespace std::filesystem;
	using ifstream = std::ifstream;
	using ofstream = std::ofstream;
	using fstream = std::fstream;
}

#else

#include <ghc/fs_fwd.hpp>

namespace fs
{
	using namespace ghc::filesystem;
	using ifstream = ghc::filesystem::ifstream;
	using ofstream = ghc::filesystem::ofstream;
	using fstream = ghc::filesystem::fstream;
}

#endif

#pragma region "File: ext/tinyformat/tinyformat.h"
// tinyformat.h
// Copyright (C) 2011, Chris Foster [chris42f (at) gmail (d0t) com]
//
// Boost Software License - Version 1.0
//
// Permission is hereby granted, free of charge, to any person or organization
// obtaining a copy of the software and accompanying documentation covered by
// this license (the "Software") to use, reproduce, display, distribute,
// execute, and transmit the Software, and to prepare derivative works of the
// Software, and to permit third-parties to whom the Software is furnished to
// do so, all subject to the following:
//
// The copyright notices in the Software and this entire statement, including
// the above license grant, this restriction and the following disclaimer,
// must be included in all copies of the Software, in whole or in part, and
// all derivative works of the Software, unless such copies or derivative
// works are solely in the form of machine-executable object code generated by
// a source language processor.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
// SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
// FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.

//------------------------------------------------------------------------------
// Tinyformat: A minimal type safe printf replacement
//
// tinyformat.h is a type safe printf replacement library in a single C++
// header file.  Design goals include:
//
// * Type safety and extensibility for user defined types.
// * C99 printf() compatibility, to the extent possible using std::ostream
// * Simplicity and minimalism.  A single header file to include and distribute
//   with your projects.
// * Augment rather than replace the standard stream formatting mechanism
// * C++98 support, with optional C++11 niceties
//
//
// Main interface example usage
// ----------------------------
//
// To print a date to std::cout:
//
//   std::string weekday = "Wednesday";
//   const char* month = "July";
//   size_t day = 27;
//   long hour = 14;
//   int min = 44;
//
//   tfm::printf("%s, %s %d, %.2d:%.2d\n", weekday, month, day, hour, min);
//
// The strange types here emphasize the type safety of the interface; it is
// possible to print a std::string using the "%s" conversion, and a
// size_t using the "%d" conversion.  A similar result could be achieved
// using either of the tfm::format() functions.  One prints on a user provided
// stream:
//
//   tfm::format(std::cerr, "%s, %s %d, %.2d:%.2d\n",
//               weekday, month, day, hour, min);
//
// The other returns a std::string:
//
//   std::string date = tfm::format("%s, %s %d, %.2d:%.2d\n",
//                                  weekday, month, day, hour, min);
//   std::cout << date;
//
// These are the three primary interface functions.  There is also a
// convenience function printfln() which appends a newline to the usual result
// of printf() for super simple logging.
//
//
// User defined format functions
// -----------------------------
//
// Simulating variadic templates in C++98 is pretty painful since it requires
// writing out the same function for each desired number of arguments.  To make
// this bearable tinyformat comes with a set of macros which are used
// internally to generate the API, but which may also be used in user code.
//
// The three macros TINYFORMAT_ARGTYPES(n), TINYFORMAT_VARARGS(n) and
// TINYFORMAT_PASSARGS(n) will generate a list of n argument types,
// type/name pairs and argument names respectively when called with an integer
// n between 1 and 16.  We can use these to define a macro which generates the
// desired user defined function with n arguments.  To generate all 16 user
// defined function bodies, use the macro TINYFORMAT_FOREACH_ARGNUM.  For an
// example, see the implementation of printf() at the end of the source file.
//
// Sometimes it's useful to be able to pass a list of format arguments through
// to a non-template function.  The FormatList class is provided as a way to do
// this by storing the argument list in a type-opaque way.  Continuing the
// example from above, we construct a FormatList using makeFormatList():
//
//   FormatListRef formatList = tfm::makeFormatList(weekday, month, day, hour, min);
//
// The format list can now be passed into any non-template function and used
// via a call to the vformat() function:
//
//   tfm::vformat(std::cout, "%s, %s %d, %.2d:%.2d\n", formatList);
//
//
// Additional API information
// --------------------------
//
// Error handling: Define TINYFORMAT_ERROR to customize the error handling for
// format strings which are unsupported or have the wrong number of format
// specifiers (calls assert() by default).
//
// User defined types: Uses operator<< for user defined types by default.
// Overload formatValue() for more control.


#ifndef TINYFORMAT_H_INCLUDED
#define TINYFORMAT_H_INCLUDED

namespace tinyformat {}
//------------------------------------------------------------------------------
// Config section.  Customize to your liking!

// Namespace alias to encourage brevity
namespace tfm = tinyformat;

// Error handling; calls assert() by default.
// #define TINYFORMAT_ERROR(reasonString) your_error_handler(reasonString)

// Define for C++11 variadic templates which make the code shorter & more
// general.  If you don't define this, C++11 support is autodetected below.
#define TINYFORMAT_USE_VARIADIC_TEMPLATES


//------------------------------------------------------------------------------
// Implementation details.
#include <algorithm>
#include <iostream>
#include <sstream>

#ifndef TINYFORMAT_ASSERT
#   include <cassert>
#   define TINYFORMAT_ASSERT(cond) assert(cond)
#endif

#ifndef TINYFORMAT_ERROR
#   include <cassert>
#   define TINYFORMAT_ERROR(reason) assert(0 && reason)
#endif

#if !defined(TINYFORMAT_USE_VARIADIC_TEMPLATES) && !defined(TINYFORMAT_NO_VARIADIC_TEMPLATES)
#   ifdef __GXX_EXPERIMENTAL_CXX0X__
#       define TINYFORMAT_USE_VARIADIC_TEMPLATES
#   endif
#endif

#if defined(__GLIBCXX__) && __GLIBCXX__ < 20080201
//  std::showpos is broken on old libstdc++ as provided with OSX.  See
//  http://gcc.gnu.org/ml/libstdc++/2007-11/msg00075.html
#   define TINYFORMAT_OLD_LIBSTDCPLUSPLUS_WORKAROUND
#endif

#ifdef __APPLE__
// Workaround OSX linker warning: xcode uses different default symbol
// visibilities for static libs vs executables (see issue #25)
#   define TINYFORMAT_HIDDEN __attribute__((visibility("hidden")))
#else
#   define TINYFORMAT_HIDDEN
#endif

namespace tinyformat {

//------------------------------------------------------------------------------
namespace detail {

// Test whether type T1 is convertible to type T2
template <typename T1, typename T2>
struct is_convertible
{
    private:
        // two types of different size
        struct fail { char dummy[2]; };
        struct succeed { char dummy; };
        // Try to convert a T1 to a T2 by plugging into tryConvert
        static fail tryConvert(...);
        static succeed tryConvert(const T2&);
        static const T1& makeT1();
    public:
#       ifdef _MSC_VER
        // Disable spurious loss of precision warnings in tryConvert(makeT1())
#       pragma warning(push)
#       pragma warning(disable:4244)
#       pragma warning(disable:4267)
#       endif
        // Standard trick: the (...) version of tryConvert will be chosen from
        // the overload set only if the version taking a T2 doesn't match.
        // Then we compare the sizes of the return types to check which
        // function matched.  Very neat, in a disgusting kind of way :)
        static const bool value =
            sizeof(tryConvert(makeT1())) == sizeof(succeed);
#       ifdef _MSC_VER
#       pragma warning(pop)
#       endif
};


// Detect when a type is not a wchar_t string
template<typename T> struct is_wchar { typedef int tinyformat_wchar_is_not_supported; };
template<> struct is_wchar<wchar_t*> {};
template<> struct is_wchar<const wchar_t*> {};
template<int n> struct is_wchar<const wchar_t[n]> {};
template<int n> struct is_wchar<wchar_t[n]> {};


// Format the value by casting to type fmtT.  This default implementation
// should never be called.
template<typename T, typename fmtT, bool convertible = is_convertible<T, fmtT>::value>
struct formatValueAsType
{
    static void invoke(std::ostream& /*out*/, const T& /*value*/) { TINYFORMAT_ASSERT(0); }
};
// Specialized version for types that can actually be converted to fmtT, as
// indicated by the "convertible" template parameter.
template<typename T, typename fmtT>
struct formatValueAsType<T,fmtT,true>
{
    static void invoke(std::ostream& out, const T& value)
        { out << static_cast<fmtT>(value); }
};

#ifdef TINYFORMAT_OLD_LIBSTDCPLUSPLUS_WORKAROUND
template<typename T, bool convertible = is_convertible<T, int>::value>
struct formatZeroIntegerWorkaround
{
    static bool invoke(std::ostream& /**/, const T& /**/) { return false; }
};
template<typename T>
struct formatZeroIntegerWorkaround<T,true>
{
    static bool invoke(std::ostream& out, const T& value)
    {
        if (static_cast<int>(value) == 0 && out.flags() & std::ios::showpos)
        {
            out << "+0";
            return true;
        }
        return false;
    }
};
#endif // TINYFORMAT_OLD_LIBSTDCPLUSPLUS_WORKAROUND

// Convert an arbitrary type to integer.  The version with convertible=false
// throws an error.
template<typename T, bool convertible = is_convertible<T,int>::value>
struct convertToInt
{
    static int invoke(const T& /*value*/)
    {
        TINYFORMAT_ERROR("tinyformat: Cannot convert from argument type to "
                         "integer for use as variable width or precision");
        return 0;
    }
};
// Specialization for convertToInt when conversion is possible
template<typename T>
struct convertToInt<T,true>
{
    static int invoke(const T& value) { return static_cast<int>(value); }
};

// Format at most ntrunc characters to the given stream.
template<typename T>
inline void formatTruncated(std::ostream& out, const T& value, int ntrunc)
{
    std::ostringstream tmp;
    tmp << value;
    std::string result = tmp.str();
    out.write(result.c_str(), (std::min)(ntrunc, static_cast<int>(result.size())));
}
#define TINYFORMAT_DEFINE_FORMAT_TRUNCATED_CSTR(type)       \
inline void formatTruncated(std::ostream& out, type* value, int ntrunc) \
{                                                           \
    std::streamsize len = 0;                                \
    while(len < ntrunc && value[len] != 0)                  \
        ++len;                                              \
    out.write(value, len);                                  \
}
// Overload for const char* and char*.  Could overload for signed & unsigned
// char too, but these are technically unneeded for printf compatibility.
TINYFORMAT_DEFINE_FORMAT_TRUNCATED_CSTR(const char)
TINYFORMAT_DEFINE_FORMAT_TRUNCATED_CSTR(char)
#undef TINYFORMAT_DEFINE_FORMAT_TRUNCATED_CSTR

} // namespace detail


//------------------------------------------------------------------------------
// Variable formatting functions.  May be overridden for user-defined types if
// desired.


/// Format a value into a stream, delegating to operator<< by default.
///
/// Users may override this for their own types.  When this function is called,
/// the stream flags will have been modified according to the format string.
/// The format specification is provided in the range [fmtBegin, fmtEnd).  For
/// truncating conversions, ntrunc is set to the desired maximum number of
/// characters, for example "%.7s" calls formatValue with ntrunc = 7.
///
/// By default, formatValue() uses the usual stream insertion operator
/// operator<< to format the type T, with special cases for the %c and %p
/// conversions.
template<typename T>
inline void formatValue(std::ostream& out, const char* /*fmtBegin*/,
                        const char* fmtEnd, int ntrunc, const T& value)
{
#ifndef TINYFORMAT_ALLOW_WCHAR_STRINGS
    // Since we don't support printing of wchar_t using "%ls", make it fail at
    // compile time in preference to printing as a void* at runtime.
    typedef typename detail::is_wchar<T>::tinyformat_wchar_is_not_supported DummyType;
    (void) DummyType(); // avoid unused type warning with gcc-4.8
#endif
    // The mess here is to support the %c and %p conversions: if these
    // conversions are active we try to convert the type to a char or const
    // void* respectively and format that instead of the value itself.  For the
    // %p conversion it's important to avoid dereferencing the pointer, which
    // could otherwise lead to a crash when printing a dangling (const char*).
    const bool canConvertToChar = detail::is_convertible<T,char>::value;
    const bool canConvertToVoidPtr = detail::is_convertible<T, const void*>::value;
    if(canConvertToChar && *(fmtEnd-1) == 'c')
        detail::formatValueAsType<T, char>::invoke(out, value);
    else if(canConvertToVoidPtr && *(fmtEnd-1) == 'p')
        detail::formatValueAsType<T, const void*>::invoke(out, value);
#ifdef TINYFORMAT_OLD_LIBSTDCPLUSPLUS_WORKAROUND
    else if(detail::formatZeroIntegerWorkaround<T>::invoke(out, value)) /**/;
#endif
    else if(ntrunc >= 0)
    {
        // Take care not to overread C strings in truncating conversions like
        // "%.4s" where at most 4 characters may be read.
        detail::formatTruncated(out, value, ntrunc);
    }
    else
        out << value;
}


// Overloaded version for char types to support printing as an integer
#define TINYFORMAT_DEFINE_FORMATVALUE_CHAR(charType)                  \
inline void formatValue(std::ostream& out, const char* /*fmtBegin*/,  \
                        const char* fmtEnd, int /**/, charType value) \
{                                                                     \
    switch(*(fmtEnd-1))                                               \
    {                                                                 \
        case 'u': case 'd': case 'i': case 'o': case 'X': case 'x':   \
            out << static_cast<int>(value); break;                    \
        default:                                                      \
            out << value;                   break;                    \
    }                                                                 \
}
// per 3.9.1: char, signed char and unsigned char are all distinct types
TINYFORMAT_DEFINE_FORMATVALUE_CHAR(char)
TINYFORMAT_DEFINE_FORMATVALUE_CHAR(signed char)
TINYFORMAT_DEFINE_FORMATVALUE_CHAR(unsigned char)
#undef TINYFORMAT_DEFINE_FORMATVALUE_CHAR


//------------------------------------------------------------------------------
// Tools for emulating variadic templates in C++98.  The basic idea here is
// stolen from the boost preprocessor metaprogramming library and cut down to
// be just general enough for what we need.

#define TINYFORMAT_ARGTYPES(n) TINYFORMAT_ARGTYPES_ ## n
#define TINYFORMAT_VARARGS(n) TINYFORMAT_VARARGS_ ## n
#define TINYFORMAT_PASSARGS(n) TINYFORMAT_PASSARGS_ ## n
#define TINYFORMAT_PASSARGS_TAIL(n) TINYFORMAT_PASSARGS_TAIL_ ## n

// To keep it as transparent as possible, the macros below have been generated
// using python via the excellent cog.py code generation script.  This avoids
// the need for a bunch of complex (but more general) preprocessor tricks as
// used in boost.preprocessor.
//
// To rerun the code generation in place, use `cog.py -r tinyformat.h`
// (see http://nedbatchelder.com/code/cog).  Alternatively you can just create
// extra versions by hand.

/*[[[cog
maxParams = 16

def makeCommaSepLists(lineTemplate, elemTemplate, startInd=1):
    for j in range(startInd,maxParams+1):
        list = ', '.join([elemTemplate % {'i':i} for i in range(startInd,j+1)])
        cog.outl(lineTemplate % {'j':j, 'list':list})

makeCommaSepLists('#define TINYFORMAT_ARGTYPES_%(j)d %(list)s',
                  'class T%(i)d')

cog.outl()
makeCommaSepLists('#define TINYFORMAT_VARARGS_%(j)d %(list)s',
                  'const T%(i)d& v%(i)d')

cog.outl()
makeCommaSepLists('#define TINYFORMAT_PASSARGS_%(j)d %(list)s', 'v%(i)d')

cog.outl()
cog.outl('#define TINYFORMAT_PASSARGS_TAIL_1')
makeCommaSepLists('#define TINYFORMAT_PASSARGS_TAIL_%(j)d , %(list)s',
                  'v%(i)d', startInd = 2)

cog.outl()
cog.outl('#define TINYFORMAT_FOREACH_ARGNUM(m) \\\n    ' +
         ' '.join(['m(%d)' % (j,) for j in range(1,maxParams+1)]))
]]]*/
#define TINYFORMAT_ARGTYPES_1 class T1
#define TINYFORMAT_ARGTYPES_2 class T1, class T2
#define TINYFORMAT_ARGTYPES_3 class T1, class T2, class T3
#define TINYFORMAT_ARGTYPES_4 class T1, class T2, class T3, class T4
#define TINYFORMAT_ARGTYPES_5 class T1, class T2, class T3, class T4, class T5
#define TINYFORMAT_ARGTYPES_6 class T1, class T2, class T3, class T4, class T5, class T6
#define TINYFORMAT_ARGTYPES_7 class T1, class T2, class T3, class T4, class T5, class T6, class T7
#define TINYFORMAT_ARGTYPES_8 class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8
#define TINYFORMAT_ARGTYPES_9 class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8, class T9
#define TINYFORMAT_ARGTYPES_10 class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8, class T9, class T10
#define TINYFORMAT_ARGTYPES_11 class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8, class T9, class T10, class T11
#define TINYFORMAT_ARGTYPES_12 class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8, class T9, class T10, class T11, class T12
#define TINYFORMAT_ARGTYPES_13 class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8, class T9, class T10, class T11, class T12, class T13
#define TINYFORMAT_ARGTYPES_14 class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8, class T9, class T10, class T11, class T12, class T13, class T14
#define TINYFORMAT_ARGTYPES_15 class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8, class T9, class T10, class T11, class T12, class T13, class T14, class T15
#define TINYFORMAT_ARGTYPES_16 class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8, class T9, class T10, class T11, class T12, class T13, class T14, class T15, class T16

#define TINYFORMAT_VARARGS_1 const T1& v1
#define TINYFORMAT_VARARGS_2 const T1& v1, const T2& v2
#define TINYFORMAT_VARARGS_3 const T1& v1, const T2& v2, const T3& v3
#define TINYFORMAT_VARARGS_4 const T1& v1, const T2& v2, const T3& v3, const T4& v4
#define TINYFORMAT_VARARGS_5 const T1& v1, const T2& v2, const T3& v3, const T4& v4, const T5& v5
#define TINYFORMAT_VARARGS_6 const T1& v1, const T2& v2, const T3& v3, const T4& v4, const T5& v5, const T6& v6
#define TINYFORMAT_VARARGS_7 const T1& v1, const T2& v2, const T3& v3, const T4& v4, const T5& v5, const T6& v6, const T7& v7
#define TINYFORMAT_VARARGS_8 const T1& v1, const T2& v2, const T3& v3, const T4& v4, const T5& v5, const T6& v6, const T7& v7, const T8& v8
#define TINYFORMAT_VARARGS_9 const T1& v1, const T2& v2, const T3& v3, const T4& v4, const T5& v5, const T6& v6, const T7& v7, const T8& v8, const T9& v9
#define TINYFORMAT_VARARGS_10 const T1& v1, const T2& v2, const T3& v3, const T4& v4, const T5& v5, const T6& v6, const T7& v7, const T8& v8, const T9& v9, const T10& v10
#define TINYFORMAT_VARARGS_11 const T1& v1, const T2& v2, const T3& v3, const T4& v4, const T5& v5, const T6& v6, const T7& v7, const T8& v8, const T9& v9, const T10& v10, const T11& v11
#define TINYFORMAT_VARARGS_12 const T1& v1, const T2& v2, const T3& v3, const T4& v4, const T5& v5, const T6& v6, const T7& v7, const T8& v8, const T9& v9, const T10& v10, const T11& v11, const T12& v12
#define TINYFORMAT_VARARGS_13 const T1& v1, const T2& v2, const T3& v3, const T4& v4, const T5& v5, const T6& v6, const T7& v7, const T8& v8, const T9& v9, const T10& v10, const T11& v11, const T12& v12, const T13& v13
#define TINYFORMAT_VARARGS_14 const T1& v1, const T2& v2, const T3& v3, const T4& v4, const T5& v5, const T6& v6, const T7& v7, const T8& v8, const T9& v9, const T10& v10, const T11& v11, const T12& v12, const T13& v13, const T14& v14
#define TINYFORMAT_VARARGS_15 const T1& v1, const T2& v2, const T3& v3, const T4& v4, const T5& v5, const T6& v6, const T7& v7, const T8& v8, const T9& v9, const T10& v10, const T11& v11, const T12& v12, const T13& v13, const T14& v14, const T15& v15
#define TINYFORMAT_VARARGS_16 const T1& v1, const T2& v2, const T3& v3, const T4& v4, const T5& v5, const T6& v6, const T7& v7, const T8& v8, const T9& v9, const T10& v10, const T11& v11, const T12& v12, const T13& v13, const T14& v14, const T15& v15, const T16& v16

#define TINYFORMAT_PASSARGS_1 v1
#define TINYFORMAT_PASSARGS_2 v1, v2
#define TINYFORMAT_PASSARGS_3 v1, v2, v3
#define TINYFORMAT_PASSARGS_4 v1, v2, v3, v4
#define TINYFORMAT_PASSARGS_5 v1, v2, v3, v4, v5
#define TINYFORMAT_PASSARGS_6 v1, v2, v3, v4, v5, v6
#define TINYFORMAT_PASSARGS_7 v1, v2, v3, v4, v5, v6, v7
#define TINYFORMAT_PASSARGS_8 v1, v2, v3, v4, v5, v6, v7, v8
#define TINYFORMAT_PASSARGS_9 v1, v2, v3, v4, v5, v6, v7, v8, v9
#define TINYFORMAT_PASSARGS_10 v1, v2, v3, v4, v5, v6, v7, v8, v9, v10
#define TINYFORMAT_PASSARGS_11 v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11
#define TINYFORMAT_PASSARGS_12 v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12
#define TINYFORMAT_PASSARGS_13 v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13
#define TINYFORMAT_PASSARGS_14 v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14
#define TINYFORMAT_PASSARGS_15 v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15
#define TINYFORMAT_PASSARGS_16 v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16

#define TINYFORMAT_PASSARGS_TAIL_1
#define TINYFORMAT_PASSARGS_TAIL_2 , v2
#define TINYFORMAT_PASSARGS_TAIL_3 , v2, v3
#define TINYFORMAT_PASSARGS_TAIL_4 , v2, v3, v4
#define TINYFORMAT_PASSARGS_TAIL_5 , v2, v3, v4, v5
#define TINYFORMAT_PASSARGS_TAIL_6 , v2, v3, v4, v5, v6
#define TINYFORMAT_PASSARGS_TAIL_7 , v2, v3, v4, v5, v6, v7
#define TINYFORMAT_PASSARGS_TAIL_8 , v2, v3, v4, v5, v6, v7, v8
#define TINYFORMAT_PASSARGS_TAIL_9 , v2, v3, v4, v5, v6, v7, v8, v9
#define TINYFORMAT_PASSARGS_TAIL_10 , v2, v3, v4, v5, v6, v7, v8, v9, v10
#define TINYFORMAT_PASSARGS_TAIL_11 , v2, v3, v4, v5, v6, v7, v8, v9, v10, v11
#define TINYFORMAT_PASSARGS_TAIL_12 , v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12
#define TINYFORMAT_PASSARGS_TAIL_13 , v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13
#define TINYFORMAT_PASSARGS_TAIL_14 , v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14
#define TINYFORMAT_PASSARGS_TAIL_15 , v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15
#define TINYFORMAT_PASSARGS_TAIL_16 , v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16

#define TINYFORMAT_FOREACH_ARGNUM(m) \
    m(1) m(2) m(3) m(4) m(5) m(6) m(7) m(8) m(9) m(10) m(11) m(12) m(13) m(14) m(15) m(16)
//[[[end]]]



namespace detail {

// Type-opaque holder for an argument to format(), with associated actions on
// the type held as explicit function pointers.  This allows FormatArg's for
// each argument to be allocated as a homogenous array inside FormatList
// whereas a naive implementation based on inheritance does not.
class FormatArg
{
    public:
        FormatArg()
            : m_value(NULL),
            m_formatImpl(NULL),
            m_toIntImpl(NULL)
        { }

        template<typename T>
        FormatArg(const T& value)
            : m_value(static_cast<const void*>(&value)),
            m_formatImpl(&formatImpl<T>),
            m_toIntImpl(&toIntImpl<T>)
        { }

        void format(std::ostream& out, const char* fmtBegin,
                    const char* fmtEnd, int ntrunc) const
        {
            TINYFORMAT_ASSERT(m_value);
            TINYFORMAT_ASSERT(m_formatImpl);
            m_formatImpl(out, fmtBegin, fmtEnd, ntrunc, m_value);
        }

        int toInt() const
        {
            TINYFORMAT_ASSERT(m_value);
            TINYFORMAT_ASSERT(m_toIntImpl);
            return m_toIntImpl(m_value);
        }

    private:
        template<typename T>
        TINYFORMAT_HIDDEN static void formatImpl(std::ostream& out, const char* fmtBegin,
                        const char* fmtEnd, int ntrunc, const void* value)
        {
            formatValue(out, fmtBegin, fmtEnd, ntrunc, *static_cast<const T*>(value));
        }

        template<typename T>
        TINYFORMAT_HIDDEN static int toIntImpl(const void* value)
        {
            return convertToInt<T>::invoke(*static_cast<const T*>(value));
        }

        const void* m_value;
        void (*m_formatImpl)(std::ostream& out, const char* fmtBegin,
                             const char* fmtEnd, int ntrunc, const void* value);
        int (*m_toIntImpl)(const void* value);
};


// Parse and return an integer from the string c, as atoi()
// On return, c is set to one past the end of the integer.
inline int parseIntAndAdvance(const char*& c)
{
    int i = 0;
    for(;*c >= '0' && *c <= '9'; ++c)
        i = 10*i + (*c - '0');
    return i;
}

// Print literal part of format string and return next format spec
// position.
//
// Skips over any occurrences of '%%', printing a literal '%' to the
// output.  The position of the first % character of the next
// nontrivial format spec is returned, or the end of string.
inline const char* printFormatStringLiteral(std::ostream& out, const char* fmt)
{
    const char* c = fmt;
    for(;; ++c)
    {
        switch(*c)
        {
            case '\0':
                out.write(fmt, c - fmt);
                return c;
            case '%':
                out.write(fmt, c - fmt);
                if(*(c+1) != '%')
                    return c;
                // for "%%", tack trailing % onto next literal section.
                fmt = ++c;
                break;
            default:
                break;
        }
    }
}


// Parse a format string and set the stream state accordingly.
//
// The format mini-language recognized here is meant to be the one from C99,
// with the form "%[flags][width][.precision][length]type".
//
// Formatting options which can't be natively represented using the ostream
// state are returned in spacePadPositive (for space padded positive numbers)
// and ntrunc (for truncating conversions).  argIndex is incremented if
// necessary to pull out variable width and precision .  The function returns a
// pointer to the character after the end of the current format spec.
inline const char* streamStateFromFormat(std::ostream& out, bool& spacePadPositive,
                                         int& ntrunc, const char* fmtStart,
                                         const detail::FormatArg* formatters,
                                         int& argIndex, int numFormatters)
{
    if(*fmtStart != '%')
    {
        TINYFORMAT_ERROR("tinyformat: Not enough conversion specifiers in format string");
        return fmtStart;
    }
    // Reset stream state to defaults.
    out.width(0);
    out.precision(6);
    out.fill(' ');
    // Reset most flags; ignore irrelevant unitbuf & skipws.
    out.unsetf(std::ios::adjustfield | std::ios::basefield |
               std::ios::floatfield | std::ios::showbase | std::ios::boolalpha |
               std::ios::showpoint | std::ios::showpos | std::ios::uppercase);
    bool precisionSet = false;
    bool widthSet = false;
    int widthExtra = 0;
    const char* c = fmtStart + 1;
    // 1) Parse flags
    for(;; ++c)
    {
        switch(*c)
        {
            case '#':
                out.setf(std::ios::showpoint | std::ios::showbase);
                continue;
            case '0':
                // overridden by left alignment ('-' flag)
                if(!(out.flags() & std::ios::left))
                {
                    // Use internal padding so that numeric values are
                    // formatted correctly, eg -00010 rather than 000-10
                    out.fill('0');
                    out.setf(std::ios::internal, std::ios::adjustfield);
                }
                continue;
            case '-':
                out.fill(' ');
                out.setf(std::ios::left, std::ios::adjustfield);
                continue;
            case ' ':
                // overridden by show positive sign, '+' flag.
                if(!(out.flags() & std::ios::showpos))
                    spacePadPositive = true;
                continue;
            case '+':
                out.setf(std::ios::showpos);
                spacePadPositive = false;
                widthExtra = 1;
                continue;
            default:
                break;
        }
        break;
    }
    // 2) Parse width
    if(*c >= '0' && *c <= '9')
    {
        widthSet = true;
        out.width(parseIntAndAdvance(c));
    }
    if(*c == '*')
    {
        widthSet = true;
        int width = 0;
        if(argIndex < numFormatters)
            width = formatters[argIndex++].toInt();
        else
            TINYFORMAT_ERROR("tinyformat: Not enough arguments to read variable width");
        if(width < 0)
        {
            // negative widths correspond to '-' flag set
            out.fill(' ');
            out.setf(std::ios::left, std::ios::adjustfield);
            width = -width;
        }
        out.width(width);
        ++c;
    }
    // 3) Parse precision
    if(*c == '.')
    {
        ++c;
        int precision = 0;
        if(*c == '*')
        {
            ++c;
            if(argIndex < numFormatters)
                precision = formatters[argIndex++].toInt();
            else
                TINYFORMAT_ERROR("tinyformat: Not enough arguments to read variable precision");
        }
        else
        {
            if(*c >= '0' && *c <= '9')
                precision = parseIntAndAdvance(c);
            else if(*c == '-') // negative precisions ignored, treated as zero.
                parseIntAndAdvance(++c);
        }
        out.precision(precision);
        precisionSet = true;
    }
    // 4) Ignore any C99 length modifier
    while(*c == 'l' || *c == 'h' || *c == 'L' ||
          *c == 'j' || *c == 'z' || *c == 't')
        ++c;
    // 5) We're up to the conversion specifier character.
    // Set stream flags based on conversion specifier (thanks to the
    // boost::format class for forging the way here).
    bool intConversion = false;
    switch(*c)
    {
        case 'u': case 'd': case 'i':
            out.setf(std::ios::dec, std::ios::basefield);
            intConversion = true;
            break;
        case 'o':
            out.setf(std::ios::oct, std::ios::basefield);
            intConversion = true;
            break;
        case 'X':
            out.setf(std::ios::uppercase);
            [[fallthrough]]; // Falls through
        case 'x': case 'p':
            out.setf(std::ios::hex, std::ios::basefield);
            intConversion = true;
            break;
        case 'E':
            out.setf(std::ios::uppercase);
            [[fallthrough]]; // Falls through
        case 'e':
            out.setf(std::ios::scientific, std::ios::floatfield);
            out.setf(std::ios::dec, std::ios::basefield);
            break;
        case 'F':
            out.setf(std::ios::uppercase);
            [[fallthrough]]; // Falls through
        case 'f':
            out.setf(std::ios::fixed, std::ios::floatfield);
            break;
        case 'G':
            out.setf(std::ios::uppercase);
            [[fallthrough]]; // Falls through
        case 'g':
            out.setf(std::ios::dec, std::ios::basefield);
            // As in boost::format, let stream decide float format.
            out.flags(out.flags() & ~std::ios::floatfield);
            break;
        case 'a': case 'A':
            TINYFORMAT_ERROR("tinyformat: the %a and %A conversion specs "
                             "are not supported");
            break;
        case 'c':
            // Handled as special case inside formatValue()
            break;
        case 's':
            if(precisionSet)
                ntrunc = static_cast<int>(out.precision());
            // Make %s print booleans as "true" and "false"
            out.setf(std::ios::boolalpha);
            break;
        case 'n':
            // Not supported - will cause problems!
            TINYFORMAT_ERROR("tinyformat: %n conversion spec not supported");
            break;
        case '\0':
            TINYFORMAT_ERROR("tinyformat: Conversion spec incorrectly "
                             "terminated by end of string");
            return c;
        default:
            break;
    }
    if(intConversion && precisionSet && !widthSet)
    {
        // "precision" for integers gives the minimum number of digits (to be
        // padded with zeros on the left).  This isn't really supported by the
        // iostreams, but we can approximately simulate it with the width if
        // the width isn't otherwise used.
        out.width(out.precision() + widthExtra);
        out.setf(std::ios::internal, std::ios::adjustfield);
        out.fill('0');
    }
    return c+1;
}


//------------------------------------------------------------------------------
inline void formatImpl(std::ostream& out, const char* fmt,
                       const detail::FormatArg* formatters,
                       int numFormatters)
{
    // Saved stream state
    std::streamsize origWidth = out.width();
    std::streamsize origPrecision = out.precision();
    std::ios::fmtflags origFlags = out.flags();
    char origFill = out.fill();

    for (int argIndex = 0; argIndex < numFormatters; ++argIndex)
    {
        // Parse the format string
        fmt = printFormatStringLiteral(out, fmt);
        bool spacePadPositive = false;
        int ntrunc = -1;
        const char* fmtEnd = streamStateFromFormat(out, spacePadPositive, ntrunc, fmt,
                                                   formatters, argIndex, numFormatters);
        if (argIndex >= numFormatters)
        {
            // Check args remain after reading any variable width/precision
            TINYFORMAT_ERROR("tinyformat: Not enough format arguments");
            return;
        }
        const FormatArg& arg = formatters[argIndex];
        // Format the arg into the stream.
        if(!spacePadPositive)
            arg.format(out, fmt, fmtEnd, ntrunc);
        else
        {
            // The following is a special case with no direct correspondence
            // between stream formatting and the printf() behaviour.  Simulate
            // it crudely by formatting into a temporary string stream and
            // munging the resulting string.
            std::ostringstream tmpStream;
            tmpStream.copyfmt(out);
            tmpStream.setf(std::ios::showpos);
            arg.format(tmpStream, fmt, fmtEnd, ntrunc);
            std::string result = tmpStream.str(); // allocates... yuck.
            for(size_t i = 0, iend = result.size(); i < iend; ++i)
                if(result[i] == '+') result[i] = ' ';
            out << result;
        }
        fmt = fmtEnd;
    }

    // Print remaining part of format string.
    fmt = printFormatStringLiteral(out, fmt);
    if(*fmt != '\0')
        TINYFORMAT_ERROR("tinyformat: Too many conversion specifiers in format string");

    // Restore stream state
    out.width(origWidth);
    out.precision(origPrecision);
    out.flags(origFlags);
    out.fill(origFill);
}

} // namespace detail


/// List of template arguments format(), held in a type-opaque way.
///
/// A const reference to FormatList (typedef'd as FormatListRef) may be
/// conveniently used to pass arguments to non-template functions: All type
/// information has been stripped from the arguments, leaving just enough of a
/// common interface to perform formatting as required.
class FormatList
{
    public:
        FormatList(detail::FormatArg* formatters, int N)
            : m_formatters(formatters), m_N(N) { }

        friend void vformat(std::ostream& out, const char* fmt,
                            const FormatList& list);

    private:
        const detail::FormatArg* m_formatters;
        int m_N;
};

/// Reference to type-opaque format list for passing to vformat()
typedef const FormatList& FormatListRef;


namespace detail {

// Format list subclass with fixed storage to avoid dynamic allocation
template<int N>
class FormatListN : public FormatList
{
    public:
#ifdef TINYFORMAT_USE_VARIADIC_TEMPLATES
        template<typename... Args>
        FormatListN(const Args&... args)
            : FormatList(&m_formatterStore[0], N),
            m_formatterStore { FormatArg(args)... }
        { static_assert(sizeof...(args) == N, "Number of args must be N"); }
#else // C++98 version
        void init(int) {}
#       define TINYFORMAT_MAKE_FORMATLIST_CONSTRUCTOR(n)                \
                                                                        \
        template<TINYFORMAT_ARGTYPES(n)>                                \
        FormatListN(TINYFORMAT_VARARGS(n))                              \
            : FormatList(&m_formatterStore[0], n)                       \
        { TINYFORMAT_ASSERT(n == N); init(0, TINYFORMAT_PASSARGS(n)); } \
                                                                        \
        template<TINYFORMAT_ARGTYPES(n)>                                \
        void init(int i, TINYFORMAT_VARARGS(n))                         \
        {                                                               \
            m_formatterStore[i] = FormatArg(v1);                        \
            init(i+1 TINYFORMAT_PASSARGS_TAIL(n));                      \
        }

        TINYFORMAT_FOREACH_ARGNUM(TINYFORMAT_MAKE_FORMATLIST_CONSTRUCTOR)
#       undef TINYFORMAT_MAKE_FORMATLIST_CONSTRUCTOR
#endif

    private:
        FormatArg m_formatterStore[N];
};

// Special 0-arg version - MSVC says zero-sized C array in struct is nonstandard
template<> class FormatListN<0> : public FormatList
{
    public: FormatListN() : FormatList(0, 0) {}
};

} // namespace detail


//------------------------------------------------------------------------------
// Primary API functions

#ifdef TINYFORMAT_USE_VARIADIC_TEMPLATES

/// Make type-agnostic format list from list of template arguments.
///
/// The exact return type of this function is an implementation detail and
/// shouldn't be relied upon.  Instead it should be stored as a FormatListRef:
///
///   FormatListRef formatList = makeFormatList( /*...*/ );
template<typename... Args>
detail::FormatListN<sizeof...(Args)> makeFormatList(const Args&... args)
{
    return detail::FormatListN<sizeof...(args)>(args...);
}

#else // C++98 version

inline detail::FormatListN<0> makeFormatList()
{
    return detail::FormatListN<0>();
}
#define TINYFORMAT_MAKE_MAKEFORMATLIST(n)                     \
template<TINYFORMAT_ARGTYPES(n)>                              \
detail::FormatListN<n> makeFormatList(TINYFORMAT_VARARGS(n))  \
{                                                             \
    return detail::FormatListN<n>(TINYFORMAT_PASSARGS(n));    \
}
TINYFORMAT_FOREACH_ARGNUM(TINYFORMAT_MAKE_MAKEFORMATLIST)
#undef TINYFORMAT_MAKE_MAKEFORMATLIST

#endif

/// Format list of arguments to the stream according to the given format string.
///
/// The name vformat() is chosen for the semantic similarity to vprintf(): the
/// list of format arguments is held in a single function argument.
inline void vformat(std::ostream& out, const char* fmt, FormatListRef list)
{
    detail::formatImpl(out, fmt, list.m_formatters, list.m_N);
}


#ifdef TINYFORMAT_USE_VARIADIC_TEMPLATES

/// Format list of arguments to the stream according to given format string.
template<typename... Args>
void format(std::ostream& out, const char* fmt, const Args&... args)
{
    vformat(out, fmt, makeFormatList(args...));
}

/// Format list of arguments according to the given format string and return
/// the result as a string.
template<typename... Args>
std::string format(const char* fmt, const Args&... args)
{
    std::ostringstream oss;
    format(oss, fmt, args...);
    return oss.str();
}

/// Format list of arguments to std::cout, according to the given format string
template<typename... Args>
void printf(const char* fmt, const Args&... args)
{
    format(std::cout, fmt, args...);
}

template<typename... Args>
void printfln(const char* fmt, const Args&... args)
{
    format(std::cout, fmt, args...);
    std::cout << '\n';
}


#else // C++98 version

inline void format(std::ostream& out, const char* fmt)
{
    vformat(out, fmt, makeFormatList());
}

inline std::string format(const char* fmt)
{
    std::ostringstream oss;
    format(oss, fmt);
    return oss.str();
}

inline void printf(const char* fmt)
{
    format(std::cout, fmt);
}

inline void printfln(const char* fmt)
{
    format(std::cout, fmt);
    std::cout << '\n';
}

#define TINYFORMAT_MAKE_FORMAT_FUNCS(n)                                   \
                                                                          \
template<TINYFORMAT_ARGTYPES(n)>                                          \
void format(std::ostream& out, const char* fmt, TINYFORMAT_VARARGS(n))    \
{                                                                         \
    vformat(out, fmt, makeFormatList(TINYFORMAT_PASSARGS(n)));            \
}                                                                         \
                                                                          \
template<TINYFORMAT_ARGTYPES(n)>                                          \
std::string format(const char* fmt, TINYFORMAT_VARARGS(n))                \
{                                                                         \
    std::ostringstream oss;                                               \
    format(oss, fmt, TINYFORMAT_PASSARGS(n));                             \
    return oss.str();                                                     \
}                                                                         \
                                                                          \
template<TINYFORMAT_ARGTYPES(n)>                                          \
void printf(const char* fmt, TINYFORMAT_VARARGS(n))                       \
{                                                                         \
    format(std::cout, fmt, TINYFORMAT_PASSARGS(n));                       \
}                                                                         \
                                                                          \
template<TINYFORMAT_ARGTYPES(n)>                                          \
void printfln(const char* fmt, TINYFORMAT_VARARGS(n))                     \
{                                                                         \
    format(std::cout, fmt, TINYFORMAT_PASSARGS(n));                       \
    std::cout << '\n';                                                    \
}

TINYFORMAT_FOREACH_ARGNUM(TINYFORMAT_MAKE_FORMAT_FUNCS)
#undef TINYFORMAT_MAKE_FORMAT_FUNCS

#endif


} // namespace tinyformat

#endif // TINYFORMAT_H_INCLUDED

#pragma region "File: Core/Types.h"


#include <cstring>
#include <ostream>
#include <string>

class Identifier
{
public:
	explicit Identifier() = default;
	explicit Identifier(std::string name);
	Identifier(const Identifier &) = default;
	Identifier(Identifier &&) = default;

	Identifier &operator=(const Identifier &) = default;
	Identifier &operator=(Identifier &&) = default;

	size_t size() const
	{
		return _name.size();
	}

	bool startsWith(char value) const
	{
		return _name.front() == value;
	}

	bool startsWith(std::string_view value) const
	{
		if (_name.size() < value.size())
			return false;

		return memcmp(_name.data(), value.data(), value.size()) == 0;
	}

	const std::string &string() const
	{
		return _name;
	}

	bool operator<(const Identifier& other) const
	{
		return _name < other._name;
	}

	bool operator==(const Identifier& other) const
	{
		return _name == other._name;
	}

	bool operator!=(const Identifier& other) const
	{
		return _name != other._name;
	}

	bool operator==(const std::string_view other) const
	{
		return _name == other;
	}

	bool operator!=(const std::string_view other) const
	{
		return _name != other;
	}
private:
	std::string _name;
};

inline bool operator==(const std::string_view first, const Identifier &second)
{
	return second == first;
}

inline bool operator!=(const std::string_view first, const Identifier &second)
{
	return second != first;
}

std::ostream& operator<<(std::ostream &output, const Identifier &identifier);


class StringLiteral
{
public:
	StringLiteral() = default;
	StringLiteral(std::string value);
	StringLiteral(const StringLiteral &) = default;
	StringLiteral(StringLiteral &&) = default;

	StringLiteral &operator=(const StringLiteral &) = default;
	StringLiteral &operator=(StringLiteral &&) = default;

	size_t size() const
	{
		return _value.size();
	}

	const std::string &string() const
	{
		return _value;
	}

	fs::path path() const
	{
		return fs::u8path(_value);
	}

	StringLiteral operator+(const StringLiteral &other) const;
	bool operator==(const StringLiteral &other) const;
	bool operator!=(const StringLiteral &other) const;
	bool operator<(const StringLiteral &other) const;
	bool operator<=(const StringLiteral &other) const;
	bool operator>(const StringLiteral &other) const;
	bool operator>=(const StringLiteral &other) const;

private:
	std::string _value;
};

std::ostream& operator<<(std::ostream &output, const StringLiteral &string);

#pragma region "File: Core/Types.cpp"

Identifier::Identifier(std::string name) :
	_name(std::move(name))
{
}

std::ostream &operator<<(std::ostream &output, const Identifier &identifier)
{
	output << identifier.string();
	return output;
}

StringLiteral::StringLiteral(std::string value) :
	_value(std::move(value))
{
}

StringLiteral StringLiteral::operator+(const StringLiteral &other) const
{
	return StringLiteral(_value + other._value);
}

bool StringLiteral::operator==(const StringLiteral &other) const
{
	return _value == other._value;
}

bool StringLiteral::operator!=(const StringLiteral &other) const
{
	return _value != other._value;
}

bool StringLiteral::operator<(const StringLiteral &other) const
{
	return _value < other._value;
}

bool StringLiteral::operator<=(const StringLiteral &other) const
{
	return _value <= other._value;
}

bool StringLiteral::operator>(const StringLiteral &other) const
{
	return _value > other._value;
}

bool StringLiteral::operator>=(const StringLiteral &other) const
{
	return _value >= other._value;
}

std::ostream &operator<<(std::ostream &output, const StringLiteral &value)
{
	output << value.string();
	return output;
}

#pragma region "File: Commands/CAssemblerCommand.h"

class TempData;
class SymbolData;

struct ValidateState
{
	bool noFileChange = false;
	const char *noFileChangeDirective = nullptr;
	int passes = 0;
};

class CAssemblerCommand
{
public:
	CAssemblerCommand();
	virtual ~CAssemblerCommand() { };
	virtual bool Validate(const ValidateState &state) = 0;
	virtual void Encode() const = 0;
	virtual void writeTempData(TempData& tempData) const = 0;
	virtual void writeSymData(SymbolData& symData) const { };
	void applyFileInfo();
	int getSection() { return section; }
	void updateSection(int num) { section = num; }
protected:
	int FileNum;
	int FileLine;
private:
	int section;
};

class DummyCommand: public CAssemblerCommand
{
public:
	bool Validate(const ValidateState &state) override { return false; }
	void Encode() const override { };
	void writeTempData(TempData& tempData) const override { };
	void writeSymData(SymbolData& symData) const override { };
};

class InvalidCommand: public CAssemblerCommand
{
public:
	bool Validate(const ValidateState &state) override { return false; }
	void Encode() const override { };
	void writeTempData(TempData& tempData) const override { };
	void writeSymData(SymbolData& symData) const override { };
};

#pragma region "File: Core/Expression.h"


#include <cassert>
#include <memory>
#include <string>
#include <variant>
#include <vector>

class Label;

struct ExpressionFunctionEntry;
struct ExpressionLabelFunctionEntry;

enum class OperatorType
{
	Invalid,
	Integer,
	Float,
	Identifier,
	String,
	MemoryPos,
	Add,
	Sub,
	Mult,
	Div,
	Mod,
	Neg,
	LogNot,
	BitNot,
	LeftShift,
	RightShift,
	Less,
	Greater,
	LessEqual,
	GreaterEqual,
	Equal,
	NotEqual,
	BitAnd,
	Xor,
	BitOr,
	LogAnd,
	LogOr,
	TertiaryIf,
	ToString,
	FunctionCall
};

enum class ExpressionValueType { Invalid, Integer, Float, String };

struct ExpressionValue
{
	ExpressionValueType type;

	ExpressionValue()
	{
		type = ExpressionValueType::Invalid;
		intValue = 0;
	}

	ExpressionValue(int64_t value)
	{
		type = ExpressionValueType::Integer;
		intValue = value;
	}

	ExpressionValue(double value)
	{
		type = ExpressionValueType::Float;
		floatValue = value;
	}

	ExpressionValue(const StringLiteral& value)
		: type(ExpressionValueType::String), strValue(value)
	{
		intValue = 0;
	}

	bool isFloat() const
	{
		return type == ExpressionValueType::Float;
	}

	bool isInt() const
	{
		return type == ExpressionValueType::Integer;
	}

	bool isString() const
	{
		return type == ExpressionValueType::String;
	}

	bool isValid() const
	{
		return type != ExpressionValueType::Invalid;
	}

	union
	{
		int64_t intValue;
		double floatValue;
	};

	StringLiteral strValue;

	ExpressionValue operator!() const;
	ExpressionValue operator~() const;
	bool operator<(const ExpressionValue& other) const;
	bool operator<=(const ExpressionValue& other) const;
	bool operator>(const ExpressionValue& other) const;
	bool operator>=(const ExpressionValue& other) const;
	bool operator==(const ExpressionValue& other) const;
	bool operator!=(const ExpressionValue& other) const;
	ExpressionValue operator+(const ExpressionValue& other) const;
	ExpressionValue operator-(const ExpressionValue& other) const;
	ExpressionValue operator*(const ExpressionValue& other) const;
	ExpressionValue operator/(const ExpressionValue& other) const;
	ExpressionValue operator%(const ExpressionValue& other) const;
	ExpressionValue operator<<(const ExpressionValue& other) const;
	ExpressionValue operator>>(const ExpressionValue& other) const;
	ExpressionValue operator&(const ExpressionValue& other) const;
	ExpressionValue operator|(const ExpressionValue& other) const;
	ExpressionValue operator&&(const ExpressionValue& other) const;
	ExpressionValue operator||(const ExpressionValue& other) const;
	ExpressionValue operator^(const ExpressionValue& other) const;
};

class ExpressionInternal
{
public:
	ExpressionInternal() = default;
	~ExpressionInternal() = default;
	ExpressionInternal(int64_t value);
	ExpressionInternal(double value);
	ExpressionInternal(Identifier value);
	ExpressionInternal(StringLiteral value);

	template<typename... ARGS>
	ExpressionInternal(OperatorType op, ARGS... parameters) :
		type(op)
	{
		( children.push_back(std::move(parameters)), ... );
	}

	ExpressionInternal(const Identifier& name, std::vector<std::unique_ptr<ExpressionInternal>> parameters);
	ExpressionValue evaluate();
	std::string toString();
	bool isIdentifier() { return type == OperatorType::Identifier; }
	const Identifier &getIdentifier() { return valueAs<Identifier>(); }
	void replaceMemoryPos(const Identifier& identifierName);
	bool simplify(bool inUnknownOrFalseBlock);
	unsigned int getFileNum() { return fileNum; }
	unsigned int getSection() { return section; }
private:
	using ValueTypes = std::variant<std::monostate, int64_t, double, StringLiteral, Identifier>;

	template<typename T>
	const T &valueAs() const
	{
		assert(std::holds_alternative<T>(value));
		return *std::get_if<T>(&value);
	}

	std::string formatFunctionCall();
	ExpressionValue executeFunctionCall();

	OperatorType type = OperatorType::Invalid;
	std::vector<std::unique_ptr<ExpressionInternal>> children;

	ValueTypes value;

	unsigned int fileNum, section;
};

class Expression
{
public:
	Expression() = default;
	Expression(std::unique_ptr<ExpressionInternal> exp, bool inUnknownOrFalseBlock);

	ExpressionValue evaluate();
	bool isLoaded() const { return expression != nullptr; }
	void replaceMemoryPos(const Identifier& identifierName);
	bool isConstExpression() { return constExpression; }

	template<typename T>
	bool evaluateInteger(T& dest)
	{
		if (expression == nullptr)
			return false;

		ExpressionValue value = expression->evaluate();
		if (value.isInt() == false)
			return false;

		dest = (T) value.intValue;
		return true;
	}

	bool evaluateString(StringLiteral& dest, bool convert);
	bool evaluateIdentifier(Identifier& dest);
	std::string toString();
private:
	std::shared_ptr<ExpressionInternal> expression;
	bool constExpression = true;
};

Expression createConstExpression(int64_t value);

#pragma region "File: Parser/Tokenizer.h"


#include <cassert>
#include <list>
#include <string>
#include <variant>
#include <vector>

class TextFile;

enum class TokenType
{
	Invalid,
	Identifier,
	Integer,
	String,
	Float,
	LParen,
	RParen,
	Plus,
	Minus,
	Mult,
	Div,
	Mod,
	Caret,
	Tilde,
	LeftShift,
	RightShift,
	Less,
	Greater,
	LessEqual,
	GreaterEqual,
	Equal,
	NotEqual,
	BitAnd,
	BitOr,
	LogAnd,
	LogOr,
	Exclamation,
	Question,
	Colon,
	LBrack,
	RBrack,
	Comma,
	Assign,
	Equ,
	EquValue,
	Hash,
	LBrace,
	RBrace,
	Dollar,
	NumberString,
	Degree,
	Separator
};

struct Token
{
	friend class Tokenizer;

	const std::string &getOriginalText() const
	{
		return originalText;
	}

	template<typename T>
	void setValue(T value, std::string originalText)
	{
		this->value = std::move(value);
		this->originalText = std::move(originalText);
	}

	const Identifier &identifierValue() const
	{
		assert(std::holds_alternative<Identifier>(value));
		return *std::get_if<Identifier>(&value);
	}

	const StringLiteral &stringValue() const
	{
		assert(std::holds_alternative<StringLiteral>(value));
		return *std::get_if<StringLiteral>(&value);
	}

	int64_t intValue() const
	{
		assert(std::holds_alternative<int64_t>(value));
		return *std::get_if<int64_t>(&value);
	}

	double floatValue() const
	{
		assert(std::holds_alternative<double>(value));
		return *std::get_if<double>(&value);
	}

	size_t line = 0;
	size_t column = 0;
	TokenType type = TokenType::Invalid;

protected:
	bool checked = false;

	using ValueType = std::variant<std::monostate, int64_t, double, StringLiteral, Identifier>;
	ValueType value;
	std::string originalText;
};

typedef std::list<Token> TokenList;

struct TokenizerPosition
{
	friend class Tokenizer;

	TokenizerPosition previous()
	{
		TokenizerPosition pos = *this;
		--pos.it;
		return pos;
	}
private:
	TokenList::iterator it;
};

class Tokenizer
{
public:
	Tokenizer();
	const Token& nextToken();
	const Token& peekToken(int ahead = 0);
	void eatToken() { eatTokens(1); }
	void eatTokens(int num);
	bool atEnd() { return position.it == tokens.end(); }
	TokenizerPosition getPosition() { return position; }
	void setPosition(TokenizerPosition pos) { position = pos; }
	void skipLookahead();
	std::vector<Token> getTokens(TokenizerPosition start, TokenizerPosition end) const;
	void registerReplacement(const Identifier& identifier, std::vector<Token>& tokens);
	void registerReplacement(const Identifier& identifier, const std::string& newValue);
	void registerReplacementString(const Identifier& identifier, const StringLiteral& newValue);
	void registerReplacementInteger(const Identifier& identifier, int64_t newValue);
	void registerReplacementFloat(const Identifier& identifier, double newValue);
	static size_t addEquValue(const std::vector<Token>& tokens);
	static void clearEquValues() { equValues.clear(); }
	void resetLookaheadCheckMarks();
protected:
	void clearTokens() { tokens.clear(); };
	void resetPosition() { position.it = tokens.begin(); }
	void addToken(Token token);
private:
	bool processElement(TokenList::iterator& it);

	TokenList tokens;
	TokenizerPosition position;

	struct Replacement
	{
		Identifier identifier;
		std::vector<Token> value;
	};

	Token invalidToken;
	std::vector<Replacement> replacements;
	static std::vector<std::vector<Token>> equValues;
};

class FileTokenizer: public Tokenizer
{
public:
	bool init(TextFile* input);
protected:
	Token loadToken();
	bool isInputAtEnd();

	void skipWhitespace();
	void createToken(TokenType type, size_t length);
	void createToken(TokenType type, size_t length, int64_t value);
	void createToken(TokenType type, size_t length, double value);
	void createToken(TokenType type, size_t length, const std::string& value);
	void createToken(TokenType type, size_t length, const std::string& value, size_t valuePos, size_t valueLength);
	void createTokenCurrentString(TokenType type, size_t length);

	bool convertInteger(size_t start, size_t end, int64_t& result);
	bool convertFloat(size_t start, size_t end, double& result);
	bool parseOperator();

	TextFile* input;
	std::string currentLine;
	size_t lineNumber;
	size_t linePos;

	Token token;
	bool equActive;
};

class TokenStreamTokenizer: public Tokenizer
{
public:
	void init(const std::vector<Token>& tokens)
	{
		clearTokens();

		for (const Token &tok: tokens)
			addToken(tok);

		resetPosition();
	}
};

#pragma region "File: Core/ExpressionFunctionHandler.h"

#include <optional>
#include <functional>
#include <map>
#include <memory>
#include <string>
#include <vector>

class ExpressionFunctionHandle;
class ExpressionInternal;
class Identifier;
class Label;

struct ExpressionValue;
struct Token;

using ExpressionFunction = ExpressionValue (*)(const Identifier& funcName, const std::vector<ExpressionValue>&);
using ExpressionLabelFunction = ExpressionValue (*)(const Identifier& funcName, const std::vector<std::shared_ptr<Label>> &);

enum class ExpFuncSafety
{
	// Result may depend entirely on the internal state
	Unsafe,
	// Result is unsafe in conditional blocks, safe otherwise
	ConditionalUnsafe,
	// Result is completely independent of the internal state
	Safe,
};

class ExpressionFunctionHandler
{
	friend class ExpressionFunctionHandle;

public:
	static ExpressionFunctionHandler &instance();

	std::optional<ExpressionFunctionHandle> find(const Identifier &name) const;

	void reset();
	void updateArchitecture();

	bool addFunction(const Identifier &name, ExpressionFunction functor, size_t minParams, size_t maxParams, ExpFuncSafety safety);
	bool addLabelFunction(const Identifier &name, ExpressionLabelFunction functor, size_t minParams, size_t maxParams, ExpFuncSafety safety);
	bool addUserFunction(const Identifier &name, const std::vector<Identifier> &parameters, const std::vector<Token> &content);
private:
	struct Entry
	{
		std::function<ExpressionValue(const std::vector<std::unique_ptr<ExpressionInternal>> &)> f;
		size_t minParams = 0;
		size_t maxParams = 0;
		ExpFuncSafety safety = ExpFuncSafety::Unsafe;
	};

	ExpressionFunctionHandler();
	bool registerEntry(const Identifier &name, Entry entry);

	std::map<Identifier,Entry> entries;
	std::vector<Identifier> architectureFunctions;
	bool registeringArchitecture = false;
};

class ExpressionFunctionHandle
{
public:
	ExpressionFunctionHandle(const ExpressionFunctionHandler::Entry &entry);

	size_t minParams() const;
	size_t maxParams() const;
	ExpFuncSafety safety() const;
	ExpressionValue execute(const std::vector<std::unique_ptr<ExpressionInternal>> &parameters) const;

private:
	const ExpressionFunctionHandler::Entry &impl;
};

#pragma region "File: Core/ExpressionFunctions.h"

#include <map>
#include <memory>
#include <string>
#include <vector>


bool getExpFuncParameter(const std::vector<ExpressionValue>& parameters, size_t index, int64_t& dest,
	const Identifier &funcName, bool optional);

bool getExpFuncParameter(const std::vector<ExpressionValue>& parameters, size_t index, const StringLiteral*& dest,
	const Identifier &funcName, bool optional);

struct ExpressionFunctionEntry
{
	const char *name;
	ExpressionFunction function;
	size_t minParams;
	size_t maxParams;
	ExpFuncSafety safety;
};

struct ExpressionLabelFunctionEntry
{
	const char *name;
	ExpressionLabelFunction function;
	size_t minParams;
	size_t maxParams;
	ExpFuncSafety safety;
};

void registerExpressionFunctions(ExpressionFunctionHandler &handler);

#pragma region "File: Core/SymbolData.h"


#include <cstdint>
#include <set>
#include <string>
#include <vector>

class AssemblerFile;

struct SymDataSymbol
{
	std::string name;
	int64_t address;

	bool operator<(const SymDataSymbol& other) const
	{
		return address < other.address;
	}
};

struct SymDataAddressInfo
{
	int64_t address;
	size_t fileIndex;
	size_t lineNumber;

	bool operator<(const SymDataAddressInfo& other) const
	{
		return address < other.address;
	}
};

struct SymDataFunction
{
	int64_t address;
	size_t size;

	bool operator<(const SymDataFunction& other) const
	{
		return address < other.address;
	}
};

struct SymDataData
{
	int64_t address;
	size_t size;
	int type;

	bool operator<(const SymDataData& other) const
	{
		if (address != other.address)
			return address < other.address;

		if (size != other.size)
			return size < other.size;

		return type < other.type;
	}
};

struct SymDataModule
{
	AssemblerFile* file;
	std::vector<SymDataSymbol> symbols;
	std::vector<SymDataFunction> functions;
	std::set<SymDataData> data;
};

struct SymDataModuleInfo
{
	unsigned int crc32;
};

class SymbolData
{
public:
	enum DataType { Data8, Data16, Data32, Data64, DataAscii };

	SymbolData();
	void clear();
	void setNocashSymFileName(const fs::path& name, int version) { nocashSymFileName = name; nocashSymVersion = version; };
	void write();
	void setEnabled(bool b) { enabled = b; };

	void addLabel(int64_t address, const std::string& name);
	void addData(int64_t address, size_t size, DataType type);
	void startModule(AssemblerFile* file);
	void endModule(AssemblerFile* file);
	void startFunction(int64_t address);
	void endFunction(int64_t address);
private:
	void writeNocashSym();
	size_t addFileName(const std::string& fileName);

	fs::path nocashSymFileName;
	bool enabled;
	int nocashSymVersion;

	// entry 0 is for data without parent modules
	std::vector<SymDataModule> modules;
	std::vector<std::string> files;
	int currentModule;
	int currentFunction;
};

#pragma region "File: Util/Util.h"

#include <cstdint>
#include <string>
#include <vector>

std::string convertUnicodeCharToUtf8(char32_t character);
std::string convertWStringToUtf8(std::wstring_view source);

bool stringToInt(const std::string& line, size_t start, size_t end, int64_t& result);
int32_t getFloatBits(float value);
float bitsToFloat(int32_t value);
int64_t getDoubleBits(double value);

std::string toLowercase(const std::string& str);
size_t replaceAll(std::string& str, const char* oldValue,const std::string& newValue);
bool startsWith(const std::string& str, const char* value, size_t stringPos = 0);

#pragma region "File: Util/FileClasses.h"


#include <list>
#include <optional>
#include <vector>


class TextFile
{
public:
	enum Encoding { ASCII, UTF8, UTF16LE, UTF16BE, SJIS, GUESS };
	enum Mode { Read, Write };

	TextFile();
	~TextFile();
	void openMemory(const std::string& content);
	bool open(const fs::path& fileName, Mode mode, Encoding defaultEncoding = GUESS);
	bool open(Mode mode, Encoding defaultEncoding = GUESS);
	bool isOpen() { return fromMemory || stream.is_open(); };
	bool atEnd() { return isOpen() && mode == Read && tell() >= size_; };
	long size() { return size_; };
	void close();

	bool hasGuessedEncoding() { return guessedEncoding; };
	bool isFromMemory() { return fromMemory; }
	int getNumLines() { return lineCount; }

	void setFileName(const fs::path& name) { fileName = name; };
	const fs::path& getFileName() { return fileName; };

	std::string readLine();
	std::vector<std::string> readAll();
	void write(const char* value);
	void write(const std::string& value);
	void writeLine(const char* line);
	void writeLine(const std::string& line);
	void writeLines(std::vector<std::string>& list);

	template <typename... Args>
	void writeFormat(const char* text, const Args&... args)
	{
		std::string message = tfm::format(text,args...);
		write(message);
	}

	bool hasError() { return errorText.size() != 0 && !errorRetrieved; };
	const std::string& getErrorText() { errorRetrieved = true; return errorText; };
private:
	char32_t readCharacter();

	std::string readLineUtf8();
	std::string readLineSJIS();
	long tell();
	void seek(long pos);

	fs::fstream stream;
	fs::path fileName;
	Encoding encoding;
	Mode mode;
	bool recursion;
	bool guessedEncoding;
	long size_;
	std::string errorText;
	bool errorRetrieved;
	bool fromMemory;
	std::string content;
	size_t contentPos;
	int lineCount;

	std::string buf;
	size_t bufPos;

	inline unsigned char bufGetChar()
	{
		if (fromMemory)
			return content[contentPos++];

		if (buf.size() <= bufPos)
		{
			bufFillRead();
			if (buf.size() == 0)
				return 0;
		}
		++contentPos;
		return buf[bufPos++];
	}
	inline unsigned short bufGet16LE()
	{
		unsigned char c1 = bufGetChar();
		unsigned char c2 = bufGetChar();
		return c1 | (c2 << 8);
	}
	inline unsigned short bufGet16BE()
	{
		unsigned char c1 = bufGetChar();
		unsigned char c2 = bufGetChar();
		return c2 | (c1 << 8);
	}

	void bufPut(const void *p, const size_t len);
	void bufPut(const char c);

	void bufFillRead();
	void bufDrainWrite();
};

std::optional<char16_t> sjisToUnicode(unsigned short);
TextFile::Encoding getEncodingFromString(const std::string& str);

#pragma region "File: Util/ByteArray.h"


#include <string>

#include <sys/types.h>

#if defined(_MSC_VER) && !defined(ssize_t)
typedef intptr_t ssize_t;
#endif

typedef unsigned char byte;

enum class Endianness { Big, Little };

class ByteArray
{
public:
	ByteArray();
	ByteArray(const ByteArray& other);
	ByteArray(byte* data, size_t size);
	ByteArray(ByteArray&& other);
	~ByteArray();
	ByteArray& operator=(ByteArray& other);
	ByteArray& operator=(ByteArray&& other);

	size_t append(const ByteArray& other);
	size_t append(void* data, size_t size);
	size_t appendByte(byte b) { return append(&b,1); };
	void replaceByte(size_t pos, byte b) { data_[pos] = b; };
	void replaceBytes(size_t pos, byte* data, size_t size);
	void reserveBytes(size_t count, byte value = 0);
	void alignSize(size_t alignment);

	int getWord(size_t pos, Endianness endianness = Endianness::Little) const
	{
		if (pos+1 >= this->size()) return -1;
		unsigned char* d = (unsigned char*) this->data();

		if (endianness == Endianness::Little)
		{
			return d[pos+0] | (d[pos+1] << 8);
		} else {
			return d[pos+1] | (d[pos+0] << 8);
		}
	}

	int getDoubleWord(size_t pos, Endianness endianness = Endianness::Little) const
	{
		if (pos+3 >= this->size()) return -1;
		unsigned char* d = (unsigned char*) this->data();

		if (endianness == Endianness::Little)
		{
			return d[pos+0] | (d[pos+1] << 8) | (d[pos+2] << 16) | (d[pos+3] << 24);
		} else {
			return d[pos+3] | (d[pos+2] << 8) | (d[pos+1] << 16) | (d[pos+0] << 24);
		}
	}

	void replaceWord(size_t pos, unsigned int w, Endianness endianness = Endianness::Little)
	{
		if (pos+1 >= this->size()) return;
		unsigned char* d = (unsigned char*) this->data();

		if (endianness == Endianness::Little)
		{
			d[pos+0] = w & 0xFF;
			d[pos+1] = (w >> 8) & 0xFF;
		} else {
			d[pos+0] = (w >> 8) & 0xFF;
			d[pos+1] = w & 0xFF;
		}
	}

	void replaceDoubleWord(size_t pos, unsigned int w, Endianness endianness = Endianness::Little)
	{
		if (pos+3 >= this->size()) return;
		unsigned char* d = (unsigned char*) this->data();

		if (endianness == Endianness::Little)
		{
			d[pos+0] = w & 0xFF;
			d[pos+1] = (w >> 8) & 0xFF;
			d[pos+2] = (w >> 16) & 0xFF;
			d[pos+3] = (w >> 24) & 0xFF;
		} else {
			d[pos+0] = (w >> 24) & 0xFF;
			d[pos+1] = (w >> 16) & 0xFF;
			d[pos+2] = (w >> 8) & 0xFF;
			d[pos+3] = w & 0xFF;
		}
	}

	byte& operator [](size_t index)
	{
		return data_[index];
	};

	const byte& operator [](size_t index) const
	{
		return data_[index];
	};

	size_t size() const { return size_; };
	byte* data(size_t pos = 0) const { return &data_[pos]; };
	void clear() { size_ = 0; };
	void resize(size_t newSize);
	ByteArray mid(size_t start, ssize_t length = 0);
	ByteArray left(size_t length) { return mid(0,length); };
	ByteArray right(size_t length) { return mid(size_-length,length); };

	static ByteArray fromFile(const fs::path& fileName, long start = 0, size_t size = 0);
	bool toFile(const fs::path& fileName);
private:
	void grow(size_t neededSize);
	byte* data_;
	size_t size_;
	size_t allocatedSize_;
};

#pragma region "File: Core/FileManager.h"


#include <memory>
#include <vector>

class SymbolData;

struct SymDataModuleInfo;

class AssemblerFile
{
public:
	virtual ~AssemblerFile() { };

	virtual bool open(bool onlyCheck) = 0;
	virtual void close() = 0;
	virtual bool isOpen() = 0;
	virtual bool write(void* data, size_t length) = 0;
	virtual int64_t getVirtualAddress() = 0;
	virtual int64_t getPhysicalAddress() = 0;
	virtual int64_t getHeaderSize() = 0;
	virtual bool seekVirtual(int64_t virtualAddress) = 0;
	virtual bool seekPhysical(int64_t physicalAddress) = 0;
	virtual bool getModuleInfo(SymDataModuleInfo& info) { return false; };
	virtual bool hasFixedVirtualAddress() { return false; };
	virtual void beginSymData(SymbolData& symData) { };
	virtual void endSymData(SymbolData& symData) { };
	virtual const fs::path& getFileName() = 0;
};

class GenericAssemblerFile: public AssemblerFile
{
public:
	GenericAssemblerFile(const fs::path& fileName, int64_t headerSize, bool overwrite);
	GenericAssemblerFile(const fs::path& fileName, const fs::path& originalFileName, int64_t headerSize);

	virtual bool open(bool onlyCheck);
	virtual void close() { if (stream.is_open()) stream.close(); };
	virtual bool isOpen() { return stream.is_open(); };
	virtual bool write(void* data, size_t length);
	virtual int64_t getVirtualAddress() { return virtualAddress; };
	virtual int64_t getPhysicalAddress() { return virtualAddress-headerSize; };
	virtual int64_t getHeaderSize() { return headerSize; };
	virtual bool seekVirtual(int64_t virtualAddress);
	virtual bool seekPhysical(int64_t physicalAddress);
	virtual bool hasFixedVirtualAddress() { return true; };

	virtual const fs::path& getFileName() { return fileName; };
	const fs::path& getOriginalFileName() { return originalName; };
	int64_t getOriginalHeaderSize() { return originalHeaderSize; };
	void setHeaderSize(int64_t size) { headerSize = size; };

private:
	enum Mode { Open, Create, Copy };

	Mode mode;
	int64_t originalHeaderSize;
	int64_t headerSize;
	int64_t virtualAddress;
	fs::ofstream stream;
	fs::path fileName;
	fs::path originalName;
};


class FileManager
{
public:
	FileManager();
	~FileManager();
	void reset();
	bool openFile(std::shared_ptr<AssemblerFile> file, bool onlyCheck);
	void addFile(std::shared_ptr<AssemblerFile> file);
	bool hasOpenFile() { return activeFile != nullptr; };
	void closeFile();
	bool write(void* data, size_t length);
	bool writeU8(uint8_t data);
	bool writeU16(uint16_t data);
	bool writeU32(uint32_t data);
	bool writeU64(uint64_t data);
	int64_t getVirtualAddress();
	int64_t getPhysicalAddress();
	int64_t getHeaderSize();
	bool seekVirtual(int64_t virtualAddress);
	bool seekPhysical(int64_t physicalAddress);
	bool advanceMemory(size_t bytes);
	std::shared_ptr<AssemblerFile> getOpenFile() { return activeFile; };
	int64_t getOpenFileID();
	void setEndianness(Endianness endianness) { this->endianness = endianness; };
	Endianness getEndianness() { return endianness; }
private:
	bool checkActiveFile();
	std::vector<std::shared_ptr<AssemblerFile>> files;
	std::shared_ptr<AssemblerFile> activeFile;
	Endianness endianness;
	Endianness ownEndianness;
};

#pragma region "File: Core/ELF/ElfTypes.h"

///////////////////////
// ELF Header Constants

// File type
enum ElfType
{
	ET_NONE        =0,
	ET_REL         =1,
	ET_EXEC        =2,
	ET_DYN         =3,
	ET_CORE        =4,
	ET_LOPROC =0xFF00,
	ET_HIPROC =0xFFFF,
};

// Machine/Architecture
enum ElfMachine
{
	EM_NONE  =0,
	EM_MIPS  =8,
	EM_ARM   =40,
	EM_SH2	 =42
};

// File version
#define EV_NONE    0
#define EV_CURRENT 1

// Identification index
#define EI_MAG0    0
#define EI_MAG1    1
#define EI_MAG2    2
#define EI_MAG3    3
#define EI_CLASS   4
#define EI_DATA    5
#define EI_VERSION 6
#define EI_PAD     7
#define EI_NIDENT 16

// Magic number
#define ELFMAG0 0x7F
#define ELFMAG1  'E'
#define ELFMAG2  'L'
#define ELFMAG3  'F'

// File class
#define ELFCLASSNONE 0
#define ELFCLASS32   1
#define ELFCLASS64   2

// Encoding
#define ELFDATANONE 0
#define ELFDATA2LSB 1
#define ELFDATA2MSB 2


/////////////////////
// Sections constants

// Section indexes
#define SHN_UNDEF          0
#define SHN_LORESERVE 0xFF00
#define SHN_LOPROC    0xFF00
#define SHN_HIPROC    0xFF1F
#define SHN_ABS       0xFFF1
#define SHN_COMMON    0xFFF2
#define SHN_HIRESERVE 0xFFFF

// Section types
#define SHT_NULL            0
#define SHT_PROGBITS        1
#define SHT_SYMTAB          2
#define SHT_STRTAB          3
#define SHT_RELA            4
#define SHT_HASH            5
#define SHT_DYNAMIC         6
#define SHT_NOTE            7
#define SHT_NOBITS          8
#define SHT_REL             9
#define SHT_SHLIB          10
#define SHT_DYNSYM         11
#define SHT_INIT_ARRAY     14
#define SHT_LOPROC 0x70000000
#define SHT_HIPROC 0x7FFFFFFF
#define SHT_LOUSER 0x80000000
#define SHT_HIUSER 0xFFFFFFFF

// Custom section types
#define SHT_PSPREL 0x700000a0


// Section flags
enum ElfSectionFlags
{
	SHF_WRITE     =0x1,
	SHF_ALLOC     =0x2,
	SHF_EXECINSTR =0x4,
	SHF_MASKPROC  =0xF0000000,
};

// Symbol binding
#define STB_LOCAL   0
#define STB_GLOBAL  1
#define STB_WEAK    2
#define STB_LOPROC 13
#define STB_HIPROC 15

// Symbol types
#define STT_NOTYPE   0
#define STT_OBJECT   1
#define STT_FUNC     2
#define STT_SECTION  3
#define STT_FILE     4
#define STT_LOPROC  13
#define STT_HIPROC  15

// Undefined name
#define STN_UNDEF 0

// Relocation types
#define R_386_NONE      0
#define R_386_32        1
#define R_386_PC32      2
#define R_386_GOT32     3
#define R_386_PLT32     4
#define R_386_COPY      5
#define R_386_GLOB_DAT  6
#define R_386_JMP_SLOT  7
#define R_386_RELATIVE  8
#define R_386_GOTOFF    9
#define R_386_GOTPC    10

// Segment types
#define PT_NULL             0
#define PT_LOAD             1
#define PT_DYNAMIC          2
#define PT_INTERP           3
#define PT_NOTE             4
#define PT_SHLIB            5
#define PT_PHDR             6
#define PT_LOPROC  0x70000000
#define PT_HIPROC  0x7FFFFFFF

// Segment flags
#define PF_X 1
#define PF_W 2
#define PF_R 4

// Dynamic Array Tags
#define DT_NULL              0
#define DT_NEEDED            1
#define DT_PLTRELSZ          2
#define DT_PLTGOT            3
#define DT_HASH              4
#define DT_STRTAB            5
#define DT_SYMTAB            6
#define DT_RELA              7
#define DT_RELASZ            8
#define DT_RELAENT           9
#define DT_STRSZ            10
#define DT_SYMENT           11
#define DT_INIT             12
#define DT_FINI             13
#define DT_SONAME           14
#define DT_RPATH            15
#define DT_SYMBOLIC         16
#define DT_REL              17
#define DT_RELSZ            18
#define DT_RELENT           19
#define DT_PLTREL           20
#define DT_DEBUG            21
#define DT_TEXTREL          22
#define DT_JMPREL           23
#define DT_LOPROC   0x70000000
#define DT_HIPROC   0x7FFFFFFF

typedef unsigned int Elf32_Addr;
typedef unsigned short Elf32_Half;
typedef unsigned int Elf32_Off;
typedef signed int Elf32_Sword;
typedef unsigned int Elf32_Word;


// ELF file header
struct Elf32_Ehdr
{
	unsigned char e_ident[EI_NIDENT];
	Elf32_Half    e_type;
	Elf32_Half    e_machine;
	Elf32_Word    e_version;
	Elf32_Addr    e_entry;
	Elf32_Off     e_phoff;
	Elf32_Off     e_shoff;
	Elf32_Word    e_flags;
	Elf32_Half    e_ehsize;
	Elf32_Half    e_phentsize;
	Elf32_Half    e_phnum;
	Elf32_Half    e_shentsize;
	Elf32_Half    e_shnum;
	Elf32_Half    e_shstrndx;
};

// Section header
struct Elf32_Shdr
{
	Elf32_Word sh_name;
	Elf32_Word sh_type;
	Elf32_Word sh_flags;
	Elf32_Addr sh_addr;
	Elf32_Off  sh_offset;
	Elf32_Word sh_size;
	Elf32_Word sh_link;
	Elf32_Word sh_info;
	Elf32_Word sh_addralign;
	Elf32_Word sh_entsize;
};

// Segment header
struct Elf32_Phdr
{
	Elf32_Word p_type;
	Elf32_Off  p_offset;
	Elf32_Addr p_vaddr;
	Elf32_Addr p_paddr;
	Elf32_Word p_filesz;
	Elf32_Word p_memsz;
	Elf32_Word p_flags;
	Elf32_Word p_align;
};

// Symbol table entry
struct Elf32_Sym
{
	Elf32_Word    st_name;
	Elf32_Addr    st_value;
	Elf32_Word    st_size;
	unsigned char st_info;
	unsigned char st_other;
	Elf32_Half    st_shndx;
};

#define ELF32_ST_BIND(i)   ((i)>>4)
#define ELF32_ST_TYPE(i)   ((i)&0xf)
#define ELF32_ST_INFO(b,t) (((b)<<4)+((t)&0xf))

// Relocation entries
struct Elf32_Rel
{
	Elf32_Addr r_offset;
	Elf32_Word r_info;

	unsigned char getType()
	{
		return r_info & 0xFF;
	}

	Elf32_Word getSymbolNum()
	{
		return r_info >> 8;
	}
};

struct Elf32_Rela
{
	Elf32_Addr  r_offset;
	Elf32_Word  r_info;
	Elf32_Sword r_addend;

	unsigned char getType()
	{
		return r_info & 0xFF;
	}

	Elf32_Word getSymbolNum()
	{
		return r_info >> 8;
	}
};

#define ELF32_R_SYM(i) ((i)>>8)
#define ELF32_R_TYPE(i) ((unsigned char)(i))
#define ELF32_R_INFO(s,t) (((s)<<8 )+(unsigned char)(t))

#pragma region "File: Core/ELF/ElfFile.h"


#include <vector>

enum ElfPart { ELFPART_SEGMENTTABLE, ELFPART_SECTIONTABLE, ELFPART_SEGMENTS, ELFPART_SEGMENTLESSSECTIONS };

class ElfSegment;
class ElfSection;

class ElfFile
{
public:

	bool load(const fs::path&fileName, bool sort);
	bool load(ByteArray& data, bool sort);
	void save(const fs::path& fileName);

	Elf32_Half getType() { return fileHeader.e_type; };
	Elf32_Half getMachine() { return fileHeader.e_machine; };
	Endianness getEndianness()
	{
		return fileHeader.e_ident[EI_DATA] == ELFDATA2MSB ? Endianness::Big : Endianness::Little;
	}
	size_t getSegmentCount() { return segments.size(); };
	ElfSegment* getSegment(size_t index) { return segments[index]; };

	int findSegmentlessSection(const std::string& name);
	ElfSection* getSegmentlessSection(size_t index) { return segmentlessSections[index]; };
	size_t getSegmentlessSectionCount() { return segmentlessSections.size(); };
	ByteArray& getFileData() { return fileData; }

	int getSymbolCount();
	bool getSymbol(Elf32_Sym& symbol, size_t index);
	const char* getStrTableString(size_t pos);
private:
	void loadElfHeader();
	void writeHeader(ByteArray& data, size_t pos, Endianness endianness);
	void loadProgramHeader(Elf32_Phdr& header, ByteArray& data, size_t pos);
	void loadSectionHeader(Elf32_Shdr& header, ByteArray& data, size_t pos);
	void loadSectionNames();
	void determinePartOrder();

	Elf32_Ehdr fileHeader;
	std::vector<ElfSegment*> segments;
	std::vector<ElfSection*> sections;
	std::vector<ElfSection*> segmentlessSections;
	ByteArray fileData;
	ElfPart partsOrder[4];

	ElfSection* symTab;
	ElfSection* strTab;
};


class ElfSection
{
public:
	ElfSection(Elf32_Shdr header);
	void setName(std::string& name) { this->name = name; };
	const std::string& getName() { return name; };
	void setData(ByteArray& data) { this->data = data; };
	void setOwner(ElfSegment* segment);
	bool hasOwner() { return owner != nullptr; };
	void writeHeader(ByteArray& data, size_t pos, Endianness endianness);
	void writeData(ByteArray& output);
	void setOffsetBase(int base);
	ByteArray& getData() { return data; };

	Elf32_Word getType() { return header.sh_type; };
	Elf32_Off getOffset() { return header.sh_offset; };
	Elf32_Word getSize() { return header.sh_size; };
	Elf32_Word getNameOffset() { return header.sh_name; };
	Elf32_Word getAlignment() { return header.sh_addralign; };
	Elf32_Addr getAddress() { return header.sh_addr; };
	Elf32_Half getInfo() { return header.sh_info; };
	Elf32_Word getFlags() { return header.sh_flags; };
private:
	Elf32_Shdr header;
	std::string name;
	ByteArray data;
	ElfSegment* owner;
};

class ElfSegment
{
public:
	ElfSegment(Elf32_Phdr header, ByteArray& segmentData);
	bool isSectionPartOf(ElfSection* section);
	void addSection(ElfSection* section);
	Elf32_Off getOffset() { return header.p_offset; };
	Elf32_Word getPhysSize() { return header.p_filesz; };
	Elf32_Word getType() { return header.p_type; };
	Elf32_Addr getVirtualAddress() { return header.p_vaddr; };
	size_t getSectionCount() { return sections.size(); };
	void writeHeader(ByteArray& data, size_t pos, Endianness endianness);
	void writeData(ByteArray& output);
	void splitSections();

	int findSection(const std::string& name);
	ElfSection* getSection(size_t index) { return sections[index]; };
	void writeToData(size_t offset, void* data, size_t size);
	void sortSections();
private:
	Elf32_Phdr header;
	ByteArray data;
	std::vector<ElfSection*> sections;
	ElfSection* paddrSection;
};

struct RelocationData
{
	int64_t opcodeOffset;
	int64_t relocationBase;
	uint32_t opcode;
	int32_t addend;

	int64_t symbolAddress;
	int targetSymbolType;
	int targetSymbolInfo;
};

#pragma region "File: Core/ELF/ElfRelocator.h"


#include <memory>

struct ElfRelocatorCtor
{
	Identifier symbolName;
	size_t size;
};

struct RelocationAction
{
	RelocationAction(int64_t offset, uint32_t newValue) : offset(offset), newValue(newValue) {}
	int64_t offset;
	uint32_t newValue;
};

class CAssemblerCommand;
class Identifier;
class Parser;

class IElfRelocator
{
public:
	virtual ~IElfRelocator() {};
	virtual int expectedMachine() const = 0;
	virtual bool isDummyRelocationType(int type) const { return false; }
	virtual bool relocateOpcode(int type, const RelocationData& data, std::vector<RelocationAction>& actions, std::vector<std::string>& errors) = 0;
	virtual bool finish(std::vector<RelocationAction>& actions, std::vector<std::string>& errors) { return true; }
	virtual void setSymbolAddress(RelocationData& data, int64_t symbolAddress, int symbolType) = 0;

	virtual std::unique_ptr<CAssemblerCommand> generateCtorStub(std::vector<ElfRelocatorCtor>& ctors);
};


class Label;
class SymbolData;

struct ElfRelocatorSection
{
	ElfSection* section;
	size_t index;
	ElfSection* relSection;
	std::shared_ptr<Label> label;
};

struct ElfRelocatorSymbol
{
	std::shared_ptr<Label> label;
	std::string name;
	int64_t relativeAddress;
	int64_t relocatedAddress;
	size_t section;
	size_t size;
	int type;
};

struct ElfRelocatorFile
{
	ElfFile* elf;
	std::vector<ElfRelocatorSection> sections;
	std::vector<ElfRelocatorSymbol> symbols;
	std::string name;
};

class ElfRelocator
{
public:
	bool init(const fs::path& inputName);
	bool exportSymbols();
	void writeSymbols(SymbolData& symData) const;
	std::unique_ptr<CAssemblerCommand> generateCtor(const Identifier& ctorName);
	bool relocate(int64_t& memoryAddress);
	bool hasDataChanged() { return dataChanged; };
	const ByteArray& getData() const { return outputData; };
private:
	bool relocateFile(ElfRelocatorFile& file, int64_t& relocationAddress);
	void loadRelocation(Elf32_Rela& rela, bool addend, ByteArray& data, int offset, Endianness endianness);

	ByteArray outputData;
	std::unique_ptr<IElfRelocator> relocator;
	std::vector<ElfRelocatorFile> files;
	std::vector<ElfRelocatorCtor> ctors;
	bool dataChanged;
};

#pragma region "File: Archs/Architecture.h"


#include <map>
#include <memory>

class ExpressionFunctionHandler;
class IElfRelocator;
class Tokenizer;
class Parser;

class Architecture
{
public:
	static Architecture &current();
	static void setCurrent(Architecture &arch);

	virtual std::unique_ptr<CAssemblerCommand> parseDirective(Parser& parser) { return nullptr; }
	virtual std::unique_ptr<CAssemblerCommand> parseOpcode(Parser& parser) { return nullptr; }
	virtual void registerExpressionFunctions(ExpressionFunctionHandler &handler);
	virtual void NextSection() = 0;
	virtual void Pass2() = 0;
	virtual void Revalidate() = 0;
	virtual std::unique_ptr<IElfRelocator> getElfRelocator() = 0;
	virtual Endianness getEndianness() = 0;
	virtual int getWordSize() = 0;
private:
	static Architecture *currentArchitecture;
};

class ArchitectureCommand: public CAssemblerCommand
{
public:
	ArchitectureCommand(const std::string& tempText, const std::string& symText);
	bool Validate(const ValidateState &state) override;
	void Encode() const override;
	void writeTempData(TempData& tempData) const override;
	void writeSymData(SymbolData& symData) const override;
private:
	Architecture *architecture = nullptr;
	int64_t position;
	Endianness endianness;
	std::string tempText;
	std::string symText;
};

class CInvalidArchitecture: public Architecture
{
public:
	void NextSection() override;
	void Pass2() override;
	void Revalidate() override;
	std::unique_ptr<IElfRelocator> getElfRelocator() override;
	Endianness getEndianness() override { return Endianness::Little; }
	int getWordSize() override { return 4; };
};

extern CInvalidArchitecture InvalidArchitecture;

#pragma region "File: Archs/MIPS/Mips.h"


class Expression;

enum MipsArchType { MARCH_PSX = 0, MARCH_N64, MARCH_PS2, MARCH_PSP, MARCH_RSP, MARCH_INVALID };

class CMipsArchitecture: public Architecture
{
public:
	CMipsArchitecture();
	virtual std::unique_ptr<CAssemblerCommand> parseDirective(Parser& parser);
	virtual std::unique_ptr<CAssemblerCommand> parseOpcode(Parser& parser);
	virtual void registerExpressionFunctions(ExpressionFunctionHandler &handler);
	virtual void NextSection();
	virtual void Pass2() { return; };
	virtual void Revalidate();
	virtual std::unique_ptr<IElfRelocator> getElfRelocator();
	virtual Endianness getEndianness()
	{
		return Version == MARCH_N64 || Version == MARCH_RSP ? Endianness::Big : Endianness::Little;
	};
	virtual int getWordSize() { return 4; };
	void SetLoadDelay(bool Delay, int Register);
	bool GetLoadDelay() { return LoadDelay; };
	int GetLoadDelayRegister() { return LoadDelayRegister; };
	bool GetIgnoreDelay() { return IgnoreLoadDelay; };
	void SetIgnoreDelay(bool b) { IgnoreLoadDelay = b; };
	void SetFixLoadDelay(bool b) { FixLoadDelay = b; };
	bool GetFixLoadDelay() { return FixLoadDelay; };
	void SetVersion(MipsArchType v) { Version = v; };
	MipsArchType GetVersion() { return Version; };
	bool GetDelaySlot() { return DelaySlot; };
	void SetDelaySlot(bool b) {DelaySlot = b; };
	bool hasLoadDelay() { return Version == MARCH_PSX; };
private:
	bool FixLoadDelay;
	bool IgnoreLoadDelay;
	bool LoadDelay;
	int LoadDelayRegister;
	bool DelaySlot;
	MipsArchType Version;
};

typedef struct {
	const char* name;
	short num;
	short len;
} tMipsRegister;

typedef struct {
	char name[5];
	short num;
} MipsRegisterInfo;

enum MipsVfpuType { MIPSVFPU_VECTOR, MIPSVFPU_MATRIX };

struct MipsVFPURegister
{
	MipsVfpuType type;
	int num;
	char name[8];
};

extern const tMipsRegister MipsRegister[];
extern CMipsArchitecture Mips;

bool MipsGetRegister(const char* source, int& RetLen, MipsRegisterInfo& Result);
int MipsGetRegister(const char* source, int& RetLen);
bool MipsGetFloatRegister(const char* source, int& RetLen, MipsRegisterInfo& Result);
bool MipsGetPs2VectorRegister(const char* source, int& RetLen, MipsRegisterInfo& Result);
int MipsGetFloatRegister(const char* source, int& RetLen);
bool MipsCheckImmediate(const char* Source, Expression& Dest, int& RetLen);


#pragma region "File: Archs/MIPS/MipsOpcodes.h"

#include <cstdint>

#define MA_MIPS1		(1 << 0)
#define MA_MIPS2		(1 << 1)
#define MA_MIPS3		(1 << 2)
#define MA_MIPS4		(1 << 3)
#define MA_PSX			(1 << 4)
#define MA_PS2			(1 << 6)
#define MA_PSP			(1 << 7)
#define MA_RSP			(1 << 8)

#define MA_EXPSX		(1 << 12)
#define MA_EXN64		(1 << 13)
#define MA_EXPS2		(1 << 14)
#define MA_EXPSP		(1 << 15)
#define MA_EXRSP		(1 << 16)

#define MO_IPCA							(1 << 0)	// pc >> 2
#define MO_IPCR							(1 << 1)	// PC, -> difference >> 2
#define MO_RSD							(1 << 2)	// rs = rd
#define MO_RST							(1 << 3)	// rs = rt
#define MO_RDT							(1 << 4)	// rd = rt
#define MO_DELAY						(1 << 5)	// delay slot follows
#define MO_NODELAYSLOT					(1 << 6)	// can't be in a delay slot
#define MO_DELAYRT						(1 << 7)	// rt won't be available for one instruction
#define MO_IGNORERTD					(1 << 8)	// don't care for rt delay
#define MO_FRSD							(1 << 9)	// float rs + rd
#define MO_IMMALIGNED					(1 << 10)	// immediate 4 byte aligned
#define MO_NEGIMM						(1 << 11)	// negated immediate (for subi)
#define MO_64BIT						(1 << 12)	// only available on 64 bit cpus
#define MO_FPU							(1 << 13)	// only available with an fpu
#define MO_DFPU							(1 << 14)	// double-precision fpu opcodes

#define MO_VFPU							(1 << 16)	// vfpu type opcode
#define MO_VFPU_MIXED					(1 << 17)	// mixed mode vfpu register
#define MO_VFPU_6BIT					(1 << 18)	// vfpu register can have 6 bits max
#define MO_VFPU_SINGLE					(1 << 19)	// single vfpu reg
#define MO_VFPU_QUAD					(1 << 20)	// quad vfpu reg
#define MO_VFPU_TRANSPOSE_VS			(1 << 21)	// matrix vs has to be transposed
#define MO_VFPU_PAIR					(1 << 22)	// pair vfpu reg
#define MO_VFPU_TRIPLE					(1 << 23)	// triple vfpu reg

#define MO_RSP_VRSD						(1 << 24)	// rsp vector rs + rd
#define MO_RSP_HWOFFSET					(1 << 25) // RSP halfword load/store offset
#define MO_RSP_WOFFSET					(1 << 26) // RSP word load/store offset
#define MO_RSP_DWOFFSET					(1 << 27)	// RSP doubleword load/store offset
#define MO_RSP_QWOFFSET					(1 << 28)	// RSP quadword load/store offset


#define BITFIELD(START,LENGTH,VALUE)	(((VALUE) & ((1 << (LENGTH)) - 1)) << (START))
#define MIPS_FUNC(VALUE)				BITFIELD(0,6,(VALUE))
#define MIPS_SA(VALUE)					BITFIELD(6,5,(VALUE))
#define MIPS_SECFUNC(VALUE)				MIPS_SA((VALUE))
#define MIPS_OP(VALUE)					BITFIELD(26,6,(VALUE))

#define MIPS_RS(VALUE)					BITFIELD(21,5,(VALUE))
#define MIPS_RT(VALUE)					BITFIELD(16,5,(VALUE))
#define MIPS_RD(VALUE)					BITFIELD(11,5,(VALUE))
#define MIPS_FS(VALUE)					MIPS_RD((VALUE))
#define MIPS_FT(VALUE)					MIPS_RT((VALUE))
#define MIPS_FD(VALUE)					MIPS_SA((VALUE))

#define MIPS_SPECIAL(VALUE)				(MIPS_OP(0) | MIPS_FUNC(VALUE))
#define MIPS_REGIMM(VALUE)				(MIPS_OP(1) | MIPS_RT(VALUE))
#define MIPS_COP0(VALUE)				(MIPS_OP(16) | MIPS_RS(VALUE))
#define MIPS_COP0FUNCT(VALUE)			(MIPS_COP0(16) | MIPS_FUNC(VALUE))
#define MIPS_COP1(VALUE)				(MIPS_OP(17) | MIPS_RS(VALUE))
#define MIPS_COP1BC(VALUE)				(MIPS_COP1(8) | MIPS_RT(VALUE))
#define MIPS_COP1S(VALUE)				(MIPS_COP1(16) | MIPS_FUNC(VALUE))
#define MIPS_COP1D(VALUE)				(MIPS_COP1(17) | MIPS_FUNC(VALUE))
#define MIPS_COP1W(VALUE)				(MIPS_COP1(20) | MIPS_FUNC(VALUE))
#define MIPS_COP1L(VALUE)				(MIPS_COP1(21) | MIPS_FUNC(VALUE))

#define MIPS_VFPUSIZE(VALUE)			( (((VALUE) & 1) << 7) | (((VALUE) & 2) << 14) )
#define MIPS_VFPUFUNC(VALUE)			BITFIELD(23, 3, (VALUE))
#define MIPS_COP2(VALUE)				(MIPS_OP(18) | MIPS_RS(VALUE))
#define MIPS_COP2BC(VALUE)				(MIPS_COP2(8) | MIPS_RT(VALUE))
#define MIPS_RSP_COP2(VALUE)			(MIPS_OP(18) | (1 << 25) | MIPS_FUNC(VALUE))
#define MIPS_RSP_LWC2(VALUE)			(MIPS_OP(50) | MIPS_RD(VALUE))
#define MIPS_RSP_SWC2(VALUE)			(MIPS_OP(58) | MIPS_RD(VALUE))
#define MIPS_RSP_VE(VALUE)				BITFIELD(21, 4, (VALUE))
#define MIPS_RSP_VDE(VALUE)				BITFIELD(11, 4, (VALUE))
#define MIPS_RSP_VEALT(VALUE)			BITFIELD(7, 4, (VALUE))
#define MIPS_VFPU0(VALUE)				(MIPS_OP(24) | MIPS_VFPUFUNC(VALUE))
#define MIPS_VFPU1(VALUE)				(MIPS_OP(25) | MIPS_VFPUFUNC(VALUE))
#define MIPS_VFPU3(VALUE)				(MIPS_OP(27) | MIPS_VFPUFUNC(VALUE))
#define MIPS_SPECIAL3(VALUE)			(MIPS_OP(31) | MIPS_FUNC(VALUE))
#define MIPS_ALLEGREX0(VALUE)			(MIPS_SPECIAL3(32) | MIPS_SECFUNC(VALUE))
#define MIPS_VFPU4(VALUE)				(MIPS_OP(52) | MIPS_RS(VALUE))
#define MIPS_VFPU4_11(VALUE)			(MIPS_VFPU4(0) | MIPS_RT(VALUE))
#define MIPS_VFPU4_12(VALUE)			(MIPS_VFPU4(1) | MIPS_RT(VALUE))
#define MIPS_VFPU4_13(VALUE)			(MIPS_VFPU4(2) | MIPS_RT(VALUE))
#define MIPS_VFPU5(VALUE)				(MIPS_OP(55) | MIPS_VFPUFUNC(VALUE))
#define MIPS_VFPU6(VALUE)				(MIPS_OP(60) | MIPS_VFPUFUNC(VALUE))
#define MIPS_VFPU6_1(VALUE)				(MIPS_VFPU6(7) | BITFIELD(20, 3, VALUE))
// This is a bit ugly, VFPU opcodes are encoded strangely.
#define MIPS_VFPU6_1VROT()				(MIPS_VFPU6(7) | BITFIELD(21, 2, 1))
#define MIPS_VFPU6_2(VALUE)				(MIPS_VFPU6_1(0) | MIPS_RT(VALUE))


struct MipsArchDefinition
{
	const char* name;
	int supportSets;
	int excludeMask;
	int flags;
};

extern const MipsArchDefinition mipsArchs[];

struct tMipsOpcode
{
	const char* name;
	const char* encoding;
	int32_t destencoding;
	int archs;
	int flags;
};

extern const tMipsOpcode MipsOpcodes[];

#pragma region "File: Archs/MIPS/CMipsInstruction.h"


enum class MipsRegisterType
{
	Normal,
	Float,
	FpuControl,
	Cop0,
	Ps2Cop2,
	PsxCop2Data,
	PsxCop2Control,
	VfpuVector,
	VfpuMatrix,
	RspCop0,
	RspVector,
	RspVectorControl,
	RspVectorElement,
	RspScalarElement,
	RspOffsetElement
};

enum class MipsImmediateType
{
	None,
	Immediate5,
	Immediate10,
	Immediate16,
	Immediate20,
	Immediate25,
	Immediate26,
	Immediate20_0,
	ImmediateHalfFloat,
	Immediate7,
	CacheOp,
	Ext,
	Ins,
	Cop2BranchType
};

struct MipsRegisterValue
{
	MipsRegisterType type;
	Identifier name{};
	int num;
};

struct MipsRegisterData {
	MipsRegisterValue grs;			// general source reg
	MipsRegisterValue grt;			// general target reg
	MipsRegisterValue grd;			// general dest reg

	MipsRegisterValue frs;			// float source reg
	MipsRegisterValue frt;			// float target reg
	MipsRegisterValue frd;			// float dest reg

	MipsRegisterValue ps2vrs;		// ps2 vector source reg
	MipsRegisterValue ps2vrt;		// ps2 vector target reg
	MipsRegisterValue ps2vrd;		// ps2 vector dest reg

	MipsRegisterValue rspvrs;		// rsp vector source reg
	MipsRegisterValue rspvrt;		// rsp vector target reg
	MipsRegisterValue rspvrd;		// rsp vector dest reg
	MipsRegisterValue rspve;		// rsp vector element reg
	MipsRegisterValue rspvde;		// rsp vector dest element reg
	MipsRegisterValue rspvealt;		// rsp vector element reg (alt. placement)

	MipsRegisterValue vrs;			// vfpu source reg
	MipsRegisterValue vrt;			// vfpu target reg
	MipsRegisterValue vrd;			// vfpu dest reg

	void reset()
	{
		grs.num = grt.num = grd.num = -1;
		frs.num = frt.num = frd.num = -1;
		vrs.num = vrt.num = vrd.num = -1;
		ps2vrs.num = ps2vrt.num = ps2vrd.num = -1;
		rspvrs.num = rspvrt.num = rspvrd.num = -1;
		rspve.num = rspvde.num = rspvealt.num = -1;
	}
};

struct MipsImmediateData
{
	struct
	{
		MipsImmediateType type;
		Expression expression;
		int value;
		int originalValue;
	} primary;

	struct
	{
		MipsImmediateType type;
		Expression expression;
		int value;
		int originalValue;
	} secondary;

	void reset()
	{
		primary.type = MipsImmediateType::None;
		if (primary.expression.isLoaded())
			primary.expression = Expression();

		secondary.type = MipsImmediateType::None;
		if (secondary.expression.isLoaded())
			secondary.expression = Expression();
	}
};

struct MipsOpcodeData
{
	tMipsOpcode opcode;
	int vfpuSize;
	int vectorCondition;

	void reset()
	{
		vfpuSize = vectorCondition = -1;
	}
};

class CMipsInstruction: public CAssemblerCommand
{
public:
	CMipsInstruction(MipsOpcodeData& opcode, MipsImmediateData& immediate, MipsRegisterData& registers);
	~CMipsInstruction();
	bool Validate(const ValidateState &state) override;
	void Encode() const override;
	void writeTempData(TempData& tempData) const override;
private:
	void encodeNormal() const;
	void encodeVfpu() const;
	int floatToHalfFloat(int i);

	bool IgnoreLoadDelay;
	int64_t RamPos;
	bool addNop;

	// opcode variables
	MipsOpcodeData opcodeData;
	MipsImmediateData immediateData;
	MipsRegisterData registerData;
};

#pragma region "File: Util/EncodingTable.h"


#include <map>
#include <vector>

class Trie
{
public:
	Trie();
	void insert(const char* text, size_t value);
	bool findLongestPrefix(const char* text, size_t& result);
private:
	struct LookupEntry
	{
		size_t node;
		char input;

		bool operator<(const LookupEntry& other) const
		{
			if (node != other.node)
				return node < other.node;
			return input < other.input;
		}
	};

	struct Node
	{
		size_t index;
		bool hasValue;
		size_t value;
	};

	std::vector<Node> nodes;
	std::map<LookupEntry,size_t> lookup;
};

class EncodingTable
{
public:
	EncodingTable();
	~EncodingTable();
	void clear();
	bool load(const fs::path& fileName, TextFile::Encoding encoding = TextFile::GUESS);
	bool isLoaded() { return entries.size() != 0; };
	void addEntry(unsigned char* hex, size_t hexLength, const std::string& value);
	void setTerminationEntry(unsigned char* hex, size_t hexLength);
	ByteArray encodeString(const std::string& str, bool writeTermination = true);
	ByteArray encodeTermination();
private:
	struct TableEntry
	{
		size_t hexPos;
		size_t hexLen;
		size_t valueLen;
	};

	ByteArray hexData;
	std::vector<TableEntry> entries;
	Trie lookup;
	TableEntry terminationEntry;
};

#pragma region "File: Core/Misc.h"


#include <string_view>
#include <vector>


class Logger
{
public:
	enum ErrorType { Warning, Error, FatalError, Notice };

	static void clear();
	static void printLine(std::string_view text);

	template <typename... Args>
	static void printLine(const char* text, const Args&... args)
	{
		std::string message = tfm::format(text,args...);
		printLine(message);
	}

	static void print(std::string_view text);

	template <typename... Args>
	static void print(const char* text, const Args&... args)
	{
		std::string message = tfm::format(text,args...);
		print(message);
	}

	static void printError(ErrorType type, std::string_view text);
	static void queueError(ErrorType type, std::string_view text);

	template <typename... Args>
	static void printError(ErrorType type, const char* text, const Args&... args)
	{
		std::string message = tfm::format(text,args...);
		printError(type,message);
	}

	template <typename... Args>
	static void queueError(ErrorType type, const char* text, const Args&... args)
	{
		std::string message = tfm::format(text,args...);
		queueError(type,message);
	}

	static void printQueue();
	static void clearQueue() { queue.clear(); };
	static std::vector<std::string> getErrors() { return errors; };
	static bool hasError() { return error; };
	static bool hasFatalError() { return fatalError; };
	static void setErrorOnWarning(bool b) { errorOnWarning = b; };
	static void setSilent(bool b) { silent = b; };
	static bool isSilent() { return silent; }
	static void suppressErrors() { ++suppressLevel; }
	static void unsuppressErrors() { if (suppressLevel) --suppressLevel; }
private:
	static std::string formatError(ErrorType type, const char* text);
	static void setFlags(ErrorType type);

	struct QueueEntry
	{
		ErrorType type;
		std::string text;
	};

	static std::vector<QueueEntry> queue;
	static std::vector<std::string> errors;
	static bool error;
	static bool fatalError;
	static bool errorOnWarning;
	static bool silent;
	static int suppressLevel;
};

class TempData
{
public:
	void setFileName(const fs::path& name) { file.setFileName(name); };
	void clear() { file.setFileName({}); }
	void start();
	void end();
	void writeLine(int64_t memoryAddress, const std::string& text);
	bool isOpen() { return file.isOpen(); }
private:
	TextFile file;
};

#pragma region "File: Core/Assembler.h"


#include <memory>
#include <string>
#include <vector>

class AssemblerFile;

#define ARMIPS_VERSION_MAJOR    0
#define ARMIPS_VERSION_MINOR    11
#define ARMIPS_VERSION_REVISION 0

enum class ArmipsMode { FILE, MEMORY };

struct LabelDefinition
{
	Identifier name;
	int64_t value;
};

struct EquationDefinition
{
	Identifier name;
	std::string value;
};

struct ArmipsArguments
{
	// common
	ArmipsMode mode;
	int symFileVersion;
	bool errorOnWarning;
	bool silent;
	bool showStats;
	std::vector<std::string>* errorsResult;
	std::vector<EquationDefinition> equList;
	std::vector<LabelDefinition> labels;

	// file mode
	fs::path inputFileName;
	fs::path tempFileName;
	fs::path symFileName;
	bool useAbsoluteFileNames;

	// memory mode
	std::shared_ptr<AssemblerFile> memoryFile;
	std::string content;

	ArmipsArguments()
	{
		mode = ArmipsMode::FILE;
		symFileVersion = 0;
		errorOnWarning = false;
		silent = false;
		showStats = false;
		errorsResult = nullptr;
		useAbsoluteFileNames = true;
	}
};

bool runArmips(ArmipsArguments& settings);

#pragma region "File: Core/SymbolTable.h"


#include <algorithm>
#include <map>
#include <memory>
#include <set>
#include <string>
#include <vector>

struct LabelDefinition;

struct SymbolKey
{
	SymbolKey(const Identifier& name, int file, int section) :
		name(name.string()),
		file(file),
		section(section)
	{
		std::transform(this->name.begin(), this->name.end(), this->name.begin(), ::tolower);
	}

	std::string name;
	int file;
	int section;
};

bool operator<(SymbolKey const& lhs, SymbolKey const& rhs);

class Label
{
public:
	Label(const Identifier&	name);
	const Identifier &getName() { return name; };
	void setOriginalName(const Identifier& name) { originalName = name; }
	const Identifier &getOriginalName() { return originalName; }
	int64_t getValue() { return value; };
	void setValue(int64_t val) { value = val; };
	bool hasPhysicalValue() { return physicalValueSet; }
	int64_t getPhysicalValue() { return physicalValue; }
	void setPhysicalValue(int64_t val) { physicalValue = val; physicalValueSet = true; }
	bool isDefined() { return defined; };
	void setDefined(bool b) { defined = b; };
	bool isData() { return data; };
	void setIsData(bool b) { data = b; };
	void setInfo(int inf) { info = inf; };
	int getInfo() { return info; };
	void setUpdateInfo(bool b) { updateInfo = b; };
	bool getUpdateInfo() { return updateInfo; };
	void setSection(int num) { section = num; }
	int getSection() { return section; }
private:
	Identifier name;
	Identifier originalName;
	int64_t value = 0;
	int64_t physicalValue = 0;
	bool physicalValueSet = false;
	bool defined = false;
	bool data = false;
	bool updateInfo = true;
	int info = 0;
	int section = 0;
};

class SymbolTable
{
public:
	SymbolTable();
	~SymbolTable();
	void clear();
	bool symbolExists(const Identifier& symbol, int file, int section);
	static bool isValidSymbolName(const Identifier& symbol);
	static bool isValidSymbolCharacter(char character, bool first = false);
	static bool isLocalSymbol(const Identifier& symbol) { return symbol.size() >= 2 && symbol.string()[0] == '@' && symbol.string()[1] == '@'; }
	static bool isStaticSymbol(const Identifier& symbol) { return symbol.size() >= 1 && symbol.string()[0] == '@'; }
	static bool isGlobalSymbol(const Identifier& symbol) { return !isLocalSymbol(symbol) && !isStaticSymbol(symbol); }

	std::shared_ptr<Label> getLabel(const Identifier& symbol, int file, int section);
	bool addEquation(const Identifier& name, int file, int section, size_t referenceIndex);
	bool findEquation(const Identifier& name, int file, int section, size_t& dest);
	void addLabels(const std::vector<LabelDefinition>& labels);
	int findSection(int64_t address);

	Identifier getUniqueLabelName(bool local = false);
	size_t getLabelCount() { return labels.size(); };
	size_t getEquationCount() { return equationsCount; };
	bool isGeneratedLabel(const Identifier& name) { return generatedLabels.find(name) != generatedLabels.end(); }
private:
	void setFileSectionValues(const Identifier& symbol, int& file, int& section);

	enum SymbolType { LabelSymbol, EquationSymbol };
	struct SymbolInfo
	{
		SymbolType type;
		size_t index;
	};

	std::map<SymbolKey,SymbolInfo> symbols;
	std::vector<std::shared_ptr<Label>> labels;
	size_t equationsCount;
	size_t uniqueCount;
	std::set<Identifier> generatedLabels;
};

#pragma region "File: Core/Common.h"


#include <string>
#include <vector>

class AssemblerFile;
class CArchitecture;

class FileList
{
public:
	void add(const fs::path& path);

	const fs::path& path(int fileIndex) const;
	const fs::path& relative_path(int fileIndex) const;
	const std::string& string(int fileIndex) const;
	const std::string& relativeString(int fileIndex) const;

	size_t size() const;
	void clear();

private:
	class Entry
	{
	public:
		Entry(const fs::path& path);

		const fs::path& path() const;
		const fs::path& relativePath() const;
		const std::string& string() const;
		const std::string& relativeString() const;

	private:
		fs::path _path;
		fs::path _relativePath;
		std::string _string; // preconverted for performance
		std::string _relativeString; // preconverted for performance
	};

	std::vector<Entry> _entries;
};

typedef struct {
	int FileNum;
	int LineNumber;
	int TotalLineCount;
} tFileInfo;

typedef struct {
	FileList fileList;
	tFileInfo FileInfo;
	SymbolTable symbolTable;
	EncodingTable Table;
	int Section;
	bool nocash;
	bool relativeInclude;
	bool memoryMode;
	std::shared_ptr<AssemblerFile> memoryFile;
	bool multiThreading;
} tGlobal;

extern tGlobal Global;

class FileManager;
extern FileManager* g_fileManager;

fs::path getFullPathName(const fs::path& path);

bool checkLabelDefined(const Identifier& labelName, int section);
bool checkValidLabelName(const Identifier& labelName);

bool isPowerOfTwo(int64_t n);

#pragma region "File: Core/Allocations.h"

#include <cstdint>
#include <map>

struct AllocationStats
{
	int64_t largestPosition;
	int64_t largestSize;
	int64_t largestUsage;

	int64_t largestFreePosition;
	int64_t largestFreeSize;
	int64_t largestFreeUsage;

	int64_t sharedFreePosition;
	int64_t sharedFreeSize;
	int64_t sharedFreeUsage;

	int64_t totalSize;
	int64_t totalUsage;
	int64_t sharedSize;
	int64_t sharedUsage;

	int64_t largestPoolPosition;
	int64_t largestPoolSize;
	int64_t totalPoolSize;
};

class Allocations
{
public:
	static void clear();
	static void setArea(int64_t fileID, int64_t position, int64_t space, int64_t usage, bool usesFill, bool shared);
	static void forgetArea(int64_t fileID, int64_t position, int64_t space);

	static void setPool(int64_t fileID, int64_t position, int64_t size);
	static void forgetPool(int64_t fileID, int64_t position, int64_t size);

	static void clearSubAreas();
	static bool allocateSubArea(int64_t fileID, int64_t& position, int64_t minRange, int64_t maxRange, int64_t size);
	static int64_t getSubAreaUsage(int64_t fileID, int64_t position);

	static bool canTrimSpace();

	static void validateOverlap();
	static AllocationStats collectStats();

private:
	struct Key
	{
		int64_t fileID;
		int64_t position;

		inline bool operator <(const Allocations::Key &other) const
		{
			return std::tie(fileID, position) < std::tie(other.fileID, other.position);
		}
	};
	struct Usage
	{
		int64_t space;
		int64_t usage;
		bool usesFill;
		bool shared;
	};

	struct SubArea
	{
		int64_t offset;
		int64_t size;
	};

	static void collectAreaStats(AllocationStats &stats);
	static void collectPoolStats(AllocationStats &stats);

	static int64_t getSubAreaUsage(Key key)
	{
		return getSubAreaUsage(key.fileID, key.position);
	}

	static std::map<Key, Usage> allocations;
	static std::map<Key, int64_t> pools;
	static std::multimap<Key, SubArea> subAreas;
	static bool keepPositions;
	static bool nextKeepPositions;
	static bool keptPositions;
};

#pragma region "File: Core/Allocations.cpp"


std::map<Allocations::Key, Allocations::Usage> Allocations::allocations;
std::map<Allocations::Key, int64_t> Allocations::pools;
std::multimap<Allocations::Key, Allocations::SubArea> Allocations::subAreas;

// Are we keeping existing positions this round?  Start with no, since we have none.
bool Allocations::keepPositions = false;
// Should we keep positions next time?  This is set to false on allocation failure.
bool Allocations::nextKeepPositions = true;
// Did we adjust any positions to keep the old?
bool Allocations::keptPositions = false;

void Allocations::clear()
{
	allocations.clear();
	keepPositions = false;
	nextKeepPositions = true;
	keptPositions = false;
}

void Allocations::setArea(int64_t fileID, int64_t position, int64_t space, int64_t usage, bool usesFill, bool shared)
{
	Key key{ fileID, position };
	allocations[key] = Usage{ space, usage, usesFill, shared };
}

void Allocations::forgetArea(int64_t fileID, int64_t position, int64_t space)
{
	Key key{ fileID, position };
	auto it = allocations.find(key);
	if (it != allocations.end() && it->second.space == space)
		allocations.erase(it);

	subAreas.erase(key);
}

void Allocations::setPool(int64_t fileID, int64_t position, int64_t size)
{
	Key key{ fileID, position };
	pools[key] = size;
}

void Allocations::forgetPool(int64_t fileID, int64_t position, int64_t size)
{
	Key key{ fileID, position };
	auto it = pools.find(key);
	if (it != pools.end() && it->second == size)
		pools.erase(it);
}

bool Allocations::allocateSubArea(int64_t fileID, int64_t& position, int64_t minRange, int64_t maxRange, int64_t size)
{
	for (auto it : allocations)
	{
		if (it.first.fileID != fileID || !it.second.shared)
			continue;
		if (minRange != -1 && it.first.position + it.second.space < minRange)
			continue;
		if (maxRange != -1 && it.first.position > maxRange)
			continue;

		int64_t actualUsage = it.second.usage + getSubAreaUsage(it.first);
		int64_t possiblePosition = it.first.position + actualUsage;
		if (minRange != -1 && possiblePosition < minRange)
			continue;
		if (maxRange != -1 && possiblePosition > maxRange)
			continue;

		// Can we use the position it had before?  Nudge up size if so.
		if (keepPositions && position != -1 && position > possiblePosition)
		{
			int64_t offset = position - it.first.position;
			if (it.second.space >= offset + size && offset != actualUsage)
			{
				size += offset - actualUsage;
				keptPositions = true;
				// Fall through to reuse the emplace.
				possiblePosition = position;
			}
		}

		if (it.second.space >= actualUsage + size)
		{
			position = possiblePosition;
			subAreas.emplace(it.first, SubArea{ actualUsage, size });
			return true;
		}
	}

	nextKeepPositions = false;
	return false;
}

void Allocations::clearSubAreas()
{
	subAreas.clear();
	keepPositions = nextKeepPositions;
	nextKeepPositions = true;
	keptPositions = false;
}

bool Allocations::canTrimSpace()
{
	return keptPositions;
}

int64_t Allocations::getSubAreaUsage(int64_t fileID, int64_t position)
{
	Key key{ fileID, position };
	// For safety, just find the end of the last sub area.
	int64_t maxExtent = 0;
	auto range = subAreas.equal_range(key);
	for (auto it = range.first; it != range.second; ++it)
	{
		int64_t extent = it->second.offset + it->second.size;
		maxExtent = std::max(extent, maxExtent);
	}

	// Now subtract out the original usage.
	if (maxExtent <= allocations[key].usage)
		return 0;
	return maxExtent - allocations[key].usage;
}

void Allocations::validateOverlap()
{
	// An easy mistake to make is a "subarea" where the parent area fills, and erases the subarea.
	// Let's detect any sort of area overlap and report a warning.
	Key lastKey{ -1, -1 };
	int64_t lastEndPosition = -1;
	Usage lastUsage{};

	for (auto it : allocations) {
		if (it.first.fileID == lastKey.fileID && it.first.position > lastKey.position && it.first.position < lastEndPosition) {
			// First, the obvious: does the content overlap?
			if (it.first.position < lastKey.position + lastUsage.usage)
				Logger::queueError(Logger::Warning, "Content of areas %08llX and %08llx overlap", lastKey.position, it.first.position);
			// Next question, does the earlier one fill?
			else if (it.second.usesFill && lastUsage.usesFill)
				Logger::queueError(Logger::Warning, "Areas %08llX and %08llx overlap and both fill", lastKey.position, it.first.position);

			// If the new area ends before the last, keep it as the last.
			if (lastEndPosition > it.first.position + it.second.space) {
				// But update the usage to the max position.
				int64_t newUsageEnd = it.first.position + it.second.usage + getSubAreaUsage(it.first);
				lastUsage.usage = newUsageEnd - lastKey.position;
				continue;
			}
		}

		lastKey = it.first;
		lastUsage = it.second;
		lastUsage.usage += getSubAreaUsage(lastKey);
		lastEndPosition = it.first.position + it.second.space;
	}
}

AllocationStats Allocations::collectStats()
{
	AllocationStats stats{};
	collectAreaStats(stats);
	collectPoolStats(stats);
	return stats;
}

void Allocations::collectAreaStats(AllocationStats &stats)
{
	// Need to work out overlaps.
	Key lastKey{ -1, -1 };
	int64_t lastEndPosition = -1;
	Usage lastUsage{};

	auto applyUsage = [&stats](int64_t position, const Usage &usage)
	{
		if (usage.space > stats.largestSize)
		{
			stats.largestPosition = position;
			stats.largestSize = usage.space;
			stats.largestUsage = usage.usage;
		}

		if (usage.space - usage.usage > stats.largestFreeSize - stats.largestFreeUsage)
		{
			stats.largestFreePosition = position;
			stats.largestFreeSize = usage.space;
			stats.largestFreeUsage = usage.usage;
		}

		// We assume overlaps agree on sharing, hopefully...
		if (usage.shared && usage.space - usage.usage > stats.sharedFreeSize - stats.sharedFreeUsage)
		{
			stats.sharedFreePosition = position;
			stats.sharedFreeSize = usage.space;
			stats.sharedFreeUsage = usage.usage;
		}

		stats.totalSize += usage.space;
		stats.totalUsage += usage.usage;
		if (usage.shared)
		{
			stats.sharedSize += usage.space;
			stats.sharedUsage += usage.usage;
		}
	};

	for (auto it : allocations)
	{
		if (it.first.fileID == lastKey.fileID && it.first.position > lastKey.position && it.first.position < lastEndPosition)
		{
			// Overlap, merge.
			int64_t lastUsageEnd = lastKey.position + lastUsage.usage;
			int64_t newUsageEnd = it.first.position + it.second.usage + getSubAreaUsage(it.first);

			if (lastUsageEnd >= it.first.position)
				lastUsage.usage += newUsageEnd - lastUsageEnd;
			else
				lastUsage.usage += it.second.usage + getSubAreaUsage(it.first);

			lastEndPosition = it.first.position + it.second.space;
			lastUsage.space = lastEndPosition - lastKey.position;

			continue;
		}

		if (lastKey.position != -1)
			applyUsage(lastKey.position, lastUsage);

		lastKey = it.first;
		lastUsage = it.second;
		lastUsage.usage += getSubAreaUsage(lastKey);
		lastEndPosition = it.first.position + it.second.space;
	}

	if (lastKey.position != -1)
		applyUsage(lastKey.position, lastUsage);
}

void Allocations::collectPoolStats(AllocationStats &stats)
{
	for (auto it : pools)
	{
		if (it.second > stats.largestPoolSize)
		{
			stats.largestPoolPosition = it.first.position;
			stats.largestPoolSize = it.second;
		}

		stats.totalPoolSize += it.second;
	}
}

#pragma region "File: Parser/DirectivesParser.h"

#include <memory>
#include <string>
#include <unordered_map>
#include <optional>

class CAssemblerCommand;
class Parser;

using DirectiveFunc = std::unique_ptr<CAssemblerCommand> (*)(Parser&,int);

struct DirectiveEntry {
	DirectiveFunc function;
	int flags;
};

using DirectiveMap = std::unordered_multimap<std::string, const DirectiveEntry>;

#define DIRECTIVE_USERMASK			0x0000FFFF

// Global flags
#define DIRECTIVE_NOCASHON			0x00010000
#define DIRECTIVE_NOCASHOFF			0x00020000
#define DIRECTIVE_MIPSRESETDELAY	0x00040000
#define DIRECTIVE_DISABLED			0x00080000
#define DIRECTIVE_NOTINMEMORY		0x00100000
#define DIRECTIVE_MANUALSEPARATOR	0x00200000

// file directive flags
#define DIRECTIVE_POS_PHYSICAL		0x00000001
#define DIRECTIVE_POS_VIRTUAL		0x00000002
#define DIRECTIVE_ALIGN_PHYSICAL	0x00000001
#define DIRECTIVE_ALIGN_VIRTUAL		0x00000002
#define DIRECTIVE_ALIGN_FILL		0x00000004

// conditional directive flags
#define DIRECTIVE_COND_IF			0x00000001
#define DIRECTIVE_COND_IFDEF		0x00000002
#define DIRECTIVE_COND_IFNDEF		0x00000003

// data directive flags
#define DIRECTIVE_DATA_8			0x00000001
#define DIRECTIVE_DATA_16			0x00000002
#define DIRECTIVE_DATA_32			0x00000003
#define DIRECTIVE_DATA_64			0x00000004
#define DIRECTIVE_DATA_ASCII		0x00000005
#define DIRECTIVE_DATA_SJIS			0x00000006
#define DIRECTIVE_DATA_CUSTOM		0x00000007
#define DIRECTIVE_DATA_FLOAT		0x00000008
#define DIRECTIVE_DATA_DOUBLE		0x00000009
#define DIRECTIVE_DATA_HWORD        0x0000000A
#define DIRECTIVE_DATA_WORD         0x0000000B
#define DIRECTIVE_DATA_DWORD        0x0000000C
#define DIRECTIVE_DATA_TERMINATION	0x00000100

// message directive flags
#define DIRECTIVE_MSG_WARNING		0x00000001
#define DIRECTIVE_MSG_ERROR			0x00000002
#define DIRECTIVE_MSG_NOTICE		0x00000003

// MIPS directive flags
#define DIRECTIVE_MIPS_PSX			0x00000001
#define DIRECTIVE_MIPS_PS2			0x00000002
#define DIRECTIVE_MIPS_PSP			0x00000003
#define DIRECTIVE_MIPS_N64			0x00000004
#define DIRECTIVE_MIPS_RSP			0x00000005

// ARM directive flags
#define DIRECTIVE_ARM_GBA			0x00000001
#define DIRECTIVE_ARM_NDS			0x00000002
#define DIRECTIVE_ARM_3DS			0x00000003
#define DIRECTIVE_ARM_BIG			0x00000004
#define DIRECTIVE_ARM_LITTLE		0x00000005

#define DIRECTIVE_SH_SATURN			0x00000001

// Area directive flags
#define DIRECTIVE_AREA_SHARED		0x00000001

extern const DirectiveMap directives;

std::unique_ptr<CAssemblerCommand> parseDirectiveDefineLabel(Parser& parser, int flags, std::optional<bool> thumbMode);

#pragma region "File: Archs/MIPS/MipsMacros.h"


#include <memory>

struct MipsImmediateData;
struct MipsRegisterData;

#define MIPSM_B						0x00000001
#define MIPSM_BU					0x00000002
#define MIPSM_HW					0x00000003
#define MIPSM_HWU					0x00000004
#define MIPSM_W						0x00000005
#define MIPSM_WU					0x00000006
#define MIPSM_DW					0x00000007
#define MIPSM_LLSCW					0x00000008
#define MIPSM_LLSCDW				0x00000009
#define MIPSM_COP1					0x0000000a
#define MIPSM_COP2					0x0000000b
#define MIPSM_DCOP1					0x0000000c
#define MIPSM_DCOP2					0x0000000d
#define MIPSM_ACCESSMASK			0x0000000f

#define MIPSM_NE					0x00000001
#define MIPSM_LT					0x00000002
#define MIPSM_LTU					0x00000003
#define MIPSM_GE					0x00000004
#define MIPSM_GEU					0x00000005
#define MIPSM_EQ					0x00000006
#define MIPSM_CONDITIONMASK			0x00000007

#define MIPSM_IMM					0x00000200
#define MIPSM_LEFT					0x00000400
#define MIPSM_RIGHT					0x00000800
#define MIPSM_UNALIGNED				0x00001000
#define MIPSM_DONTWARNDELAYSLOT		0x00002000
#define MIPSM_UPPER					0x00004000
#define MIPSM_LOWER					0x00008000
#define MIPSM_LOAD					0x00010000
#define MIPSM_STORE					0x00020000
#define MIPSM_LIKELY				0x00040000
#define MIPSM_REVCMP				0x00080000

class Parser;

using MipsMacroFunc = std::unique_ptr<CAssemblerCommand> (*)(Parser&,MipsRegisterData&,MipsImmediateData&,int);

struct MipsMacroDefinition {
	const char* name;
	const char* args;
	MipsMacroFunc function;
	int flags;
};

extern const MipsMacroDefinition mipsMacros[];

class MipsMacroCommand: public CAssemblerCommand
{
public:
	MipsMacroCommand(std::unique_ptr<CAssemblerCommand> content, int macroFlags);
	bool Validate(const ValidateState &state) override;
	void Encode() const override;
	void writeTempData(TempData& tempData) const override;
private:
	std::unique_ptr<CAssemblerCommand> content;
	int macroFlags;
	bool IgnoreLoadDelay;
};

#pragma region "File: Archs/MIPS/MipsParser.h"


#include <memory>
#include <string>

class CAssemblerCommand;
class Expression;
class Parser;

struct MipsMacroDefinition;
struct tMipsOpcode;

struct MipsRegisterDescriptor {
	const char* name;
	int num;
};

class MipsParser
{
public:
	std::unique_ptr<CAssemblerCommand> parseDirective(Parser& parser);
	std::unique_ptr<CMipsInstruction> parseOpcode(Parser& parser);
	std::unique_ptr<CAssemblerCommand> parseMacro(Parser& parser);
private:
	bool parseRegisterNumber(Parser& parser, MipsRegisterValue& dest, int numValues);
	bool parseRegisterTable(Parser& parser, MipsRegisterValue& dest, const MipsRegisterDescriptor* table, size_t count);
	bool parseRegister(Parser& parser, MipsRegisterValue& dest);
	bool parseFpuRegister(Parser& parser, MipsRegisterValue& dest);
	bool parseFpuControlRegister(Parser& parser, MipsRegisterValue& dest);
	bool parseCop0Register(Parser& parser, MipsRegisterValue& dest);
	bool parsePs2Cop2Register(Parser& parser, MipsRegisterValue& dest);
	bool parsePsxCop2DataRegister(Parser& parser, MipsRegisterValue& dest);
	bool parsePsxCop2ControlRegister(Parser& parser, MipsRegisterValue& dest);
	bool parseRspCop0Register(Parser& parser, MipsRegisterValue& dest);
	bool parseRspVectorControlRegister(Parser& parser, MipsRegisterValue& dest);
	bool parseRspVectorRegister(Parser& parser, MipsRegisterValue& dest);
	bool parseRspVectorElement(Parser& parser, MipsRegisterValue& dest);
	bool parseRspScalarElement(Parser& parser, MipsRegisterValue& dest);
	bool parseRspOffsetElement(Parser& parser, MipsRegisterValue& dest);
	bool parseVfpuRegister(Parser& parser, MipsRegisterValue& reg, int size);
	bool parseVfpuControlRegister(Parser& parser, MipsRegisterValue& reg);
	bool parseImmediate(Parser& parser, Expression& dest);
	bool parseVcstParameter(Parser& parser, int& result);
	bool parseVfpuVrot(Parser& parser, int& result, int size);
	bool parseVfpuCondition(Parser& parser, int& result);
	bool parseVpfxsParameter(Parser& parser, int& result);
	bool parseVpfxdParameter(Parser& parser, int& result);
	bool parseCop2BranchCondition(Parser& parser, int& result);
	bool parseWb(Parser& parser);

	bool decodeCop2BranchCondition(const std::string& text, size_t& pos, int& result);
	bool decodeVfpuType(const std::string& name, size_t& pos, int& dest);
	bool decodeOpcode(const std::string& name, const tMipsOpcode& opcode);

	void setOmittedRegisters(const tMipsOpcode& opcode);
	bool matchSymbol(Parser& parser, char symbol);
	bool parseParameters(Parser& parser, const tMipsOpcode& opcode);
	bool parseMacroParameters(Parser& parser, const MipsMacroDefinition& macro);

	MipsRegisterData registers;
	MipsImmediateData immediate;
	MipsOpcodeData opcodeData;
	bool hasFixedSecondaryImmediate;
};

class MipsOpcodeFormatter
{
public:
	const std::string& formatOpcode(const MipsOpcodeData& opData, const MipsRegisterData& regData,
		const MipsImmediateData& immData);
private:
	void handleOpcodeName(const MipsOpcodeData& opData);
	void handleOpcodeParameters(const MipsOpcodeData& opData, const MipsRegisterData& regData,
		const MipsImmediateData& immData);
	void handleImmediate(MipsImmediateType type, unsigned int originalValue, unsigned int opcodeFlags);

	std::string buffer;
};

#pragma region "File: Archs/MIPS/CMipsInstruction.cpp"


CMipsInstruction::CMipsInstruction(MipsOpcodeData& opcode, MipsImmediateData& immediate, MipsRegisterData& registers)
{
	this->opcodeData = opcode;
	this->immediateData = immediate;
	this->registerData = registers;

	addNop = false;
	IgnoreLoadDelay = Mips.GetIgnoreDelay();
}

CMipsInstruction::~CMipsInstruction()
{

}

int getImmediateBits(MipsImmediateType type)
{
	switch (type)
	{
	case MipsImmediateType::Immediate5:
		return 5;
	case MipsImmediateType::Immediate7:
		return 7;
	case MipsImmediateType::Immediate10:
		return 10;
	case MipsImmediateType::Immediate16:
	case MipsImmediateType::ImmediateHalfFloat:
		return 16;
	case MipsImmediateType::Immediate20:
	case MipsImmediateType::Immediate20_0:
		return 20;
	case MipsImmediateType::Immediate25:
		return 25;
	case MipsImmediateType::Immediate26:
		return 26;
	default:
		return 0;
	}
}

// http://code.google.com/p/jpcsp/source/browse/trunk/src/jpcsp/Allegrex/VfpuState.java?spec=svn3676&r=3383#1196
int CMipsInstruction::floatToHalfFloat(int i)
{
	int s = ((i >> 16) & 0x00008000); // sign
	int e = ((i >> 23) & 0x000000ff) - (127 - 15); // exponent
	int f = ((i >> 0) & 0x007fffff); // fraction

	// need to handle NaNs and Inf?
	if (e <= 0) {
		if (e < -10) {
			if (s != 0) {
				// handle -0.0
				return 0x8000;
			}
			return 0;
		}
		f = (f | 0x00800000) >> (1 - e);
		return s | (f >> 13);
	} else if (e == 0xff - (127 - 15)) {
		if (f == 0) {
			// Inf
			return s | 0x7c00;
		}
		// NAN
		return s | 0x7fff;
	}

	if (e > 30) {
		// Overflow
		return s | 0x7c00;
	}

	return s | (e << 10) | (f >> 13);
}

bool CMipsInstruction::Validate(const ValidateState &state)
{
	bool Result = false;

	bool previousNop = addNop;
	addNop = false;

	RamPos = g_fileManager->getVirtualAddress();
	if (RamPos % 4)
	{
		Logger::queueError(Logger::Error, "opcode not aligned to word boundary");
		return false;
	}

	// check immediates
	if (immediateData.primary.type != MipsImmediateType::None)
	{
		if (immediateData.primary.expression.isLoaded())
		{
			if (!immediateData.primary.expression.evaluateInteger(immediateData.primary.value))
			{
				Logger::queueError(Logger::Error, "Invalid immediate expression");
				return false;
			}

			immediateData.primary.originalValue = immediateData.primary.value;
		}

		if (immediateData.primary.type == MipsImmediateType::ImmediateHalfFloat)
			immediateData.primary.value = floatToHalfFloat(immediateData.primary.originalValue);

		if (opcodeData.opcode.flags & MO_IMMALIGNED)	// immediate must be aligned
		{
			if (immediateData.primary.value % 4)
			{
				Logger::queueError(Logger::Error, "Immediate must be word aligned");
				return false;
			}
		}

		if (opcodeData.opcode.flags & MO_NEGIMM) 		// negated immediate
		{
			immediateData.primary.value = -immediateData.primary.value;
		} else if (opcodeData.opcode.flags & MO_IPCA)	// absolute value >> 2
		{
			immediateData.primary.value = (immediateData.primary.value >> 2) & 0x3FFFFFF;
		} else if (opcodeData.opcode.flags & MO_IPCR)	// relative 16 bit value
		{
			int num = (int) (immediateData.primary.value-RamPos-4);

			if (num > 0x20000 || num < (-0x20000))
			{
				Logger::queueError(Logger::Error, "Branch target %08X out of range",immediateData.primary.value);
				return false;
			}
			immediateData.primary.value = num >> 2;
		} else if (opcodeData.opcode.flags & (MO_RSP_HWOFFSET | MO_RSP_WOFFSET | MO_RSP_DWOFFSET | MO_RSP_QWOFFSET))
		{
			int shift = 0;

			if (opcodeData.opcode.flags & MO_RSP_HWOFFSET) shift = 1;
			else if (opcodeData.opcode.flags & MO_RSP_WOFFSET) shift = 2;
			else if (opcodeData.opcode.flags & MO_RSP_DWOFFSET) shift = 3;
			else if (opcodeData.opcode.flags & MO_RSP_QWOFFSET) shift = 4;

			if (immediateData.primary.value & ((1 << shift) - 1))
			{
				Logger::queueError(Logger::Error, "Offset must be %d-byte aligned",1<<shift);
				return false;
			}
			immediateData.primary.value = immediateData.primary.value >> shift;
		}

		int immediateBits = getImmediateBits(immediateData.primary.type);
		unsigned int mask = (0xFFFFFFFF << (32-immediateBits)) >> (32-immediateBits);
		int digits = (immediateBits+3) / 4;

		if ((unsigned int)std::abs(immediateData.primary.value) > mask)
		{
			Logger::queueError(Logger::Error, "Immediate value 0x%0*X out of range",digits,immediateData.primary.value);
			return false;
		}

		immediateData.primary.value &= mask;
	}

	if (immediateData.secondary.type != MipsImmediateType::None)
	{
		if (immediateData.secondary.expression.isLoaded())
		{
			if (!immediateData.secondary.expression.evaluateInteger(immediateData.secondary.value))
			{
				Logger::queueError(Logger::Error, "Invalid immediate expression");
				return false;
			}

			immediateData.secondary.originalValue = immediateData.secondary.value;
		}

		switch (immediateData.secondary.type)
		{
		case MipsImmediateType::CacheOp:
			if ((unsigned int)immediateData.secondary.value > 0x1f)
			{
				Logger::queueError(Logger::Error, "Immediate value %02X out of range",immediateData.secondary.value);
				return false;
			}
			break;
		case MipsImmediateType::Ext:
		case MipsImmediateType::Ins:
			if (immediateData.secondary.value > 32 || immediateData.secondary.value == 0)
			{
				Logger::queueError(Logger::Error, "Immediate value %02X out of range",immediateData.secondary.value);
				return false;
			}

			immediateData.secondary.value--;
			if (immediateData.secondary.type == MipsImmediateType::Ins)
				immediateData.secondary.value += immediateData.primary.value;
			break;
		case MipsImmediateType::Cop2BranchType:
		default:
			break;
		}
	}

	// check load delay
	if (Mips.hasLoadDelay() && Mips.GetLoadDelay() && !IgnoreLoadDelay)
	{
		bool fix = false;

		if (registerData.grd.num != -1 && registerData.grd.num == Mips.GetLoadDelayRegister())
		{
			Logger::queueError(Logger::Warning, "register %S may not be available due to load delay",registerData.grd.name);
			fix = true;
		} else if (registerData.grs.num != -1 && registerData.grs.num == Mips.GetLoadDelayRegister())
		{
			Logger::queueError(Logger::Warning, "register %S may not be available due to load delay",registerData.grs.name);
			fix = true;
		} else if (registerData.grt.num != -1 && registerData.grt.num == Mips.GetLoadDelayRegister()
			&& !(opcodeData.opcode.flags & MO_IGNORERTD))
		{
			Logger::queueError(Logger::Warning, "register %S may not be available due to load delay",registerData.grt.name);
			fix = true;
		}

		if (Mips.GetFixLoadDelay() && fix)
		{
			addNop = true;
			Logger::queueError(Logger::Notice, "added nop to ensure correct behavior");
		}
	}

	if ((opcodeData.opcode.flags & MO_NODELAYSLOT) && Mips.GetDelaySlot() && !IgnoreLoadDelay)
	{
		Logger::queueError(Logger::Error, "This instruction can't be in a delay slot");
	}

	Mips.SetDelaySlot((opcodeData.opcode.flags & MO_DELAY) != 0);

	// now check if this opcode causes a load delay
	if (Mips.hasLoadDelay())
		Mips.SetLoadDelay((opcodeData.opcode.flags & MO_DELAYRT) != 0,registerData.grt.num);

	if (previousNop != addNop)
		Result = true;

	g_fileManager->advanceMemory(addNop ? 8 : 4);
	return Result;
}

void CMipsInstruction::encodeNormal() const
{
	int32_t encoding = opcodeData.opcode.destencoding;

	if (registerData.grs.num != -1) encoding |= MIPS_RS(registerData.grs.num);	// source reg
	if (registerData.grt.num != -1) encoding |= MIPS_RT(registerData.grt.num);	// target reg
	if (registerData.grd.num != -1) encoding |= MIPS_RD(registerData.grd.num);	// dest reg

	if (registerData.frt.num != -1) encoding |= MIPS_FT(registerData.frt.num);	// float target reg
	if (registerData.frs.num != -1) encoding |= MIPS_FS(registerData.frs.num);	// float source reg
	if (registerData.frd.num != -1) encoding |= MIPS_FD(registerData.frd.num);	// float dest reg

	if (registerData.ps2vrt.num != -1) encoding |= (registerData.ps2vrt.num << 16);	// ps2 vector target reg
	if (registerData.ps2vrs.num != -1) encoding |= (registerData.ps2vrs.num << 21);	// ps2 vector source reg
	if (registerData.ps2vrd.num != -1) encoding |= (registerData.ps2vrd.num << 6);	// ps2 vector dest reg

	if (registerData.rspvrt.num != -1) encoding |= MIPS_FT(registerData.rspvrt.num);	// rsp vector target reg
	if (registerData.rspvrs.num != -1) encoding |= MIPS_FS(registerData.rspvrs.num);	// rsp vector source reg
	if (registerData.rspvrd.num != -1) encoding |= MIPS_FD(registerData.rspvrd.num);	// rsp vector dest reg

	if (registerData.rspve.num != -1) encoding |= MIPS_RSP_VE(registerData.rspve.num);			// rsp element
	if (registerData.rspvde.num != -1) encoding |= MIPS_RSP_VDE(registerData.rspvde.num);		// rsp destination element
	if (registerData.rspvealt.num != -1) encoding |= MIPS_RSP_VEALT(registerData.rspvealt.num);	// rsp element (alt. placement)

	if (!(opcodeData.opcode.flags & MO_VFPU_MIXED) && registerData.vrt.num != -1)			// vfpu rt
		encoding |= registerData.vrt.num << 16;

	switch (immediateData.primary.type)
	{
	case MipsImmediateType::Immediate5:
	case MipsImmediateType::Immediate10:
	case MipsImmediateType::Immediate20:
		encoding |= immediateData.primary.value << 6;
		break;
	case MipsImmediateType::Immediate16:
	case MipsImmediateType::Immediate25:
	case MipsImmediateType::Immediate26:
	case MipsImmediateType::Immediate20_0:
	case MipsImmediateType::Immediate7:
	case MipsImmediateType::ImmediateHalfFloat:
		encoding |= immediateData.primary.value;
		break;
	default:
		// TODO: Assert?
		break;
	}

	switch (immediateData.secondary.type)
	{
	case MipsImmediateType::CacheOp:
		encoding |= immediateData.secondary.value << 16;
		break;
	case MipsImmediateType::Ext:
	case MipsImmediateType::Ins:
		encoding |= immediateData.secondary.value << 11;
		break;
	case MipsImmediateType::Cop2BranchType:
		encoding |= immediateData.secondary.value << 18;
		break;
	default:
		// TODO: Assert?
		break;
	}

	if (opcodeData.opcode.flags & MO_VFPU_MIXED)
	{
		// always vrt
		encoding |= registerData.vrt.num >> 5;
		encoding |= (registerData.vrt.num & 0x1F) << 16;
	}

	g_fileManager->writeU32((uint32_t)encoding);
}

void CMipsInstruction::encodeVfpu() const
{
	int encoding = opcodeData.opcode.destencoding;

	if (opcodeData.vectorCondition != -1) encoding |= (opcodeData.vectorCondition << 0);
	if (registerData.vrd.num != -1) encoding |= (registerData.vrd.num << 0);
	if (registerData.vrs.num != -1) encoding |= (registerData.vrs.num << 8);
	if (registerData.vrt.num != -1) encoding |= (registerData.vrt.num << 16);
	if (opcodeData.vfpuSize != -1 && (opcodeData.opcode.flags & (MO_VFPU_PAIR|MO_VFPU_SINGLE|MO_VFPU_TRIPLE|MO_VFPU_QUAD)) == 0)
	{
		if (opcodeData.vfpuSize & 1) encoding |= (1 << 7);
		if (opcodeData.vfpuSize & 2) encoding |= (1 << 15);
	}

	if (registerData.grt.num != -1) encoding |= (registerData.grt.num << 16);

	switch (immediateData.primary.type)
	{
	case MipsImmediateType::Immediate5:
		encoding |= immediateData.primary.value << 16;
		break;
	case MipsImmediateType::Immediate7:
		encoding |= immediateData.primary.value << 0;
		break;
	default:
		// TODO: Assert?
		break;
	}

	g_fileManager->writeU32((uint32_t)encoding);
}

void CMipsInstruction::Encode() const
{
	if (addNop)
		g_fileManager->writeU32(0);

	if (opcodeData.opcode.flags & MO_VFPU)
		encodeVfpu();
	else
		encodeNormal();
}

void CMipsInstruction::writeTempData(TempData& tempData) const
{
	MipsOpcodeFormatter formatter;
	tempData.writeLine(RamPos, formatter.formatOpcode(opcodeData,registerData,immediateData));
}

#pragma region "File: Archs/MIPS/MipsExpressionFunctions.h"

class ExpressionFunctionHandler;

void registerMipsExpressionFunctions(ExpressionFunctionHandler &handler);

#pragma region "File: Archs/MIPS/MipsElfRelocator.h"

enum {
	R_MIPS_NONE,
	R_MIPS_16,
	R_MIPS_32,
	R_MIPS_REL32,
	R_MIPS_26,
	R_MIPS_HI16,
	R_MIPS_LO16,
	R_MIPS_GPREL16,
	R_MIPS_LITERAL,
	R_MIPS_GOT16,
	R_MIPS_PC16,
	R_MIPS_CALL16,
	R_MIPS_GPREL32
};

class MipsElfRelocator: public IElfRelocator
{
public:
	int expectedMachine() const override;
	bool relocateOpcode(int type, const RelocationData& data, std::vector<RelocationAction>& actions, std::vector<std::string>& errors) override;
	bool finish(std::vector<RelocationAction>& actions, std::vector<std::string>& errors) override;
	void setSymbolAddress(RelocationData& data, int64_t symbolAddress, int symbolType) override;
	std::unique_ptr<CAssemblerCommand> generateCtorStub(std::vector<ElfRelocatorCtor>& ctors) override;
private:
	bool processHi16Entries(uint32_t lo16Opcode, int64_t lo16RelocationBase, std::vector<RelocationAction>& actions, std::vector<std::string>& errors);

	struct Hi16Entry
	{
		Hi16Entry(int64_t offset, int64_t relocationBase, uint32_t opcode) : offset(offset), relocationBase(relocationBase), opcode(opcode) {}
		int64_t offset;
		int64_t relocationBase;
		uint32_t opcode;
	};

	std::vector<Hi16Entry> hi16Entries;
};

#pragma region "File: Archs/MIPS/Mips.cpp"


CMipsArchitecture Mips;

CMipsArchitecture::CMipsArchitecture()
{
	FixLoadDelay = false;
	IgnoreLoadDelay = false;
	LoadDelay = false;
	LoadDelayRegister = 0;
	DelaySlot = false;
	Version = MARCH_INVALID;
}

std::unique_ptr<CAssemblerCommand> CMipsArchitecture::parseDirective(Parser& parser)
{
	MipsParser mipsParser;
	return mipsParser.parseDirective(parser);
}

std::unique_ptr<CAssemblerCommand> CMipsArchitecture::parseOpcode(Parser& parser)
{
	MipsParser mipsParser;

	std::unique_ptr<CAssemblerCommand> macro = mipsParser.parseMacro(parser);
	if (macro != nullptr)
		return macro;

	return mipsParser.parseOpcode(parser);
}

void CMipsArchitecture::registerExpressionFunctions(ExpressionFunctionHandler &handler)
{
	registerMipsExpressionFunctions(handler);
}

void CMipsArchitecture::NextSection()
{
	LoadDelay = false;
	LoadDelayRegister = 0;
	DelaySlot = false;
}

void CMipsArchitecture::Revalidate()
{
	LoadDelay = false;
	LoadDelayRegister = 0;
	DelaySlot = false;
}

std::unique_ptr<IElfRelocator> CMipsArchitecture::getElfRelocator()
{
	switch (Version)
	{
	case MARCH_PS2:
	case MARCH_PSP:
	case MARCH_N64:
	case MARCH_PSX:
		return std::make_unique<MipsElfRelocator>();
	case MARCH_RSP:
	default:
		return nullptr;
	}
}

void CMipsArchitecture::SetLoadDelay(bool Delay, int Register)
{
	LoadDelay = Delay;
	LoadDelayRegister = Register;
}

#pragma region "File: Archs/MIPS/MipsElfFile.h"


class MipsElfFile: public AssemblerFile
{
public:
	MipsElfFile();
	virtual bool open(bool onlyCheck);
	virtual void close();
	virtual bool isOpen() { return opened; };
	virtual bool write(void* data, size_t length);
	virtual int64_t getVirtualAddress();
	virtual int64_t getPhysicalAddress();
	virtual int64_t getHeaderSize();
	virtual bool seekVirtual(int64_t virtualAddress);
	virtual bool seekPhysical(int64_t physicalAddress);
	virtual bool getModuleInfo(SymDataModuleInfo& info);
	virtual void beginSymData(SymbolData& symData);
	virtual void endSymData(SymbolData& symData);
	virtual const fs::path& getFileName() { return fileName; };

	bool load(const fs::path& fileName, const fs::path& outputFileName);
	void save();
	bool setSection(const std::string& name);
private:
	ElfFile elf;
	fs::path fileName;
	fs::path outputFileName;
	bool opened;
	int platform;

	int segment;
	int section;
	size_t sectionOffset;
};


class DirectiveLoadMipsElf: public CAssemblerCommand
{
public:
	DirectiveLoadMipsElf(const fs::path& fileName);
	DirectiveLoadMipsElf(const fs::path& inputName, const fs::path& outputName);
	bool Validate(const ValidateState &state) override;
	void Encode() const override;
	void writeTempData(TempData& tempData) const override;
	void writeSymData(SymbolData& symData) const override;
private:
	std::shared_ptr<MipsElfFile> file;
	fs::path inputName;
	fs::path outputName;
};

#pragma region "File: Util/CRC.h"

#include <cstddef>

unsigned short getCrc16(unsigned char* Source, size_t len);
unsigned int getCrc32(unsigned char* Source, size_t len);
unsigned int getChecksum(unsigned char* Source, size_t len);

#pragma region "File: Archs/MIPS/MipsElfFile.cpp"


MipsElfFile::MipsElfFile()
{
	platform = Mips.GetVersion();
	section = segment = -1;
	opened = false;
}

bool MipsElfFile::open(bool onlyCheck)
{
	opened = !onlyCheck;
	return true;
}

void MipsElfFile::close()
{
	if (isOpen())
		save();
}

void MipsElfFile::beginSymData(SymbolData& symData)
{
	symData.startModule(this);
}

void MipsElfFile::endSymData(SymbolData& symData)
{
	symData.endModule(this);
}

int64_t MipsElfFile::getVirtualAddress()
{
	if (segment != -1)
	{
		ElfSegment* seg = elf.getSegment(segment);
		ElfSection* sect = seg->getSection(section);
		int64_t addr = seg->getVirtualAddress() + sect->getOffset();
		return addr+sectionOffset;
	}

	// segmentless sections don't have a virtual address
	Logger::queueError(Logger::Error, "Not inside a mapped section");
	return -1;
}

int64_t MipsElfFile::getPhysicalAddress()
{
	if (segment != -1)
	{
		ElfSegment* seg = elf.getSegment(segment);
		ElfSection* sect = seg->getSection(section);
		int64_t addr = seg->getOffset() + sect->getOffset();
		return addr;
	}

	if (section != -1)
	{
		ElfSection* sect = elf.getSegmentlessSection(section);
		return sect->getOffset();
	}

	Logger::queueError(Logger::Error, "Not inside a section");
	return -1;
}

int64_t MipsElfFile::getHeaderSize()
{
	// this method is not used
	Logger::queueError(Logger::Error, "Unimplemented method");
	return -1;
}

bool MipsElfFile::seekVirtual(int64_t virtualAddress)
{
	// search in segments
	for (size_t i = 0; i < elf.getSegmentCount(); i++)
	{
		ElfSegment* seg = elf.getSegment(i);
		int64_t segStart = seg->getVirtualAddress();
		int64_t segEnd = segStart+seg->getPhysSize();

		if (segStart <= virtualAddress && virtualAddress < segEnd)
		{
			// find section
			for (size_t l = 0; l < seg->getSectionCount(); l++)
			{
				ElfSection* sect = seg->getSection(l);
				int64_t sectStart = segStart+sect->getOffset();
				int64_t sectEnd = sectStart+sect->getSize();

				if (sectStart <= virtualAddress && virtualAddress < sectEnd)
				{
					segment = (int) i;
					section = (int) l;
					sectionOffset = (size_t) (virtualAddress-sectStart);
					return true;
				}
			}

			Logger::queueError(Logger::Error, "Found segment, but no containing section");
			return false;
		}
	}

	// segmentless sections don't have a virtual address
	Logger::printError(Logger::Error, "Couldn't find a mapped section");
	return false;
}

bool MipsElfFile::seekPhysical(int64_t physicalAddress)
{
	// search in segments
	for (size_t i = 0; i < elf.getSegmentCount(); i++)
	{
		ElfSegment* seg = elf.getSegment(i);
		int64_t segStart = seg->getOffset();
		int64_t segEnd = segStart+seg->getPhysSize();

		if (segStart <= physicalAddress && physicalAddress < segEnd)
		{
			// find section
			for (size_t l = 0; l < seg->getSectionCount(); l++)
			{
				ElfSection* sect = seg->getSection(l);
				int64_t sectStart = segStart+sect->getOffset();
				int64_t sectEnd = sectStart+sect->getSize();

				if (sectStart <= physicalAddress && physicalAddress < sectEnd)
				{
					segment = (int) i;
					section = (int) l;
					sectionOffset = physicalAddress-sectStart;
					return true;
				}
			}

			Logger::queueError(Logger::Error, "Found segment, but no containing section");
			return false;
		}
	}

	// search in segmentless sections
	for (size_t i = 0; i < elf.getSegmentlessSectionCount(); i++)
	{
		ElfSection* sect = elf.getSegmentlessSection(i);
		int64_t sectStart = sect->getOffset();
		int64_t sectEnd = sectStart+sect->getSize();

		if (sectStart <= physicalAddress && physicalAddress < sectEnd)
		{
			segment = -1;
			section = (int) i;
			sectionOffset = physicalAddress-sectStart;
			return true;
		}
	}

	segment = -1;
	section = -1;
	Logger::queueError(Logger::Error, "Couldn't find a section");
	return false;
}

bool MipsElfFile::getModuleInfo(SymDataModuleInfo& info)
{
	info.crc32 = getCrc32(elf.getFileData().data(),elf.getFileData().size());
	return true;
}

bool MipsElfFile::write(void* data, size_t length)
{
	if (segment != -1)
	{
		ElfSegment* seg = elf.getSegment(segment);
		ElfSection* sect = seg->getSection(section);

		int64_t pos = sect->getOffset()+sectionOffset;
		seg->writeToData(pos,data,length);
		sectionOffset += length;
		return true;
	}

	if (section != -1)
	{
		// TODO: segmentless sections
		return false;
	}

	Logger::printError(Logger::Error, "Not inside a section");
	return false;
}

bool MipsElfFile::load(const fs::path& fileName, const fs::path& outputFileName)
{
	this->outputFileName = outputFileName;

	if (!elf.load(fileName,true))
	{
		Logger::printError(Logger::FatalError, "Failed to load %s",fileName.u8string());
		return false;
	}

	if (elf.getType() == 0xFFA0)
	{
		Logger::printError(Logger::FatalError, "Relocatable ELF %s not supported yet",fileName.u8string());
		return false;
	}

	if (elf.getType() != 2)
	{
		Logger::printError(Logger::FatalError, "Unknown ELF %s type %d",fileName,elf.getType());
		return false;
	}

	if (elf.getSegmentCount() != 0)
		seekVirtual(elf.getSegment(0)->getVirtualAddress());

	return true;
}

bool MipsElfFile::setSection(const std::string& name)
{
	// look in segments
	for (size_t i = 0; i < elf.getSegmentCount(); i++)
	{
		ElfSegment* seg = elf.getSegment(i);
		int n = seg->findSection(name);
		if (n != -1)
		{
			segment = (int) i;
			section = n;
			return true;
		}
	}

	// look in stray sections
	int n = elf.findSegmentlessSection(name);
	if (n != -1)
	{
		segment = -1;
		section = n;
		return true;
	}

	Logger::queueError(Logger::Warning, "Section %s not found",name);
	return false;
}

void MipsElfFile::save()
{
	elf.save(outputFileName);
}

//
// DirectiveLoadPspElf
//

DirectiveLoadMipsElf::DirectiveLoadMipsElf(const fs::path& fileName)
{
	file = std::make_shared<MipsElfFile>();

	this->inputName = getFullPathName(fileName);
	if (!file->load(this->inputName,this->inputName))
	{
		file = nullptr;
		return;
	}

	g_fileManager->addFile(file);
}

DirectiveLoadMipsElf::DirectiveLoadMipsElf(const fs::path& inputName, const fs::path& outputName)
{
	file = std::make_shared<MipsElfFile>();

	this->inputName = getFullPathName(inputName);
	this->outputName = getFullPathName(outputName);
	if (!file->load(this->inputName,this->outputName))
	{
		file = nullptr;
		return;
	}

	g_fileManager->addFile(file);
}

bool DirectiveLoadMipsElf::Validate(const ValidateState &state)
{
	Architecture::current().NextSection();
	g_fileManager->openFile(file,true);
	return false;
}

void DirectiveLoadMipsElf::Encode() const
{
	g_fileManager->openFile(file,false);
}

void DirectiveLoadMipsElf::writeTempData(TempData& tempData) const
{
	if (outputName.empty())
	{
		tempData.writeLine(g_fileManager->getVirtualAddress(),tfm::format(".loadelf \"%s\"",inputName.u8string()));
	} else {
		tempData.writeLine(g_fileManager->getVirtualAddress(),tfm::format(".loadelf \"%s\",\"%s\"",
			inputName.u8string(),outputName.u8string()));
	}
}

void DirectiveLoadMipsElf::writeSymData(SymbolData& symData) const
{
	file->beginSymData(symData);
}

#pragma region "File: Commands/CommandSequence.h"


#include <memory>
#include <vector>

class Label;

class CommandSequence: public CAssemblerCommand
{
public:
	CommandSequence();
	bool Validate(const ValidateState &state) override;
	void Encode() const override;
	void writeTempData(TempData& tempData) const override;
	void writeSymData(SymbolData& symData) const override;
	void addCommand(std::unique_ptr<CAssemblerCommand> cmd) { commands.push_back(std::move(cmd)); }
private:
	std::vector<std::unique_ptr<CAssemblerCommand>> commands;
};

#pragma region "File: Parser/Parser.h"


#include <map>
#include <memory>
#include <optional>
#include <set>
#include <string>
#include <unordered_map>
#include <vector>


class CAssemblerCommand;
class Expression;
class TextFile;

struct DirectiveEntry;

using DirectiveMap = std::unordered_multimap<std::string, const DirectiveEntry>;

struct AssemblyTemplateArgument
{
	const char* variableName;
	std::string value;
};

struct ParserMacro
{
	Identifier name;
	std::vector<Identifier> parameters;
	std::set<Identifier> labels;
	std::vector<Token> content;
	size_t counter;
};

enum class ConditionalResult { Unknown, True, False };

class Parser
{
public:
	Parser();
	bool atEnd() { return entries.back().tokenizer->atEnd(); }

	void addEquation(const Token& start, const Identifier& name, const std::string& value);

	Expression parseExpression();
	bool parseExpressionList(std::vector<Expression>& list, int min = -1, int max = -1);
	bool parseIdentifier(Identifier& dest);
	std::unique_ptr<CAssemblerCommand> parseCommand();
	std::unique_ptr<CAssemblerCommand> parseCommandSequence(char indicator = 0, const std::initializer_list<const char*> terminators = {});
	std::unique_ptr<CAssemblerCommand> parseFile(TextFile& file, bool virtualFile = false);
	std::unique_ptr<CAssemblerCommand> parseString(const std::string& text);
	std::unique_ptr<CAssemblerCommand> parseTemplate(const std::string& text, const std::initializer_list<AssemblyTemplateArgument> variables = {});
	std::unique_ptr<CAssemblerCommand> parseDirective(const DirectiveMap &directiveSet);
	bool matchToken(TokenType type, bool optional = false);

	Tokenizer* getTokenizer() { return entries.back().tokenizer; };
	const Token& peekToken(int ahead = 0) { return getTokenizer()->peekToken(ahead); };
	const Token& nextToken() { return getTokenizer()->nextToken(); };
	void eatToken() { getTokenizer()->eatToken(); };
	void eatTokens(int num) { getTokenizer()->eatTokens(num); };

	void pushConditionalResult(ConditionalResult cond);
	void popConditionalResult() { conditionStack.pop_back(); };
	bool isInsideTrueBlock() { return conditionStack.back().inTrueBlock; }
	bool isInsideUnknownBlock() { return conditionStack.back().inUnknownBlock; }

	void printError(const Token &token, const std::string &text);

	template <typename... Args>
	void printError(const Token& token, const char* text, const Args&... args)
	{
		printError(token, tfm::format(text,args...));
	}

	bool hasError() { return error; }
	void updateFileInfo();
protected:
	void clearError() { error = false; }
	std::unique_ptr<CAssemblerCommand> handleError();

	std::unique_ptr<CAssemblerCommand> parse(Tokenizer* tokenizer, bool virtualFile, const fs::path& name = {});
	std::unique_ptr<CAssemblerCommand> parseLabel();
	bool checkEquLabel();
	bool parseFunctionDeclaration(Identifier& name, std::vector<Identifier>& parameters);
	bool checkExpFuncDefinition();
	bool checkMacroDefinition();

	std::optional<std::vector<Token>> extractMacroParameter(const Token &macroStart);
	std::unique_ptr<CAssemblerCommand> parseMacroCall();

	struct FileEntry
	{
		Tokenizer* tokenizer;
		bool virtualFile;
		int fileNum;
		int previousCommandLine;
	};

	std::vector<FileEntry> entries;
	std::map<Identifier, ParserMacro> macros;
	std::set<Identifier> macroLabels;
	bool initializingMacro;
	bool error;
	size_t errorLine;

	bool overrideFileInfo;
	int overrideFileNum;
	int overrideLineNum;

	struct ConditionInfo
	{
		bool inTrueBlock;
		bool inUnknownBlock;
	};

	std::vector<ConditionInfo> conditionStack;
};

struct TokenSequenceValue
{
	TokenSequenceValue(const char* text)
	{
		type = TokenType::Identifier;
		textValue = text;
	}

	TokenSequenceValue(int64_t num)
	{
		type = TokenType::Integer;
		intValue = num;
	}

	TokenSequenceValue(double num)
	{
		type = TokenType::Float;
		floatValue = num;
	}


	TokenType type;
	union
	{
		const char* textValue;
		int64_t intValue;
		double floatValue;
	};
};

using TokenSequence = std::initializer_list<TokenType>;
using TokenValueSequence = std::initializer_list<TokenSequenceValue>;

class TokenSequenceParser
{
public:
	void addEntry(int result, TokenSequence tokens, TokenValueSequence values);
	bool parse(Parser& parser, int& result);
	size_t getEntryCount() { return entries.size(); }
private:
	struct Entry
	{
		std::vector<TokenType> tokens;
		std::vector<TokenSequenceValue> values;
		int result;
	};

	std::vector<Entry> entries;
};

#pragma region "File: Archs/MIPS/MipsElfRelocator.cpp"


int MipsElfRelocator::expectedMachine() const
{
	return EM_MIPS;
}

bool MipsElfRelocator::processHi16Entries(uint32_t lo16Opcode, int64_t lo16RelocationBase, std::vector<RelocationAction>& actions, std::vector<std::string>& errors)
{
	bool result = true;

	for (const Hi16Entry &hi16: hi16Entries)
	{
		if (hi16.relocationBase != lo16RelocationBase)
		{
			errors.push_back(tfm::format("Mismatched R_MIPS_HI16 with R_MIPS_LO16 of a different symbol"));
			result = false;
			continue;
		}

		int32_t addend = (int32_t)((hi16.opcode & 0xFFFF) << 16) + (int16_t)(lo16Opcode & 0xFFFF);
		int64_t fullPosition = addend + hi16.relocationBase;
		uint32_t opcode = (hi16.opcode & 0xffff0000) | (((fullPosition >> 16) + ((fullPosition & 0x8000) != 0)) & 0xFFFF);
		actions.emplace_back(hi16.offset, opcode);
	}

	hi16Entries.clear();
	return result;
}

bool MipsElfRelocator::relocateOpcode(int type, const RelocationData& data, std::vector<RelocationAction>& actions, std::vector<std::string>& errors)
{
	unsigned int op = data.opcode;
	bool result = true;

	switch (type)
	{
	case R_MIPS_26: //j, jal
		op = (op & 0xFC000000) | (((op&0x03FFFFFF)+(data.relocationBase>>2))&0x03FFFFFF);
		break;
	case R_MIPS_32:
		op += (int) data.relocationBase;
		break;
	case R_MIPS_HI16:
		hi16Entries.emplace_back(data.opcodeOffset, data.relocationBase, data.opcode);
		break;
	case R_MIPS_LO16:
		if (!processHi16Entries(op, data.relocationBase, actions, errors))
			result = false;
		op = (op&0xffff0000) | (((op&0xffff)+data.relocationBase)&0xffff);
		break;
	default:
		errors.emplace_back(tfm::format("Unknown MIPS relocation type %d",type));
		return false;
	}

	actions.emplace_back(data.opcodeOffset, op);
	return result;
}

bool MipsElfRelocator::finish(std::vector<RelocationAction>& actions, std::vector<std::string>& errors)
{
	// This shouldn't happen. If it does, relocate as if there was no lo16 opcode
	if (!hi16Entries.empty())
		return processHi16Entries(0, hi16Entries.front().relocationBase, actions, errors);
	return true;
}

void MipsElfRelocator::setSymbolAddress(RelocationData& data, int64_t symbolAddress, int symbolType)
{
	data.symbolAddress = symbolAddress;
	data.targetSymbolType = symbolType;
}

const char* mipsCtorTemplate = R"(
	addiu	sp,-32
	sw		ra,0(sp)
	sw		s0,4(sp)
	sw		s1,8(sp)
	sw		s2,12(sp)
	sw		s3,16(sp)
	li		s0,%ctorTable%
	li		s1,%ctorTable%+%ctorTableSize%
	%outerLoopLabel%:
	lw		s2,(s0)
	lw		s3,4(s0)
	addiu	s0,8
	%innerLoopLabel%:
	lw		a0,(s2)
	jalr	a0
	addiu	s2,4h
	bne		s2,s3,%innerLoopLabel%
	nop
	bne		s0,s1,%outerLoopLabel%
	nop
	lw		ra,0(sp)
	lw		s0,4(sp)
	lw		s1,8(sp)
	lw		s2,12(sp)
	lw		s3,16(sp)
	jr		ra
	addiu	sp,32
	%ctorTable%:
	.word	%ctorContent%
)";

std::unique_ptr<CAssemblerCommand> MipsElfRelocator::generateCtorStub(std::vector<ElfRelocatorCtor>& ctors)
{
	Parser parser;
	if (ctors.size() != 0)
	{
		// create constructor table
		std::string table;
		for (size_t i = 0; i < ctors.size(); i++)
		{
			if (i != 0)
				table += ',';
			table += tfm::format("%s,%s+0x%08X",ctors[i].symbolName,ctors[i].symbolName,ctors[i].size);
		}

		return parser.parseTemplate(mipsCtorTemplate,{
			{ "%ctorTable%",		Global.symbolTable.getUniqueLabelName().string() },
			{ "%ctorTableSize%",	tfm::format("%d",ctors.size()*8) },
			{ "%outerLoopLabel%",	Global.symbolTable.getUniqueLabelName().string() },
			{ "%innerLoopLabel%",	Global.symbolTable.getUniqueLabelName().string() },
			{ "%ctorContent%",		table },
		});
	} else {
		return parser.parseTemplate("jr ra :: nop");
	}
}

#pragma region "File: Archs/MIPS/MipsExpressionFunctions.cpp"


#define GET_PARAM(params,index,dest) \
	if (getExpFuncParameter(params,index,dest,funcName,false) == false) \
		return ExpressionValue();

ExpressionValue expFuncHi(const Identifier &funcName, const std::vector<ExpressionValue>& parameters)
{
	int64_t value;

	GET_PARAM(parameters,0,value);

	return ExpressionValue((int64_t)((value >> 16) + ((value & 0x8000) != 0)) & 0xFFFF);
}

ExpressionValue expFuncLo(const Identifier &funcName, const std::vector<ExpressionValue>& parameters)
{
	int64_t value;

	GET_PARAM(parameters,0,value);

	return ExpressionValue((int64_t)(int16_t)(value & 0xFFFF));
}

const ExpressionFunctionEntry mipsExpressionFunctions[] = {
	{ "lo", &expFuncLo, 1, 1, ExpFuncSafety::Safe },
	{ "hi", &expFuncHi, 1, 1, ExpFuncSafety::Safe },
};

void registerMipsExpressionFunctions(ExpressionFunctionHandler &handler)
{
	for (const auto &func : mipsExpressionFunctions)
	{
		handler.addFunction(Identifier(func.name), func.function, func.minParams, func.maxParams, func.safety);
	}
}

#pragma region "File: Archs/MIPS/MipsMacros.cpp"


MipsMacroCommand::MipsMacroCommand(std::unique_ptr<CAssemblerCommand> content, int macroFlags)
{
	this->content = std::move(content);
	this->macroFlags = macroFlags;
	IgnoreLoadDelay = Mips.GetIgnoreDelay();
}

bool MipsMacroCommand::Validate(const ValidateState &state)
{
	int64_t memoryPos = g_fileManager->getVirtualAddress();
	content->applyFileInfo();
	bool result = content->Validate(state);
	int64_t newMemoryPos = g_fileManager->getVirtualAddress();

	applyFileInfo();

	if (!IgnoreLoadDelay && Mips.GetDelaySlot() && (newMemoryPos-memoryPos) > 4
		&& (macroFlags & MIPSM_DONTWARNDELAYSLOT) == 0)
	{
		Logger::queueError(Logger::Warning, "Macro with multiple opcodes used inside a delay slot");
	}

	if (newMemoryPos == memoryPos)
		Logger::queueError(Logger::Warning, "Empty macro content");

	return result;
}

void MipsMacroCommand::Encode() const
{
	content->Encode();
}

void MipsMacroCommand::writeTempData(TempData& tempData) const
{
	content->applyFileInfo();
	content->writeTempData(tempData);
}

std::string preprocessMacro(const char* text, MipsImmediateData& immediates)
{
	// A macro is turned into a sequence of opcodes that are parsed seperately.
	// Any expressions used in the macro may be evaluated at a different memory
	// position, so the '.' operator needs to be replaced by a label at the start
	// of the macro
	Identifier labelName = Global.symbolTable.getUniqueLabelName(true);
	immediates.primary.expression.replaceMemoryPos(labelName);
	immediates.secondary.expression.replaceMemoryPos(labelName);

	return tfm::format("%s: %s",labelName,text);
}

std::unique_ptr<CAssemblerCommand> createMacro(Parser& parser, const std::string& text, int flags, std::initializer_list<AssemblyTemplateArgument> variables)
{
	std::unique_ptr<CAssemblerCommand> content = parser.parseTemplate(text,variables);
	return std::make_unique<MipsMacroCommand>(std::move(content),flags);
}

std::unique_ptr<CAssemblerCommand> generateMipsMacroAbs(Parser& parser, MipsRegisterData& registers, MipsImmediateData& immediates, int flags)
{
	const char* templateAbs = R"(
		%sraop% 	r1,%rs%,31
		xor 		%rd%,%rs%,r1
		%subop% 	%rd%,%rd%,r1
	)";

	std::string sraop, subop;

	switch (flags & MIPSM_ACCESSMASK)
	{
	case MIPSM_W:	sraop = "sra"; subop = "subu"; break;
	case MIPSM_DW:	sraop = "dsra32"; subop = "dsubu"; break;
	default: return nullptr;
	}

	std::string macroText = preprocessMacro(templateAbs,immediates);
	return createMacro(parser,macroText,flags, {
			{ "%rd%",		registers.grd.name.string() },
			{ "%rs%",		registers.grs.name.string() },
			{ "%sraop%",	sraop },
			{ "%subop%",	subop },
	});
}

std::unique_ptr<CAssemblerCommand> generateMipsMacroLiFloat(Parser& parser, MipsRegisterData& registers, MipsImmediateData& immediates, int flags)
{
	const char* templateLiFloat = R"(
		li 		r1,float(%imm%)
		mtc1	r1,%rs%
	)";

	std::string macroText = preprocessMacro(templateLiFloat,immediates);
	return createMacro(parser,macroText,flags, {
			{ "%imm%",		immediates.secondary.expression.toString() },
			{ "%rs%",		registers.frs.name.string() },
	});
}

std::unique_ptr<CAssemblerCommand> generateMipsMacroLi(Parser& parser, MipsRegisterData& registers, MipsImmediateData& immediates, int flags)
{
	const char* templateLi = R"(
		.if abs(%imm%) > 0xFFFFFFFF
			.error "Immediate value too big"
		.elseif %imm% & ~0xFFFF
			.if (%imm% & 0xFFFF8000) == 0xFFFF8000
				.if %lower%
					addiu	%rs%,r0, lo(%imm%)
				.endif
			.elseif (%imm% & 0xFFFF) == 0
				.if %upper%
					lui		%rs%, hi(%imm%)
				.elseif %lower%
					nop
				.endif
			.else
				.if %upper%
					lui		%rs%, hi(%imm%)
				.endif
				.if %lower%
					addiu 	%rs%, lo(%imm%)
				.endif
			.endif
		.else
			.if %lower%
				ori		%rs%,r0,%imm%
			.endif
		.endif
	)";

	// floats need to be treated as integers, convert them
	if (immediates.secondary.expression.isConstExpression())
	{
		ExpressionValue value = immediates.secondary.expression.evaluate();
		if (value.isFloat())
		{
			int32_t newValue = getFloatBits((float)value.floatValue);
			immediates.secondary.expression = createConstExpression(newValue);
		}
	}

	std::string macroText = preprocessMacro(templateLi,immediates);
	return createMacro(parser,macroText,flags, {
			{ "%upper%",	(flags & MIPSM_UPPER) ? "1" : "0" },
			{ "%lower%",	(flags & MIPSM_LOWER) ? "1" : "0" },
			{ "%rs%",		registers.grs.name.string() },
			{ "%imm%",		immediates.secondary.expression.toString() },
	});
}

std::unique_ptr<CAssemblerCommand> generateMipsMacroLoadStore(Parser& parser, MipsRegisterData& registers, MipsImmediateData& immediates, int flags)
{
	const char* templateLoadStore = R"(
		.if %imm% & ~0xFFFFFFFF
			.error "Address too big"
		.elseif %imm% < 0x8000 || (%imm% & 0xFFFF8000) == 0xFFFF8000
			.if %lower%
				%op%	%rs%, lo(%imm%)(r0)
			.elseif %upper%
				nop
			.endif
		.else
			.if %upper%
				lui		%temp%, hi(%imm%)
			.endif
			.if %lower%
				%op%	%rs%, lo(%imm%)(%temp%)
			.endif
		.endif
	)";

	const char* op;
	bool isCop = false;
	switch (flags & (MIPSM_ACCESSMASK|MIPSM_LOAD|MIPSM_STORE))
	{
	case MIPSM_LOAD|MIPSM_B:		op = "lb"; break;
	case MIPSM_LOAD|MIPSM_BU:		op = "lbu"; break;
	case MIPSM_LOAD|MIPSM_HW:		op = "lh"; break;
	case MIPSM_LOAD|MIPSM_HWU:		op = "lhu"; break;
	case MIPSM_LOAD|MIPSM_W:		op = "lw"; break;
	case MIPSM_LOAD|MIPSM_WU:		op = "lwu"; break;
	case MIPSM_LOAD|MIPSM_DW:		op = "ld"; break;
	case MIPSM_LOAD|MIPSM_LLSCW:	op = "ll"; break;
	case MIPSM_LOAD|MIPSM_LLSCDW:	op = "lld"; break;
	case MIPSM_LOAD|MIPSM_COP1:		op = "lwc1"; isCop = true; break;
	case MIPSM_LOAD|MIPSM_COP2:		op = "lwc2"; isCop = true; break;
	case MIPSM_LOAD|MIPSM_DCOP1:	op = "ldc1"; isCop = true; break;
	case MIPSM_LOAD|MIPSM_DCOP2:	op = "ldc2"; isCop = true; break;
	case MIPSM_STORE|MIPSM_B:		op = "sb"; break;
	case MIPSM_STORE|MIPSM_HW:		op = "sh"; break;
	case MIPSM_STORE|MIPSM_W:		op = "sw"; break;
	case MIPSM_STORE|MIPSM_DW:		op = "sd"; break;
	case MIPSM_STORE|MIPSM_LLSCW:	op = "sc"; break;
	case MIPSM_STORE|MIPSM_LLSCDW:	op = "scd"; break;
	case MIPSM_STORE|MIPSM_COP1:	op = "swc1"; isCop = true; break;
	case MIPSM_STORE|MIPSM_COP2:	op = "swc2"; isCop = true; break;
	case MIPSM_STORE|MIPSM_DCOP1:	op = "sdc1"; isCop = true; break;
	case MIPSM_STORE|MIPSM_DCOP2:	op = "sdc2"; isCop = true; break;
	default: return nullptr;
	}

	std::string macroText = preprocessMacro(templateLoadStore,immediates);

	bool store = (flags & MIPSM_STORE) != 0;
	return createMacro(parser,macroText,flags, {
			{ "%upper%",	(flags & MIPSM_UPPER) ? "1" : "0" },
			{ "%lower%",	(flags & MIPSM_LOWER) ? "1" : "0" },
			{ "%rs%",		isCop ? registers.frs.name.string() : registers.grs.name.string() },
			{ "%temp%",	isCop || store ? "r1" : registers.grs.name.string() },
			{ "%imm%",		immediates.secondary.expression.toString() },
			{ "%op%",		op },
	});
}

std::unique_ptr<CAssemblerCommand> generateMipsMacroLoadUnaligned(Parser& parser, MipsRegisterData& registers, MipsImmediateData& immediates, int flags)
{
	const char* selectedTemplate;

	std::string op, size;
	int type = flags & MIPSM_ACCESSMASK;
	if (type == MIPSM_HW || type == MIPSM_HWU)
	{
		const char* templateHalfword = R"(
			.if (%off% < 0x8000) && ((%off%+1) >= 0x8000)
				.error "Immediate offset too big"
			.else
				%op%	r1,%off%+1(%rs%)
				%op%	%rd%,%off%(%rs%)
				sll		r1,8
				or		%rd%,r1
			.endif
		)";

		op = type == MIPSM_HWU ? "lbu" : "lb";
		selectedTemplate = templateHalfword;
	} else if (type == MIPSM_W || type == MIPSM_DW)
	{
		const char* templateWord = R"(
			.if (%off% < 0x8000) && ((%off%+%size%-1) >= 0x8000)
				.error "Immediate offset too big"
			.else
				%op%l	%rd%,%off%+%size%-1(%rs%)
				%op%r	%rd%,%off%(%rs%)
			.endif
		)";

		if (registers.grs.num == registers.grd.num)
		{
			Logger::printError(Logger::Error, "Cannot use same register as source and destination");
			return std::make_unique<DummyCommand>();
		}

		op = type == MIPSM_W ? "lw" : "ld";
		size = type == MIPSM_W ? "4" : "8";
		selectedTemplate = templateWord;
	} else {
		return nullptr;
	}

	std::string macroText = preprocessMacro(selectedTemplate,immediates);
	return createMacro(parser,macroText,flags, {
			{ "%rs%",		registers.grs.name.string() },
			{ "%rd%",		registers.grd.name.string() },
			{ "%off%",		immediates.primary.expression.toString() },
			{ "%op%",		op },
			{ "%size%",    size },
	});
}

std::unique_ptr<CAssemblerCommand> generateMipsMacroStoreUnaligned(Parser& parser, MipsRegisterData& registers, MipsImmediateData& immediates, int flags)
{
	const char* selectedTemplate;

	std::string op, size;
	int type = flags & MIPSM_ACCESSMASK;
	if (type == MIPSM_HW)
	{
		const char* templateHalfword = R"(
			.if (%off% < 0x8000) && ((%off%+1) >= 0x8000)
				.error "Immediate offset too big"
			.else
				sb		%rd%,%off%(%rs%)
				srl		r1,%rd%,8
				sb		r1,%off%+1(%rs%)
			.endif
		)";

		selectedTemplate = templateHalfword;
	} else if (type == MIPSM_W || type == MIPSM_DW)
	{
		const char* templateWord = R"(
			.if (%off% < 0x8000) && ((%off%+%size%-1) >= 0x8000)
				.error "Immediate offset too big"
			.else
				%op%l	%rd%,%off%+%size%-1(%rs%)
				%op%r	%rd%,%off%(%rs%)
			.endif
		)";

		if (registers.grs.num == registers.grd.num)
		{
			Logger::printError(Logger::Error, "Cannot use same register as source and destination");
			return std::make_unique<DummyCommand>();
		}

		op = type == MIPSM_W ? "sw" : "sd";
		size = type == MIPSM_W ? "4" : "8";
		selectedTemplate = templateWord;
	} else {
		return nullptr;
	}

	std::string macroText = preprocessMacro(selectedTemplate,immediates);
	return createMacro(parser,macroText,flags, {
			{ "%rs%",		registers.grs.name.string() },
			{ "%rd%",		registers.grd.name.string() },
			{ "%off%",		immediates.primary.expression.toString() },
			{ "%op%",		op },
			{ "%size%",	size },
	});
}

std::unique_ptr<CAssemblerCommand> generateMipsMacroBranch(Parser& parser, MipsRegisterData& registers, MipsImmediateData& immediates, int flags)
{
	const char* selectedTemplate;

	int type = flags & MIPSM_CONDITIONMASK;

	bool bne = type == MIPSM_NE;
	bool beq = type == MIPSM_EQ;
	bool beqz = type == MIPSM_GE || type == MIPSM_GEU;
	bool bnez = type == MIPSM_LT || type == MIPSM_LTU;
	bool unsigned_ = type == MIPSM_GEU || type == MIPSM_LTU;
	bool immediate = (flags & MIPSM_IMM) != 0;
	bool likely = (flags & MIPSM_LIKELY) != 0;
	bool revcmp = (flags & MIPSM_REVCMP) != 0;

	std::string op;
	if (bne || beq)
	{
		const char* templateNeEq = R"(
			.if %imm% == 0
				%op%	%rs%,r0,%dest%
			.else
				li		r1,%imm%
				%op%	%rs%,r1,%dest%
			.endif
		)";

		selectedTemplate = templateNeEq;
		if(likely)
			op = bne ? "bnel" : "beql";
		else
			op = bne ? "bne" : "beq";
	} else if (immediate && (beqz || bnez))
	{
		const char* templateImmediate = R"(
			.if %revcmp% && %imm% == 0
				slt%u% 	r1,r0,%rs%
			.elseif %revcmp%
				li		r1,%imm%
				slt%u%	r1,r1,%rs%
			.elseif (%imm% < -0x8000) || (%imm% >= 0x8000)
				li		r1,%imm%
				slt%u%	r1,%rs%,r1
			.else
				slti%u%	r1,%rs%,%imm%
			.endif
			%op%	r1,%dest%
		)";

		selectedTemplate = templateImmediate;
		if(likely)
			op = bnez ? "bnezl" : "beqzl";
		else
			op = bnez ? "bnez" : "beqz";
	} else if (beqz || bnez)
	{
		const char* templateRegister = R"(
			.if %revcmp%
				slt%u%	r1,%rt%,%rs%
			.else
				slt%u%	r1,%rs%,%rt%
			.endif
			%op%	r1,%dest%
		)";

		selectedTemplate = templateRegister;
		if(likely)
			op = bnez ? "bnezl" : "beqzl";
		else
			op = bnez ? "bnez" : "beqz";
	} else {
		return nullptr;
	}

	std::string macroText = preprocessMacro(selectedTemplate,immediates);
	return createMacro(parser,macroText,flags, {
			{ "%op%",		op },
			{ "%u%",		unsigned_ ? "u" : " "},
			{ "%revcmp%",	revcmp ? "1" : "0"},
			{ "%rs%",		registers.grs.name.string() },
			{ "%rt%",		registers.grt.name.string() },
			{ "%imm%",		immediates.primary.expression.toString() },
			{ "%dest%",	immediates.secondary.expression.toString() },
	});
}

std::unique_ptr<CAssemblerCommand> generateMipsMacroSet(Parser& parser, MipsRegisterData& registers, MipsImmediateData& immediates, int flags)
{
	const char* selectedTemplate;

	int type = flags & MIPSM_CONDITIONMASK;

	bool ne = type == MIPSM_NE;
	bool eq = type == MIPSM_EQ;
	bool ge = type == MIPSM_GE || type == MIPSM_GEU;
	bool lt = type == MIPSM_LT || type == MIPSM_LTU;
	bool unsigned_ = type == MIPSM_GEU || type == MIPSM_LTU;
	bool immediate = (flags & MIPSM_IMM) != 0;
	bool revcmp = (flags & MIPSM_REVCMP) != 0;

	if (immediate && (ne || eq))
	{
		const char* templateImmediateEqNe = R"(
			.if %imm% & ~0xFFFF
				li		%rd%,%imm%
				xor		%rd%,%rs%,%rd%
			.else
				xori	%rd%,%rs%,%imm%
			.endif
			.if %eq%
				sltiu	%rd%,%rd%,1
			.else
				sltu	%rd%,r0,%rd%
			.endif
		)";

		selectedTemplate = templateImmediateEqNe;
	} else if (ne || eq)
	{
		const char* templateEqNe = R"(
			xor		%rd%,%rs%,%rt%
			.if %eq%
				sltiu	%rd%,%rd%,1
			.else
				sltu	%rd%,r0,%rd%
			.endif
		)";

		selectedTemplate = templateEqNe;
	} else if (immediate && (ge || lt))
	{
		const char* templateImmediateGeLt = R"(
			.if %revcmp% && %imm% == 0
				slt%u%	%rd%,r0,%rs%
			.elseif %revcmp%
				li		%rd%,%imm%
				slt%u%	%rd%,%rd%,%rs%
			.elseif (%imm% < -0x8000) || (%imm% >= 0x8000)
				li		%rd%,%imm%
				slt%u%	%rd%,%rs%,%rd%
			.else
				slti%u%	%rd%,%rs%,%imm%
			.endif
			.if %ge%
				xori	%rd%,%rd%,1
			.endif
		)";

		selectedTemplate = templateImmediateGeLt;
	} else if (ge)
	{
		const char* templateGe = R"(
			.if %revcmp%
				slt%u%	%rd%,%rt%,%rs%
			.else
				slt%u%	%rd%,%rs%,%rt%
			.endif
			xori	%rd%,%rd%,1
		)";

		selectedTemplate = templateGe;
	} else
	{
		return nullptr;
	}

	std::string macroText = preprocessMacro(selectedTemplate,immediates);
	return createMacro(parser,macroText,flags, {
			{ "%u%",		unsigned_ ? "u" : " "},
			{ "%eq%",		eq ? "1" : "0" },
			{ "%ge%",		ge ? "1" : "0" },
			{ "%revcmp%",	revcmp ? "1" : "0" },
			{ "%rd%",		registers.grd.name.string() },
			{ "%rs%",		registers.grs.name.string() },
			{ "%rt%",		registers.grt.name.string() },
			{ "%imm%",		immediates.secondary.expression.toString() },
	});
}

std::unique_ptr<CAssemblerCommand> generateMipsMacroRotate(Parser& parser, MipsRegisterData& registers, MipsImmediateData& immediates, int flags)
{
	bool left = (flags & MIPSM_LEFT) != 0;
	bool immediate = (flags & MIPSM_IMM) != 0;
	bool psp = Mips.GetVersion() == MARCH_PSP;

	const char* selectedTemplate;
	if (psp && immediate)
	{
		const char* templatePspImmediate = R"(
			.if %amount% != 0
				.if %left%
					rotr	%rd%,%rs%,-%amount%&31
				.else
					rotr	%rd%,%rs%,%amount%
				.endif
			.else
				move	%rd%,%rs%
			.endif
		)";

		selectedTemplate = templatePspImmediate;
	} else if (psp)
	{
		const char* templatePspRegister = R"(
			.if %left%
				negu	r1,%rt%
				rotrv	%rd%,%rs%,r1
			.else
				rotrv	%rd%,%rs%,%rt%
			.endif
		)";

		selectedTemplate = templatePspRegister;
	} else if (immediate)
	{
		const char* templateImmediate = R"(
			.if %amount% != 0
				.if %left%
					srl	r1,%rs%,-%amount%&31
					sll	%rd%,%rs%,%amount%
				.else
					sll	r1,%rs%,-%amount%&31
					srl	%rd%,%rs%,%amount%
				.endif
				or		%rd%,%rd%,r1
			.else
				move	%rd%,%rs%
			.endif
		)";

		selectedTemplate = templateImmediate;
	} else {
		const char* templateRegister = R"(
			negu	r1,%rt%
			.if %left%
				srlv	r1,%rs%,r1
				sllv	%rd%,%rs%,%rt%
			.else
				sllv	r1,%rs%,r1
				srlv	%rd%,%rs%,%rt%
			.endif
			or	%rd%,%rd%,r1
		)";

		selectedTemplate = templateRegister;
	}

	std::string macroText = preprocessMacro(selectedTemplate,immediates);
	return createMacro(parser,macroText,flags, {
			{ "%left%",	left ? "1" : "0" },
			{ "%rd%",		registers.grd.name.string() },
			{ "%rs%",		registers.grs.name.string() },
			{ "%rt%",		registers.grt.name.string() },
			{ "%amount%",	immediates.primary.expression.toString() },
	});
}

/* Placeholders
	i = i1 = 16 bit immediate
	I = i2 = 32 bit immediate
	s,t,d = registers */
const MipsMacroDefinition mipsMacros[] = {
	{ "abs",     "d,s",     &generateMipsMacroAbs,             MIPSM_W },
	{ "dabs",    "d,s",     &generateMipsMacroAbs,             MIPSM_DW },

	{ "li",      "s,I",     &generateMipsMacroLi,              MIPSM_IMM|MIPSM_UPPER|MIPSM_LOWER },
	{ "li.u",    "s,I",     &generateMipsMacroLi,              MIPSM_IMM|MIPSM_UPPER },
	{ "li.l",    "s,I",     &generateMipsMacroLi,              MIPSM_IMM|MIPSM_LOWER },
	{ "la",      "s,I",     &generateMipsMacroLi,              MIPSM_IMM|MIPSM_UPPER|MIPSM_LOWER },
	{ "la.u",    "s,I",     &generateMipsMacroLi,              MIPSM_IMM|MIPSM_UPPER },
	{ "la.l",    "s,I",     &generateMipsMacroLi,              MIPSM_IMM|MIPSM_LOWER },

	{ "li.s",    "S,I",     &generateMipsMacroLiFloat,         MIPSM_IMM },

	{ "lb",      "s,I",     &generateMipsMacroLoadStore,       MIPSM_LOAD|MIPSM_B|MIPSM_UPPER|MIPSM_LOWER },
	{ "lbu",     "s,I",     &generateMipsMacroLoadStore,       MIPSM_LOAD|MIPSM_BU|MIPSM_UPPER|MIPSM_LOWER },
	{ "lh",      "s,I",     &generateMipsMacroLoadStore,       MIPSM_LOAD|MIPSM_HW|MIPSM_UPPER|MIPSM_LOWER },
	{ "lhu",     "s,I",     &generateMipsMacroLoadStore,       MIPSM_LOAD|MIPSM_HWU|MIPSM_UPPER|MIPSM_LOWER },
	{ "lw",      "s,I",     &generateMipsMacroLoadStore,       MIPSM_LOAD|MIPSM_W|MIPSM_UPPER|MIPSM_LOWER },
	{ "lwu",     "s,I",     &generateMipsMacroLoadStore,       MIPSM_LOAD|MIPSM_WU|MIPSM_UPPER|MIPSM_LOWER },
	{ "ld",      "s,I",     &generateMipsMacroLoadStore,       MIPSM_LOAD|MIPSM_DW|MIPSM_UPPER|MIPSM_LOWER },
	{ "ll",      "s,I",     &generateMipsMacroLoadStore,       MIPSM_LOAD|MIPSM_LLSCW|MIPSM_UPPER|MIPSM_LOWER },
	{ "lld",     "s,I",     &generateMipsMacroLoadStore,       MIPSM_LOAD|MIPSM_LLSCDW|MIPSM_UPPER|MIPSM_LOWER },
	{ "lwc1",    "S,I",     &generateMipsMacroLoadStore,       MIPSM_LOAD|MIPSM_COP1|MIPSM_UPPER|MIPSM_LOWER },
	{ "l.s",     "S,I",     &generateMipsMacroLoadStore,       MIPSM_LOAD|MIPSM_COP1|MIPSM_UPPER|MIPSM_LOWER },
	{ "lwc2",    "S,I",     &generateMipsMacroLoadStore,       MIPSM_LOAD|MIPSM_COP2|MIPSM_UPPER|MIPSM_LOWER },
	{ "ldc1",    "S,I",     &generateMipsMacroLoadStore,       MIPSM_LOAD|MIPSM_DCOP1|MIPSM_UPPER|MIPSM_LOWER },
	{ "l.d",     "S,I",     &generateMipsMacroLoadStore,       MIPSM_LOAD|MIPSM_DCOP1|MIPSM_UPPER|MIPSM_LOWER },
	{ "ldc2",    "S,I",     &generateMipsMacroLoadStore,       MIPSM_LOAD|MIPSM_DCOP2|MIPSM_UPPER|MIPSM_LOWER },

	{ "lb.u",    "s,I",     &generateMipsMacroLoadStore,       MIPSM_LOAD|MIPSM_B|MIPSM_UPPER },
	{ "lbu.u",   "s,I",     &generateMipsMacroLoadStore,       MIPSM_LOAD|MIPSM_BU|MIPSM_UPPER },
	{ "lh.u",    "s,I",     &generateMipsMacroLoadStore,       MIPSM_LOAD|MIPSM_HW|MIPSM_UPPER },
	{ "lhu.u",   "s,I",     &generateMipsMacroLoadStore,       MIPSM_LOAD|MIPSM_HWU|MIPSM_UPPER },
	{ "lw.u",    "s,I",     &generateMipsMacroLoadStore,       MIPSM_LOAD|MIPSM_W|MIPSM_UPPER },
	{ "lwu.u",   "s,I",     &generateMipsMacroLoadStore,       MIPSM_LOAD|MIPSM_WU|MIPSM_UPPER },
	{ "ld.u",    "s,I",     &generateMipsMacroLoadStore,       MIPSM_LOAD|MIPSM_DW|MIPSM_UPPER },
	{ "ll.u",    "s,I",     &generateMipsMacroLoadStore,       MIPSM_LOAD|MIPSM_LLSCW|MIPSM_UPPER },
	{ "lld.u",   "s,I",     &generateMipsMacroLoadStore,       MIPSM_LOAD|MIPSM_LLSCDW|MIPSM_UPPER },
	{ "lwc1.u",  "S,I",     &generateMipsMacroLoadStore,       MIPSM_LOAD|MIPSM_COP1|MIPSM_UPPER },
	{ "l.s.u",   "S,I",     &generateMipsMacroLoadStore,       MIPSM_LOAD|MIPSM_COP1|MIPSM_UPPER },
	{ "lwc2.u",  "S,I",     &generateMipsMacroLoadStore,       MIPSM_LOAD|MIPSM_COP2|MIPSM_UPPER },
	{ "ldc1.u",  "S,I",     &generateMipsMacroLoadStore,       MIPSM_LOAD|MIPSM_DCOP1|MIPSM_UPPER },
	{ "l.d.u",   "S,I",     &generateMipsMacroLoadStore,       MIPSM_LOAD|MIPSM_DCOP1|MIPSM_UPPER },
	{ "ldc2.u",  "S,I",     &generateMipsMacroLoadStore,       MIPSM_LOAD|MIPSM_DCOP2|MIPSM_UPPER },

	{ "lb.l",    "s,I",     &generateMipsMacroLoadStore,       MIPSM_LOAD|MIPSM_B|MIPSM_LOWER },
	{ "lbu.l",   "s,I",     &generateMipsMacroLoadStore,       MIPSM_LOAD|MIPSM_BU|MIPSM_LOWER },
	{ "lh.l",    "s,I",     &generateMipsMacroLoadStore,       MIPSM_LOAD|MIPSM_HW|MIPSM_LOWER },
	{ "lhu.l",   "s,I",     &generateMipsMacroLoadStore,       MIPSM_LOAD|MIPSM_HWU|MIPSM_LOWER },
	{ "lw.l",    "s,I",     &generateMipsMacroLoadStore,       MIPSM_LOAD|MIPSM_W|MIPSM_LOWER },
	{ "lwu.l",   "s,I",     &generateMipsMacroLoadStore,       MIPSM_LOAD|MIPSM_WU|MIPSM_LOWER },
	{ "ld.l",    "s,I",     &generateMipsMacroLoadStore,       MIPSM_LOAD|MIPSM_DW|MIPSM_LOWER },
	{ "ll.l",    "s,I",     &generateMipsMacroLoadStore,       MIPSM_LOAD|MIPSM_LLSCW|MIPSM_LOWER },
	{ "lld.l",   "s,I",     &generateMipsMacroLoadStore,       MIPSM_LOAD|MIPSM_LLSCDW|MIPSM_LOWER },
	{ "lwc1.l",  "S,I",     &generateMipsMacroLoadStore,       MIPSM_LOAD|MIPSM_COP1|MIPSM_LOWER },
	{ "l.s.l",   "S,I",     &generateMipsMacroLoadStore,       MIPSM_LOAD|MIPSM_COP1|MIPSM_LOWER },
	{ "lwc2.l",  "S,I",     &generateMipsMacroLoadStore,       MIPSM_LOAD|MIPSM_COP2|MIPSM_LOWER },
	{ "ldc1.l",  "S,I",     &generateMipsMacroLoadStore,       MIPSM_LOAD|MIPSM_DCOP1|MIPSM_LOWER },
	{ "l.d.l",   "S,I",     &generateMipsMacroLoadStore,       MIPSM_LOAD|MIPSM_DCOP1|MIPSM_LOWER },
	{ "ldc2.l",  "S,I",     &generateMipsMacroLoadStore,       MIPSM_LOAD|MIPSM_DCOP2|MIPSM_LOWER },

	{ "ulh",     "d,i(s)",  &generateMipsMacroLoadUnaligned,   MIPSM_HW|MIPSM_IMM },
	{ "ulh",     "d,(s)",   &generateMipsMacroLoadUnaligned,   MIPSM_HW },
	{ "ulhu",    "d,i(s)",  &generateMipsMacroLoadUnaligned,   MIPSM_HWU|MIPSM_IMM },
	{ "ulhu",    "d,(s)",   &generateMipsMacroLoadUnaligned,   MIPSM_HWU },
	{ "ulw",     "d,i(s)",  &generateMipsMacroLoadUnaligned,   MIPSM_W|MIPSM_IMM },
	{ "ulw",     "d,(s)",   &generateMipsMacroLoadUnaligned,   MIPSM_W },
	{ "uld",     "d,i(s)",  &generateMipsMacroLoadUnaligned,   MIPSM_DW|MIPSM_IMM },
	{ "uld",     "d,(s)",   &generateMipsMacroLoadUnaligned,   MIPSM_DW },

	{ "sb",      "s,I",     &generateMipsMacroLoadStore,       MIPSM_STORE|MIPSM_B|MIPSM_UPPER|MIPSM_LOWER },
	{ "sh",      "s,I",     &generateMipsMacroLoadStore,       MIPSM_STORE|MIPSM_HW|MIPSM_UPPER|MIPSM_LOWER },
	{ "sw",      "s,I",     &generateMipsMacroLoadStore,       MIPSM_STORE|MIPSM_W|MIPSM_UPPER|MIPSM_LOWER },
	{ "sd",      "s,I",     &generateMipsMacroLoadStore,       MIPSM_STORE|MIPSM_DW|MIPSM_UPPER|MIPSM_LOWER },
	{ "sc",      "s,I",     &generateMipsMacroLoadStore,       MIPSM_STORE|MIPSM_LLSCW|MIPSM_UPPER|MIPSM_LOWER },
	{ "scd",     "s,I",     &generateMipsMacroLoadStore,       MIPSM_STORE|MIPSM_LLSCDW|MIPSM_UPPER|MIPSM_LOWER },
	{ "swc1",    "S,I",     &generateMipsMacroLoadStore,       MIPSM_STORE|MIPSM_COP1|MIPSM_UPPER|MIPSM_LOWER },
	{ "s.s",     "S,I",     &generateMipsMacroLoadStore,       MIPSM_STORE|MIPSM_COP1|MIPSM_UPPER|MIPSM_LOWER },
	{ "swc2",    "S,I",     &generateMipsMacroLoadStore,       MIPSM_STORE|MIPSM_COP2|MIPSM_UPPER|MIPSM_LOWER },
	{ "sdc1",    "S,I",     &generateMipsMacroLoadStore,       MIPSM_STORE|MIPSM_DCOP1|MIPSM_UPPER|MIPSM_LOWER },
	{ "s.d",     "S,I",     &generateMipsMacroLoadStore,       MIPSM_STORE|MIPSM_DCOP1|MIPSM_UPPER|MIPSM_LOWER },
	{ "sdc2",    "S,I",     &generateMipsMacroLoadStore,       MIPSM_STORE|MIPSM_DCOP2|MIPSM_UPPER|MIPSM_LOWER },

	{ "sb.u",    "s,I",     &generateMipsMacroLoadStore,       MIPSM_STORE|MIPSM_B|MIPSM_UPPER },
	{ "sh.u",    "s,I",     &generateMipsMacroLoadStore,       MIPSM_STORE|MIPSM_HW|MIPSM_UPPER },
	{ "sw.u",    "s,I",     &generateMipsMacroLoadStore,       MIPSM_STORE|MIPSM_W|MIPSM_UPPER },
	{ "sd.u",    "s,I",     &generateMipsMacroLoadStore,       MIPSM_STORE|MIPSM_DW|MIPSM_UPPER },
	{ "sc.u",    "s,I",     &generateMipsMacroLoadStore,       MIPSM_STORE|MIPSM_LLSCW|MIPSM_UPPER },
	{ "scd.u",   "s,I",     &generateMipsMacroLoadStore,       MIPSM_STORE|MIPSM_LLSCDW|MIPSM_UPPER },
	{ "swc1.u",  "S,I",     &generateMipsMacroLoadStore,       MIPSM_STORE|MIPSM_COP1|MIPSM_UPPER },
	{ "s.s.u",   "S,I",     &generateMipsMacroLoadStore,       MIPSM_STORE|MIPSM_COP1|MIPSM_UPPER },
	{ "swc2.u",  "S,I",     &generateMipsMacroLoadStore,       MIPSM_STORE|MIPSM_COP2|MIPSM_UPPER },
	{ "sdc1.u",  "S,I",     &generateMipsMacroLoadStore,       MIPSM_STORE|MIPSM_DCOP1|MIPSM_UPPER },
	{ "s.d.u",   "S,I",     &generateMipsMacroLoadStore,       MIPSM_STORE|MIPSM_DCOP1|MIPSM_UPPER },
	{ "sdc2.u",  "S,I",     &generateMipsMacroLoadStore,       MIPSM_STORE|MIPSM_DCOP2|MIPSM_UPPER },

	{ "sb.l",    "s,I",     &generateMipsMacroLoadStore,       MIPSM_STORE|MIPSM_B|MIPSM_LOWER },
	{ "sh.l",    "s,I",     &generateMipsMacroLoadStore,       MIPSM_STORE|MIPSM_HW|MIPSM_LOWER },
	{ "sw.l",    "s,I",     &generateMipsMacroLoadStore,       MIPSM_STORE|MIPSM_W|MIPSM_LOWER },
	{ "sd.l",    "s,I",     &generateMipsMacroLoadStore,       MIPSM_STORE|MIPSM_DW|MIPSM_LOWER },
	{ "sc.l",    "s,I",     &generateMipsMacroLoadStore,       MIPSM_STORE|MIPSM_LLSCW|MIPSM_LOWER },
	{ "scd.l",   "s,I",     &generateMipsMacroLoadStore,       MIPSM_STORE|MIPSM_LLSCDW|MIPSM_LOWER },
	{ "swc1.l",  "S,I",     &generateMipsMacroLoadStore,       MIPSM_STORE|MIPSM_COP1|MIPSM_LOWER },
	{ "s.s.l",   "S,I",     &generateMipsMacroLoadStore,       MIPSM_STORE|MIPSM_COP1|MIPSM_LOWER },
	{ "swc2.l",  "S,I",     &generateMipsMacroLoadStore,       MIPSM_STORE|MIPSM_COP2|MIPSM_LOWER },
	{ "sdc1.l",  "S,I",     &generateMipsMacroLoadStore,       MIPSM_STORE|MIPSM_DCOP1|MIPSM_LOWER },
	{ "s.d.l",   "S,I",     &generateMipsMacroLoadStore,       MIPSM_STORE|MIPSM_DCOP1|MIPSM_LOWER },
	{ "sdc2.l",  "S,I",     &generateMipsMacroLoadStore,       MIPSM_STORE|MIPSM_DCOP2|MIPSM_LOWER },

	{ "ush",     "d,i(s)",  &generateMipsMacroStoreUnaligned,  MIPSM_HW|MIPSM_IMM },
	{ "ush",     "d,(s)",   &generateMipsMacroStoreUnaligned,  MIPSM_HW },
	{ "usw",     "d,i(s)",  &generateMipsMacroStoreUnaligned,  MIPSM_W|MIPSM_IMM },
	{ "usw",     "d,(s)",   &generateMipsMacroStoreUnaligned,  MIPSM_W },
	{ "usd",     "d,i(s)",  &generateMipsMacroStoreUnaligned,  MIPSM_DW|MIPSM_IMM },
	{ "usd",     "d,(s)",   &generateMipsMacroStoreUnaligned,  MIPSM_DW },

	{ "blt",     "s,t,I",   &generateMipsMacroBranch,          MIPSM_LT|MIPSM_DONTWARNDELAYSLOT },
	{ "blt",     "s,i,I",   &generateMipsMacroBranch,          MIPSM_LT|MIPSM_IMM|MIPSM_DONTWARNDELAYSLOT },
	{ "bgt",     "s,t,I",   &generateMipsMacroBranch,          MIPSM_LT|MIPSM_REVCMP|MIPSM_DONTWARNDELAYSLOT },
	{ "bgt",     "s,i,I",   &generateMipsMacroBranch,          MIPSM_LT|MIPSM_IMM|MIPSM_REVCMP|MIPSM_DONTWARNDELAYSLOT },
	{ "bltu",    "s,t,I",   &generateMipsMacroBranch,          MIPSM_LTU|MIPSM_DONTWARNDELAYSLOT },
	{ "bltu",    "s,i,I",   &generateMipsMacroBranch,          MIPSM_LTU|MIPSM_IMM|MIPSM_DONTWARNDELAYSLOT },
	{ "bgtu",    "s,t,I",   &generateMipsMacroBranch,          MIPSM_LTU|MIPSM_REVCMP|MIPSM_DONTWARNDELAYSLOT },
	{ "bgtu",    "s,i,I",   &generateMipsMacroBranch,          MIPSM_LTU|MIPSM_IMM|MIPSM_REVCMP|MIPSM_DONTWARNDELAYSLOT },
	{ "bge",     "s,t,I",   &generateMipsMacroBranch,          MIPSM_GE|MIPSM_DONTWARNDELAYSLOT },
	{ "bge",     "s,i,I",   &generateMipsMacroBranch,          MIPSM_GE|MIPSM_IMM|MIPSM_DONTWARNDELAYSLOT },
	{ "ble",     "s,t,I",   &generateMipsMacroBranch,          MIPSM_GE|MIPSM_REVCMP|MIPSM_DONTWARNDELAYSLOT },
	{ "ble",     "s,i,I",   &generateMipsMacroBranch,          MIPSM_GE|MIPSM_IMM|MIPSM_REVCMP|MIPSM_DONTWARNDELAYSLOT },
	{ "bgeu",    "s,t,I",   &generateMipsMacroBranch,          MIPSM_GEU|MIPSM_DONTWARNDELAYSLOT },
	{ "bgeu",    "s,i,I",   &generateMipsMacroBranch,          MIPSM_GEU|MIPSM_IMM|MIPSM_DONTWARNDELAYSLOT },
	{ "bleu",    "s,t,I",   &generateMipsMacroBranch,          MIPSM_GEU|MIPSM_REVCMP|MIPSM_DONTWARNDELAYSLOT },
	{ "bleu",    "s,i,I",   &generateMipsMacroBranch,          MIPSM_GEU|MIPSM_IMM|MIPSM_REVCMP|MIPSM_DONTWARNDELAYSLOT },
	{ "bne",     "s,i,I",   &generateMipsMacroBranch,          MIPSM_NE|MIPSM_IMM|MIPSM_DONTWARNDELAYSLOT },
	{ "beq",     "s,i,I",   &generateMipsMacroBranch,          MIPSM_EQ|MIPSM_IMM|MIPSM_DONTWARNDELAYSLOT },
	{ "bltl",    "s,t,I",   &generateMipsMacroBranch,          MIPSM_LT|MIPSM_DONTWARNDELAYSLOT|MIPSM_LIKELY },
	{ "bltl",    "s,i,I",   &generateMipsMacroBranch,          MIPSM_LT|MIPSM_IMM|MIPSM_DONTWARNDELAYSLOT|MIPSM_LIKELY },
	{ "bgtl",    "s,t,I",   &generateMipsMacroBranch,          MIPSM_LT|MIPSM_REVCMP|MIPSM_DONTWARNDELAYSLOT|MIPSM_LIKELY },
	{ "bgtl",    "s,i,I",   &generateMipsMacroBranch,          MIPSM_LT|MIPSM_IMM|MIPSM_REVCMP|MIPSM_DONTWARNDELAYSLOT|MIPSM_LIKELY },
	{ "bltul",   "s,t,I",   &generateMipsMacroBranch,          MIPSM_LTU|MIPSM_DONTWARNDELAYSLOT|MIPSM_LIKELY },
	{ "bltul",   "s,i,I",   &generateMipsMacroBranch,          MIPSM_LTU|MIPSM_IMM|MIPSM_DONTWARNDELAYSLOT|MIPSM_LIKELY },
	{ "bgtul",   "s,t,I",   &generateMipsMacroBranch,          MIPSM_LTU|MIPSM_REVCMP|MIPSM_DONTWARNDELAYSLOT|MIPSM_LIKELY },
	{ "bgtul",   "s,i,I",   &generateMipsMacroBranch,          MIPSM_LTU|MIPSM_IMM|MIPSM_REVCMP|MIPSM_DONTWARNDELAYSLOT|MIPSM_LIKELY },
	{ "bgel",    "s,t,I",   &generateMipsMacroBranch,          MIPSM_GE|MIPSM_DONTWARNDELAYSLOT|MIPSM_LIKELY },
	{ "bgel",    "s,i,I",   &generateMipsMacroBranch,          MIPSM_GE|MIPSM_IMM|MIPSM_DONTWARNDELAYSLOT|MIPSM_LIKELY },
	{ "blel",    "s,t,I",   &generateMipsMacroBranch,          MIPSM_GE|MIPSM_REVCMP|MIPSM_DONTWARNDELAYSLOT|MIPSM_LIKELY },
	{ "blel",    "s,i,I",   &generateMipsMacroBranch,          MIPSM_GE|MIPSM_IMM|MIPSM_REVCMP|MIPSM_DONTWARNDELAYSLOT|MIPSM_LIKELY },
	{ "bgeul",   "s,t,I",   &generateMipsMacroBranch,          MIPSM_GEU|MIPSM_DONTWARNDELAYSLOT|MIPSM_LIKELY },
	{ "bgeul",   "s,i,I",   &generateMipsMacroBranch,          MIPSM_GEU|MIPSM_IMM|MIPSM_DONTWARNDELAYSLOT|MIPSM_LIKELY },
	{ "bleul",   "s,t,I",   &generateMipsMacroBranch,          MIPSM_GEU|MIPSM_REVCMP|MIPSM_DONTWARNDELAYSLOT|MIPSM_LIKELY },
	{ "bleul",   "s,i,I",   &generateMipsMacroBranch,          MIPSM_GEU|MIPSM_IMM|MIPSM_REVCMP|MIPSM_DONTWARNDELAYSLOT|MIPSM_LIKELY },
	{ "bnel",    "s,i,I",   &generateMipsMacroBranch,          MIPSM_NE|MIPSM_IMM|MIPSM_DONTWARNDELAYSLOT|MIPSM_LIKELY },
	{ "beql",    "s,i,I",   &generateMipsMacroBranch,          MIPSM_EQ|MIPSM_IMM|MIPSM_DONTWARNDELAYSLOT|MIPSM_LIKELY },

	{ "slt",     "d,s,I",   &generateMipsMacroSet,             MIPSM_LT|MIPSM_IMM },
	{ "sltu",    "d,s,I",   &generateMipsMacroSet,             MIPSM_LTU|MIPSM_IMM },
	{ "sgt",     "d,s,I",   &generateMipsMacroSet,             MIPSM_LT|MIPSM_IMM|MIPSM_REVCMP },
	{ "sgtu",    "d,s,I",   &generateMipsMacroSet,             MIPSM_LTU|MIPSM_IMM|MIPSM_REVCMP },
	{ "sge",     "d,s,t",   &generateMipsMacroSet,             MIPSM_GE },
	{ "sge",     "d,s,I",   &generateMipsMacroSet,             MIPSM_GE|MIPSM_IMM },
	{ "sle",     "d,s,t",   &generateMipsMacroSet,             MIPSM_GE|MIPSM_REVCMP },
	{ "sle",     "d,s,I",   &generateMipsMacroSet,             MIPSM_GE|MIPSM_IMM|MIPSM_REVCMP },
	{ "sgeu",    "d,s,t",   &generateMipsMacroSet,             MIPSM_GEU },
	{ "sgeu",    "d,s,I",   &generateMipsMacroSet,             MIPSM_GEU|MIPSM_IMM },
	{ "sleu",    "d,s,t",   &generateMipsMacroSet,             MIPSM_GEU|MIPSM_REVCMP },
	{ "sleu",    "d,s,I",   &generateMipsMacroSet,             MIPSM_GEU|MIPSM_IMM|MIPSM_REVCMP },
	{ "sne",     "d,s,t",   &generateMipsMacroSet,             MIPSM_NE },
	{ "sne",     "d,s,I",   &generateMipsMacroSet,             MIPSM_NE|MIPSM_IMM },
	{ "seq",     "d,s,t",   &generateMipsMacroSet,             MIPSM_EQ },
	{ "seq",     "d,s,I",   &generateMipsMacroSet,             MIPSM_EQ|MIPSM_IMM },

	{ "rol",     "d,s,t",   &generateMipsMacroRotate,          MIPSM_LEFT },
	{ "rol",     "d,s,i",   &generateMipsMacroRotate,          MIPSM_LEFT|MIPSM_IMM },
	{ "ror",     "d,s,t",   &generateMipsMacroRotate,          MIPSM_RIGHT },
	{ "ror",     "d,s,i",   &generateMipsMacroRotate,          MIPSM_RIGHT|MIPSM_IMM },

	{ nullptr,   nullptr,   nullptr,                           0 }
};

#pragma region "File: Archs/MIPS/MipsOpcodes.cpp"

const tMipsOpcode MipsOpcodes[] = {
//     31---------26---------------------------------------------------0
//     |  opcode   |                                                   |
//     ------6----------------------------------------------------------
//     |--000--|--001--|--010--|--011--|--100--|--101--|--110--|--111--| lo
// 000 | *1    | *2    | J     | JAL   | BEQ   | BNE   | BLEZ  | BGTZ  | 00..07
// 001 | ADDI  | ADDIU | SLTI  | SLTIU | ANDI  | ORI   | XORI  | LUI   | 08..0F
// 010 | *3    | *4    | *5    | ---   | BEQL  | BNEL  | BLEZL | BGTZL | 10..17
// 011 | DADDI | DADDIU| LDL   | LDR   | ---   | ---   | LQ    | SQ    | 18..1F
// 100 | LB    | LH    | LWL   | LW    | LBU   | LHU   | LWR   | LWU   | 20..27
// 101 | SB    | SH    | SWL   | SW    | SDL   | SDR   | SWR   | CACHE | 28..2F
// 110 | LL    | LWC1  | LV.S  | ---   | LLD   | ULV.Q | LV.Q  | LD    | 30..37
// 111 | SC    | SWC1  | SV.S  | ---   | SCD   | USV.Q | SV.Q  | SD    | 38..3F
//  hi |-------|-------|-------|-------|-------|-------|-------|-------|
//		*1 = SPECIAL	*2 = REGIMM		*3 = COP0		*4 = COP1		*5 = COP2
	{ "j",		"i26",				MIPS_OP(0x02), 			MA_MIPS1,	MO_IPCA|MO_DELAY|MO_NODELAYSLOT },
	{ "jal",	"i26",				MIPS_OP(0x03),			MA_MIPS1,	MO_IPCA|MO_DELAY|MO_NODELAYSLOT },
	{ "beq",	"s,t,i16",			MIPS_OP(0x04),			MA_MIPS1,	MO_IPCR|MO_DELAY|MO_NODELAYSLOT },
	{ "beqz",	"s,i16",			MIPS_OP(0x04),			MA_MIPS1,	MO_IPCR|MO_DELAY|MO_NODELAYSLOT },
	{ "b",		"i16",				MIPS_OP(0x04), 			MA_MIPS1,	MO_IPCR|MO_DELAY|MO_NODELAYSLOT },
	{ "bne",	"s,t,i16",			MIPS_OP(0x05),			MA_MIPS1,	MO_IPCR|MO_DELAY|MO_NODELAYSLOT },
	{ "bnez",	"s,i16",			MIPS_OP(0x05),			MA_MIPS1,	MO_IPCR|MO_DELAY|MO_NODELAYSLOT },
	{ "blez",	"s,i16",			MIPS_OP(0x06),			MA_MIPS1,	MO_IPCR|MO_DELAY|MO_NODELAYSLOT },
	{ "bgtz",	"s,i16",			MIPS_OP(0x07),			MA_MIPS1,	MO_IPCR|MO_DELAY|MO_NODELAYSLOT },
	{ "addi",	"t,s,i16",			MIPS_OP(0x08),			MA_MIPS1,	MO_IGNORERTD },
	{ "addi",	"s,i16",			MIPS_OP(0x08),			MA_MIPS1,	MO_RST },
	{ "subi",	"t,s,i16",			MIPS_OP(0x08),			MA_MIPS1,	MO_IGNORERTD|MO_NEGIMM },
	{ "subi",	"s,i16",			MIPS_OP(0x08),			MA_MIPS1,	MO_RST|MO_NEGIMM },
	{ "addiu",	"t,s,i16",			MIPS_OP(0x09),			MA_MIPS1,	MO_IGNORERTD },
	{ "addiu",	"s,i16",			MIPS_OP(0x09),			MA_MIPS1,	MO_RST },
	{ "subiu",	"t,s,i16",			MIPS_OP(0x09),			MA_MIPS1,	MO_IGNORERTD|MO_NEGIMM },
	{ "subiu",	"s,i16",			MIPS_OP(0x09),			MA_MIPS1,	MO_RST|MO_NEGIMM },
	{ "slti",	"t,s,i16",			MIPS_OP(0x0A),			MA_MIPS1,	MO_IGNORERTD },
	{ "slti",	"s,i16",			MIPS_OP(0x0A),			MA_MIPS1,	MO_RST },
	{ "sltiu",	"t,s,i16",			MIPS_OP(0x0B),			MA_MIPS1,	MO_IGNORERTD },
	{ "sltiu",	"s,i16",			MIPS_OP(0x0B),			MA_MIPS1,	MO_RST },
	{ "andi",	"t,s,i16",			MIPS_OP(0x0C),			MA_MIPS1,	MO_IGNORERTD },
	{ "andi",	"s,i16",			MIPS_OP(0x0C),			MA_MIPS1,	MO_RST },
	{ "ori",	"t,s,i16",			MIPS_OP(0x0D),			MA_MIPS1,	MO_IGNORERTD },
	{ "ori",	"s,i16",			MIPS_OP(0x0D),			MA_MIPS1,	MO_RST },
	{ "xori",	"t,s,i16",			MIPS_OP(0x0E),			MA_MIPS1,	MO_IGNORERTD },
	{ "xori",	"s,i16",			MIPS_OP(0x0E),			MA_MIPS1,	MO_RST },
	{ "lui",	"t,i16",			MIPS_OP(0x0F),			MA_MIPS1,	MO_IGNORERTD },
	{ "cop2",	"i25",				MIPS_OP(0x12)|(1<<25), 	MA_PSX,		0 },
	{ "beql",	"s,t,i16",			MIPS_OP(0x14),			MA_MIPS2,	MO_IPCR|MO_DELAY|MO_NODELAYSLOT },
	{ "beqzl",	"s,i16",			MIPS_OP(0x14),			MA_MIPS2,	MO_IPCR|MO_DELAY|MO_NODELAYSLOT },
	{ "bnel",	"s,t,i16",			MIPS_OP(0x15),			MA_MIPS2,	MO_IPCR|MO_DELAY|MO_NODELAYSLOT },
	{ "bnezl",	"s,i16",			MIPS_OP(0x15),			MA_MIPS2,	MO_IPCR|MO_DELAY|MO_NODELAYSLOT },
	{ "blezl",	"s,i16",			MIPS_OP(0x16),			MA_MIPS2,	MO_IPCR|MO_DELAY|MO_NODELAYSLOT },
	{ "bgtzl",	"s,i16",			MIPS_OP(0x17),			MA_MIPS2,	MO_IPCR|MO_DELAY|MO_NODELAYSLOT },
	{ "daddi",	"t,s,i16",			MIPS_OP(0x18),			MA_MIPS3,	MO_64BIT },
	{ "daddi",	"s,i16",			MIPS_OP(0x18),			MA_MIPS3,	MO_64BIT|MO_RST },
	{ "dsubi",	"t,s,i16",			MIPS_OP(0x18),			MA_MIPS3,	MO_64BIT|MO_NEGIMM },
	{ "dsubi",	"s,i16",			MIPS_OP(0x18),			MA_MIPS3,	MO_64BIT|MO_RST|MO_NEGIMM },
	{ "daddiu",	"t,s,i16",			MIPS_OP(0x19),			MA_MIPS3,	MO_64BIT },
	{ "daddiu",	"s,i16",			MIPS_OP(0x19),			MA_MIPS3,	MO_64BIT|MO_RST },
	{ "dsubiu",	"t,s,i16",			MIPS_OP(0x19),			MA_MIPS3,	MO_64BIT|MO_NEGIMM },
	{ "dsubiu",	"s,i16",			MIPS_OP(0x19),			MA_MIPS3,	MO_64BIT|MO_RST|MO_NEGIMM },
	{ "ldl",	"t,i16(s)",			MIPS_OP(0x1A),			MA_MIPS3,	MO_64BIT|MO_DELAYRT|MO_IGNORERTD },
	{ "ldl",	"t,(s)",			MIPS_OP(0x1A),			MA_MIPS3,	MO_64BIT|MO_DELAYRT|MO_IGNORERTD },
	{ "ldr",	"t,i16(s)",			MIPS_OP(0x1B),			MA_MIPS3,	MO_64BIT|MO_DELAYRT|MO_IGNORERTD },
	{ "ldr",	"t,(s)",			MIPS_OP(0x1B),			MA_MIPS3,	MO_64BIT|MO_DELAYRT|MO_IGNORERTD },
	{ "lq",		"t,i16(s)",			MIPS_OP(0x1E),			MA_PS2,		MO_DELAYRT|MO_IGNORERTD },
	{ "lq",		"t,(s)",			MIPS_OP(0x1E),			MA_PS2,		MO_DELAYRT|MO_IGNORERTD },
	{ "sq",		"t,i16(s)",			MIPS_OP(0x1F),			MA_PS2,		MO_DELAYRT|MO_IGNORERTD },
	{ "sq",		"t,(s)",			MIPS_OP(0x1F),			MA_PS2,		MO_DELAYRT|MO_IGNORERTD },
	{ "lb",		"t,i16(s)",			MIPS_OP(0x20),			MA_MIPS1,	MO_DELAYRT|MO_IGNORERTD },
	{ "lb",		"t,(s)",			MIPS_OP(0x20),			MA_MIPS1,	MO_DELAYRT|MO_IGNORERTD },
	{ "lh",		"t,i16(s)",			MIPS_OP(0x21),			MA_MIPS1,	MO_DELAYRT|MO_IGNORERTD },
	{ "lh",		"t,(s)",			MIPS_OP(0x21),			MA_MIPS1,	MO_DELAYRT|MO_IGNORERTD },
	{ "lwl",	"t,i16(s)",			MIPS_OP(0x22),			MA_MIPS1|MA_EXRSP,	MO_DELAYRT|MO_IGNORERTD },
	{ "lwl",	"t,(s)",			MIPS_OP(0x22),			MA_MIPS1|MA_EXRSP,	MO_DELAYRT|MO_IGNORERTD },
	{ "lw",		"t,i16(s)",			MIPS_OP(0x23),			MA_MIPS1,	MO_DELAYRT|MO_IGNORERTD },
	{ "lw",		"t,(s)",			MIPS_OP(0x23),			MA_MIPS1,	MO_DELAYRT|MO_IGNORERTD },
	{ "lbu",	"t,i16(s)",			MIPS_OP(0x24),			MA_MIPS1,	MO_DELAYRT|MO_IGNORERTD },
	{ "lbu",	"t,(s)",			MIPS_OP(0x24),			MA_MIPS1,	MO_DELAYRT|MO_IGNORERTD },
	{ "lhu",	"t,i16(s)",			MIPS_OP(0x25),			MA_MIPS1,	MO_DELAYRT|MO_IGNORERTD },
	{ "lhu",	"t,(s)",			MIPS_OP(0x25),			MA_MIPS1,	MO_DELAYRT|MO_IGNORERTD },
	{ "lwr",	"t,i16(s)",			MIPS_OP(0x26),			MA_MIPS1|MA_EXRSP,	MO_DELAYRT|MO_IGNORERTD },
	{ "lwr",	"t,(s)",			MIPS_OP(0x26),			MA_MIPS1|MA_EXRSP,	MO_DELAYRT|MO_IGNORERTD },
	{ "lwu",	"t,i16(s)",			MIPS_OP(0x27),			MA_MIPS3,	MO_64BIT|MO_DELAYRT },
	{ "lwu",	"t,(s)",			MIPS_OP(0x27),			MA_MIPS3,	MO_64BIT|MO_DELAYRT },
	{ "sb",		"t,i16(s)",			MIPS_OP(0x28),			MA_MIPS1,	0 },
	{ "sb",		"t,(s)",			MIPS_OP(0x28),			MA_MIPS1,	0 },
	{ "sh",		"t,i16(s)",			MIPS_OP(0x29),			MA_MIPS1,	0 },
	{ "sh",		"t,(s)",			MIPS_OP(0x29),			MA_MIPS1,	0 },
	{ "swl",	"t,i16(s)",			MIPS_OP(0x2A),			MA_MIPS1|MA_EXRSP,	0 },
	{ "swl",	"t,(s)",			MIPS_OP(0x2A),			MA_MIPS1|MA_EXRSP,	0 },
	{ "sw",		"t,i16(s)",			MIPS_OP(0x2B),			MA_MIPS1,	0 },
	{ "sw",		"t,(s)",			MIPS_OP(0x2B),			MA_MIPS1,	0 },
	{ "sdl",	"t,i16(s)",			MIPS_OP(0x2C),			MA_MIPS3,	MO_64BIT },
	{ "sdl",	"t,(s)",			MIPS_OP(0x2C),			MA_MIPS3,	MO_64BIT },
	{ "sdr",	"t,i16(s)",			MIPS_OP(0x2D),			MA_MIPS3,	MO_64BIT|MO_IGNORERTD },
	{ "sdr",	"t,(s)",			MIPS_OP(0x2D),			MA_MIPS3,	MO_64BIT|MO_IGNORERTD },
	{ "swr",	"t,i16(s)",			MIPS_OP(0x2E),			MA_MIPS1|MA_EXRSP,	0 },
	{ "swr",	"t,(s)",			MIPS_OP(0x2E),			MA_MIPS1|MA_EXRSP,	0 },
	{ "cache",	"jc,i16(s)",		MIPS_OP(0x2F),			MA_MIPS2,	0 },
	{ "cache",	"jc,(s)",			MIPS_OP(0x2F),			MA_MIPS2,	0 },
	{ "ll",		"t,i16(s)",			MIPS_OP(0x30),			MA_MIPS2,	MO_DELAYRT|MO_IGNORERTD },
	{ "ll",		"t,(s)",			MIPS_OP(0x30),			MA_MIPS2,	MO_DELAYRT|MO_IGNORERTD },
	{ "lwc1",	"T,i16(s)",			MIPS_OP(0x31),			MA_MIPS1,	MO_FPU },
	{ "lwc1",	"T,(s)",			MIPS_OP(0x31),			MA_MIPS1,	MO_FPU },
	{ "l.s",	"T,i16(s)",			MIPS_OP(0x31),			MA_MIPS1,	MO_FPU },
	{ "l.s",	"T,(s)",			MIPS_OP(0x31),			MA_MIPS1,	MO_FPU },
	{ "lwc2",	"gt,i16(s)",		MIPS_OP(0x32),			MA_PSX,		0 },
	{ "lwc2",	"gt,(s)",			MIPS_OP(0x32),			MA_PSX,		0 },
	{ "lv.s",	"vt,i16(s)",		MIPS_OP(0x32),			MA_PSP,		MO_VFPU_SINGLE|MO_VFPU_MIXED|MO_IMMALIGNED },
	{ "lv.s",	"vt,(s)",			MIPS_OP(0x32),			MA_PSP,		MO_VFPU_SINGLE|MO_VFPU_MIXED },
	{ "lld",	"t,i16(s)",			MIPS_OP(0x34),			MA_MIPS3,	MO_64BIT|MO_DELAYRT },
	{ "lld",	"t,(s)",			MIPS_OP(0x34),			MA_MIPS3,	MO_64BIT|MO_DELAYRT },
	{ "ldc1",	"T,i16(s)",			MIPS_OP(0x35),			MA_MIPS2,	MO_DFPU },
	{ "ldc1",	"T,(s)",			MIPS_OP(0x35),			MA_MIPS2,	MO_DFPU },
	{ "l.d",	"T,i16(s)",			MIPS_OP(0x35),			MA_MIPS2,	MO_DFPU },
	{ "l.d",	"T,(s)",			MIPS_OP(0x35),			MA_MIPS2,	MO_DFPU },
	{ "ulv.q",	"vt,i16(s)",		MIPS_OP(0x35),			MA_PSP,		MO_VFPU_QUAD|MO_VFPU_MIXED|MO_IMMALIGNED },
	{ "ulv.q",	"vt,(s)",			MIPS_OP(0x35),			MA_PSP,		MO_VFPU_QUAD|MO_VFPU_MIXED },
	{ "lvl.q",	"vt,i16(s)",		MIPS_OP(0x35),			MA_PSP,		MO_VFPU_QUAD|MO_VFPU_MIXED|MO_VFPU_6BIT|MO_IMMALIGNED },
	{ "lvl.q",	"vt,(s)",			MIPS_OP(0x35),			MA_PSP,		MO_VFPU_QUAD|MO_VFPU_MIXED|MO_VFPU_6BIT },
	{ "lvr.q",	"vt,i16(s)",		MIPS_OP(0x35)|0x02,		MA_PSP,		MO_VFPU_QUAD|MO_VFPU_MIXED|MO_VFPU_6BIT|MO_IMMALIGNED },
	{ "lvr.q",	"vt,(s)",			MIPS_OP(0x35)|0x02,		MA_PSP,		MO_VFPU_QUAD|MO_VFPU_MIXED|MO_VFPU_6BIT },
	{ "lv.q",	"vt,i16(s)",		MIPS_OP(0x36),			MA_PSP,		MO_VFPU_QUAD|MO_VFPU_MIXED|MO_VFPU_6BIT|MO_IMMALIGNED },
	{ "lv.q",	"vt,(s)",			MIPS_OP(0x36),			MA_PSP,		MO_VFPU_QUAD|MO_VFPU_MIXED|MO_VFPU_6BIT },
	{ "lqc2",	"Vt,i16(s)",		MIPS_OP(0x36),			MA_PS2,		MO_DELAYRT },
	{ "ld",		"t,i16(s)",			MIPS_OP(0x37),			MA_MIPS3,	MO_64BIT|MO_DELAYRT },
	{ "ld",		"t,(s)",			MIPS_OP(0x37),			MA_MIPS3,	MO_64BIT|MO_DELAYRT },
	{ "sc",		"t,i16(s)",			MIPS_OP(0x38),			MA_MIPS2,	0 },
	{ "sc",		"t,(s)",			MIPS_OP(0x38),			MA_MIPS2,	0 },
	{ "swc1",	"T,i16(s)",			MIPS_OP(0x39),			MA_MIPS1,	MO_FPU },
	{ "swc1",	"T,(s)",			MIPS_OP(0x39),			MA_MIPS1,	MO_FPU },
	{ "s.s",	"T,i16(s)",			MIPS_OP(0x39),			MA_MIPS1,	MO_FPU },
	{ "s.s",	"T,(s)",			MIPS_OP(0x39),			MA_MIPS1,	MO_FPU },
	{ "swc2",	"gt,i16(s)",		MIPS_OP(0x3A),			MA_PSX,		0 },
	{ "swc2",	"gt,(s)",			MIPS_OP(0x3A),			MA_PSX,		0 },
	{ "sv.s",	"vt,i16(s)",		MIPS_OP(0x3A),			MA_PSP,		MO_VFPU_SINGLE|MO_VFPU_MIXED|MO_IMMALIGNED },
	{ "sv.s",	"vt,(s)",			MIPS_OP(0x3A),			MA_PSP,		MO_VFPU_SINGLE|MO_VFPU_MIXED },
	{ "scd",	"t,i16(s)",			MIPS_OP(0x3C),			MA_MIPS3,	MO_64BIT|MO_DELAYRT },
	{ "scd",	"t,(s)",			MIPS_OP(0x3C),			MA_MIPS3,	MO_64BIT|MO_DELAYRT },
	{ "sdc1",	"T,i16(s)",			MIPS_OP(0x3D),			MA_MIPS2,	MO_DFPU },
	{ "sdc1",	"T,(s)",			MIPS_OP(0x3D),			MA_MIPS2,	MO_DFPU },
	{ "s.d",	"T,i16(s)",			MIPS_OP(0x3D),			MA_MIPS2,	MO_DFPU },
	{ "s.d",	"T,(s)",			MIPS_OP(0x3D),			MA_MIPS2,	MO_DFPU },
	{ "usv.q",	"vt,i16(s)",		MIPS_OP(0x3D),			MA_PSP,		MO_VFPU_QUAD|MO_VFPU_MIXED|MO_IMMALIGNED },
	{ "usv.q",	"vt,(s)",			MIPS_OP(0x3D),			MA_PSP,		MO_VFPU_QUAD|MO_VFPU_MIXED },
	{ "svl.q",	"vt,i16(s)",		MIPS_OP(0x3D),			MA_PSP,		MO_VFPU_QUAD|MO_VFPU_MIXED|MO_VFPU_6BIT|MO_IMMALIGNED },
	{ "svl.q",	"vt,(s)",			MIPS_OP(0x3D),			MA_PSP,		MO_VFPU_QUAD|MO_VFPU_MIXED|MO_VFPU_6BIT },
	{ "svr.q",	"vt,i16(s)",		MIPS_OP(0x3D)|0x02,		MA_PSP,		MO_VFPU_QUAD|MO_VFPU_MIXED|MO_VFPU_6BIT|MO_IMMALIGNED },
	{ "svr.q",	"vt,(s)",			MIPS_OP(0x3D)|0x02,		MA_PSP,		MO_VFPU_QUAD|MO_VFPU_MIXED|MO_VFPU_6BIT },
	{ "sv.q",	"vt,i16(s),w",		MIPS_OP(0x3E)|0x02,		MA_PSP,		MO_VFPU_QUAD|MO_VFPU_MIXED|MO_VFPU_6BIT|MO_IMMALIGNED },
	{ "sv.q",	"vt,(s),w",			MIPS_OP(0x3E)|0x02,		MA_PSP,		MO_VFPU_QUAD|MO_VFPU_MIXED|MO_VFPU_6BIT },
	{ "sv.q",	"vt,i16(s)",		MIPS_OP(0x3E),			MA_PSP,		MO_VFPU_QUAD|MO_VFPU_MIXED|MO_VFPU_6BIT|MO_IMMALIGNED },
	{ "sv.q",	"vt,(s)",			MIPS_OP(0x3E),			MA_PSP,		MO_VFPU_QUAD|MO_VFPU_MIXED|MO_VFPU_6BIT },
	{ "sqc2",	"Vt,i16(s)",		MIPS_OP(0x3E),			MA_PS2,		MO_DELAYRT },
	{ "sd",		"t,i16(s)",			MIPS_OP(0x3F),			MA_MIPS3,	MO_64BIT|MO_DELAYRT },
	{ "sd",		"t,(s)",			MIPS_OP(0x3F),			MA_MIPS3,	MO_64BIT|MO_DELAYRT },

//     31---------26------------------------------------------5--------0
//     |=   SPECIAL|                                         | function|
//     ------6----------------------------------------------------6-----
//     |--000--|--001--|--010--|--011--|--100--|--101--|--110--|--111--| lo
// 000 | SLL   | ---   | SRL*1 | SRA   | SLLV  |  ---  | SRLV*2| SRAV  | 00..07
// 001 | JR    | JALR  | MOVZ  | MOVN  |SYSCALL| BREAK |  ---  | SYNC  | 08..0F
// 010 | MFHI  | MTHI  | MFLO  | MTLO  | DSLLV |  ---  |   *3  |  *4   | 10..17
// 011 | MULT  | MULTU | DIV   | DIVU  | MADD  | MADDU | ----  | ----- | 18..1F
// 100 | ADD   | ADDU  | SUB   | SUBU  | AND   | OR    | XOR   | NOR   | 20..27
// 101 | mfsa  | mtsa  | SLT   | SLTU  |  *5   |  *6   |  *7   |  *8   | 28..2F
// 110 | TGE   | TGEU  | TLT   | TLTU  | TEQ   |  ---  | TNE   |  ---  | 30..37
// 111 | dsll  |  ---  | dsrl  | dsra  |dsll32 |  ---  |dsrl32 |dsra32 | 38..3F
//  hi |-------|-------|-------|-------|-------|-------|-------|-------|
// *1:	rotr when rs = 1 (PSP only)		*2:	rotrv when sa = 1 (PSP only)
// *3:	dsrlv on PS2, clz on PSP		*4:	dsrav on PS2, clo on PSP
// *5:	dadd on PS2, max on PSP			*6:	daddu on PS2, min on PSP
// *7:	dsub on PS2, msub on PSP		*8:	dsubu on PS2, msubu on PSP
	{ "sll",	"d,t,i5",	MIPS_SPECIAL(0x00),				MA_MIPS1,	0 },
	{ "sll",	"d,i5",		MIPS_SPECIAL(0x00),				MA_MIPS1,	MO_RDT },
	{ "nop",	"",			MIPS_SPECIAL(0x00),				MA_MIPS1,	0 },
	{ "srl",	"d,t,i5",	MIPS_SPECIAL(0x02),				MA_MIPS1,	0 },
	{ "srl",	"d,i5",		MIPS_SPECIAL(0x02),				MA_MIPS1,	MO_RDT },
	{ "rotr",	"d,t,i5",	MIPS_SPECIAL(0x02)|MIPS_RS(1),	MA_PSP,		0 },
	{ "rotr",	"d,i5",		MIPS_SPECIAL(0x02)|MIPS_RS(1),	MA_PSP,		MO_RDT },
	{ "sra",	"d,t,i5",	MIPS_SPECIAL(0x03),				MA_MIPS1,	0 },
	{ "sra",	"d,i5",		MIPS_SPECIAL(0x03),				MA_MIPS1,	MO_RDT },
	{ "sllv",	"d,t,s",	MIPS_SPECIAL(0x04),				MA_MIPS1,	0 },
	{ "sllv",	"d,s",		MIPS_SPECIAL(0x04),				MA_MIPS1,	MO_RDT },
	{ "srlv",	"d,t,s",	MIPS_SPECIAL(0x06),				MA_MIPS1,	0 },
	{ "srlv",	"d,s",		MIPS_SPECIAL(0x06),				MA_MIPS1,	MO_RDT },
	{ "rotrv",	"d,t,s",	MIPS_SPECIAL(0x06)|MIPS_SA(1),	MA_PSP,		0 },
	{ "rotrv",	"d,s",		MIPS_SPECIAL(0x06)|MIPS_SA(1),	MA_PSP,		MO_RDT },
	{ "srav",	"d,t,s",	MIPS_SPECIAL(0x07),				MA_MIPS1,	0 },
	{ "srav",	"d,s",		MIPS_SPECIAL(0x07),				MA_MIPS1,	MO_RDT },
	{ "jr",		"s",		MIPS_SPECIAL(0x08),				MA_MIPS1,	MO_DELAY|MO_NODELAYSLOT },
	{ "jalr",	"s,d",		MIPS_SPECIAL(0x09),				MA_MIPS1,	MO_DELAY|MO_NODELAYSLOT },
	{ "jalr",	"s",		MIPS_SPECIAL(0x09)|MIPS_RD(31),	MA_MIPS1,	MO_DELAY|MO_NODELAYSLOT },
	{ "movz",	"d,s,t",	MIPS_SPECIAL(0x0A),				MA_MIPS4|MA_PS2|MA_PSP,	0 },
	{ "movn",	"d,s,t",	MIPS_SPECIAL(0x0B),				MA_MIPS4|MA_PS2|MA_PSP,	0 },
	{ "syscall","i20",		MIPS_SPECIAL(0x0C),				MA_MIPS1|MA_EXRSP,	MO_NODELAYSLOT },
	{ "syscall","",			MIPS_SPECIAL(0x0C),				MA_MIPS1|MA_EXRSP,	MO_NODELAYSLOT },
	{ "break",	"i20",		MIPS_SPECIAL(0x0D),				MA_MIPS1,	MO_NODELAYSLOT },
	{ "break",	"",			MIPS_SPECIAL(0x0D),				MA_MIPS1,	MO_NODELAYSLOT },
	{ "sync",	"",			MIPS_SPECIAL(0x0F),				MA_MIPS2,	0 },
	{ "mfhi",	"d",		MIPS_SPECIAL(0x10),				MA_MIPS1|MA_EXRSP,	0 },
	{ "mthi",	"s",		MIPS_SPECIAL(0x11),				MA_MIPS1|MA_EXRSP,	0 },
	{ "mflo",	"d",		MIPS_SPECIAL(0x12),				MA_MIPS1|MA_EXRSP,	0 },
	{ "mtlo",	"s",		MIPS_SPECIAL(0x13),				MA_MIPS1|MA_EXRSP,	0 },
	{ "dsllv",	"d,t,s",	MIPS_SPECIAL(0x14),				MA_MIPS3,	MO_64BIT },
	{ "dsllv",	"d,s",		MIPS_SPECIAL(0x14),				MA_MIPS3,	MO_64BIT|MO_RDT },
	{ "dsrlv",	"d,t,s",	MIPS_SPECIAL(0x16),				MA_MIPS3,	MO_64BIT },
	{ "dsrlv",	"d,s",		MIPS_SPECIAL(0x16),				MA_MIPS3,	MO_64BIT|MO_RDT },
	{ "clz",	"d,s",		MIPS_SPECIAL(0x16),				MA_PSP,		0 },
	{ "dsrav",	"d,t,s",	MIPS_SPECIAL(0x17),				MA_MIPS3,	MO_64BIT },
	{ "dsrav",	"d,s",		MIPS_SPECIAL(0x17),				MA_MIPS3,	MO_64BIT|MO_RDT },
	{ "clo",	"d,s",		MIPS_SPECIAL(0x17),				MA_PSP,		0 },
	{ "mult",	"d,s,t",	MIPS_SPECIAL(0x18),				MA_PS2,		0 },
	{ "multu",	"d,s,t",	MIPS_SPECIAL(0x19),				MA_PS2,		0 },
	{ "mult",	"s,t",		MIPS_SPECIAL(0x18),				MA_MIPS1|MA_EXRSP,	0 },
	{ "mult",	"r\x0,s,t",	MIPS_SPECIAL(0x18),				MA_MIPS1|MA_EXRSP,	0 },
	{ "multu",	"s,t",		MIPS_SPECIAL(0x19),				MA_MIPS1|MA_EXRSP,	0 },
	{ "multu",	"r\x0,s,t",	MIPS_SPECIAL(0x19),				MA_MIPS1|MA_EXRSP,	0 },
	{ "div",	"s,t",		MIPS_SPECIAL(0x1A),				MA_MIPS1|MA_EXRSP,	0 },
	{ "div",	"r\x0,s,t",	MIPS_SPECIAL(0x1A),				MA_MIPS1|MA_EXRSP,	0 },
	{ "divu",	"s,t",		MIPS_SPECIAL(0x1B),				MA_MIPS1|MA_EXRSP,	0 },
	{ "divu",	"r\x0,s,t",	MIPS_SPECIAL(0x1B),				MA_MIPS1|MA_EXRSP,	0 },
	{ "dmult",	"s,t",		MIPS_SPECIAL(0x1C),				MA_MIPS3|MA_EXPS2,	MO_64BIT },
	{ "dmult",	"r\x0,s,t",	MIPS_SPECIAL(0x1C),				MA_MIPS3|MA_EXPS2,	MO_64BIT },
	{ "madd",	"s,t",		MIPS_SPECIAL(0x1C),				MA_PSP,		0 },
	{ "dmultu",	"s,t",		MIPS_SPECIAL(0x1D),				MA_MIPS3|MA_EXPS2,	MO_64BIT },
	{ "dmultu",	"r\x0,s,t",	MIPS_SPECIAL(0x1D),				MA_MIPS3|MA_EXPS2,	MO_64BIT },
	{ "maddu",	"s,t",		MIPS_SPECIAL(0x1D),				MA_PSP,		0 },
	{ "ddiv",	"s,t",		MIPS_SPECIAL(0x1E),				MA_MIPS3|MA_EXPS2,	MO_64BIT },
	{ "ddiv",	"r\x0,s,t",	MIPS_SPECIAL(0x1E),				MA_MIPS3|MA_EXPS2,	MO_64BIT },
	{ "ddivu",	"s,t",		MIPS_SPECIAL(0x1F),				MA_MIPS3|MA_EXPS2,	MO_64BIT },
	{ "ddivu",	"r\x0,s,t",	MIPS_SPECIAL(0x1F),				MA_MIPS3|MA_EXPS2,	MO_64BIT },
	{ "add",	"d,s,t",	MIPS_SPECIAL(0x20),				MA_MIPS1,	0 },
	{ "add",	"s,t",		MIPS_SPECIAL(0x20),				MA_MIPS1,	MO_RSD },
	{ "addu",	"d,s,t",	MIPS_SPECIAL(0x21),				MA_MIPS1,	0 },
	{ "addu",	"s,t",		MIPS_SPECIAL(0x21),				MA_MIPS1,	MO_RSD },
	{ "move",	"d,s",		MIPS_SPECIAL(0x21),				MA_MIPS1,	0 },
	{ "clear",	"d",		MIPS_SPECIAL(0x21),				MA_MIPS1,	0 },
	{ "sub",	"d,s,t",	MIPS_SPECIAL(0x22),				MA_MIPS1,	0 },
	{ "sub",	"s,t",		MIPS_SPECIAL(0x22),				MA_MIPS1,	MO_RSD },
	{ "neg",	"d,t",		MIPS_SPECIAL(0x22),				MA_MIPS1,	0 },
	{ "subu",	"d,s,t",	MIPS_SPECIAL(0x23),				MA_MIPS1,	0 },
	{ "subu",	"s,t",		MIPS_SPECIAL(0x23),				MA_MIPS1,	MO_RSD },
	{ "negu",	"d,t",		MIPS_SPECIAL(0x23),				MA_MIPS1,	0 },
	{ "and",	"d,s,t",	MIPS_SPECIAL(0x24),				MA_MIPS1,	0 },
	{ "and",	"s,t",		MIPS_SPECIAL(0x24),				MA_MIPS1,	MO_RSD },
	{ "or",		"d,s,t",	MIPS_SPECIAL(0x25),				MA_MIPS1,	0 },
	{ "or",		"s,t",		MIPS_SPECIAL(0x25),				MA_MIPS1,	MO_RSD },
	{ "xor",	"d,s,t",	MIPS_SPECIAL(0x26), 			MA_MIPS1,	0 },
	{ "eor",	"d,s,t",	MIPS_SPECIAL(0x26),				MA_MIPS1,	0 },
	{ "xor",	"s,t",		MIPS_SPECIAL(0x26), 			MA_MIPS1,	MO_RSD },
	{ "eor",	"s,t",		MIPS_SPECIAL(0x26), 			MA_MIPS1,	MO_RSD },
	{ "nor",	"d,s,t",	MIPS_SPECIAL(0x27),				MA_MIPS1,	0 },
	{ "nor",	"s,t",		MIPS_SPECIAL(0x27),				MA_MIPS1,	MO_RSD },
	{ "not",	"d,s",		MIPS_SPECIAL(0x27),				MA_MIPS1,	0 },
	{ "mfsa",	"d",		MIPS_SPECIAL(0x28),				MA_PS2,		0 },
	{ "mtsa",	"s",		MIPS_SPECIAL(0x29),				MA_PS2,		0 },
	{ "slt",	"d,s,t",	MIPS_SPECIAL(0x2A),				MA_MIPS1,	0 },
	{ "slt",	"s,t",		MIPS_SPECIAL(0x2A),				MA_MIPS1,	MO_RSD},
	{ "sgt",	"d,t,s",	MIPS_SPECIAL(0x2A),				MA_MIPS1,	0 },
	{ "sgt",	"d,s",		MIPS_SPECIAL(0x2A),				MA_MIPS1,	MO_RDT},
	{ "sltu",	"d,s,t",	MIPS_SPECIAL(0x2B),				MA_MIPS1,	0 },
	{ "sltu",	"s,t",		MIPS_SPECIAL(0x2B),				MA_MIPS1,	MO_RSD },
	{ "sgtu",	"d,t,s",	MIPS_SPECIAL(0x2B),				MA_MIPS1,	0 },
	{ "sgtu",	"d,s",		MIPS_SPECIAL(0x2B),				MA_MIPS1,	MO_RDT},
	{ "dadd",	"d,s,t",	MIPS_SPECIAL(0x2C),				MA_MIPS3,	MO_64BIT },
	{ "dadd",	"s,t",		MIPS_SPECIAL(0x2C),				MA_MIPS3,	MO_64BIT|MO_RSD },
	{ "max",	"d,s,t",	MIPS_SPECIAL(0x2C),				MA_PSP,		0 },
	{ "daddu",	"d,s,t",	MIPS_SPECIAL(0x2D), 			MA_MIPS3,	MO_64BIT },
	{ "daddu",	"s,t",		MIPS_SPECIAL(0x2D), 			MA_MIPS3,	MO_64BIT|MO_RSD },
	{ "dmove",	"d,s",		MIPS_SPECIAL(0x2D), 			MA_MIPS3,	MO_64BIT },
	{ "min",	"d,s,t",	MIPS_SPECIAL(0x2D), 			MA_PSP,		0 },
	{ "dsub",	"d,s,t",	MIPS_SPECIAL(0x2E), 			MA_MIPS3,	MO_64BIT },
	{ "dsub",	"s,t",		MIPS_SPECIAL(0x2E), 			MA_MIPS3,	MO_64BIT|MO_RSD },
	{ "dneg",	"d,t",		MIPS_SPECIAL(0x2E),				MA_MIPS3,	MO_64BIT },
	{ "msub",	"s,t",		MIPS_SPECIAL(0x2E),				MA_PSP,		0 },
	{ "dsubu",	"d,s,t",	MIPS_SPECIAL(0x2F), 			MA_MIPS3,	MO_64BIT },
	{ "dsubu",	"s,t",		MIPS_SPECIAL(0x2F), 			MA_MIPS3,	MO_64BIT|MO_RSD },
	{ "dnegu",	"d,t",		MIPS_SPECIAL(0x2F),				MA_MIPS3,	MO_64BIT },
	{ "msubu",	"s,t",		MIPS_SPECIAL(0x2F),				MA_PSP,		0 },
	{ "tge",	"s,t,i10",	MIPS_SPECIAL(0x30),				MA_MIPS2,	0 },
	{ "tge",	"s,t",		MIPS_SPECIAL(0x30),				MA_MIPS2,	0 },
	{ "tgeu",	"s,t,i10",	MIPS_SPECIAL(0x31),				MA_MIPS2,	0 },
	{ "tgeu",	"s,t",		MIPS_SPECIAL(0x31),				MA_MIPS2,	0 },
	{ "tlt",	"s,t,i10",	MIPS_SPECIAL(0x32),				MA_MIPS2,	0 },
	{ "tlt",	"s,t",		MIPS_SPECIAL(0x32),				MA_MIPS2,	0 },
	{ "tltu",	"s,t,i10",	MIPS_SPECIAL(0x33),				MA_MIPS2,	0 },
	{ "tltu",	"s,t",		MIPS_SPECIAL(0x33),				MA_MIPS2,	0 },
	{ "teq",	"s,t,i10",	MIPS_SPECIAL(0x34),				MA_MIPS2,	0 },
	{ "teq",	"s,t",		MIPS_SPECIAL(0x34),				MA_MIPS2,	0 },
	{ "tne",	"s,t,i10",	MIPS_SPECIAL(0x36),				MA_MIPS2,	0 },
	{ "tne",	"s,t",		MIPS_SPECIAL(0x36),				MA_MIPS2,	0 },
	{ "dsll",	"d,t,i5",	MIPS_SPECIAL(0x38),				MA_MIPS3,	MO_64BIT },
	{ "dsll",	"d,i5",		MIPS_SPECIAL(0x38),				MA_MIPS3,	MO_64BIT|MO_RDT },
	{ "dsrl",	"d,t,i5",	MIPS_SPECIAL(0x3A),				MA_MIPS3,	MO_64BIT },
	{ "dsrl",	"d,i5",		MIPS_SPECIAL(0x3A),				MA_MIPS3,	MO_64BIT|MO_RDT },
	{ "dsra",	"d,t,i5",	MIPS_SPECIAL(0x3B),				MA_MIPS3,	MO_64BIT },
	{ "dsra",	"d,i5",		MIPS_SPECIAL(0x3B),				MA_MIPS3,	MO_64BIT|MO_RDT },
	{ "dsll32",	"d,t,i5",	MIPS_SPECIAL(0x3C),				MA_MIPS3,	MO_64BIT },
	{ "dsll32",	"d,i5",		MIPS_SPECIAL(0x3C),				MA_MIPS3,	MO_64BIT|MO_RDT },
	{ "dsrl32",	"d,t,i5",	MIPS_SPECIAL(0x3E),				MA_MIPS3,	MO_64BIT },
	{ "dsrl32",	"d,i5",		MIPS_SPECIAL(0x3E),				MA_MIPS3,	MO_64BIT|MO_RDT },
	{ "dsra32",	"d,t,i5",	MIPS_SPECIAL(0x3F),				MA_MIPS3,	MO_64BIT },
	{ "dsra32",	"d,i5",		MIPS_SPECIAL(0x3F),				MA_MIPS3,	MO_64BIT|MO_RDT },

//     REGIMM: encoded by the rt field when opcode field = REGIMM.
//     31---------26----------20-------16------------------------------0
//     |=    REGIMM|          |   rt    |                              |
//     ------6---------------------5------------------------------------
//     |--000--|--001--|--010--|--011--|--100--|--101--|--110--|--111--| lo
//  00 | BLTZ  | BGEZ  | BLTZL | BGEZL |  ---  |  ---  |  ---  |  ---  | 00-07
//  01 | tgei  | tgeiu | tlti  | tltiu | teqi  |  ---  | tnei  |  ---  | 08-0F
//  10 | BLTZAL| BGEZAL|BLTZALL|BGEZALL|  ---  |  ---  |  ---  |  ---  | 10-17
//  11 | mtsab | mtsah |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  | 18-1F
//  hi |-------|-------|-------|-------|-------|-------|-------|-------|
	{ "bltz",	"s,i16",	MIPS_REGIMM(0x00),				MA_MIPS1,	MO_IPCR|MO_DELAY|MO_NODELAYSLOT },
	{ "bgez",	"s,i16",	MIPS_REGIMM(0x01),				MA_MIPS1,	MO_IPCR|MO_DELAY|MO_NODELAYSLOT },
	{ "bltzl",	"s,i16",	MIPS_REGIMM(0x02),				MA_MIPS2,	MO_IPCR|MO_DELAY|MO_NODELAYSLOT },
	{ "bgezl",	"s,i16",	MIPS_REGIMM(0x03),				MA_MIPS2,	MO_IPCR|MO_DELAY|MO_NODELAYSLOT },
	{ "tgei",	"s,i16",	MIPS_REGIMM(0x08),				MA_MIPS2,	0 },
	{ "tgeiu",	"s,i16",	MIPS_REGIMM(0x09),				MA_MIPS2,	0 },
	{ "tlti",	"s,i16",	MIPS_REGIMM(0x0A),				MA_MIPS2,	0 },
	{ "tltiu",	"s,i16",	MIPS_REGIMM(0x0B),				MA_MIPS2,	0 },
	{ "teqi",	"s,i16",	MIPS_REGIMM(0x0C),				MA_MIPS2,	0 },
	{ "tnei",	"s,i16",	MIPS_REGIMM(0x0E),				MA_MIPS2,	0 },
	{ "bltzal",	"s,i16",	MIPS_REGIMM(0x10),				MA_MIPS1,	MO_IPCR|MO_DELAY|MO_NODELAYSLOT },
	{ "bgezal",	"s,i16",	MIPS_REGIMM(0x11),				MA_MIPS1,	MO_IPCR|MO_DELAY|MO_NODELAYSLOT },
	{ "bal",	"i16",		MIPS_REGIMM(0x11),				MA_MIPS1,	MO_IPCR|MO_DELAY|MO_NODELAYSLOT },
	{ "bltzall","s,i16",	MIPS_REGIMM(0x12),				MA_MIPS2,	MO_IPCR|MO_DELAY|MO_NODELAYSLOT },
	{ "bgezall","s,i16",	MIPS_REGIMM(0x13),				MA_MIPS2,	MO_IPCR|MO_DELAY|MO_NODELAYSLOT },
	{ "mtsab",	"s,i16",	MIPS_REGIMM(0x18),				MA_PS2,		0 },
	{ "mtsah",	"s,i16",	MIPS_REGIMM(0x19),				MA_PS2,		0 },

//     31---------26---------21----------------------------------------0
//     |=      COP0|    rs    |                                        |
//     -----6-------5---------------------------------------------------
//     |--000--|--001--|--010--|--011--|--100--|--101--|--110--|--111--| lo
//  00 |  MFC0 | DMFC0 |  ---  |  ---  |  MTC0 | DMTC0 |  ---  |  ---  | 00..07
//  01 |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  | 08..0F
//  10 |FUNCT* |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  | 10..17
//  11 |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  | 18..1F
//  hi |-------|-------|-------|-------|-------|-------|-------|-------|
	{ "mfc0",	"t,z",		MIPS_COP0(0x00),				MA_MIPS1|MA_EXRSP,	0 },
	{ "mfc0",	"t,Rz",		MIPS_COP0(0x00),				MA_RSP,		0 },
	{ "dmfc0",	"t,z",		MIPS_COP0(0x01),				MA_MIPS3,	MO_64BIT },
	{ "mtc0",	"t,z",		MIPS_COP0(0x04),				MA_MIPS1|MA_EXRSP,	0 },
	{ "mtc0",	"t,Rz",		MIPS_COP0(0x04),				MA_RSP,		0 },
	{ "dmtc0",	"t,z",		MIPS_COP0(0x05),				MA_MIPS3,	MO_64BIT },

//     31--------------------21-------------------------------5--------0
//     |=            COP0FUNCT|                              | function|
//     -----11----------------------------------------------------6-----
//     |--000--|--001--|--010--|--011--|--100--|--101--|--110--|--111--| lo
// 000 |  ---  | TLBR  | TLBWI |  ---  |  ---  |  ---  | TLBWR |  ---  | 00..07
// 001 | TLBP  |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  | 08..0F
// 010 | RFE   |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  | 10..17
// 011 | ERET  |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  | 18..1F
// 100 |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  | 20..27
// 101 |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  | 28..2F
// 110 |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  | 30..37
// 110 |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  | 38..3F
//  hi |-------|-------|-------|-------|-------|-------|-------|-------|
	{ "tlbr",	"",			MIPS_COP0FUNCT(0x01),			MA_MIPS1|MA_EXRSP,	0 },
	{ "tlbwi",	"",			MIPS_COP0FUNCT(0x02),			MA_MIPS1|MA_EXRSP,	0 },
	{ "tlbwr",	"",			MIPS_COP0FUNCT(0x06),			MA_MIPS1|MA_EXRSP,	0 },
	{ "tlbp",	"",			MIPS_COP0FUNCT(0x08),			MA_MIPS1|MA_EXRSP,	0 },
	{ "rfe",	"",			MIPS_COP0FUNCT(0x10),			MA_PSX,		0 },
	{ "eret",	"",			MIPS_COP0FUNCT(0x18),			MA_MIPS3,	0 },

//     31---------26---------21----------------------------------------0
//     |=      COP1|    rs    |                                        |
//     -----6-------5---------------------------------------------------
//     |--000--|--001--|--010--|--011--|--100--|--101--|--110--|--111--| lo
//  00 |  MFC1 | DMFC1 |  CFC1 |  ---  |  MTC1 | DMTC1 |  CTC1 |  ---  | 00..07
//  01 |  BC*  |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  | 08..0F
//  10 |  S*   |  ---  |  ---  |  ---  |  W*   |  ---  |  ---  |  ---  | 10..17
//  11 |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  | 18..1F
//  hi |-------|-------|-------|-------|-------|-------|-------|-------|

	{ "mfc1",	"t,S",		MIPS_COP1(0x00),				MA_MIPS1,	MO_FPU },
	{ "dmfc1",	"t,S",		MIPS_COP1(0x01),				MA_MIPS3,	MO_DFPU|MO_64BIT },
	{ "cfc1",	"t,f",		MIPS_COP1(0x02),				MA_MIPS1,	MO_FPU },
	{ "mtc1",	"t,S",		MIPS_COP1(0x04),				MA_MIPS1,	MO_FPU },
	{ "dmtc1",	"t,S",		MIPS_COP1(0x05),				MA_MIPS3,	MO_DFPU|MO_64BIT },
	{ "ctc1",	"t,f",		MIPS_COP1(0x06),				MA_MIPS1,	MO_FPU },

//     31---------26----------20-------16------------------------------0
//     |=    COP1BC|          |   rt    |                              |
//     ------11--------------5------------------------------------------
//     |--000--|--001--|--010--|--011--|--100--|--101--|--110--|--111--| lo
//  00 |  BC1F | BC1T  | BC1FL | BC1TL |  ---  |  ---  |  ---  |  ---  | 00..07
//  01 |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  | 08..0F
//  10 |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  | 10..17
//  11 |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  | 18..1F
//  hi |-------|-------|-------|-------|-------|-------|-------|-------|
	{ "bc1f",	"i16",		MIPS_COP1BC(0x00),				MA_MIPS1,	MO_FPU|MO_IPCR|MO_DELAY|MO_NODELAYSLOT },
	{ "bc1t",	"i16",		MIPS_COP1BC(0x01),				MA_MIPS1,	MO_FPU|MO_IPCR|MO_DELAY|MO_NODELAYSLOT },
	{ "bc1fl",	"i16",		MIPS_COP1BC(0x02),				MA_MIPS2,	MO_FPU|MO_IPCR|MO_DELAY|MO_NODELAYSLOT },
	{ "bc1tl",	"i16",		MIPS_COP1BC(0x03),				MA_MIPS2,	MO_FPU|MO_IPCR|MO_DELAY|MO_NODELAYSLOT },

//     31--------------------21-------------------------------5--------0
//     |=                COP1S|                              | function|
//     -----11----------------------------------------------------6-----
//     |--000--|--001--|--010--|--011--|--100--|--101--|--110--|--111--| lo
// 000 |  add  |  sub  |  mul  |  div  | sqrt  |  abs  |  mov  |  neg  | 00..07
// 001 |round.l|trunc.l|ceil.l |floor.l|round.w|trunc.w|ceil.w |floor.w| 08..0F
// 010 |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  | rsqrt |  ---  | 10..17
// 011 | adda  | suba  | mula  |  ---  | madd  | msub  | madda | msuba | 18..1F
// 100 |  ---  | cvt.d |  ---  |  ---  | cvt.w | cvt.l |  ---  |  ---  | 20..27
// 101 |  max  |  min  |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  | 28..2F
// 110 |  c.f  | c.un  | c.eq  | c.ueq |c.(o)lt| c.ult |c.(o)le| c.ule | 30..37
// 110 |  c.sf | c.ngle| c.seq | c.ngl | c.lt  | c.nge | c.le  | c.ngt | 38..3F
//  hi |-------|-------|-------|-------|-------|-------|-------|-------|
	{ "add.s",		"D,S,T",	MIPS_COP1S(0x00),			MA_MIPS1,	MO_FPU },
	{ "add.s",		"S,T",		MIPS_COP1S(0x00),			MA_MIPS1,	MO_FPU|MO_FRSD },
	{ "sub.s",		"D,S,T",	MIPS_COP1S(0x01),			MA_MIPS1,	MO_FPU },
	{ "sub.s",		"S,T",		MIPS_COP1S(0x01),			MA_MIPS1,	MO_FPU|MO_FRSD },
	{ "mul.s",		"D,S,T",	MIPS_COP1S(0x02),			MA_MIPS1,	MO_FPU },
	{ "mul.s",		"S,T",		MIPS_COP1S(0x02),			MA_MIPS1,	MO_FPU|MO_FRSD },
	{ "div.s",		"D,S,T",	MIPS_COP1S(0x03),			MA_MIPS1,	MO_FPU },
	{ "div.s",		"S,T",		MIPS_COP1S(0x03),			MA_MIPS1,	MO_FPU|MO_FRSD },
	{ "sqrt.s",		"D,S",		MIPS_COP1S(0x04),			MA_MIPS2,	MO_FPU },
	{ "abs.s",		"D,S",		MIPS_COP1S(0x05),			MA_MIPS1,	MO_FPU },
	{ "mov.s",		"D,S",		MIPS_COP1S(0x06),			MA_MIPS1,	MO_FPU },
	{ "neg.s",		"D,S",		MIPS_COP1S(0x07),			MA_MIPS1,	MO_FPU },
	{ "round.l.s",	"D,S",		MIPS_COP1S(0x08),			MA_MIPS3,	MO_DFPU },
	{ "trunc.l.s",	"D,S",		MIPS_COP1S(0x09),			MA_MIPS3,	MO_DFPU },
	{ "ceil.l.s",	"D,S",		MIPS_COP1S(0x0A),			MA_MIPS3,	MO_DFPU },
	{ "floor.l.s",	"D,S",		MIPS_COP1S(0x0B),			MA_MIPS3,	MO_DFPU },
	{ "round.w.s",	"D,S",		MIPS_COP1S(0x0C),			MA_MIPS2|MA_EXPS2,	MO_FPU },
	{ "trunc.w.s",	"D,S",		MIPS_COP1S(0x0D),			MA_MIPS1|MA_EXPS2,	MO_FPU },
	{ "ceil.w.s",	"D,S",		MIPS_COP1S(0x0E),			MA_MIPS2|MA_EXPS2,	MO_FPU },
	{ "floor.w.s",	"D,S",		MIPS_COP1S(0x0F),			MA_MIPS2|MA_EXPS2,	MO_FPU },
	{ "rsqrt.w.s",	"D,S",		MIPS_COP1S(0x16),			MA_PS2,		0 },
	{ "adda.s",		"S,T",		MIPS_COP1S(0x18),			MA_PS2,		0 },
	{ "suba.s",		"S,T",		MIPS_COP1S(0x19),			MA_PS2,		0 },
	{ "mula.s",		"S,T",		MIPS_COP1S(0x1A),			MA_PS2,		0 },
	{ "madd.s",		"D,S,T",	MIPS_COP1S(0x1C),			MA_PS2,		0 },
	{ "madd.s",		"S,T",		MIPS_COP1S(0x1C),			MA_PS2,		MO_FRSD },
	{ "msub.s",		"D,S,T",	MIPS_COP1S(0x1D),			MA_PS2,		0 },
	{ "msub.s",		"S,T",		MIPS_COP1S(0x1D),			MA_PS2,		MO_FRSD },
	{ "madda.s",	"S,T",		MIPS_COP1S(0x1E),			MA_PS2,		0 },
	{ "msuba.s",	"S,T",		MIPS_COP1S(0x1F),			MA_PS2,		0 },
	{ "cvt.d.s",	"D,S",		MIPS_COP1S(0x21),			MA_MIPS1,	MO_DFPU },
	{ "cvt.w.s",	"D,S",		MIPS_COP1S(0x24),			MA_MIPS1,	MO_FPU },
	{ "cvt.l.s",	"D,S",		MIPS_COP1S(0x25),			MA_MIPS3,	MO_DFPU },
	{ "max.s",		"D,S,T",	MIPS_COP1S(0x28),			MA_PS2,		0 },
	{ "min.s",		"D,S,T",	MIPS_COP1S(0x29),			MA_PS2,		0 },
	{ "c.f.s",		"S,T",		MIPS_COP1S(0x30),			MA_MIPS1,	MO_FPU },
	{ "c.un.s",		"S,T",		MIPS_COP1S(0x31),			MA_MIPS1|MA_EXPS2,	MO_FPU },
	{ "c.eq.s",		"S,T",		MIPS_COP1S(0x32),			MA_MIPS1,	MO_FPU },
	{ "c.ueq.s",	"S,T",		MIPS_COP1S(0x33),			MA_MIPS1|MA_EXPS2,	MO_FPU },
	{ "c.olt.s",	"S,T",		MIPS_COP1S(0x34),			MA_MIPS1|MA_EXPS2,	MO_FPU },
	{ "c.lt.s",		"S,T",		MIPS_COP1S(0x34),			MA_PS2,		0 },
	{ "c.ult.s",	"S,T",		MIPS_COP1S(0x35),			MA_MIPS1|MA_EXPS2,	MO_FPU },
	{ "c.ole.s",	"S,T",		MIPS_COP1S(0x36),			MA_MIPS1|MA_EXPS2,	MO_FPU },
	{ "c.le.s",		"S,T",		MIPS_COP1S(0x36),			MA_PS2,		0 },
	{ "c.ule.s",	"S,T",		MIPS_COP1S(0x37),			MA_MIPS1|MA_EXPS2,	MO_FPU },
	{ "c.sf.s",		"S,T",		MIPS_COP1S(0x38),			MA_MIPS1|MA_EXPS2,	MO_FPU },
	{ "c.ngle.s",	"S,T",		MIPS_COP1S(0x39),			MA_MIPS1|MA_EXPS2,	MO_FPU },
	{ "c.seq.s",	"S,T",		MIPS_COP1S(0x3A),			MA_MIPS1|MA_EXPS2,	MO_FPU },
	{ "c.ngl.s",	"S,T",		MIPS_COP1S(0x3B),			MA_MIPS1|MA_EXPS2,	MO_FPU },
	{ "c.lt.s",		"S,T",		MIPS_COP1S(0x3C),			MA_MIPS1|MA_EXPS2,	MO_FPU },
	{ "c.nge.s",	"S,T",		MIPS_COP1S(0x3D),			MA_MIPS1|MA_EXPS2,	MO_FPU },
	{ "c.le.s",		"S,T",		MIPS_COP1S(0x3E),			MA_MIPS1|MA_EXPS2,	MO_FPU },
	{ "c.ngt.s",	"S,T",		MIPS_COP1S(0x3F),			MA_MIPS1|MA_EXPS2,	MO_FPU },

//     31--------------------21-------------------------------5--------0
//     |=                COP1D|                              | function|
//     -----11----------------------------------------------------6-----
//     |--000--|--001--|--010--|--011--|--100--|--101--|--110--|--111--| lo
// 000 |  add  |  sub  |  mul  |  div  | sqrt  |  abs  |  mov  |  neg  | 00..07
// 001 |round.l|trunc.l|ceil.l |floor.l|round.w|trunc.w|ceil.w |floor.w| 08..0F
// 010 |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  | 10..17
// 011 |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  | 18..1F
// 100 | cvt.s |  ---  |  ---  |  ---  | cvt.w | cvt.l |  ---  |  ---  | 20..27
// 101 |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  | 28..2F
// 110 |  c.f  | c.un  | c.eq  | c.ueq | c.olt | c.ult | c.ole | c.ule | 30..37
// 110 |  c.sf | c.ngle| c.seq | c.ngl | c.lt  | c.nge | c.le  | c.ngt | 38..3F
//  hi |-------|-------|-------|-------|-------|-------|-------|-------|
	{ "add.d",		"D,S,T",	MIPS_COP1D(0x00),			MA_MIPS1,	MO_DFPU },
	{ "add.d",		"S,T",		MIPS_COP1D(0x00),			MA_MIPS1,	MO_DFPU|MO_FRSD },
	{ "sub.d",		"D,S,T",	MIPS_COP1D(0x01),			MA_MIPS1,	MO_DFPU },
	{ "sub.d",		"S,T",		MIPS_COP1D(0x01),			MA_MIPS1,	MO_DFPU|MO_FRSD },
	{ "mul.d",		"D,S,T",	MIPS_COP1D(0x02),			MA_MIPS1,	MO_DFPU },
	{ "mul.d",		"S,T",		MIPS_COP1D(0x02),			MA_MIPS1,	MO_DFPU|MO_FRSD },
	{ "div.d",		"D,S,T",	MIPS_COP1D(0x03),			MA_MIPS1,	MO_DFPU },
	{ "div.d",		"S,T",		MIPS_COP1D(0x03),			MA_MIPS1,	MO_DFPU|MO_FRSD },
	{ "sqrt.d",		"D,S",		MIPS_COP1D(0x04),			MA_MIPS2,	MO_DFPU },
	{ "abs.d",		"D,S",		MIPS_COP1D(0x05),			MA_MIPS1,	MO_DFPU },
	{ "mov.d",		"D,S",		MIPS_COP1D(0x06),			MA_MIPS1,	MO_DFPU },
	{ "neg.d",		"D,S",		MIPS_COP1D(0x07),			MA_MIPS1,	MO_DFPU },
	{ "round.l.d",	"D,S",		MIPS_COP1D(0x08),			MA_MIPS3,	MO_DFPU },
	{ "trunc.l.d",	"D,S",		MIPS_COP1D(0x09),			MA_MIPS3,	MO_DFPU },
	{ "ceil.l.d",	"D,S",		MIPS_COP1D(0x0A),			MA_MIPS3,	MO_DFPU },
	{ "floor.l.d",	"D,S",		MIPS_COP1D(0x0B),			MA_MIPS3,	MO_DFPU },
	{ "round.w.d",	"D,S",		MIPS_COP1D(0x0C),			MA_MIPS2,	MO_DFPU },
	{ "trunc.w.d",	"D,S",		MIPS_COP1D(0x0D),			MA_MIPS1,	MO_DFPU },
	{ "ceil.w.d",	"D,S",		MIPS_COP1D(0x0E),			MA_MIPS2,	MO_DFPU },
	{ "floor.w.d",	"D,S",		MIPS_COP1D(0x0F),			MA_MIPS2,	MO_DFPU },
	{ "cvt.s.d",	"D,S",		MIPS_COP1D(0x20),			MA_MIPS1,	MO_DFPU },
	{ "cvt.w.d",	"D,S",		MIPS_COP1D(0x24),			MA_MIPS1,	MO_DFPU },
	{ "cvt.l.d",	"D,S",		MIPS_COP1D(0x25),			MA_MIPS3,	MO_DFPU },
	{ "c.f.d",		"S,T",		MIPS_COP1D(0x30),			MA_MIPS1,	MO_DFPU },
	{ "c.un.d",		"S,T",		MIPS_COP1D(0x31),			MA_MIPS1,	MO_DFPU },
	{ "c.eq.d",		"S,T",		MIPS_COP1D(0x32),			MA_MIPS1,	MO_DFPU },
	{ "c.ueq.d",	"S,T",		MIPS_COP1D(0x33),			MA_MIPS1,	MO_DFPU },
	{ "c.olt.d",	"S,T",		MIPS_COP1D(0x34),			MA_MIPS1,	MO_DFPU },
	{ "c.ult.d",	"S,T",		MIPS_COP1D(0x35),			MA_MIPS1,	MO_DFPU },
	{ "c.ole.d",	"S,T",		MIPS_COP1D(0x36),			MA_MIPS1,	MO_DFPU },
	{ "c.ule.d",	"S,T",		MIPS_COP1D(0x37),			MA_MIPS1,	MO_DFPU },
	{ "c.sf.d",		"S,T",		MIPS_COP1D(0x38),			MA_MIPS1,	MO_DFPU },
	{ "c.ngle.d",	"S,T",		MIPS_COP1D(0x39),			MA_MIPS1,	MO_DFPU },
	{ "c.seq.d",	"S,T",		MIPS_COP1D(0x3A),			MA_MIPS1,	MO_DFPU },
	{ "c.ngl.d",	"S,T",		MIPS_COP1D(0x3B),			MA_MIPS1,	MO_DFPU },
	{ "c.lt.d",		"S,T",		MIPS_COP1D(0x3C),			MA_MIPS1,	MO_DFPU },
	{ "c.nge.d",	"S,T",		MIPS_COP1D(0x3D),			MA_MIPS1,	MO_DFPU },
	{ "c.le.d",		"S,T",		MIPS_COP1D(0x3E),			MA_MIPS1,	MO_DFPU },
	{ "c.ngt.d",	"S,T",		MIPS_COP1D(0x3F),			MA_MIPS1,	MO_DFPU },

//     31--------------------21-------------------------------5--------0
//     |=                COP1W|                              | function|
//     -----11----------------------------------------------------6-----
//     |--000--|--001--|--010--|--011--|--100--|--101--|--110--|--111--| lo
// 000 |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  | 00..07
// 001 |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  | 08..0F
// 010 |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  | 10..17
// 011 |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  | 18..1F
// 100 | cvt.s | cvt.d |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  | 20..27
// 101 |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  | 28..2F
// 110 |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  | 30..37
// 110 |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  | 38..3F
//  hi |-------|-------|-------|-------|-------|-------|-------|-------|
	{ "cvt.s.w",	"D,S",		MIPS_COP1W(0x20),			MA_MIPS1,	MO_FPU },
	{ "cvt.d.w",	"D,S",		MIPS_COP1W(0x21),			MA_MIPS1,	MO_DFPU },

//     31--------------------21-------------------------------5--------0
//     |=                COP1L|                              | function|
//     -----11----------------------------------------------------6-----
//     |--000--|--001--|--010--|--011--|--100--|--101--|--110--|--111--| lo
// 000 |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  | 00..07
// 001 |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  | 08..0F
// 010 |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  | 10..17
// 011 |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  | 18..1F
// 100 | cvt.s | cvt.d |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  | 20..27
// 101 |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  | 28..2F
// 110 |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  | 30..37
// 110 |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  | 38..3F
//  hi |-------|-------|-------|-------|-------|-------|-------|-------|
	{ "cvt.s.l",	"D,S",		MIPS_COP1L(0x20),			MA_MIPS3,	MO_DFPU },
	{ "cvt.d.l",	"D,S",		MIPS_COP1L(0x21),			MA_MIPS3,	MO_DFPU },

//     31---------26---------21----------------------------------------0
//     |=      COP2|    rs    |                                        |
//     -----6-------5---------------------------------------------------
//     |--000--|--001--|--010--|--011--|--100--|--101--|--110--|--111--| lo
//  00 |  MFC2 |  ---  |  CFC2 |  MFV  |  MTC2 |  ---  |  CTC2 |  MTV  |
//  01 |  BC*  |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  |
//  10 |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  |
//  11 |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  |
//  hi |-------|-------|-------|-------|-------|-------|-------|-------|

	{ "mfc2",	"t,gs",			MIPS_COP2(0x00),			MA_PSX,		0 },
	{ "mfc2",	"t,RsRo",		MIPS_COP2(0x00),			MA_RSP,		0 },
	{ "cfc2",	"t,gc",			MIPS_COP2(0x02),			MA_PSX,		0 },
	{ "cfc2",	"t,Rc",			MIPS_COP2(0x02),			MA_RSP,		0 },
	{ "mtc2",	"t,gs",			MIPS_COP2(0x04),			MA_PSX,		0 },
	{ "mtc2",	"t,RsRo",		MIPS_COP2(0x04),			MA_RSP,		0 },
	{ "ctc2",	"t,gc",			MIPS_COP2(0x06),			MA_PSX,		0 },
	{ "ctc2",	"t,Rc",			MIPS_COP2(0x06),			MA_RSP,		0 },
	// VVVVVV VVVVV ttttt -------- C DDDDDDD
	{ "mfv",	"t,vd",			MIPS_COP2(0x03),			MA_PSP,		MO_VFPU|MO_VFPU_SINGLE },
	{ "mfvc",	"t,vc",			MIPS_COP2(0x03)|0x80,		MA_PSP,		MO_VFPU },
	{ "mtv",	"t,vd",			MIPS_COP2(0x07),			MA_PSP,		MO_VFPU|MO_VFPU_SINGLE },
	{ "mtvc",	"t,vc",			MIPS_COP2(0x07)|0x80,		MA_PSP,		MO_VFPU },


//     COP2BC: ? indicates any, * indicates all
//     31---------26----------20-------16------------------------------0
//     |=    COP2BC|          |   rt    |                              |
//     ------11---------5-----------------------------------------------
//     |--000--|--001--|--010--|--011--|--100--|--101--|--110--|--111--| lo
//  00 |  BVFx |  BVTx | BVFLx | BVTLx |  BVFy |  BVTy | BVFLy | BVTLy |
//  01 |  BVFz |  BVTz | BVFLz | BVTLz |  BVFw |  BVTw | BVFLw | BVTLw |
//  10 |  BVF? |  BVT? | BVFL? | BVTL? |  BVF* |  BVT* | BVFL* | BVTL* |
//  11 |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  |
//  hi |-------|-------|-------|-------|-------|-------|-------|-------|
	{ "bvf",		"jb,i16",	MIPS_COP2BC(0x00),			MA_PSP,		MO_IPCR|MO_DELAY|MO_NODELAYSLOT },
	{ "bvf.B",		"i16",		MIPS_COP2BC(0x00),			MA_PSP,		MO_IPCR|MO_DELAY|MO_NODELAYSLOT },
	{ "bvt",		"jb,i16",	MIPS_COP2BC(0x01),			MA_PSP,		MO_IPCR|MO_DELAY|MO_NODELAYSLOT },
	{ "bvt.B",		"i16",		MIPS_COP2BC(0x01),			MA_PSP,		MO_IPCR|MO_DELAY|MO_NODELAYSLOT },
	{ "bvfl",		"jb,i16",	MIPS_COP2BC(0x02),			MA_PSP,		MO_IPCR|MO_DELAY|MO_NODELAYSLOT },
	{ "bvfl.B",		"i16",		MIPS_COP2BC(0x02),			MA_PSP,		MO_IPCR|MO_DELAY|MO_NODELAYSLOT },
	{ "bvtl",		"jb,i16",	MIPS_COP2BC(0x03),			MA_PSP,		MO_IPCR|MO_DELAY|MO_NODELAYSLOT },
	{ "bvtl.B",		"i16",		MIPS_COP2BC(0x03),			MA_PSP,		MO_IPCR|MO_DELAY|MO_NODELAYSLOT },

//     31---------26-----23--------------------------------------------0
//     |= VFPU0| VOP | |
//     ------6--------3-------------------------------------------------
//     |--000--|--001--|--010--|--011--|--100--|--101--|--110--|--111--|
// 000 | VADD  | VSUB  | VSBN  | ---   | ---   | ---   | ---   | VDIV  | 00..07
//  hi |-------|-------|-------|-------|-------|-------|-------|-------|
	{ "vadd.S",		"vd,vs,vt",	MIPS_VFPU0(0x00),			MA_PSP,		MO_VFPU },
	{ "vsub.S",		"vd,vs,vt",	MIPS_VFPU0(0x01),			MA_PSP,		MO_VFPU },
	{ "vsbn.S",		"vd,vs,vt",	MIPS_VFPU0(0x02),			MA_PSP,		MO_VFPU },
	{ "vdiv.S",		"vd,vs,vt",	MIPS_VFPU0(0x07),			MA_PSP,		MO_VFPU },

//     31-------26-----23----------------------------------------------0
//     |=   VFPU1|  f  |                                               |
//     -----6-------3---------------------------------------------------
//     |--000--|--001--|--010--|--011--|--100--|--101--|--110--|--111--|
//     |  VMUL |  VDOT |  VSCL |  ---  |  VHDP |  VDET |  VCRS |  ---  |
//     |-------|-------|-------|-------|-------|-------|-------|-------|
	{ "vmul.S",		"vd,vs,vt",	MIPS_VFPU1(0),				MA_PSP,		MO_VFPU },
	{ "vdot.S",		"vd,vs,vt",	MIPS_VFPU1(1),				MA_PSP,		MO_VFPU },
	{ "vscl.S",		"vd,vs,vt",	MIPS_VFPU1(2),				MA_PSP,		MO_VFPU },
	{ "vhdp.S",		"vd,vs,vt",	MIPS_VFPU1(4),				MA_PSP,		MO_VFPU },
	{ "vdet.S",		"vd,vs,vt",	MIPS_VFPU1(5),				MA_PSP,		MO_VFPU },
	{ "vcrs.S",		"vd,vs,vt",	MIPS_VFPU1(6),				MA_PSP,		MO_VFPU },

//     31-------26-----23----------------------------------------------0
//     |=   VFPU3|  f  |                                               |
//     -----6-------3---------------------------------------------------
//     |--000--|--001--|--010--|--011--|--100--|--101--|--110--|--111--|
//     |  VCMP |  ---  |  VMIN |  VMAX |  ---  | VSCMP |  VSGE |  VSLT |
//     |-------|-------|-------|-------|-------|-------|-------|-------|
	// VVVVVV VVV TTTTTTT z SSSSSSS z --- CCCC
	{ "vcmp.S",		"C,vs,vt",	MIPS_VFPU3(0),				MA_PSP,		MO_VFPU },
	{ "vmin.S",		"vd,vs,vt",	MIPS_VFPU3(2),				MA_PSP,		MO_VFPU },
	{ "vmax.S",		"vd,vs,vt",	MIPS_VFPU3(3),				MA_PSP,		MO_VFPU },
	{ "vscmp.S",	"vd,vs,vt",	MIPS_VFPU3(5),				MA_PSP,		MO_VFPU },
	{ "vsge.S",		"vd,vs,vt",	MIPS_VFPU3(6),				MA_PSP,		MO_VFPU },
	{ "vslt.S",		"vd,vs,vt",	MIPS_VFPU3(7),				MA_PSP,		MO_VFPU },

//     31-------26--------------------------------------------5--------0
//     |=SPECIAL3|                                           | function|
//     -----11----------------------------------------------------6-----
//     -----6-------5---------------------------------------------------
//     |--000--|--001--|--010--|--011--|--100--|--101--|--110--|--111--| lo
// 000 |  EXT  |  ---  |  ---  |  ---  |  INS  |  ---  |  ---  |  ---  |
// 001 |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  |
// 010 |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  |
// 011 |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  |
// 100 |ALLEGRE|  ---  |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  |
// 101 |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  |
// 110 |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  |
// 110 |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  |
//  hi |-------|-------|-------|-------|-------|-------|-------|-------|
	{ "ext",	"t,s,i5,je",	MIPS_SPECIAL3(0),			MA_PSP,		0 },
	{ "ins",	"t,s,i5,ji",	MIPS_SPECIAL3(4),			MA_PSP,		0 },

//     31-------26----------------------------------10--------5--------0
//     |=SPECIAL3|                                 | secfunc |ALLEGREX0|
//     ------11---------5-------------------------------5---------6-----
//     |--000--|--001--|--010--|--011--|--100--|--101--|--110--|--111--| lo
//  00 |  ---  |  ---  | WSBH  | WSBW  |  ---  |  ---  |  ---  |  ---  |
//  01 |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  |
//  10 |  SEB  |  ---  |  ---  |  ---  |BITREV |  ---  |  ---  |  ---  |
//  11 |  SEH  |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  |
//  hi |-------|-------|-------|-------|-------|-------|-------|-------|
	// VVVVVV ----- ttttt ddddd VVVVV VVVVVV
	{ "wsbh",	"d,t",			MIPS_ALLEGREX0(0x02),		MA_PSP,		0 },
	{ "wsbh",	"d",			MIPS_ALLEGREX0(0x02),		MA_PSP,		0 },
	{ "wsbw",	"d,t",			MIPS_ALLEGREX0(0x03),		MA_PSP,		0 },
	{ "wsbw",	"d",			MIPS_ALLEGREX0(0x03),		MA_PSP,		0 },
	{ "seb",	"d,t",			MIPS_ALLEGREX0(0x10),		MA_PSP,		0 },
	{ "seb",	"d",			MIPS_ALLEGREX0(0x10),		MA_PSP,		0 },
	{ "bitrev",	"d,t",			MIPS_ALLEGREX0(0x14),		MA_PSP,		0 },
	{ "bitrev",	"d",			MIPS_ALLEGREX0(0x14),		MA_PSP,		0 },
	{ "seh",	"d,t",			MIPS_ALLEGREX0(0x18),		MA_PSP,		0 },
	{ "seh",	"d",			MIPS_ALLEGREX0(0x18),		MA_PSP,		0 },


//     VFPU4: This one is a bit messy.
//     31-------26------21---------------------------------------------0
//     |=   VFPU4|  rs  |                                              |
//     -----6-------5---------------------------------------------------
//  hi |--000--|--001--|--010--|--011--|--100--|--101--|--110--|--111--| lo
//  00 |VF4-1.1|VF4-1.2|VF4-1.3| VCST  |  ---  |  ---  |  ---  |  ---  |
//  01 |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  |
//  10 | VF2IN | VF2IZ | VF2IU | VF2ID | VI2F  | VCMOV |  ---  |  ---  |
//  11 | VWBN  | VWBN  | VWBN  | VWBN  | VWBN  | VWBN  | VWBN  | VWBN  |
//     |-------|-------|-------|-------|-------|-------|-------|-------|
	// VVVVVV VVVVV iiiii z ------- z DDDDDDD
	// Technically these also have names (as the second arg.)
	{ "vcst.S",		"vd,Wc",	MIPS_VFPU4(0x03),			MA_PSP,		MO_VFPU },
	{ "vf2in.S",	"vd,vs,i5",	MIPS_VFPU4(0x10),			MA_PSP,		MO_VFPU },
	{ "vf2iz.S",	"vd,vs,i5",	MIPS_VFPU4(0x11),			MA_PSP,		MO_VFPU },
	{ "vf2iu.S",	"vd,vs,i5",	MIPS_VFPU4(0x12),			MA_PSP,		MO_VFPU },
	{ "vf2id.S",	"vd,vs,i5",	MIPS_VFPU4(0x13),			MA_PSP,		MO_VFPU },
	{ "vi2f.S",		"vd,vs,i5",	MIPS_VFPU4(0x14),			MA_PSP,		MO_VFPU },
	{ "vcmovt.S",	"vd,vs,i5",	MIPS_VFPU4(0x15)|0,  		MA_PSP,		MO_VFPU },
	{ "vcmovf.S",	"vd,vs,i5",	MIPS_VFPU4(0x15)|(1<<19),	MA_PSP,		MO_VFPU },
	{ "vwbn.S",		"vd,vs,i5",	MIPS_VFPU4(0x18),			MA_PSP,		MO_VFPU },

//     31-------------21-------16--------------------------------------0
//     |= VF4-1.1      |   rt  |                                       |
//     --------11----------5--------------------------------------------
//  hi |--000--|--001--|--010--|--011--|--100--|--101--|--110--|--111--| lo
//  00 | VMOV  | VABS  | VNEG  | VIDT  | vsAT0 | vsAT1 | VZERO | VONE  |
//  01 |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  |
//  10 | VRCP  | VRSQ  | vsIN  | VCOS  | VEXP2 | VLOG2 | vsQRT | VASIN |
//  11 | VNRCP |  ---  | VNSIN |  ---  |VREXP2 |  ---  |  ---  |  ---  |
//     |-------|-------|-------|-------|-------|-------|-------|-------|
	{ "vmov.S",		"vd,vs",	MIPS_VFPU4_11(0x00),		MA_PSP,		MO_VFPU },
	{ "vabs.S",		"vd,vs",	MIPS_VFPU4_11(0x01),		MA_PSP,		MO_VFPU },
	{ "vneg.S",		"vd,vs",	MIPS_VFPU4_11(0x02), 		MA_PSP,		MO_VFPU },
	{ "vidt.S",		"vd",		MIPS_VFPU4_11(0x03),		MA_PSP,		MO_VFPU },
	{ "vsat0.S",	"vd,vs",	MIPS_VFPU4_11(0x04),		MA_PSP,		MO_VFPU },
	{ "vsat1.S",	"vd,vs",	MIPS_VFPU4_11(0x05),		MA_PSP,		MO_VFPU },
	{ "vzero.S",	"vd",		MIPS_VFPU4_11(0x06),		MA_PSP,		MO_VFPU },
	{ "vone.S",		"vd",		MIPS_VFPU4_11(0x07),		MA_PSP,		MO_VFPU },
	{ "vrcp.S",		"vd,vs",	MIPS_VFPU4_11(0x10),		MA_PSP,		MO_VFPU },
	{ "vrsq.S",		"vd,vs",	MIPS_VFPU4_11(0x11),		MA_PSP,		MO_VFPU },
	{ "vsin.S",		"vd,vs",	MIPS_VFPU4_11(0x12),		MA_PSP,		MO_VFPU },
	{ "vcos.S",		"vd,vs",	MIPS_VFPU4_11(0x13),		MA_PSP,		MO_VFPU },
	{ "vexp2.S",	"vd,vs",	MIPS_VFPU4_11(0x14),		MA_PSP,		MO_VFPU },
	{ "vlog2.S",	"vd,vs",	MIPS_VFPU4_11(0x15),		MA_PSP,		MO_VFPU },
	{ "vsqrt.S",	"vd,vs",	MIPS_VFPU4_11(0x16),		MA_PSP,		MO_VFPU },
	{ "vasin.S",	"vd,vs",	MIPS_VFPU4_11(0x17),		MA_PSP,		MO_VFPU },
	{ "vnrcp.S",	"vd,vs",	MIPS_VFPU4_11(0x18),		MA_PSP,		MO_VFPU },
	{ "vnsin.S",	"vd,vs",	MIPS_VFPU4_11(0x1a),		MA_PSP,		MO_VFPU },
	{ "vrexp2.S",	"vd,vs",	MIPS_VFPU4_11(0x1c),		MA_PSP,		MO_VFPU },

//     VFPU4 1.2: TODO: Unsure where vsBZ goes, no one uses it.
//     31-------------21-------16--------------------------------------0
//     |= VF4-1.2      |   rt  |                                       |
//     --------11----------5--------------------------------------------
//  hi |--000--|--001--|--010--|--011--|--100--|--101--|--110--|--111--| lo
//  00 | VRNDS | VRNDI |VRNDF1 |VRNDF2 |  ---  |  ---  |  ---  |  ---  |
//  01 |  ---  |  ---  |  ---  |  ---  | vsBZ? |  ---  |  ---  |  ---  |
//  10 |  ---  |  ---  | VF2H  | VH2F  |  ---  |  ---  | vsBZ? | VLGB  |
//  11 | VUC2I | VC2I  | VUS2I | vs2I  | VI2UC | VI2C  | VI2US | VI2S  |
//     |-------|-------|-------|-------|-------|-------|-------|-------|
	{ "vrnds.S",	"vd",		MIPS_VFPU4_12(0x00),		MA_PSP,		MO_VFPU },
	{ "vrndi.S",	"vd",		MIPS_VFPU4_12(0x01),		MA_PSP,		MO_VFPU },
	{ "vrndf1.S",	"vd",		MIPS_VFPU4_12(0x02),		MA_PSP,		MO_VFPU },
	{ "vrndf2.S",	"vd",		MIPS_VFPU4_12(0x03),		MA_PSP,		MO_VFPU },
	// TODO: vsBZ?
	{ "vf2h.S",		"vd,vs",	MIPS_VFPU4_12(0x12),		MA_PSP,		MO_VFPU },
	{ "vh2f.S",		"vd,vs",	MIPS_VFPU4_12(0x13),		MA_PSP,		MO_VFPU },
	// TODO: vsBZ?
	{ "vlgb.S",		"vd,vs",	MIPS_VFPU4_12(0x17),		MA_PSP,		MO_VFPU },
	{ "vuc2i.S",	"vd,vs",	MIPS_VFPU4_12(0x18),		MA_PSP,		MO_VFPU },
	{ "vc2i.S",		"vd,vs",	MIPS_VFPU4_12(0x19),		MA_PSP,		MO_VFPU },
	{ "vus2i.S",	"vd,vs",	MIPS_VFPU4_12(0x1a),		MA_PSP,		MO_VFPU },
	{ "vs2i.S",		"vd,vs",	MIPS_VFPU4_12(0x1b),		MA_PSP,		MO_VFPU },
	{ "vi2uc.S",	"vd,vs",	MIPS_VFPU4_12(0x1c),		MA_PSP,		MO_VFPU },
	{ "vi2c.S",		"vd,vs",	MIPS_VFPU4_12(0x1d),		MA_PSP,		MO_VFPU },
	{ "vi2us.S",	"vd,vs",	MIPS_VFPU4_12(0x1e),		MA_PSP,		MO_VFPU },
	{ "vi2s.S",		"vd,vs",	MIPS_VFPU4_12(0x1f),		MA_PSP,		MO_VFPU },

//     31--------------21------16--------------------------------------0
//     |= VF4-1.3      |   rt  |                                       |
//     --------11----------5--------------------------------------------
//  hi |--000--|--001--|--010--|--011--|--100--|--101--|--110--|--111--| lo
//  00 | vsRT1 | vsRT2 | VBFY1 | VBFY2 | VOCP  | vsOCP | VFAD  | VAVG  |
//  01 | vsRT3 | vsRT4 | vsGN  |  ---  |  ---  |  ---  |  ---  |  ---  |
//  10 | VMFVC | VMTVC |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  |
//  11 |  ---  |VT4444 |VT5551 |VT5650 |  ---  |  ---  |  ---  |  ---  |
//     |-------|-------|-------|-------|-------|-------|-------|-------|
	{ "vsrt1.S",	"vd,vs",	MIPS_VFPU4_13(0x00),		MA_PSP,		MO_VFPU },
	{ "vsrt2.S",	"vd,vs",	MIPS_VFPU4_13(0x01),		MA_PSP,		MO_VFPU },
	{ "vbfy1.S",	"vd,vs",	MIPS_VFPU4_13(0x02),		MA_PSP,		MO_VFPU },
	{ "vbfy2.S",	"vd,vs",	MIPS_VFPU4_13(0x03),		MA_PSP,		MO_VFPU },
	{ "vocp.S",		"vd,vs",	MIPS_VFPU4_13(0x04),		MA_PSP,		MO_VFPU },
	{ "vsocp.S",	"vd,vs",	MIPS_VFPU4_13(0x05),		MA_PSP,		MO_VFPU },
	{ "vfad.S",		"vd,vs",	MIPS_VFPU4_13(0x06),		MA_PSP,		MO_VFPU },
	{ "vavg.S",		"vd,vs",	MIPS_VFPU4_13(0x07),		MA_PSP,		MO_VFPU },
	{ "vsrt3.S",	"vd,vs",	MIPS_VFPU4_13(0x08),		MA_PSP,		MO_VFPU },
	{ "vsrt4.S",	"vd,vs",	MIPS_VFPU4_13(0x09),		MA_PSP,		MO_VFPU },
	{ "vsgn.S",		"vd,vs",	MIPS_VFPU4_13(0x0a),		MA_PSP,		MO_VFPU },
	{ "vmfv.S",		"vs,i7",	MIPS_VFPU4_13(0x10)|0x00,	MA_PSP,		MO_VFPU },
	{ "vmtv.S",		"vs,i7",	MIPS_VFPU4_13(0x11)|0x00,	MA_PSP,		MO_VFPU },
	{ "vmfvc.S",	"vs,i7",	MIPS_VFPU4_13(0x10)|0x80,	MA_PSP,		MO_VFPU },
	{ "vmtvc.S",	"vs,i7",	MIPS_VFPU4_13(0x11)|0x80,	MA_PSP,		MO_VFPU },
	{ "vt4444.S",	"vd,vs",	MIPS_VFPU4_13(0x19),		MA_PSP,		MO_VFPU },
	{ "vt5551.S",	"vd,vs",	MIPS_VFPU4_13(0x1a),		MA_PSP,		MO_VFPU },
	{ "vt5650.S",	"vd,vs",	MIPS_VFPU4_13(0x1b),		MA_PSP,		MO_VFPU },

//     31-------26-----23----------------------------------------------0
//     |= VFPU5| f     |                                               |
//     -----6-------3---------------------------------------------------
//     |--000--|--001--|--010--|--011--|--100--|--101--|--110--|--111--| lo
//     | VPFXS | VPFXS | VPFXT | VPFXT | VPFXD | VPFXD | VIIM  | VFIM  |
//     |-------|-------|-------|-------|-------|-------|-------|-------|
	{ "vpfxs",		"Ws",		MIPS_VFPU5(0),				MA_PSP,		0 },
	{ "vpfxt",		"Ws",		MIPS_VFPU5(2),				MA_PSP,		0 },
	{ "vpfxd",		"Wd",		MIPS_VFPU5(4),				MA_PSP,		0 },
	{ "viim.s",		"vt,i16",	MIPS_VFPU5(6),				MA_PSP,		MO_VFPU_SINGLE },
	{ "vfim.s",		"vt,ih",	MIPS_VFPU5(7),				MA_PSP,		MO_VFPU_SINGLE },

//     31-------26-----23----------------------------------------------0
//     |= VFPU6| f     |                                               |
//     -----6-------3---------------------------------------------------
//     |--000--|--001--|--010--|--011--|--100--|--101--|--110--|--111--| lo
//     | VMMUL |     V(H)TFM2/3/4      | VMSCL |   *1  |  ---  |VF6-1.1|
//     |-------|-------|-------|-------|-------|-------|-------|-------|
//		*1: vcrsp.t/vqmul.q
	{ "vmmul.S",	"md,ms,mt",		MIPS_VFPU6(0),					MA_PSP,	MO_VFPU|MO_VFPU_TRANSPOSE_VS },
	{ "vtfm2.p",	"vd,ms,vt",		MIPS_VFPU6(1)|MIPS_VFPUSIZE(1),	MA_PSP,	MO_VFPU|MO_VFPU_PAIR },
	{ "vhtfm2.p",	"vd,ms,vt",		MIPS_VFPU6(2)|MIPS_VFPUSIZE(1),	MA_PSP,	MO_VFPU|MO_VFPU_PAIR },
	{ "vtfm3.t",	"vd,ms,vt",		MIPS_VFPU6(2)|MIPS_VFPUSIZE(2),	MA_PSP,	MO_VFPU|MO_VFPU_TRIPLE },
	{ "vhtfm3.t",	"vd,ms,vt",		MIPS_VFPU6(3)|MIPS_VFPUSIZE(2),	MA_PSP,	MO_VFPU|MO_VFPU_TRIPLE },
	{ "vtfm4.q",	"vd,ms,vt",		MIPS_VFPU6(3)|MIPS_VFPUSIZE(3),	MA_PSP,	MO_VFPU|MO_VFPU_QUAD },
	{ "vmscl.S",	"md,ms,vSt",	MIPS_VFPU6(4),					MA_PSP,	MO_VFPU },
	{ "vcrsp.t",	"vd,vs,vt",		MIPS_VFPU6(5)|MIPS_VFPUSIZE(2),	MA_PSP,	MO_VFPU|MO_VFPU_TRIPLE },
	{ "vqmul.q",	"vd,vs,vt",		MIPS_VFPU6(5)|MIPS_VFPUSIZE(3),	MA_PSP,	MO_VFPU|MO_VFPU_QUAD },

//     31--------23----20----------------------------------------------0
//     |= VF6-1.1 |  f |                                               |
//     -----9-------3---------------------------------------------------
//     |--000--|--001--|--010--|--011--|--100--|--101--|--110--|--111--| lo
//     |VF6-1.2|  ---  |     VROT      |  ---  |  ---  |  ---  |  ---  |
//     |-------|-------|-------|-------|-------|-------|-------|-------|
	// VVVVVVVVVVV iiiii z SSSSSSS z DDDDDDD
	{ "vrot.S",		"vd,vSs,Wr",	MIPS_VFPU6_1VROT(),		MA_PSP,		MO_VFPU },

//     31--------20----16----------------------------------------------0
//     |= VF6-1.2 |  f |                                               |
//     -----6-------4---------------------------------------------------
//  hi |--000--|--001--|--010--|--011--|--100--|--101--|--110--|--111--| lo
//   0 | VMMOV |  ---  |  ---  | VMIDT |  ---  |  ---  |VMZERO | VMONE |
//   1 |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  |
//     |-------|-------|-------|-------|-------|-------|-------|-------|
	// VVVVVVVVVVVVVVVV z SSSSSSS z DDDDDDD
	{ "vmmov.S",	"md,ms",	MIPS_VFPU6_2(0),			MA_PSP,		MO_VFPU },
	// VVVVVVVVVVVVVVVV z ------- z DDDDDDD
	{ "vmidt.S",	"md",		MIPS_VFPU6_2(3),			MA_PSP,		MO_VFPU },
	{ "vmzero.S",	"md",		MIPS_VFPU6_2(6),			MA_PSP,		MO_VFPU },
	{ "vmone.S",	"md",		MIPS_VFPU6_2(7),			MA_PSP,		MO_VFPU },

//     31---------26------------------------------------------5--------0
//     |=       RSP|                                         | function|
//     ------6----------------------------------------------------6-----
//     |--000--|--001--|--010--|--011--|--100--|--101--|--110--|--111--| lo
// 000 | VMULF | VMULU | VRNDP | VMULQ | VMUDL | VMUDM | VMUDN | VMUDH | 00..07
// 001 | VMACF | VMACU | VRNDN | VMACQ | VMADL | VMADH | VMADN | VMADH | 08..0F
// 010 | VADD  | VSUB  | VSUT  | VABS  | VADDC | VSUBC | VADDB | VSUBB | 10..17
// 011 | VACCB | VSUCB | VSAD  | VSAC  | VSUM  | VSAR  | VACC  | VSUC  | 18..1F
// 100 | VLT   | VEQ   | VNE   | VGE   | VCL   | VCH   | VCR   | VMRG  | 20..27
// 101 | VAND  | VNAND | VOR   | VNOR  | VXOR  | VNXOR |  ---  |  ---  | 28..2F
// 110 | VRCP  | VRCPL | VRCPH | VMOV  | VRSQ  | VRSQL | VRSQH | VNOP  | 30..37
// 111 | VEXTT | VEXTQ | VEXTN |  ---  | VINST | VINSQ | VINSN | VNULL | 38..3F
//  hi |-------|-------|-------|-------|-------|-------|-------|-------|
	{ "vmulf",	"Rd,Rs,RtRe",	MIPS_RSP_COP2(0x00),		MA_RSP,		0 },
	{ "vmulf",	"Rs,RtRe",		MIPS_RSP_COP2(0x00),		MA_RSP,		MO_RSP_VRSD },
	{ "vmulu",	"Rd,Rs,RtRe",	MIPS_RSP_COP2(0x01),		MA_RSP,		0 },
	{ "vmulu",	"Rs,RtRe",		MIPS_RSP_COP2(0x01),		MA_RSP,		MO_RSP_VRSD },
	{ "vrndp",	"Rd,Rs,RtRe",	MIPS_RSP_COP2(0x02),		MA_RSP,		0 },
	{ "vrndp",	"Rs,RtRe",		MIPS_RSP_COP2(0x02),		MA_RSP,		MO_RSP_VRSD },
	{ "vmulq",	"Rd,Rs,RtRe",	MIPS_RSP_COP2(0x03),		MA_RSP,		0 },
	{ "vmulq",	"Rs,RtRe",		MIPS_RSP_COP2(0x03),		MA_RSP,		MO_RSP_VRSD },
	{ "vmudl",	"Rd,Rs,RtRe",	MIPS_RSP_COP2(0x04),		MA_RSP,		0 },
	{ "vmudl",	"Rs,RtRe",		MIPS_RSP_COP2(0x04),		MA_RSP,		MO_RSP_VRSD },
	{ "vmudm",	"Rd,Rs,RtRe",	MIPS_RSP_COP2(0x05),		MA_RSP,		0 },
	{ "vmudm",	"Rs,RtRe",		MIPS_RSP_COP2(0x05),		MA_RSP,		MO_RSP_VRSD },
	{ "vmudn",	"Rd,Rs,RtRe",	MIPS_RSP_COP2(0x06),		MA_RSP,		0 },
	{ "vmudn",	"Rs,RtRe",		MIPS_RSP_COP2(0x06),		MA_RSP,		MO_RSP_VRSD },
	{ "vmudh",	"Rd,Rs,RtRe",	MIPS_RSP_COP2(0x07),		MA_RSP,		0 },
	{ "vmudh",	"Rs,RtRe",		MIPS_RSP_COP2(0x07),		MA_RSP,		MO_RSP_VRSD },
	{ "vmacf",	"Rd,Rs,RtRe",	MIPS_RSP_COP2(0x08),		MA_RSP,		0 },
	{ "vmacf",	"Rs,RtRe",		MIPS_RSP_COP2(0x08),		MA_RSP,		MO_RSP_VRSD },
	{ "vmacu",	"Rd,Rs,RtRe",	MIPS_RSP_COP2(0x09),		MA_RSP,		0 },
	{ "vmacu",	"Rs,RtRe",		MIPS_RSP_COP2(0x09),		MA_RSP,		MO_RSP_VRSD },
	{ "vrndn",	"Rd,Rs,RtRe",	MIPS_RSP_COP2(0x0a),		MA_RSP,		0 },
	{ "vrndn",	"Rs,RtRe",		MIPS_RSP_COP2(0x0a),		MA_RSP,		MO_RSP_VRSD },
	{ "vmacq",	"Rd,Rs,RtRe",	MIPS_RSP_COP2(0x0b),		MA_RSP,		0 },
	{ "vmacq",	"Rs,RtRe",		MIPS_RSP_COP2(0x0b),		MA_RSP,		MO_RSP_VRSD },
	{ "vmadl",	"Rd,Rs,RtRe",	MIPS_RSP_COP2(0x0c),		MA_RSP,		0 },
	{ "vmadl",	"Rs,RtRe",		MIPS_RSP_COP2(0x0c),		MA_RSP,		MO_RSP_VRSD },
	{ "vmadm",	"Rd,Rs,RtRe",	MIPS_RSP_COP2(0x0d),		MA_RSP,		0 },
	{ "vmadm",	"Rs,RtRe",		MIPS_RSP_COP2(0x0d),		MA_RSP,		MO_RSP_VRSD },
	{ "vmadn",	"Rd,Rs,RtRe",	MIPS_RSP_COP2(0x0e),		MA_RSP,		0 },
	{ "vmadn",	"Rs,RtRe",		MIPS_RSP_COP2(0x0e),		MA_RSP,		MO_RSP_VRSD },
	{ "vmadh",	"Rd,Rs,RtRe",	MIPS_RSP_COP2(0x0f),		MA_RSP,		0 },
	{ "vmadh",	"Rs,RtRe",		MIPS_RSP_COP2(0x0f),		MA_RSP,		MO_RSP_VRSD },
	{ "vadd",	"Rd,Rs,RtRe",	MIPS_RSP_COP2(0x10),		MA_RSP,		0 },
	{ "vadd",	"Rs,RtRe",		MIPS_RSP_COP2(0x10),		MA_RSP,		MO_RSP_VRSD },
	{ "vsub",	"Rd,Rs,RtRe",	MIPS_RSP_COP2(0x11),		MA_RSP,		0 },
	{ "vsub",	"Rs,RtRe",		MIPS_RSP_COP2(0x11),		MA_RSP,		MO_RSP_VRSD },
	{ "vsut",	"Rd,Rs,RtRe",	MIPS_RSP_COP2(0x12),		MA_RSP,		0 },
	{ "vsut",	"Rs,RtRe",		MIPS_RSP_COP2(0x12),		MA_RSP,		MO_RSP_VRSD },
	{ "vabs",	"Rd,Rs,RtRe",	MIPS_RSP_COP2(0x13),		MA_RSP,		0 },
	{ "vabs",	"Rs,RtRe",		MIPS_RSP_COP2(0x13),		MA_RSP,		MO_RSP_VRSD },
	{ "vaddc",	"Rd,Rs,RtRe",	MIPS_RSP_COP2(0x14),		MA_RSP,		0 },
	{ "vaddc",	"Rs,RtRe",		MIPS_RSP_COP2(0x14),		MA_RSP,		MO_RSP_VRSD },
	{ "vsubc",	"Rd,Rs,RtRe",	MIPS_RSP_COP2(0x15),		MA_RSP,		0 },
	{ "vsubc",	"Rs,RtRe",		MIPS_RSP_COP2(0x15),		MA_RSP,		MO_RSP_VRSD },
	{ "vaddb",	"Rd,Rs,RtRe",	MIPS_RSP_COP2(0x16),		MA_RSP,		0 },
	{ "vaddb",	"Rs,RtRe",		MIPS_RSP_COP2(0x16),		MA_RSP,		MO_RSP_VRSD },
	{ "vsubb",	"Rd,Rs,RtRe",	MIPS_RSP_COP2(0x17),		MA_RSP,		0 },
	{ "vsubb",	"Rs,RtRe",		MIPS_RSP_COP2(0x17),		MA_RSP,		MO_RSP_VRSD },
	{ "vaccb",	"Rd,Rs,RtRe",	MIPS_RSP_COP2(0x18),		MA_RSP,		0 },
	{ "vaccb",	"Rs,RtRe",		MIPS_RSP_COP2(0x18),		MA_RSP,		MO_RSP_VRSD },
	{ "vsucb",	"Rd,Rs,RtRe",	MIPS_RSP_COP2(0x19),		MA_RSP,		0 },
	{ "vsucb",	"Rs,RtRe",		MIPS_RSP_COP2(0x19),		MA_RSP,		MO_RSP_VRSD },
	{ "vsad",	"Rd,Rs,RtRe",	MIPS_RSP_COP2(0x1a),		MA_RSP,		0 },
	{ "vsad",	"Rs,RtRe",		MIPS_RSP_COP2(0x1a),		MA_RSP,		MO_RSP_VRSD },
	{ "vsac",	"Rd,Rs,RtRe",	MIPS_RSP_COP2(0x1b),		MA_RSP,		0 },
	{ "vsac",	"Rs,RtRe",		MIPS_RSP_COP2(0x1b),		MA_RSP,		MO_RSP_VRSD },
	{ "vsum",	"Rd,Rs,RtRe",	MIPS_RSP_COP2(0x1c),		MA_RSP,		0 },
	{ "vsum",	"Rs,RtRe",		MIPS_RSP_COP2(0x1c),		MA_RSP,		MO_RSP_VRSD },
	{ "vsar",	"Rd,Rs,RtRe",	MIPS_RSP_COP2(0x1d),		MA_RSP,		0 },
	{ "vsar",	"Rs,RtRe",		MIPS_RSP_COP2(0x1d),		MA_RSP,		MO_RSP_VRSD },
	{ "vacc",	"Rd,Rs,RtRe",	MIPS_RSP_COP2(0x1e),		MA_RSP,		0 },
	{ "vacc",	"Rs,RtRe",		MIPS_RSP_COP2(0x1e),		MA_RSP,		MO_RSP_VRSD },
	{ "vsuc",	"Rd,Rs,RtRe",	MIPS_RSP_COP2(0x1f),		MA_RSP,		0 },
	{ "vsuc",	"Rs,RtRe",		MIPS_RSP_COP2(0x1f),		MA_RSP,		MO_RSP_VRSD },
	{ "vlt",	"Rd,Rs,RtRe",	MIPS_RSP_COP2(0x20),		MA_RSP,		0 },
	{ "vlt",	"Rs,RtRe",		MIPS_RSP_COP2(0x20),		MA_RSP,		MO_RSP_VRSD },
	{ "veq",	"Rd,Rs,RtRe",	MIPS_RSP_COP2(0x21),		MA_RSP,		0 },
	{ "veq",	"Rs,RtRe",		MIPS_RSP_COP2(0x21),		MA_RSP,		MO_RSP_VRSD },
	{ "vne",	"Rd,Rs,RtRe",	MIPS_RSP_COP2(0x22),		MA_RSP,		0 },
	{ "vne",	"Rs,RtRe",		MIPS_RSP_COP2(0x22),		MA_RSP,		MO_RSP_VRSD },
	{ "vge",	"Rd,Rs,RtRe",	MIPS_RSP_COP2(0x23),		MA_RSP,		0 },
	{ "vge",	"Rs,RtRe",		MIPS_RSP_COP2(0x23),		MA_RSP,		MO_RSP_VRSD },
	{ "vcl",	"Rd,Rs,RtRe",	MIPS_RSP_COP2(0x24),		MA_RSP,		0 },
	{ "vcl",	"Rs,RtRe",		MIPS_RSP_COP2(0x24),		MA_RSP,		MO_RSP_VRSD },
	{ "vch",	"Rd,Rs,RtRe",	MIPS_RSP_COP2(0x25),		MA_RSP,		0 },
	{ "vch",	"Rs,RtRe",		MIPS_RSP_COP2(0x25),		MA_RSP,		MO_RSP_VRSD },
	{ "vcr",	"Rd,Rs,RtRe",	MIPS_RSP_COP2(0x26),		MA_RSP,		0 },
	{ "vcr",	"Rs,RtRe",		MIPS_RSP_COP2(0x26),		MA_RSP,		MO_RSP_VRSD },
	{ "vmrg",	"Rd,Rs,RtRe",	MIPS_RSP_COP2(0x27),		MA_RSP,		0 },
	{ "vmrg",	"Rs,RtRe",		MIPS_RSP_COP2(0x27),		MA_RSP,		MO_RSP_VRSD },
	{ "vand",	"Rd,Rs,RtRe",	MIPS_RSP_COP2(0x28),		MA_RSP,		0 },
	{ "vand",	"Rs,RtRe",		MIPS_RSP_COP2(0x28),		MA_RSP,		MO_RSP_VRSD },
	{ "vnand",	"Rd,Rs,RtRe",	MIPS_RSP_COP2(0x29),		MA_RSP,		0 },
	{ "vnand",	"Rs,RtRe",		MIPS_RSP_COP2(0x29),		MA_RSP,		MO_RSP_VRSD },
	{ "vor",	"Rd,Rs,RtRe",	MIPS_RSP_COP2(0x2a),		MA_RSP,		0 },
	{ "vor",	"Rs,RtRe",		MIPS_RSP_COP2(0x2a),		MA_RSP,		MO_RSP_VRSD },
	{ "vnor",	"Rd,Rs,RtRe",	MIPS_RSP_COP2(0x2b),		MA_RSP,		0 },
	{ "vnor",	"Rs,RtRe",		MIPS_RSP_COP2(0x2b),		MA_RSP,		MO_RSP_VRSD },
	{ "vxor",	"Rd,Rs,RtRe",	MIPS_RSP_COP2(0x2c),		MA_RSP,		0 },
	{ "vxor",	"Rs,RtRe",		MIPS_RSP_COP2(0x2c),		MA_RSP,		MO_RSP_VRSD },
	{ "vnxor",	"Rd,Rs,RtRe",	MIPS_RSP_COP2(0x2d),		MA_RSP,		0 },
	{ "vnxor",	"Rs,RtRe",		MIPS_RSP_COP2(0x2d),		MA_RSP,		MO_RSP_VRSD },
	{ "vrcp",	"RdRm,RtRl",	MIPS_RSP_COP2(0x30),		MA_RSP,		0 },
	{ "vrcpl",	"RdRm,RtRl",	MIPS_RSP_COP2(0x31),		MA_RSP,		0 },
	{ "vrcph",	"RdRm,RtRl",	MIPS_RSP_COP2(0x32),		MA_RSP,		0 },
	{ "vmov",	"RdRm,RtRl",	MIPS_RSP_COP2(0x33),		MA_RSP,		0 },
	{ "vrsq",	"RdRm,RtRl",	MIPS_RSP_COP2(0x34),		MA_RSP,		0 },
	{ "vrsql",	"RdRm,RtRl",	MIPS_RSP_COP2(0x35),		MA_RSP,		0 },
	{ "vrsqh",	"RdRm,RtRl",	MIPS_RSP_COP2(0x36),		MA_RSP,		0 },
	{ "vnop",	"",				MIPS_RSP_COP2(0x37),		MA_RSP,		0 },
	{ "vextt",	"Rd,Rs,RtRe",	MIPS_RSP_COP2(0x38),		MA_RSP,		0 },
	{ "vextt",	"Rs,RtRe",		MIPS_RSP_COP2(0x38),		MA_RSP,		MO_RSP_VRSD },
	{ "vextq",	"Rd,Rs,RtRe",	MIPS_RSP_COP2(0x39),		MA_RSP,		0 },
	{ "vextq",	"Rs,RtRe",		MIPS_RSP_COP2(0x39),		MA_RSP,		MO_RSP_VRSD },
	{ "vextn",	"Rd,Rs,RtRe",	MIPS_RSP_COP2(0x3a),		MA_RSP,		0 },
	{ "vextn",	"Rs,RtRe",		MIPS_RSP_COP2(0x3a),		MA_RSP,		MO_RSP_VRSD },
	{ "vinst",	"Rd,Rs,RtRe",	MIPS_RSP_COP2(0x3c),		MA_RSP,		0 },
	{ "vinst",	"Rs,RtRe",		MIPS_RSP_COP2(0x3c),		MA_RSP,		MO_RSP_VRSD },
	{ "vinsq",	"Rd,Rs,RtRe",	MIPS_RSP_COP2(0x3d),		MA_RSP,		0 },
	{ "vinsq",	"Rs,RtRe",		MIPS_RSP_COP2(0x3d),		MA_RSP,		MO_RSP_VRSD },
	{ "vinsn",	"Rd,Rs,RtRe",	MIPS_RSP_COP2(0x3e),		MA_RSP,		0 },
	{ "vinsn",	"Rs,RtRe",		MIPS_RSP_COP2(0x3e),		MA_RSP,		MO_RSP_VRSD },
	{ "vnull",	"",				MIPS_RSP_COP2(0x3f),		MA_RSP,		0 },

//     31---------26--------------------15-------11--------------------0
//     |=      LWC2|                    |   rd    |                    |
//     -----6----------------------5------------------------------------
//     |--000--|--001--|--010--|--011--|--100--|--101--|--110--|--111--| lo
//  00 |  LBV  |  LSV  |  LLV  |  LDV  |  LQV  |  LRV  |  LPV  |  LUV  | 00..07
//  01 |  LHV  |  LFV  |  LWV  |  LTV  |  ---  |  ---  |  ---  |  ---  | 08..0F
//  10 |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  | 10..17
//  11 |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  | 18..1F
//  hi |-------|-------|-------|-------|-------|-------|-------|-------|
	{"lbv",		"RtRo,i7(s)",	MIPS_RSP_LWC2(0x00),		MA_RSP,		0 },
	{"lbv",		"RtRo,(s)",		MIPS_RSP_LWC2(0x00),		MA_RSP,		0 },
	{"lsv",		"RtRo,i7(s)",	MIPS_RSP_LWC2(0x01),		MA_RSP,		MO_RSP_HWOFFSET },
	{"lsv",		"RtRo,(s)",		MIPS_RSP_LWC2(0x01),		MA_RSP,		MO_RSP_HWOFFSET },
	{"llv",		"RtRo,i7(s)",	MIPS_RSP_LWC2(0x02),		MA_RSP,		MO_RSP_WOFFSET },
	{"llv",		"RtRo,(s)",		MIPS_RSP_LWC2(0x02),		MA_RSP,		MO_RSP_WOFFSET },
	{"ldv",		"RtRo,i7(s)",	MIPS_RSP_LWC2(0x03),		MA_RSP,		MO_RSP_DWOFFSET },
	{"ldv",		"RtRo,(s)",		MIPS_RSP_LWC2(0x03),		MA_RSP,		MO_RSP_DWOFFSET },
	{"lqv",		"RtRo,i7(s)",	MIPS_RSP_LWC2(0x04),		MA_RSP,		MO_RSP_QWOFFSET },
	{"lqv",		"RtRo,(s)",		MIPS_RSP_LWC2(0x04),		MA_RSP,		MO_RSP_QWOFFSET },
	{"lrv",		"RtRo,i7(s)",	MIPS_RSP_LWC2(0x05),		MA_RSP,		MO_RSP_QWOFFSET },
	{"lrv",		"RtRo,(s)",		MIPS_RSP_LWC2(0x05),		MA_RSP,		MO_RSP_QWOFFSET },
	{"lpv",		"RtRo,i7(s)",	MIPS_RSP_LWC2(0x06),		MA_RSP,		MO_RSP_DWOFFSET },
	{"lpv",		"RtRo,(s)",		MIPS_RSP_LWC2(0x06),		MA_RSP,		MO_RSP_DWOFFSET },
	{"luv",		"RtRo,i7(s)",	MIPS_RSP_LWC2(0x07),		MA_RSP,		MO_RSP_DWOFFSET },
	{"luv",		"RtRo,(s)",		MIPS_RSP_LWC2(0x07),		MA_RSP,		MO_RSP_DWOFFSET },
	{"lhv",		"RtRo,i7(s)",	MIPS_RSP_LWC2(0x08),		MA_RSP,		MO_RSP_QWOFFSET },
	{"lhv",		"RtRo,(s)",		MIPS_RSP_LWC2(0x08),		MA_RSP,		MO_RSP_QWOFFSET },
	{"lfv",		"RtRo,i7(s)",	MIPS_RSP_LWC2(0x09),		MA_RSP,		MO_RSP_QWOFFSET },
	{"lfv",		"RtRo,(s)",		MIPS_RSP_LWC2(0x09),		MA_RSP,		MO_RSP_QWOFFSET },
	{"ltv",		"RtRo,i7(s)",	MIPS_RSP_LWC2(0x0b),		MA_RSP,		MO_RSP_QWOFFSET },
	{"ltv",		"RtRo,(s)",		MIPS_RSP_LWC2(0x0b),		MA_RSP,		MO_RSP_QWOFFSET },

//     31---------26--------------------15-------11--------------------0
//     |=      SWC2|                    |   rd    |                    |
//     -----6----------------------5------------------------------------
//     |--000--|--001--|--010--|--011--|--100--|--101--|--110--|--111--| lo
//  00 |  SBV  |  SSV  |  SLV  |  SDV  |  SQV  |  SRV  |  SPV  |  SUV  | 00..07
//  01 |  SHV  |  SFV  |  SWV  |  STV  |  ---  |  ---  |  ---  |  ---  | 08..0F
//  10 |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  | 10..17
//  11 |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  |  ---  | 18..1F
//  hi |-------|-------|-------|-------|-------|-------|-------|-------|
	{"sbv",		"RtRo,i7(s)",	MIPS_RSP_SWC2(0x00),		MA_RSP,		0 },
	{"sbv",		"RtRo,(s)",		MIPS_RSP_SWC2(0x00),		MA_RSP,		0 },
	{"ssv",		"RtRo,i7(s)",	MIPS_RSP_SWC2(0x01),		MA_RSP,		MO_RSP_HWOFFSET },
	{"ssv",		"RtRo,(s)",		MIPS_RSP_SWC2(0x01),		MA_RSP,		MO_RSP_HWOFFSET },
	{"slv",		"RtRo,i7(s)",	MIPS_RSP_SWC2(0x02),		MA_RSP,		MO_RSP_WOFFSET },
	{"slv",		"RtRo,(s)",		MIPS_RSP_SWC2(0x02),		MA_RSP,		MO_RSP_WOFFSET },
	{"sdv",		"RtRo,i7(s)",	MIPS_RSP_SWC2(0x03),		MA_RSP,		MO_RSP_DWOFFSET },
	{"sdv",		"RtRo,(s)",		MIPS_RSP_SWC2(0x03),		MA_RSP,		MO_RSP_DWOFFSET },
	{"sqv",		"RtRo,i7(s)",	MIPS_RSP_SWC2(0x04),		MA_RSP,		MO_RSP_QWOFFSET },
	{"sqv",		"RtRo,(s)",		MIPS_RSP_SWC2(0x04),		MA_RSP,		MO_RSP_QWOFFSET },
	{"srv",		"RtRo,i7(s)",	MIPS_RSP_SWC2(0x05),		MA_RSP,		MO_RSP_QWOFFSET },
	{"srv",		"RtRo,(s)",		MIPS_RSP_SWC2(0x05),		MA_RSP,		MO_RSP_QWOFFSET },
	{"spv",		"RtRo,i7(s)",	MIPS_RSP_SWC2(0x06),		MA_RSP,		MO_RSP_DWOFFSET },
	{"spv",		"RtRo,(s)",		MIPS_RSP_SWC2(0x06),		MA_RSP,		MO_RSP_DWOFFSET },
	{"suv",		"RtRo,i7(s)",	MIPS_RSP_SWC2(0x07),		MA_RSP,		MO_RSP_DWOFFSET },
	{"suv",		"RtRo,(s)",		MIPS_RSP_SWC2(0x07),		MA_RSP,		MO_RSP_DWOFFSET },
	{"shv",		"RtRo,i7(s)",	MIPS_RSP_SWC2(0x08),		MA_RSP,		MO_RSP_QWOFFSET },
	{"shv",		"RtRo,(s)",		MIPS_RSP_SWC2(0x08),		MA_RSP,		MO_RSP_QWOFFSET },
	{"sfv",		"RtRo,i7(s)",	MIPS_RSP_SWC2(0x09),		MA_RSP,		MO_RSP_QWOFFSET },
	{"sfv",		"RtRo,(s)",		MIPS_RSP_SWC2(0x09),		MA_RSP,		MO_RSP_QWOFFSET },
	{"swv",		"RtRo,i7(s)",	MIPS_RSP_SWC2(0x0a),		MA_RSP,		MO_RSP_QWOFFSET },
	{"swv",		"RtRo,(s)",		MIPS_RSP_SWC2(0x0a),		MA_RSP,		MO_RSP_QWOFFSET },
	{"stv",		"RtRo,i7(s)",	MIPS_RSP_SWC2(0x0b),		MA_RSP,		MO_RSP_QWOFFSET },
	{"stv",		"RtRo,(s)",		MIPS_RSP_SWC2(0x0b),		MA_RSP,		MO_RSP_QWOFFSET },
	// END
	{ nullptr,	nullptr,		0,							0,			0 },
};

const MipsArchDefinition mipsArchs[] = {
	// MARCH_PSX
	{ "PSX",		MA_MIPS1|MA_PSX,					MA_EXPSX,	0 },
	// MARCH_N64
	{ "N64",		MA_MIPS1|MA_MIPS2|MA_MIPS3,			MA_EXN64,	MO_64BIT|MO_FPU|MO_DFPU },
	// MARCH_PS2
	{ "PS2",		MA_MIPS1|MA_MIPS2|MA_MIPS3|MA_PS2,	MA_EXPS2,	MO_64BIT|MO_FPU },
	// MARCH_PSP
	{ "PSP",		MA_MIPS1|MA_MIPS2|MA_MIPS3|MA_PSP,	MA_EXPSP,	MO_FPU },
	// MARCH_RSP
	{ "RSP",		MA_MIPS1|MA_RSP,					MA_EXRSP,	0 },
	// MARCH_INVALID
	{ "Invalid",	0,									0,			0 },
};

#pragma region "File: Parser/ExpressionParser.h"

class Expression;
class Tokenizer;

Expression parseExpression(Tokenizer& tokenizer, bool inUnknownOrFalseBlock);

#pragma region "File: Core/ExpressionFunctionHandler.cpp"


ExpressionFunctionHandler &ExpressionFunctionHandler::instance()
{
	static ExpressionFunctionHandler handler;
	return handler;
}

ExpressionFunctionHandler::ExpressionFunctionHandler()
{
}

std::optional<ExpressionFunctionHandle> ExpressionFunctionHandler::find(const Identifier &name) const
{
	auto it = entries.find(name);
	return it != entries.end() ? std::make_optional(ExpressionFunctionHandle(it->second)) : std::nullopt;
}

void ExpressionFunctionHandler::reset()
{
	entries.clear();
	architectureFunctions.clear();
}

void ExpressionFunctionHandler::updateArchitecture()
{
	// remove functions of previous architecture
	for (const auto &name : architectureFunctions)
	{
		entries.erase(name);
	}

	architectureFunctions.clear();

	// Add functions of current architecture. Remember functions that were added
	registeringArchitecture = true;
	Architecture::current().registerExpressionFunctions(*this);
	registeringArchitecture = false;
}

bool ExpressionFunctionHandler::registerEntry(const Identifier &name, Entry entry)
{
	bool result = entries.emplace(name, std::move(entry)).second;

	if (result && registeringArchitecture)
		architectureFunctions.push_back(name);

	return result;
}

bool ExpressionFunctionHandler::addFunction(const Identifier &name, ExpressionFunction functor, size_t minParams, size_t maxParams, ExpFuncSafety safety)
{
	auto executor = [name, functor](const std::vector<std::unique_ptr<ExpressionInternal>> &parameters) -> ExpressionValue
	{
		// evaluate parameters
		std::vector<ExpressionValue> params;
		params.reserve(parameters.size());

		for (size_t i = 0; i < parameters.size(); i++)
		{
			ExpressionValue result = parameters[i]->evaluate();
			if (!result.isValid())
			{
				Logger::queueError(Logger::Error, "%s: Invalid parameter %d", name, i+1);
				return result;
			}

			params.push_back(result);
		}

		// execute
		return functor(name, params);
	};

	return registerEntry(name, Entry{std::move(executor), minParams, maxParams, safety});
};

bool ExpressionFunctionHandler::addLabelFunction(const Identifier &name, ExpressionLabelFunction functor, size_t minParams, size_t maxParams, ExpFuncSafety safety)
{
	auto executor = [name, functor](const std::vector<std::unique_ptr<ExpressionInternal>> &parameters) -> ExpressionValue
	{
		// evaluate parameters
		std::vector<std::shared_ptr<Label>> params;
		params.reserve(parameters.size());

		for (size_t i = 0; i < parameters.size(); i++)
		{
			ExpressionInternal *exp = parameters[i].get();
			if (!exp || !exp->isIdentifier())
			{
				Logger::queueError(Logger::Error, "%s: Invalid parameter %d, expecting identifier", name, i+1);
				return {};
			}

			const Identifier& name = exp->getIdentifier();
			std::shared_ptr<Label> label = Global.symbolTable.getLabel(name, exp->getFileNum(), exp->getSection());
			params.push_back(label);
		}

		// execute
		return functor(name, params);
	};

	return registerEntry(name, Entry{std::move(executor), minParams, maxParams, safety});
};

bool ExpressionFunctionHandler::addUserFunction(const Identifier &name, const std::vector<Identifier> &parameters, const std::vector<Token> &content)
{
	// Executor: Evaluate parameters and instantiate function content with parameter substitutions
	auto executor = [functionName=name, parameterNames=parameters, content](const std::vector<std::unique_ptr<ExpressionInternal>> &parameters) -> ExpressionValue
	{
		// evaluate parameters
		std::vector<ExpressionValue> params;
		params.reserve(parameters.size());

		for (size_t i = 0; i < parameters.size(); i++)
		{
			ExpressionValue result = parameters[i]->evaluate();
			if (!result.isValid())
			{
				Logger::queueError(Logger::Error, "%s: Invalid parameter %d", functionName, i+1);
				return result;
			}

			params.push_back(result);
		}

		// instantiate
		TokenStreamTokenizer tok;
		tok.init(content);

		for (size_t i = 0; i < parameters.size(); ++i)
		{
			const auto &paramName = parameterNames[i];
			const auto &paramValue = params[i];

			switch (paramValue.type)
			{
			case ExpressionValueType::Float:
				tok.registerReplacementFloat(paramName, paramValue.floatValue);
				break;
			case ExpressionValueType::String:
				tok.registerReplacementString(paramName, paramValue.strValue.string());
				break;
			case ExpressionValueType::Integer:
				tok.registerReplacementInteger(paramName, paramValue.intValue);
				break;
			case ExpressionValueType::Invalid: // will not occur, invalid results are caught above
				break;
			}
		}

		Expression result = parseExpression(tok, false);
		if (!result.isLoaded())
		{
			Logger::queueError(Logger::Error, "%s: Failed to parse user function expression", functionName);
			return {};
		}

		if (!tok.atEnd())
		{
			Logger::queueError(Logger::Error, "%s: Unconsumed tokens after parsing user function expresion", functionName);
			return {};
		}

		// evaluate expression
		return result.evaluate();
	};

	return registerEntry(name, Entry{std::move(executor), parameters.size(), parameters.size(), ExpFuncSafety::Unsafe});
}


ExpressionFunctionHandle::ExpressionFunctionHandle(const ExpressionFunctionHandler::Entry &entry) :
	impl(entry)
{
}

size_t ExpressionFunctionHandle::minParams() const
{
	return impl.minParams;
}

size_t ExpressionFunctionHandle::maxParams() const
{
	return impl.maxParams;
}

ExpFuncSafety ExpressionFunctionHandle::safety() const
{
	return impl.safety;
}

ExpressionValue ExpressionFunctionHandle::execute(const std::vector<std::unique_ptr<ExpressionInternal> > &parameters) const
{
	if (parameters.size() < impl.minParams || parameters.size() > impl.maxParams)
		return {};

	return impl.f(parameters);
}

#pragma region "File: Archs/MIPS/PsxRelocator.h"


#include <memory>
#include <string>
#include <vector>

class Label;
class MipsElfRelocator;

enum class PsxRelocationType { WordLiteral, UpperImmediate, LowerImmediate, FunctionCall };
enum class PsxRelocationRefType { SymblId, SegmentOffset };

struct PsxRelocation
{
	PsxRelocationType type;
	PsxRelocationRefType refType;
	int segmentOffset = 0;
	int referenceId = 0;
	int referencePos = 0;
	int relativeOffset = 0;
	int filePos = 0;
};

struct PsxSegment
{
	std::string name;
	int id;
	ByteArray data;
	std::vector<PsxRelocation> relocations;
};


enum class PsxSymbolType { Internal, InternalID, External, BSS, Function };

struct PsxSymbol
{
	PsxSymbolType type;
	std::string name;
	int segment;
	int offset;
	int id;
	int size;
	std::shared_ptr<Label> label;
};

struct PsxRelocatorFile
{
	std::string name;
	std::vector<PsxSegment> segments;
	std::vector<PsxSymbol> symbols;
};

class PsxRelocator
{
public:
	bool init(const fs::path& inputName);
	bool relocate(int& memoryAddress);
	bool hasDataChanged() { return dataChanged; };
	const ByteArray& getData() const { return outputData; };
	void writeSymbols(SymbolData& symData) const;
private:
	size_t loadString(ByteArray& data, size_t pos, std::string& dest);
	bool parseObject(ByteArray data, PsxRelocatorFile& dest);
	bool relocateFile(PsxRelocatorFile& file, int& relocationAddress);

	ByteArray outputData;
	std::vector<PsxRelocatorFile> files;
	MipsElfRelocator* reloc;
	bool dataChanged;
};

class DirectivePsxObjImport: public CAssemblerCommand
{
public:
	DirectivePsxObjImport(const fs::path& fileName);
	~DirectivePsxObjImport() { };
	bool Validate(const ValidateState &state) override;
	void Encode() const override;
	void writeTempData(TempData& tempData) const override { };
	void writeSymData(SymbolData& symData) const override;
	inline bool isSuccessfullyImported() { return success; };
private:
	bool success;
	PsxRelocator rel;
};

#pragma region "File: Commands/CDirectiveFile.h"


class AssemblerFile;
class GenericAssemblerFile;

class CDirectiveFile: public CAssemblerCommand
{
public:
	enum class Type { Invalid, Open, Create, Copy, Close };

	CDirectiveFile();
	void initOpen(const fs::path& fileName, int64_t memory);
	void initCreate(const fs::path& fileName, int64_t memory);
	void initCopy(const fs::path& inputName, const fs::path& outputName, int64_t memory);
	void initClose();

	bool Validate(const ValidateState &state) override;
	void Encode() const override;
	void writeTempData(TempData& tempData) const override;
	void writeSymData(SymbolData& symData) const override;
private:
	Type type;
	int64_t virtualAddress;
	std::shared_ptr<GenericAssemblerFile> file;
	std::shared_ptr<AssemblerFile> closeFile;
};

class CDirectivePosition: public CAssemblerCommand
{
public:
	enum Type { Physical, Virtual };
	CDirectivePosition(Expression value, Type type);
	bool Validate(const ValidateState &state) override;
	void Encode() const override;
	void writeTempData(TempData& tempData) const override;
	void writeSymData(SymbolData& symData) const override { };
private:
	void exec() const;
	Expression expression;
	Type type;
	int64_t position;
	int64_t virtualAddress;
};

class CDirectiveIncbin: public CAssemblerCommand
{
public:
	CDirectiveIncbin(const fs::path& fileName);
	void setStart(Expression& exp) { startExpression = exp; };
	void setSize(Expression& exp) { sizeExpression = exp; };

	bool Validate(const ValidateState &state) override;
	void Encode() const override;
	void writeTempData(TempData& tempData) const override;
	void writeSymData(SymbolData& symData) const override;
private:
	fs::path fileName;
	int64_t fileSize;

	Expression startExpression;
	Expression sizeExpression;
	int64_t size;
	int64_t start;
	int64_t virtualAddress;
};

class CDirectiveAlignFill: public CAssemblerCommand
{
public:
	enum Mode { AlignPhysical, AlignVirtual, Fill };

	CDirectiveAlignFill(int64_t value, Mode mode);
	CDirectiveAlignFill(Expression& value, Mode mode);
	CDirectiveAlignFill(Expression& value, Expression& fillValue, Mode mode);
	bool Validate(const ValidateState &state) override;
	void Encode() const override;
	void writeTempData(TempData& tempData) const override;
	void writeSymData(SymbolData& symData) const override;
private:
	Mode mode;
	Expression valueExpression;
	Expression fillExpression;
	int64_t value;
	int64_t finalSize;
	int8_t fillByte;
	int64_t virtualAddress;
};

class CDirectiveSkip: public CAssemblerCommand
{
public:
	CDirectiveSkip(Expression& value);
	bool Validate(const ValidateState &state) override;
	void Encode() const override;
	void writeTempData(TempData& tempData) const override;
	void writeSymData(SymbolData& symData) const override { };
private:
	Expression expression;
	int64_t value;
	int64_t virtualAddress;
};

class CDirectiveHeaderSize: public CAssemblerCommand
{
public:
	CDirectiveHeaderSize(Expression expression);
	bool Validate(const ValidateState &state) override;
	void Encode() const override;
	void writeTempData(TempData& tempData) const override;
	void writeSymData(SymbolData& symData) const override { };
private:
	void exec() const;
	Expression expression;
	int64_t headerSize;
	int64_t virtualAddress;
};

class DirectiveObjImport: public CAssemblerCommand
{
public:
	DirectiveObjImport(const fs::path& inputName);
	DirectiveObjImport(const fs::path& inputName, const Identifier& ctorName);
	~DirectiveObjImport() { };
	bool Validate(const ValidateState &state) override;
	void Encode() const override;
	void writeTempData(TempData& tempData) const override;
	void writeSymData(SymbolData& symData) const override;
	inline bool isSuccessfullyImported() { return success; };
private:
	bool success;
	ElfRelocator rel;
	std::unique_ptr<CAssemblerCommand> ctor;
};

#pragma region "File: Archs/MIPS/MipsParser.cpp"


#define CHECK(exp) if (!(exp)) return false;

const MipsRegisterDescriptor mipsRegisters[] = {
	{ "r0", 0 },  { "zero", 0},  { "at", 1 },  { "r1", 1 },
	{ "v0", 2 },  { "r2", 2 },   { "v1", 3 },  { "r3", 3 },
	{ "a0", 4 },  { "r4", 4 },   { "a1", 5 },  { "r5", 5 },
	{ "a2", 6 },  { "r6", 6 },   { "a3", 7 },  { "r7", 7 },
	{ "t0", 8 },  { "r8", 8 },   { "t1", 9 },  { "r9", 9 },
	{ "t2", 10 }, { "r10", 10 }, { "t3", 11 }, { "r11", 11 },
	{ "t4", 12 }, { "r12", 12 }, { "t5", 13 }, { "r13", 13 },
	{ "t6", 14 }, { "r14", 14 }, { "t7", 15 }, { "r15", 15 },
	{ "s0", 16 }, { "r16", 16 }, { "s1", 17 }, { "r17", 17 },
	{ "s2", 18 }, { "r18", 18 }, { "s3", 19 }, { "r19", 19 },
	{ "s4", 20 }, { "r20", 20 }, { "s5", 21 }, { "r21", 21 },
	{ "s6", 22 }, { "r22", 22 }, { "s7", 23 }, { "r23", 23 },
	{ "t8", 24 }, { "r24", 24 }, { "t9", 25 }, { "r25", 25 },
	{ "k0", 26 }, { "r26", 26 }, { "k1", 27 }, { "r27", 27 },
	{ "gp", 28 }, { "r28", 28 }, { "sp", 29 }, { "r29", 29 },
	{ "fp", 30 }, { "r30", 30 }, { "ra", 31 }, { "r31", 31 },
	{ "s8", 30 },
};

const MipsRegisterDescriptor mipsFloatRegisters[] = {
	{ "f0", 0 },   { "f1", 1 },   { "f2", 2 },   { "f3", 3 },
	{ "f4", 4 },   { "f5", 5 },   { "f6", 6 },   { "f7", 7 },
	{ "f8", 8 },   { "f9", 9 },   { "f00", 0 },  { "f01", 1 },
	{ "f02", 2 },  { "f03", 3 },  { "f04", 4 },  { "f05", 5 },
	{ "f06", 6 },  { "f07", 7 },  { "f08", 8 },  { "f09", 9 },
	{ "f10", 10 }, { "f11", 11 }, { "f12", 12 }, { "f13", 13 },
	{ "f14", 14 }, { "f15", 15 }, { "f16", 16 }, { "f17", 17 },
	{ "f18", 18 }, { "f19", 19 }, { "f20", 20 }, { "f21", 21 },
	{ "f22", 22 }, { "f23", 23 }, { "f24", 24 }, { "f25", 25 },
	{ "f26", 26 }, { "f27", 27 }, { "f28", 28 }, { "f29", 29 },
	{ "f30", 30 }, { "f31", 31 },
};

const MipsRegisterDescriptor mipsFpuControlRegisters[] = {
	{ "fir", 0 }, { "fcr0", 0 }, { "fcsr", 31 }, { "fcr31", 31 },
};

const MipsRegisterDescriptor mipsCop0Registers[] = {
	{ "index", 0},      { "random", 1 },    { "entrylo", 2 },
	{ "entrylo0", 2 },  { "entrylo1", 3 },  { "context", 4 },
	{ "pagemask", 5 },  { "wired", 6 },     { "badvaddr", 8 },
	{ "count", 9 },     { "entryhi", 10 },  { "compare", 11 },
	{ "status", 12 },   { "sr", 12 },       { "cause", 13 },
	{ "epc", 14 },      { "prid", 15 },     { "config", 16 },
	{ "lladdr", 17 },   { "watchlo", 18 },  { "watchhi", 19 },
	{ "xcontext", 20 }, { "badpaddr", 23 }, { "ecc", 26 },
	{ "perr", 26},      { "cacheerr", 27 }, { "taglo", 28 },
	{ "taghi", 29 },    { "errorepc", 30 },
};

const MipsRegisterDescriptor mipsPs2Cop2FpRegisters[] = {
	{ "vf0", 0 },   { "vf1", 1 },   { "vf2", 2 },   { "vf3", 3 },
	{ "vf4", 4 },   { "vf5", 5 },   { "vf6", 6 },   { "vf7", 7 },
	{ "vf8", 8 },   { "vf9", 9 },   { "vf00", 0 },  { "vf01", 1 },
	{ "vf02", 2 },  { "vf03", 3 },  { "vf04", 4 },  { "vf05", 5 },
	{ "vf06", 6 },  { "vf07", 7 },  { "vf08", 8 },  { "vf09", 9 },
	{ "vf10", 10 }, { "vf11", 11 }, { "vf12", 12 }, { "vf13", 13 },
	{ "vf14", 14 }, { "vf15", 15 }, { "vf16", 16 }, { "vf17", 17 },
	{ "vf18", 18 }, { "vf19", 19 }, { "vf20", 20 }, { "vf21", 21 },
	{ "vf22", 22 }, { "vf23", 23 }, { "vf24", 24 }, { "vf25", 25 },
	{ "vf26", 26 }, { "vf27", 27 }, { "vf28", 28 }, { "vf29", 29 },
	{ "vf30", 30 }, { "vf31", 31 },
};

const MipsRegisterDescriptor mipsPsxCop2DataRegisters[] = {
	{ "vxy0", 0 },  { "vz0", 1 },   { "vxy1", 2 },  { "vz1", 3 },
	{ "vxy2", 4 },  { "vz2", 5 },   { "rgbc", 6 },  { "otz", 7 },
	{ "ir0", 8 },   { "ir1", 9 },   { "ir2", 10 },  { "ir3", 11 },
	{ "sxy0", 12 }, { "sxy1", 13 }, { "sxy2", 14 }, { "sxyp", 15 },
	{ "sz0", 16 },  { "sz1", 17 },  { "sz2", 18 },  { "sz3", 19 },
	{ "rgb0", 20 }, { "rgb1", 21 }, { "rgb2", 22 }, { "res1", 23 },
	{ "mac0", 24 }, { "mac1", 25 }, { "mac2", 26 }, { "mac3", 27 },
	{ "irgb", 28 }, { "orgb", 29 }, { "lzcs", 30 }, { "lzcr", 31 },
};

const MipsRegisterDescriptor mipsPsxCop2ControlRegisters[] = {
	{ "rt0", 0 },   { "rt1", 1 },   { "rt2", 2 },   { "rt3", 3 },
	{ "rt4", 4 },   { "trx", 5 },   { "try", 6 },   { "trz", 7 },
	{ "llm0", 8 },  { "llm1", 9 },  { "llm2", 10 }, { "llm3", 11 },
	{ "llm4", 12 }, { "rbk", 13 },  { "gbk", 14 },  { "bbk", 15 },
	{ "lcm0", 16 }, { "lcm1", 17 }, { "lcm2", 18 }, { "lcm3", 19 },
	{ "lcm4", 20 }, { "rfc", 21 },  { "gfc", 22 },  { "bfc", 23 },
	{ "ofx", 24 },  { "ofy", 25 },  { "h", 26 },    { "dqa", 27 },
	{ "dqb", 28 },  { "zsf3", 29 }, { "zsf4", 30 }, { "flag", 31 },
};

const MipsRegisterDescriptor mipsRspCop0Registers[] = {
	{ "sp_mem_addr", 0 }, { "sp_dram_addr", 1 }, { "sp_rd_len", 2 },
	{ "sp_wr_len", 3 },   { "sp_status", 4 },    { "sp_dma_full", 5 },
	{ "sp_dma_busy", 6 }, { "sp_semaphore", 7 }, { "dpc_start", 8 },
	{ "dpc_end", 9 },     { "dpc_current", 10 }, { "dpc_status", 11 },
	{ "dpc_clock", 12 },  { "dpc_bufbusy", 13 }, { "dpc_pipebusy", 14 },
	{ "dpc_tmem", 15 },
};

const MipsRegisterDescriptor mipsRspVectorControlRegisters[] = {
	{ "vco", 0 }, { "vcc", 1 }, { "vce", 2 },
};

const MipsRegisterDescriptor mipsRspVectorRegisters[] = {
	{ "v0", 0 },   { "v1", 1 },   { "v2", 2 },   { "v3", 3 },
	{ "v4", 4 },   { "v5", 5 },   { "v6", 6 },   { "v7", 7 },
	{ "v8", 8 },   { "v9", 9 },   { "v00", 0 },  { "v01", 1 },
	{ "v02", 2 },  { "v03", 3 },  { "v04", 4 },  { "v05", 5 },
	{ "v06", 6 },  { "v07", 7 },  { "v08", 8 },  { "v09", 9 },
	{ "v10", 10 }, { "v11", 11 }, { "v12", 12 }, { "v13", 13 },
	{ "v14", 14 }, { "v15", 15 }, { "v16", 16 }, { "v17", 17 },
	{ "v18", 18 }, { "v19", 19 }, { "v20", 20 }, { "v21", 21 },
	{ "v22", 22 }, { "v23", 23 }, { "v24", 24 }, { "v25", 25 },
	{ "v26", 26 }, { "v27", 27 }, { "v28", 28 }, { "v29", 29 },
	{ "v30", 30 }, { "v31", 31 },
};

std::unique_ptr<CAssemblerCommand> parseDirectiveResetDelay(Parser& parser, int flags)
{
	Mips.SetIgnoreDelay(true);
	return std::make_unique<DummyCommand>();
}

std::unique_ptr<CAssemblerCommand> parseDirectiveFixLoadDelay(Parser& parser, int flags)
{
	Mips.SetFixLoadDelay(true);
	return std::make_unique<DummyCommand>();
}

std::unique_ptr<CAssemblerCommand> parseDirectiveLoadElf(Parser& parser, int flags)
{
	std::vector<Expression> list;
	if (!parser.parseExpressionList(list,1,2))
		return nullptr;

	StringLiteral inputName, outputName;
	if (!list[0].evaluateString(inputName,true))
		return nullptr;

	if (list.size() == 2)
	{
		if (!list[1].evaluateString(outputName,true))
			return nullptr;
		return std::make_unique<DirectiveLoadMipsElf>(inputName.path(),outputName.path());
	} else {
		return std::make_unique<DirectiveLoadMipsElf>(inputName.path());
	}
}

std::unique_ptr<CAssemblerCommand> parseDirectiveImportObj(Parser& parser, int flags)
{
	const Token& start = parser.peekToken();

	std::vector<Expression> list;
	if (!parser.parseExpressionList(list,1,2))
		return nullptr;

	StringLiteral inputName;
	if (!list[0].evaluateString(inputName,true))
		return nullptr;

	if (list.size() == 2)
	{
		Identifier ctorName;
		if (!list[1].evaluateIdentifier(ctorName))
			return nullptr;

		if (Mips.GetVersion() == MARCH_PSX)
		{
			auto attempt = std::make_unique<DirectiveObjImport>(inputName.path(),ctorName);
			if (!attempt->isSuccessfullyImported())
			{
				parser.printError(start, "Constructor not supported for non-ELF PSX libraries");
				return std::make_unique<InvalidCommand>();
			}
			else
			{
				return attempt;
			}
		}

		return std::make_unique<DirectiveObjImport>(inputName.path(),ctorName);
	}

	if (Mips.GetVersion() == MARCH_PSX)
	{
		// Fallback to ELF, if the PSX library importer fails.
		auto psxLib = std::make_unique<DirectivePsxObjImport>(inputName.path());
		if (!psxLib->isSuccessfullyImported())
			return std::make_unique<DirectiveObjImport>(inputName.path());
		else
			return psxLib;
	}
	else
	{
		return std::make_unique<DirectiveObjImport>(inputName.path());
	}
}

const DirectiveMap mipsDirectives = {
	{ ".resetdelay",		{ &parseDirectiveResetDelay,	0 } },
	{ ".fixloaddelay",		{ &parseDirectiveFixLoadDelay,	0 } },
	{ ".loadelf",			{ &parseDirectiveLoadElf,		0 } },
	{ ".importobj",		{ &parseDirectiveImportObj,		0 } },
	{ ".importlib",		{ &parseDirectiveImportObj,		0 } },
};

std::unique_ptr<CAssemblerCommand> MipsParser::parseDirective(Parser& parser)
{
	return parser.parseDirective(mipsDirectives);
}

bool MipsParser::parseRegisterNumber(Parser& parser, MipsRegisterValue& dest, int numValues)
{
	// check for $0 and $1
	if (parser.peekToken().type == TokenType::Dollar)
	{
		const Token& number = parser.peekToken(1);
		if (number.type == TokenType::Integer && number.intValue() < numValues)
		{
			dest.name = Identifier(tfm::format("$%d", number.intValue()));
			dest.num = (int) number.intValue();

			parser.eatTokens(2);
			return true;
		}
	}

	return false;
}

bool MipsParser::parseRegisterTable(Parser& parser, MipsRegisterValue& dest, const MipsRegisterDescriptor* table, size_t count)
{
	int offset = 0;
	bool hasDollar = parser.peekToken().type == TokenType::Dollar;
	if (hasDollar)
		offset = 1;

	const Token &token = parser.peekToken(offset);

	if (token.type != TokenType::Identifier)
		return false;

	const auto &identifier = token.identifierValue();
	for (size_t i = 0; i < count; i++)
	{
		if (identifier == table[i].name)
		{
			dest.name = identifier;
			dest.num = table[i].num;
			parser.eatTokens(hasDollar ? 2 : 1);
			return true;
		}
	}

	return false;
}

bool MipsParser::parseRegister(Parser& parser, MipsRegisterValue& dest)
{
	dest.type = MipsRegisterType::Normal;

	if (parseRegisterNumber(parser, dest, 32))
		return true;

	return parseRegisterTable(parser,dest,mipsRegisters, std::size(mipsRegisters));
}

bool MipsParser::parseFpuRegister(Parser& parser, MipsRegisterValue& dest)
{
	dest.type = MipsRegisterType::Float;

	if (parseRegisterNumber(parser, dest, 32))
		return true;

	return parseRegisterTable(parser,dest,mipsFloatRegisters, std::size(mipsFloatRegisters));
}

bool MipsParser::parseFpuControlRegister(Parser& parser, MipsRegisterValue& dest)
{
	dest.type = MipsRegisterType::FpuControl;

	if (parseRegisterNumber(parser, dest, 32))
		return true;

	return parseRegisterTable(parser,dest,mipsFpuControlRegisters, std::size(mipsFpuControlRegisters));
}

bool MipsParser::parseCop0Register(Parser& parser, MipsRegisterValue& dest)
{
	dest.type = MipsRegisterType::Cop0;

	if (parseRegisterNumber(parser, dest, 32))
		return true;

	return parseRegisterTable(parser,dest,mipsCop0Registers, std::size(mipsCop0Registers));
}

bool MipsParser::parsePs2Cop2Register(Parser& parser, MipsRegisterValue& dest)
{
	dest.type = MipsRegisterType::Ps2Cop2;
	return parseRegisterTable(parser,dest,mipsPs2Cop2FpRegisters, std::size(mipsPs2Cop2FpRegisters));
}

bool MipsParser::parsePsxCop2DataRegister(Parser& parser, MipsRegisterValue& dest)
{
	dest.type = MipsRegisterType::PsxCop2Data;

	if (parseRegisterNumber(parser, dest, 32))
		return true;

	return parseRegisterTable(parser,dest,mipsPsxCop2DataRegisters, std::size(mipsPsxCop2DataRegisters));
}

bool MipsParser::parsePsxCop2ControlRegister(Parser& parser, MipsRegisterValue& dest)
{
	dest.type = MipsRegisterType::PsxCop2Control;

	if (parseRegisterNumber(parser, dest, 32))
		return true;

	return parseRegisterTable(parser,dest,mipsPsxCop2ControlRegisters, std::size(mipsPsxCop2ControlRegisters));
}

bool MipsParser::parseRspCop0Register(Parser& parser, MipsRegisterValue& dest)
{
	dest.type = MipsRegisterType::RspCop0;

	if (parseRegisterNumber(parser, dest, 32))
		return true;

	return parseRegisterTable(parser,dest,mipsRspCop0Registers, std::size(mipsRspCop0Registers));
}

bool MipsParser::parseRspVectorControlRegister(Parser& parser, MipsRegisterValue& dest)
{
	dest.type = MipsRegisterType::RspVectorControl;

	if (parseRegisterNumber(parser, dest, 32))
		return true;

	return parseRegisterTable(parser,dest,mipsRspVectorControlRegisters, std::size(mipsRspVectorControlRegisters));
}

bool MipsParser::parseRspVectorRegister(Parser& parser, MipsRegisterValue& dest)
{
	dest.type = MipsRegisterType::RspVector;
	return parseRegisterTable(parser,dest,mipsRspVectorRegisters, std::size(mipsRspVectorRegisters));
}

bool MipsParser::parseRspVectorElement(Parser& parser, MipsRegisterValue& dest)
{
	dest.type = MipsRegisterType::RspVectorElement;

	if (parser.peekToken().type == TokenType::LBrack)
	{
		static const MipsRegisterDescriptor rspElementNames[] = {
			{ "0q", 2 },  { "1q", 3 }, { "0h", 4 },  { "1h", 5 },
			{ "2h", 6 },  { "3h", 7 }, { "0w", 8 },  { "0", 8 },
			{ "1w", 9 },  { "1", 9 },  { "2w", 10 }, { "2", 10 },
			{ "3w", 11 }, { "3", 11 }, { "4w", 12 }, { "4", 12 },
			{ "5w", 13 }, { "5", 13 }, { "6w", 14 }, { "6", 14 },
			{ "7w", 15 }, { "7", 15 },
		};

		parser.eatToken();

		if (parseRegisterNumber(parser, dest, 16))
			return parser.nextToken().type == TokenType::RBrack;

		const Token& token = parser.nextToken();

		if (token.type != TokenType::Integer && token.type != TokenType::NumberString)
			return false;

		//ignore the numerical values, just use the original text as an identifier
		std::string stringValue = token.getOriginalText();
		if (std::any_of(stringValue.begin(), stringValue.end(), iswupper))
		{
			std::transform(stringValue.begin(), stringValue.end(), stringValue.begin(), tolower);
		}

		for (size_t i = 0; i < std::size(rspElementNames); i++)
		{
			if (stringValue == rspElementNames[i].name)
			{
				dest.num = rspElementNames[i].num;
				dest.name = Identifier(rspElementNames[i].name);

				return parser.nextToken().type == TokenType::RBrack;
			}
		}

		return false;
	}

	dest.num = 0;
	dest.name = Identifier();

	return true;

}

bool MipsParser::parseRspScalarElement(Parser& parser, MipsRegisterValue& dest)
{
	dest.type = MipsRegisterType::RspScalarElement;

	if (parser.nextToken().type != TokenType::LBrack)
		return false;

	const Token &token = parser.nextToken();

	if (token.type != TokenType::Integer || token.intValue() >= 8)
		return false;

	dest.name = Identifier(tfm::format("%d", token.intValue()));
	dest.num = (int)token.intValue() + 8;

	return parser.nextToken().type == TokenType::RBrack;
}

bool MipsParser::parseRspOffsetElement(Parser& parser, MipsRegisterValue& dest)
{
	dest.type = MipsRegisterType::RspOffsetElement;

	if (parser.peekToken().type == TokenType::LBrack)
	{
		parser.eatToken();

		const Token &token = parser.nextToken();

		if (token.type != TokenType::Integer || token.intValue() >= 16)
			return false;

		dest.name = Identifier(tfm::format("%d", token.intValue()));
		dest.num = (int)token.intValue();

		return parser.nextToken().type == TokenType::RBrack;
	}

	dest.num = 0;
	dest.name = Identifier();

	return true;
}

static bool decodeDigit(char digit, int& dest)
{
	if (digit >= '0' && digit <= '9')
	{
		dest = digit-'0';
		return true;
	}
	return false;
}

bool MipsParser::parseVfpuRegister(Parser& parser, MipsRegisterValue& reg, int size)
{
	const Token& token = parser.peekToken();
	if (token.type != TokenType::Identifier || token.identifierValue().size() != 4)
		return false;

	const Identifier &identifier = token.identifierValue();

	int mtx,col,row;
	if (!decodeDigit(identifier.string()[1],mtx)) return false;
	if (!decodeDigit(identifier.string()[2],col)) return false;
	if (!decodeDigit(identifier.string()[3],row)) return false;
	int mode = tolower(identifier.string()[0]);

	if (size < 0 || size > 3)
		return false;

	if (row > 3 || col > 3 || mtx > 7)
		return false;

	reg.num = 0;
	switch (mode)
	{
	case 'r':					// transposed vector
		reg.num |= (1 << 5);
		std::swap(col,row);
		[[fallthrough]];
	case 'c':					// vector
		reg.type = MipsRegisterType::VfpuVector;

		switch (size)
		{
		case 1:	// pair
		case 3: // quad
			if (row & 1)
				return false;
			break;
		case 2:	// triple
			if (row & 2)
				return false;
			row <<= 1;
			break;
		default:
			return false;
		}
		break;
	case 's':					// single
		reg.type = MipsRegisterType::VfpuVector;

		if (size != 0)
			return false;
		break;
	case 'e':					// transposed matrix
		reg.num |= (1 << 5);
		[[fallthrough]];
	case 'm':					// matrix
		reg.type = MipsRegisterType::VfpuMatrix;

		// check size
		switch (size)
		{
		case 1:	// 2x2
		case 3:	// 4x4
			if (row & 1)
				return false;
			break;
		case 2:	// 3x3
			if (row & ~1)
				return false;
			row <<= 1;
			break;
		default:
			return false;
		}
		break;
	default:
		return false;
	}

	reg.num |= mtx << 2;
	reg.num |= col;
	reg.num |= row << 5;

	reg.name = identifier;
	parser.eatToken();
	return true;
}

bool MipsParser::parseVfpuControlRegister(Parser& parser, MipsRegisterValue& reg)
{
	static const char* vfpuCtrlNames[16] = {
		"spfx", "tpfx", "dpfx", "cc",
		"inf4", "rsv5", "rsv6", "rev",
		"rcx0", "rcx1", "rcx2", "rcx3",
		"rcx4", "rcx5", "rcx6", "rcx7",
	};

	const Token& token = parser.peekToken();

	if (token.type == TokenType::Identifier)
	{
		const auto &identifier = token.identifierValue();
		for (int i = 0; i < 16; i++)
		{
			if (identifier == vfpuCtrlNames[i])
			{
				reg.num = i;
				reg.name = identifier;

				parser.eatToken();
				return true;
			}
		}
	} else if (token.type == TokenType::Integer && token.intValue() <= 15)
	{
		reg.num = (int) token.intValue();
		reg.name = Identifier(vfpuCtrlNames[reg.num]);

		parser.eatToken();
		return true;
	}

	return false;
}

bool MipsParser::parseImmediate(Parser& parser, Expression& dest)
{
	// check for (reg) or reg sequence
	TokenizerPosition pos = parser.getTokenizer()->getPosition();

	bool hasParen = parser.peekToken().type == TokenType::LParen;
	if (hasParen)
		parser.eatToken();

	MipsRegisterValue tempValue;
	bool isRegister = parseRegister(parser,tempValue);
	parser.getTokenizer()->setPosition(pos);

	if (isRegister)
		return false;

	dest = parser.parseExpression();
	return dest.isLoaded();
}

bool MipsParser::matchSymbol(Parser& parser, char symbol)
{
	switch (symbol)
	{
	case '(':
		return parser.matchToken(TokenType::LParen);
	case ')':
		return parser.matchToken(TokenType::RParen);
	case ',':
		return parser.matchToken(TokenType::Comma);
	}

	return false;
}

bool MipsParser::parseVcstParameter(Parser& parser, int& result)
{
	static TokenSequenceParser sequenceParser;

	// initialize on first use
	if (sequenceParser.getEntryCount() == 0)
	{
		// maxfloat
		sequenceParser.addEntry(1,
			{TokenType::Identifier},
			{"maxfloat"}
		);
		// sqrt(2)
		sequenceParser.addEntry(2,
			{TokenType::Identifier, TokenType::LParen, TokenType::Integer, TokenType::RParen},
			{"sqrt", INT64_C(2)}
		);
		// sqrt(1/2)
		sequenceParser.addEntry(3,
			{TokenType::Identifier, TokenType::LParen, TokenType::Integer, TokenType::Div, TokenType::Integer, TokenType::RParen},
			{"sqrt", INT64_C(1), INT64_C(2)}
		);
		// sqrt(0.5)
		sequenceParser.addEntry(3,
			{TokenType::Identifier, TokenType::LParen, TokenType::Float, TokenType::RParen},
			{"sqrt", 0.5}
		);
		// 2/sqrt(pi)
		sequenceParser.addEntry(4,
			{TokenType::Integer, TokenType::Div, TokenType::Identifier, TokenType::LParen, TokenType::Identifier, TokenType::RParen},
			{INT64_C(2), "sqrt", "pi"}
		);
		// 2/pi
		sequenceParser.addEntry(5,
			{TokenType::Integer, TokenType::Div, TokenType::Identifier},
			{INT64_C(2), "pi"}
		);
		// 1/pi
		sequenceParser.addEntry(6,
			{TokenType::Integer, TokenType::Div, TokenType::Identifier},
			{INT64_C(1), "pi"}
		);
		// pi/4
		sequenceParser.addEntry(7,
			{TokenType::Identifier, TokenType::Div, TokenType::Integer},
			{"pi", INT64_C(4)}
		);
		// pi/2
		sequenceParser.addEntry(8,
			{TokenType::Identifier, TokenType::Div, TokenType::Integer},
			{"pi", INT64_C(2)}
		);
		// pi/6 - early because "pi" is a prefix of it
		sequenceParser.addEntry(16,
			{TokenType::Identifier, TokenType::Div, TokenType::Integer},
			{"pi", INT64_C(6)}
		);
		// pi
		sequenceParser.addEntry(9,
			{TokenType::Identifier},
			{"pi"}
		);
		// e
		sequenceParser.addEntry(10,
			{TokenType::Identifier},
			{"e"}
		);
		// log2(e)
		sequenceParser.addEntry(11,
			{TokenType::Identifier, TokenType::LParen, TokenType::Identifier, TokenType::RParen},
			{"log2", "e"}
		);
		// log10(e)
		sequenceParser.addEntry(12,
			{TokenType::Identifier, TokenType::LParen, TokenType::Identifier, TokenType::RParen},
			{"log10", "e"}
		);
		// ln(2)
		sequenceParser.addEntry(13,
			{TokenType::Identifier, TokenType::LParen, TokenType::Integer, TokenType::RParen},
			{"ln", INT64_C(2)}
		);
		// ln(10)
		sequenceParser.addEntry(14,
			{TokenType::Identifier, TokenType::LParen, TokenType::Integer, TokenType::RParen},
			{"ln", INT64_C(10)}
		);
		// 2*pi
		sequenceParser.addEntry(15,
			{TokenType::Integer, TokenType::Mult, TokenType::Identifier},
			{INT64_C(2), "pi"}
		);
		// log10(2)
		sequenceParser.addEntry(17,
			{TokenType::Identifier, TokenType::LParen, TokenType::Integer, TokenType::RParen},
			{"log10", INT64_C(2)}
		);
		// log2(10)
		sequenceParser.addEntry(18,
			{TokenType::Identifier, TokenType::LParen, TokenType::Integer, TokenType::RParen},
			{"log2", INT64_C(10)}
		);
		// sqrt(3)/2
		sequenceParser.addEntry(19,
			{TokenType::Identifier, TokenType::LParen, TokenType::Integer, TokenType::RParen, TokenType::Div, TokenType::Integer},
			{"sqrt", INT64_C(3), INT64_C(2)}
		);
	}

	return sequenceParser.parse(parser,result);
}

bool MipsParser::parseVfpuVrot(Parser& parser, int& result, int size)
{
	int sin = -1;
	int cos = -1;
	bool negSine = false;
	int sineCount = 0;

	if (parser.nextToken().type != TokenType::LBrack)
		return false;

	int numElems = size+1;
	for (int i = 0; i < numElems; i++)
	{
		const Token* tokenFinder = &parser.nextToken();

		if (i != 0)
		{
			if (tokenFinder->type != TokenType::Comma)
				return false;

			tokenFinder = &parser.nextToken();
		}

		bool isNeg = tokenFinder->type == TokenType::Minus;
		if (isNeg)
			tokenFinder = &parser.nextToken();

		const Token& token = *tokenFinder;

		if (token.type != TokenType::Identifier || token.identifierValue().size() != 1)
			return false;

		const std::string &stringValue = token.identifierValue().string();
		switch (stringValue[0])
		{
		case 's':
			// if one is negative, all have to be
			if ((!isNeg && negSine) || (isNeg && !negSine && sineCount > 0))
				return false;

			negSine = negSine || isNeg;
			sin = i;
			sineCount++;
			break;
		case 'c':
			// can't be negative, or happen twice
			if (isNeg || cos != -1)
				return false;
			cos = i;
			break;
		case '0':
			if (isNeg)
				return false;
			break;
		default:
			return false;
		}
	}

	if (parser.nextToken().type != TokenType::RBrack)
		return false;

	result = negSine ? 0x10 : 0;

	if (sin == -1 && cos == -1)
	{
		return false;
	} else if (sin == -1)
	{
		if (numElems == 4)
			return false;

		result |= cos;
		result |= ((size+1) << 2);
	} else if (cos == -1)
	{
		if (numElems == 4)
			return false;

		if (sineCount == 1)
		{
			result |= (size+1);
			result |= (sin << 2);
		} else if (sineCount == numElems)
		{
			result |= (size+1);
			result |= ((size+1) << 2);
		} else {
			return false;
		}
	} else {
		if (sineCount > 1)
		{
			if (sineCount+1 != numElems)
				return false;

			result |= cos;
			result |= (cos << 2);
		} else {
			result |= cos;
			result |= (sin << 2);
		}
	}

	return true;
}

bool MipsParser::parseVfpuCondition(Parser& parser, int& result)
{
	static const char* conditions[] = {
		"fl", "eq", "lt", "le", "tr", "ne", "ge", "gt",
		"ez", "en", "ei", "es", "nz", "nn", "ni", "ns"
	};

	const Token& token = parser.nextToken();
	if (token.type != TokenType::Identifier)
		return false;

	const Identifier &stringValue = token.identifierValue();
	for (size_t i = 0; i <  std::size(conditions); i++)
	{
		if (stringValue == conditions[i])
		{
			result = (int)i;
			return true;
		}
	}

	return false;
}

bool MipsParser::parseVpfxsParameter(Parser& parser, int& result)
{
	static TokenSequenceParser sequenceParser;

	// initialize on first use
	if (sequenceParser.getEntryCount() == 0)
	{
		// 0
		sequenceParser.addEntry(0, {TokenType::Integer}, {INT64_C(0)} );
		// 1
		sequenceParser.addEntry(1, {TokenType::Integer}, {INT64_C(1)} );
		// 2
		sequenceParser.addEntry(2, {TokenType::Integer}, {INT64_C(2)} );
		// 1/2
		sequenceParser.addEntry(3, {TokenType::Integer, TokenType::Div, TokenType::Integer}, {INT64_C(1), INT64_C(2)} );
		// 3
		sequenceParser.addEntry(4, {TokenType::Integer}, {INT64_C(3)} );
		// 1/3
		sequenceParser.addEntry(5, {TokenType::Integer, TokenType::Div, TokenType::Integer}, {INT64_C(1), INT64_C(3)} );
		// 1/4
		sequenceParser.addEntry(6, {TokenType::Integer, TokenType::Div, TokenType::Integer}, {INT64_C(1), INT64_C(4)} );
		// 1/6
		sequenceParser.addEntry(7, {TokenType::Integer, TokenType::Div, TokenType::Integer}, {INT64_C(1), INT64_C(6)} );
	}

	if (parser.nextToken().type != TokenType::LBrack)
		return false;

	for (int i = 0; i < 4; i++)
	{
		const Token *tokenFinder = &parser.nextToken();

		if (i != 0)
		{
			if (tokenFinder->type != TokenType::Comma)
				return false;

			tokenFinder = &parser.nextToken();
		}

		// negation
		if (tokenFinder->type == TokenType::Minus)
		{
			result |= 1 << (16+i);
			tokenFinder = &parser.nextToken();
		}

		// abs
		bool abs = false;
		if (tokenFinder->type == TokenType::BitOr)
		{
			result |= 1 << (8+i);
			abs = true;
			tokenFinder = &parser.nextToken();
		}

		const Token& token = *tokenFinder;
		if (token.type != TokenType::Identifier)
			return false;

		// check for register
		const char* reg;
		static const char* vpfxstRegisters = "xyzw";
		const std::string &stringValue = token.identifierValue().string();
		if (stringValue.size() == 1 && (reg = strchr(vpfxstRegisters,stringValue[0])) != nullptr)
		{
			result |= (reg-vpfxstRegisters) << (i*2);

			if (abs && parser.nextToken().type != TokenType::BitOr)
				return false;

			continue;
		}

		// abs is invalid with constants
		if (abs)
			return false;

		result |= 1 << (12+i);

		int constNum = -1;
		if (!sequenceParser.parse(parser,constNum))
			return false;

		result |= (constNum & 3) << (i*2);
		if (constNum & 4)
			result |= 1 << (8+i);
	}

	return parser.nextToken().type == TokenType::RBrack;
}

bool MipsParser::parseVpfxdParameter(Parser& parser, int& result)
{
	static TokenSequenceParser sequenceParser;

	// initialize on first use
	if (sequenceParser.getEntryCount() == 0)
	{
		// 0-1
		sequenceParser.addEntry(1,
			{TokenType::Integer, TokenType::Minus, TokenType::Integer},
			{INT64_C(0), INT64_C(1)} );
		// 0-1
		sequenceParser.addEntry(-1,
			{TokenType::Integer, TokenType::Minus, TokenType::NumberString},
			{INT64_C(0), "1m"} );
		// 0:1
		sequenceParser.addEntry(1,
			{TokenType::Integer, TokenType::Colon, TokenType::Integer},
			{INT64_C(0), INT64_C(1)} );
		// 0:1
		sequenceParser.addEntry(-1,
			{TokenType::Integer, TokenType::Colon, TokenType::NumberString},
			{INT64_C(0), "1m"} );
		// -1-1
		sequenceParser.addEntry(3,
			{TokenType::Minus, TokenType::Integer, TokenType::Minus, TokenType::Integer},
			{INT64_C(1), INT64_C(1)} );
		// -1-1m
		sequenceParser.addEntry(-3,
			{TokenType::Minus, TokenType::Integer, TokenType::Minus, TokenType::NumberString},
			{INT64_C(1), "1m"} );
		// -1:1
		sequenceParser.addEntry(3,
			{TokenType::Minus, TokenType::Integer, TokenType::Colon, TokenType::Integer},
			{INT64_C(1), INT64_C(1)} );
		// -1:1m
		sequenceParser.addEntry(-3,
			{TokenType::Minus, TokenType::Integer, TokenType::Colon, TokenType::NumberString},
			{INT64_C(1), "1m"} );
	}

	for (int i = 0; i < 4; i++)
	{
		if (i != 0)
		{
			if (parser.nextToken().type != TokenType::Comma)
				return false;
		}

		parser.eatToken();

		int num = 0;
		if (!sequenceParser.parse(parser,num))
			return false;

		// m versions
		if (num < 0)
		{
			result |= 1 << (8+i);
			num = abs(num);
		}

		result |= num << (2*i);
	}

	return parser.nextToken().type == TokenType::RBrack;
}


bool MipsParser::decodeCop2BranchCondition(const std::string& text, size_t& pos, int& result)
{
	if (pos+3 == text.size())
	{
		if (startsWith(text,"any",pos))
		{
			result = 4;
			pos += 3;
			return true;
		}
		if (startsWith(text,"all",pos))
		{
			result = 5;
			pos += 3;
			return true;
		}
	} else if (pos+1 == text.size())
	{
		switch (text[pos++])
		{
		case 'x':
		case '0':
			result = 0;
			return true;
		case 'y':
		case '1':
			result = 1;
			return true;
		case 'z':
		case '2':
			result = 2;
			return true;
		case 'w':
		case '3':
			result = 3;
			return true;
		case '4':
			result = 4;
			return true;
		case '5':
			result = 5;
			return true;
		}

		// didn't match it
		pos--;
	}

	return false;
}

bool MipsParser::parseCop2BranchCondition(Parser& parser, int& result)
{
	const Token& token = parser.nextToken();

	if (token.type == TokenType::Integer)
	{
		result = (int) token.intValue();
		return token.intValue() <= 5;
	}

	if (token.type != TokenType::Identifier)
		return false;

	size_t pos = 0;
	return decodeCop2BranchCondition(token.identifierValue().string(),pos,result);
}

bool MipsParser::parseWb(Parser& parser)
{
	const Token& token = parser.nextToken();
	if (token.type != TokenType::Identifier)
		return false;

	return token.identifierValue() == "wb";
}

static bool decodeImmediateSize(const char*& encoding, MipsImmediateType& dest)
{
	if (*encoding == 'h')	// half float
	{
		encoding++;
		dest = MipsImmediateType::ImmediateHalfFloat;
	} else {
		int num = 0;
		while (*encoding >= '0' && *encoding <= '9')
		{
			num = num*10 + *encoding-'0';
			encoding++;
		}

		switch (num)
		{
		case 5:
			dest = MipsImmediateType::Immediate5;
			break;
		case 7:
			dest = MipsImmediateType::Immediate7;
			break;
		case 10:
			dest = MipsImmediateType::Immediate10;
			break;
		case 16:
			dest = MipsImmediateType::Immediate16;
			break;
		case 20:
			dest = MipsImmediateType::Immediate20;
			break;
		case 25:
			dest = MipsImmediateType::Immediate25;
			break;
		case 26:
			dest = MipsImmediateType::Immediate26;
			break;
		default:
			return false;
		}
	}

	return true;
}

bool MipsParser::decodeVfpuType(const std::string& name, size_t& pos, int& dest)
{
	if (pos >= name.size())
		return false;

	switch (name[pos++])
	{
	case 's':
		dest = 0;
		return true;
	case 'p':
		dest = 1;
		return true;
	case 't':
		dest = 2;
		return true;
	case 'q':
		dest = 3;
		return true;
	}

	pos--;
	return false;
}

bool MipsParser::decodeOpcode(const std::string& name, const tMipsOpcode& opcode)
{
	const char* encoding = opcode.name;
	size_t pos = 0;

	registers.reset();
	immediate.reset();
	opcodeData.reset();
	hasFixedSecondaryImmediate = false;

	while (*encoding != 0)
	{
		switch (*encoding++)
		{
		case 'S':
			CHECK(decodeVfpuType(name,pos,opcodeData.vfpuSize));
			break;
		case 'B':
			CHECK(decodeCop2BranchCondition(name,pos,immediate.secondary.originalValue));
			immediate.secondary.type = MipsImmediateType::Cop2BranchType;
			immediate.secondary.value = immediate.secondary.originalValue;
			hasFixedSecondaryImmediate = true;
			break;
		default:
			CHECK(pos < name.size());
			CHECK(*(encoding-1) == name[pos++]);
			break;
		}
	}

	return pos >= name.size();
}

void MipsParser::setOmittedRegisters(const tMipsOpcode& opcode)
{
	// copy over omitted registers
	if (opcode.flags & MO_RSD)
		registers.grd = registers.grs;

	if (opcode.flags & MO_RST)
		registers.grt = registers.grs;

	if (opcode.flags & MO_RDT)
		registers.grt = registers.grd;

	if (opcode.flags & MO_FRSD)
		registers.frd = registers.frs;

	if (opcode.flags & MO_RSP_VRSD)
		registers.rspvrd = registers.rspvrs;
}

bool MipsParser::parseParameters(Parser& parser, const tMipsOpcode& opcode)
{
	const char* encoding = opcode.encoding;

	// initialize opcode variables
	immediate.primary.type = MipsImmediateType::None;
	if (!hasFixedSecondaryImmediate)
		immediate.secondary.type = MipsImmediateType::None;

	if (opcodeData.vfpuSize == -1)
	{
		if (opcode.flags & MO_VFPU_SINGLE)
			opcodeData.vfpuSize = 0;
		else if (opcode.flags & MO_VFPU_PAIR)
			opcodeData.vfpuSize = 1;
		else if (opcode.flags & MO_VFPU_TRIPLE)
			opcodeData.vfpuSize = 2;
		else if (opcode.flags & MO_VFPU_QUAD)
			opcodeData.vfpuSize = 3;
	}

	// parse parameters
	MipsRegisterValue tempRegister;
	int actualSize = opcodeData.vfpuSize;

	while (*encoding != 0)
	{
		switch (*encoding++)
		{
		case 't':	// register
			CHECK(parseRegister(parser,registers.grt));
			break;
		case 'd':	// register
			CHECK(parseRegister(parser,registers.grd));
			break;
		case 's':	// register
			CHECK(parseRegister(parser,registers.grs));
			break;
		case 'T':	// float register
			CHECK(parseFpuRegister(parser,registers.frt));
			break;
		case 'D':	// float register
			CHECK(parseFpuRegister(parser,registers.frd));
			break;
		case 'S':	// float register
			CHECK(parseFpuRegister(parser,registers.frs));
			break;
		case 'f':	// fpu control register
			CHECK(parseFpuControlRegister(parser,registers.frs));
			break;
		case 'z':	// cop0 register
			CHECK(parseCop0Register(parser,registers.grd));
			break;
		case 'v':	// psp vfpu reg
			if (*encoding == 'S')
			{
				encoding++;
				actualSize = 0;
			}

			switch (*encoding++)
			{
			case 's':
				CHECK(parseVfpuRegister(parser,registers.vrs,actualSize));
				CHECK(registers.vrs.type == MipsRegisterType::VfpuVector);
				if (opcode.flags & MO_VFPU_6BIT) CHECK(!(registers.vrs.num & 0x40));
				break;
			case 't':
				CHECK(parseVfpuRegister(parser,registers.vrt,actualSize));
				CHECK(registers.vrt.type == MipsRegisterType::VfpuVector);
				if (opcode.flags & MO_VFPU_6BIT) CHECK(!(registers.vrt.num & 0x40));
				break;
			case 'd':
				CHECK(parseVfpuRegister(parser,registers.vrd,actualSize));
				CHECK(registers.vrd.type == MipsRegisterType::VfpuVector);
				if (opcode.flags & MO_VFPU_6BIT) CHECK(!(registers.vrd.num & 0x40));
				break;
			case 'c':
				CHECK(parseVfpuControlRegister(parser,registers.vrd));
				break;
			default:
				return false;
			}
			break;
		case 'm':	// vfpu matrix register
			switch (*encoding++)
			{
			case 's':
				CHECK(parseVfpuRegister(parser,registers.vrs,opcodeData.vfpuSize));
				CHECK(registers.vrs.type == MipsRegisterType::VfpuMatrix);
				if (opcode.flags & MO_VFPU_TRANSPOSE_VS)
					registers.vrs.num ^= 0x20;
				break;
			case 't':
				CHECK(parseVfpuRegister(parser,registers.vrt,opcodeData.vfpuSize));
				CHECK(registers.vrt.type == MipsRegisterType::VfpuMatrix);
				break;
			case 'd':
				CHECK(parseVfpuRegister(parser,registers.vrd,opcodeData.vfpuSize));
				CHECK(registers.vrd.type == MipsRegisterType::VfpuMatrix);
				break;
			default:
				return false;
			}
			break;
		case 'V':	// ps2 vector reg
			switch (*encoding++)
			{
			case 't':	// register
				CHECK(parsePs2Cop2Register(parser,registers.ps2vrt));
				break;
			case 'd':	// register
				CHECK(parsePs2Cop2Register(parser,registers.ps2vrd));
				break;
			case 's':	// register
				CHECK(parsePs2Cop2Register(parser,registers.ps2vrs));
				break;
			default:
				return false;
			}
			break;
		case 'g':	// psx cop2 reg
			switch (*encoding++)
			{
			case 't':	// gte data register
				CHECK(parsePsxCop2DataRegister(parser,registers.grt));
				break;
			case 's':	// gte data register
				CHECK(parsePsxCop2DataRegister(parser,registers.grd));
				break;
			case 'c':	// gte control register
				CHECK(parsePsxCop2ControlRegister(parser,registers.grd));
				break;
			default:
				return false;
			}
			break;
		case 'r':	// forced register
			CHECK(parseRegister(parser,tempRegister));
			CHECK(tempRegister.num == *encoding++);
			break;
		case 'R':	// rsp register
			switch (*encoding++)
			{
			case 'z':	// cop0 register
				CHECK(parseRspCop0Register(parser,registers.grd));
				break;
			case 'c':	// vector control register
				CHECK(parseRspVectorControlRegister(parser,registers.grd));
				break;
			case 't':	// vector register
				CHECK(parseRspVectorRegister(parser,registers.rspvrt));
				break;
			case 'd':	// vector register
				CHECK(parseRspVectorRegister(parser,registers.rspvrd));
				break;
			case 's':	// vector register
				CHECK(parseRspVectorRegister(parser,registers.rspvrs));
				break;
			case 'e':	// vector element
				CHECK(parseRspVectorElement(parser,registers.rspve));
				break;
			case 'l':	// scalar element
				CHECK(parseRspScalarElement(parser,registers.rspve));
				break;
			case 'm':	// scalar destination element
				CHECK(parseRspScalarElement(parser,registers.rspvde));
				break;
			case 'o':	// byte offset element
				CHECK(parseRspOffsetElement(parser,registers.rspvealt));
				break;
			default:
				return false;
			}
			break;
		case 'i':	// primary immediate
			CHECK(parseImmediate(parser,immediate.primary.expression));
			CHECK(decodeImmediateSize(encoding,immediate.primary.type));
			break;
		case 'j':	// secondary immediate
			switch (*encoding++)
			{
			case 'c':
				CHECK(parseImmediate(parser,immediate.secondary.expression));
				immediate.secondary.type = MipsImmediateType::CacheOp;
				break;
			case 'e':
				CHECK(parseImmediate(parser,immediate.secondary.expression));
				immediate.secondary.type = MipsImmediateType::Ext;
				break;
			case 'i':
				CHECK(parseImmediate(parser,immediate.secondary.expression));
				immediate.secondary.type = MipsImmediateType::Ins;
				break;
			case 'b':
				CHECK(parseCop2BranchCondition(parser,immediate.secondary.originalValue));
				immediate.secondary.type = MipsImmediateType::Cop2BranchType;
				immediate.secondary.value = immediate.secondary.originalValue;
				break;
			default:
				return false;
			}
			break;
		case 'C':	// vfpu condition
			CHECK(parseVfpuCondition(parser,opcodeData.vectorCondition));
			break;
		case 'W':	// vfpu argument
			switch (*encoding++)
			{
			case 's':
				CHECK(parseVpfxsParameter(parser,immediate.primary.originalValue));
				immediate.primary.value = immediate.primary.originalValue;
				immediate.primary.type = MipsImmediateType::Immediate20_0;
				break;
			case 'd':
				CHECK(parseVpfxdParameter(parser,immediate.primary.originalValue));
				immediate.primary.value = immediate.primary.originalValue;
				immediate.primary.type = MipsImmediateType::Immediate16;
				break;
			case 'c':
				CHECK(parseVcstParameter(parser,immediate.primary.originalValue));
				immediate.primary.value = immediate.primary.originalValue;
				immediate.primary.type = MipsImmediateType::Immediate5;
				break;
			case 'r':
				CHECK(parseVfpuVrot(parser,immediate.primary.originalValue,opcodeData.vfpuSize));
				immediate.primary.value = immediate.primary.originalValue;
				immediate.primary.type = MipsImmediateType::Immediate5;
				break;
			default:
				return false;
			}
			break;
		case 'w':	// 'wb' characters
			CHECK(parseWb(parser));
			break;
		default:
			CHECK(matchSymbol(parser,*(encoding-1)));
			break;
		}
	}

	opcodeData.opcode = opcode;
	setOmittedRegisters(opcode);

	// the next token has to be a separator, else the parameters aren't
	// completely parsed

	return parser.nextToken().type == TokenType::Separator;

}

std::unique_ptr<CMipsInstruction> MipsParser::parseOpcode(Parser& parser)
{
	if (parser.peekToken().type != TokenType::Identifier)
		return nullptr;

	const Token &token = parser.nextToken();

	bool paramFail = false;
	const MipsArchDefinition& arch = mipsArchs[Mips.GetVersion()];
	const Identifier &identifier = token.identifierValue();

	for (int z = 0; MipsOpcodes[z].name != nullptr; z++)
	{
		if ((MipsOpcodes[z].archs & arch.supportSets) == 0)
			continue;
		if ((MipsOpcodes[z].archs & arch.excludeMask) != 0)
			continue;

		if ((MipsOpcodes[z].flags & MO_64BIT) && !(arch.flags & MO_64BIT))
			continue;
		if ((MipsOpcodes[z].flags & MO_FPU) && !(arch.flags & MO_FPU))
			continue;
		if ((MipsOpcodes[z].flags & MO_DFPU) && !(arch.flags & MO_DFPU))
			continue;

		if (decodeOpcode(identifier.string(),MipsOpcodes[z]))
		{
			TokenizerPosition tokenPos = parser.getTokenizer()->getPosition();

			if (parseParameters(parser,MipsOpcodes[z]))
			{
				// success, return opcode
				return std::make_unique<CMipsInstruction>(opcodeData,immediate,registers);
			}

			parser.getTokenizer()->setPosition(tokenPos);
			paramFail = true;
		}
	}

	if (paramFail)
		parser.printError(token, "MIPS parameter failure");
	else
		parser.printError(token, "Invalid MIPS opcode '%s'",identifier);

	return nullptr;
}

bool MipsParser::parseMacroParameters(Parser& parser, const MipsMacroDefinition& macro)
{
	const char* encoding = macro.args;

	while (*encoding != 0)
	{
		switch (*encoding++)
		{
		case 't':	// register
			CHECK(parseRegister(parser,registers.grt));
			break;
		case 'd':	// register
			CHECK(parseRegister(parser,registers.grd));
			break;
		case 's':	// register
			CHECK(parseRegister(parser,registers.grs));
			break;
		case 'S':	// register
			CHECK(parseFpuRegister(parser,registers.frs));
			break;
		case 'i':	// primary immediate
			CHECK(parseImmediate(parser,immediate.primary.expression));
			break;
		case 'I':	// secondary immediate
			CHECK(parseImmediate(parser,immediate.secondary.expression));
			break;
		default:
			CHECK(matchSymbol(parser,*(encoding-1)));
			break;
		}
	}

	// lw rx,imm is a prefix of lw rx,imm(ry)
	if (parser.peekToken().type == TokenType::LParen)
		return false;

	// the next token has to be a separator, else the parameters aren't
	// completely parsed
	return parser.nextToken().type == TokenType::Separator;
}

std::unique_ptr<CAssemblerCommand> MipsParser::parseMacro(Parser& parser)
{
	Tokenizer* tokenizer = parser.getTokenizer();
	TokenizerPosition startPos = tokenizer->getPosition();

	// Cannot be a reference (we eat below.)
	const Token token = parser.peekToken();
	if (token.type != TokenType::Identifier)
		return nullptr;

	parser.eatToken();
	const Identifier &identifier = token.identifierValue();
	for (int z = 0; mipsMacros[z].name != nullptr; z++)
	{
		if (identifier == mipsMacros[z].name)
		{
			TokenizerPosition tokenPos = tokenizer->getPosition();

			if (parseMacroParameters(parser,mipsMacros[z]))
			{
				return mipsMacros[z].function(parser,registers,immediate,mipsMacros[z].flags);
			}

			tokenizer->setPosition(tokenPos);
		}
	}

	// no matching macro found, restore state
	tokenizer->setPosition(startPos);
	return nullptr;
}

void MipsOpcodeFormatter::handleOpcodeName(const MipsOpcodeData& opData)
{
	const char* encoding = opData.opcode.name;

	while (*encoding != 0)
	{
		switch (*encoding++)
		{
		case 'S':
			buffer += "sptq"[opData.vfpuSize];
			break;
		case 'B':
			// TODO
			break;
		default:
			buffer += *(encoding-1);
			break;
		}
	}
}

void MipsOpcodeFormatter::handleImmediate(MipsImmediateType type, unsigned int originalValue, unsigned int opcodeFlags)
{
	switch (type)
	{
	case MipsImmediateType::ImmediateHalfFloat:
		buffer += tfm::format("%f", bitsToFloat(originalValue));
		break;
	case MipsImmediateType::Immediate16:
		if (!(opcodeFlags & MO_IPCR) && originalValue & 0x8000)
			buffer += tfm::format("-0x%X", 0x10000-(originalValue & 0xFFFF));
		else
			buffer += tfm::format("0x%X", originalValue);
		break;
	default:
		buffer += tfm::format("0x%X", originalValue);
		break;
	}
}

void MipsOpcodeFormatter::handleOpcodeParameters(const MipsOpcodeData& opData, const MipsRegisterData& regData,
	const MipsImmediateData& immData)
{
	const char* encoding = opData.opcode.encoding;

	MipsImmediateType type;
	while (*encoding != 0)
	{
		switch (*encoding++)
		{
		case 'r':	// forced register
			buffer += tfm::format("r%d",*encoding);
			encoding += 1;
			break;
		case 's':	// register
			buffer += regData.grs.name.string();
			break;
		case 'd':	// register
			buffer += regData.grd.name.string();
			break;
		case 't':	// register
			buffer += regData.grt.name.string();
			break;
		case 'S':	// fpu register
			buffer += regData.frs.name.string();
			break;
		case 'D':	// fpu register
			buffer += regData.frd.name.string();
			break;
		case 'T':	// fpu register
			buffer += regData.frt.name.string();
			break;
		case 'v':	// psp vfpu reg
		case 'm':	// vfpu matrix register
			switch (*encoding++)
			{
			case 'd':
				buffer += regData.vrd.name.string();
				break;
			case 's':
				buffer += regData.vrs.name.string();
				break;
			case 't':
				buffer += regData.vrt.name.string();
				break;
			}
			break;
		case 'V':	// ps2 vector reg
			switch (*encoding++)
			{
			case 'd':
				buffer += regData.ps2vrd.name.string();
				break;
			case 's':
				buffer += regData.ps2vrs.name.string();
				break;
			case 't':
				buffer += regData.ps2vrt.name.string();
				break;
			}
			break;
		case 'i':	// primary immediate
			decodeImmediateSize(encoding,type);
			handleImmediate(immData.primary.type,immData.primary.originalValue,opData.opcode.flags);
			break;
		case 'j':	// secondary immediate
			handleImmediate(immData.secondary.type,immData.secondary.originalValue, opData.opcode.flags);
			encoding++;
			break;
		case 'C':	// vfpu condition
		case 'W':	// vfpu argument
			// TODO
			break;
		case 'w':	// 'wb' characters
			buffer += "wb";
			break;
		default:
			buffer += *(encoding-1);
			break;
		}
	}
}

const std::string& MipsOpcodeFormatter::formatOpcode(const MipsOpcodeData& opData, const MipsRegisterData& regData,
	const MipsImmediateData& immData)
{
	buffer = "   ";
	handleOpcodeName(opData);

	while (buffer.size() < 11)
		buffer += ' ';

	handleOpcodeParameters(opData,regData,immData);
	return buffer;
}

#pragma region "File: Archs/MIPS/PsxRelocator.cpp"


#include <cstring>
#include <map>

struct PsxLibEntry
{
	std::string name;
	ByteArray data;
};

const unsigned char psxObjectFileMagicNum[6] = { 'L', 'N', 'K', '\x02', '\x2E', '\x07' };

std::vector<PsxLibEntry> loadPsxLibrary(const fs::path& inputName)
{
	ByteArray input = ByteArray::fromFile(inputName);
	std::vector<PsxLibEntry> result;

	if (input.size() == 0)
		return result;

	if (memcmp(input.data(),psxObjectFileMagicNum,sizeof(psxObjectFileMagicNum)) == 0)
	{
		PsxLibEntry entry;
		entry.name = inputName.filename().u8string();
		entry.data = input;
		result.push_back(entry);
		return result;
	}

	if (memcmp(input.data(),"LIB\x01",4) != 0)
		return result;

	size_t pos = 4;
	while (pos < input.size())
	{
		PsxLibEntry entry;

		for (int i = 0; i < 16 && input[pos+i] != ' '; i++)
		{
			entry.name += input[pos+i];
		}

		int size = input.getDoubleWord(pos+16);
		int skip = 20;

		while (input[pos+skip] != 0)
		{
			skip += 1+input[pos+skip];
		}

		skip++;

		entry.data = input.mid(pos+skip,size-skip);
		pos += size;

		result.push_back(entry);
	}

	return result;
}

size_t PsxRelocator::loadString(ByteArray& data, size_t pos, std::string& dest)
{
	dest.clear();
	int len = data[pos++];

	for (int i = 0; i < len; i++)
	{
		dest += data[pos++];
	}

	return len+1;
}

bool PsxRelocator::parseObject(ByteArray data, PsxRelocatorFile& dest)
{
	if (memcmp(data.data(),psxObjectFileMagicNum,sizeof(psxObjectFileMagicNum)) != 0)
		return false;

	size_t pos = 6;

	std::vector<PsxSegment>& segments = dest.segments;
	std::vector<PsxSymbol>& syms = dest.symbols;

	int activeSegment = -1;
	int lastSegmentPartStart = -1;
	while (pos < data.size())
	{
		switch (data[pos])
		{
		case 0x10:	// segment definition
			{
				PsxSegment seg;
				seg.id = data.getDoubleWord(pos+1);
				segments.push_back(seg);
				pos += 5;

				if (data[pos] != 8)
					return false;

				std::string& name = segments[segments.size()-1].name;
				pos += 1 + loadString(data,pos+1,name);
			}
			break;
		case 0x14:	// group?
			pos += data[pos+4]+5;
			break;
		case 0x1C:	// source file name
			pos += data[pos+3]+4;
			break;

		case 0x06:	// set segment id
			{
				int id = data.getWord(pos+1);
				pos += 3;

				int num = -1;
				for (size_t i = 0; i < segments.size(); i++)
					{
					if (segments[i].id == id)
					{
						num = (int) i;
						break;
					}
				}

				activeSegment = num;
			}
			break;
		case 0x02:	// append to data segment
			{
				int size = data.getWord(pos+1);
				pos += 3;

				ByteArray d = data.mid(pos,size);
				pos += size;

				lastSegmentPartStart = (int) segments[activeSegment].data.size();
				segments[activeSegment].data.append(d);
			}
			break;
		case 0x08:	// append zeroes data segment
			{
				int size = data.getWord(pos+1);
				pos += 3;

				ByteArray d;
				d.reserveBytes(size);
				segments[activeSegment].data.append(d);
			}
			break;
		case 0x0A:	// relocation data
			{
				int type = data[pos+1];
				pos += 2;

				PsxRelocation rel;
				rel.relativeOffset = 0;
				rel.filePos = (int) pos-2;

				switch (type)
				{
				case 0x10:	// 32 bit word
					rel.type = PsxRelocationType::WordLiteral;
					rel.segmentOffset = data.getWord(pos);
					pos += 2;
					break;
				case 0x4A:	// jal
					rel.type = PsxRelocationType::FunctionCall;
					rel.segmentOffset = data.getWord(pos);
					pos += 2;
					break;
				case 0x52:	// upper immerdiate
					rel.type = PsxRelocationType::UpperImmediate;
					rel.segmentOffset = data.getWord(pos);
					pos += 2;
					break;
				case 0x54:	// lower immediate (add)
					rel.type = PsxRelocationType::LowerImmediate;
					rel.segmentOffset = data.getWord(pos);
					pos += 2;
					break;
				default:
					return false;
				}

				rel.segmentOffset += lastSegmentPartStart;
checkothertype:
				int otherType = data[pos++];
				switch (otherType)
				{
				case 0x02:	// reference to symbol with id num
					rel.refType = PsxRelocationRefType::SymblId;
					rel.referenceId = data.getWord(pos);
					pos += 2;
					break;
				case 0x2C:	// ref to other segment?
					rel.refType = PsxRelocationRefType::SegmentOffset;

					switch (data[pos++])
					{
					case 0x00:
						rel.relativeOffset = data.getDoubleWord(pos);
						pos += 4;
						goto checkothertype;
					case 0x04:
						rel.referenceId = data.getWord(pos);	// segment id
						pos += 2;

						if (data[pos++] != 0x00)
						{
							return false;
						}

						rel.referencePos = data.getDoubleWord(pos);
						pos += 4;
						break;
					default:
						return false;
					}
					break;
				case 0x2E:	// negative ref?
					rel.refType = PsxRelocationRefType::SegmentOffset;

					switch (data[pos++])
					{
					case 0x00:
						rel.relativeOffset = -data.getDoubleWord(pos);
						pos += 4;
						goto checkothertype;
					default:
						return false;
					}
					break;
				default:
					return false;
				}

				segments[activeSegment].relocations.push_back(rel);
			}
			break;
		case 0x12:	// internal symbol
			{
				PsxSymbol sym;
				sym.type = PsxSymbolType::Internal;
				sym.segment = data.getWord(pos+1);
				sym.offset = data.getDoubleWord(pos+3);
				pos += 7 + loadString(data,pos+7,sym.name);
				syms.push_back(sym);
			}
			break;
		case 0x0E:	// external symbol
			{
				PsxSymbol sym;
				sym.type = PsxSymbolType::External;
				sym.id = data.getWord(pos+1);
				pos += 3 + loadString(data,pos+3,sym.name);
				syms.push_back(sym);
			}
			break;
		case 0x30:	// bss symbol?
			{
				PsxSymbol sym;
				sym.type = PsxSymbolType::BSS;
				sym.id = data.getWord(pos+1);
				sym.segment = data.getWord(pos+3);
				sym.size = data.getDoubleWord(pos+5);
				pos += 9 + loadString(data,pos+9,sym.name);
				syms.push_back(sym);
			}
			break;
		case 0x0C:	// internal with id
			{
				PsxSymbol sym;
				sym.type = PsxSymbolType::InternalID;
				sym.id = data.getWord(pos+1);
				sym.segment = data.getWord(pos+3);
				sym.offset = data.getDoubleWord(pos+5);
				pos += 9 + loadString(data,pos+9,sym.name);
				syms.push_back(sym);
			}
			break;
		case 0x4A:	// function
			{
				PsxSymbol sym;
				sym.type = PsxSymbolType::Function;
				sym.segment = data.getWord(pos+1);
				sym.offset = data.getDoubleWord(pos+3);
				pos += 0x1D + loadString(data,pos+0x1D,sym.name);
				syms.push_back(sym);
			}
			break;
		case 0x4C:	// function size
			pos += 11;
			break;
		case 0x3C:	// ??
			pos += 3;
			break;
		case 0x00:	// ??
			pos++;
			break;
		case 0x32:	// ??
			pos += 3;
			break;
		case 0x3A:	// ??
			pos += 9;
			break;
		default:
			return false;
		}
	}

	return true;
}

bool PsxRelocator::init(const fs::path& inputName)
{
	auto inputFiles = loadPsxLibrary(inputName);

	// The ELF loader will report the error messages, if needed.
	if (inputFiles.size() == 0)
		return false;

	reloc = new MipsElfRelocator();

	for (PsxLibEntry& entry: inputFiles)
	{
		PsxRelocatorFile file;
		file.name = entry.name;

		if (!parseObject(entry.data,file))
			return false;

		// sort relocations
		for (PsxSegment& seg: file.segments)
		{
			auto sortFunc = [](const PsxRelocation &a, const PsxRelocation &b)
			{
				// Sort in order of...
				// - reference type - symbol or offset
				// - reference id - this groups references to the same symbol/segment/?
				// - referencePos - this ensure references to the same offset are grouped
				// - type - this ensures that HI16 is before LO16
				auto tie = [](const PsxRelocation &rel)
				{
					return std::tie(rel.refType, rel.referenceId, rel.referencePos, rel.type);
				};

				return tie(a) < tie(b);
			};

			std::stable_sort(seg.relocations.begin(), seg.relocations.end(), sortFunc);
		}

		// init symbols
		for (PsxSymbol& sym: file.symbols)
		{
			std::string lowered = sym.name;
			std::transform(lowered.begin(), lowered.end(), lowered.begin(), ::tolower);

			sym.label = Global.symbolTable.getLabel(Identifier(lowered),-1,-1);
			if (sym.label == nullptr)
			{
				Logger::printError(Logger::Error, "Invalid label name \"%s\"",sym.name);
				continue;
			}

			if (sym.label->isDefined() && sym.type != PsxSymbolType::External)
			{
				Logger::printError(Logger::Error, "Label \"%s\" already defined",sym.name);
				continue;
			}

			sym.label->setOriginalName(Identifier(sym.name));
		}

		files.push_back(file);
	}

	return true;
}

bool PsxRelocator::relocateFile(PsxRelocatorFile& file, int& relocationAddress)
{
	std::map<int,int> relocationOffsets;
	std::map<int,int> symbolOffsets;
	int start = relocationAddress;

	// assign addresses to segments
	for (PsxSegment& seg: file.segments)
	{
		int index = seg.id;
		size_t size = seg.data.size();

		relocationOffsets[index] = relocationAddress;
		relocationAddress += (int) size;

		while (relocationAddress % 4)
			relocationAddress++;
	}

	// parse/add/relocate symbols
	bool error = false;
	for (PsxSymbol& sym: file.symbols)
	{
		int pos;
		switch (sym.type)
		{
		case PsxSymbolType::Internal:
		case PsxSymbolType::Function:
			sym.label->setValue(relocationOffsets[sym.segment]+sym.offset);
			sym.label->setDefined(true);
			break;
		case PsxSymbolType::InternalID:
			pos = relocationOffsets[sym.segment]+sym.offset;
			sym.label->setValue(pos);
			sym.label->setDefined(true);
			symbolOffsets[sym.id] = pos;
			break;
		case PsxSymbolType::BSS:
			sym.label->setValue(relocationAddress);
			sym.label->setDefined(true);
			symbolOffsets[sym.id] = relocationAddress;
			relocationAddress += sym.size;

			while (relocationAddress % 4)
				relocationAddress++;
			break;
		case PsxSymbolType::External:
			if (!sym.label->isDefined())
			{
				Logger::queueError(Logger::Error, "Undefined external symbol %s in file %s",sym.name,file.name);
				error = true;
				continue;
			}

			symbolOffsets[sym.id] = (int) sym.label->getValue();
			break;
		}
	}

	if (error)
		return false;

	size_t dataStart = outputData.size();
	outputData.reserveBytes(relocationAddress-start);

	// load code and data
	for (PsxSegment& seg: file.segments)
	{
		// relocate
		ByteArray sectionData = seg.data;

		std::vector<RelocationAction> relocationActions;
		for (PsxRelocation& rel: seg.relocations)
		{
			RelocationData relData;
			int pos = rel.segmentOffset;
			relData.opcodeOffset = pos;
			relData.opcode = sectionData.getDoubleWord(pos);

			switch (rel.refType)
			{
			case PsxRelocationRefType::SymblId:
				relData.relocationBase = symbolOffsets[rel.referenceId]+rel.relativeOffset;
				break;
			case PsxRelocationRefType::SegmentOffset:
				relData.relocationBase = relocationOffsets[rel.referenceId] + rel.referencePos+rel.relativeOffset;
				break;
			}

			std::vector<std::string> errors;
			bool result = false;

			switch (rel.type)
			{
			case PsxRelocationType::WordLiteral:
				result = reloc->relocateOpcode(R_MIPS_32,relData, relocationActions, errors);
				break;
			case PsxRelocationType::UpperImmediate:
				result = reloc->relocateOpcode(R_MIPS_HI16,relData, relocationActions, errors);
				break;
			case PsxRelocationType::LowerImmediate:
				result = reloc->relocateOpcode(R_MIPS_LO16,relData, relocationActions, errors);
				break;
			case PsxRelocationType::FunctionCall:
				result = reloc->relocateOpcode(R_MIPS_26,relData, relocationActions, errors);
				break;
			}

			if (!result)
			{
				for (const std::string& error : errors)
				{
					Logger::queueError(Logger::Error, error);
				}
				error = true;
			}
		}

		// finish any dangling relocations
		std::vector<std::string> errors;
		if (!reloc->finish(relocationActions, errors))
		{
			for (const std::string& error : errors)
			{
				Logger::queueError(Logger::Error, error);
			}
			error = true;
		}

		// now actually write the relocated values
		for (const RelocationAction& action : relocationActions)
		{
			sectionData.replaceDoubleWord(action.offset, action.newValue);
		}

		size_t arrayStart = dataStart+relocationOffsets[seg.id]-start;
		memcpy(outputData.data(arrayStart),sectionData.data(),sectionData.size());
	}

	return !error;
}

bool PsxRelocator::relocate(int& memoryAddress)
{
	int oldCrc = getCrc32(outputData.data(),outputData.size());
	outputData.clear();
	dataChanged = false;

	bool error = false;
	int start = memoryAddress;

	for (PsxRelocatorFile& file: files)
	{
		if (!relocateFile(file,memoryAddress))
			error = true;
	}

	int newCrc = getCrc32(outputData.data(),outputData.size());
	if (oldCrc != newCrc)
		dataChanged = true;

	memoryAddress -= start;
	return !error;
}


void PsxRelocator::writeSymbols(SymbolData& symData) const
{
	for (const PsxRelocatorFile& file: files)
	{
		for (const PsxSymbol& sym: file.symbols)
		{
			if (sym.type != PsxSymbolType::External)
				symData.addLabel(sym.label->getValue(), sym.name);
		}
	}
}

//
// DirectivePsxObjImport
//

DirectivePsxObjImport::DirectivePsxObjImport(const fs::path& fileName)
{
	success = rel.init(fileName);
}

bool DirectivePsxObjImport::Validate(const ValidateState &state)
{
	int memory = (int) g_fileManager->getVirtualAddress();
	rel.relocate(memory);
	g_fileManager->advanceMemory(memory);
	return rel.hasDataChanged();
}

void DirectivePsxObjImport::Encode() const
{
	const ByteArray& data = rel.getData();
	g_fileManager->write(data.data(),data.size());
}

void DirectivePsxObjImport::writeSymData(SymbolData& symData) const
{
	rel.writeSymbols(symData);
}

#pragma region "File: Archs/Architecture.cpp"


CInvalidArchitecture InvalidArchitecture;

Architecture *Architecture::currentArchitecture = &InvalidArchitecture;

Architecture &Architecture::current()
{
	assert(currentArchitecture);
	return *currentArchitecture;
}

void Architecture::setCurrent(Architecture &arch)
{
	currentArchitecture = &arch;
	ExpressionFunctionHandler::instance().updateArchitecture();
}

void Architecture::registerExpressionFunctions([[maybe_unused]] ExpressionFunctionHandler &handler)
{
}

ArchitectureCommand::ArchitectureCommand(const std::string& tempText, const std::string& symText)
	: tempText(tempText), symText(symText)
{
	this->architecture = &Architecture::current();
	this->endianness = Architecture::current().getEndianness();
}

bool ArchitectureCommand::Validate(const ValidateState &state)
{
	Architecture::setCurrent(*architecture);
	position = g_fileManager->getVirtualAddress();
	g_fileManager->setEndianness(endianness);
	return false;
}

void ArchitectureCommand::Encode() const
{
	g_fileManager->setEndianness(endianness);
}

void ArchitectureCommand::writeTempData(TempData& tempData) const
{
	if (tempText.size() != 0)
	{
		std::stringstream stream(tempText);

		std::string line;
		while (std::getline(stream,line,'\n'))
		{
			if (line.size() != 0)
				tempData.writeLine(position,line);
		}
	}
}

void ArchitectureCommand::writeSymData(SymbolData& symData) const
{
	// TODO: find a less ugly way to check for undefined memory positions
	if (position == -1)
		return;

	if (symText.size() != 0)
		symData.addLabel(position,symText);
}


void CInvalidArchitecture::NextSection()
{
	Logger::printError(Logger::FatalError, "No architecture specified");
}

void CInvalidArchitecture::Pass2()
{
	Logger::printError(Logger::FatalError, "No architecture specified");
}

void CInvalidArchitecture::Revalidate()
{
	Logger::printError(Logger::FatalError, "No architecture specified");
}

std::unique_ptr<IElfRelocator> CInvalidArchitecture::getElfRelocator()
{
	Logger::printError(Logger::FatalError, "No architecture specified");
	return nullptr;
}

#pragma region "File: Commands/CAssemblerCommand.cpp"


CAssemblerCommand::CAssemblerCommand()
{
	FileNum = Global.FileInfo.FileNum;
	FileLine = Global.FileInfo.LineNumber;
	section = Global.Section;
}

void CAssemblerCommand::applyFileInfo()
{
	Global.FileInfo.FileNum = FileNum;
	Global.FileInfo.LineNumber = FileLine;
}

#pragma region "File: Commands/CAssemblerLabel.h"


#include <optional>

class Label;

class CAssemblerLabel: public CAssemblerCommand
{
public:
	CAssemblerLabel(const Identifier& name, const Identifier& originalName, std::optional<bool> thumbMode = std::nullopt);
	CAssemblerLabel(const Identifier& name, const Identifier& originalName, Expression& value, std::optional<bool> thumbMode = std::nullopt);
	bool Validate(const ValidateState &state) override;
	void Encode() const override;
	void writeTempData(TempData& tempData) const override;
	void writeSymData(SymbolData& symData) const override;
private:
	Expression labelValue;
	std::shared_ptr<Label> label;
	bool defined;
};

class CDirectiveFunction: public CAssemblerCommand
{
public:
	CDirectiveFunction(const Identifier& name, const Identifier& originalName);
	bool Validate(const ValidateState &state) override;
	void Encode() const override;
	void writeTempData(TempData& tempData) const override;
	void writeSymData(SymbolData& symData) const override;
	void setContent(std::unique_ptr<CAssemblerCommand> content) { this->content = std::move(content); }
private:
	std::unique_ptr<CAssemblerLabel> label;
	std::unique_ptr<CAssemblerCommand> content;
	int64_t start, end;
};

#pragma region "File: Commands/CAssemblerLabel.cpp"


CAssemblerLabel::CAssemblerLabel(const Identifier& name, const Identifier& originalName, std::optional<bool> thumbMode)
{
	this->defined = false;
	this->label = nullptr;

	if (!Global.symbolTable.isLocalSymbol(name))
		updateSection(++Global.Section);

	label = Global.symbolTable.getLabel(name, FileNum, getSection());
	if (label == nullptr)
	{
		Logger::printError(Logger::Error, "Invalid label name \"%s\"", name);
		return;
	}

	label->setOriginalName(originalName);

	// does this need to be in validate?
	if (label->getUpdateInfo())
        label->setInfo(0);
}

CAssemblerLabel::CAssemblerLabel(const Identifier& name, const Identifier& originalName, Expression& value, std::optional<bool> thumbMode)
	: CAssemblerLabel(name,originalName,thumbMode)
{
	labelValue = value;
}

bool CAssemblerLabel::Validate(const ValidateState &state)
{
	bool result = false;
	if (!defined)
	{
		if (label->isDefined())
		{
			Logger::queueError(Logger::Error, "Label \"%s\" already defined", label->getName());
			return false;
		}

		label->setDefined(true);
		defined = true;
		result = true;
	}

	bool hasPhysicalValue = false;
	int64_t virtualValue = 0;
	int64_t physicalValue = 0;

	if (labelValue.isLoaded())
	{
		// label value is given by expression
		if (!labelValue.evaluateInteger(virtualValue))
		{
			Logger::printError(Logger::Error, "Invalid expression");
			return result;
		}
	} else {
		// label value is given by current address
		virtualValue = g_fileManager->getVirtualAddress();
		physicalValue = g_fileManager->getPhysicalAddress();
		hasPhysicalValue = true;
	}

	if (label->getValue() != virtualValue)
	{
		label->setValue(virtualValue);
		result = true;
	}

	if (hasPhysicalValue && (!label->hasPhysicalValue() || physicalValue != label->getPhysicalValue()))
	{
		label->setPhysicalValue(physicalValue);
		result = true;
	}

	return result;
}

void CAssemblerLabel::Encode() const
{

}

void CAssemblerLabel::writeTempData(TempData& tempData) const
{
	if (!Global.symbolTable.isGeneratedLabel(label->getName()))
		tempData.writeLine(label->getValue(),tfm::format("%s:",label->getName()));
}

void CAssemblerLabel::writeSymData(SymbolData& symData) const
{
	// TODO: find a less ugly way to check for undefined memory positions
	if (label->getValue() == -1 || Global.symbolTable.isGeneratedLabel(label->getName()))
		return;

	symData.addLabel(label->getValue(),label->getOriginalName().string());
}




CDirectiveFunction::CDirectiveFunction(const Identifier& name, const Identifier& originalName)
{
	this->label = std::make_unique<CAssemblerLabel>(name,originalName);
	this->content = nullptr;
	this->start = this->end = 0;
}

bool CDirectiveFunction::Validate(const ValidateState &state)
{
	start = g_fileManager->getVirtualAddress();

	label->applyFileInfo();
	bool result = label->Validate(state);

	ValidateState contentValidation = state;
	contentValidation.noFileChange = true;
	contentValidation.noFileChangeDirective = "function";
	content->applyFileInfo();
	if (content->Validate(contentValidation))
		result = true;

	end = g_fileManager->getVirtualAddress();
	return result;
}

void CDirectiveFunction::Encode() const
{
	label->Encode();
	content->Encode();
}

void CDirectiveFunction::writeTempData(TempData& tempData) const
{
	label->writeTempData(tempData);
	content->applyFileInfo();
	content->writeTempData(tempData);
}

void CDirectiveFunction::writeSymData(SymbolData& symData) const
{
	symData.startFunction(start);
	label->writeSymData(symData);
	content->writeSymData(symData);
	symData.endFunction(end);
}

#pragma region "File: Commands/CDirectiveArea.h"


class CDirectiveArea: public CAssemblerCommand
{
public:
	CDirectiveArea(bool shared, Expression& size);
	bool Validate(const ValidateState &state) override;
	void Encode() const override;
	void writeTempData(TempData& tempData) const override;
	void writeSymData(SymbolData& symData) const override;
	void setFillExpression(Expression& exp);
	void setPositionExpression(Expression& exp);
	void setContent(std::unique_ptr<CAssemblerCommand> content) { this->content = std::move(content); }
private:
	bool shared;
	int64_t position = 0;
	Expression sizeExpression;
	int64_t areaSize = 0;
	int64_t contentSize = 0;
	Expression fillExpression;
	int8_t fillValue = 0;
	int64_t fileID = 0;
	Expression positionExpression;
	std::unique_ptr<CAssemblerCommand> content;
};

class CDirectiveAutoRegion : public CAssemblerCommand
{
public:
	CDirectiveAutoRegion();
	bool Validate(const ValidateState &state) override;
	void Encode() const override;
	void writeTempData(TempData& tempData) const override;
	void writeSymData(SymbolData& symData) const override;
	void setMinRangeExpression(Expression& exp);
	void setRangeExpressions(Expression& minExp, Expression& maxExp);
	void setContent(std::unique_ptr<CAssemblerCommand> content) { this->content = std::move(content); }

private:
	int64_t resetPosition;
	int64_t position;
	int64_t contentSize;
	int64_t fileID = 0;
	Expression minRangeExpression;
	Expression maxRangeExpression;
	std::unique_ptr<CAssemblerCommand> content;
};

#pragma region "File: Commands/CDirectiveArea.cpp"


#include <algorithm>
#include <cstring>

CDirectiveArea::CDirectiveArea(bool shared, Expression& size)
	: shared(shared), sizeExpression(size), content(nullptr)
{
}

void CDirectiveArea::setFillExpression(Expression& exp)
{
	fillExpression = exp;
}

void CDirectiveArea::setPositionExpression(Expression& exp)
{
	positionExpression = exp;
}

bool CDirectiveArea::Validate(const ValidateState &state)
{
	int64_t oldAreaSize = areaSize;
	int64_t oldContentSize = contentSize;
	int64_t oldPosition = position;

	if (positionExpression.isLoaded())
	{
		if (!positionExpression.evaluateInteger(position))
		{
			Logger::queueError(Logger::Error, "Invalid position expression");
			return false;
		}
		Architecture::current().NextSection();
		g_fileManager->seekVirtual(position);
	}
	else
		position = g_fileManager->getVirtualAddress();

	if (!sizeExpression.evaluateInteger(areaSize))
	{
		Logger::queueError(Logger::Error, "Invalid size expression");
		return false;
	}

	if (areaSize < 0)
	{
		Logger::queueError(Logger::Error, "Negative area size");
		return false;
	}

	if (fillExpression.isLoaded())
	{
		if (!fillExpression.evaluateInteger(fillValue))
		{
			Logger::queueError(Logger::Error, "Invalid fill expression");
			return false;
		}
	}

	bool result = false;
	if (content)
	{
		ValidateState contentValidation = state;
		contentValidation.noFileChange = true;
		contentValidation.noFileChangeDirective = "area";
		content->applyFileInfo();
		result = content->Validate(contentValidation);
	}
	contentSize = g_fileManager->getVirtualAddress()-position;

	// restore info of this command
	applyFileInfo();

	if (areaSize < contentSize)
	{
		Logger::queueError(Logger::Error, "Area at %08x overflowed by %d bytes", position, contentSize - areaSize);
	}

	if (fillExpression.isLoaded() || shared)
		g_fileManager->advanceMemory(areaSize-contentSize);

	if (areaSize != oldAreaSize || contentSize != oldContentSize)
		result = true;

	int64_t oldFileID = fileID;
	fileID = g_fileManager->getOpenFileID();

	if ((oldFileID != fileID || oldPosition != position || areaSize == 0) && oldAreaSize != 0)
		Allocations::forgetArea(oldFileID, oldPosition, oldAreaSize);
	if (areaSize != 0)
		Allocations::setArea(fileID, position, areaSize, contentSize, fillExpression.isLoaded(), shared);

	return result;
}

void CDirectiveArea::Encode() const
{
	if (positionExpression.isLoaded())
	{
		Architecture::current().NextSection();
		g_fileManager->seekVirtual(position);
	}

	if (content)
		content->Encode();

	if (fillExpression.isLoaded())
	{
		int64_t subAreaUsage = Allocations::getSubAreaUsage(fileID, position);
		if (subAreaUsage != 0)
			g_fileManager->advanceMemory(subAreaUsage);

		unsigned char buffer[64];
		memset(buffer,fillValue,64);

		size_t writeSize = areaSize-contentSize-subAreaUsage;
		while (writeSize > 0)
		{
			size_t part = std::min<size_t>(64,writeSize);
			g_fileManager->write(buffer,part);
			writeSize -= part;
		}
	}
	else if (shared)
		g_fileManager->advanceMemory(areaSize-contentSize);
}

void CDirectiveArea::writeTempData(TempData& tempData) const
{
	const char *directiveType = shared ? "region" : "area";
	if (positionExpression.isLoaded())
		tempData.writeLine(position, tfm::format(".org 0x%08llX", position));
	if (shared && fillExpression.isLoaded())
		tempData.writeLine(position,tfm::format(".%S 0x%08X,0x%02x",directiveType,areaSize,fillValue));
	else
		tempData.writeLine(position,tfm::format(".%S 0x%08X",directiveType,areaSize));
	if (content)
	{
		content->applyFileInfo();
		content->writeTempData(tempData);
	}

	if (fillExpression.isLoaded() && !shared)
	{
		int64_t subAreaUsage = Allocations::getSubAreaUsage(fileID, position);
		if (subAreaUsage != 0)
			tempData.writeLine(position+contentSize, tfm::format(".skip 0x%08llX",subAreaUsage));

		std::string fillString = tfm::format(".fill 0x%08X,0x%02X",areaSize-contentSize-subAreaUsage,fillValue);
		tempData.writeLine(position+contentSize+subAreaUsage,fillString);
		tempData.writeLine(position+areaSize,tfm::format(".end%S",directiveType));
	} else {
		tempData.writeLine(position+contentSize,tfm::format(".end%S",directiveType));
	}
}

void CDirectiveArea::writeSymData(SymbolData& symData) const
{
	if (content)
		content->writeSymData(symData);

	if (fillExpression.isLoaded())
	{
		int64_t subAreaUsage = Allocations::getSubAreaUsage(fileID, position);
		symData.addData(position+contentSize+subAreaUsage,areaSize-contentSize-subAreaUsage,SymbolData::Data8);
	}
}

CDirectiveAutoRegion::CDirectiveAutoRegion()
{
	this->contentSize = 0;
	this->resetPosition = 0;
	this->position = -1;

	this->content = nullptr;
}

void CDirectiveAutoRegion::setMinRangeExpression(Expression& exp)
{
	minRangeExpression = exp;
}

void CDirectiveAutoRegion::setRangeExpressions(Expression& minExp, Expression& maxExp)
{
	minRangeExpression = minExp;
	maxRangeExpression = maxExp;
}

bool CDirectiveAutoRegion::Validate(const ValidateState &state)
{
	resetPosition = g_fileManager->getVirtualAddress();

	ValidateState contentValidation = state;
	contentValidation.noFileChange = true;
	contentValidation.noFileChangeDirective = "region";

	// We need at least one full pass run before we can get an address.
	if (state.passes < 1)
	{
		// Just calculate contentSize.
		position = g_fileManager->getVirtualAddress();
		content->applyFileInfo();
		content->Validate(contentValidation);
		contentSize = g_fileManager->getVirtualAddress() - position;

		g_fileManager->seekVirtual(resetPosition);
		return true;
	}

	int64_t oldPosition = position;
	int64_t oldContentSize = contentSize;

	int64_t minRange = -1;
	int64_t maxRange = -1;
	if (minRangeExpression.isLoaded())
	{
		if (!minRangeExpression.evaluateInteger(minRange))
		{
			Logger::queueError(Logger::Error, "Invalid range expression for .autoregion");
			return false;
		}
	}
	if (maxRangeExpression.isLoaded())
	{
		if (!maxRangeExpression.evaluateInteger(maxRange))
		{
			Logger::queueError(Logger::Error, "Invalid range expression for .autoregion");
			return false;
		}
	}

	fileID = g_fileManager->getOpenFileID();
	if (!Allocations::allocateSubArea(fileID, position, minRange, maxRange, contentSize))
	{
		Logger::queueError(Logger::Error, "No space available for .autoregion of size %d", contentSize);
		// We might be able to do better next time.
		return Allocations::canTrimSpace();
	}

	Architecture::current().NextSection();
	g_fileManager->seekVirtual(position);

	content->applyFileInfo();
	bool result = content->Validate(contentValidation);
	contentSize = g_fileManager->getVirtualAddress() - position;

	// restore info of this command
	applyFileInfo();
	g_fileManager->seekVirtual(resetPosition);

	if (position != oldPosition || contentSize != oldContentSize)
		result = true;

	return result;
}

void CDirectiveAutoRegion::Encode() const
{
	Architecture::current().NextSection();
	g_fileManager->seekVirtual(position);
	content->Encode();
	g_fileManager->seekVirtual(resetPosition);
}

void CDirectiveAutoRegion::writeTempData(TempData& tempData) const
{
	tempData.writeLine(position,tfm::format(".autoregion 0x%08X",position));
	content->applyFileInfo();
	content->writeTempData(tempData);
	tempData.writeLine(position+contentSize,".endautoregion");
}

void CDirectiveAutoRegion::writeSymData(SymbolData& symData) const
{
	content->writeSymData(symData);
}

#pragma region "File: Commands/CDirectiveConditional.h"


enum class ConditionType
{
	IF,
	ELSE,
	ELSEIF,
	ENDIF,
	IFDEF,
	IFNDEF,
};

class CDirectiveConditional: public CAssemblerCommand
{
public:
	CDirectiveConditional(ConditionType type);
	CDirectiveConditional(ConditionType type, const Identifier& name);
	CDirectiveConditional(ConditionType type, const Expression& exp);
	bool Validate(const ValidateState &state) override;
	void Encode() const override;
	void writeTempData(TempData& tempData) const override;
	void writeSymData(SymbolData& symData) const override;
	void setContent(std::unique_ptr<CAssemblerCommand> ifBlock, std::unique_ptr<CAssemblerCommand> elseBlock);
private:
	bool evaluate();

	Expression expression;
	std::shared_ptr<Label> label;
	bool previousResult;

	ConditionType type;
	std::unique_ptr<CAssemblerCommand> ifBlock;
	std::unique_ptr<CAssemblerCommand> elseBlock;
};

#pragma region "File: Commands/CDirectiveConditional.cpp"


CDirectiveConditional::CDirectiveConditional(ConditionType type)
{
	this->type = type;

	ifBlock = nullptr;
	elseBlock = nullptr;
	previousResult = false;
}

CDirectiveConditional::CDirectiveConditional(ConditionType type, const Identifier& name)
	: CDirectiveConditional(type)
{
	label = Global.symbolTable.getLabel(name,Global.FileInfo.FileNum,Global.Section);
	if (label == nullptr)
		Logger::printError(Logger::Error, "Invalid label name \"%s\"",name);
}

CDirectiveConditional::CDirectiveConditional(ConditionType type, const Expression& exp)
	: CDirectiveConditional(type)
{
	this->expression = exp;
}

void CDirectiveConditional::setContent(std::unique_ptr<CAssemblerCommand> ifBlock, std::unique_ptr<CAssemblerCommand> elseBlock)
{
	this->ifBlock = std::move(ifBlock);
	this->elseBlock = std::move(elseBlock);
}

bool CDirectiveConditional::evaluate()
{
	int64_t value = 0;
	if (expression.isLoaded())
	{
		if (!expression.evaluateInteger(value))
		{
			Logger::queueError(Logger::Error, "Invalid conditional expression");
			return false;
		}
	}

	switch (type)
	{
	case ConditionType::IF:
		return value != 0;
	case ConditionType::IFDEF:
		return label->isDefined();
	case ConditionType::IFNDEF:
		return !label->isDefined();
	default:
		break;
	}

	Logger::queueError(Logger::Error, "Invalid conditional type");
	return false;
}

bool CDirectiveConditional::Validate(const ValidateState &state)
{
	bool result = evaluate();
	bool returnValue = result != previousResult;
	previousResult = result;

	if (result)
	{
		ifBlock->applyFileInfo();
		if (ifBlock->Validate(state))
			returnValue = true;
	} else if (elseBlock != nullptr)
	{
		elseBlock->applyFileInfo();
		if (elseBlock->Validate(state))
			returnValue = true;
	}

	return returnValue;
}

void CDirectiveConditional::Encode() const
{
	if (previousResult)
	{
		ifBlock->Encode();
	} else if (elseBlock != nullptr)
	{
		elseBlock->Encode();
	}
}

void CDirectiveConditional::writeTempData(TempData& tempData) const
{
	if (previousResult)
	{
		ifBlock->applyFileInfo();
		ifBlock->writeTempData(tempData);
	} else if (elseBlock != nullptr)
	{
		elseBlock->applyFileInfo();
		elseBlock->writeTempData(tempData);
	}
}

void CDirectiveConditional::writeSymData(SymbolData& symData) const
{
	if (previousResult)
	{
		ifBlock->writeSymData(symData);
	} else if (elseBlock != nullptr)
	{
		elseBlock->writeSymData(symData);
	}
}

#pragma region "File: Commands/CDirectiveData.h"


enum class EncodingMode { Invalid, U8, U16, U32, U64, Ascii, Float, Double, Sjis, Custom };

class TableCommand: public CAssemblerCommand
{
public:
	TableCommand(const fs::path& fileName, TextFile::Encoding encoding);
	bool Validate(const ValidateState &state) override;
	void Encode() const override { };
	void writeTempData(TempData& tempData) const override { };
	void writeSymData(SymbolData& symData) const override { };
private:
	EncodingTable table;
};

class CDirectiveData: public CAssemblerCommand
{
public:
	CDirectiveData();
	~CDirectiveData();
	void setNormal(std::vector<Expression>& entries, size_t unitSize);
	void setFloat(std::vector<Expression>& entries);
	void setDouble(std::vector<Expression>& entries);
	void setAscii(std::vector<Expression>& entries, bool terminate);
	void setSjis(std::vector<Expression>& entries, bool terminate);
	void setCustom(std::vector<Expression>& entries, bool terminate);
	bool Validate(const ValidateState &state) override;
	void Encode() const override;
	void writeTempData(TempData& tempData) const override;
	void writeSymData(SymbolData& symData) const override;
private:
	void encodeCustom(EncodingTable& table);
	void encodeSjis();
	void encodeFloat();
	void encodeDouble();
	void encodeNormal();
	size_t getUnitSize() const;
	size_t getDataSize() const;

	int64_t position = 0;
	EncodingMode mode;
	bool writeTermination;
	std::vector<Expression> entries;
	ByteArray customData;
	std::vector<int64_t> normalData;
	Endianness endianness;
};

#pragma region "File: Commands/CDirectiveData.cpp"

#include <cinttypes>

//
// TableCommand
//

TableCommand::TableCommand(const fs::path& fileName, TextFile::Encoding encoding)
{
	auto fullName = getFullPathName(fileName);

	if (!fs::exists(fullName))
	{
		Logger::printError(Logger::Error, "Table file \"%s\" does not exist", fileName.u8string());
		return;
	}

	if (!table.load(fullName,encoding))
	{
		Logger::printError(Logger::Error, "Invalid table file \"%s\"",fileName.u8string());
		return;
	}
}

bool TableCommand::Validate(const ValidateState &state)
{
	Global.Table = table;
	return false;
}


//
// CDirectiveData
//

CDirectiveData::CDirectiveData()
{
	mode = EncodingMode::Invalid;
	writeTermination = false;
	endianness = Architecture::current().getEndianness();
}

CDirectiveData::~CDirectiveData()
{

}

void CDirectiveData::setNormal(std::vector<Expression>& entries, size_t unitSize)
{
	switch (unitSize)
	{
	case 1:
		this->mode = EncodingMode::U8;
		break;
	case 2:
		this->mode = EncodingMode::U16;
		break;
	case 4:
		this->mode = EncodingMode::U32;
		break;
	case 8:
		this->mode = EncodingMode::U64;
		break;
	default:
		Logger::printError(Logger::Error, "Invalid data unit size %d",unitSize);
		return;
	}

	this->entries = entries;
	this->writeTermination = false;
	normalData.reserve(entries.size());
}

void CDirectiveData::setFloat(std::vector<Expression>& entries)
{
	this->mode = EncodingMode::Float;
	this->entries = entries;
	this->writeTermination = false;
}

void CDirectiveData::setDouble(std::vector<Expression>& entries)
{
	this->mode = EncodingMode::Double;
	this->entries = entries;
	this->writeTermination = false;
}

void CDirectiveData::setAscii(std::vector<Expression>& entries, bool terminate)
{
	this->mode = EncodingMode::Ascii;
	this->entries = entries;
	this->writeTermination = terminate;
}

void CDirectiveData::setSjis(std::vector<Expression>& entries, bool terminate)
{
	this->mode = EncodingMode::Sjis;
	this->entries = entries;
	this->writeTermination = terminate;
}

void CDirectiveData::setCustom(std::vector<Expression>& entries, bool terminate)
{
	this->mode = EncodingMode::Custom;
	this->entries = entries;
	this->writeTermination = terminate;
}

size_t CDirectiveData::getUnitSize() const
{
	switch (mode)
	{
	case EncodingMode::U8:
	case EncodingMode::Ascii:
	case EncodingMode::Sjis:
	case EncodingMode::Custom:
		return 1;
	case EncodingMode::U16:
		return 2;
	case EncodingMode::U32:
	case EncodingMode::Float:
		return 4;
	case EncodingMode::U64:
	case EncodingMode::Double:
		return 8;
	case EncodingMode::Invalid:
		break;
	}

	return 0;
}

size_t CDirectiveData::getDataSize() const
{
	switch (mode)
	{
	case EncodingMode::Sjis:
	case EncodingMode::Custom:
		return customData.size();
	case EncodingMode::U8:
	case EncodingMode::Ascii:
	case EncodingMode::U16:
	case EncodingMode::U32:
	case EncodingMode::U64:
	case EncodingMode::Float:
	case EncodingMode::Double:
		return normalData.size()*getUnitSize();
	case EncodingMode::Invalid:
		break;
	}

	return 0;
}

void CDirectiveData::encodeCustom(EncodingTable& table)
{
	customData.clear();
	for (size_t i = 0; i < entries.size(); i++)
	{
		ExpressionValue value = entries[i].evaluate();
		if (!value.isValid())
		{
			Logger::queueError(Logger::Error, "Invalid expression");
			continue;
		}

		if (value.isInt())
		{
			customData.appendByte((byte)value.intValue);
		} else if (value.isString())
		{
			ByteArray encoded = table.encodeString(value.strValue.string(),false);
			if (encoded.size() == 0 && value.strValue.size() > 0)
			{
				Logger::queueError(Logger::Error, "Failed to encode \"%s\"",value.strValue);
			}
			customData.append(encoded);
		} else {
			Logger::queueError(Logger::Error, "Invalid expression type");
		}
	}

	if (writeTermination)
	{
		ByteArray encoded = table.encodeTermination();
		customData.append(encoded);
	}
}

void CDirectiveData::encodeSjis()
{
	static EncodingTable sjisTable;
	if (!sjisTable.isLoaded())
	{
		unsigned char hexBuffer[2];

		sjisTable.setTerminationEntry((unsigned char*)"\0",1);

		for (unsigned short SJISValue = 0x0001; SJISValue < 0x0100; SJISValue++)
		{
			if (auto unicodeValue = sjisToUnicode(SJISValue))
			{
				hexBuffer[0] = SJISValue & 0xFF;
				sjisTable.addEntry(hexBuffer, 1, convertUnicodeCharToUtf8(*unicodeValue));
			}
		}
		for (unsigned short SJISValue = 0x8100; SJISValue < 0xEF00; SJISValue++)
		{
			if (auto unicodeValue = sjisToUnicode(SJISValue))
			{
				hexBuffer[0] = (SJISValue >> 8) & 0xFF;
				hexBuffer[1] = SJISValue & 0xFF;
				sjisTable.addEntry(hexBuffer, 2, convertUnicodeCharToUtf8(*unicodeValue));
			}
		}
	}

	encodeCustom(sjisTable);
}

void CDirectiveData::encodeFloat()
{
	normalData.clear();
	for (size_t i = 0; i < entries.size(); i++)
	{
		ExpressionValue value = entries[i].evaluate();
		if (!value.isValid())
		{
			Logger::queueError(Logger::Error, "Invalid expression");
			continue;
		}

		if (value.isInt() && mode == EncodingMode::Float)
		{
			int32_t num = getFloatBits((float)value.intValue);
			normalData.push_back(num);
		} else if (value.isInt() && mode == EncodingMode::Double)
		{
			int64_t num = getDoubleBits((double)value.intValue);
			normalData.push_back(num);
		} else if (value.isFloat() && mode == EncodingMode::Float)
		{
			int32_t num = getFloatBits((float)value.floatValue);
			normalData.push_back(num);
		} else if (value.isFloat() && mode == EncodingMode::Double)
		{
			int64_t num = getDoubleBits((double)value.floatValue);
			normalData.push_back(num);
		} else {
			Logger::queueError(Logger::Error, "Invalid expression type");
		}
	}
}

void CDirectiveData::encodeNormal()
{
	normalData.clear();
	for (size_t i = 0; i < entries.size(); i++)
	{
		ExpressionValue value = entries[i].evaluate();
		if (!value.isValid())
		{
			Logger::queueError(Logger::Error, "Invalid expression");
			continue;
		}

		if (value.isString())
		{
			bool hadNonAscii = false;
			for (size_t l = 0; l < value.strValue.size(); l++)
			{
				uint64_t num = value.strValue.string()[l];
				normalData.push_back(num);

				if (num >= 0x80 && !hadNonAscii)
				{
					Logger::queueError(Logger::Error, "Non-ASCII character in data directive. Use .string instead");
					hadNonAscii = true;
				}
			}
		} else if (value.isInt())
		{
			int64_t num = value.intValue;
			normalData.push_back(num);
		} else if (value.isFloat() && mode == EncodingMode::U32)
		{
			int32_t num = getFloatBits((float)value.floatValue);
			normalData.push_back(num);
		} else if(value.isFloat() && mode == EncodingMode::U64) {
			int64_t num = getDoubleBits((double)value.floatValue);
			normalData.push_back(num);
		} else {
			Logger::queueError(Logger::Error, "Invalid expression type");
		}
	}

	if (writeTermination)
	{
		normalData.push_back(0);
	}
}

bool CDirectiveData::Validate(const ValidateState &state)
{
	position = g_fileManager->getVirtualAddress();

	size_t oldSize = getDataSize();
	switch (mode)
	{
	case EncodingMode::U8:
	case EncodingMode::U16:
	case EncodingMode::U32:
	case EncodingMode::U64:
	case EncodingMode::Ascii:
		encodeNormal();
		break;
	case EncodingMode::Float:
	case EncodingMode::Double:
		encodeFloat();
		break;
	case EncodingMode::Sjis:
		encodeSjis();
		break;
	case EncodingMode::Custom:
		encodeCustom(Global.Table);
		break;
	default:
		Logger::queueError(Logger::Error, "Invalid encoding type");
		break;
	}

	g_fileManager->advanceMemory(getDataSize());
	return oldSize != getDataSize();
}

void CDirectiveData::Encode() const
{
	switch (mode)
	{
	case EncodingMode::Sjis:
	case EncodingMode::Custom:
		g_fileManager->write(customData.data(),customData.size());
		break;
	case EncodingMode::U8:
	case EncodingMode::Ascii:
		for (auto value: normalData)
		{
			g_fileManager->writeU8((uint8_t)value);
		}
		break;
	case EncodingMode::U16:
		for (auto value: normalData)
		{
			g_fileManager->writeU16((uint16_t)value);
		}
		break;
	case EncodingMode::U32:
	case EncodingMode::Float:
		for (auto value: normalData)
		{
			g_fileManager->writeU32((uint32_t)value);
		}
		break;
	case EncodingMode::U64:
	case EncodingMode::Double:
		for (auto value: normalData)
		{
			g_fileManager->writeU64((uint64_t)value);
		}
		break;
	case EncodingMode::Invalid:
		// TODO: Assert?
		break;
	}
}

void CDirectiveData::writeTempData(TempData& tempData) const
{
	size_t size = (getUnitSize()*2+3)*getDataSize()+20;
	char* str = new char[size];
	char* start = str;
	char* end = start + size;

	switch (mode)
	{
	case EncodingMode::Sjis:
	case EncodingMode::Custom:
		str += snprintf(str,end-str,".byte ");

		for (size_t i = 0; i < customData.size(); i++)
		{
			str += snprintf(str,end-str,"0x%02X,",(uint8_t)customData[i]);
		}
		break;
	case EncodingMode::U8:
	case EncodingMode::Ascii:
		str += snprintf(str,end-str,".byte ");

		for (size_t i = 0; i < normalData.size(); i++)
		{
			str += snprintf(str,end-str,"0x%02X,",(uint8_t)normalData[i]);
		}
		break;
	case EncodingMode::U16:
		str += snprintf(str,end-str,".halfword ");

		for (size_t i = 0; i < normalData.size(); i++)
		{
			str += snprintf(str,end-str,"0x%04X,",(uint16_t)normalData[i]);
		}
		break;
	case EncodingMode::U32:
	case EncodingMode::Float:
		str += snprintf(str,end-str, ".word ");

		for (size_t i = 0; i < normalData.size(); i++)
		{
			str += snprintf(str,end-str,"0x%08X,",(uint32_t)normalData[i]);
		}
		break;
	case EncodingMode::U64:
	case EncodingMode::Double:
		str += snprintf(str,end-str,".doubleword ");

		for (size_t i = 0; i < normalData.size(); i++)
		{
			str += snprintf(str,end-str,"0x%16" PRIx64 ",",(uint64_t)normalData[i]);
		}
		break;
	case EncodingMode::Invalid:
		// TODO: Assert?
		str[0] = '\0';
		break;
	}

	tempData.writeLine(position,start);
	delete[] start;
}

void CDirectiveData::writeSymData(SymbolData& symData) const
{
	switch (mode)
	{
	case EncodingMode::Ascii:
		symData.addData(position,getDataSize(),SymbolData::DataAscii);
		break;
	case EncodingMode::U8:
	case EncodingMode::Sjis:
	case EncodingMode::Custom:
		symData.addData(position,getDataSize(),SymbolData::Data8);
		break;
	case EncodingMode::U16:
		symData.addData(position,getDataSize(),SymbolData::Data16);
		break;
	case EncodingMode::U32:
	case EncodingMode::Float:
		symData.addData(position,getDataSize(),SymbolData::Data32);
		break;
	case EncodingMode::U64:
	case EncodingMode::Double:
		symData.addData(position,getDataSize(),SymbolData::Data64);
		break;
	case EncodingMode::Invalid:
		// TODO: Assert?
		break;
	}
}

#pragma region "File: Commands/CDirectiveFile.cpp"


#include <cstring>

//
// CDirectiveFile
//

CDirectiveFile::CDirectiveFile()
{
	type = Type::Invalid;
	file = nullptr;
}

void CDirectiveFile::initOpen(const fs::path& fileName, int64_t memory)
{
	type = Type::Open;
	fs::path fullName = getFullPathName(fileName);

	file = std::make_shared<GenericAssemblerFile>(fullName,memory,false);
	g_fileManager->addFile(file);

	updateSection(++Global.Section);
}

void CDirectiveFile::initCreate(const fs::path& fileName, int64_t memory)
{
	type = Type::Create;
	fs::path fullName = getFullPathName(fileName);

	file = std::make_shared<GenericAssemblerFile>(fullName,memory,true);
	g_fileManager->addFile(file);

	updateSection(++Global.Section);
}

void CDirectiveFile::initCopy(const fs::path& inputName, const fs::path& outputName, int64_t memory)
{
	type = Type::Copy;
	fs::path fullInputName = getFullPathName(inputName);
	fs::path fullOutputName = getFullPathName(outputName);

	file = std::make_shared<GenericAssemblerFile>(fullOutputName,fullInputName,memory);
	g_fileManager->addFile(file);

	updateSection(++Global.Section);
}

void CDirectiveFile::initClose()
{
	type = Type::Close;
	updateSection(++Global.Section);
}

bool CDirectiveFile::Validate(const ValidateState &state)
{
	if (state.noFileChange)
	{
		if (type == Type::Close)
			Logger::queueError(Logger::Error, "Cannot close file within %S", state.noFileChangeDirective);
		else
			Logger::queueError(Logger::Error, "Cannot open new file within %S", state.noFileChangeDirective);
		return false;
	}

	virtualAddress = g_fileManager->getVirtualAddress();
	Architecture::current().NextSection();

	switch (type)
	{
	case Type::Open:
	case Type::Create:
	case Type::Copy:
		g_fileManager->openFile(file,true);
		return false;
	case Type::Close:
		closeFile = g_fileManager->getOpenFile();
		g_fileManager->closeFile();
		return false;
	case Type::Invalid:
		break;
	}

	return false;
}

void CDirectiveFile::Encode() const
{
	switch (type)
	{
	case Type::Open:
	case Type::Create:
	case Type::Copy:
		g_fileManager->openFile(file,false);
		break;
	case Type::Close:
		g_fileManager->closeFile();
		break;
	case Type::Invalid:
		// TODO: Assert?
		break;
	}
}

void CDirectiveFile::writeTempData(TempData& tempData) const
{
	std::string str;

	switch (type)
	{
	case Type::Open:
		str = tfm::format(".open \"%s\",0x%08X",file->getFileName().u8string(),file->getOriginalHeaderSize());
		break;
	case Type::Create:
		str = tfm::format(".create \"%s\",0x%08X",file->getFileName().u8string(),file->getOriginalHeaderSize());
		break;
	case Type::Copy:
		str = tfm::format(".open \"%s\",\"%s\",0x%08X",file->getOriginalFileName().u8string(),
			file->getFileName().u8string(),file->getOriginalHeaderSize());
		break;
	case Type::Close:
		str = ".close";
		break;
	case Type::Invalid:
		// TODO: Assert?
		break;
	}

	tempData.writeLine(virtualAddress,str);
}

void CDirectiveFile::writeSymData(SymbolData& symData) const
{
	switch (type)
	{
	case Type::Open:
	case Type::Create:
	case Type::Copy:
		file->beginSymData(symData);
		break;
	case Type::Close:
		if (closeFile)
			closeFile->endSymData(symData);
		break;
	case Type::Invalid:
		// TODO: Assert?
		break;
	}
}

//
// CDirectivePosition
//

CDirectivePosition::CDirectivePosition(Expression expression, Type type)
	: expression(expression), type(type)
{
	updateSection(++Global.Section);
}

void CDirectivePosition::exec() const
{
	switch (type)
	{
	case Physical:
		g_fileManager->seekPhysical(position);
		break;
	case Virtual:
		g_fileManager->seekVirtual(position);
		break;
	}
}

bool CDirectivePosition::Validate(const ValidateState &state)
{
	virtualAddress = g_fileManager->getVirtualAddress();

	if (!expression.evaluateInteger(position))
	{
		Logger::queueError(Logger::FatalError, "Invalid position");
		return false;
	}

	Architecture::current().NextSection();
	exec();
	return false;
}

void CDirectivePosition::Encode() const
{
	Architecture::current().NextSection();
	exec();
}

void CDirectivePosition::writeTempData(TempData& tempData) const
{
	switch (type)
	{
	case Physical:
		tempData.writeLine(virtualAddress,tfm::format(".orga 0x%08X",position));
		break;
	case Virtual:
		tempData.writeLine(virtualAddress,tfm::format(".org 0x%08X",position));
		break;
	}
}

//
// CDirectiveIncbin
//

CDirectiveIncbin::CDirectiveIncbin(const fs::path& fileName)
	: size(0), start(0)
{
	this->fileName = getFullPathName(fileName);

	if (!fs::exists(this->fileName))
	{
		Logger::printError(Logger::FatalError, "File %s not found",this->fileName.u8string());
	}

	std::error_code error;
	this->fileSize = static_cast<int64_t>(fs::file_size(this->fileName, error));
}

bool CDirectiveIncbin::Validate(const ValidateState &state)
{
	virtualAddress = g_fileManager->getVirtualAddress();

	if (startExpression.isLoaded())
	{
		if (!startExpression.evaluateInteger(start))
		{
			Logger::queueError(Logger::Error, "Invalid position expression");
			return false;
		}

		if (start > fileSize)
		{
			Logger::queueError(Logger::Error, "Start position past end of file");
			return false;
		}
	} else {
		start = 0;
	}

	if (sizeExpression.isLoaded())
	{
		if (!sizeExpression.evaluateInteger(size))
		{
			Logger::queueError(Logger::Error, "Invalid size expression");
			return false;
		}
	} else {
		size = fileSize-start;
	}

	if (start+size > fileSize)
	{
		Logger::queueError(Logger::Warning, "Read size truncated due to file size");
		size = fileSize-start;
	}

	Architecture::current().NextSection();
	g_fileManager->advanceMemory(size);
	return false;
}

void CDirectiveIncbin::Encode() const
{
	if (size != 0)
	{
		ByteArray data = ByteArray::fromFile(fileName,(long)start,size);
		if ((int) data.size() != size)
		{
			Logger::printError(Logger::Error, "Could not read file \"%s\"",fileName.u8string());
			return;
		}
		g_fileManager->write(data.data(),data.size());
	}
}

void CDirectiveIncbin::writeTempData(TempData& tempData) const
{
	tempData.writeLine(virtualAddress,tfm::format(".incbin \"%s\"",fileName.u8string()));
}

void CDirectiveIncbin::writeSymData(SymbolData& symData) const
{
	symData.addData(virtualAddress,size,SymbolData::Data8);
}


//
// CDirectiveAlignFill
//

CDirectiveAlignFill::CDirectiveAlignFill(int64_t value, Mode mode)
{
	this->mode = mode;
	this->value = value;
	this->finalSize = 0;
	this->fillByte = 0;
}

CDirectiveAlignFill::CDirectiveAlignFill(Expression& value, Mode mode)
	: CDirectiveAlignFill(0,mode)
{
	valueExpression = value;
}

CDirectiveAlignFill::CDirectiveAlignFill(Expression& value, Expression& fillValue, Mode mode)
	: CDirectiveAlignFill(value,mode)
{
	fillExpression = fillValue;
}

bool CDirectiveAlignFill::Validate(const ValidateState &state)
{
	virtualAddress = g_fileManager->getVirtualAddress();

	if (valueExpression.isLoaded())
	{
		if (!valueExpression.evaluateInteger(value))
		{
			Logger::queueError(Logger::FatalError, "Invalid %s",mode == Fill ? "size" : "alignment");
			return false;
		}
	}

	if (mode != Fill && !isPowerOfTwo(value))
	{
		Logger::queueError(Logger::Error, "Invalid alignment %d", value);
		return false;
	}

	int64_t oldSize = finalSize;
	int64_t mod;
	switch (mode)
	{
	case AlignVirtual:
		mod = g_fileManager->getVirtualAddress() % value;
		finalSize = mod ? value-mod : 0;
		break;
	case AlignPhysical:
		mod = g_fileManager->getPhysicalAddress() % value;
		finalSize = mod ? value-mod : 0;
		break;
	case Fill:
		finalSize = value;
		break;
	}

	if (fillExpression.isLoaded())
	{
		if (!fillExpression.evaluateInteger(fillByte))
		{
			Logger::printError(Logger::FatalError, "Invalid fill value");
			return false;
		}
	}

	Architecture::current().NextSection();
	g_fileManager->advanceMemory(finalSize);

	bool result = oldSize != finalSize;
	oldSize = finalSize;
	return result;
}

void CDirectiveAlignFill::Encode() const
{
	unsigned char buffer[128];
	int64_t n = finalSize;

	memset(buffer,fillByte,n > 128 ? 128 : n);
	while (n > 128)
	{
		g_fileManager->write(buffer,128);
		n -= 128;
	}

	g_fileManager->write(buffer,n);
}

void CDirectiveAlignFill::writeTempData(TempData& tempData) const
{
	switch (mode)
	{
	case AlignVirtual:
		tempData.writeLine(virtualAddress,tfm::format(".align 0x%08X",value));
		break;
	case AlignPhysical:
		tempData.writeLine(virtualAddress, tfm::format(".aligna 0x%08X", value));
		break;
	case Fill:
		tempData.writeLine(virtualAddress,tfm::format(".fill 0x%08X,0x%02X",value,fillByte));
		break;
	}
}

void CDirectiveAlignFill::writeSymData(SymbolData& symData) const
{
	switch (mode)
	{
	case AlignVirtual:	// ?
	case AlignPhysical:	// ?
		break;
	case Fill:
		symData.addData(virtualAddress,value,SymbolData::Data8);
		break;
	}
}

//
// CDirectiveSkip
//

CDirectiveSkip::CDirectiveSkip(Expression& expression)
	: expression(expression) {}

bool CDirectiveSkip::Validate(const ValidateState &state)
{
	virtualAddress = g_fileManager->getVirtualAddress();

	if (expression.isLoaded())
	{
		if (!expression.evaluateInteger(value))
		{
			Logger::queueError(Logger::FatalError, "Invalid skip length");
			return false;
		}
	}

	Architecture::current().NextSection();
	g_fileManager->advanceMemory(value);

	return false;
}

void CDirectiveSkip::Encode() const
{
	Architecture::current().NextSection();
	g_fileManager->advanceMemory(value);
}

void CDirectiveSkip::writeTempData(TempData& tempData) const
{
	tempData.writeLine(virtualAddress,tfm::format(".skip 0x%08X",value));
}

//
// CDirectiveHeaderSize
//

CDirectiveHeaderSize::CDirectiveHeaderSize(Expression expression)
	: expression(expression) {}

void CDirectiveHeaderSize::exec() const
{
	std::shared_ptr<AssemblerFile> openFile = g_fileManager->getOpenFile();
	if (!openFile->hasFixedVirtualAddress())
	{
		Logger::printError(Logger::Error, "Header size not applicable for this file");
		return;
	}
	std::shared_ptr<GenericAssemblerFile> file = std::static_pointer_cast<GenericAssemblerFile>(openFile);
	int64_t physicalAddress = file->getPhysicalAddress();
	file->setHeaderSize(headerSize);
	file->seekPhysical(physicalAddress);
}

bool CDirectiveHeaderSize::Validate(const ValidateState &state)
{
	virtualAddress = g_fileManager->getVirtualAddress();

	if (!expression.evaluateInteger(headerSize))
	{
		Logger::queueError(Logger::FatalError, "Invalid header size");
		return false;
	}

	exec();
	return false;
}

void CDirectiveHeaderSize::Encode() const
{
	exec();
}

void CDirectiveHeaderSize::writeTempData(TempData& tempData) const
{
	tempData.writeLine(virtualAddress,tfm::format(".headersize %s0x%08X",
		headerSize < 0 ? "-" : "", headerSize < 0 ? -headerSize : headerSize));
}


//
// DirectiveObjImport
//

DirectiveObjImport::DirectiveObjImport(const fs::path& inputName)
{
	ctor = nullptr;
	success = rel.init(inputName);
	if (success)
		rel.exportSymbols();
}

DirectiveObjImport::DirectiveObjImport(const fs::path& inputName, const Identifier& ctorName)
{
	success = rel.init(inputName);
	if (success)
	{
		rel.exportSymbols();
		ctor = rel.generateCtor(ctorName);
	}
}

bool DirectiveObjImport::Validate(const ValidateState &state)
{
	bool result = false;
	if (ctor != nullptr && ctor->Validate(state))
		result = true;

	int64_t memory = g_fileManager->getVirtualAddress();
	rel.relocate(memory);
	g_fileManager->advanceMemory((size_t)memory);

	return rel.hasDataChanged() || result;
}

void DirectiveObjImport::Encode() const
{
	if (ctor != nullptr)
		ctor->Encode();

	const ByteArray& data = rel.getData();
	g_fileManager->write(data.data(),data.size());
}

void DirectiveObjImport::writeTempData(TempData& tempData) const
{
	if (ctor != nullptr)
		ctor->writeTempData(tempData);
}

void DirectiveObjImport::writeSymData(SymbolData& symData) const
{
	if (ctor != nullptr)
		ctor->writeSymData(symData);

	rel.writeSymbols(symData);
}

#pragma region "File: Commands/CDirectiveMessage.h"


class CDirectiveMessage: public CAssemblerCommand
{
public:
	enum class Type { Warning, Error, Notice };
	CDirectiveMessage(Type type, Expression exp);
	bool Validate(const ValidateState &state) override;
	void Encode() const override {};
	void writeTempData(TempData& tempData) const override { };
private:
	Type errorType;
	Expression exp;
};

class CDirectiveSym: public CAssemblerCommand
{
public:
	CDirectiveSym(bool enable) {enabled = enable; };
	bool Validate(const ValidateState &state) override { return false; }
	void Encode() const override { };
	void writeTempData(TempData& tempData) const override { };
	void writeSymData(SymbolData& symData) const override;
private:
	bool enabled;
};

#pragma region "File: Commands/CDirectiveMessage.cpp"


CDirectiveMessage::CDirectiveMessage(Type type, Expression exp)
	: errorType(type), exp(exp)
{
}

bool CDirectiveMessage::Validate(const ValidateState &state)
{
	StringLiteral text;
	if (!exp.evaluateString(text,true))
	{
		Logger::queueError(Logger::Error, "Invalid expression");
		return false;
	}

	switch (errorType)
	{
	case Type::Warning:
		Logger::queueError(Logger::Warning,text.string());
		break;
	case Type::Error:
		Logger::queueError(Logger::Error,text.string());
		break;
	case Type::Notice:
		Logger::queueError(Logger::Notice,text.string());
		break;
	}
	return false;
}

void CDirectiveSym::writeSymData(SymbolData &symData) const
{
	symData.setEnabled(enabled);
}

#pragma region "File: Commands/CommandSequence.cpp"

CommandSequence::CommandSequence()
	: CAssemblerCommand()
{

}

bool CommandSequence::Validate(const ValidateState &state)
{
	bool result = false;

	for (const std::unique_ptr<CAssemblerCommand>& cmd: commands)
	{
		cmd->applyFileInfo();
		if (cmd->Validate(state))
			result = true;
	}

	return result;
}

void CommandSequence::Encode() const
{
	for (const std::unique_ptr<CAssemblerCommand>& cmd: commands)
	{
		cmd->Encode();
	}
}

void CommandSequence::writeTempData(TempData& tempData) const
{
	for (const std::unique_ptr<CAssemblerCommand>& cmd: commands)
	{
		cmd->applyFileInfo();
		cmd->writeTempData(tempData);
	}
}

void CommandSequence::writeSymData(SymbolData& symData) const
{
	for (const std::unique_ptr<CAssemblerCommand>& cmd: commands)
	{
		cmd->writeSymData(symData);
	}
}

#pragma region "File: Parser/DirectivesParser.cpp"


#include <algorithm>
#include <initializer_list>
#include <optional>

std::unique_ptr<CAssemblerCommand> parseDirectiveOpen(Parser& parser, int flags)
{
	std::vector<Expression> list;
	if (!parser.parseExpressionList(list,2,3))
		return nullptr;

	int64_t memoryAddress;
	StringLiteral inputName, outputName;

	if (!list[0].evaluateString(inputName,false))
		return nullptr;

	if (!list.back().evaluateInteger(memoryAddress))
		return nullptr;

	auto file = std::make_unique<CDirectiveFile>();
	if (list.size() == 3)
	{
		if (!list[1].evaluateString(outputName,false))
			return nullptr;

		file->initCopy(inputName.path(),outputName.path(),memoryAddress);
		return file;
	} else {
		file->initOpen(inputName.path(),memoryAddress);
		return file;
	}
}

std::unique_ptr<CAssemblerCommand> parseDirectiveCreate(Parser& parser, int flags)
{
	std::vector<Expression> list;
	if (!parser.parseExpressionList(list,2,2))
		return nullptr;

	int64_t memoryAddress;
	StringLiteral inputName;

	if (!list[0].evaluateString(inputName,false))
		return nullptr;

	if (!list.back().evaluateInteger(memoryAddress))
		return nullptr;

	auto file = std::make_unique<CDirectiveFile>();
	file->initCreate(inputName.path(),memoryAddress);
	return file;
}

std::unique_ptr<CAssemblerCommand> parseDirectiveClose(Parser& parser, int flags)
{
	auto file = std::make_unique<CDirectiveFile>();
	file->initClose();
	return file;
}

std::unique_ptr<CAssemblerCommand> parseDirectiveIncbin(Parser& parser, int flags)
{
	std::vector<Expression> list;
	if (!parser.parseExpressionList(list,1,3))
		return nullptr;

	StringLiteral fileName;
	if (!list[0].evaluateString(fileName,false))
		return nullptr;

	auto incbin = std::make_unique<CDirectiveIncbin>(fileName.path());
	if (list.size() >= 2)
		incbin->setStart(list[1]);

	if (list.size() == 3)
		incbin->setSize(list[2]);

	return incbin;
}

std::unique_ptr<CAssemblerCommand> parseDirectivePosition(Parser& parser, int flags)
{
	Expression exp = parser.parseExpression();
	if (!exp.isLoaded())
		return nullptr;

	CDirectivePosition::Type type;
	switch (flags & DIRECTIVE_USERMASK)
	{
	case DIRECTIVE_POS_PHYSICAL:
		type = CDirectivePosition::Physical;
		break;
	case DIRECTIVE_POS_VIRTUAL:
		type = CDirectivePosition::Virtual;
		break;
	default:
		return nullptr;
	}

	return std::make_unique<CDirectivePosition>(exp,type);
}

std::unique_ptr<CAssemblerCommand> parseDirectiveAlignFill(Parser& parser, int flags)
{
	CDirectiveAlignFill::Mode mode;
	switch (flags & DIRECTIVE_USERMASK)
	{
	case DIRECTIVE_ALIGN_VIRTUAL:
		mode = CDirectiveAlignFill::AlignVirtual;
		break;
	case DIRECTIVE_ALIGN_PHYSICAL:
		mode = CDirectiveAlignFill::AlignPhysical;
		break;
	case DIRECTIVE_ALIGN_FILL:
		mode = CDirectiveAlignFill::Fill;
		break;
	default:
		return nullptr;
	}

	if (mode != CDirectiveAlignFill::Fill && parser.peekToken().type == TokenType::Separator)
		return std::make_unique<CDirectiveAlignFill>(UINT64_C(4),mode);

	std::vector<Expression> list;
	if (!parser.parseExpressionList(list,1,2))
		return nullptr;

	if (list.size() == 2)
		return std::make_unique<CDirectiveAlignFill>(list[0],list[1],mode);
	else
		return std::make_unique<CDirectiveAlignFill>(list[0],mode);
}

std::unique_ptr<CAssemblerCommand> parseDirectiveSkip(Parser& parser, int flags)
{
	std::vector<Expression> list;
	if (!parser.parseExpressionList(list,1,1))
		return nullptr;

	return std::make_unique<CDirectiveSkip>(list[0]);
}

std::unique_ptr<CAssemblerCommand> parseDirectiveHeaderSize(Parser& parser, int flags)
{
	Expression exp = parser.parseExpression();
	if (!exp.isLoaded())
		return nullptr;

	return std::make_unique<CDirectiveHeaderSize>(exp);
}

std::unique_ptr<CAssemblerCommand> parseDirectiveObjImport(Parser& parser, int flags)
{
	std::vector<Expression> list;
	if (!parser.parseExpressionList(list,1,2))
		return nullptr;

	StringLiteral fileName;
	if (!list[0].evaluateString(fileName,true))
		return nullptr;

	if (list.size() == 2)
	{
		Identifier ctorName;
		if (!list[1].evaluateIdentifier(ctorName))
			return nullptr;

		return std::make_unique<DirectiveObjImport>(fileName.path(),ctorName);
	}

	return std::make_unique<DirectiveObjImport>(fileName.path());
}

std::unique_ptr<CAssemblerCommand> parseDirectiveConditional(Parser& parser, int flags)
{
	ConditionType type;
	Identifier name;
	Expression exp;

	const Token& start = parser.peekToken();
	ConditionalResult condResult = ConditionalResult::Unknown;
	switch (flags)
	{
	case DIRECTIVE_COND_IF:
		type = ConditionType::IF;
		exp = parser.parseExpression();
		if (!exp.isLoaded())
		{
			parser.printError(start, "Invalid condition");
			return std::make_unique<DummyCommand>();
		}

		if (exp.isConstExpression())
		{
			ExpressionValue result = exp.evaluate();
			if (result.isInt())
				condResult = result.intValue != 0 ? ConditionalResult::True : ConditionalResult::False;
		}
		break;
	case DIRECTIVE_COND_IFDEF:
		type = ConditionType::IFDEF;
		if (!parser.parseIdentifier(name))
			return nullptr;
		break;
	case DIRECTIVE_COND_IFNDEF:
		type = ConditionType::IFNDEF;
		if (!parser.parseIdentifier(name))
			return nullptr;
		break;
	}

	if(parser.nextToken().type != TokenType::Separator)
	{
		parser.printError(start, "Directive not terminated");
		return nullptr;
	}

	parser.pushConditionalResult(condResult);
	std::unique_ptr<CAssemblerCommand> ifBlock = parser.parseCommandSequence('.', {".else", ".elseif", ".elseifdef", ".elseifndef", ".endif"});
	parser.popConditionalResult();

	// update the file info so that else commands get the right line number
	parser.updateFileInfo();

	std::unique_ptr<CAssemblerCommand> elseBlock = nullptr;
	const Token &next = parser.nextToken();

	if (next.type != TokenType::Identifier)
	{
		parser.printError(start, "Expected identifier after if block");
		return nullptr;
	}

	const Identifier &identifier = next.identifierValue();

	ConditionalResult elseResult;
	switch (condResult)
	{
	case ConditionalResult::True:
		elseResult = ConditionalResult::False;
		break;
	case ConditionalResult::False:
		elseResult = ConditionalResult::True;
		break;
	case ConditionalResult::Unknown:
		elseResult = condResult;
		break;
	}

	parser.pushConditionalResult(elseResult);
	if (identifier == ".else")
	{
		elseBlock = parser.parseCommandSequence('.', {".endif"});

		parser.eatToken();	// eat .endif
	} else if (identifier == ".elseif")
	{
		elseBlock = parseDirectiveConditional(parser,DIRECTIVE_COND_IF);
	} else if (identifier == ".elseifdef")
	{
		elseBlock = parseDirectiveConditional(parser,DIRECTIVE_COND_IFDEF);
	} else if (identifier == ".elseifndef")
	{
		elseBlock = parseDirectiveConditional(parser,DIRECTIVE_COND_IFNDEF);
	} else if (identifier != ".endif")
	{
		parser.popConditionalResult();
		return nullptr;
	}

	parser.popConditionalResult();

	// for true or false blocks, there's no need to create a conditional command
	if (condResult == ConditionalResult::True)
	{
		return ifBlock;
	}

	if (condResult == ConditionalResult::False)
	{
		if (elseBlock != nullptr)
			return elseBlock;
		else
			return std::make_unique<DummyCommand>();
	}

	std::unique_ptr<CDirectiveConditional> cond;
	if (exp.isLoaded())
		cond = std::make_unique<CDirectiveConditional>(type,exp);
	else if (name.size() != 0)
		cond = std::make_unique<CDirectiveConditional>(type, name);
	else
		cond = std::make_unique<CDirectiveConditional>(type);

	cond->setContent(std::move(ifBlock),std::move(elseBlock));
	return cond;
}

std::unique_ptr<CAssemblerCommand> parseDirectiveTable(Parser& parser, int flags)
{
	const Token& start = parser.peekToken();

	std::vector<Expression> list;
	if (!parser.parseExpressionList(list,1,2))
		return nullptr;

	StringLiteral fileName;
	if (!list[0].evaluateString(fileName,true))
	{
		parser.printError(start, "Invalid file name");
		return nullptr;
	}

	TextFile::Encoding encoding = TextFile::GUESS;
	if (list.size() == 2)
	{
		StringLiteral encodingName;
		if (!list[1].evaluateString(encodingName,true))
		{
			parser.printError(start, "Invalid encoding name");
			return nullptr;
		}

		encoding = getEncodingFromString(encodingName.string());
	}

	return std::make_unique<TableCommand>(fileName.path(),encoding);
}

std::unique_ptr<CAssemblerCommand> parseDirectiveData(Parser& parser, int flags)
{
	bool terminate = false;
	if (flags & DIRECTIVE_DATA_TERMINATION)
	{
		terminate = true;
		flags &= ~DIRECTIVE_DATA_TERMINATION;
	}

	std::vector<Expression> list;
	if (!parser.parseExpressionList(list,1,-1))
		return nullptr;

	auto data = std::make_unique<CDirectiveData>();
	switch (flags & DIRECTIVE_USERMASK)
	{
	case DIRECTIVE_DATA_8:
		data->setNormal(list,1);
		break;
	case DIRECTIVE_DATA_16:
		data->setNormal(list,2);
		break;
	case DIRECTIVE_DATA_32:
		data->setNormal(list,4);
		break;
	case DIRECTIVE_DATA_64:
		data->setNormal(list,8);
		break;
	case DIRECTIVE_DATA_HWORD:
		data->setNormal(list,Architecture::current().getWordSize()/2);
		break;
	case DIRECTIVE_DATA_WORD:
		data->setNormal(list,Architecture::current().getWordSize());
		break;
	case DIRECTIVE_DATA_DWORD:
		data->setNormal(list,Architecture::current().getWordSize()*2);
		break;
	case DIRECTIVE_DATA_ASCII:
		data->setAscii(list,terminate);
		break;
	case DIRECTIVE_DATA_SJIS:
		data->setSjis(list,terminate);
		break;
	case DIRECTIVE_DATA_CUSTOM:
		data->setCustom(list,terminate);
		break;
	case DIRECTIVE_DATA_FLOAT:
		data->setFloat(list);
		break;
	case DIRECTIVE_DATA_DOUBLE:
		data->setDouble(list);
		break;
	}

	return data;
}

std::unique_ptr<CAssemblerCommand> parseDirectiveMipsArch(Parser& parser, int flags)
{
	Architecture::setCurrent(Mips);
	Mips.SetLoadDelay(false, 0);

	switch (flags)
	{
	case DIRECTIVE_MIPS_PSX:
		Mips.SetVersion(MARCH_PSX);
		return std::make_unique<ArchitectureCommand>(".psx", "");
	case DIRECTIVE_MIPS_PS2:
		Mips.SetVersion(MARCH_PS2);
		return std::make_unique<ArchitectureCommand>(".ps2", "");
	case DIRECTIVE_MIPS_PSP:
		Mips.SetVersion(MARCH_PSP);
		return std::make_unique<ArchitectureCommand>(".psp", "");
	case DIRECTIVE_MIPS_N64:
		Mips.SetVersion(MARCH_N64);
		return std::make_unique<ArchitectureCommand>(".n64", "");
	case DIRECTIVE_MIPS_RSP:
		Mips.SetVersion(MARCH_RSP);
		return std::make_unique<ArchitectureCommand>(".rsp", "");
	}

	return nullptr;
}

std::unique_ptr<CAssemblerCommand> parseDirectiveArea(Parser& parser, int flags)
{
	std::vector<Expression> parameters;
	if (!parser.parseExpressionList(parameters,1,2))
		return nullptr;

	bool shared = (flags & DIRECTIVE_AREA_SHARED) != 0;
	auto area = std::make_unique<CDirectiveArea>(shared, parameters[0]);
	if (parameters.size() == 2)
		area->setFillExpression(parameters[1]);

	std::unique_ptr<CAssemblerCommand> content = parser.parseCommandSequence('.', { ".endarea", ".endregion" });
	parser.eatToken();

	area->setContent(std::move(content));
	return area;
}

std::unique_ptr<CAssemblerCommand> parseDirectiveDefineArea(Parser& parser, int flags)
{
	std::vector<Expression> parameters;
	if (!parser.parseExpressionList(parameters,2,3))
		return nullptr;

	bool shared = (flags & DIRECTIVE_AREA_SHARED) != 0;
	auto area = std::make_unique<CDirectiveArea>(shared, parameters[1]);
	area->setPositionExpression(parameters[0]);
	if (parameters.size() == 3)
		area->setFillExpression(parameters[2]);

	return area;
}

std::unique_ptr<CAssemblerCommand> parseDirectiveAutoRegion(Parser& parser, int flags)
{
	std::vector<Expression> parameters;
	if (parser.peekToken().type != TokenType::Separator)
	{
		if (!parser.parseExpressionList(parameters, 0, 2))
			return nullptr;
	}

	auto area = std::make_unique<CDirectiveAutoRegion>();
	if (parameters.size() == 1)
		area->setMinRangeExpression(parameters[0]);
	else if (parameters.size() == 2)
		area->setRangeExpressions(parameters[0], parameters[1]);

	std::unique_ptr<CAssemblerCommand> content = parser.parseCommandSequence('.', {".endautoregion"});
	parser.eatToken();

	area->setContent(std::move(content));
	return area;
}

std::optional<std::string> getStringOrIdentifier(Parser& parser)
{
	const Token &tok = parser.nextToken();
	if (tok.type == TokenType::Identifier)
		return tok.identifierValue().string();

	if (tok.type == TokenType::String)
	{
		auto stringValue = tok.stringValue().string();
		std::transform(stringValue.begin(),stringValue.end(),stringValue.begin(),::tolower);
		return stringValue;
	}

	return std::nullopt;
}

std::unique_ptr<CAssemblerCommand> parseDirectiveErrorWarning(Parser& parser, int flags)
{
	auto stringValue = getStringOrIdentifier(parser);
	if (!stringValue)
		return nullptr;

	if (stringValue == "on")
	{
		Logger::setErrorOnWarning(true);
		return std::make_unique<DummyCommand>();
	} else if (stringValue == "off")
	{
		Logger::setErrorOnWarning(false);
		return std::make_unique<DummyCommand>();
	}

	return nullptr;
}

std::unique_ptr<CAssemblerCommand> parseDirectiveRelativeInclude(Parser& parser, int flags)
{
	auto stringValue = getStringOrIdentifier(parser);
	if (!stringValue)
		return nullptr;

	if (stringValue == "on")
	{
		Global.relativeInclude = true;
		return std::make_unique<DummyCommand>();
	} else if (stringValue == "off")
	{
		Global.relativeInclude = false;
		return std::make_unique<DummyCommand>();
	}

	return nullptr;
}

std::unique_ptr<CAssemblerCommand> parseDirectiveNocash(Parser& parser, int flags)
{
	auto stringValue = getStringOrIdentifier(parser);
	if (!stringValue)
		return nullptr;

	if (stringValue == "on")
	{
		Global.nocash = true;
		return std::make_unique<DummyCommand>();
	} else if (stringValue == "off")
	{
		Global.nocash = false;
		return std::make_unique<DummyCommand>();
	}

	return nullptr;
}

std::unique_ptr<CAssemblerCommand> parseDirectiveSym(Parser& parser, int flags)
{
	auto stringValue = getStringOrIdentifier(parser);
	if (!stringValue)
		return nullptr;

	if (stringValue == "on")
		return std::make_unique<CDirectiveSym>(true);
	else if (stringValue == "off")
		return std::make_unique<CDirectiveSym>(false);
	else
		return nullptr;
}

std::unique_ptr<CAssemblerCommand> parseDirectiveDefineLabel(Parser& parser, int flags, std::optional<bool> thumbMode)
{
	const Token& tok = parser.nextToken();
	if (tok.type != TokenType::Identifier)
		return nullptr;

	if (parser.nextToken().type != TokenType::Comma)
		return nullptr;

	Expression value = parser.parseExpression();
	if (!value.isLoaded())
		return nullptr;

	const Identifier &identifier = tok.identifierValue();
	if (!Global.symbolTable.isValidSymbolName(identifier))
	{
		parser.printError(tok, "Invalid label name \"%s\"",identifier);
		return nullptr;
	}

	return std::make_unique<CAssemblerLabel>(identifier,Identifier(tok.getOriginalText()),value,thumbMode);
}

std::unique_ptr<CAssemblerCommand> parseDirectiveDefineLabel(Parser& parser, int flags)
{
	return parseDirectiveDefineLabel(parser, flags, std::nullopt);
}

std::unique_ptr<CAssemblerCommand> parseDirectiveDefineDataLabel(Parser& parser, int flags)
{
	return parseDirectiveDefineLabel(parser, flags, false);
}

std::unique_ptr<CAssemblerCommand> parseDirectiveFunction(Parser& parser, int flags)
{
	const Token& tok = parser.nextToken();
	if (tok.type != TokenType::Identifier)
		return nullptr;

	if (parser.nextToken().type != TokenType::Separator)
	{
		parser.printError(tok, "Directive not terminated");
		return nullptr;
	}

	auto func = std::make_unique<CDirectiveFunction>(Identifier(tok.identifierValue()), Identifier(tok.getOriginalText()));
	std::unique_ptr<CAssemblerCommand> seq = parser.parseCommandSequence('.', {".endfunc",".endfunction",".func",".function"});

	const Token &next = parser.peekToken();
	if (next.type == TokenType::Identifier)
	{
		const Identifier &identifier = next.identifierValue();
		if (identifier == ".endfunc" || identifier == ".endfunction")
		{
			parser.eatToken();
			if(parser.nextToken().type != TokenType::Separator)
			{
				parser.printError(tok, "Directive not terminated");
				return nullptr;
			}
		}
	}

	func->setContent(std::move(seq));
	return func;
}

std::unique_ptr<CAssemblerCommand> parseDirectiveMessage(Parser& parser, int flags)
{
	Expression exp = parser.parseExpression();

	switch (flags)
	{
	case DIRECTIVE_MSG_WARNING:
		return std::make_unique<CDirectiveMessage>(CDirectiveMessage::Type::Warning,exp);
	case DIRECTIVE_MSG_ERROR:
		return std::make_unique<CDirectiveMessage>(CDirectiveMessage::Type::Error,exp);
	case DIRECTIVE_MSG_NOTICE:
		return std::make_unique<CDirectiveMessage>(CDirectiveMessage::Type::Notice,exp);
	}

	return nullptr;
}

std::unique_ptr<CAssemblerCommand> parseDirectiveInclude(Parser& parser, int flags)
{
	const Token& start = parser.peekToken();

	std::vector<Expression> parameters;
	if (!parser.parseExpressionList(parameters,1,2))
		return nullptr;

	StringLiteral fileNameParameter;
	if (!parameters[0].evaluateString(fileNameParameter,true))
		return nullptr;

	auto fileName = getFullPathName(fileNameParameter.path());

	TextFile::Encoding encoding = TextFile::GUESS;
	if (parameters.size() == 2)
	{
		StringLiteral encodingName;
		if (!parameters[1].evaluateString(encodingName,true))
			return nullptr;

		encoding = getEncodingFromString(encodingName.string());
	}

	// don't include the file if it's inside a false block
	if (!parser.isInsideTrueBlock())
		return std::make_unique<DummyCommand>();

	if (!fs::exists(fileName))
	{
		parser.printError(start, "Included file \"%s\" does not exist",fileName.u8string());
		return nullptr;
	}

	TextFile f;
	if (!f.open(fileName,TextFile::Read,encoding))
	{
		parser.printError(start, "Could not open included file \"%s\"",fileName.u8string());
		return nullptr;
	}

	return parser.parseFile(f);
}

const DirectiveMap directives = {
	{ ".open",            { &parseDirectiveOpen,            DIRECTIVE_NOTINMEMORY } },
	{ ".openfile",        { &parseDirectiveOpen,            DIRECTIVE_NOTINMEMORY } },
	{ ".create",          { &parseDirectiveCreate,          DIRECTIVE_NOTINMEMORY } },
	{ ".createfile",      { &parseDirectiveCreate,          DIRECTIVE_NOTINMEMORY } },
	{ ".close",           { &parseDirectiveClose,           DIRECTIVE_NOTINMEMORY } },
	{ ".closefile",       { &parseDirectiveClose,           DIRECTIVE_NOTINMEMORY } },
	{ ".incbin",          { &parseDirectiveIncbin,          0 } },
	{ ".import",          { &parseDirectiveIncbin,          0 } },
	{ ".org",             { &parseDirectivePosition,        DIRECTIVE_POS_VIRTUAL } },
	{ "org",              { &parseDirectivePosition,        DIRECTIVE_POS_VIRTUAL } },
	{ ".orga",            { &parseDirectivePosition,        DIRECTIVE_POS_PHYSICAL } },
	{ "orga",             { &parseDirectivePosition,        DIRECTIVE_POS_PHYSICAL } },
	{ ".headersize",      { &parseDirectiveHeaderSize,      0 } },
	{ ".align",           { &parseDirectiveAlignFill,       DIRECTIVE_ALIGN_VIRTUAL } },
	{ ".aligna",          { &parseDirectiveAlignFill,       DIRECTIVE_ALIGN_PHYSICAL } },
	{ ".fill",            { &parseDirectiveAlignFill,       DIRECTIVE_ALIGN_FILL } },
	{ "defs",             { &parseDirectiveAlignFill,       DIRECTIVE_ALIGN_FILL } },
	{ ".skip",            { &parseDirectiveSkip,            0 } },

	{ ".if",              { &parseDirectiveConditional,     DIRECTIVE_COND_IF } },
	{ ".ifdef",           { &parseDirectiveConditional,     DIRECTIVE_COND_IFDEF } },
	{ ".ifndef",          { &parseDirectiveConditional,     DIRECTIVE_COND_IFNDEF } },

	{ ".loadtable",       { &parseDirectiveTable,           0 } },
	{ ".table",           { &parseDirectiveTable,           0 } },
	{ ".byte",            { &parseDirectiveData,            DIRECTIVE_DATA_8 } },
	{ ".halfword",        { &parseDirectiveData,            DIRECTIVE_DATA_HWORD } },
	{ ".hword",           { &parseDirectiveData,            DIRECTIVE_DATA_HWORD } },
	{ ".word",            { &parseDirectiveData,            DIRECTIVE_DATA_WORD } },
	{ ".doubleword",      { &parseDirectiveData,            DIRECTIVE_DATA_DWORD } },
	{ ".dword",           { &parseDirectiveData,            DIRECTIVE_DATA_DWORD } },
	{ ".db",              { &parseDirectiveData,            DIRECTIVE_DATA_8 } },
	{ ".dh",              { &parseDirectiveData,            DIRECTIVE_DATA_HWORD|DIRECTIVE_NOCASHOFF } },
	{ ".dw",              { &parseDirectiveData,            DIRECTIVE_DATA_WORD|DIRECTIVE_NOCASHOFF } },
	{ ".dd",              { &parseDirectiveData,            DIRECTIVE_DATA_DWORD|DIRECTIVE_NOCASHOFF } },
	{ ".dw",              { &parseDirectiveData,            DIRECTIVE_DATA_16|DIRECTIVE_NOCASHON } },
	{ ".dd",              { &parseDirectiveData,            DIRECTIVE_DATA_32|DIRECTIVE_NOCASHON } },
	{ ".dcb",             { &parseDirectiveData,            DIRECTIVE_DATA_8 } },
	{ ".dcw",             { &parseDirectiveData,            DIRECTIVE_DATA_HWORD } },
	{ ".dcd",             { &parseDirectiveData,            DIRECTIVE_DATA_WORD } },
	{ ".dcq",             { &parseDirectiveData,            DIRECTIVE_DATA_DWORD } },
	{ "db",               { &parseDirectiveData,            DIRECTIVE_DATA_8 } },
	{ "dh",               { &parseDirectiveData,            DIRECTIVE_DATA_HWORD|DIRECTIVE_NOCASHOFF } },
	{ "dw",               { &parseDirectiveData,            DIRECTIVE_DATA_WORD|DIRECTIVE_NOCASHOFF } },
	{ "dd",               { &parseDirectiveData,            DIRECTIVE_DATA_DWORD|DIRECTIVE_NOCASHOFF } },
	{ "dw",               { &parseDirectiveData,            DIRECTIVE_DATA_16|DIRECTIVE_NOCASHON } },
	{ "dd",               { &parseDirectiveData,            DIRECTIVE_DATA_32|DIRECTIVE_NOCASHON } },
	{ "dcb",              { &parseDirectiveData,            DIRECTIVE_DATA_8 } },
	{ "dcw",              { &parseDirectiveData,            DIRECTIVE_DATA_HWORD } },
	{ "dcd",              { &parseDirectiveData,            DIRECTIVE_DATA_WORD } },
	{ "dcq",              { &parseDirectiveData,            DIRECTIVE_DATA_DWORD } },
	{ ".d8",              { &parseDirectiveData,            DIRECTIVE_DATA_8 } },
	{ ".d16",             { &parseDirectiveData,            DIRECTIVE_DATA_16 } },
	{ ".d32",             { &parseDirectiveData,            DIRECTIVE_DATA_32 } },
	{ ".d64",             { &parseDirectiveData,            DIRECTIVE_DATA_64 } },
	{ ".float",           { &parseDirectiveData,            DIRECTIVE_DATA_FLOAT } },
	{ ".double",          { &parseDirectiveData,            DIRECTIVE_DATA_DOUBLE } },
	{ ".ascii",           { &parseDirectiveData,            DIRECTIVE_DATA_ASCII } },
	{ ".asciiz",          { &parseDirectiveData,            DIRECTIVE_DATA_ASCII|DIRECTIVE_DATA_TERMINATION } },
	{ ".string",          { &parseDirectiveData,            DIRECTIVE_DATA_CUSTOM|DIRECTIVE_DATA_TERMINATION } },
	{ ".str",             { &parseDirectiveData,            DIRECTIVE_DATA_CUSTOM|DIRECTIVE_DATA_TERMINATION } },
	{ ".stringn",         { &parseDirectiveData,            DIRECTIVE_DATA_CUSTOM } },
	{ ".strn",            { &parseDirectiveData,            DIRECTIVE_DATA_CUSTOM } },
	{ ".sjis",            { &parseDirectiveData,            DIRECTIVE_DATA_SJIS|DIRECTIVE_DATA_TERMINATION } },
	{ ".sjisn",           { &parseDirectiveData,            DIRECTIVE_DATA_SJIS } },

	{ ".psx",             { &parseDirectiveMipsArch,        DIRECTIVE_MIPS_PSX } },
	{ ".ps2",             { &parseDirectiveMipsArch,        DIRECTIVE_MIPS_PS2 } },
	{ ".psp",             { &parseDirectiveMipsArch,        DIRECTIVE_MIPS_PSP } },
	{ ".n64",             { &parseDirectiveMipsArch,        DIRECTIVE_MIPS_N64 } },
	{ ".rsp",             { &parseDirectiveMipsArch,        DIRECTIVE_MIPS_RSP } },

	{ ".area",            { &parseDirectiveArea,            0 } },
	{ ".autoregion",      { &parseDirectiveAutoRegion,      0 } },
	{ ".region",          { &parseDirectiveArea,            DIRECTIVE_AREA_SHARED } },
	{ ".defineregion",    { &parseDirectiveDefineArea,      DIRECTIVE_AREA_SHARED } },

	{ ".importobj",       { &parseDirectiveObjImport,       0 } },
	{ ".importlib",       { &parseDirectiveObjImport,       0 } },

	{ ".erroronwarning",  { &parseDirectiveErrorWarning,    0 } },
	{ ".relativeinclude", { &parseDirectiveRelativeInclude, 0 } },
	{ ".nocash",          { &parseDirectiveNocash,          0 } },
	{ ".sym",             { &parseDirectiveSym,             0 } },

	{ ".definelabel",     { &parseDirectiveDefineLabel,     0 } },
	{ ".definedatalabel", { &parseDirectiveDefineDataLabel, 0 } },
	{ ".function",        { &parseDirectiveFunction,        DIRECTIVE_MANUALSEPARATOR } },
	{ ".func",            { &parseDirectiveFunction,        DIRECTIVE_MANUALSEPARATOR } },

	{ ".warning",         { &parseDirectiveMessage,         DIRECTIVE_MSG_WARNING } },
	{ ".error",           { &parseDirectiveMessage,         DIRECTIVE_MSG_ERROR } },
	{ ".notice",          { &parseDirectiveMessage,         DIRECTIVE_MSG_NOTICE } },

	{ ".include",         { &parseDirectiveInclude,         0 } },

};

#pragma region "File: Parser/ExpressionParser.cpp"

static std::unique_ptr<ExpressionInternal> expression(Tokenizer& tokenizer);

static std::unique_ptr<ExpressionInternal> primaryExpression(Tokenizer& tokenizer)
{
	const Token &tok = tokenizer.peekToken();

	switch (tok.type)
	{
	case TokenType::Float:
		tokenizer.eatToken();
		return std::make_unique<ExpressionInternal>(tok.floatValue());
	case TokenType::Identifier:
		{
			const Identifier &identifier = tok.identifierValue();
			tokenizer.eatToken();
			if (identifier == ".")
				return std::make_unique<ExpressionInternal>(OperatorType::MemoryPos);
			else
				return std::make_unique<ExpressionInternal>(identifier);
		}
	case TokenType::String:
		tokenizer.eatToken();
		return std::make_unique<ExpressionInternal>(tok.stringValue());
	case TokenType::Integer:
		tokenizer.eatToken();
		return std::make_unique<ExpressionInternal>(tok.intValue());
	case TokenType::LParen:
		{
			tokenizer.eatToken();
			std::unique_ptr<ExpressionInternal> exp = expression(tokenizer);

			if (tokenizer.nextToken().type != TokenType::RParen)
				return nullptr;

			return exp;
		}
	case TokenType::Invalid:
	default:
		break;
	}

	return nullptr;
}

static std::unique_ptr<ExpressionInternal> postfixExpression(Tokenizer& tokenizer)
{
	if (tokenizer.peekToken(0).type == TokenType::Identifier &&
		tokenizer.peekToken(1).type == TokenType::LParen &&
		ExpressionFunctionHandler::instance().find(tokenizer.peekToken(0).identifierValue()))
	{
		const Identifier &functionName = tokenizer.nextToken().identifierValue();
		tokenizer.eatToken();

		std::vector<std::unique_ptr<ExpressionInternal>> parameters;
		while (tokenizer.peekToken().type != TokenType::RParen)
		{
			if (parameters.size() != 0 && tokenizer.nextToken().type != TokenType::Comma)
				return nullptr;

			std::unique_ptr<ExpressionInternal> exp = expression(tokenizer);
			if (exp == nullptr)
				return nullptr;

			parameters.push_back(std::move(exp));
		}

		tokenizer.eatToken();

		return std::make_unique<ExpressionInternal>(functionName, std::move(parameters));
	}

	return primaryExpression(tokenizer);
}

static std::unique_ptr<ExpressionInternal> unaryExpression(Tokenizer& tokenizer)
{
	std::unique_ptr<ExpressionInternal> exp = postfixExpression(tokenizer);
	if (exp != nullptr)
		return exp;

	const TokenType opType = tokenizer.nextToken().type;
	exp = postfixExpression(tokenizer);
	if (exp == nullptr)
		return nullptr;

	switch (opType)
	{
	case TokenType::Plus:
		return exp;
	case TokenType::Minus:
		return std::make_unique<ExpressionInternal>(OperatorType::Neg, std::move(exp));
	case TokenType::Tilde:
		return std::make_unique<ExpressionInternal>(OperatorType::BitNot, std::move(exp));
	case TokenType::Exclamation:
		return std::make_unique<ExpressionInternal>(OperatorType::LogNot, std::move(exp));
	case TokenType::Degree:
		return std::make_unique<ExpressionInternal>(OperatorType::ToString, std::move(exp));
	default:
		return nullptr;
	}
}

static std::unique_ptr<ExpressionInternal> multiplicativeExpression(Tokenizer& tokenizer)
{
	std::unique_ptr<ExpressionInternal> exp = unaryExpression(tokenizer);
	if (exp ==  nullptr)
		return nullptr;

	while (true)
	{
		OperatorType op = OperatorType::Invalid;
		switch (tokenizer.peekToken().type)
		{
		case TokenType::Mult:
			op = OperatorType::Mult;
			break;
		case TokenType::Div:
			op = OperatorType::Div;
			break;
		case TokenType::Mod:
			op = OperatorType::Mod;
			break;
		default:
			break;
		}

		if (op == OperatorType::Invalid)
			break;

		tokenizer.eatToken();

		std::unique_ptr<ExpressionInternal> exp2 = unaryExpression(tokenizer);
		if (exp2 == nullptr)
			return nullptr;

		exp = std::make_unique<ExpressionInternal>(op, std::move(exp), std::move(exp2));
	}

	return exp;
}

static std::unique_ptr<ExpressionInternal> additiveExpression(Tokenizer& tokenizer)
{
	std::unique_ptr<ExpressionInternal> exp = multiplicativeExpression(tokenizer);
	if (exp == nullptr)
		return nullptr;

	while (true)
	{
		OperatorType op = OperatorType::Invalid;
		switch (tokenizer.peekToken().type)
		{
		case TokenType::Plus:
			op = OperatorType::Add;
			break;
		case TokenType::Minus:
			op = OperatorType::Sub;
			break;
		default:
			break;
		}

		if (op == OperatorType::Invalid)
			break;

		tokenizer.eatToken();

		std::unique_ptr<ExpressionInternal> exp2 = multiplicativeExpression(tokenizer);
		if (exp2 == nullptr)
			return nullptr;

		exp = std::make_unique<ExpressionInternal>(op, std::move(exp), std::move(exp2));
	}

	return exp;
}

static std::unique_ptr<ExpressionInternal> shiftExpression(Tokenizer& tokenizer)
{
	std::unique_ptr<ExpressionInternal> exp = additiveExpression(tokenizer);
	if (exp == nullptr)
		return nullptr;

	while (true)
	{
		OperatorType op = OperatorType::Invalid;
		switch (tokenizer.peekToken().type)
		{
		case TokenType::LeftShift:
			op = OperatorType::LeftShift;
			break;
		case TokenType::RightShift:
			op = OperatorType::RightShift;
			break;
		default:
			break;
		}

		if (op == OperatorType::Invalid)
			break;

		tokenizer.eatToken();

		std::unique_ptr<ExpressionInternal> exp2 = additiveExpression(tokenizer);
		if (exp2 == nullptr)
			return nullptr;

		exp = std::make_unique<ExpressionInternal>(op, std::move(exp), std::move(exp2));
	}

	return exp;
}

static std::unique_ptr<ExpressionInternal> relationalExpression(Tokenizer& tokenizer)
{
	std::unique_ptr<ExpressionInternal> exp = shiftExpression(tokenizer);
	if (exp == nullptr)
		return nullptr;

	while (true)
	{
		OperatorType op = OperatorType::Invalid;
		switch (tokenizer.peekToken().type)
		{
		case TokenType::Less:
			op = OperatorType::Less;
			break;
		case TokenType::LessEqual:
			op = OperatorType::LessEqual;
			break;
		case TokenType::Greater:
			op = OperatorType::Greater;
			break;
		case TokenType::GreaterEqual:
			op = OperatorType::GreaterEqual;
			break;
		default:
			break;
		}

		if (op == OperatorType::Invalid)
			break;

		tokenizer.eatToken();

		std::unique_ptr<ExpressionInternal> exp2 = shiftExpression(tokenizer);
		if (exp2 == nullptr)
			return nullptr;
		exp = std::make_unique<ExpressionInternal>(op, std::move(exp), std::move(exp2));
	}

	return exp;
}

static std::unique_ptr<ExpressionInternal> equalityExpression(Tokenizer& tokenizer)
{
	std::unique_ptr<ExpressionInternal> exp = relationalExpression(tokenizer);
	if (exp == nullptr)
		return nullptr;

	while (true)
	{
		OperatorType op = OperatorType::Invalid;
		switch (tokenizer.peekToken().type)
		{
		case TokenType::Equal:
			op = OperatorType::Equal;
			break;
		case TokenType::NotEqual:
			op = OperatorType::NotEqual;
			break;
		default:
			break;
		}

		if (op == OperatorType::Invalid)
			break;

		tokenizer.eatToken();

		std::unique_ptr<ExpressionInternal> exp2 = relationalExpression(tokenizer);
		if (exp2 == nullptr)
			return nullptr;

		exp = std::make_unique<ExpressionInternal>(op, std::move(exp), std::move(exp2));
	}

	return exp;
}

static std::unique_ptr<ExpressionInternal> andExpression(Tokenizer& tokenizer)
{
	std::unique_ptr<ExpressionInternal> exp = equalityExpression(tokenizer);
	if (exp == nullptr)
		return nullptr;

	while (tokenizer.peekToken().type == TokenType::BitAnd)
	{
		tokenizer.eatToken();

		std::unique_ptr<ExpressionInternal> exp2 = equalityExpression(tokenizer);
		if (exp2 == nullptr)
			return nullptr;

		exp = std::make_unique<ExpressionInternal>(OperatorType::BitAnd, std::move(exp), std::move(exp2));
	}

	return exp;
}

static std::unique_ptr<ExpressionInternal> exclusiveOrExpression(Tokenizer& tokenizer)
{
	std::unique_ptr<ExpressionInternal> exp = andExpression(tokenizer);
	if (exp == nullptr)
		return nullptr;

	while (tokenizer.peekToken().type == TokenType::Caret)
	{
		tokenizer.eatToken();

		std::unique_ptr<ExpressionInternal> exp2 = andExpression(tokenizer);
		if (exp2 == nullptr)
			return nullptr;

		exp = std::make_unique<ExpressionInternal>(OperatorType::Xor, std::move(exp), std::move(exp2));
	}

	return exp;
}

static std::unique_ptr<ExpressionInternal> inclusiveOrExpression(Tokenizer& tokenizer)
{
	std::unique_ptr<ExpressionInternal> exp = exclusiveOrExpression(tokenizer);
	if (exp == nullptr)
		return nullptr;

	while (tokenizer.peekToken().type == TokenType::BitOr)
	{
		tokenizer.eatToken();

		std::unique_ptr<ExpressionInternal> exp2 = exclusiveOrExpression(tokenizer);
		if (exp2 == nullptr)
			return nullptr;

		exp = std::make_unique<ExpressionInternal>(OperatorType::BitOr, std::move(exp), std::move(exp2));
	}

	return exp;
}

static std::unique_ptr<ExpressionInternal> logicalAndExpression(Tokenizer& tokenizer)
{
	std::unique_ptr<ExpressionInternal> exp = inclusiveOrExpression(tokenizer);
	if (exp == nullptr)
		return nullptr;

	while (tokenizer.peekToken().type == TokenType::LogAnd)
	{
		tokenizer.eatToken();

		std::unique_ptr<ExpressionInternal> exp2 = inclusiveOrExpression(tokenizer);
		if (exp2 == nullptr)
			return nullptr;

		exp = std::make_unique<ExpressionInternal>(OperatorType::LogAnd, std::move(exp), std::move(exp2));
	}

	return exp;
}

static std::unique_ptr<ExpressionInternal> logicalOrExpression(Tokenizer& tokenizer)
{
	std::unique_ptr<ExpressionInternal> exp = logicalAndExpression(tokenizer);
	if (exp == nullptr)
		return nullptr;

	while (tokenizer.peekToken().type == TokenType::LogOr)
	{
		tokenizer.eatToken();

		std::unique_ptr<ExpressionInternal> exp2 = logicalAndExpression(tokenizer);
		if (exp2 == nullptr)
			return nullptr;

		exp = std::make_unique<ExpressionInternal>(OperatorType::LogOr, std::move(exp), std::move(exp2));
	}

	return exp;
}

static std::unique_ptr<ExpressionInternal> conditionalExpression(Tokenizer& tokenizer)
{
	std::unique_ptr<ExpressionInternal> exp = logicalOrExpression(tokenizer);
	if (exp == nullptr)
		return nullptr;

	// check a ? b : c
	if (tokenizer.peekToken().type != TokenType::Question)
		return exp;

	tokenizer.eatToken();
	std::unique_ptr<ExpressionInternal> second = expression(tokenizer);

	if (second != nullptr && tokenizer.nextToken().type == TokenType::Colon)
	{
		std::unique_ptr<ExpressionInternal> third = expression(tokenizer);
		if (third != nullptr)
			return std::make_unique<ExpressionInternal>(OperatorType::TertiaryIf, std::move(exp), std::move(second), std::move(third));
	}

	return nullptr;
}

static std::unique_ptr<ExpressionInternal> expression(Tokenizer& tokenizer)
{
	return conditionalExpression(tokenizer);
}

Expression parseExpression(Tokenizer& tokenizer, bool inUnknownOrFalseBlock)
{
	TokenizerPosition pos = tokenizer.getPosition();

	// parse expression, revert tokenizer to previous position
	// if it failed
	std::unique_ptr<ExpressionInternal> exp = expression(tokenizer);
	if (exp == nullptr)
		tokenizer.setPosition(pos);

	return Expression(std::move(exp), inUnknownOrFalseBlock);
}

#pragma region "File: Parser/Parser.cpp"


inline bool isPartOfList(const Identifier& value, const std::initializer_list<const char*>& terminators)
{
	for (const char* term: terminators)
	{
		if (value == term)
			return true;
	}

	return false;
}

Parser::Parser()
{
	initializingMacro = false;
	overrideFileInfo = false;
	conditionStack.push_back({true,false});
	clearError();
}

void Parser::pushConditionalResult(ConditionalResult cond)
{
	ConditionInfo info = conditionStack.back();
	info.inTrueBlock = info.inTrueBlock && cond != ConditionalResult::False;
	info.inUnknownBlock = info.inUnknownBlock || cond == ConditionalResult::Unknown;
	conditionStack.push_back(info);
}

void Parser::printError(const Token &token, const std::string &text)
{
	errorLine = token.line;
	Global.FileInfo.LineNumber = (int) token.line;
	Logger::printError(Logger::Error, text);
	error = true;
}

Expression Parser::parseExpression()
{
	return ::parseExpression(*getTokenizer(), !isInsideTrueBlock() || isInsideUnknownBlock());
}

bool Parser::parseExpressionList(std::vector<Expression>& list, int min, int max)
{
	bool valid = true;
	list.clear();
	list.reserve(max >= 0 ? max : 32);

	const Token& start = peekToken();

	Expression exp = parseExpression();
	list.push_back(exp);

	if (!exp.isLoaded())
	{
		printError(start, "Parameter failure");
		getTokenizer()->skipLookahead();
		valid = false;
	}

	while (peekToken().type == TokenType::Comma)
	{
		eatToken();

		exp = parseExpression();
		list.push_back(exp);

		if (!exp.isLoaded())
		{
			printError(start, "Parameter failure");
			getTokenizer()->skipLookahead();
			valid = false;
		}
	}

	if (list.size() < (size_t) min)
	{
		printError(start, "Not enough parameters (min %d)",min);
		return false;
	}

	if (max != -1 && (size_t) max < list.size())
	{
		printError(start, "Too many parameters (max %d)",max);
		return false;
	}

	return valid;
}

bool Parser::parseIdentifier(Identifier& dest)
{
	const Token& tok = nextToken();
	if (tok.type != TokenType::Identifier)
		return false;

	dest = tok.identifierValue();
	return true;
}

std::unique_ptr<CAssemblerCommand> Parser::parseCommandSequence(char indicator, const std::initializer_list<const char*> terminators)
{
	auto sequence = std::make_unique<CommandSequence>();

	bool foundTermination = false;
	while (!atEnd())
	{
		const Token &next = peekToken();

		if(next.type == TokenType::Separator)
		{
			eatToken();
			continue;
		}

		if (next.type == TokenType::Identifier)
		{
			const auto &identifier = next.identifierValue();
			if (identifier.startsWith(indicator) && isPartOfList(identifier, terminators))
			{
				foundTermination = true;
				break;
			}
		}

		bool foundSomething = false;
		while (checkEquLabel() || checkMacroDefinition() || checkExpFuncDefinition())
		{
			// do nothing, just parse all the equs and macros there are
			if (hasError())
				sequence->addCommand(handleError());

			foundSomething = true;
		}

		if (foundSomething)
			continue;

		std::unique_ptr<CAssemblerCommand> cmd = parseCommand();

		// omit commands inside blocks that are trivially false
		if (!isInsideTrueBlock())
		{
			continue;
		}

		sequence->addCommand(std::move(cmd));
	}

	if (!foundTermination && terminators.size())
	{
		std::string expected;
		for (const char* terminator : terminators)
		{
			if (!expected.empty())
				expected += ", ";
			expected += terminator;
		}

		Logger::printError(Logger::Error, "Unterminated command sequence, expected any of %s.", expected);
	}

	return sequence;
}

std::unique_ptr<CAssemblerCommand> Parser::parseFile(TextFile& file, bool virtualFile)
{
	FileTokenizer tokenizer;
	if (!tokenizer.init(&file))
		return nullptr;

	std::unique_ptr<CAssemblerCommand> result = parse(&tokenizer,virtualFile,file.getFileName());

	if (!file.isFromMemory())
		Global.FileInfo.TotalLineCount += file.getNumLines();

	return result;
}

std::unique_ptr<CAssemblerCommand> Parser::parseString(const std::string& text)
{
	TextFile file;
	file.openMemory(text);
	return parseFile(file,true);
}

std::unique_ptr<CAssemblerCommand> Parser::parseTemplate(const std::string& text, std::initializer_list<AssemblyTemplateArgument> variables)
{
	std::string fullText = text;

	overrideFileInfo = true;
	overrideFileNum = Global.FileInfo.FileNum;
	overrideLineNum = Global.FileInfo.LineNumber;

	for (auto& arg: variables)
	{
		size_t count = replaceAll(fullText,arg.variableName,arg.value);
		(void)count;
#ifdef _DEBUG
		if (count != 0 && arg.value.empty())
			Logger::printError(Logger::Warning, "Empty replacement for %s",arg.variableName);
#endif
	}

	std::unique_ptr<CAssemblerCommand> result = parseString(fullText);
	overrideFileInfo = false;

	return result;
}

std::unique_ptr<CAssemblerCommand> Parser::parseDirective(const DirectiveMap &directiveSet)
{
	const Token &tok = peekToken();
	if (tok.type != TokenType::Identifier)
		return nullptr;

	const std::string &stringValue = tok.identifierValue().string();

	auto matchRange = directiveSet.equal_range(stringValue);
	for (auto it = matchRange.first; it != matchRange.second; ++it)
	{
		const DirectiveEntry &directive = it->second;

		if (directive.flags & DIRECTIVE_DISABLED)
			continue;
		if ((directive.flags & DIRECTIVE_NOCASHOFF) && Global.nocash)
			continue;
		if ((directive.flags & DIRECTIVE_NOCASHON) && !Global.nocash)
			continue;
		if ((directive.flags & DIRECTIVE_NOTINMEMORY) && Global.memoryMode)
			continue;

		if (directive.flags & DIRECTIVE_MIPSRESETDELAY)
			Architecture::current().NextSection();

		eatToken();
		std::unique_ptr<CAssemblerCommand> result = directive.function(*this,directive.flags);
		if (result == nullptr)
		{
			if (!hasError())
				printError(tok, "Directive parameter failure");
			return nullptr;
		} else if (!(directive.flags & DIRECTIVE_MANUALSEPARATOR) && nextToken().type != TokenType::Separator)
		{
			printError(tok, "Directive not terminated");
			return nullptr;
		}

		return result;
	}

	return nullptr;
}

bool Parser::matchToken(TokenType type, bool optional)
{
	if (optional)
	{
		const Token& token = peekToken();
		if (token.type == type)
			eatToken();
		return true;
	}

	return nextToken().type == type;
}

std::unique_ptr<CAssemblerCommand> Parser::parse(Tokenizer* tokenizer, bool virtualFile, const fs::path& name)
{
	if (entries.size() >= 150)
	{
		Logger::queueError(Logger::Error, "Max include/recursion depth reached");
		return nullptr;
	}

	FileEntry entry;
	entry.tokenizer = tokenizer;
	entry.virtualFile = virtualFile;

	if (!virtualFile && !name.empty())
	{
		entry.fileNum = (int) Global.fileList.size();
		Global.fileList.add(name);
	} else {
		entry.fileNum = -1;
	}

	entries.push_back(entry);

	std::unique_ptr<CAssemblerCommand> sequence = parseCommandSequence();
	entries.pop_back();

	return sequence;
}

void Parser::addEquation(const Token& startToken, const Identifier& name, const std::string& value)
{
	// parse value string
	TextFile f;
	f.openMemory(value);

	FileTokenizer tok;
	tok.init(&f);

	TokenizerPosition start = tok.getPosition();
	while (!tok.atEnd() && tok.peekToken().type != TokenType::Separator)
	{
		const Token& token = tok.nextToken();
		if (token.type == TokenType::Identifier && token.identifierValue() == name)
		{
			printError(startToken, "Recursive equ definition for \"%s\" not allowed",name);
			return;
		}

		if (token.type == TokenType::Equ)
		{
			printError(startToken, "equ value must not contain another equ instance");
			return;
		}
	}

	// extract tokens
	TokenizerPosition end = tok.getPosition();
	std::vector<Token> tokens = tok.getTokens(start, end);
	size_t index = Tokenizer::addEquValue(tokens);

	for (FileEntry& entry : entries)
		entry.tokenizer->resetLookaheadCheckMarks();

	// register equation
	Global.symbolTable.addEquation(name, Global.FileInfo.FileNum, Global.Section, index);
}

bool Parser::checkEquLabel()
{
	updateFileInfo();

	const Token& start = peekToken();
	if (start.type == TokenType::Identifier)
	{
		int pos = 1;
		if (peekToken(pos).type == TokenType::Colon)
			pos++;

		if (peekToken(pos).type == TokenType::Equ &&
			peekToken(pos+1).type == TokenType::EquValue)
		{
			const Identifier &name = peekToken(0).identifierValue();
			const StringLiteral &value = peekToken(pos+1).stringValue();
			eatTokens(pos+2);

			// skip the equ if it's inside a false conditional block
			if (!isInsideTrueBlock())
				return true;

			// equs can't be inside blocks whose condition can only be
			// evaluated during validation
			if (isInsideUnknownBlock())
			{
				printError(start, "equ not allowed inside of block with non-trivial condition");
				return true;
			}

			// when parsing a macro, just remember the equ name
			if (initializingMacro)
			{
				macroLabels.insert(name);
				return true;
			}

			if (!Global.symbolTable.isValidSymbolName(name))
			{
				printError(start, "Invalid equation name \"%s\"",name);
				return true;
			}

			if (Global.symbolTable.symbolExists(name,Global.FileInfo.FileNum,Global.Section))
			{
				printError(start, "Equation name \"%s\" already defined",name);
				return true;
			}

			addEquation(start,name,value.string());
			return true;
		}
	}

	return false;
}

bool Parser::parseFunctionDeclaration(Identifier& name, std::vector<Identifier> &parameters)
{
	const Token& first = peekToken();
	if (first.type != TokenType::Identifier)
		return false;

	name = nextToken().identifierValue();

	if (nextToken().type != TokenType::LParen)
		return false;

	parameters.clear();
	while (!atEnd() && peekToken().type != TokenType::RParen)
	{
		if (!parameters.empty() && peekToken().type == TokenType::Comma)
			eatToken();

		const Token& token = nextToken();
		if (token.type != TokenType::Identifier)
			return false;

		parameters.emplace_back(token.identifierValue());
	}

	return !atEnd() && nextToken().type == TokenType::RParen;
}

bool Parser::checkExpFuncDefinition()
{
	const Token& first = peekToken();
	if (first.type != TokenType::Identifier)
		return false;

	const auto &identifier = first.identifierValue();
	if (!identifier.startsWith('.') || first.identifierValue() != ".expfunc")
		return false;

	eatToken();

	Identifier functionName;
	std::vector<Identifier> functionParameters;

	// load declarationn
	if (!parseFunctionDeclaration(functionName, functionParameters))
	{
		printError(first, "Invalid expression function declaration");
		return false;
	}

	if (nextToken().type != TokenType::Comma)
	{
		printError(first, "Invalid expression function declaration");
		return false;
	}

	// load definition
	TokenizerPosition start = getTokenizer()->getPosition();

	while (!atEnd() && peekToken().type != TokenType::Separator)
		eatToken();

	TokenizerPosition end = getTokenizer()->getPosition();
	auto functionContent = getTokenizer()->getTokens(start,end);

	// checks

	// Expression functions have to be defined at parse time, so they can't be defined in blocks
	// with non-trivial conditions
	if (isInsideUnknownBlock())
	{
		printError(first, "Expression function definition not allowed inside of block with non-trivial condition");
		return false;
	}

	// if we are in a known false block, don't define the function
	if (!isInsideTrueBlock())
		return true;

	if(nextToken().type != TokenType::Separator)
	{
		printError(first, ".expfunc directive not terminated");
		return false;
	}

	// duplicate check
	if (ExpressionFunctionHandler::instance().find(functionName))
	{
		printError(first, "Expression function \"%s\" already declared", functionName);
		return false;
	}

	// register function
	ExpressionFunctionHandler::instance().addUserFunction(functionName, functionParameters, functionContent);
	return true;
}

bool Parser::checkMacroDefinition()
{
	const Token& first = peekToken();
	if (first.type != TokenType::Identifier)
		return false;

	const auto &identifier = first.identifierValue();
	if (!identifier.startsWith('.') || first.identifierValue() != ".macro")
		return false;

	eatToken();

	// nested macro definitions are not allowed
	if (initializingMacro)
	{
		printError(first, "Nested macro definitions not allowed");
		while (!atEnd())
		{
			const Token& token = nextToken();
			if (token.type == TokenType::Identifier && token.identifierValue() == ".endmacro")
				break;
		}

		return true;
	}

	std::vector<Expression> parameters;
	if (!parseExpressionList(parameters,1,-1))
		return false;

	ParserMacro macro;
	macro.counter = 0;

	// load name
	if (!parameters[0].evaluateIdentifier(macro.name))
		return false;

	// load parameters
	for (size_t i = 1; i < parameters.size(); i++)
	{
		Identifier name;
		if (!parameters[i].evaluateIdentifier(name))
			return false;

		macro.parameters.push_back(name);
	}

	if(nextToken().type != TokenType::Separator)
	{
		printError(first, "Macro directive not terminated");
		return false;
	}

	// load macro content

	TokenizerPosition start = getTokenizer()->getPosition();
	bool valid = false;
	while (!atEnd())
	{
		const Token& tok = nextToken();
		if (tok.type == TokenType::Identifier && tok.identifierValue() == ".endmacro")
		{
			valid = true;
			break;
		}
	}

	// Macros have to be defined at parse time, so they can't be defined in blocks
	// with non-trivial conditions
	if (isInsideUnknownBlock())
	{
		printError(first, "Macro definition not allowed inside of block with non-trivial condition");
		return false;
	}

	// if we are in a known false block, don't define the macro
	if (!isInsideTrueBlock())
		return true;

	// duplicate check
	if (macros.find(macro.name) != macros.end())
	{
		printError(first, "Macro \"%s\" already defined", macro.name);
		return false;
	}

	// no .endmacro, not valid
	if (!valid)
	{
		printError(first, "Macro \"%s\" not terminated", macro.name);
		return true;
	}

	// get content
	TokenizerPosition end = getTokenizer()->getPosition().previous();
	macro.content = getTokenizer()->getTokens(start,end);

	if(nextToken().type != TokenType::Separator)
	{
		printError(first, "Endmacro directive not terminated");
		return false;
	}

	macros[macro.name] = macro;
	return true;
}

std::optional<std::vector<Token>> Parser::extractMacroParameter(const Token &macroStart)
{
	TokenizerPosition startPos = getTokenizer()->getPosition();

	// Find the end of the parameter. The parameter may contain expressions with function calls,
	// so keep track of the current parenthesis depth level
	int parenCount = 0;
	int braceCount = 0;
	int bracketCount = 0;

	while (peekToken().type != TokenType::Separator)
	{
		// if the next token is a comma, only exit the loop if parentheses are balanced
		auto type = peekToken().type;
		if (type == TokenType::Comma && parenCount == 0 && braceCount == 0 && bracketCount == 0)
			break;

		// keep track of parenthesis depth
		switch (type)
		{
		case TokenType::LParen:
			++parenCount;
			break;
		case TokenType::RParen:
			--parenCount;
			break;
		case TokenType::LBrace:
			++braceCount;
			break;
		case TokenType::RBrace:
			--braceCount;
			break;
		case TokenType::LBrack:
			++bracketCount;
			break;
		case TokenType::RBrack:
			--bracketCount;
			break;
		default:
			break;
		}

		eatToken();
	}

	if (parenCount != 0)
	{
		printError(macroStart, "Unbalanced parentheses in macro parameter");
		return std::nullopt;
	}

	TokenizerPosition endPos = getTokenizer()->getPosition();
	std::vector<Token> tokens = getTokenizer()->getTokens(startPos,endPos);
	if (tokens.size() == 0)
	{
		printError(macroStart, "Empty macro argument");
		return std::nullopt;
	}

	return tokens;
}

std::unique_ptr<CAssemblerCommand> Parser::parseMacroCall()
{
	const Token& start = peekToken();
	if (start.type != TokenType::Identifier)
		return nullptr;

	auto it = macros.find(start.identifierValue());
	if (it == macros.end())
		return nullptr;

	ParserMacro& macro = it->second;
	eatToken();

	// create a token stream for the macro content,
	// registering replacements for parameter values
	TokenStreamTokenizer macroTokenizer;

	std::set<Identifier> identifierParameters;
	for (size_t i = 0; i < macro.parameters.size(); i++)
	{
		if (peekToken().type == TokenType::Separator)
		{
			printError(start, "Too few macro arguments (%d vs %d)",i,macro.parameters.size());
			return nullptr;
		}

		if (i != 0)
		{
			if (nextToken().type != TokenType::Comma)
			{
				printError(start, "Macro arguments not comma-separated");
				return nullptr;
			}
		}

		auto tokens = extractMacroParameter(start);
		if (!tokens)
			return nullptr;

		// remember any single identifier parameters for the label replacement
		if (tokens->size() == 1 && tokens->front().type == TokenType::Identifier)
			identifierParameters.insert(tokens->front().identifierValue());

		// give them as a replacement to new tokenizer
		macroTokenizer.registerReplacement(macro.parameters[i], *tokens);
	}

	if (peekToken().type == TokenType::Comma)
	{
		size_t count = macro.parameters.size();
		while (peekToken().type == TokenType::Comma)
		{
			// skip comma
			eatToken();

			// skip parameter value
			extractMacroParameter(start);
			++count;
		}

		printError(start, "Too many macro arguments (%d vs %d)",count,macro.parameters.size());
		return nullptr;
	}

	if(nextToken().type != TokenType::Separator)
	{
		printError(start, "Macro call not terminated");
		return nullptr;
	}

	// skip macro instantiation in known false blocks
	if (!isInsideUnknownBlock() && !isInsideTrueBlock())
		return std::make_unique<DummyCommand>();

	// a macro is fully parsed once when it's loaded
	// to gather all labels. it's not necessary to
	// instantiate other macros at that time
	if (initializingMacro)
		return std::make_unique<DummyCommand>();

	// the first time a macro is instantiated, it needs to be analyzed
	// for labels
	if (macro.counter == 0)
	{
		initializingMacro = true;

		// parse the short lived next command
		macroTokenizer.init(macro.content);
		Logger::suppressErrors();
		std::unique_ptr<CAssemblerCommand> command =  parse(&macroTokenizer,true);
		Logger::unsuppressErrors();

		macro.labels = macroLabels;
		macroLabels.clear();

		initializingMacro = false;
	}

	// register labels and replacements
	for (const Identifier& label: macro.labels)
	{
		// check if the label is using the name of a parameter
		// in that case, don't register a unique replacement
		if (identifierParameters.find(label) != identifierParameters.end())
			continue;

		// otherwise make sure the name is unique
		std::string fullName;
		if (Global.symbolTable.isLocalSymbol(Identifier(label)))
			fullName = tfm::format("@@%s_%s_%08X",macro.name,label.string().substr(2),macro.counter);
		else if (Global.symbolTable.isStaticSymbol(Identifier(label)))
			fullName = tfm::format("@%s_%s_%08X",macro.name,label.string().substr(1),macro.counter);
		else
			fullName = tfm::format("%s_%s_%08X",macro.name,label,macro.counter);

		macroTokenizer.registerReplacement(label,fullName);
	}

	macroTokenizer.init(macro.content);
	macro.counter++;

	return parse(&macroTokenizer,true);

}

std::unique_ptr<CAssemblerCommand> Parser::parseLabel()
{
	updateFileInfo();

	const Token& start = peekToken(0);

	if (peekToken(0).type == TokenType::Identifier &&
		peekToken(1).type == TokenType::Colon)
	{
		const Identifier &name = start.identifierValue();
		eatTokens(2);

		if (initializingMacro)
			macroLabels.insert(name);

		if (!Global.symbolTable.isValidSymbolName(name))
		{
			printError(start, "Invalid label name \"%s\"",name);
			return nullptr;
		}

		return std::make_unique<CAssemblerLabel>(name,Identifier(start.getOriginalText()));
	}

	return nullptr;
}

std::unique_ptr<CAssemblerCommand> Parser::handleError()
{
	// skip the rest of the statement
	while (!atEnd() && nextToken().type != TokenType::Separator);

	clearError();
	return std::make_unique<InvalidCommand>();
}


void Parser::updateFileInfo()
{
	if (overrideFileInfo)
	{
		Global.FileInfo.FileNum = overrideFileNum;
		Global.FileInfo.LineNumber = overrideLineNum;
		return;
	}

	for (size_t i = entries.size(); i > 0; i--)
	{
		size_t index = i-1;

		if (!entries[index].virtualFile && entries[index].fileNum != -1)
		{
			Global.FileInfo.FileNum = entries[index].fileNum;

			// if it's not the topmost file, then the command to instantiate the
			// following files was already parsed -> take the previous command's line
			if (index != entries.size() - 1)
				Global.FileInfo.LineNumber = entries[index].previousCommandLine;
			else
			{
				Global.FileInfo.LineNumber = (int)entries[index].tokenizer->peekToken().line;
				entries[index].previousCommandLine = Global.FileInfo.LineNumber;
			}
			return;
		}
	}
}

std::unique_ptr<CAssemblerCommand> Parser::parseCommand()
{
	std::unique_ptr<CAssemblerCommand> command;

	updateFileInfo();

	if (atEnd())
		return std::make_unique<DummyCommand>();

	if ((command = parseLabel()) != nullptr)
		return command;
	if (hasError())
		return handleError();

	if ((command = parseMacroCall()) != nullptr)
		return command;
	if (hasError())
		return handleError();

	if ((command = Architecture::current().parseDirective(*this)) != nullptr)
		return command;
	if (hasError())
		return handleError();

	if ((command = parseDirective(directives)) != nullptr)
		return command;
	if (hasError())
		return handleError();

	if ((command = Architecture::current().parseOpcode(*this)) != nullptr)
		return command;
	if (hasError())
		return handleError();

	const Token& token = peekToken();
	printError(token,"Parse error '%s'", token.getOriginalText());
	return handleError();
}

void TokenSequenceParser::addEntry(int result, TokenSequence tokens, TokenValueSequence values)
{
	Entry entry = { tokens, values, result };
	entries.push_back(entry);
}

bool TokenSequenceParser::parse(Parser& parser, int& result)
{
	for (Entry& entry: entries)
	{
		TokenizerPosition pos = parser.getTokenizer()->getPosition();
		auto values = entry.values.begin();

		bool valid = true;
		for (TokenType type: entry.tokens)
		{
			// check of token type matches
			const Token& token = parser.nextToken();
			if (token.type != type)
			{
				valid = false;
				break;
			}

			// if necessary, check if the value of the token also matches
			if (type == TokenType::Identifier)
			{
				if (values == entry.values.end() || values->textValue != token.identifierValue())
				{
					valid = false;
					break;
				}

				values++;
			} else if (type == TokenType::Integer)
			{
				if (values == entry.values.end() || values->intValue != token.intValue())
				{
					valid = false;
					break;
				}

				values++;
			}
		}

		if (valid && values == entry.values.end())
		{
			result = entry.result;
			return true;
		}

		parser.getTokenizer()->setPosition(pos);
	}

	return false;
}

#pragma region "File: Parser/Tokenizer.cpp"


#include <algorithm>


//
// Tokenizer
//

namespace
{
	// Assumes that there is a valid character at index
	char32_t decodeUtf8Character(const std::string &source, size_t &index)
	{
		size_t extraBytes = 0;
		int value = source[index++];

		if ((value & 0xE0) == 0xC0)
		{
			extraBytes = 1;
			value &= 0x1F;
		} else if ((value & 0xF0) == 0xE0)
		{
			extraBytes = 2;
			value &= 0x0F;
		}

		for (size_t i = 0; i < extraBytes; i++)
		{
			int b = source[index++];
			value = (value << 6) | (b & 0x3F);
		}

		return char32_t(value);
	}
}

std::vector<std::vector<Token>> Tokenizer::equValues;

Tokenizer::Tokenizer()
{
	position.it = tokens.begin();
	invalidToken.type = TokenType::Invalid;
	invalidToken.setValue(std::monostate{}, "Unexpected end of token stream");
}

bool Tokenizer::processElement(TokenList::iterator& it)
{
	if (it == tokens.end())
		return false;

	while (!(*it).checked)
	{
		bool replaced = false;
		if ((*it).type == TokenType::Identifier)
		{
			const Identifier &identifier = (*it).identifierValue();
			for (const Replacement& replacement: replacements)
			{
				// if the identifier matches, add all of its tokens
				if (replacement.identifier == identifier)
				{
					TokenList::iterator insertIt = it;
					++insertIt;

					// replace old token with the new tokens
					// replace the first token manually so that any iterators
					// are still guaranteed to be valid
					(*it) = replacement.value[0];
					tokens.insert(insertIt,replacement.value.begin()+1, replacement.value.end());

					// If the value at this position didn't change, then just keep going.
					// Otherwise we'd be stuck in an endless replace loop
					if ((*it).type != TokenType::Identifier || identifier != (*it).identifierValue())
						replaced = true;
					break;
				}
			}

			if (replaced)
				continue;

			// check for equs
			size_t index;
			if (Global.symbolTable.findEquation(identifier,Global.FileInfo.FileNum,Global.Section,index))
			{
				TokenList::iterator nextIt = it;
				std::advance(nextIt, 1);

				// check if this is another equ with the same name.
				// if so, keep equ redefinitions for later error handling
				if (nextIt != tokens.end() && nextIt->type == TokenType::Equ)
					break;

				// make room for the replacement tokens
				const std::vector<Token>& replacement = equValues[index];
				tokens.insert(nextIt, replacement.size()-1, {});

				// insert replacement tokens, while keeping the file info of the original token
				Token originalToken = *it;

				TokenList::iterator insertIt = it;
				for (const Token& token: replacement)
				{
					(*insertIt) = token;
					insertIt->line = originalToken.line;
					insertIt->column = originalToken.column;
					std::advance(insertIt, 1);
				}

				replaced = true;
				continue;
			}
		}

		if (!replaced)
			(*it).checked = true;
	}

	return true;
}

const Token& Tokenizer::nextToken()
{
	if (!processElement(position.it))
		return invalidToken;

	return *position.it++;
}

const Token& Tokenizer::peekToken(int ahead)
{
	auto it = position.it;
	for (int i = 0; i < ahead; i++)
	{
		if (!processElement(it))
			return invalidToken;

		++it;
	}

	if (!processElement(it))
		return invalidToken;

	return *it;
}

void Tokenizer::eatTokens(int num)
{
	for (int i = 0; i < num; i++)
	{
		if (!processElement(position.it))
			break;
		++position.it;
	}
}

void Tokenizer::skipLookahead()
{
	//position.index = tokens.size();
}

std::vector<Token> Tokenizer::getTokens(TokenizerPosition start, TokenizerPosition end) const
{
	std::vector<Token> result;

	for (auto it = start.it; it != end.it; it++)
	{
		Token tok = *it;
		tok.checked = false;
		result.push_back(tok);
	}

	return result;
}

void Tokenizer::registerReplacement(const Identifier& identifier, std::vector<Token>& tokens)
{
	Replacement replacement { identifier, tokens };
	replacements.push_back(replacement);
}

void Tokenizer::registerReplacement(const Identifier& identifier, const std::string& newValue)
{
	// Ensure the new identifier is lower case as it would be as a normally parsed string
	std::string lowerCase = newValue;
	std::transform(lowerCase.begin(), lowerCase.end(), lowerCase.begin(), ::tolower);

	Token tok;
	tok.type = TokenType::Identifier;
	tok.setValue(Identifier(lowerCase), newValue);

	Replacement replacement;
	replacement.identifier = identifier;
	replacement.value.push_back(tok);

	replacements.push_back(replacement);
}

void Tokenizer::registerReplacementString(const Identifier& identifier, const StringLiteral& newValue)
{
	Token tok;
	tok.type = TokenType::String;
	tok.setValue(newValue, newValue.string());

	Replacement replacement;
	replacement.identifier = identifier;
	replacement.value.push_back(tok);

	replacements.push_back(replacement);
}

void Tokenizer::registerReplacementInteger(const Identifier& identifier, int64_t newValue)
{
	Token tok;
	tok.type = TokenType::Integer;
	tok.setValue(newValue, tfm::format("%d", newValue));

	Replacement replacement;
	replacement.identifier = identifier;
	replacement.value.push_back(tok);

	replacements.push_back(replacement);
}

void Tokenizer::registerReplacementFloat(const Identifier& identifier, double newValue)
{
	Token tok;
	tok.type = TokenType::Float;
	tok.setValue(newValue, tfm::format("%g", newValue));

	Replacement replacement;
	replacement.identifier = identifier;
	replacement.value.push_back(tok);

	replacements.push_back(replacement);
}

void Tokenizer::addToken(Token token)
{
	tokens.push_back(std::move(token));
}

size_t Tokenizer::addEquValue(const std::vector<Token>& tokens)
{
	size_t index = equValues.size();
	equValues.push_back(tokens);
	return index;
}

void Tokenizer::resetLookaheadCheckMarks()
{
	auto it = position.it;
	while (it != tokens.end() && it->checked)
	{
		it->checked = false;
		it++;
	}
}

//
// FileTokenizer
//

inline bool isWhitespace(const std::string& text, size_t pos)
{
	if (pos >= text.size())
		return false;

	return text[pos] == ' ' || text[pos] == '\t';
}

inline bool isComment(const std::string& text, size_t pos)
{
	if (pos < text.size() && text[pos] == ';')
		return true;

	if (pos+1 < text.size() && text[pos+0] == '/' && text[pos+1] == '/')
		return true;

	return false;
}

inline bool isContinuation(const std::string& text, size_t pos)
{
	if (pos >= text.size())
		return false;

	return text[pos] == '\\';
}

inline bool isBlockComment(const std::string& text, size_t pos){
	return pos+1 < text.size() && text[pos+0] == '/' && text[pos+1] == '*';
}

inline bool isBlockCommentEnd(const std::string& text, size_t pos){
	return pos+1 < text.size() && text[pos+0] == '*' && text[pos+1] == '/';
}

void FileTokenizer::skipWhitespace()
{
	while (true)
	{
		if (isWhitespace(currentLine,linePos))
		{
			do { linePos++; } while (isWhitespace(currentLine,linePos));
		} else if (isComment(currentLine,linePos))
		{
			linePos = currentLine.size();
		} else if (isBlockComment(currentLine,linePos))
		{
			linePos += 2;
			while(!isBlockCommentEnd(currentLine,linePos))
			{
				linePos++;
				if (linePos >= currentLine.size())
				{
					if (isInputAtEnd())
					{
						createToken(TokenType::Invalid,linePos, "Unexpected end of file in block comment");
						addToken(token);
						return;
					}
					currentLine = input->readLine();
					linePos = 0;
					lineNumber++;
				}
			}
			linePos += 2;
		} else
		{
			break;
		}
	}
}

void FileTokenizer::createToken(TokenType type, size_t length)
{
	token.type = type;
	token.line = lineNumber;
	token.column = linePos+1;
	token.setValue(std::monostate{}, currentLine.substr(linePos, length));

	linePos += length;
}

void FileTokenizer::createToken(TokenType type, size_t length, int64_t value)
{
	token.type = type;
	token.line = lineNumber;
	token.column = linePos+1;
	token.setValue(value, currentLine.substr(linePos, length));

	linePos += length;
}

void FileTokenizer::createToken(TokenType type, size_t length, double value)
{
	token.type = type;
	token.line = lineNumber;
	token.column = linePos+1;
	token.setValue(value, currentLine.substr(linePos, length));

	linePos += length;
}

void FileTokenizer::createToken(TokenType type, size_t length, const std::string& value)
{
	createToken(type, length, value, 0, value.length());
}

void FileTokenizer::createToken(TokenType type, size_t length, const std::string& value, size_t valuePos, size_t valueLength)
{
	token.type = type;
	token.line = lineNumber;
	token.column = linePos+1;

	std::string valueStr = value.substr(valuePos, valueLength);
	std::string originalStr = currentLine.substr(linePos, length);
	if (type == TokenType::Identifier || type == TokenType::Equ)
		token.setValue(Identifier(std::move(valueStr)), std::move(originalStr));
	else
		token.setValue(std::move(valueStr), std::move(originalStr));

	linePos += length;
}

void FileTokenizer::createTokenCurrentString(TokenType type, size_t length)
{
	token.type = type;
	token.line = lineNumber;
	token.column = linePos+1;

	std::string valueStr = currentLine.substr(linePos, length);
	std::string originalStr = currentLine.substr(linePos, length);
	if (type == TokenType::Identifier || type == TokenType::Equ)
		token.setValue(Identifier(std::move(valueStr)), std::move(originalStr));
	else
		token.setValue(std::move(valueStr), std::move(originalStr));

	linePos += length;
}

bool FileTokenizer::parseOperator()
{
	char first = currentLine[linePos];
	char second = linePos+1 >= currentLine.size() ? '\0' : currentLine[linePos+1];

	switch (first)
	{
	case '(':
		createToken(TokenType::LParen,1);
		return true;
	case ')':
		createToken(TokenType::RParen,1);
		return true;
	case '+':
		createToken(TokenType::Plus,1);
		return true;
	case '-':
		createToken(TokenType::Minus,1);
		return true;
	case '*':
		createToken(TokenType::Mult,1);
		return true;
	case '/':
		createToken(TokenType::Div,1);
		return true;
	case '%':
		createToken(TokenType::Mod,1);
		return true;
	case '^':
		createToken(TokenType::Caret,1);
		return true;
	case '~':
		createToken(TokenType::Tilde,1);
		return true;
	case '<':
		if (second == '<')
			createToken(TokenType::LeftShift,2);
		else if (second == '=')
			createToken(TokenType::LessEqual,2);
		else
			createToken(TokenType::Less,1);
		return true;
	case '>':
		if (second == '>')
			createToken(TokenType::RightShift,2);
		else if (second == '=')
			createToken(TokenType::GreaterEqual,2);
		else
			createToken(TokenType::Greater,1);
		return true;
	case '=':
		if (second == '=')
			createToken(TokenType::Equal,2);
		else
			createToken(TokenType::Assign,1);
		return true;
	case '!':
		if (second == '=')
			createToken(TokenType::NotEqual,2);
		else
			createToken(TokenType::Exclamation,1);
		return true;
	case '&':
		if (second == '&')
			createToken(TokenType::LogAnd,2);
		else
			createToken(TokenType::BitAnd,1);
		return true;
	case '|':
		if (second == '|')
			createToken(TokenType::LogOr,2);
		else
			createToken(TokenType::BitOr,1);
		return true;
	case '?':
		createToken(TokenType::Question,1);
		return true;
	case ':':
		if (second == ':')
			createToken(TokenType::Separator,2);
		else
			createToken(TokenType::Colon,1);
		return true;
	case ',':
		createToken(TokenType::Comma,1);
		return true;
	case '[':
		createToken(TokenType::LBrack,1);
		return true;
	case ']':
		createToken(TokenType::RBrack,1);
		return true;
	case '#':
		createToken(TokenType::Hash,1);
		return true;
	case '{':
		createToken(TokenType::LBrace,1);
		return true;
	case '}':
		createToken(TokenType::RBrace,1);
		return true;
	case '$':
		createToken(TokenType::Dollar,1);
		return true;
	case char(0xC2):	// degree sign
		if (second == char(0xB0))
		{
			createToken(TokenType::Degree,2);
			return true;
		}
		break;
	}

	return false;
}

bool FileTokenizer::convertInteger(size_t start, size_t end, int64_t& result)
{
	return stringToInt(currentLine, start, end, result);
}

bool FileTokenizer::convertFloat(size_t start, size_t end, double& result)
{
	std::string str = currentLine.substr(start, end - start);
	char* end_ptr;

	result = strtod(str.c_str(), &end_ptr);
	return str.c_str() + str.size() == end_ptr;
}

Token FileTokenizer::loadToken()
{
	if (isInputAtEnd())
	{
		createToken(TokenType::Invalid,0);
		return std::move(token);
	}

	size_t pos = linePos;

	if (equActive)
	{
		while (pos < currentLine.size() && !isComment(currentLine,pos))
			pos++;

		createTokenCurrentString(TokenType::EquValue,pos-linePos);

		equActive = false;
		return std::move(token);
	}

	if (parseOperator())
		return std::move(token);

	char first = currentLine[pos];

	// character constants
	if (first == '\'')
	{
		size_t decodePos = pos+1;

		auto value = int64_t(decodeUtf8Character(currentLine, decodePos));
		if (currentLine[decodePos] == '\'')
		{
			createToken(TokenType::Integer,decodePos+1-pos,value);
			return std::move(token);
		}
	}

	// strings
	if (first == '"')
	{
		std::string text;
		pos++;

		bool valid = false;
		while (pos < currentLine.size())
		{
			if (pos+1 < currentLine.size() && currentLine[pos] == '\\')
			{
				if (currentLine[pos+1] == '"')
				{
					text += '"';
					pos += 2;
					continue;
				}

				if (currentLine[pos+1] == '\\')
				{
					text += '\\';
					pos += 2;
					continue;
				}
			}

			if (currentLine[pos] == '"')
			{
				pos++;
				valid = true;
				break;
			}

			text += currentLine[pos++];
		}

		if (!valid)
		{
			createToken(TokenType::Invalid,pos-linePos, "Unexpected end of line in string constant");
			return std::move(token);
		}

		createToken(TokenType::String,pos-linePos,text);
		return std::move(token);
	}

	// numbers
	if (first >= '0' && first <= '9')
	{
		// find end of number
		size_t start = pos;
		size_t end = pos;
		bool isValid = true;
		bool foundPoint = false;
		bool foundExp = false;
		bool foundExpSign = false;
		bool isHex = start+1 < currentLine.size() && currentLine[start] == '0' && tolower(currentLine[start+1]) == 'x';

		while (end < currentLine.size() && (isalnum(currentLine[end]) || currentLine[end] == '.'))
		{
			if (currentLine[end] == '.')
			{
				if (foundExp || foundPoint)
					isValid = false;
				foundPoint = true;
			} else if (tolower(currentLine[end]) == 'h' && !foundExpSign) {
				isHex = true;
			} else if (tolower(currentLine[end]) == 'e' && !isHex)
			{
				if (foundExp)
				{
					isValid = false;
				} else if (end+1 < currentLine.size() && (currentLine[end+1] == '+' || currentLine[end+1] == '-')){
					end++;
					if (end+1 >= currentLine.size() || !isalnum(currentLine[end+1]))
						isValid = false;
					foundExpSign = true;
				}
				foundExp = true;
			}

			end++;
		}

		bool isFloat = foundPoint || (foundExp && !isHex);

		if (!isFloat)
		{
			int64_t value;
			if (!convertInteger(start,end,value))
			{
				createTokenCurrentString(TokenType::NumberString,end-start);
				return std::move(token);
			}

			createToken(TokenType::Integer,end-start,value);
		} else { // isFloat
			double value;
			if (!isValid)
			{
				createToken(TokenType::Invalid,end-start, "Invalid floating point number");
				return std::move(token);
			}

			if (!convertFloat(start,end,value))
			{
				createTokenCurrentString(TokenType::NumberString,end-start);
				return std::move(token);
			}

			createToken(TokenType::Float,end-start,value);
		}

		return std::move(token);
	}

	// identifiers
	bool isFirst = true;
	while (pos < currentLine.size() && Global.symbolTable.isValidSymbolCharacter(currentLine[pos],isFirst))
	{
		pos++;
		isFirst = false;
	}

	if (pos == linePos)
	{
		auto characterStart = pos;
		auto value = decodeUtf8Character(currentLine, pos);
		std::string text = tfm::format("Invalid input '%s'",convertUnicodeCharToUtf8(value));
		createToken(TokenType::Invalid,pos-characterStart,text);
		return std::move(token);
	}

	std::string text = currentLine.substr(linePos,pos-linePos);
	bool textLowered = false;
	// Lowercase is common, let's try to avoid a copy.
	if (std::any_of(text.begin(), text.end(), ::iswupper))
	{
		std::transform(text.begin(), text.end(), text.begin(), ::tolower);
		textLowered = true;
	}

	if (text == "equ")
	{
		createToken(TokenType::Equ,pos-linePos);
		equActive = true;
	} else if (textLowered) {
		createToken(TokenType::Identifier,pos-linePos,text);
	} else {
		createTokenCurrentString(TokenType::Identifier,pos-linePos);
	}

	return std::move(token);
}

bool FileTokenizer::isInputAtEnd()
{
	return linePos >= currentLine.size() && input->atEnd();
}

bool FileTokenizer::init(TextFile* input)
{
	clearTokens();

	lineNumber = 1;
	linePos = 0;
	equActive = false;
	currentLine = input->readLine();

	this->input = input;
	if (input != nullptr && input->isOpen())
	{
		while (!isInputAtEnd())
		{
			bool addSeparator = true;

			skipWhitespace();
			if (isContinuation(currentLine, linePos))
			{
				linePos++;
				skipWhitespace();
				if (linePos < currentLine.size())
				{
					createToken(TokenType::Invalid,0,
						"Unexpected character after line continuation character");
					addToken(token);
				}

				addSeparator = false;
			} else if(linePos < currentLine.size())
			{
				addToken(loadToken());
			}

			if (linePos >= currentLine.size())
			{
				if (addSeparator)
				{
					createToken(TokenType::Separator,0);
					addToken(token);
				}

				if (input->atEnd())
					break;

				currentLine = input->readLine();
				linePos = 0;
				lineNumber++;
			}
		}

		resetPosition();
		return true;
	}

	return false;
}

#pragma region "File: Util/ByteArray.cpp"


#include <cstring>

ByteArray::ByteArray()
{
	data_ = nullptr;
	size_ = allocatedSize_ = 0;
}

ByteArray::ByteArray(const ByteArray& other)
{
	data_ = nullptr;
	size_ = allocatedSize_ = 0;
	append(other);
}

ByteArray::ByteArray(byte* data, size_t size)
{
	data_ = nullptr;
	size_ = allocatedSize_ = 0;
	append(data,size);
}

ByteArray::ByteArray(ByteArray&& other)
{
	data_ = other.data_;
	size_ = other.size_;
	allocatedSize_ = other.allocatedSize_;
	other.data_ = nullptr;
	other.allocatedSize_ = other.size_ = 0;
}

ByteArray::~ByteArray()
{
	free(data_);
}

ByteArray& ByteArray::operator=(ByteArray& other)
{
	free(data_);
	data_ = nullptr;
	size_ = allocatedSize_ = 0;
	append(other);

	return *this;
}

ByteArray& ByteArray::operator=(ByteArray&& other)
{
	data_ = other.data_;
	size_ = other.size_;
	allocatedSize_ = other.allocatedSize_;
	other.data_ = nullptr;
	other.allocatedSize_ = other.size_ = 0;
	return *this;
}

void ByteArray::grow(size_t neededSize)
{
	if (neededSize < allocatedSize_) return;

	// align to next 0.5kb... it's a start
	allocatedSize_ = ((neededSize+511)/512)*512;
	if (data_ == nullptr)
	{
		data_ = (byte*) malloc(allocatedSize_);
	} else {
		data_ = (byte*) realloc(data_,allocatedSize_);
	}
}

size_t ByteArray::append(const ByteArray& other)
{
	size_t oldSize = size();
	size_t otherSize = other.size();
	grow(size()+otherSize);
	memcpy(&data_[size_],other.data(),otherSize);
	size_ += otherSize;
	return oldSize;
}

size_t ByteArray::append(void* data, size_t size)
{
	size_t oldSize = this->size();
	grow(this->size()+size);
	memcpy(&data_[size_],data,size);
	this->size_ += size;
	return oldSize;
}

void ByteArray::replaceBytes(size_t pos, byte* data, size_t size)
{
	for (size_t i = 0; i < size; i++)
	{
		replaceByte(pos+i,data[i]);
	}
}

void ByteArray::reserveBytes(size_t count, byte value)
{
	grow(this->size()+count);
	memset(&data_[size_],value,count);
	size_ += count;
}

void ByteArray::alignSize(size_t alignment)
{
	if (alignment <= 0) return;

	while (size_ % alignment)
	{
		appendByte(0);
	}
}

void ByteArray::resize(size_t newSize)
{
	grow(newSize);
	size_ = newSize;
}

ByteArray ByteArray::mid(size_t start, ssize_t length)
{
	ByteArray ret;

	if (length < 0)
		length = size_-start;

	if (start >= size_)
		return ret;

	ret.grow(length);
	ret.size_ = length;
	memcpy(ret.data_,&data_[start],length);
	return ret;
}

ByteArray ByteArray::fromFile(const fs::path& fileName, long start, size_t size)
{
	fs::ifstream stream(fileName, fs::fstream::in | fs::fstream::binary);
	if (!stream.is_open())
		return {};

	auto fileSize = fs::file_size(fileName);
	if (start >= fileSize)
		return {};

	if (size == 0 || start+(long)size > fileSize)
		size = fileSize-start;

	stream.seekg(start);

	ByteArray ret;
	ret.grow(size);

	stream.read(reinterpret_cast<char *>(ret.data()), size);
	ret.size_ = stream.gcount();

	return ret;
}

bool ByteArray::toFile(const fs::path& fileName)
{
	fs::ofstream stream(fileName, fs::fstream::out | fs::fstream::binary | fs::fstream::trunc);
	if (!stream.is_open())
		return {};

	stream.write(reinterpret_cast<const char *>(data_), size_);
	return !stream.fail();
}

#pragma region "File: Util/CRC.cpp"

#include <stdio.h>

const unsigned short Crc16Table[] = /* CRC lookup table */
{
	0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241,
	0xC601, 0x06C0, 0x0780, 0xC741, 0x0500, 0xC5C1, 0xC481, 0x0440,
	0xCC01, 0x0CC0, 0x0D80, 0xCD41, 0x0F00, 0xCFC1, 0xCE81, 0x0E40,
	0x0A00, 0xCAC1, 0xCB81, 0x0B40, 0xC901, 0x09C0, 0x0880, 0xC841,
	0xD801, 0x18C0, 0x1980, 0xD941, 0x1B00, 0xDBC1, 0xDA81, 0x1A40,
	0x1E00, 0xDEC1, 0xDF81, 0x1F40, 0xDD01, 0x1DC0, 0x1C80, 0xDC41,
	0x1400, 0xD4C1, 0xD581, 0x1540, 0xD701, 0x17C0, 0x1680, 0xD641,
	0xD201, 0x12C0, 0x1380, 0xD341, 0x1100, 0xD1C1, 0xD081, 0x1040,
	0xF001, 0x30C0, 0x3180, 0xF141, 0x3300, 0xF3C1, 0xF281, 0x3240,
	0x3600, 0xF6C1, 0xF781, 0x3740, 0xF501, 0x35C0, 0x3480, 0xF441,
	0x3C00, 0xFCC1, 0xFD81, 0x3D40, 0xFF01, 0x3FC0, 0x3E80, 0xFE41,
	0xFA01, 0x3AC0, 0x3B80, 0xFB41, 0x3900, 0xF9C1, 0xF881, 0x3840,
	0x2800, 0xE8C1, 0xE981, 0x2940, 0xEB01, 0x2BC0, 0x2A80, 0xEA41,
	0xEE01, 0x2EC0, 0x2F80, 0xEF41, 0x2D00, 0xEDC1, 0xEC81, 0x2C40,
	0xE401, 0x24C0, 0x2580, 0xE541, 0x2700, 0xE7C1, 0xE681, 0x2640,
	0x2200, 0xE2C1, 0xE381, 0x2340, 0xE101, 0x21C0, 0x2080, 0xE041,
	0xA001, 0x60C0, 0x6180, 0xA141, 0x6300, 0xA3C1, 0xA281, 0x6240,
	0x6600, 0xA6C1, 0xA781, 0x6740, 0xA501, 0x65C0, 0x6480, 0xA441,
	0x6C00, 0xACC1, 0xAD81, 0x6D40, 0xAF01, 0x6FC0, 0x6E80, 0xAE41,
	0xAA01, 0x6AC0, 0x6B80, 0xAB41, 0x6900, 0xA9C1, 0xA881, 0x6840,
	0x7800, 0xB8C1, 0xB981, 0x7940, 0xBB01, 0x7BC0, 0x7A80, 0xBA41,
	0xBE01, 0x7EC0, 0x7F80, 0xBF41, 0x7D00, 0xBDC1, 0xBC81, 0x7C40,
	0xB401, 0x74C0, 0x7580, 0xB541, 0x7700, 0xB7C1, 0xB681, 0x7640,
	0x7200, 0xB2C1, 0xB381, 0x7340, 0xB101, 0x71C0, 0x7080, 0xB041,
	0x5000, 0x90C1, 0x9181, 0x5140, 0x9301, 0x53C0, 0x5280, 0x9241,
	0x9601, 0x56C0, 0x5780, 0x9741, 0x5500, 0x95C1, 0x9481, 0x5440,
	0x9C01, 0x5CC0, 0x5D80, 0x9D41, 0x5F00, 0x9FC1, 0x9E81, 0x5E40,
	0x5A00, 0x9AC1, 0x9B81, 0x5B40, 0x9901, 0x59C0, 0x5880, 0x9841,
	0x8801, 0x48C0, 0x4980, 0x8941, 0x4B00, 0x8BC1, 0x8A81, 0x4A40,
	0x4E00, 0x8EC1, 0x8F81, 0x4F40, 0x8D01, 0x4DC0, 0x4C80, 0x8C41,
	0x4400, 0x84C1, 0x8581, 0x4540, 0x8701, 0x47C0, 0x4680, 0x8641,
	0x8201, 0x42C0, 0x4380, 0x8341, 0x4100, 0x81C1, 0x8081, 0x4040
};

const unsigned int Crc32Table[256] = {
	0x00000000,0x77073096,0xEE0E612C,0x990951BA,0x076DC419,0x706AF48F,0xE963A535,0x9E6495A3,
	0x0EDB8832,0x79DCB8A4,0xE0D5E91E,0x97D2D988,0x09B64C2B,0x7EB17CBD,0xE7B82D07,0x90BF1D91,
	0x1DB71064,0x6AB020F2,0xF3B97148,0x84BE41DE,0x1ADAD47D,0x6DDDE4EB,0xF4D4B551,0x83D385C7,
	0x136C9856,0x646BA8C0,0xFD62F97A,0x8A65C9EC,0x14015C4F,0x63066CD9,0xFA0F3D63,0x8D080DF5,
	0x3B6E20C8,0x4C69105E,0xD56041E4,0xA2677172,0x3C03E4D1,0x4B04D447,0xD20D85FD,0xA50AB56B,
	0x35B5A8FA,0x42B2986C,0xDBBBC9D6,0xACBCF940,0x32D86CE3,0x45DF5C75,0xDCD60DCF,0xABD13D59,
	0x26D930AC,0x51DE003A,0xC8D75180,0xBFD06116,0x21B4F4B5,0x56B3C423,0xCFBA9599,0xB8BDA50F,
	0x2802B89E,0x5F058808,0xC60CD9B2,0xB10BE924,0x2F6F7C87,0x58684C11,0xC1611DAB,0xB6662D3D,
	0x76DC4190,0x01DB7106,0x98D220BC,0xEFD5102A,0x71B18589,0x06B6B51F,0x9FBFE4A5,0xE8B8D433,
	0x7807C9A2,0x0F00F934,0x9609A88E,0xE10E9818,0x7F6A0DBB,0x086D3D2D,0x91646C97,0xE6635C01,
	0x6B6B51F4,0x1C6C6162,0x856530D8,0xF262004E,0x6C0695ED,0x1B01A57B,0x8208F4C1,0xF50FC457,
	0x65B0D9C6,0x12B7E950,0x8BBEB8EA,0xFCB9887C,0x62DD1DDF,0x15DA2D49,0x8CD37CF3,0xFBD44C65,
	0x4DB26158,0x3AB551CE,0xA3BC0074,0xD4BB30E2,0x4ADFA541,0x3DD895D7,0xA4D1C46D,0xD3D6F4FB,
	0x4369E96A,0x346ED9FC,0xAD678846,0xDA60B8D0,0x44042D73,0x33031DE5,0xAA0A4C5F,0xDD0D7CC9,
	0x5005713C,0x270241AA,0xBE0B1010,0xC90C2086,0x5768B525,0x206F85B3,0xB966D409,0xCE61E49F,
	0x5EDEF90E,0x29D9C998,0xB0D09822,0xC7D7A8B4,0x59B33D17,0x2EB40D81,0xB7BD5C3B,0xC0BA6CAD,
	0xEDB88320,0x9ABFB3B6,0x03B6E20C,0x74B1D29A,0xEAD54739,0x9DD277AF,0x04DB2615,0x73DC1683,
	0xE3630B12,0x94643B84,0x0D6D6A3E,0x7A6A5AA8,0xE40ECF0B,0x9309FF9D,0x0A00AE27,0x7D079EB1,
	0xF00F9344,0x8708A3D2,0x1E01F268,0x6906C2FE,0xF762575D,0x806567CB,0x196C3671,0x6E6B06E7,
	0xFED41B76,0x89D32BE0,0x10DA7A5A,0x67DD4ACC,0xF9B9DF6F,0x8EBEEFF9,0x17B7BE43,0x60B08ED5,
	0xD6D6A3E8,0xA1D1937E,0x38D8C2C4,0x4FDFF252,0xD1BB67F1,0xA6BC5767,0x3FB506DD,0x48B2364B,
	0xD80D2BDA,0xAF0A1B4C,0x36034AF6,0x41047A60,0xDF60EFC3,0xA867DF55,0x316E8EEF,0x4669BE79,
	0xCB61B38C,0xBC66831A,0x256FD2A0,0x5268E236,0xCC0C7795,0xBB0B4703,0x220216B9,0x5505262F,
	0xC5BA3BBE,0xB2BD0B28,0x2BB45A92,0x5CB36A04,0xC2D7FFA7,0xB5D0CF31,0x2CD99E8B,0x5BDEAE1D,
	0x9B64C2B0,0xEC63F226,0x756AA39C,0x026D930A,0x9C0906A9,0xEB0E363F,0x72076785,0x05005713,
	0x95BF4A82,0xE2B87A14,0x7BB12BAE,0x0CB61B38,0x92D28E9B,0xE5D5BE0D,0x7CDCEFB7,0x0BDBDF21,
	0x86D3D2D4,0xF1D4E242,0x68DDB3F8,0x1FDA836E,0x81BE16CD,0xF6B9265B,0x6FB077E1,0x18B74777,
	0x88085AE6,0xFF0F6A70,0x66063BCA,0x11010B5C,0x8F659EFF,0xF862AE69,0x616BFFD3,0x166CCF45,
	0xA00AE278,0xD70DD2EE,0x4E048354,0x3903B3C2,0xA7672661,0xD06016F7,0x4969474D,0x3E6E77DB,
	0xAED16A4A,0xD9D65ADC,0x40DF0B66,0x37D83BF0,0xA9BCAE53,0xDEBB9EC5,0x47B2CF7F,0x30B5FFE9,
	0xBDBDF21C,0xCABAC28A,0x53B39330,0x24B4A3A6,0xBAD03605,0xCDD70693,0x54DE5729,0x23D967BF,
	0xB3667A2E,0xC4614AB8,0x5D681B02,0x2A6F2B94,0xB40BBE37,0xC30C8EA1,0x5A05DF1B,0x2D02EF8D
};

unsigned short getCrc16(unsigned char* Source, size_t len)
{
	unsigned short crc = 0xFFFF;

	while (len--)
	{
		crc = (crc >> 8) ^ Crc16Table[(crc ^ *Source++) & 0xFF];
	}
	return crc;
}

unsigned int getCrc32(unsigned char* Source, size_t len)
{
	unsigned int crc = 0xFFFFFFFF;

	while (len--)
	{
		crc = (crc >> 8) ^ Crc32Table[(crc & 0xFF) ^ *Source++];
	}

	return crc ^ 0xffffffff;
}


unsigned int getChecksum(unsigned char* Source, size_t len)
{
	int checksum = 0;
	for (size_t i = 0; i < len; i++)
		checksum += *Source++;
	return checksum;
}

#pragma region "File: Util/EncodingTable.cpp"


#define MAXHEXLENGTH 32

Trie::Trie()
{
	Node root { 0, false, 0 };
	nodes.push_back(root);
}

void Trie::insert(const char* text, size_t value)
{
	size_t node = 0;	// root node

	// traverse existing nodes
	while (*text != 0)
	{
		LookupEntry lookupEntry { node, *text };
		auto it = lookup.find(lookupEntry);
		if (it == lookup.end())
			break;

		node = it->second;
		text++;
	}

	// add new nodes as necessary
	while (*text != 0)
	{
		Node newNode { nodes.size(), false, 0 };
		nodes.push_back(newNode);

		LookupEntry lookupEntry { node, *text };
		lookup[lookupEntry] = newNode.index;
		node = newNode.index;
		text++;
	}

	// set value
	nodes[node].hasValue = true;
	nodes[node].value = value;
}

bool Trie::findLongestPrefix(const char* text, size_t& result)
{
	size_t node = 0;		// root node
	size_t valueNode = 0;	// remember last node that had a value

	while (*text != 0)
	{
		if (nodes[node].hasValue)
			valueNode = node;

		LookupEntry lookupEntry { node, *text++ };
		auto it = lookup.find(lookupEntry);

		if (it == lookup.end())
			break;

		node = it->second;
	}

	if (nodes[node].hasValue)
		valueNode = node;

	result = nodes[valueNode].value;
	return nodes[valueNode].hasValue;
}

EncodingTable::EncodingTable() = default;

EncodingTable::~EncodingTable() = default;

void EncodingTable::clear()
{
	hexData.clear();
	entries.clear();
}

int parseHexString(std::string& hex, unsigned char* dest)
{
	for (size_t i = 0; i < hex.size(); i++)
	{
		int source = tolower(hex[i]);
		int value;

		if (source >= 'a' && source <= 'f')
		{
			value = source-'a'+10;
		} else if (source >= '0' && source <= '9')
		{
			value = source-'0';
		} else {
			return -1;
		}

		size_t index = i/2;
		if (i % 2)
			dest[index] = (dest[index] << 4) | value;
		else
			dest[index] = value;
	}

	return (int) hex.size()/2;
}

bool EncodingTable::load(const fs::path& fileName, TextFile::Encoding encoding)
{
	unsigned char hexBuffer[MAXHEXLENGTH];

	TextFile input;
	if (!input.open(fileName,TextFile::Read,encoding))
		return false;

	hexData.clear();
	entries.clear();
	setTerminationEntry((unsigned char*)"\0",1);

	while (!input.atEnd())
	{
		const std::string &line = input.readLine();
		if (line.empty() || line[0] == '*') continue;

		if (line[0] == '/')
		{
			std::string hex = line.substr(1);
			if (hex.empty() || hex.length() > 2*MAXHEXLENGTH)
			{
				// error
				continue;
			}

			int length = parseHexString(hex,hexBuffer);
			if (length == -1)
			{
				// error
				continue;
			}

			setTerminationEntry(hexBuffer,length);
		} else {
			size_t pos = line.find(L'=');
			std::string hex = line.substr(0,pos);
			std::string value = line.substr(pos+1);

			if (hex.empty() || value.empty() || hex.length() > 2*MAXHEXLENGTH)
			{
				// error
				continue;
			}

			int length = parseHexString(hex,hexBuffer);
			if (length == -1)
			{
				// error
				continue;
			}

			addEntry(hexBuffer,length,value);
		}
	}

	return true;
}

void EncodingTable::addEntry(unsigned char* hex, size_t hexLength, const std::string& value)
{
	if (value.size() == 0)
		return;

	// insert into trie
	size_t index = entries.size();
	lookup.insert(value.c_str(),index);

	// add entry
	TableEntry entry;
	entry.hexPos = hexData.append(hex,hexLength);
	entry.hexLen = hexLength;
	entry.valueLen = value.size();

	entries.push_back(entry);
}

void EncodingTable::setTerminationEntry(unsigned char* hex, size_t hexLength)
{
	terminationEntry.hexPos = hexData.append(hex,hexLength);
	terminationEntry.hexLen = hexLength;
	terminationEntry.valueLen = 0;
}

ByteArray EncodingTable::encodeString(const std::string& str, bool writeTermination)
{
	ByteArray result;

	size_t pos = 0;
	while (pos < str.size())
	{
		size_t index;
		if (!lookup.findLongestPrefix(str.c_str()+pos,index))
		{
			// error
			return ByteArray();
		}

		TableEntry& entry = entries[index];
		for (size_t i = 0; i < entry.hexLen; i++)
		{
			result.appendByte(hexData[entry.hexPos+i]);
		}

		pos += entry.valueLen;
	}

	if (writeTermination)
	{
		TableEntry& entry = terminationEntry;
		for (size_t i = 0; i < entry.hexLen; i++)
		{
			result.appendByte(hexData[entry.hexPos+i]);
		}
	}

	return result;
}

ByteArray EncodingTable::encodeTermination()
{
	ByteArray result;

	TableEntry& entry = terminationEntry;
	for (size_t i = 0; i < entry.hexLen; i++)
	{
		result.appendByte(hexData[entry.hexPos+i]);
	}

	return result;
}

#pragma region "File: Util/FileClasses.cpp"


#include <cassert>
#include <cstring>


namespace
{
	void encodeUtf8(std::string &dest, char32_t character)
	{
		if (character < 0x80)
		{
			dest += character & 0x7F;
		}
		else if (character < 0x800)
		{
			dest += 0xC0 | ((character >> 6) & 0x1F);
			dest += (0x80 | (character & 0x3F));
		}
		else
		{
			dest += 0xE0 | ((character >> 12) & 0xF);
			dest += 0x80 | ((character >> 6) & 0x3F);
			dest += 0x80 | (character & 0x3F);
		}
	}
}


const char16_t SjisToUnicodeTable1[] =
{
	// 0X0080 to 0X00FF
	0x0080, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
	0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
	0xFFFF, 0xFF61, 0xFF62, 0xFF63, 0xFF64, 0xFF65, 0xFF66, 0xFF67, 0xFF68, 0xFF69, 0xFF6A, 0xFF6B, 0xFF6C, 0xFF6D, 0xFF6E, 0xFF6F,
	0xFF70, 0xFF71, 0xFF72, 0xFF73, 0xFF74, 0xFF75, 0xFF76, 0xFF77, 0xFF78, 0xFF79, 0xFF7A, 0xFF7B, 0xFF7C, 0xFF7D, 0xFF7E, 0xFF7F,
	0xFF80, 0xFF81, 0xFF82, 0xFF83, 0xFF84, 0xFF85, 0xFF86, 0xFF87, 0xFF88, 0xFF89, 0xFF8A, 0xFF8B, 0xFF8C, 0xFF8D, 0xFF8E, 0xFF8F,
	0xFF90, 0xFF91, 0xFF92, 0xFF93, 0xFF94, 0xFF95, 0xFF96, 0xFF97, 0xFF98, 0xFF99, 0xFF9A, 0xFF9B, 0xFF9C, 0xFF9D, 0xFF9E, 0xFF9F,
	0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
	0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
};

const char16_t SjisToUnicodeTable2[] =
{
	// 0X8100 to 0X81FF
	0x3000, 0x3001, 0x3002, 0xFF0C, 0xFF0E, 0x30FB, 0xFF1A, 0xFF1B, 0xFF1F, 0xFF01, 0x309B, 0x309C, 0x00B4, 0xFF40, 0x00A8, 0xFF3E,
	0xFFE3, 0xFF3F, 0x30FD, 0x30FE, 0x309D, 0x309E, 0x3003, 0x4EDD, 0x3005, 0x3006, 0x3007, 0x30FC, 0x2015, 0x2010, 0xFF0F, 0xFF3C,
	0xFF5E, 0x2225, 0xFF5C, 0x2026, 0x2025, 0x2018, 0x2019, 0x201C, 0x201D, 0xFF08, 0xFF09, 0x3014, 0x3015, 0xFF3B, 0xFF3D, 0xFF5B,
	0xFF5D, 0x3008, 0x3009, 0x300A, 0x300B, 0x300C, 0x300D, 0x300E, 0x300F, 0x3010, 0x3011, 0xFF0B, 0xFF0D, 0x00B1, 0x00D7, 0xFFFF,
	0x00F7, 0xFF1D, 0x2260, 0xFF1C, 0xFF1E, 0x2266, 0x2267, 0x221E, 0x2234, 0x2642, 0x2640, 0x00B0, 0x2032, 0x2033, 0x2103, 0xFFE5,
	0xFF04, 0xFFE0, 0xFFE1, 0xFF05, 0xFF03, 0xFF06, 0xFF0A, 0xFF20, 0x00A7, 0x2606, 0x2605, 0x25CB, 0x25CF, 0x25CE, 0x25C7, 0x25C6,
	0x25A1, 0x25A0, 0x25B3, 0x25B2, 0x25BD, 0x25BC, 0x203B, 0x3012, 0x2192, 0x2190, 0x2191, 0x2193, 0x3013, 0xFFFF, 0xFFFF, 0xFFFF,
	0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x2208, 0x220B, 0x2286, 0x2287, 0x2282, 0x2283, 0x222A, 0x2229,
	0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x2227, 0x2228, 0xFFE2, 0x21D2, 0x21D4, 0x2200, 0x2203, 0xFFFF,
	0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x2220, 0x22A5, 0x2312, 0x2202, 0x2207, 0x2261,
	0x2252, 0x226A, 0x226B, 0x221A, 0x223D, 0x221D, 0x2235, 0x222B, 0x222C, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
	0x212B, 0x2030, 0x266F, 0x266D, 0x266A, 0x2020, 0x2021, 0x00B6, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x25EF, 0xFFFF, 0xFFFF, 0xFFFF,
	// 0X8200 to 0X82FF
	0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFF10,
	0xFF11, 0xFF12, 0xFF13, 0xFF14, 0xFF15, 0xFF16, 0xFF17, 0xFF18, 0xFF19, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
	0xFF21, 0xFF22, 0xFF23, 0xFF24, 0xFF25, 0xFF26, 0xFF27, 0xFF28, 0xFF29, 0xFF2A, 0xFF2B, 0xFF2C, 0xFF2D, 0xFF2E, 0xFF2F, 0xFF30,
	0xFF31, 0xFF32, 0xFF33, 0xFF34, 0xFF35, 0xFF36, 0xFF37, 0xFF38, 0xFF39, 0xFF3A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
	0xFFFF, 0xFF41, 0xFF42, 0xFF43, 0xFF44, 0xFF45, 0xFF46, 0xFF47, 0xFF48, 0xFF49, 0xFF4A, 0xFF4B, 0xFF4C, 0xFF4D, 0xFF4E, 0xFF4F,
	0xFF50, 0xFF51, 0xFF52, 0xFF53, 0xFF54, 0xFF55, 0xFF56, 0xFF57, 0xFF58, 0xFF59, 0xFF5A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x3041,
	0x3042, 0x3043, 0x3044, 0x3045, 0x3046, 0x3047, 0x3048, 0x3049, 0x304A, 0x304B, 0x304C, 0x304D, 0x304E, 0x304F, 0x3050, 0x3051,
	0x3052, 0x3053, 0x3054, 0x3055, 0x3056, 0x3057, 0x3058, 0x3059, 0x305A, 0x305B, 0x305C, 0x305D, 0x305E, 0x305F, 0x3060, 0x3061,
	0x3062, 0x3063, 0x3064, 0x3065, 0x3066, 0x3067, 0x3068, 0x3069, 0x306A, 0x306B, 0x306C, 0x306D, 0x306E, 0x306F, 0x3070, 0x3071,
	0x3072, 0x3073, 0x3074, 0x3075, 0x3076, 0x3077, 0x3078, 0x3079, 0x307A, 0x307B, 0x307C, 0x307D, 0x307E, 0x307F, 0x3080, 0x3081,
	0x3082, 0x3083, 0x3084, 0x3085, 0x3086, 0x3087, 0x3088, 0x3089, 0x308A, 0x308B, 0x308C, 0x308D, 0x308E, 0x308F, 0x3090, 0x3091,
	0x3092, 0x3093, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
	// 0X8300 to 0X83FF
	0x30A1, 0x30A2, 0x30A3, 0x30A4, 0x30A5, 0x30A6, 0x30A7, 0x30A8, 0x30A9, 0x30AA, 0x30AB, 0x30AC, 0x30AD, 0x30AE, 0x30AF, 0x30B0,
	0x30B1, 0x30B2, 0x30B3, 0x30B4, 0x30B5, 0x30B6, 0x30B7, 0x30B8, 0x30B9, 0x30BA, 0x30BB, 0x30BC, 0x30BD, 0x30BE, 0x30BF, 0x30C0,
	0x30C1, 0x30C2, 0x30C3, 0x30C4, 0x30C5, 0x30C6, 0x30C7, 0x30C8, 0x30C9, 0x30CA, 0x30CB, 0x30CC, 0x30CD, 0x30CE, 0x30CF, 0x30D0,
	0x30D1, 0x30D2, 0x30D3, 0x30D4, 0x30D5, 0x30D6, 0x30D7, 0x30D8, 0x30D9, 0x30DA, 0x30DB, 0x30DC, 0x30DD, 0x30DE, 0x30DF, 0xFFFF,
	0x30E0, 0x30E1, 0x30E2, 0x30E3, 0x30E4, 0x30E5, 0x30E6, 0x30E7, 0x30E8, 0x30E9, 0x30EA, 0x30EB, 0x30EC, 0x30ED, 0x30EE, 0x30EF,
	0x30F0, 0x30F1, 0x30F2, 0x30F3, 0x30F4, 0x30F5, 0x30F6, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0391,
	0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397, 0x0398, 0x0399, 0x039A, 0x039B, 0x039C, 0x039D, 0x039E, 0x039F, 0x03A0, 0x03A1,
	0x03A3, 0x03A4, 0x03A5, 0x03A6, 0x03A7, 0x03A8, 0x03A9, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x03B1,
	0x03B2, 0x03B3, 0x03B4, 0x03B5, 0x03B6, 0x03B7, 0x03B8, 0x03B9, 0x03BA, 0x03BB, 0x03BC, 0x03BD, 0x03BE, 0x03BF, 0x03C0, 0x03C1,
	0x03C3, 0x03C4, 0x03C5, 0x03C6, 0x03C7, 0x03C8, 0x03C9, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
	0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
	0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
	// 0X8400 to 0X84FF
	0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0401, 0x0416, 0x0417, 0x0418, 0x0419, 0x041A, 0x041B, 0x041C, 0x041D, 0x041E,
	0x041F, 0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426, 0x0427, 0x0428, 0x0429, 0x042A, 0x042B, 0x042C, 0x042D, 0x042E,
	0x042F, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
	0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0451, 0x0436, 0x0437, 0x0438, 0x0439, 0x043A, 0x043B, 0x043C, 0x043D, 0xFFFF,
	0x043E, 0x043F, 0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447, 0x0448, 0x0449, 0x044A, 0x044B, 0x044C, 0x044D,
	0x044E, 0x044F, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x2500,
	0x2502, 0x250C, 0x2510, 0x2518, 0x2514, 0x251C, 0x252C, 0x2524, 0x2534, 0x253C, 0x2501, 0x2503, 0x250F, 0x2513, 0x251B, 0x2517,
	0x2523, 0x2533, 0x252B, 0x253B, 0x254B, 0x2520, 0x252F, 0x2528, 0x2537, 0x253F, 0x251D, 0x2530, 0x2525, 0x2538, 0x2542, 0xFFFF,
	0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
	0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
	0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
	0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
};

const char16_t SjisToUnicodeTable3[] =
{
	// 0X8700 to 0X87FF
	0x2460, 0x2461, 0x2462, 0x2463, 0x2464, 0x2465, 0x2466, 0x2467, 0x2468, 0x2469, 0x246A, 0x246B, 0x246C, 0x246D, 0x246E, 0x246F,
	0x2470, 0x2471, 0x2472, 0x2473, 0x2160, 0x2161, 0x2162, 0x2163, 0x2164, 0x2165, 0x2166, 0x2167, 0x2168, 0x2169, 0xFFFF, 0x3349,
	0x3314, 0x3322, 0x334D, 0x3318, 0x3327, 0x3303, 0x3336, 0x3351, 0x3357, 0x330D, 0x3326, 0x3323, 0x332B, 0x334A, 0x333B, 0x339C,
	0x339D, 0x339E, 0x338E, 0x338F, 0x33C4, 0x33A1, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x337B, 0xFFFF,
	0x301D, 0x301F, 0x2116, 0x33CD, 0x2121, 0x32A4, 0x32A5, 0x32A6, 0x32A7, 0x32A8, 0x3231, 0x3232, 0x3239, 0x337E, 0x337D, 0x337C,
	0x2252, 0x2261, 0x222B, 0x222E, 0x2211, 0x221A, 0x22A5, 0x2220, 0x221F, 0x22BF, 0x2235, 0x2229, 0x222A, 0xFFFF, 0xFFFF, 0xFFFF,
	0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
	0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
	0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
	0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
	0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
	0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
	// 0X8800 to 0X88FF
	0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
	0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
	0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
	0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
	0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
	0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x4E9C,
	0x5516, 0x5A03, 0x963F, 0x54C0, 0x611B, 0x6328, 0x59F6, 0x9022, 0x8475, 0x831C, 0x7A50, 0x60AA, 0x63E1, 0x6E25, 0x65ED, 0x8466,
	0x82A6, 0x9BF5, 0x6893, 0x5727, 0x65A1, 0x6271, 0x5B9B, 0x59D0, 0x867B, 0x98F4, 0x7D62, 0x7DBE, 0x9B8E, 0x6216, 0x7C9F, 0x88B7,
	0x5B89, 0x5EB5, 0x6309, 0x6697, 0x6848, 0x95C7, 0x978D, 0x674F, 0x4EE5, 0x4F0A, 0x4F4D, 0x4F9D, 0x5049, 0x56F2, 0x5937, 0x59D4,
	0x5A01, 0x5C09, 0x60DF, 0x610F, 0x6170, 0x6613, 0x6905, 0x70BA, 0x754F, 0x7570, 0x79FB, 0x7DAD, 0x7DEF, 0x80C3, 0x840E, 0x8863,
	0x8B02, 0x9055, 0x907A, 0x533B, 0x4E95, 0x4EA5, 0x57DF, 0x80B2, 0x90C1, 0x78EF, 0x4E00, 0x58F1, 0x6EA2, 0x9038, 0x7A32, 0x8328,
	0x828B, 0x9C2F, 0x5141, 0x5370, 0x54BD, 0x54E1, 0x56E0, 0x59FB, 0x5F15, 0x98F2, 0x6DEB, 0x80E4, 0x852D, 0xFFFF, 0xFFFF, 0xFFFF,
	// 0X8900 to 0X89FF
	0x9662, 0x9670, 0x96A0, 0x97FB, 0x540B, 0x53F3, 0x5B87, 0x70CF, 0x7FBD, 0x8FC2, 0x96E8, 0x536F, 0x9D5C, 0x7ABA, 0x4E11, 0x7893,
	0x81FC, 0x6E26, 0x5618, 0x5504, 0x6B1D, 0x851A, 0x9C3B, 0x59E5, 0x53A9, 0x6D66, 0x74DC, 0x958F, 0x5642, 0x4E91, 0x904B, 0x96F2,
	0x834F, 0x990C, 0x53E1, 0x55B6, 0x5B30, 0x5F71, 0x6620, 0x66F3, 0x6804, 0x6C38, 0x6CF3, 0x6D29, 0x745B, 0x76C8, 0x7A4E, 0x9834,
	0x82F1, 0x885B, 0x8A60, 0x92ED, 0x6DB2, 0x75AB, 0x76CA, 0x99C5, 0x60A6, 0x8B01, 0x8D8A, 0x95B2, 0x698E, 0x53AD, 0x5186, 0xFFFF,
	0x5712, 0x5830, 0x5944, 0x5BB4, 0x5EF6, 0x6028, 0x63A9, 0x63F4, 0x6CBF, 0x6F14, 0x708E, 0x7114, 0x7159, 0x71D5, 0x733F, 0x7E01,
	0x8276, 0x82D1, 0x8597, 0x9060, 0x925B, 0x9D1B, 0x5869, 0x65BC, 0x6C5A, 0x7525, 0x51F9, 0x592E, 0x5965, 0x5F80, 0x5FDC, 0x62BC,
	0x65FA, 0x6A2A, 0x6B27, 0x6BB4, 0x738B, 0x7FC1, 0x8956, 0x9D2C, 0x9D0E, 0x9EC4, 0x5CA1, 0x6C96, 0x837B, 0x5104, 0x5C4B, 0x61B6,
	0x81C6, 0x6876, 0x7261, 0x4E59, 0x4FFA, 0x5378, 0x6069, 0x6E29, 0x7A4F, 0x97F3, 0x4E0B, 0x5316, 0x4EEE, 0x4F55, 0x4F3D, 0x4FA1,
	0x4F73, 0x52A0, 0x53EF, 0x5609, 0x590F, 0x5AC1, 0x5BB6, 0x5BE1, 0x79D1, 0x6687, 0x679C, 0x67B6, 0x6B4C, 0x6CB3, 0x706B, 0x73C2,
	0x798D, 0x79BE, 0x7A3C, 0x7B87, 0x82B1, 0x82DB, 0x8304, 0x8377, 0x83EF, 0x83D3, 0x8766, 0x8AB2, 0x5629, 0x8CA8, 0x8FE6, 0x904E,
	0x971E, 0x868A, 0x4FC4, 0x5CE8, 0x6211, 0x7259, 0x753B, 0x81E5, 0x82BD, 0x86FE, 0x8CC0, 0x96C5, 0x9913, 0x99D5, 0x4ECB, 0x4F1A,
	0x89E3, 0x56DE, 0x584A, 0x58CA, 0x5EFB, 0x5FEB, 0x602A, 0x6094, 0x6062, 0x61D0, 0x6212, 0x62D0, 0x6539, 0xFFFF, 0xFFFF, 0xFFFF,
	// 0X8A00 to 0X8AFF
	0x9B41, 0x6666, 0x68B0, 0x6D77, 0x7070, 0x754C, 0x7686, 0x7D75, 0x82A5, 0x87F9, 0x958B, 0x968E, 0x8C9D, 0x51F1, 0x52BE, 0x5916,
	0x54B3, 0x5BB3, 0x5D16, 0x6168, 0x6982, 0x6DAF, 0x788D, 0x84CB, 0x8857, 0x8A72, 0x93A7, 0x9AB8, 0x6D6C, 0x99A8, 0x86D9, 0x57A3,
	0x67FF, 0x86CE, 0x920E, 0x5283, 0x5687, 0x5404, 0x5ED3, 0x62E1, 0x64B9, 0x683C, 0x6838, 0x6BBB, 0x7372, 0x78BA, 0x7A6B, 0x899A,
	0x89D2, 0x8D6B, 0x8F03, 0x90ED, 0x95A3, 0x9694, 0x9769, 0x5B66, 0x5CB3, 0x697D, 0x984D, 0x984E, 0x639B, 0x7B20, 0x6A2B, 0xFFFF,
	0x6A7F, 0x68B6, 0x9C0D, 0x6F5F, 0x5272, 0x559D, 0x6070, 0x62EC, 0x6D3B, 0x6E07, 0x6ED1, 0x845B, 0x8910, 0x8F44, 0x4E14, 0x9C39,
	0x53F6, 0x691B, 0x6A3A, 0x9784, 0x682A, 0x515C, 0x7AC3, 0x84B2, 0x91DC, 0x938C, 0x565B, 0x9D28, 0x6822, 0x8305, 0x8431, 0x7CA5,
	0x5208, 0x82C5, 0x74E6, 0x4E7E, 0x4F83, 0x51A0, 0x5BD2, 0x520A, 0x52D8, 0x52E7, 0x5DFB, 0x559A, 0x582A, 0x59E6, 0x5B8C, 0x5B98,
	0x5BDB, 0x5E72, 0x5E79, 0x60A3, 0x611F, 0x6163, 0x61BE, 0x63DB, 0x6562, 0x67D1, 0x6853, 0x68FA, 0x6B3E, 0x6B53, 0x6C57, 0x6F22,
	0x6F97, 0x6F45, 0x74B0, 0x7518, 0x76E3, 0x770B, 0x7AFF, 0x7BA1, 0x7C21, 0x7DE9, 0x7F36, 0x7FF0, 0x809D, 0x8266, 0x839E, 0x89B3,
	0x8ACC, 0x8CAB, 0x9084, 0x9451, 0x9593, 0x9591, 0x95A2, 0x9665, 0x97D3, 0x9928, 0x8218, 0x4E38, 0x542B, 0x5CB8, 0x5DCC, 0x73A9,
	0x764C, 0x773C, 0x5CA9, 0x7FEB, 0x8D0B, 0x96C1, 0x9811, 0x9854, 0x9858, 0x4F01, 0x4F0E, 0x5371, 0x559C, 0x5668, 0x57FA, 0x5947,
	0x5B09, 0x5BC4, 0x5C90, 0x5E0C, 0x5E7E, 0x5FCC, 0x63EE, 0x673A, 0x65D7, 0x65E2, 0x671F, 0x68CB, 0x68C4, 0xFFFF, 0xFFFF, 0xFFFF,
	// 0X8B00 to 0X8BFF
	0x6A5F, 0x5E30, 0x6BC5, 0x6C17, 0x6C7D, 0x757F, 0x7948, 0x5B63, 0x7A00, 0x7D00, 0x5FBD, 0x898F, 0x8A18, 0x8CB4, 0x8D77, 0x8ECC,
	0x8F1D, 0x98E2, 0x9A0E, 0x9B3C, 0x4E80, 0x507D, 0x5100, 0x5993, 0x5B9C, 0x622F, 0x6280, 0x64EC, 0x6B3A, 0x72A0, 0x7591, 0x7947,
	0x7FA9, 0x87FB, 0x8ABC, 0x8B70, 0x63AC, 0x83CA, 0x97A0, 0x5409, 0x5403, 0x55AB, 0x6854, 0x6A58, 0x8A70, 0x7827, 0x6775, 0x9ECD,
	0x5374, 0x5BA2, 0x811A, 0x8650, 0x9006, 0x4E18, 0x4E45, 0x4EC7, 0x4F11, 0x53CA, 0x5438, 0x5BAE, 0x5F13, 0x6025, 0x6551, 0xFFFF,
	0x673D, 0x6C42, 0x6C72, 0x6CE3, 0x7078, 0x7403, 0x7A76, 0x7AAE, 0x7B08, 0x7D1A, 0x7CFE, 0x7D66, 0x65E7, 0x725B, 0x53BB, 0x5C45,
	0x5DE8, 0x62D2, 0x62E0, 0x6319, 0x6E20, 0x865A, 0x8A31, 0x8DDD, 0x92F8, 0x6F01, 0x79A6, 0x9B5A, 0x4EA8, 0x4EAB, 0x4EAC, 0x4F9B,
	0x4FA0, 0x50D1, 0x5147, 0x7AF6, 0x5171, 0x51F6, 0x5354, 0x5321, 0x537F, 0x53EB, 0x55AC, 0x5883, 0x5CE1, 0x5F37, 0x5F4A, 0x602F,
	0x6050, 0x606D, 0x631F, 0x6559, 0x6A4B, 0x6CC1, 0x72C2, 0x72ED, 0x77EF, 0x80F8, 0x8105, 0x8208, 0x854E, 0x90F7, 0x93E1, 0x97FF,
	0x9957, 0x9A5A, 0x4EF0, 0x51DD, 0x5C2D, 0x6681, 0x696D, 0x5C40, 0x66F2, 0x6975, 0x7389, 0x6850, 0x7C81, 0x50C5, 0x52E4, 0x5747,
	0x5DFE, 0x9326, 0x65A4, 0x6B23, 0x6B3D, 0x7434, 0x7981, 0x79BD, 0x7B4B, 0x7DCA, 0x82B9, 0x83CC, 0x887F, 0x895F, 0x8B39, 0x8FD1,
	0x91D1, 0x541F, 0x9280, 0x4E5D, 0x5036, 0x53E5, 0x533A, 0x72D7, 0x7396, 0x77E9, 0x82E6, 0x8EAF, 0x99C6, 0x99C8, 0x99D2, 0x5177,
	0x611A, 0x865E, 0x55B0, 0x7A7A, 0x5076, 0x5BD3, 0x9047, 0x9685, 0x4E32, 0x6ADB, 0x91E7, 0x5C51, 0x5C48, 0xFFFF, 0xFFFF, 0xFFFF,
	// 0X8C00 to 0X8CFF
	0x6398, 0x7A9F, 0x6C93, 0x9774, 0x8F61, 0x7AAA, 0x718A, 0x9688, 0x7C82, 0x6817, 0x7E70, 0x6851, 0x936C, 0x52F2, 0x541B, 0x85AB,
	0x8A13, 0x7FA4, 0x8ECD, 0x90E1, 0x5366, 0x8888, 0x7941, 0x4FC2, 0x50BE, 0x5211, 0x5144, 0x5553, 0x572D, 0x73EA, 0x578B, 0x5951,
	0x5F62, 0x5F84, 0x6075, 0x6176, 0x6167, 0x61A9, 0x63B2, 0x643A, 0x656C, 0x666F, 0x6842, 0x6E13, 0x7566, 0x7A3D, 0x7CFB, 0x7D4C,
	0x7D99, 0x7E4B, 0x7F6B, 0x830E, 0x834A, 0x86CD, 0x8A08, 0x8A63, 0x8B66, 0x8EFD, 0x981A, 0x9D8F, 0x82B8, 0x8FCE, 0x9BE8, 0xFFFF,
	0x5287, 0x621F, 0x6483, 0x6FC0, 0x9699, 0x6841, 0x5091, 0x6B20, 0x6C7A, 0x6F54, 0x7A74, 0x7D50, 0x8840, 0x8A23, 0x6708, 0x4EF6,
	0x5039, 0x5026, 0x5065, 0x517C, 0x5238, 0x5263, 0x55A7, 0x570F, 0x5805, 0x5ACC, 0x5EFA, 0x61B2, 0x61F8, 0x62F3, 0x6372, 0x691C,
	0x6A29, 0x727D, 0x72AC, 0x732E, 0x7814, 0x786F, 0x7D79, 0x770C, 0x80A9, 0x898B, 0x8B19, 0x8CE2, 0x8ED2, 0x9063, 0x9375, 0x967A,
	0x9855, 0x9A13, 0x9E78, 0x5143, 0x539F, 0x53B3, 0x5E7B, 0x5F26, 0x6E1B, 0x6E90, 0x7384, 0x73FE, 0x7D43, 0x8237, 0x8A00, 0x8AFA,
	0x9650, 0x4E4E, 0x500B, 0x53E4, 0x547C, 0x56FA, 0x59D1, 0x5B64, 0x5DF1, 0x5EAB, 0x5F27, 0x6238, 0x6545, 0x67AF, 0x6E56, 0x72D0,
	0x7CCA, 0x88B4, 0x80A1, 0x80E1, 0x83F0, 0x864E, 0x8A87, 0x8DE8, 0x9237, 0x96C7, 0x9867, 0x9F13, 0x4E94, 0x4E92, 0x4F0D, 0x5348,
	0x5449, 0x543E, 0x5A2F, 0x5F8C, 0x5FA1, 0x609F, 0x68A7, 0x6A8E, 0x745A, 0x7881, 0x8A9E, 0x8AA4, 0x8B77, 0x9190, 0x4E5E, 0x9BC9,
	0x4EA4, 0x4F7C, 0x4FAF, 0x5019, 0x5016, 0x5149, 0x516C, 0x529F, 0x52B9, 0x52FE, 0x539A, 0x53E3, 0x5411, 0xFFFF, 0xFFFF, 0xFFFF,
	// 0X8D00 to 0X8DFF
	0x540E, 0x5589, 0x5751, 0x57A2, 0x597D, 0x5B54, 0x5B5D, 0x5B8F, 0x5DE5, 0x5DE7, 0x5DF7, 0x5E78, 0x5E83, 0x5E9A, 0x5EB7, 0x5F18,
	0x6052, 0x614C, 0x6297, 0x62D8, 0x63A7, 0x653B, 0x6602, 0x6643, 0x66F4, 0x676D, 0x6821, 0x6897, 0x69CB, 0x6C5F, 0x6D2A, 0x6D69,
	0x6E2F, 0x6E9D, 0x7532, 0x7687, 0x786C, 0x7A3F, 0x7CE0, 0x7D05, 0x7D18, 0x7D5E, 0x7DB1, 0x8015, 0x8003, 0x80AF, 0x80B1, 0x8154,
	0x818F, 0x822A, 0x8352, 0x884C, 0x8861, 0x8B1B, 0x8CA2, 0x8CFC, 0x90CA, 0x9175, 0x9271, 0x783F, 0x92FC, 0x95A4, 0x964D, 0xFFFF,
	0x9805, 0x9999, 0x9AD8, 0x9D3B, 0x525B, 0x52AB, 0x53F7, 0x5408, 0x58D5, 0x62F7, 0x6FE0, 0x8C6A, 0x8F5F, 0x9EB9, 0x514B, 0x523B,
	0x544A, 0x56FD, 0x7A40, 0x9177, 0x9D60, 0x9ED2, 0x7344, 0x6F09, 0x8170, 0x7511, 0x5FFD, 0x60DA, 0x9AA8, 0x72DB, 0x8FBC, 0x6B64,
	0x9803, 0x4ECA, 0x56F0, 0x5764, 0x58BE, 0x5A5A, 0x6068, 0x61C7, 0x660F, 0x6606, 0x6839, 0x68B1, 0x6DF7, 0x75D5, 0x7D3A, 0x826E,
	0x9B42, 0x4E9B, 0x4F50, 0x53C9, 0x5506, 0x5D6F, 0x5DE6, 0x5DEE, 0x67FB, 0x6C99, 0x7473, 0x7802, 0x8A50, 0x9396, 0x88DF, 0x5750,
	0x5EA7, 0x632B, 0x50B5, 0x50AC, 0x518D, 0x6700, 0x54C9, 0x585E, 0x59BB, 0x5BB0, 0x5F69, 0x624D, 0x63A1, 0x683D, 0x6B73, 0x6E08,
	0x707D, 0x91C7, 0x7280, 0x7815, 0x7826, 0x796D, 0x658E, 0x7D30, 0x83DC, 0x88C1, 0x8F09, 0x969B, 0x5264, 0x5728, 0x6750, 0x7F6A,
	0x8CA1, 0x51B4, 0x5742, 0x962A, 0x583A, 0x698A, 0x80B4, 0x54B2, 0x5D0E, 0x57FC, 0x7895, 0x9DFA, 0x4F5C, 0x524A, 0x548B, 0x643E,
	0x6628, 0x6714, 0x67F5, 0x7A84, 0x7B56, 0x7D22, 0x932F, 0x685C, 0x9BAD, 0x7B39, 0x5319, 0x518A, 0x5237, 0xFFFF, 0xFFFF, 0xFFFF,
	// 0X8E00 to 0X8EFF
	0x5BDF, 0x62F6, 0x64AE, 0x64E6, 0x672D, 0x6BBA, 0x85A9, 0x96D1, 0x7690, 0x9BD6, 0x634C, 0x9306, 0x9BAB, 0x76BF, 0x6652, 0x4E09,
	0x5098, 0x53C2, 0x5C71, 0x60E8, 0x6492, 0x6563, 0x685F, 0x71E6, 0x73CA, 0x7523, 0x7B97, 0x7E82, 0x8695, 0x8B83, 0x8CDB, 0x9178,
	0x9910, 0x65AC, 0x66AB, 0x6B8B, 0x4ED5, 0x4ED4, 0x4F3A, 0x4F7F, 0x523A, 0x53F8, 0x53F2, 0x55E3, 0x56DB, 0x58EB, 0x59CB, 0x59C9,
	0x59FF, 0x5B50, 0x5C4D, 0x5E02, 0x5E2B, 0x5FD7, 0x601D, 0x6307, 0x652F, 0x5B5C, 0x65AF, 0x65BD, 0x65E8, 0x679D, 0x6B62, 0xFFFF,
	0x6B7B, 0x6C0F, 0x7345, 0x7949, 0x79C1, 0x7CF8, 0x7D19, 0x7D2B, 0x80A2, 0x8102, 0x81F3, 0x8996, 0x8A5E, 0x8A69, 0x8A66, 0x8A8C,
	0x8AEE, 0x8CC7, 0x8CDC, 0x96CC, 0x98FC, 0x6B6F, 0x4E8B, 0x4F3C, 0x4F8D, 0x5150, 0x5B57, 0x5BFA, 0x6148, 0x6301, 0x6642, 0x6B21,
	0x6ECB, 0x6CBB, 0x723E, 0x74BD, 0x75D4, 0x78C1, 0x793A, 0x800C, 0x8033, 0x81EA, 0x8494, 0x8F9E, 0x6C50, 0x9E7F, 0x5F0F, 0x8B58,
	0x9D2B, 0x7AFA, 0x8EF8, 0x5B8D, 0x96EB, 0x4E03, 0x53F1, 0x57F7, 0x5931, 0x5AC9, 0x5BA4, 0x6089, 0x6E7F, 0x6F06, 0x75BE, 0x8CEA,
	0x5B9F, 0x8500, 0x7BE0, 0x5072, 0x67F4, 0x829D, 0x5C61, 0x854A, 0x7E1E, 0x820E, 0x5199, 0x5C04, 0x6368, 0x8D66, 0x659C, 0x716E,
	0x793E, 0x7D17, 0x8005, 0x8B1D, 0x8ECA, 0x906E, 0x86C7, 0x90AA, 0x501F, 0x52FA, 0x5C3A, 0x6753, 0x707C, 0x7235, 0x914C, 0x91C8,
	0x932B, 0x82E5, 0x5BC2, 0x5F31, 0x60F9, 0x4E3B, 0x53D6, 0x5B88, 0x624B, 0x6731, 0x6B8A, 0x72E9, 0x73E0, 0x7A2E, 0x816B, 0x8DA3,
	0x9152, 0x9996, 0x5112, 0x53D7, 0x546A, 0x5BFF, 0x6388, 0x6A39, 0x7DAC, 0x9700, 0x56DA, 0x53CE, 0x5468, 0xFFFF, 0xFFFF, 0xFFFF,
	// 0X8F00 to 0X8FFF
	0x5B97, 0x5C31, 0x5DDE, 0x4FEE, 0x6101, 0x62FE, 0x6D32, 0x79C0, 0x79CB, 0x7D42, 0x7E4D, 0x7FD2, 0x81ED, 0x821F, 0x8490, 0x8846,
	0x8972, 0x8B90, 0x8E74, 0x8F2F, 0x9031, 0x914B, 0x916C, 0x96C6, 0x919C, 0x4EC0, 0x4F4F, 0x5145, 0x5341, 0x5F93, 0x620E, 0x67D4,
	0x6C41, 0x6E0B, 0x7363, 0x7E26, 0x91CD, 0x9283, 0x53D4, 0x5919, 0x5BBF, 0x6DD1, 0x795D, 0x7E2E, 0x7C9B, 0x587E, 0x719F, 0x51FA,
	0x8853, 0x8FF0, 0x4FCA, 0x5CFB, 0x6625, 0x77AC, 0x7AE3, 0x821C, 0x99FF, 0x51C6, 0x5FAA, 0x65EC, 0x696F, 0x6B89, 0x6DF3, 0xFFFF,
	0x6E96, 0x6F64, 0x76FE, 0x7D14, 0x5DE1, 0x9075, 0x9187, 0x9806, 0x51E6, 0x521D, 0x6240, 0x6691, 0x66D9, 0x6E1A, 0x5EB6, 0x7DD2,
	0x7F72, 0x66F8, 0x85AF, 0x85F7, 0x8AF8, 0x52A9, 0x53D9, 0x5973, 0x5E8F, 0x5F90, 0x6055, 0x92E4, 0x9664, 0x50B7, 0x511F, 0x52DD,
	0x5320, 0x5347, 0x53EC, 0x54E8, 0x5546, 0x5531, 0x5617, 0x5968, 0x59BE, 0x5A3C, 0x5BB5, 0x5C06, 0x5C0F, 0x5C11, 0x5C1A, 0x5E84,
	0x5E8A, 0x5EE0, 0x5F70, 0x627F, 0x6284, 0x62DB, 0x638C, 0x6377, 0x6607, 0x660C, 0x662D, 0x6676, 0x677E, 0x68A2, 0x6A1F, 0x6A35,
	0x6CBC, 0x6D88, 0x6E09, 0x6E58, 0x713C, 0x7126, 0x7167, 0x75C7, 0x7701, 0x785D, 0x7901, 0x7965, 0x79F0, 0x7AE0, 0x7B11, 0x7CA7,
	0x7D39, 0x8096, 0x83D6, 0x848B, 0x8549, 0x885D, 0x88F3, 0x8A1F, 0x8A3C, 0x8A54, 0x8A73, 0x8C61, 0x8CDE, 0x91A4, 0x9266, 0x937E,
	0x9418, 0x969C, 0x9798, 0x4E0A, 0x4E08, 0x4E1E, 0x4E57, 0x5197, 0x5270, 0x57CE, 0x5834, 0x58CC, 0x5B22, 0x5E38, 0x60C5, 0x64FE,
	0x6761, 0x6756, 0x6D44, 0x72B6, 0x7573, 0x7A63, 0x84B8, 0x8B72, 0x91B8, 0x9320, 0x5631, 0x57F4, 0x98FE, 0xFFFF, 0xFFFF, 0xFFFF,
	// 0X9000 to 0X90FF
	0x62ED, 0x690D, 0x6B96, 0x71ED, 0x7E54, 0x8077, 0x8272, 0x89E6, 0x98DF, 0x8755, 0x8FB1, 0x5C3B, 0x4F38, 0x4FE1, 0x4FB5, 0x5507,
	0x5A20, 0x5BDD, 0x5BE9, 0x5FC3, 0x614E, 0x632F, 0x65B0, 0x664B, 0x68EE, 0x699B, 0x6D78, 0x6DF1, 0x7533, 0x75B9, 0x771F, 0x795E,
	0x79E6, 0x7D33, 0x81E3, 0x82AF, 0x85AA, 0x89AA, 0x8A3A, 0x8EAB, 0x8F9B, 0x9032, 0x91DD, 0x9707, 0x4EBA, 0x4EC1, 0x5203, 0x5875,
	0x58EC, 0x5C0B, 0x751A, 0x5C3D, 0x814E, 0x8A0A, 0x8FC5, 0x9663, 0x976D, 0x7B25, 0x8ACF, 0x9808, 0x9162, 0x56F3, 0x53A8, 0xFFFF,
	0x9017, 0x5439, 0x5782, 0x5E25, 0x63A8, 0x6C34, 0x708A, 0x7761, 0x7C8B, 0x7FE0, 0x8870, 0x9042, 0x9154, 0x9310, 0x9318, 0x968F,
	0x745E, 0x9AC4, 0x5D07, 0x5D69, 0x6570, 0x67A2, 0x8DA8, 0x96DB, 0x636E, 0x6749, 0x6919, 0x83C5, 0x9817, 0x96C0, 0x88FE, 0x6F84,
	0x647A, 0x5BF8, 0x4E16, 0x702C, 0x755D, 0x662F, 0x51C4, 0x5236, 0x52E2, 0x59D3, 0x5F81, 0x6027, 0x6210, 0x653F, 0x6574, 0x661F,
	0x6674, 0x68F2, 0x6816, 0x6B63, 0x6E05, 0x7272, 0x751F, 0x76DB, 0x7CBE, 0x8056, 0x58F0, 0x88FD, 0x897F, 0x8AA0, 0x8A93, 0x8ACB,
	0x901D, 0x9192, 0x9752, 0x9759, 0x6589, 0x7A0E, 0x8106, 0x96BB, 0x5E2D, 0x60DC, 0x621A, 0x65A5, 0x6614, 0x6790, 0x77F3, 0x7A4D,
	0x7C4D, 0x7E3E, 0x810A, 0x8CAC, 0x8D64, 0x8DE1, 0x8E5F, 0x78A9, 0x5207, 0x62D9, 0x63A5, 0x6442, 0x6298, 0x8A2D, 0x7A83, 0x7BC0,
	0x8AAC, 0x96EA, 0x7D76, 0x820C, 0x8749, 0x4ED9, 0x5148, 0x5343, 0x5360, 0x5BA3, 0x5C02, 0x5C16, 0x5DDD, 0x6226, 0x6247, 0x64B0,
	0x6813, 0x6834, 0x6CC9, 0x6D45, 0x6D17, 0x67D3, 0x6F5C, 0x714E, 0x717D, 0x65CB, 0x7A7F, 0x7BAD, 0x7DDA, 0xFFFF, 0xFFFF, 0xFFFF,
	// 0X9100 to 0X91FF
	0x7E4A, 0x7FA8, 0x817A, 0x821B, 0x8239, 0x85A6, 0x8A6E, 0x8CCE, 0x8DF5, 0x9078, 0x9077, 0x92AD, 0x9291, 0x9583, 0x9BAE, 0x524D,
	0x5584, 0x6F38, 0x7136, 0x5168, 0x7985, 0x7E55, 0x81B3, 0x7CCE, 0x564C, 0x5851, 0x5CA8, 0x63AA, 0x66FE, 0x66FD, 0x695A, 0x72D9,
	0x758F, 0x758E, 0x790E, 0x7956, 0x79DF, 0x7C97, 0x7D20, 0x7D44, 0x8607, 0x8A34, 0x963B, 0x9061, 0x9F20, 0x50E7, 0x5275, 0x53CC,
	0x53E2, 0x5009, 0x55AA, 0x58EE, 0x594F, 0x723D, 0x5B8B, 0x5C64, 0x531D, 0x60E3, 0x60F3, 0x635C, 0x6383, 0x633F, 0x63BB, 0xFFFF,
	0x64CD, 0x65E9, 0x66F9, 0x5DE3, 0x69CD, 0x69FD, 0x6F15, 0x71E5, 0x4E89, 0x75E9, 0x76F8, 0x7A93, 0x7CDF, 0x7DCF, 0x7D9C, 0x8061,
	0x8349, 0x8358, 0x846C, 0x84BC, 0x85FB, 0x88C5, 0x8D70, 0x9001, 0x906D, 0x9397, 0x971C, 0x9A12, 0x50CF, 0x5897, 0x618E, 0x81D3,
	0x8535, 0x8D08, 0x9020, 0x4FC3, 0x5074, 0x5247, 0x5373, 0x606F, 0x6349, 0x675F, 0x6E2C, 0x8DB3, 0x901F, 0x4FD7, 0x5C5E, 0x8CCA,
	0x65CF, 0x7D9A, 0x5352, 0x8896, 0x5176, 0x63C3, 0x5B58, 0x5B6B, 0x5C0A, 0x640D, 0x6751, 0x905C, 0x4ED6, 0x591A, 0x592A, 0x6C70,
	0x8A51, 0x553E, 0x5815, 0x59A5, 0x60F0, 0x6253, 0x67C1, 0x8235, 0x6955, 0x9640, 0x99C4, 0x9A28, 0x4F53, 0x5806, 0x5BFE, 0x8010,
	0x5CB1, 0x5E2F, 0x5F85, 0x6020, 0x614B, 0x6234, 0x66FF, 0x6CF0, 0x6EDE, 0x80CE, 0x817F, 0x82D4, 0x888B, 0x8CB8, 0x9000, 0x902E,
	0x968A, 0x9EDB, 0x9BDB, 0x4EE3, 0x53F0, 0x5927, 0x7B2C, 0x918D, 0x984C, 0x9DF9, 0x6EDD, 0x7027, 0x5353, 0x5544, 0x5B85, 0x6258,
	0x629E, 0x62D3, 0x6CA2, 0x6FEF, 0x7422, 0x8A17, 0x9438, 0x6FC1, 0x8AFE, 0x8338, 0x51E7, 0x86F8, 0x53EA, 0xFFFF, 0xFFFF, 0xFFFF,
	// 0X9200 to 0X92FF
	0x53E9, 0x4F46, 0x9054, 0x8FB0, 0x596A, 0x8131, 0x5DFD, 0x7AEA, 0x8FBF, 0x68DA, 0x8C37, 0x72F8, 0x9C48, 0x6A3D, 0x8AB0, 0x4E39,
	0x5358, 0x5606, 0x5766, 0x62C5, 0x63A2, 0x65E6, 0x6B4E, 0x6DE1, 0x6E5B, 0x70AD, 0x77ED, 0x7AEF, 0x7BAA, 0x7DBB, 0x803D, 0x80C6,
	0x86CB, 0x8A95, 0x935B, 0x56E3, 0x58C7, 0x5F3E, 0x65AD, 0x6696, 0x6A80, 0x6BB5, 0x7537, 0x8AC7, 0x5024, 0x77E5, 0x5730, 0x5F1B,
	0x6065, 0x667A, 0x6C60, 0x75F4, 0x7A1A, 0x7F6E, 0x81F4, 0x8718, 0x9045, 0x99B3, 0x7BC9, 0x755C, 0x7AF9, 0x7B51, 0x84C4, 0xFFFF,
	0x9010, 0x79E9, 0x7A92, 0x8336, 0x5AE1, 0x7740, 0x4E2D, 0x4EF2, 0x5B99, 0x5FE0, 0x62BD, 0x663C, 0x67F1, 0x6CE8, 0x866B, 0x8877,
	0x8A3B, 0x914E, 0x92F3, 0x99D0, 0x6A17, 0x7026, 0x732A, 0x82E7, 0x8457, 0x8CAF, 0x4E01, 0x5146, 0x51CB, 0x558B, 0x5BF5, 0x5E16,
	0x5E33, 0x5E81, 0x5F14, 0x5F35, 0x5F6B, 0x5FB4, 0x61F2, 0x6311, 0x66A2, 0x671D, 0x6F6E, 0x7252, 0x753A, 0x773A, 0x8074, 0x8139,
	0x8178, 0x8776, 0x8ABF, 0x8ADC, 0x8D85, 0x8DF3, 0x929A, 0x9577, 0x9802, 0x9CE5, 0x52C5, 0x6357, 0x76F4, 0x6715, 0x6C88, 0x73CD,
	0x8CC3, 0x93AE, 0x9673, 0x6D25, 0x589C, 0x690E, 0x69CC, 0x8FFD, 0x939A, 0x75DB, 0x901A, 0x585A, 0x6802, 0x63B4, 0x69FB, 0x4F43,
	0x6F2C, 0x67D8, 0x8FBB, 0x8526, 0x7DB4, 0x9354, 0x693F, 0x6F70, 0x576A, 0x58F7, 0x5B2C, 0x7D2C, 0x722A, 0x540A, 0x91E3, 0x9DB4,
	0x4EAD, 0x4F4E, 0x505C, 0x5075, 0x5243, 0x8C9E, 0x5448, 0x5824, 0x5B9A, 0x5E1D, 0x5E95, 0x5EAD, 0x5EF7, 0x5F1F, 0x608C, 0x62B5,
	0x633A, 0x63D0, 0x68AF, 0x6C40, 0x7887, 0x798E, 0x7A0B, 0x7DE0, 0x8247, 0x8A02, 0x8AE6, 0x8E44, 0x9013, 0xFFFF, 0xFFFF, 0xFFFF,
	// 0X9300 to 0X93FF
	0x90B8, 0x912D, 0x91D8, 0x9F0E, 0x6CE5, 0x6458, 0x64E2, 0x6575, 0x6EF4, 0x7684, 0x7B1B, 0x9069, 0x93D1, 0x6EBA, 0x54F2, 0x5FB9,
	0x64A4, 0x8F4D, 0x8FED, 0x9244, 0x5178, 0x586B, 0x5929, 0x5C55, 0x5E97, 0x6DFB, 0x7E8F, 0x751C, 0x8CBC, 0x8EE2, 0x985B, 0x70B9,
	0x4F1D, 0x6BBF, 0x6FB1, 0x7530, 0x96FB, 0x514E, 0x5410, 0x5835, 0x5857, 0x59AC, 0x5C60, 0x5F92, 0x6597, 0x675C, 0x6E21, 0x767B,
	0x83DF, 0x8CED, 0x9014, 0x90FD, 0x934D, 0x7825, 0x783A, 0x52AA, 0x5EA6, 0x571F, 0x5974, 0x6012, 0x5012, 0x515A, 0x51AC, 0xFFFF,
	0x51CD, 0x5200, 0x5510, 0x5854, 0x5858, 0x5957, 0x5B95, 0x5CF6, 0x5D8B, 0x60BC, 0x6295, 0x642D, 0x6771, 0x6843, 0x68BC, 0x68DF,
	0x76D7, 0x6DD8, 0x6E6F, 0x6D9B, 0x706F, 0x71C8, 0x5F53, 0x75D8, 0x7977, 0x7B49, 0x7B54, 0x7B52, 0x7CD6, 0x7D71, 0x5230, 0x8463,
	0x8569, 0x85E4, 0x8A0E, 0x8B04, 0x8C46, 0x8E0F, 0x9003, 0x900F, 0x9419, 0x9676, 0x982D, 0x9A30, 0x95D8, 0x50CD, 0x52D5, 0x540C,
	0x5802, 0x5C0E, 0x61A7, 0x649E, 0x6D1E, 0x77B3, 0x7AE5, 0x80F4, 0x8404, 0x9053, 0x9285, 0x5CE0, 0x9D07, 0x533F, 0x5F97, 0x5FB3,
	0x6D9C, 0x7279, 0x7763, 0x79BF, 0x7BE4, 0x6BD2, 0x72EC, 0x8AAD, 0x6803, 0x6A61, 0x51F8, 0x7A81, 0x6934, 0x5C4A, 0x9CF6, 0x82EB,
	0x5BC5, 0x9149, 0x701E, 0x5678, 0x5C6F, 0x60C7, 0x6566, 0x6C8C, 0x8C5A, 0x9041, 0x9813, 0x5451, 0x66C7, 0x920D, 0x5948, 0x90A3,
	0x5185, 0x4E4D, 0x51EA, 0x8599, 0x8B0E, 0x7058, 0x637A, 0x934B, 0x6962, 0x99B4, 0x7E04, 0x7577, 0x5357, 0x6960, 0x8EDF, 0x96E3,
	0x6C5D, 0x4E8C, 0x5C3C, 0x5F10, 0x8FE9, 0x5302, 0x8CD1, 0x8089, 0x8679, 0x5EFF, 0x65E5, 0x4E73, 0x5165, 0xFFFF, 0xFFFF, 0xFFFF,
	// 0X9400 to 0X94FF
	0x5982, 0x5C3F, 0x97EE, 0x4EFB, 0x598A, 0x5FCD, 0x8A8D, 0x6FE1, 0x79B0, 0x7962, 0x5BE7, 0x8471, 0x732B, 0x71B1, 0x5E74, 0x5FF5,
	0x637B, 0x649A, 0x71C3, 0x7C98, 0x4E43, 0x5EFC, 0x4E4B, 0x57DC, 0x56A2, 0x60A9, 0x6FC3, 0x7D0D, 0x80FD, 0x8133, 0x81BF, 0x8FB2,
	0x8997, 0x86A4, 0x5DF4, 0x628A, 0x64AD, 0x8987, 0x6777, 0x6CE2, 0x6D3E, 0x7436, 0x7834, 0x5A46, 0x7F75, 0x82AD, 0x99AC, 0x4FF3,
	0x5EC3, 0x62DD, 0x6392, 0x6557, 0x676F, 0x76C3, 0x724C, 0x80CC, 0x80BA, 0x8F29, 0x914D, 0x500D, 0x57F9, 0x5A92, 0x6885, 0xFFFF,
	0x6973, 0x7164, 0x72FD, 0x8CB7, 0x58F2, 0x8CE0, 0x966A, 0x9019, 0x877F, 0x79E4, 0x77E7, 0x8429, 0x4F2F, 0x5265, 0x535A, 0x62CD,
	0x67CF, 0x6CCA, 0x767D, 0x7B94, 0x7C95, 0x8236, 0x8584, 0x8FEB, 0x66DD, 0x6F20, 0x7206, 0x7E1B, 0x83AB, 0x99C1, 0x9EA6, 0x51FD,
	0x7BB1, 0x7872, 0x7BB8, 0x8087, 0x7B48, 0x6AE8, 0x5E61, 0x808C, 0x7551, 0x7560, 0x516B, 0x9262, 0x6E8C, 0x767A, 0x9197, 0x9AEA,
	0x4F10, 0x7F70, 0x629C, 0x7B4F, 0x95A5, 0x9CE9, 0x567A, 0x5859, 0x86E4, 0x96BC, 0x4F34, 0x5224, 0x534A, 0x53CD, 0x53DB, 0x5E06,
	0x642C, 0x6591, 0x677F, 0x6C3E, 0x6C4E, 0x7248, 0x72AF, 0x73ED, 0x7554, 0x7E41, 0x822C, 0x85E9, 0x8CA9, 0x7BC4, 0x91C6, 0x7169,
	0x9812, 0x98EF, 0x633D, 0x6669, 0x756A, 0x76E4, 0x78D0, 0x8543, 0x86EE, 0x532A, 0x5351, 0x5426, 0x5983, 0x5E87, 0x5F7C, 0x60B2,
	0x6249, 0x6279, 0x62AB, 0x6590, 0x6BD4, 0x6CCC, 0x75B2, 0x76AE, 0x7891, 0x79D8, 0x7DCB, 0x7F77, 0x80A5, 0x88AB, 0x8AB9, 0x8CBB,
	0x907F, 0x975E, 0x98DB, 0x6A0B, 0x7C38, 0x5099, 0x5C3E, 0x5FAE, 0x6787, 0x6BD8, 0x7435, 0x7709, 0x7F8E, 0xFFFF, 0xFFFF, 0xFFFF,
	// 0X9500 to 0X95FF
	0x9F3B, 0x67CA, 0x7A17, 0x5339, 0x758B, 0x9AED, 0x5F66, 0x819D, 0x83F1, 0x8098, 0x5F3C, 0x5FC5, 0x7562, 0x7B46, 0x903C, 0x6867,
	0x59EB, 0x5A9B, 0x7D10, 0x767E, 0x8B2C, 0x4FF5, 0x5F6A, 0x6A19, 0x6C37, 0x6F02, 0x74E2, 0x7968, 0x8868, 0x8A55, 0x8C79, 0x5EDF,
	0x63CF, 0x75C5, 0x79D2, 0x82D7, 0x9328, 0x92F2, 0x849C, 0x86ED, 0x9C2D, 0x54C1, 0x5F6C, 0x658C, 0x6D5C, 0x7015, 0x8CA7, 0x8CD3,
	0x983B, 0x654F, 0x74F6, 0x4E0D, 0x4ED8, 0x57E0, 0x592B, 0x5A66, 0x5BCC, 0x51A8, 0x5E03, 0x5E9C, 0x6016, 0x6276, 0x6577, 0xFFFF,
	0x65A7, 0x666E, 0x6D6E, 0x7236, 0x7B26, 0x8150, 0x819A, 0x8299, 0x8B5C, 0x8CA0, 0x8CE6, 0x8D74, 0x961C, 0x9644, 0x4FAE, 0x64AB,
	0x6B66, 0x821E, 0x8461, 0x856A, 0x90E8, 0x5C01, 0x6953, 0x98A8, 0x847A, 0x8557, 0x4F0F, 0x526F, 0x5FA9, 0x5E45, 0x670D, 0x798F,
	0x8179, 0x8907, 0x8986, 0x6DF5, 0x5F17, 0x6255, 0x6CB8, 0x4ECF, 0x7269, 0x9B92, 0x5206, 0x543B, 0x5674, 0x58B3, 0x61A4, 0x626E,
	0x711A, 0x596E, 0x7C89, 0x7CDE, 0x7D1B, 0x96F0, 0x6587, 0x805E, 0x4E19, 0x4F75, 0x5175, 0x5840, 0x5E63, 0x5E73, 0x5F0A, 0x67C4,
	0x4E26, 0x853D, 0x9589, 0x965B, 0x7C73, 0x9801, 0x50FB, 0x58C1, 0x7656, 0x78A7, 0x5225, 0x77A5, 0x8511, 0x7B86, 0x504F, 0x5909,
	0x7247, 0x7BC7, 0x7DE8, 0x8FBA, 0x8FD4, 0x904D, 0x4FBF, 0x52C9, 0x5A29, 0x5F01, 0x97AD, 0x4FDD, 0x8217, 0x92EA, 0x5703, 0x6355,
	0x6B69, 0x752B, 0x88DC, 0x8F14, 0x7A42, 0x52DF, 0x5893, 0x6155, 0x620A, 0x66AE, 0x6BCD, 0x7C3F, 0x83E9, 0x5023, 0x4FF8, 0x5305,
	0x5446, 0x5831, 0x5949, 0x5B9D, 0x5CF0, 0x5CEF, 0x5D29, 0x5E96, 0x62B1, 0x6367, 0x653E, 0x65B9, 0x670B, 0xFFFF, 0xFFFF, 0xFFFF,
	// 0X9600 to 0X96FF
	0x6CD5, 0x6CE1, 0x70F9, 0x7832, 0x7E2B, 0x80DE, 0x82B3, 0x840C, 0x84EC, 0x8702, 0x8912, 0x8A2A, 0x8C4A, 0x90A6, 0x92D2, 0x98FD,
	0x9CF3, 0x9D6C, 0x4E4F, 0x4EA1, 0x508D, 0x5256, 0x574A, 0x59A8, 0x5E3D, 0x5FD8, 0x5FD9, 0x623F, 0x66B4, 0x671B, 0x67D0, 0x68D2,
	0x5192, 0x7D21, 0x80AA, 0x81A8, 0x8B00, 0x8C8C, 0x8CBF, 0x927E, 0x9632, 0x5420, 0x982C, 0x5317, 0x50D5, 0x535C, 0x58A8, 0x64B2,
	0x6734, 0x7267, 0x7766, 0x7A46, 0x91E6, 0x52C3, 0x6CA1, 0x6B86, 0x5800, 0x5E4C, 0x5954, 0x672C, 0x7FFB, 0x51E1, 0x76C6, 0xFFFF,
	0x6469, 0x78E8, 0x9B54, 0x9EBB, 0x57CB, 0x59B9, 0x6627, 0x679A, 0x6BCE, 0x54E9, 0x69D9, 0x5E55, 0x819C, 0x6795, 0x9BAA, 0x67FE,
	0x9C52, 0x685D, 0x4EA6, 0x4FE3, 0x53C8, 0x62B9, 0x672B, 0x6CAB, 0x8FC4, 0x4FAD, 0x7E6D, 0x9EBF, 0x4E07, 0x6162, 0x6E80, 0x6F2B,
	0x8513, 0x5473, 0x672A, 0x9B45, 0x5DF3, 0x7B95, 0x5CAC, 0x5BC6, 0x871C, 0x6E4A, 0x84D1, 0x7A14, 0x8108, 0x5999, 0x7C8D, 0x6C11,
	0x7720, 0x52D9, 0x5922, 0x7121, 0x725F, 0x77DB, 0x9727, 0x9D61, 0x690B, 0x5A7F, 0x5A18, 0x51A5, 0x540D, 0x547D, 0x660E, 0x76DF,
	0x8FF7, 0x9298, 0x9CF4, 0x59EA, 0x725D, 0x6EC5, 0x514D, 0x68C9, 0x7DBF, 0x7DEC, 0x9762, 0x9EBA, 0x6478, 0x6A21, 0x8302, 0x5984,
	0x5B5F, 0x6BDB, 0x731B, 0x76F2, 0x7DB2, 0x8017, 0x8499, 0x5132, 0x6728, 0x9ED9, 0x76EE, 0x6762, 0x52FF, 0x9905, 0x5C24, 0x623B,
	0x7C7E, 0x8CB0, 0x554F, 0x60B6, 0x7D0B, 0x9580, 0x5301, 0x4E5F, 0x51B6, 0x591C, 0x723A, 0x8036, 0x91CE, 0x5F25, 0x77E2, 0x5384,
	0x5F79, 0x7D04, 0x85AC, 0x8A33, 0x8E8D, 0x9756, 0x67F3, 0x85AE, 0x9453, 0x6109, 0x6108, 0x6CB9, 0x7652, 0xFFFF, 0xFFFF, 0xFFFF,
	// 0X9700 to 0X97FF
	0x8AED, 0x8F38, 0x552F, 0x4F51, 0x512A, 0x52C7, 0x53CB, 0x5BA5, 0x5E7D, 0x60A0, 0x6182, 0x63D6, 0x6709, 0x67DA, 0x6E67, 0x6D8C,
	0x7336, 0x7337, 0x7531, 0x7950, 0x88D5, 0x8A98, 0x904A, 0x9091, 0x90F5, 0x96C4, 0x878D, 0x5915, 0x4E88, 0x4F59, 0x4E0E, 0x8A89,
	0x8F3F, 0x9810, 0x50AD, 0x5E7C, 0x5996, 0x5BB9, 0x5EB8, 0x63DA, 0x63FA, 0x64C1, 0x66DC, 0x694A, 0x69D8, 0x6D0B, 0x6EB6, 0x7194,
	0x7528, 0x7AAF, 0x7F8A, 0x8000, 0x8449, 0x84C9, 0x8981, 0x8B21, 0x8E0A, 0x9065, 0x967D, 0x990A, 0x617E, 0x6291, 0x6B32, 0xFFFF,
	0x6C83, 0x6D74, 0x7FCC, 0x7FFC, 0x6DC0, 0x7F85, 0x87BA, 0x88F8, 0x6765, 0x83B1, 0x983C, 0x96F7, 0x6D1B, 0x7D61, 0x843D, 0x916A,
	0x4E71, 0x5375, 0x5D50, 0x6B04, 0x6FEB, 0x85CD, 0x862D, 0x89A7, 0x5229, 0x540F, 0x5C65, 0x674E, 0x68A8, 0x7406, 0x7483, 0x75E2,
	0x88CF, 0x88E1, 0x91CC, 0x96E2, 0x9678, 0x5F8B, 0x7387, 0x7ACB, 0x844E, 0x63A0, 0x7565, 0x5289, 0x6D41, 0x6E9C, 0x7409, 0x7559,
	0x786B, 0x7C92, 0x9686, 0x7ADC, 0x9F8D, 0x4FB6, 0x616E, 0x65C5, 0x865C, 0x4E86, 0x4EAE, 0x50DA, 0x4E21, 0x51CC, 0x5BEE, 0x6599,
	0x6881, 0x6DBC, 0x731F, 0x7642, 0x77AD, 0x7A1C, 0x7CE7, 0x826F, 0x8AD2, 0x907C, 0x91CF, 0x9675, 0x9818, 0x529B, 0x7DD1, 0x502B,
	0x5398, 0x6797, 0x6DCB, 0x71D0, 0x7433, 0x81E8, 0x8F2A, 0x96A3, 0x9C57, 0x9E9F, 0x7460, 0x5841, 0x6D99, 0x7D2F, 0x985E, 0x4EE4,
	0x4F36, 0x4F8B, 0x51B7, 0x52B1, 0x5DBA, 0x601C, 0x73B2, 0x793C, 0x82D3, 0x9234, 0x96B7, 0x96F6, 0x970A, 0x9E97, 0x9F62, 0x66A6,
	0x6B74, 0x5217, 0x52A3, 0x70C8, 0x88C2, 0x5EC9, 0x604B, 0x6190, 0x6F23, 0x7149, 0x7C3E, 0x7DF4, 0x806F, 0xFFFF, 0xFFFF, 0xFFFF,
	// 0X9800 to 0X98FF
	0x84EE, 0x9023, 0x932C, 0x5442, 0x9B6F, 0x6AD3, 0x7089, 0x8CC2, 0x8DEF, 0x9732, 0x52B4, 0x5A41, 0x5ECA, 0x5F04, 0x6717, 0x697C,
	0x6994, 0x6D6A, 0x6F0F, 0x7262, 0x72FC, 0x7BED, 0x8001, 0x807E, 0x874B, 0x90CE, 0x516D, 0x9E93, 0x7984, 0x808B, 0x9332, 0x8AD6,
	0x502D, 0x548C, 0x8A71, 0x6B6A, 0x8CC4, 0x8107, 0x60D1, 0x67A0, 0x9DF2, 0x4E99, 0x4E98, 0x9C10, 0x8A6B, 0x85C1, 0x8568, 0x6900,
	0x6E7E, 0x7897, 0x8155, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
	0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
	0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x5F0C,
	0x4E10, 0x4E15, 0x4E2A, 0x4E31, 0x4E36, 0x4E3C, 0x4E3F, 0x4E42, 0x4E56, 0x4E58, 0x4E82, 0x4E85, 0x8C6B, 0x4E8A, 0x8212, 0x5F0D,
	0x4E8E, 0x4E9E, 0x4E9F, 0x4EA0, 0x4EA2, 0x4EB0, 0x4EB3, 0x4EB6, 0x4ECE, 0x4ECD, 0x4EC4, 0x4EC6, 0x4EC2, 0x4ED7, 0x4EDE, 0x4EED,
	0x4EDF, 0x4EF7, 0x4F09, 0x4F5A, 0x4F30, 0x4F5B, 0x4F5D, 0x4F57, 0x4F47, 0x4F76, 0x4F88, 0x4F8F, 0x4F98, 0x4F7B, 0x4F69, 0x4F70,
	0x4F91, 0x4F6F, 0x4F86, 0x4F96, 0x5118, 0x4FD4, 0x4FDF, 0x4FCE, 0x4FD8, 0x4FDB, 0x4FD1, 0x4FDA, 0x4FD0, 0x4FE4, 0x4FE5, 0x501A,
	0x5028, 0x5014, 0x502A, 0x5025, 0x5005, 0x4F1C, 0x4FF6, 0x5021, 0x5029, 0x502C, 0x4FFE, 0x4FEF, 0x5011, 0x5006, 0x5043, 0x5047,
	0x6703, 0x5055, 0x5050, 0x5048, 0x505A, 0x5056, 0x506C, 0x5078, 0x5080, 0x509A, 0x5085, 0x50B4, 0x50B2, 0xFFFF, 0xFFFF, 0xFFFF,
	// 0X9900 to 0X99FF
	0x50C9, 0x50CA, 0x50B3, 0x50C2, 0x50D6, 0x50DE, 0x50E5, 0x50ED, 0x50E3, 0x50EE, 0x50F9, 0x50F5, 0x5109, 0x5101, 0x5102, 0x5116,
	0x5115, 0x5114, 0x511A, 0x5121, 0x513A, 0x5137, 0x513C, 0x513B, 0x513F, 0x5140, 0x5152, 0x514C, 0x5154, 0x5162, 0x7AF8, 0x5169,
	0x516A, 0x516E, 0x5180, 0x5182, 0x56D8, 0x518C, 0x5189, 0x518F, 0x5191, 0x5193, 0x5195, 0x5196, 0x51A4, 0x51A6, 0x51A2, 0x51A9,
	0x51AA, 0x51AB, 0x51B3, 0x51B1, 0x51B2, 0x51B0, 0x51B5, 0x51BD, 0x51C5, 0x51C9, 0x51DB, 0x51E0, 0x8655, 0x51E9, 0x51ED, 0xFFFF,
	0x51F0, 0x51F5, 0x51FE, 0x5204, 0x520B, 0x5214, 0x520E, 0x5227, 0x522A, 0x522E, 0x5233, 0x5239, 0x524F, 0x5244, 0x524B, 0x524C,
	0x525E, 0x5254, 0x526A, 0x5274, 0x5269, 0x5273, 0x527F, 0x527D, 0x528D, 0x5294, 0x5292, 0x5271, 0x5288, 0x5291, 0x8FA8, 0x8FA7,
	0x52AC, 0x52AD, 0x52BC, 0x52B5, 0x52C1, 0x52CD, 0x52D7, 0x52DE, 0x52E3, 0x52E6, 0x98ED, 0x52E0, 0x52F3, 0x52F5, 0x52F8, 0x52F9,
	0x5306, 0x5308, 0x7538, 0x530D, 0x5310, 0x530F, 0x5315, 0x531A, 0x5323, 0x532F, 0x5331, 0x5333, 0x5338, 0x5340, 0x5346, 0x5345,
	0x4E17, 0x5349, 0x534D, 0x51D6, 0x535E, 0x5369, 0x536E, 0x5918, 0x537B, 0x5377, 0x5382, 0x5396, 0x53A0, 0x53A6, 0x53A5, 0x53AE,
	0x53B0, 0x53B6, 0x53C3, 0x7C12, 0x96D9, 0x53DF, 0x66FC, 0x71EE, 0x53EE, 0x53E8, 0x53ED, 0x53FA, 0x5401, 0x543D, 0x5440, 0x542C,
	0x542D, 0x543C, 0x542E, 0x5436, 0x5429, 0x541D, 0x544E, 0x548F, 0x5475, 0x548E, 0x545F, 0x5471, 0x5477, 0x5470, 0x5492, 0x547B,
	0x5480, 0x5476, 0x5484, 0x5490, 0x5486, 0x54C7, 0x54A2, 0x54B8, 0x54A5, 0x54AC, 0x54C4, 0x54C8, 0x54A8, 0xFFFF, 0xFFFF, 0xFFFF,
	// 0X9A00 to 0X9AFF
	0x54AB, 0x54C2, 0x54A4, 0x54BE, 0x54BC, 0x54D8, 0x54E5, 0x54E6, 0x550F, 0x5514, 0x54FD, 0x54EE, 0x54ED, 0x54FA, 0x54E2, 0x5539,
	0x5540, 0x5563, 0x554C, 0x552E, 0x555C, 0x5545, 0x5556, 0x5557, 0x5538, 0x5533, 0x555D, 0x5599, 0x5580, 0x54AF, 0x558A, 0x559F,
	0x557B, 0x557E, 0x5598, 0x559E, 0x55AE, 0x557C, 0x5583, 0x55A9, 0x5587, 0x55A8, 0x55DA, 0x55C5, 0x55DF, 0x55C4, 0x55DC, 0x55E4,
	0x55D4, 0x5614, 0x55F7, 0x5616, 0x55FE, 0x55FD, 0x561B, 0x55F9, 0x564E, 0x5650, 0x71DF, 0x5634, 0x5636, 0x5632, 0x5638, 0xFFFF,
	0x566B, 0x5664, 0x562F, 0x566C, 0x566A, 0x5686, 0x5680, 0x568A, 0x56A0, 0x5694, 0x568F, 0x56A5, 0x56AE, 0x56B6, 0x56B4, 0x56C2,
	0x56BC, 0x56C1, 0x56C3, 0x56C0, 0x56C8, 0x56CE, 0x56D1, 0x56D3, 0x56D7, 0x56EE, 0x56F9, 0x5700, 0x56FF, 0x5704, 0x5709, 0x5708,
	0x570B, 0x570D, 0x5713, 0x5718, 0x5716, 0x55C7, 0x571C, 0x5726, 0x5737, 0x5738, 0x574E, 0x573B, 0x5740, 0x574F, 0x5769, 0x57C0,
	0x5788, 0x5761, 0x577F, 0x5789, 0x5793, 0x57A0, 0x57B3, 0x57A4, 0x57AA, 0x57B0, 0x57C3, 0x57C6, 0x57D4, 0x57D2, 0x57D3, 0x580A,
	0x57D6, 0x57E3, 0x580B, 0x5819, 0x581D, 0x5872, 0x5821, 0x5862, 0x584B, 0x5870, 0x6BC0, 0x5852, 0x583D, 0x5879, 0x5885, 0x58B9,
	0x589F, 0x58AB, 0x58BA, 0x58DE, 0x58BB, 0x58B8, 0x58AE, 0x58C5, 0x58D3, 0x58D1, 0x58D7, 0x58D9, 0x58D8, 0x58E5, 0x58DC, 0x58E4,
	0x58DF, 0x58EF, 0x58FA, 0x58F9, 0x58FB, 0x58FC, 0x58FD, 0x5902, 0x590A, 0x5910, 0x591B, 0x68A6, 0x5925, 0x592C, 0x592D, 0x5932,
	0x5938, 0x593E, 0x7AD2, 0x5955, 0x5950, 0x594E, 0x595A, 0x5958, 0x5962, 0x5960, 0x5967, 0x596C, 0x5969, 0xFFFF, 0xFFFF, 0xFFFF,
	// 0X9B00 to 0X9BFF
	0x5978, 0x5981, 0x599D, 0x4F5E, 0x4FAB, 0x59A3, 0x59B2, 0x59C6, 0x59E8, 0x59DC, 0x598D, 0x59D9, 0x59DA, 0x5A25, 0x5A1F, 0x5A11,
	0x5A1C, 0x5A09, 0x5A1A, 0x5A40, 0x5A6C, 0x5A49, 0x5A35, 0x5A36, 0x5A62, 0x5A6A, 0x5A9A, 0x5ABC, 0x5ABE, 0x5ACB, 0x5AC2, 0x5ABD,
	0x5AE3, 0x5AD7, 0x5AE6, 0x5AE9, 0x5AD6, 0x5AFA, 0x5AFB, 0x5B0C, 0x5B0B, 0x5B16, 0x5B32, 0x5AD0, 0x5B2A, 0x5B36, 0x5B3E, 0x5B43,
	0x5B45, 0x5B40, 0x5B51, 0x5B55, 0x5B5A, 0x5B5B, 0x5B65, 0x5B69, 0x5B70, 0x5B73, 0x5B75, 0x5B78, 0x6588, 0x5B7A, 0x5B80, 0xFFFF,
	0x5B83, 0x5BA6, 0x5BB8, 0x5BC3, 0x5BC7, 0x5BC9, 0x5BD4, 0x5BD0, 0x5BE4, 0x5BE6, 0x5BE2, 0x5BDE, 0x5BE5, 0x5BEB, 0x5BF0, 0x5BF6,
	0x5BF3, 0x5C05, 0x5C07, 0x5C08, 0x5C0D, 0x5C13, 0x5C20, 0x5C22, 0x5C28, 0x5C38, 0x5C39, 0x5C41, 0x5C46, 0x5C4E, 0x5C53, 0x5C50,
	0x5C4F, 0x5B71, 0x5C6C, 0x5C6E, 0x4E62, 0x5C76, 0x5C79, 0x5C8C, 0x5C91, 0x5C94, 0x599B, 0x5CAB, 0x5CBB, 0x5CB6, 0x5CBC, 0x5CB7,
	0x5CC5, 0x5CBE, 0x5CC7, 0x5CD9, 0x5CE9, 0x5CFD, 0x5CFA, 0x5CED, 0x5D8C, 0x5CEA, 0x5D0B, 0x5D15, 0x5D17, 0x5D5C, 0x5D1F, 0x5D1B,
	0x5D11, 0x5D14, 0x5D22, 0x5D1A, 0x5D19, 0x5D18, 0x5D4C, 0x5D52, 0x5D4E, 0x5D4B, 0x5D6C, 0x5D73, 0x5D76, 0x5D87, 0x5D84, 0x5D82,
	0x5DA2, 0x5D9D, 0x5DAC, 0x5DAE, 0x5DBD, 0x5D90, 0x5DB7, 0x5DBC, 0x5DC9, 0x5DCD, 0x5DD3, 0x5DD2, 0x5DD6, 0x5DDB, 0x5DEB, 0x5DF2,
	0x5DF5, 0x5E0B, 0x5E1A, 0x5E19, 0x5E11, 0x5E1B, 0x5E36, 0x5E37, 0x5E44, 0x5E43, 0x5E40, 0x5E4E, 0x5E57, 0x5E54, 0x5E5F, 0x5E62,
	0x5E64, 0x5E47, 0x5E75, 0x5E76, 0x5E7A, 0x9EBC, 0x5E7F, 0x5EA0, 0x5EC1, 0x5EC2, 0x5EC8, 0x5ED0, 0x5ECF, 0xFFFF, 0xFFFF, 0xFFFF,
	// 0X9C00 to 0X9CFF
	0x5ED6, 0x5EE3, 0x5EDD, 0x5EDA, 0x5EDB, 0x5EE2, 0x5EE1, 0x5EE8, 0x5EE9, 0x5EEC, 0x5EF1, 0x5EF3, 0x5EF0, 0x5EF4, 0x5EF8, 0x5EFE,
	0x5F03, 0x5F09, 0x5F5D, 0x5F5C, 0x5F0B, 0x5F11, 0x5F16, 0x5F29, 0x5F2D, 0x5F38, 0x5F41, 0x5F48, 0x5F4C, 0x5F4E, 0x5F2F, 0x5F51,
	0x5F56, 0x5F57, 0x5F59, 0x5F61, 0x5F6D, 0x5F73, 0x5F77, 0x5F83, 0x5F82, 0x5F7F, 0x5F8A, 0x5F88, 0x5F91, 0x5F87, 0x5F9E, 0x5F99,
	0x5F98, 0x5FA0, 0x5FA8, 0x5FAD, 0x5FBC, 0x5FD6, 0x5FFB, 0x5FE4, 0x5FF8, 0x5FF1, 0x5FDD, 0x60B3, 0x5FFF, 0x6021, 0x6060, 0xFFFF,
	0x6019, 0x6010, 0x6029, 0x600E, 0x6031, 0x601B, 0x6015, 0x602B, 0x6026, 0x600F, 0x603A, 0x605A, 0x6041, 0x606A, 0x6077, 0x605F,
	0x604A, 0x6046, 0x604D, 0x6063, 0x6043, 0x6064, 0x6042, 0x606C, 0x606B, 0x6059, 0x6081, 0x608D, 0x60E7, 0x6083, 0x609A, 0x6084,
	0x609B, 0x6096, 0x6097, 0x6092, 0x60A7, 0x608B, 0x60E1, 0x60B8, 0x60E0, 0x60D3, 0x60B4, 0x5FF0, 0x60BD, 0x60C6, 0x60B5, 0x60D8,
	0x614D, 0x6115, 0x6106, 0x60F6, 0x60F7, 0x6100, 0x60F4, 0x60FA, 0x6103, 0x6121, 0x60FB, 0x60F1, 0x610D, 0x610E, 0x6147, 0x613E,
	0x6128, 0x6127, 0x614A, 0x613F, 0x613C, 0x612C, 0x6134, 0x613D, 0x6142, 0x6144, 0x6173, 0x6177, 0x6158, 0x6159, 0x615A, 0x616B,
	0x6174, 0x616F, 0x6165, 0x6171, 0x615F, 0x615D, 0x6153, 0x6175, 0x6199, 0x6196, 0x6187, 0x61AC, 0x6194, 0x619A, 0x618A, 0x6191,
	0x61AB, 0x61AE, 0x61CC, 0x61CA, 0x61C9, 0x61F7, 0x61C8, 0x61C3, 0x61C6, 0x61BA, 0x61CB, 0x7F79, 0x61CD, 0x61E6, 0x61E3, 0x61F6,
	0x61FA, 0x61F4, 0x61FF, 0x61FD, 0x61FC, 0x61FE, 0x6200, 0x6208, 0x6209, 0x620D, 0x620C, 0x6214, 0x621B, 0xFFFF, 0xFFFF, 0xFFFF,
	// 0X9D00 to 0X9DFF
	0x621E, 0x6221, 0x622A, 0x622E, 0x6230, 0x6232, 0x6233, 0x6241, 0x624E, 0x625E, 0x6263, 0x625B, 0x6260, 0x6268, 0x627C, 0x6282,
	0x6289, 0x627E, 0x6292, 0x6293, 0x6296, 0x62D4, 0x6283, 0x6294, 0x62D7, 0x62D1, 0x62BB, 0x62CF, 0x62FF, 0x62C6, 0x64D4, 0x62C8,
	0x62DC, 0x62CC, 0x62CA, 0x62C2, 0x62C7, 0x629B, 0x62C9, 0x630C, 0x62EE, 0x62F1, 0x6327, 0x6302, 0x6308, 0x62EF, 0x62F5, 0x6350,
	0x633E, 0x634D, 0x641C, 0x634F, 0x6396, 0x638E, 0x6380, 0x63AB, 0x6376, 0x63A3, 0x638F, 0x6389, 0x639F, 0x63B5, 0x636B, 0xFFFF,
	0x6369, 0x63BE, 0x63E9, 0x63C0, 0x63C6, 0x63E3, 0x63C9, 0x63D2, 0x63F6, 0x63C4, 0x6416, 0x6434, 0x6406, 0x6413, 0x6426, 0x6436,
	0x651D, 0x6417, 0x6428, 0x640F, 0x6467, 0x646F, 0x6476, 0x644E, 0x652A, 0x6495, 0x6493, 0x64A5, 0x64A9, 0x6488, 0x64BC, 0x64DA,
	0x64D2, 0x64C5, 0x64C7, 0x64BB, 0x64D8, 0x64C2, 0x64F1, 0x64E7, 0x8209, 0x64E0, 0x64E1, 0x62AC, 0x64E3, 0x64EF, 0x652C, 0x64F6,
	0x64F4, 0x64F2, 0x64FA, 0x6500, 0x64FD, 0x6518, 0x651C, 0x6505, 0x6524, 0x6523, 0x652B, 0x6534, 0x6535, 0x6537, 0x6536, 0x6538,
	0x754B, 0x6548, 0x6556, 0x6555, 0x654D, 0x6558, 0x655E, 0x655D, 0x6572, 0x6578, 0x6582, 0x6583, 0x8B8A, 0x659B, 0x659F, 0x65AB,
	0x65B7, 0x65C3, 0x65C6, 0x65C1, 0x65C4, 0x65CC, 0x65D2, 0x65DB, 0x65D9, 0x65E0, 0x65E1, 0x65F1, 0x6772, 0x660A, 0x6603, 0x65FB,
	0x6773, 0x6635, 0x6636, 0x6634, 0x661C, 0x664F, 0x6644, 0x6649, 0x6641, 0x665E, 0x665D, 0x6664, 0x6667, 0x6668, 0x665F, 0x6662,
	0x6670, 0x6683, 0x6688, 0x668E, 0x6689, 0x6684, 0x6698, 0x669D, 0x66C1, 0x66B9, 0x66C9, 0x66BE, 0x66BC, 0xFFFF, 0xFFFF, 0xFFFF,
	// 0X9E00 to 0X9EFF
	0x66C4, 0x66B8, 0x66D6, 0x66DA, 0x66E0, 0x663F, 0x66E6, 0x66E9, 0x66F0, 0x66F5, 0x66F7, 0x670F, 0x6716, 0x671E, 0x6726, 0x6727,
	0x9738, 0x672E, 0x673F, 0x6736, 0x6741, 0x6738, 0x6737, 0x6746, 0x675E, 0x6760, 0x6759, 0x6763, 0x6764, 0x6789, 0x6770, 0x67A9,
	0x677C, 0x676A, 0x678C, 0x678B, 0x67A6, 0x67A1, 0x6785, 0x67B7, 0x67EF, 0x67B4, 0x67EC, 0x67B3, 0x67E9, 0x67B8, 0x67E4, 0x67DE,
	0x67DD, 0x67E2, 0x67EE, 0x67B9, 0x67CE, 0x67C6, 0x67E7, 0x6A9C, 0x681E, 0x6846, 0x6829, 0x6840, 0x684D, 0x6832, 0x684E, 0xFFFF,
	0x68B3, 0x682B, 0x6859, 0x6863, 0x6877, 0x687F, 0x689F, 0x688F, 0x68AD, 0x6894, 0x689D, 0x689B, 0x6883, 0x6AAE, 0x68B9, 0x6874,
	0x68B5, 0x68A0, 0x68BA, 0x690F, 0x688D, 0x687E, 0x6901, 0x68CA, 0x6908, 0x68D8, 0x6922, 0x6926, 0x68E1, 0x690C, 0x68CD, 0x68D4,
	0x68E7, 0x68D5, 0x6936, 0x6912, 0x6904, 0x68D7, 0x68E3, 0x6925, 0x68F9, 0x68E0, 0x68EF, 0x6928, 0x692A, 0x691A, 0x6923, 0x6921,
	0x68C6, 0x6979, 0x6977, 0x695C, 0x6978, 0x696B, 0x6954, 0x697E, 0x696E, 0x6939, 0x6974, 0x693D, 0x6959, 0x6930, 0x6961, 0x695E,
	0x695D, 0x6981, 0x696A, 0x69B2, 0x69AE, 0x69D0, 0x69BF, 0x69C1, 0x69D3, 0x69BE, 0x69CE, 0x5BE8, 0x69CA, 0x69DD, 0x69BB, 0x69C3,
	0x69A7, 0x6A2E, 0x6991, 0x69A0, 0x699C, 0x6995, 0x69B4, 0x69DE, 0x69E8, 0x6A02, 0x6A1B, 0x69FF, 0x6B0A, 0x69F9, 0x69F2, 0x69E7,
	0x6A05, 0x69B1, 0x6A1E, 0x69ED, 0x6A14, 0x69EB, 0x6A0A, 0x6A12, 0x6AC1, 0x6A23, 0x6A13, 0x6A44, 0x6A0C, 0x6A72, 0x6A36, 0x6A78,
	0x6A47, 0x6A62, 0x6A59, 0x6A66, 0x6A48, 0x6A38, 0x6A22, 0x6A90, 0x6A8D, 0x6AA0, 0x6A84, 0x6AA2, 0x6AA3, 0xFFFF, 0xFFFF, 0xFFFF,
	// 0X9F00 to 0X9FFF
	0x6A97, 0x8617, 0x6ABB, 0x6AC3, 0x6AC2, 0x6AB8, 0x6AB3, 0x6AAC, 0x6ADE, 0x6AD1, 0x6ADF, 0x6AAA, 0x6ADA, 0x6AEA, 0x6AFB, 0x6B05,
	0x8616, 0x6AFA, 0x6B12, 0x6B16, 0x9B31, 0x6B1F, 0x6B38, 0x6B37, 0x76DC, 0x6B39, 0x98EE, 0x6B47, 0x6B43, 0x6B49, 0x6B50, 0x6B59,
	0x6B54, 0x6B5B, 0x6B5F, 0x6B61, 0x6B78, 0x6B79, 0x6B7F, 0x6B80, 0x6B84, 0x6B83, 0x6B8D, 0x6B98, 0x6B95, 0x6B9E, 0x6BA4, 0x6BAA,
	0x6BAB, 0x6BAF, 0x6BB2, 0x6BB1, 0x6BB3, 0x6BB7, 0x6BBC, 0x6BC6, 0x6BCB, 0x6BD3, 0x6BDF, 0x6BEC, 0x6BEB, 0x6BF3, 0x6BEF, 0xFFFF,
	0x9EBE, 0x6C08, 0x6C13, 0x6C14, 0x6C1B, 0x6C24, 0x6C23, 0x6C5E, 0x6C55, 0x6C62, 0x6C6A, 0x6C82, 0x6C8D, 0x6C9A, 0x6C81, 0x6C9B,
	0x6C7E, 0x6C68, 0x6C73, 0x6C92, 0x6C90, 0x6CC4, 0x6CF1, 0x6CD3, 0x6CBD, 0x6CD7, 0x6CC5, 0x6CDD, 0x6CAE, 0x6CB1, 0x6CBE, 0x6CBA,
	0x6CDB, 0x6CEF, 0x6CD9, 0x6CEA, 0x6D1F, 0x884D, 0x6D36, 0x6D2B, 0x6D3D, 0x6D38, 0x6D19, 0x6D35, 0x6D33, 0x6D12, 0x6D0C, 0x6D63,
	0x6D93, 0x6D64, 0x6D5A, 0x6D79, 0x6D59, 0x6D8E, 0x6D95, 0x6FE4, 0x6D85, 0x6DF9, 0x6E15, 0x6E0A, 0x6DB5, 0x6DC7, 0x6DE6, 0x6DB8,
	0x6DC6, 0x6DEC, 0x6DDE, 0x6DCC, 0x6DE8, 0x6DD2, 0x6DC5, 0x6DFA, 0x6DD9, 0x6DE4, 0x6DD5, 0x6DEA, 0x6DEE, 0x6E2D, 0x6E6E, 0x6E2E,
	0x6E19, 0x6E72, 0x6E5F, 0x6E3E, 0x6E23, 0x6E6B, 0x6E2B, 0x6E76, 0x6E4D, 0x6E1F, 0x6E43, 0x6E3A, 0x6E4E, 0x6E24, 0x6EFF, 0x6E1D,
	0x6E38, 0x6E82, 0x6EAA, 0x6E98, 0x6EC9, 0x6EB7, 0x6ED3, 0x6EBD, 0x6EAF, 0x6EC4, 0x6EB2, 0x6ED4, 0x6ED5, 0x6E8F, 0x6EA5, 0x6EC2,
	0x6E9F, 0x6F41, 0x6F11, 0x704C, 0x6EEC, 0x6EF8, 0x6EFE, 0x6F3F, 0x6EF2, 0x6F31, 0x6EEF, 0x6F32, 0x6ECC, 0xFFFF, 0xFFFF, 0xFFFF,
};

const char16_t SjisToUnicodeTable4[] =
{
	// 0XE000 to 0XE0FF
	0x6F3E, 0x6F13, 0x6EF7, 0x6F86, 0x6F7A, 0x6F78, 0x6F81, 0x6F80, 0x6F6F, 0x6F5B, 0x6FF3, 0x6F6D, 0x6F82, 0x6F7C, 0x6F58, 0x6F8E,
	0x6F91, 0x6FC2, 0x6F66, 0x6FB3, 0x6FA3, 0x6FA1, 0x6FA4, 0x6FB9, 0x6FC6, 0x6FAA, 0x6FDF, 0x6FD5, 0x6FEC, 0x6FD4, 0x6FD8, 0x6FF1,
	0x6FEE, 0x6FDB, 0x7009, 0x700B, 0x6FFA, 0x7011, 0x7001, 0x700F, 0x6FFE, 0x701B, 0x701A, 0x6F74, 0x701D, 0x7018, 0x701F, 0x7030,
	0x703E, 0x7032, 0x7051, 0x7063, 0x7099, 0x7092, 0x70AF, 0x70F1, 0x70AC, 0x70B8, 0x70B3, 0x70AE, 0x70DF, 0x70CB, 0x70DD, 0xFFFF,
	0x70D9, 0x7109, 0x70FD, 0x711C, 0x7119, 0x7165, 0x7155, 0x7188, 0x7166, 0x7162, 0x714C, 0x7156, 0x716C, 0x718F, 0x71FB, 0x7184,
	0x7195, 0x71A8, 0x71AC, 0x71D7, 0x71B9, 0x71BE, 0x71D2, 0x71C9, 0x71D4, 0x71CE, 0x71E0, 0x71EC, 0x71E7, 0x71F5, 0x71FC, 0x71F9,
	0x71FF, 0x720D, 0x7210, 0x721B, 0x7228, 0x722D, 0x722C, 0x7230, 0x7232, 0x723B, 0x723C, 0x723F, 0x7240, 0x7246, 0x724B, 0x7258,
	0x7274, 0x727E, 0x7282, 0x7281, 0x7287, 0x7292, 0x7296, 0x72A2, 0x72A7, 0x72B9, 0x72B2, 0x72C3, 0x72C6, 0x72C4, 0x72CE, 0x72D2,
	0x72E2, 0x72E0, 0x72E1, 0x72F9, 0x72F7, 0x500F, 0x7317, 0x730A, 0x731C, 0x7316, 0x731D, 0x7334, 0x732F, 0x7329, 0x7325, 0x733E,
	0x734E, 0x734F, 0x9ED8, 0x7357, 0x736A, 0x7368, 0x7370, 0x7378, 0x7375, 0x737B, 0x737A, 0x73C8, 0x73B3, 0x73CE, 0x73BB, 0x73C0,
	0x73E5, 0x73EE, 0x73DE, 0x74A2, 0x7405, 0x746F, 0x7425, 0x73F8, 0x7432, 0x743A, 0x7455, 0x743F, 0x745F, 0x7459, 0x7441, 0x745C,
	0x7469, 0x7470, 0x7463, 0x746A, 0x7476, 0x747E, 0x748B, 0x749E, 0x74A7, 0x74CA, 0x74CF, 0x74D4, 0x73F1, 0xFFFF, 0xFFFF, 0xFFFF,
	// 0XE100 to 0XE1FF
	0x74E0, 0x74E3, 0x74E7, 0x74E9, 0x74EE, 0x74F2, 0x74F0, 0x74F1, 0x74F8, 0x74F7, 0x7504, 0x7503, 0x7505, 0x750C, 0x750E, 0x750D,
	0x7515, 0x7513, 0x751E, 0x7526, 0x752C, 0x753C, 0x7544, 0x754D, 0x754A, 0x7549, 0x755B, 0x7546, 0x755A, 0x7569, 0x7564, 0x7567,
	0x756B, 0x756D, 0x7578, 0x7576, 0x7586, 0x7587, 0x7574, 0x758A, 0x7589, 0x7582, 0x7594, 0x759A, 0x759D, 0x75A5, 0x75A3, 0x75C2,
	0x75B3, 0x75C3, 0x75B5, 0x75BD, 0x75B8, 0x75BC, 0x75B1, 0x75CD, 0x75CA, 0x75D2, 0x75D9, 0x75E3, 0x75DE, 0x75FE, 0x75FF, 0xFFFF,
	0x75FC, 0x7601, 0x75F0, 0x75FA, 0x75F2, 0x75F3, 0x760B, 0x760D, 0x7609, 0x761F, 0x7627, 0x7620, 0x7621, 0x7622, 0x7624, 0x7634,
	0x7630, 0x763B, 0x7647, 0x7648, 0x7646, 0x765C, 0x7658, 0x7661, 0x7662, 0x7668, 0x7669, 0x766A, 0x7667, 0x766C, 0x7670, 0x7672,
	0x7676, 0x7678, 0x767C, 0x7680, 0x7683, 0x7688, 0x768B, 0x768E, 0x7696, 0x7693, 0x7699, 0x769A, 0x76B0, 0x76B4, 0x76B8, 0x76B9,
	0x76BA, 0x76C2, 0x76CD, 0x76D6, 0x76D2, 0x76DE, 0x76E1, 0x76E5, 0x76E7, 0x76EA, 0x862F, 0x76FB, 0x7708, 0x7707, 0x7704, 0x7729,
	0x7724, 0x771E, 0x7725, 0x7726, 0x771B, 0x7737, 0x7738, 0x7747, 0x775A, 0x7768, 0x776B, 0x775B, 0x7765, 0x777F, 0x777E, 0x7779,
	0x778E, 0x778B, 0x7791, 0x77A0, 0x779E, 0x77B0, 0x77B6, 0x77B9, 0x77BF, 0x77BC, 0x77BD, 0x77BB, 0x77C7, 0x77CD, 0x77D7, 0x77DA,
	0x77DC, 0x77E3, 0x77EE, 0x77FC, 0x780C, 0x7812, 0x7926, 0x7820, 0x792A, 0x7845, 0x788E, 0x7874, 0x7886, 0x787C, 0x789A, 0x788C,
	0x78A3, 0x78B5, 0x78AA, 0x78AF, 0x78D1, 0x78C6, 0x78CB, 0x78D4, 0x78BE, 0x78BC, 0x78C5, 0x78CA, 0x78EC, 0xFFFF, 0xFFFF, 0xFFFF,
	// 0XE200 to 0XE2FF
	0x78E7, 0x78DA, 0x78FD, 0x78F4, 0x7907, 0x7912, 0x7911, 0x7919, 0x792C, 0x792B, 0x7940, 0x7960, 0x7957, 0x795F, 0x795A, 0x7955,
	0x7953, 0x797A, 0x797F, 0x798A, 0x799D, 0x79A7, 0x9F4B, 0x79AA, 0x79AE, 0x79B3, 0x79B9, 0x79BA, 0x79C9, 0x79D5, 0x79E7, 0x79EC,
	0x79E1, 0x79E3, 0x7A08, 0x7A0D, 0x7A18, 0x7A19, 0x7A20, 0x7A1F, 0x7980, 0x7A31, 0x7A3B, 0x7A3E, 0x7A37, 0x7A43, 0x7A57, 0x7A49,
	0x7A61, 0x7A62, 0x7A69, 0x9F9D, 0x7A70, 0x7A79, 0x7A7D, 0x7A88, 0x7A97, 0x7A95, 0x7A98, 0x7A96, 0x7AA9, 0x7AC8, 0x7AB0, 0xFFFF,
	0x7AB6, 0x7AC5, 0x7AC4, 0x7ABF, 0x9083, 0x7AC7, 0x7ACA, 0x7ACD, 0x7ACF, 0x7AD5, 0x7AD3, 0x7AD9, 0x7ADA, 0x7ADD, 0x7AE1, 0x7AE2,
	0x7AE6, 0x7AED, 0x7AF0, 0x7B02, 0x7B0F, 0x7B0A, 0x7B06, 0x7B33, 0x7B18, 0x7B19, 0x7B1E, 0x7B35, 0x7B28, 0x7B36, 0x7B50, 0x7B7A,
	0x7B04, 0x7B4D, 0x7B0B, 0x7B4C, 0x7B45, 0x7B75, 0x7B65, 0x7B74, 0x7B67, 0x7B70, 0x7B71, 0x7B6C, 0x7B6E, 0x7B9D, 0x7B98, 0x7B9F,
	0x7B8D, 0x7B9C, 0x7B9A, 0x7B8B, 0x7B92, 0x7B8F, 0x7B5D, 0x7B99, 0x7BCB, 0x7BC1, 0x7BCC, 0x7BCF, 0x7BB4, 0x7BC6, 0x7BDD, 0x7BE9,
	0x7C11, 0x7C14, 0x7BE6, 0x7BE5, 0x7C60, 0x7C00, 0x7C07, 0x7C13, 0x7BF3, 0x7BF7, 0x7C17, 0x7C0D, 0x7BF6, 0x7C23, 0x7C27, 0x7C2A,
	0x7C1F, 0x7C37, 0x7C2B, 0x7C3D, 0x7C4C, 0x7C43, 0x7C54, 0x7C4F, 0x7C40, 0x7C50, 0x7C58, 0x7C5F, 0x7C64, 0x7C56, 0x7C65, 0x7C6C,
	0x7C75, 0x7C83, 0x7C90, 0x7CA4, 0x7CAD, 0x7CA2, 0x7CAB, 0x7CA1, 0x7CA8, 0x7CB3, 0x7CB2, 0x7CB1, 0x7CAE, 0x7CB9, 0x7CBD, 0x7CC0,
	0x7CC5, 0x7CC2, 0x7CD8, 0x7CD2, 0x7CDC, 0x7CE2, 0x9B3B, 0x7CEF, 0x7CF2, 0x7CF4, 0x7CF6, 0x7CFA, 0x7D06, 0xFFFF, 0xFFFF, 0xFFFF,
	// 0XE300 to 0XE3FF
	0x7D02, 0x7D1C, 0x7D15, 0x7D0A, 0x7D45, 0x7D4B, 0x7D2E, 0x7D32, 0x7D3F, 0x7D35, 0x7D46, 0x7D73, 0x7D56, 0x7D4E, 0x7D72, 0x7D68,
	0x7D6E, 0x7D4F, 0x7D63, 0x7D93, 0x7D89, 0x7D5B, 0x7D8F, 0x7D7D, 0x7D9B, 0x7DBA, 0x7DAE, 0x7DA3, 0x7DB5, 0x7DC7, 0x7DBD, 0x7DAB,
	0x7E3D, 0x7DA2, 0x7DAF, 0x7DDC, 0x7DB8, 0x7D9F, 0x7DB0, 0x7DD8, 0x7DDD, 0x7DE4, 0x7DDE, 0x7DFB, 0x7DF2, 0x7DE1, 0x7E05, 0x7E0A,
	0x7E23, 0x7E21, 0x7E12, 0x7E31, 0x7E1F, 0x7E09, 0x7E0B, 0x7E22, 0x7E46, 0x7E66, 0x7E3B, 0x7E35, 0x7E39, 0x7E43, 0x7E37, 0xFFFF,
	0x7E32, 0x7E3A, 0x7E67, 0x7E5D, 0x7E56, 0x7E5E, 0x7E59, 0x7E5A, 0x7E79, 0x7E6A, 0x7E69, 0x7E7C, 0x7E7B, 0x7E83, 0x7DD5, 0x7E7D,
	0x8FAE, 0x7E7F, 0x7E88, 0x7E89, 0x7E8C, 0x7E92, 0x7E90, 0x7E93, 0x7E94, 0x7E96, 0x7E8E, 0x7E9B, 0x7E9C, 0x7F38, 0x7F3A, 0x7F45,
	0x7F4C, 0x7F4D, 0x7F4E, 0x7F50, 0x7F51, 0x7F55, 0x7F54, 0x7F58, 0x7F5F, 0x7F60, 0x7F68, 0x7F69, 0x7F67, 0x7F78, 0x7F82, 0x7F86,
	0x7F83, 0x7F88, 0x7F87, 0x7F8C, 0x7F94, 0x7F9E, 0x7F9D, 0x7F9A, 0x7FA3, 0x7FAF, 0x7FB2, 0x7FB9, 0x7FAE, 0x7FB6, 0x7FB8, 0x8B71,
	0x7FC5, 0x7FC6, 0x7FCA, 0x7FD5, 0x7FD4, 0x7FE1, 0x7FE6, 0x7FE9, 0x7FF3, 0x7FF9, 0x98DC, 0x8006, 0x8004, 0x800B, 0x8012, 0x8018,
	0x8019, 0x801C, 0x8021, 0x8028, 0x803F, 0x803B, 0x804A, 0x8046, 0x8052, 0x8058, 0x805A, 0x805F, 0x8062, 0x8068, 0x8073, 0x8072,
	0x8070, 0x8076, 0x8079, 0x807D, 0x807F, 0x8084, 0x8086, 0x8085, 0x809B, 0x8093, 0x809A, 0x80AD, 0x5190, 0x80AC, 0x80DB, 0x80E5,
	0x80D9, 0x80DD, 0x80C4, 0x80DA, 0x80D6, 0x8109, 0x80EF, 0x80F1, 0x811B, 0x8129, 0x8123, 0x812F, 0x814B, 0xFFFF, 0xFFFF, 0xFFFF,
	// 0XE400 to 0XE4FF
	0x968B, 0x8146, 0x813E, 0x8153, 0x8151, 0x80FC, 0x8171, 0x816E, 0x8165, 0x8166, 0x8174, 0x8183, 0x8188, 0x818A, 0x8180, 0x8182,
	0x81A0, 0x8195, 0x81A4, 0x81A3, 0x815F, 0x8193, 0x81A9, 0x81B0, 0x81B5, 0x81BE, 0x81B8, 0x81BD, 0x81C0, 0x81C2, 0x81BA, 0x81C9,
	0x81CD, 0x81D1, 0x81D9, 0x81D8, 0x81C8, 0x81DA, 0x81DF, 0x81E0, 0x81E7, 0x81FA, 0x81FB, 0x81FE, 0x8201, 0x8202, 0x8205, 0x8207,
	0x820A, 0x820D, 0x8210, 0x8216, 0x8229, 0x822B, 0x8238, 0x8233, 0x8240, 0x8259, 0x8258, 0x825D, 0x825A, 0x825F, 0x8264, 0xFFFF,
	0x8262, 0x8268, 0x826A, 0x826B, 0x822E, 0x8271, 0x8277, 0x8278, 0x827E, 0x828D, 0x8292, 0x82AB, 0x829F, 0x82BB, 0x82AC, 0x82E1,
	0x82E3, 0x82DF, 0x82D2, 0x82F4, 0x82F3, 0x82FA, 0x8393, 0x8303, 0x82FB, 0x82F9, 0x82DE, 0x8306, 0x82DC, 0x8309, 0x82D9, 0x8335,
	0x8334, 0x8316, 0x8332, 0x8331, 0x8340, 0x8339, 0x8350, 0x8345, 0x832F, 0x832B, 0x8317, 0x8318, 0x8385, 0x839A, 0x83AA, 0x839F,
	0x83A2, 0x8396, 0x8323, 0x838E, 0x8387, 0x838A, 0x837C, 0x83B5, 0x8373, 0x8375, 0x83A0, 0x8389, 0x83A8, 0x83F4, 0x8413, 0x83EB,
	0x83CE, 0x83FD, 0x8403, 0x83D8, 0x840B, 0x83C1, 0x83F7, 0x8407, 0x83E0, 0x83F2, 0x840D, 0x8422, 0x8420, 0x83BD, 0x8438, 0x8506,
	0x83FB, 0x846D, 0x842A, 0x843C, 0x855A, 0x8484, 0x8477, 0x846B, 0x84AD, 0x846E, 0x8482, 0x8469, 0x8446, 0x842C, 0x846F, 0x8479,
	0x8435, 0x84CA, 0x8462, 0x84B9, 0x84BF, 0x849F, 0x84D9, 0x84CD, 0x84BB, 0x84DA, 0x84D0, 0x84C1, 0x84C6, 0x84D6, 0x84A1, 0x8521,
	0x84FF, 0x84F4, 0x8517, 0x8518, 0x852C, 0x851F, 0x8515, 0x8514, 0x84FC, 0x8540, 0x8563, 0x8558, 0x8548, 0xFFFF, 0xFFFF, 0xFFFF,
	// 0XE500 to 0XE5FF
	0x8541, 0x8602, 0x854B, 0x8555, 0x8580, 0x85A4, 0x8588, 0x8591, 0x858A, 0x85A8, 0x856D, 0x8594, 0x859B, 0x85EA, 0x8587, 0x859C,
	0x8577, 0x857E, 0x8590, 0x85C9, 0x85BA, 0x85CF, 0x85B9, 0x85D0, 0x85D5, 0x85DD, 0x85E5, 0x85DC, 0x85F9, 0x860A, 0x8613, 0x860B,
	0x85FE, 0x85FA, 0x8606, 0x8622, 0x861A, 0x8630, 0x863F, 0x864D, 0x4E55, 0x8654, 0x865F, 0x8667, 0x8671, 0x8693, 0x86A3, 0x86A9,
	0x86AA, 0x868B, 0x868C, 0x86B6, 0x86AF, 0x86C4, 0x86C6, 0x86B0, 0x86C9, 0x8823, 0x86AB, 0x86D4, 0x86DE, 0x86E9, 0x86EC, 0xFFFF,
	0x86DF, 0x86DB, 0x86EF, 0x8712, 0x8706, 0x8708, 0x8700, 0x8703, 0x86FB, 0x8711, 0x8709, 0x870D, 0x86F9, 0x870A, 0x8734, 0x873F,
	0x8737, 0x873B, 0x8725, 0x8729, 0x871A, 0x8760, 0x875F, 0x8778, 0x874C, 0x874E, 0x8774, 0x8757, 0x8768, 0x876E, 0x8759, 0x8753,
	0x8763, 0x876A, 0x8805, 0x87A2, 0x879F, 0x8782, 0x87AF, 0x87CB, 0x87BD, 0x87C0, 0x87D0, 0x96D6, 0x87AB, 0x87C4, 0x87B3, 0x87C7,
	0x87C6, 0x87BB, 0x87EF, 0x87F2, 0x87E0, 0x880F, 0x880D, 0x87FE, 0x87F6, 0x87F7, 0x880E, 0x87D2, 0x8811, 0x8816, 0x8815, 0x8822,
	0x8821, 0x8831, 0x8836, 0x8839, 0x8827, 0x883B, 0x8844, 0x8842, 0x8852, 0x8859, 0x885E, 0x8862, 0x886B, 0x8881, 0x887E, 0x889E,
	0x8875, 0x887D, 0x88B5, 0x8872, 0x8882, 0x8897, 0x8892, 0x88AE, 0x8899, 0x88A2, 0x888D, 0x88A4, 0x88B0, 0x88BF, 0x88B1, 0x88C3,
	0x88C4, 0x88D4, 0x88D8, 0x88D9, 0x88DD, 0x88F9, 0x8902, 0x88FC, 0x88F4, 0x88E8, 0x88F2, 0x8904, 0x890C, 0x890A, 0x8913, 0x8943,
	0x891E, 0x8925, 0x892A, 0x892B, 0x8941, 0x8944, 0x893B, 0x8936, 0x8938, 0x894C, 0x891D, 0x8960, 0x895E, 0xFFFF, 0xFFFF, 0xFFFF,
	// 0XE600 to 0XE6FF
	0x8966, 0x8964, 0x896D, 0x896A, 0x896F, 0x8974, 0x8977, 0x897E, 0x8983, 0x8988, 0x898A, 0x8993, 0x8998, 0x89A1, 0x89A9, 0x89A6,
	0x89AC, 0x89AF, 0x89B2, 0x89BA, 0x89BD, 0x89BF, 0x89C0, 0x89DA, 0x89DC, 0x89DD, 0x89E7, 0x89F4, 0x89F8, 0x8A03, 0x8A16, 0x8A10,
	0x8A0C, 0x8A1B, 0x8A1D, 0x8A25, 0x8A36, 0x8A41, 0x8A5B, 0x8A52, 0x8A46, 0x8A48, 0x8A7C, 0x8A6D, 0x8A6C, 0x8A62, 0x8A85, 0x8A82,
	0x8A84, 0x8AA8, 0x8AA1, 0x8A91, 0x8AA5, 0x8AA6, 0x8A9A, 0x8AA3, 0x8AC4, 0x8ACD, 0x8AC2, 0x8ADA, 0x8AEB, 0x8AF3, 0x8AE7, 0xFFFF,
	0x8AE4, 0x8AF1, 0x8B14, 0x8AE0, 0x8AE2, 0x8AF7, 0x8ADE, 0x8ADB, 0x8B0C, 0x8B07, 0x8B1A, 0x8AE1, 0x8B16, 0x8B10, 0x8B17, 0x8B20,
	0x8B33, 0x97AB, 0x8B26, 0x8B2B, 0x8B3E, 0x8B28, 0x8B41, 0x8B4C, 0x8B4F, 0x8B4E, 0x8B49, 0x8B56, 0x8B5B, 0x8B5A, 0x8B6B, 0x8B5F,
	0x8B6C, 0x8B6F, 0x8B74, 0x8B7D, 0x8B80, 0x8B8C, 0x8B8E, 0x8B92, 0x8B93, 0x8B96, 0x8B99, 0x8B9A, 0x8C3A, 0x8C41, 0x8C3F, 0x8C48,
	0x8C4C, 0x8C4E, 0x8C50, 0x8C55, 0x8C62, 0x8C6C, 0x8C78, 0x8C7A, 0x8C82, 0x8C89, 0x8C85, 0x8C8A, 0x8C8D, 0x8C8E, 0x8C94, 0x8C7C,
	0x8C98, 0x621D, 0x8CAD, 0x8CAA, 0x8CBD, 0x8CB2, 0x8CB3, 0x8CAE, 0x8CB6, 0x8CC8, 0x8CC1, 0x8CE4, 0x8CE3, 0x8CDA, 0x8CFD, 0x8CFA,
	0x8CFB, 0x8D04, 0x8D05, 0x8D0A, 0x8D07, 0x8D0F, 0x8D0D, 0x8D10, 0x9F4E, 0x8D13, 0x8CCD, 0x8D14, 0x8D16, 0x8D67, 0x8D6D, 0x8D71,
	0x8D73, 0x8D81, 0x8D99, 0x8DC2, 0x8DBE, 0x8DBA, 0x8DCF, 0x8DDA, 0x8DD6, 0x8DCC, 0x8DDB, 0x8DCB, 0x8DEA, 0x8DEB, 0x8DDF, 0x8DE3,
	0x8DFC, 0x8E08, 0x8E09, 0x8DFF, 0x8E1D, 0x8E1E, 0x8E10, 0x8E1F, 0x8E42, 0x8E35, 0x8E30, 0x8E34, 0x8E4A, 0xFFFF, 0xFFFF, 0xFFFF,
	// 0XE700 to 0XE7FF
	0x8E47, 0x8E49, 0x8E4C, 0x8E50, 0x8E48, 0x8E59, 0x8E64, 0x8E60, 0x8E2A, 0x8E63, 0x8E55, 0x8E76, 0x8E72, 0x8E7C, 0x8E81, 0x8E87,
	0x8E85, 0x8E84, 0x8E8B, 0x8E8A, 0x8E93, 0x8E91, 0x8E94, 0x8E99, 0x8EAA, 0x8EA1, 0x8EAC, 0x8EB0, 0x8EC6, 0x8EB1, 0x8EBE, 0x8EC5,
	0x8EC8, 0x8ECB, 0x8EDB, 0x8EE3, 0x8EFC, 0x8EFB, 0x8EEB, 0x8EFE, 0x8F0A, 0x8F05, 0x8F15, 0x8F12, 0x8F19, 0x8F13, 0x8F1C, 0x8F1F,
	0x8F1B, 0x8F0C, 0x8F26, 0x8F33, 0x8F3B, 0x8F39, 0x8F45, 0x8F42, 0x8F3E, 0x8F4C, 0x8F49, 0x8F46, 0x8F4E, 0x8F57, 0x8F5C, 0xFFFF,
	0x8F62, 0x8F63, 0x8F64, 0x8F9C, 0x8F9F, 0x8FA3, 0x8FAD, 0x8FAF, 0x8FB7, 0x8FDA, 0x8FE5, 0x8FE2, 0x8FEA, 0x8FEF, 0x9087, 0x8FF4,
	0x9005, 0x8FF9, 0x8FFA, 0x9011, 0x9015, 0x9021, 0x900D, 0x901E, 0x9016, 0x900B, 0x9027, 0x9036, 0x9035, 0x9039, 0x8FF8, 0x904F,
	0x9050, 0x9051, 0x9052, 0x900E, 0x9049, 0x903E, 0x9056, 0x9058, 0x905E, 0x9068, 0x906F, 0x9076, 0x96A8, 0x9072, 0x9082, 0x907D,
	0x9081, 0x9080, 0x908A, 0x9089, 0x908F, 0x90A8, 0x90AF, 0x90B1, 0x90B5, 0x90E2, 0x90E4, 0x6248, 0x90DB, 0x9102, 0x9112, 0x9119,
	0x9132, 0x9130, 0x914A, 0x9156, 0x9158, 0x9163, 0x9165, 0x9169, 0x9173, 0x9172, 0x918B, 0x9189, 0x9182, 0x91A2, 0x91AB, 0x91AF,
	0x91AA, 0x91B5, 0x91B4, 0x91BA, 0x91C0, 0x91C1, 0x91C9, 0x91CB, 0x91D0, 0x91D6, 0x91DF, 0x91E1, 0x91DB, 0x91FC, 0x91F5, 0x91F6,
	0x921E, 0x91FF, 0x9214, 0x922C, 0x9215, 0x9211, 0x925E, 0x9257, 0x9245, 0x9249, 0x9264, 0x9248, 0x9295, 0x923F, 0x924B, 0x9250,
	0x929C, 0x9296, 0x9293, 0x929B, 0x925A, 0x92CF, 0x92B9, 0x92B7, 0x92E9, 0x930F, 0x92FA, 0x9344, 0x932E, 0xFFFF, 0xFFFF, 0xFFFF,
	// 0XE800 to 0XE8FF
	0x9319, 0x9322, 0x931A, 0x9323, 0x933A, 0x9335, 0x933B, 0x935C, 0x9360, 0x937C, 0x936E, 0x9356, 0x93B0, 0x93AC, 0x93AD, 0x9394,
	0x93B9, 0x93D6, 0x93D7, 0x93E8, 0x93E5, 0x93D8, 0x93C3, 0x93DD, 0x93D0, 0x93C8, 0x93E4, 0x941A, 0x9414, 0x9413, 0x9403, 0x9407,
	0x9410, 0x9436, 0x942B, 0x9435, 0x9421, 0x943A, 0x9441, 0x9452, 0x9444, 0x945B, 0x9460, 0x9462, 0x945E, 0x946A, 0x9229, 0x9470,
	0x9475, 0x9477, 0x947D, 0x945A, 0x947C, 0x947E, 0x9481, 0x947F, 0x9582, 0x9587, 0x958A, 0x9594, 0x9596, 0x9598, 0x9599, 0xFFFF,
	0x95A0, 0x95A8, 0x95A7, 0x95AD, 0x95BC, 0x95BB, 0x95B9, 0x95BE, 0x95CA, 0x6FF6, 0x95C3, 0x95CD, 0x95CC, 0x95D5, 0x95D4, 0x95D6,
	0x95DC, 0x95E1, 0x95E5, 0x95E2, 0x9621, 0x9628, 0x962E, 0x962F, 0x9642, 0x964C, 0x964F, 0x964B, 0x9677, 0x965C, 0x965E, 0x965D,
	0x965F, 0x9666, 0x9672, 0x966C, 0x968D, 0x9698, 0x9695, 0x9697, 0x96AA, 0x96A7, 0x96B1, 0x96B2, 0x96B0, 0x96B4, 0x96B6, 0x96B8,
	0x96B9, 0x96CE, 0x96CB, 0x96C9, 0x96CD, 0x894D, 0x96DC, 0x970D, 0x96D5, 0x96F9, 0x9704, 0x9706, 0x9708, 0x9713, 0x970E, 0x9711,
	0x970F, 0x9716, 0x9719, 0x9724, 0x972A, 0x9730, 0x9739, 0x973D, 0x973E, 0x9744, 0x9746, 0x9748, 0x9742, 0x9749, 0x975C, 0x9760,
	0x9764, 0x9766, 0x9768, 0x52D2, 0x976B, 0x9771, 0x9779, 0x9785, 0x977C, 0x9781, 0x977A, 0x9786, 0x978B, 0x978F, 0x9790, 0x979C,
	0x97A8, 0x97A6, 0x97A3, 0x97B3, 0x97B4, 0x97C3, 0x97C6, 0x97C8, 0x97CB, 0x97DC, 0x97ED, 0x9F4F, 0x97F2, 0x7ADF, 0x97F6, 0x97F5,
	0x980F, 0x980C, 0x9838, 0x9824, 0x9821, 0x9837, 0x983D, 0x9846, 0x984F, 0x984B, 0x986B, 0x986F, 0x9870, 0xFFFF, 0xFFFF, 0xFFFF,
	// 0XE900 to 0XE9FF
	0x9871, 0x9874, 0x9873, 0x98AA, 0x98AF, 0x98B1, 0x98B6, 0x98C4, 0x98C3, 0x98C6, 0x98E9, 0x98EB, 0x9903, 0x9909, 0x9912, 0x9914,
	0x9918, 0x9921, 0x991D, 0x991E, 0x9924, 0x9920, 0x992C, 0x992E, 0x993D, 0x993E, 0x9942, 0x9949, 0x9945, 0x9950, 0x994B, 0x9951,
	0x9952, 0x994C, 0x9955, 0x9997, 0x9998, 0x99A5, 0x99AD, 0x99AE, 0x99BC, 0x99DF, 0x99DB, 0x99DD, 0x99D8, 0x99D1, 0x99ED, 0x99EE,
	0x99F1, 0x99F2, 0x99FB, 0x99F8, 0x9A01, 0x9A0F, 0x9A05, 0x99E2, 0x9A19, 0x9A2B, 0x9A37, 0x9A45, 0x9A42, 0x9A40, 0x9A43, 0xFFFF,
	0x9A3E, 0x9A55, 0x9A4D, 0x9A5B, 0x9A57, 0x9A5F, 0x9A62, 0x9A65, 0x9A64, 0x9A69, 0x9A6B, 0x9A6A, 0x9AAD, 0x9AB0, 0x9ABC, 0x9AC0,
	0x9ACF, 0x9AD1, 0x9AD3, 0x9AD4, 0x9ADE, 0x9ADF, 0x9AE2, 0x9AE3, 0x9AE6, 0x9AEF, 0x9AEB, 0x9AEE, 0x9AF4, 0x9AF1, 0x9AF7, 0x9AFB,
	0x9B06, 0x9B18, 0x9B1A, 0x9B1F, 0x9B22, 0x9B23, 0x9B25, 0x9B27, 0x9B28, 0x9B29, 0x9B2A, 0x9B2E, 0x9B2F, 0x9B32, 0x9B44, 0x9B43,
	0x9B4F, 0x9B4D, 0x9B4E, 0x9B51, 0x9B58, 0x9B74, 0x9B93, 0x9B83, 0x9B91, 0x9B96, 0x9B97, 0x9B9F, 0x9BA0, 0x9BA8, 0x9BB4, 0x9BC0,
	0x9BCA, 0x9BB9, 0x9BC6, 0x9BCF, 0x9BD1, 0x9BD2, 0x9BE3, 0x9BE2, 0x9BE4, 0x9BD4, 0x9BE1, 0x9C3A, 0x9BF2, 0x9BF1, 0x9BF0, 0x9C15,
	0x9C14, 0x9C09, 0x9C13, 0x9C0C, 0x9C06, 0x9C08, 0x9C12, 0x9C0A, 0x9C04, 0x9C2E, 0x9C1B, 0x9C25, 0x9C24, 0x9C21, 0x9C30, 0x9C47,
	0x9C32, 0x9C46, 0x9C3E, 0x9C5A, 0x9C60, 0x9C67, 0x9C76, 0x9C78, 0x9CE7, 0x9CEC, 0x9CF0, 0x9D09, 0x9D08, 0x9CEB, 0x9D03, 0x9D06,
	0x9D2A, 0x9D26, 0x9DAF, 0x9D23, 0x9D1F, 0x9D44, 0x9D15, 0x9D12, 0x9D41, 0x9D3F, 0x9D3E, 0x9D46, 0x9D48, 0xFFFF, 0xFFFF, 0xFFFF,
	// 0XEA00 to 0XEAFF
	0x9D5D, 0x9D5E, 0x9D64, 0x9D51, 0x9D50, 0x9D59, 0x9D72, 0x9D89, 0x9D87, 0x9DAB, 0x9D6F, 0x9D7A, 0x9D9A, 0x9DA4, 0x9DA9, 0x9DB2,
	0x9DC4, 0x9DC1, 0x9DBB, 0x9DB8, 0x9DBA, 0x9DC6, 0x9DCF, 0x9DC2, 0x9DD9, 0x9DD3, 0x9DF8, 0x9DE6, 0x9DED, 0x9DEF, 0x9DFD, 0x9E1A,
	0x9E1B, 0x9E1E, 0x9E75, 0x9E79, 0x9E7D, 0x9E81, 0x9E88, 0x9E8B, 0x9E8C, 0x9E92, 0x9E95, 0x9E91, 0x9E9D, 0x9EA5, 0x9EA9, 0x9EB8,
	0x9EAA, 0x9EAD, 0x9761, 0x9ECC, 0x9ECE, 0x9ECF, 0x9ED0, 0x9ED4, 0x9EDC, 0x9EDE, 0x9EDD, 0x9EE0, 0x9EE5, 0x9EE8, 0x9EEF, 0xFFFF,
	0x9EF4, 0x9EF6, 0x9EF7, 0x9EF9, 0x9EFB, 0x9EFC, 0x9EFD, 0x9F07, 0x9F08, 0x76B7, 0x9F15, 0x9F21, 0x9F2C, 0x9F3E, 0x9F4A, 0x9F52,
	0x9F54, 0x9F63, 0x9F5F, 0x9F60, 0x9F61, 0x9F66, 0x9F67, 0x9F6C, 0x9F6A, 0x9F77, 0x9F72, 0x9F76, 0x9F95, 0x9F9C, 0x9FA0, 0x582F,
	0x69C7, 0x9059, 0x7464, 0x51DC, 0x7199, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
	0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
	0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
	0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
	0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
	0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
};

const char16_t SjisToUnicodeTable5[] =
{
	// 0XED00 to 0XEDFF
	0x7E8A, 0x891C, 0x9348, 0x9288, 0x84DC, 0x4FC9, 0x70BB, 0x6631, 0x68C8, 0x92F9, 0x66FB, 0x5F45, 0x4E28, 0x4EE1, 0x4EFC, 0x4F00,
	0x4F03, 0x4F39, 0x4F56, 0x4F92, 0x4F8A, 0x4F9A, 0x4F94, 0x4FCD, 0x5040, 0x5022, 0x4FFF, 0x501E, 0x5046, 0x5070, 0x5042, 0x5094,
	0x50F4, 0x50D8, 0x514A, 0x5164, 0x519D, 0x51BE, 0x51EC, 0x5215, 0x529C, 0x52A6, 0x52C0, 0x52DB, 0x5300, 0x5307, 0x5324, 0x5372,
	0x5393, 0x53B2, 0x53DD, 0xFA0E, 0x549C, 0x548A, 0x54A9, 0x54FF, 0x5586, 0x5759, 0x5765, 0x57AC, 0x57C8, 0x57C7, 0xFA0F, 0xFFFF,
	0xFA10, 0x589E, 0x58B2, 0x590B, 0x5953, 0x595B, 0x595D, 0x5963, 0x59A4, 0x59BA, 0x5B56, 0x5BC0, 0x752F, 0x5BD8, 0x5BEC, 0x5C1E,
	0x5CA6, 0x5CBA, 0x5CF5, 0x5D27, 0x5D53, 0xFA11, 0x5D42, 0x5D6D, 0x5DB8, 0x5DB9, 0x5DD0, 0x5F21, 0x5F34, 0x5F67, 0x5FB7, 0x5FDE,
	0x605D, 0x6085, 0x608A, 0x60DE, 0x60D5, 0x6120, 0x60F2, 0x6111, 0x6137, 0x6130, 0x6198, 0x6213, 0x62A6, 0x63F5, 0x6460, 0x649D,
	0x64CE, 0x654E, 0x6600, 0x6615, 0x663B, 0x6609, 0x662E, 0x661E, 0x6624, 0x6665, 0x6657, 0x6659, 0xFA12, 0x6673, 0x6699, 0x66A0,
	0x66B2, 0x66BF, 0x66FA, 0x670E, 0xF929, 0x6766, 0x67BB, 0x6852, 0x67C0, 0x6801, 0x6844, 0x68CF, 0xFA13, 0x6968, 0xFA14, 0x6998,
	0x69E2, 0x6A30, 0x6A6B, 0x6A46, 0x6A73, 0x6A7E, 0x6AE2, 0x6AE4, 0x6BD6, 0x6C3F, 0x6C5C, 0x6C86, 0x6C6F, 0x6CDA, 0x6D04, 0x6D87,
	0x6D6F, 0x6D96, 0x6DAC, 0x6DCF, 0x6DF8, 0x6DF2, 0x6DFC, 0x6E39, 0x6E5C, 0x6E27, 0x6E3C, 0x6EBF, 0x6F88, 0x6FB5, 0x6FF5, 0x7005,
	0x7007, 0x7028, 0x7085, 0x70AB, 0x710F, 0x7104, 0x715C, 0x7146, 0x7147, 0xFA15, 0x71C1, 0x71FE, 0x72B1, 0xFFFF, 0xFFFF, 0xFFFF,
	// 0XEE00 to 0XEEFF
	0x72BE, 0x7324, 0xFA16, 0x7377, 0x73BD, 0x73C9, 0x73D6, 0x73E3, 0x73D2, 0x7407, 0x73F5, 0x7426, 0x742A, 0x7429, 0x742E, 0x7462,
	0x7489, 0x749F, 0x7501, 0x756F, 0x7682, 0x769C, 0x769E, 0x769B, 0x76A6, 0xFA17, 0x7746, 0x52AF, 0x7821, 0x784E, 0x7864, 0x787A,
	0x7930, 0xFA18, 0xFA19, 0xFA1A, 0x7994, 0xFA1B, 0x799B, 0x7AD1, 0x7AE7, 0xFA1C, 0x7AEB, 0x7B9E, 0xFA1D, 0x7D48, 0x7D5C, 0x7DB7,
	0x7DA0, 0x7DD6, 0x7E52, 0x7F47, 0x7FA1, 0xFA1E, 0x8301, 0x8362, 0x837F, 0x83C7, 0x83F6, 0x8448, 0x84B4, 0x8553, 0x8559, 0xFFFF,
	0x856B, 0xFA1F, 0x85B0, 0xFA20, 0xFA21, 0x8807, 0x88F5, 0x8A12, 0x8A37, 0x8A79, 0x8AA7, 0x8ABE, 0x8ADF, 0xFA22, 0x8AF6, 0x8B53,
	0x8B7F, 0x8CF0, 0x8CF4, 0x8D12, 0x8D76, 0xFA23, 0x8ECF, 0xFA24, 0xFA25, 0x9067, 0x90DE, 0xFA26, 0x9115, 0x9127, 0x91DA, 0x91D7,
	0x91DE, 0x91ED, 0x91EE, 0x91E4, 0x91E5, 0x9206, 0x9210, 0x920A, 0x923A, 0x9240, 0x923C, 0x924E, 0x9259, 0x9251, 0x9239, 0x9267,
	0x92A7, 0x9277, 0x9278, 0x92E7, 0x92D7, 0x92D9, 0x92D0, 0xFA27, 0x92D5, 0x92E0, 0x92D3, 0x9325, 0x9321, 0x92FB, 0xFA28, 0x931E,
	0x92FF, 0x931D, 0x9302, 0x9370, 0x9357, 0x93A4, 0x93C6, 0x93DE, 0x93F8, 0x9431, 0x9445, 0x9448, 0x9592, 0xF9DC, 0xFA29, 0x969D,
	0x96AF, 0x9733, 0x973B, 0x9743, 0x974D, 0x974F, 0x9751, 0x9755, 0x9857, 0x9865, 0xFA2A, 0xFA2B, 0x9927, 0xFA2C, 0x999E, 0x9A4E,
	0x9AD9, 0x9ADC, 0x9B75, 0x9B72, 0x9B8F, 0x9BB1, 0x9BBB, 0x9C00, 0x9D70, 0x9D6B, 0xFA2D, 0x9E19, 0x9ED1, 0xFFFF, 0xFFFF, 0x2170,
	0x2171, 0x2172, 0x2173, 0x2174, 0x2175, 0x2176, 0x2177, 0x2178, 0x2179, 0xFFE2, 0xFFE4, 0xFF07, 0xFF02, 0xFFFF, 0xFFFF, 0xFFFF,
};

std::optional<char16_t> sjisToUnicode(unsigned short SjisCharacter)
{
	if (SjisCharacter < 0x80)
	{
		return SjisCharacter;
	} else if (SjisCharacter < 0x100)
	{
		return SjisToUnicodeTable1[SjisCharacter-0x80];
	}

	if ((SjisCharacter & 0xFF) < 0x40)
		return std::nullopt;

	if (SjisCharacter >= 0x8100 && SjisCharacter < 0x8500)
	{
		SjisCharacter -= 0x8140;
		SjisCharacter -= (SjisCharacter >> 8) * 0x40;
		return SjisToUnicodeTable2[SjisCharacter];
	} else if (SjisCharacter >= 0x8700 && SjisCharacter < 0xA000)
	{
		SjisCharacter -= 0x8740;
		SjisCharacter -= (SjisCharacter >> 8) * 0x40;
		return SjisToUnicodeTable3[SjisCharacter];
	} else if (SjisCharacter >= 0xE000 && SjisCharacter < 0xEB00)
	{
		SjisCharacter -= 0xE040;
		SjisCharacter -= (SjisCharacter >> 8) * 0x40;
		return SjisToUnicodeTable4[SjisCharacter];
	} else if (SjisCharacter >= 0xED00 && SjisCharacter < 0xEF00)
	{
		SjisCharacter -= 0xED40;
		SjisCharacter -= (SjisCharacter >> 8) * 0x40;
		return SjisToUnicodeTable5[SjisCharacter];
	} else {
		return std::nullopt;
	}
}

const size_t TEXTFILE_BUF_MAX_SIZE = 4096;

TextFile::TextFile()
{
	recursion = false;
	errorRetrieved = false;
	fromMemory = false;
	bufPos = 0;
	lineCount = 0;
}

TextFile::~TextFile()
{
	close();
}

void TextFile::openMemory(const std::string& content)
{
	fromMemory = true;
	this->content = content;
	contentPos = 0;
	size_ = (long) content.size();
	encoding = UTF8;
	mode = Read;
	lineCount = 0;
}

bool TextFile::open(const fs::path& fileName, Mode mode, Encoding defaultEncoding)
{
	setFileName(fileName);
	return open(mode,defaultEncoding);
}

bool TextFile::open(Mode mode, Encoding defaultEncoding)
{
	if (fileName.empty())
		return false;

	if (isOpen())
		close();

	fromMemory = false;
	guessedEncoding = false;
	encoding = defaultEncoding;
	this->mode = mode;

	// open all files as binary due to unicode
	switch (mode)
	{
	case Read:
		stream.open(fileName, fs::fstream::in | fs::fstream::binary);

		if (!stream.is_open())
			return false;
		break;
	case Write:
		stream.open(fileName, fs::fstream::out | fs::fstream::binary | fs::fstream::trunc);

		if (!stream.is_open())
			return false;

		buf.resize(TEXTFILE_BUF_MAX_SIZE);
		if (encoding != ASCII)
		{
			encoding = UTF8;
			bufPut(char(0xEF));
			bufPut(char(0xBB));
			bufPut(char(0xBF));
		}
		break;
	}

	// detect encoding
	unsigned char numBuffer[3] = {0};
	contentPos = 0;

	if (mode == Read)
	{
		size_ = long(fs::file_size(fileName));

		stream.read(reinterpret_cast<char *>(numBuffer), 3);
		switch (numBuffer[0] | (numBuffer[1] << 8))
		{
		case 0xFFFE:
			encoding = UTF16BE;
			stream.seekg(2);
			contentPos = 2;
			break;
		case 0xFEFF:
			encoding = UTF16LE;
			stream.seekg(2);
			contentPos = 2;
			break;
		case 0xBBEF:
			if (numBuffer[2] == 0xBF)
			{
				encoding = UTF8;
				contentPos = 3;
				break;
			}
			[[fallthrough]];
		default:
			if (defaultEncoding == GUESS)
			{
				encoding = UTF8;
				guessedEncoding = true;
			}
			stream.seekg(0);
			break;
		}
	} else {
		if (defaultEncoding == GUESS)
		{
			encoding = UTF8;
			guessedEncoding = true;
		}
	}

	return true;
}

void TextFile::close()
{
	if (isOpen() && !fromMemory)
	{
		bufDrainWrite();
		stream.close();
	}
	bufPos = 0;
}

long TextFile::tell()
{
	return (long) contentPos;
}

void TextFile::seek(long pos)
{
	if (fromMemory)
		contentPos = pos;
	else
		stream.seekg(pos);
}

void TextFile::bufFillRead()
{
	assert(mode == Read);

	buf.resize(TEXTFILE_BUF_MAX_SIZE);
	stream.read(&buf[0], TEXTFILE_BUF_MAX_SIZE);
	buf.resize(stream.gcount());
	bufPos = 0;
}

char32_t TextFile::readCharacter()
{
	char32_t value = 0;

	switch (encoding)
	{
	case UTF8:
		{
			value = bufGetChar();

			int extraBytes = 0;
			if ((value & 0xE0) == 0xC0)
			{
				extraBytes = 1;
				value &= 0x1F;
			} else if ((value & 0xF0) == 0xE0)
			{
				extraBytes = 2;
				value &= 0x0F;
			} else if (value > 0x7F)
			{
				errorText = tfm::format("One or more invalid UTF-8 characters in this file");
			}

			for (int i = 0; i < extraBytes; i++)
			{
				int b = bufGetChar();
				if ((b & 0xC0) != 0x80)
				{
					errorText = tfm::format("One or more invalid UTF-8 characters in this file");
				}

				value = (value << 6) | (b & 0x3F);
			}
		}
		break;
	case UTF16LE:
		value = bufGet16LE();
		break;
	case UTF16BE:
		value = bufGet16BE();
		break;
	case SJIS:
		{
			unsigned short sjis = bufGetChar();
			if (sjis >= 0x80)
				sjis = (sjis << 8) | bufGetChar();

			if (auto unicode = sjisToUnicode(sjis))
				value = *unicode;
			else
				errorText = tfm::format("One or more invalid Shift-JIS characters in this file");
		}
		break;
	case ASCII:
		value = bufGetChar();
		contentPos++;
		break;

	case GUESS:
		errorText = tfm::format("Cannot read from GUESS encoding");
		break;
	}

	// convert \r\n to \n
	if (value == L'\r' && !recursion && !atEnd())
	{
		recursion = true;
		long pos = tell();
		char32_t nextValue = readCharacter();
		recursion = false;

		if (nextValue == L'\n')
			return nextValue;
		seek(pos);
	}

	return value;
}

std::string TextFile::readLine()
{
	std::string result;
	char32_t value;

	if (isOpen())
	{
		while (tell() < size() && (value = readCharacter()) != L'\n')
		{
			encodeUtf8(result, value);
		}
	}

	lineCount++;
	return result;
}

std::vector<std::string> TextFile::readAll()
{
	std::vector<std::string> result;
	while (!atEnd())
	{
		result.push_back(readLine());
	}

	return result;
}

void TextFile::bufPut(const void *p, const size_t len)
{
	assert(mode == Write);

	if (len > TEXTFILE_BUF_MAX_SIZE)
	{
		// Lots of data.  Let's write directly.
		bufDrainWrite();
		stream.write(reinterpret_cast<const char*>(p), len);
	}
	else
	{
		if (bufPos + len > TEXTFILE_BUF_MAX_SIZE)
			bufDrainWrite();

		memcpy(&buf[bufPos], p, len);
		bufPos += len;
	}
}

void TextFile::bufPut(const char c)
{
	assert(mode == Write);

	if (bufPos >= TEXTFILE_BUF_MAX_SIZE)
		bufDrainWrite();

	buf[bufPos++] = c;
}

void TextFile::bufDrainWrite()
{
	stream.write(buf.c_str(), bufPos);
	bufPos = 0;
}

void TextFile::write(const char* line)
{
	if (mode != Write) return;

	while (*line != 0)
	{
#ifdef _WIN32
		if (*line == '\n')
			bufPut('\r');
#endif
		bufPut(*line);
		line++;
	}
}

void TextFile::write(const std::string& line)
{
	write(line.c_str());
}

void TextFile::writeLine(const char* line)
{
	if (mode != Write) return;
	write(line);
	bufPut('\n');
}

void TextFile::writeLine(const std::string& line)
{
	writeLine(line.c_str());
}

void TextFile::writeLines(std::vector<std::string>& list)
{
	for (size_t i = 0; i < list.size(); i++)
	{
		writeLine(list[i]);
	}
}

struct EncodingValue
{
	const char* name;
	TextFile::Encoding value;
};

const EncodingValue encodingValues[] = {
	{ "sjis",			TextFile::SJIS },
	{ "shift-jis",		TextFile::SJIS },
	{ "utf8",			TextFile::UTF8 },
	{ "utf-8",			TextFile::UTF8 },
	{ "utf16",			TextFile::UTF16LE },
	{ "utf-16",		TextFile::UTF16LE },
	{ "utf16-be",		TextFile::UTF16BE },
	{ "utf-16-be",		TextFile::UTF16BE },
	{ "ascii",			TextFile::ASCII },
};

TextFile::Encoding getEncodingFromString(const std::string& str)
{
	auto lowerCase = str;
	std::transform(lowerCase.begin(), lowerCase.end(), lowerCase.begin(), &::tolower);

	for (size_t i = 0; i < sizeof(encodingValues)/sizeof(EncodingValue); i++)
	{
		if (lowerCase.compare(encodingValues[i].name) == 0)
			return encodingValues[i].value;
	}

	return TextFile::GUESS;
}

#pragma region "File: Util/Util.cpp"

#include <cstring>
#include <sstream>

std::string convertUnicodeCharToUtf8(char32_t character)
{
	std::string result;

	if (character < 0x80)
	{
		result += character & 0x7F;
	} else if (character < 0x800)
	{
		result += 0xC0 | ((character >> 6) & 0x1F);
		result += (0x80 | (character & 0x3F));
	} else {
		result += 0xE0 | ((character >> 12) & 0xF);
		result += 0x80 | ((character >> 6) & 0x3F);
		result += 0x80 | (character & 0x3F);
	}

	return result;
}

std::string convertWStringToUtf8(std::wstring_view source)
{
	std::string result;

	for (size_t i = 0; i < source.size(); i++)
	{
		wchar_t character = source[i];
		if (character < 0x80)
		{
			result += character & 0x7F;
		} else if (character < 0x800)
		{
			result += 0xC0 | ((character >> 6) & 0x1F);
			result += (0x80 | (character & 0x3F));
		} else {
			result += 0xE0 | ((character >> 12) & 0xF);
			result += 0x80 | ((character >> 6) & 0x3F);
			result += 0x80 | (character & 0x3F);
		}
	}

	return result;
}

bool stringToInt(const std::string& line, size_t start, size_t end, int64_t& result)
{
	// find base of number
	int32_t base = 10;
	if (line[start] == '0')
	{
		if (tolower(line[start+1]) == 'x')
		{
			base = 16;
			start += 2;
		} else if (tolower(line[start+1]) == 'o')
		{
			base = 8;
			start += 2;
		} else if (tolower(line[start+1]) == 'b' && tolower(line[end-1]) != 'h')
		{
			base = 2;
			start += 2;
		}
	}

	if (base == 10)
	{
		if (tolower(line[end-1]) == 'h')
		{
			base = 16;
			end--;
		} else if (tolower(line[end-1]) == 'b')
		{
			base = 2;
			end--;
		} else if (tolower(line[end-1]) == 'o')
		{
			base = 8;
			end--;
		}
	}

	// convert number
	result = 0;
	while (start < end)
	{
		int c = tolower(line[start++]);

		int32_t value = c >= 'a' ? c-'a'+10 : c-'0';

		if (value >= base)
			return false;

		result = (result*base) + value;
	}

	return true;
}

int32_t getFloatBits(float value)
{
	union { float f; int32_t i; } u;
	u.f = value;
	return u.i;
}

float bitsToFloat(int32_t value)
{
	union { float f; int32_t i; } u;
	u.i = value;
	return u.f;
}

int64_t getDoubleBits(double value)
{
	union { double f; int64_t i; } u;
	u.f = value;
	return u.i;
}

std::string toLowercase(const std::string& str)
{
	std::string result;
	for (size_t i = 0; i < str.size(); i++)
	{
		result += tolower(str[i]);
	}

	return result;
}

size_t replaceAll(std::string& str, const char* oldValue,const std::string& newValue)
{
	size_t pos = 0;
	size_t len = strlen(oldValue);

	size_t count = 0;
	while ((pos = str.find(oldValue, pos)) != std::string::npos)
	{
		str.replace(pos,len,newValue);
		pos += newValue.length();
		count++;
	}

	return count;
}

bool startsWith(const std::string& str, const char* value, size_t stringPos)
{
	while (*value != 0 && stringPos < str.size())
	{
		if (str[stringPos++] != *value++)
			return false;
	}

	return *value == 0;
}

#pragma region "File: Main/CommandLineInterface.h"


int runFromCommandLine(const std::vector<std::string>& arguments, ArmipsArguments settings = {});

#pragma region "File: Main/CommandLineInterface.cpp"


static void printUsage(const std::string &executableName)
{
	Logger::printLine("armips assembler v%d.%d.%d (%s %s) by Kingcom",
		ARMIPS_VERSION_MAJOR, ARMIPS_VERSION_MINOR, ARMIPS_VERSION_REVISION, __DATE__, __TIME__);
	Logger::printLine("Usage: %s [optional parameters] <FILE>", executableName);
	Logger::printLine("");
	Logger::printLine("Optional parameters:");
	Logger::printLine(" -temp <TEMP>              Output temporary assembly data to <TEMP> file");
	Logger::printLine(" -sym  <SYM>               Output symbol data in the sym format to <SYM> file");
	Logger::printLine(" -sym2 <SYM2>              Output symbol data in the sym2 format to <SYM2> file");
	Logger::printLine(" -root <ROOT>              Use <ROOT> as working directory during execution");
	Logger::printLine(" -equ  <NAME> <VAL>        Equivalent to \'<NAME> equ <VAL>\' in code");
	Logger::printLine(" -strequ <NAME> <VAL>      Equivalent to \'<NAME> equ \"<VAL>\"\' in code");
	Logger::printLine(" -definelabel <NAME> <VAL> Equivalent to \'.definelabel <NAME>, <VAL>\' in code");
	Logger::printLine(" -erroronwarning           Treat all warnings like errors");
	Logger::printLine(" -stat                     Show area usage statistics");
	Logger::printLine("");
	Logger::printLine("File arguments:");
	Logger::printLine(" <FILE>                    Main assembly code file");
}

static bool parseArguments(const std::vector<std::string>& arguments, ArmipsArguments& settings)
{
	size_t argpos = 1;
	bool readflags = true;
	while (argpos < arguments.size())
	{
		if (readflags && arguments[argpos][0] == '-')
		{
			if (arguments[argpos] == "--")
			{
				readflags = false;
				argpos += 1;
			}
			else if (arguments[argpos] == "-temp" && argpos + 1 < arguments.size())
			{
				settings.tempFileName = arguments[argpos + 1];
				argpos += 2;
			}
			else if (arguments[argpos] == "-sym" && argpos + 1 < arguments.size())
			{
				settings.symFileName = arguments[argpos + 1];
				settings.symFileVersion = 1;
				argpos += 2;
			}
			else if (arguments[argpos] == "-sym2" && argpos + 1 < arguments.size())
			{
				settings.symFileName = arguments[argpos + 1];
				settings.symFileVersion = 2;
				argpos += 2;
			}
			else if (arguments[argpos] == "-erroronwarning")
			{
				settings.errorOnWarning = true;
				argpos += 1;
			}
			else if (arguments[argpos] == "-stat")
			{
				settings.showStats = true;
				argpos += 1;
			}
			else if (arguments[argpos] == "-equ" && argpos + 2 < arguments.size())
			{
				Identifier name = Identifier(arguments[argpos+1]);

				if (!checkValidLabelName(name))
				{
					Logger::printError(Logger::Error, "Invalid equation name \"%s\"", name);
					return false;
				}

				auto it = std::find_if(settings.equList.begin(), settings.equList.end(),
						[&name](const EquationDefinition& x) -> bool {return name == x.name;});
				if(it != settings.equList.end())
				{
					Logger::printError(Logger::Error, "Equation name \"%s\" already defined", name);
					return false;
				}

				const std::string &value = arguments[argpos + 2];
				settings.equList.emplace_back(EquationDefinition{name, value});
				argpos += 3;
			}
			else if (arguments[argpos] == "-strequ" && argpos + 2 < arguments.size())
			{
				Identifier name = Identifier(arguments[argpos+1]);

				if (!checkValidLabelName(name))
				{
					Logger::printError(Logger::Error, "Invalid equation name \"%s\"", name);
					return false;
				}

				auto it = std::find_if(settings.equList.begin(), settings.equList.end(),
						[&name](const EquationDefinition& x) -> bool {return name == x.name;});
				if(it != settings.equList.end())
				{
					Logger::printError(Logger::Error, "Equation name \"%s\" already defined", name);
					return false;
				}

				std::string value = tfm::format("\"%s\"", arguments[argpos + 2]);
				settings.equList.emplace_back(EquationDefinition{name, value});
				argpos += 3;
			}
			else if (arguments[argpos] == "-time")
			{
				Logger::printError(Logger::Warning, "-time flag is deprecated");
				argpos += 1;
			}
			else if (arguments[argpos] == "-root" && argpos + 1 < arguments.size())
			{
				std::error_code errorCode;
				fs::current_path(arguments[argpos + 1], errorCode);

				if (errorCode)
				{
					Logger::printError(Logger::Error, "Could not open directory \"%s\"", arguments[argpos + 1]);
					return false;
				}
				argpos += 2;
			}
			else if (arguments[argpos] == "-definelabel" && argpos + 2 < arguments.size())
			{
				Identifier name = Identifier(arguments[argpos + 1]);

				if (!checkValidLabelName(name))
				{
					Logger::printError(Logger::Error, "Invalid label name \"%s\"", name);
					return false;
				}

				auto it = std::find_if(settings.labels.begin(), settings.labels.end(),
						[&name](const LabelDefinition& x) -> bool {return name == x.name;});
				if(it != settings.labels.end())
				{
					Logger::printError(Logger::Error, "Label name \"%s\" already defined", name);
					return false;
				}

				int64_t value;
				if (!stringToInt(arguments[argpos + 2], 0, arguments[argpos + 2].size(), value))
				{
					Logger::printError(Logger::Error, "Invalid label value \"%s\"", arguments[argpos + 2]);
					return false;
				}

				settings.labels.emplace_back(LabelDefinition{name, value});
				argpos += 3;
			}
			else {
				Logger::printError(Logger::Error, "Invalid command line argument \"%s\"\n", arguments[argpos]);
				printUsage(arguments[0]);
				return false;
			}
		}
		else {
			// only allow one input filename
			if (settings.inputFileName.empty())
			{
				settings.inputFileName = arguments[argpos];
				argpos++;
			}
			else {
				Logger::printError(Logger::Error, "Multiple input assembly files specified\n");
				printUsage(arguments[0]);
				return false;
			}
		}
	}

	// ensure input file was specified
	if (settings.inputFileName.empty())
	{
		if (arguments.size() > 1)
			Logger::printError(Logger::Error, "Missing input assembly file\n");

		printUsage(arguments[0]);
		return false;
	}

	// turn input filename into an absolute path
	if (settings.useAbsoluteFileNames)
		settings.inputFileName = fs::absolute(settings.inputFileName).lexically_normal();

	if (!fs::exists(settings.inputFileName))
	{
		Logger::printError(Logger::Error, "File \"%s\" not found", settings.inputFileName.u8string());
		return false;
	}
	return true;
}

int runFromCommandLine(const std::vector<std::string>& arguments, ArmipsArguments settings)
{
	if (!parseArguments(arguments, settings))
	{
		if (arguments.size() > 1 && !settings.silent)
			Logger::printLine("Cannot parse arguments; aborting.");

		return 1;
	}

	if (!runArmips(settings))
	{
		if (!settings.silent)
			Logger::printLine("Aborting.");

		return 1;
	}

	return 0;
}

#pragma region "File: Core/ELF/ElfFile.cpp"


#include <algorithm>
#include <cctype>
#include <cstring>
#include <vector>

static bool stringEqualInsensitive(const std::string& a, const std::string& b)
{
	if (a.size() != b.size())
		return false;

	auto compare = [](char c1, char c2)
	{
		return std::tolower(c1) == std::tolower(c2);
	};

	return std::equal(a.begin(), a.end(), b.begin(), compare);
}

bool compareSection(ElfSection* a, ElfSection* b)
{
	return a->getOffset() < b->getOffset();
}

ElfSection::ElfSection(Elf32_Shdr header): header(header)
{
	owner = nullptr;
}

void ElfSection::setOwner(ElfSegment* segment)
{
	header.sh_offset -= segment->getOffset();
	owner = segment;
}

void ElfSection::writeHeader(ByteArray& data, size_t pos, Endianness endianness)
{
	data.replaceDoubleWord(pos + 0x00, header.sh_name, endianness);
	data.replaceDoubleWord(pos + 0x04, header.sh_type, endianness);
	data.replaceDoubleWord(pos + 0x08, header.sh_flags, endianness);
	data.replaceDoubleWord(pos + 0x0C, header.sh_addr, endianness);
	data.replaceDoubleWord(pos + 0x10, header.sh_offset, endianness);
	data.replaceDoubleWord(pos + 0x14, header.sh_size, endianness);
	data.replaceDoubleWord(pos + 0x18, header.sh_link, endianness);
	data.replaceDoubleWord(pos + 0x1C, header.sh_info, endianness);
	data.replaceDoubleWord(pos + 0x20, header.sh_addralign, endianness);
	data.replaceDoubleWord(pos + 0x24, header.sh_entsize, endianness);
}

// only called for segmentless sections
void ElfSection::writeData(ByteArray& output)
{
	if (header.sh_type == SHT_NULL) return;

	// nobits sections still get a provisional file address
	if (header.sh_type == SHT_NOBITS)
	{
		header.sh_offset = (Elf32_Off) output.size();
	}

	if (header.sh_addralign != (unsigned) -1)
		output.alignSize(header.sh_addralign);
	header.sh_offset = (Elf32_Off) output.size();
	output.append(data);
}

void ElfSection::setOffsetBase(int base)
{
	header.sh_offset += base;
}

ElfSegment::ElfSegment(Elf32_Phdr header, ByteArray& segmentData)
	: header(header), data(segmentData), paddrSection(nullptr)
{
}

bool ElfSegment::isSectionPartOf(ElfSection* section)
{
	int sectionStart = section->getOffset();
	int sectionSize = section->getType() == SHT_NOBITS ? 0 : section->getSize();
	int sectionEnd = sectionStart+sectionSize;

	int segmentStart = header.p_offset;
	int segmentEnd = segmentStart+header.p_filesz;

	// exclusive > in case the size is 0
	if (sectionStart < (int)header.p_offset || sectionStart > segmentEnd) return false;

	// does an empty section belong to this or the next segment? hm...
	if (sectionStart == segmentEnd) return sectionSize == 0;

	// the start is inside the section and the size is not 0, so the end should be in here too
	if (sectionEnd > segmentEnd)
	{
		Logger::printError(Logger::Error, "Section partially contained in segment");
		return false;
	}

	return true;
}

void ElfSegment::addSection(ElfSection* section)
{
	if (header.p_paddr != 0)
	{
		if (section->getOffset() == header.p_paddr)
		{
			paddrSection = section;
		}
	}

	section->setOwner(this);
	sections.push_back(section);
}

void ElfSegment::writeData(ByteArray& output)
{
	if (sections.size() == 0)
	{
		output.alignSize(header.p_align);
		if (header.p_offset == header.p_paddr)
			header.p_paddr = (Elf32_Addr) output.size();

		header.p_offset = (Elf32_Off) output.size();
		return;
	}

	// align segment to alignment of first section
	int align = std::max<int>(sections[0]->getAlignment(),16);
	output.alignSize(align);

	header.p_offset = (Elf32_Off) output.size();
	for (int i = 0; i < (int)sections.size(); i++)
	{
		sections[i]->setOffsetBase(header.p_offset);
	}

	if (paddrSection)
	{
		header.p_paddr = paddrSection->getOffset();
	}

	output.append(data);
}

void ElfSegment::writeHeader(ByteArray& data, size_t pos, Endianness endianness)
{
	data.replaceDoubleWord(pos + 0x00, header.p_type, endianness);
	data.replaceDoubleWord(pos + 0x04, header.p_offset, endianness);
	data.replaceDoubleWord(pos + 0x08, header.p_vaddr, endianness);
	data.replaceDoubleWord(pos + 0x0C, header.p_paddr, endianness);
	data.replaceDoubleWord(pos + 0x10, header.p_filesz, endianness);
	data.replaceDoubleWord(pos + 0x14, header.p_memsz, endianness);
	data.replaceDoubleWord(pos + 0x18, header.p_flags, endianness);
	data.replaceDoubleWord(pos + 0x1C, header.p_align, endianness);
}

void ElfSegment::splitSections()
{

}

int ElfSegment::findSection(const std::string& name)
{
	for (size_t i = 0; i < sections.size(); i++)
	{
		if (stringEqualInsensitive(name,sections[i]->getName()))
			return (int)i;
	}

	return -1;
}

void ElfSegment::writeToData(size_t offset, void* src, size_t size)
{
	for (size_t i = 0; i < size; i++)
	{
		data[offset+i] = ((byte*)src)[i];
	}
}

void ElfSegment::sortSections()
{
	std::sort(sections.begin(),sections.end(),compareSection);
}

void ElfFile::loadSectionNames()
{
	if (fileHeader.e_shstrndx == SHN_UNDEF) return;

	// check if the string table is actually a string table
	// sometimes it gives the wrong section id
	size_t strTablePos = sections[fileHeader.e_shstrndx]->getOffset();
	size_t strTableSize = sections[fileHeader.e_shstrndx]->getSize();
	for (size_t i = 0; i < strTableSize; i++)
	{
		if (fileData[strTablePos+i] != 0 && fileData[strTablePos+i] < 0x20)
			return;
		if (fileData[strTablePos+i] > 0x7F)
			return;
	}

	for (size_t i = 0; i < sections.size(); i++)
	{
		ElfSection* section = sections[i];
		if (section->getType() == SHT_NULL) continue;

		int strTablePos = sections[fileHeader.e_shstrndx]->getOffset();
		int offset = strTablePos+section->getNameOffset();

		char* name = (char*) fileData.data(offset);
		std::string strName = name;
		section->setName(strName);
	}
}

void ElfFile::determinePartOrder()
{
	size_t segmentTable = fileHeader.e_phoff;
	size_t sectionTable = fileHeader.e_shoff;

	// segments
	size_t firstSegmentStart = fileData.size(), lastSegmentEnd = 0;
	for (size_t i = 0; i < fileHeader.e_phnum; i++)
	{
		size_t pos = fileHeader.e_phoff+i*fileHeader.e_phentsize;

		Elf32_Phdr segmentHeader;
		loadProgramHeader(segmentHeader, fileData, pos);
		size_t end = segmentHeader.p_offset + segmentHeader.p_filesz;

		if (segmentHeader.p_offset < firstSegmentStart) firstSegmentStart = segmentHeader.p_offset;
		if (lastSegmentEnd < end) lastSegmentEnd = end;
	}

	// segmentless sections
	size_t firstSectionStart = fileData.size(), lastSectionEnd = 0;
	for (size_t i = 0; i < segmentlessSections.size(); i++)
	{
		if (segmentlessSections[i]->getType() == SHT_NULL) continue;

		size_t start = segmentlessSections[i]->getOffset();
		size_t end = start+segmentlessSections[i]->getSize();

		if (start == 0 && end == 0)
			continue;
		if (start < firstSectionStart) firstSectionStart = start;
		if (lastSectionEnd < end) lastSectionEnd = end;
	}

	struct PartsSort {
		size_t offset;
		ElfPart type;
		bool operator<(const PartsSort& other) const { return offset < other.offset; };
	};

	PartsSort temp[4] = {
		{ segmentTable,				ELFPART_SEGMENTTABLE },
		{ sectionTable,				ELFPART_SECTIONTABLE },
		{ firstSegmentStart,		ELFPART_SEGMENTS },
		{ firstSectionStart,		ELFPART_SEGMENTLESSSECTIONS },
	};

	std::sort(&temp[0],&temp[4]);

	for (size_t i = 0; i < 4; i++)
	{
		partsOrder[i] = temp[i].type;
	}
}

int ElfFile::findSegmentlessSection(const std::string& name)
{
	for (size_t i = 0; i < segmentlessSections.size(); i++)
	{
		if (stringEqualInsensitive(name,segmentlessSections[i]->getName()))
			return (int)i;
	}

	return -1;
}

void ElfFile::loadElfHeader()
{
	memcpy(fileHeader.e_ident, &fileData[0], sizeof(fileHeader.e_ident));
	Endianness endianness = getEndianness();
	fileHeader.e_type = fileData.getWord(0x10, endianness);
	fileHeader.e_machine = fileData.getWord(0x12, endianness);
	fileHeader.e_version = fileData.getDoubleWord(0x14, endianness);
	fileHeader.e_entry = fileData.getDoubleWord(0x18, endianness);
	fileHeader.e_phoff = fileData.getDoubleWord(0x1C, endianness);
	fileHeader.e_shoff = fileData.getDoubleWord(0x20, endianness);
	fileHeader.e_flags = fileData.getDoubleWord(0x24, endianness);
	fileHeader.e_ehsize = fileData.getWord(0x28, endianness);
	fileHeader.e_phentsize = fileData.getWord(0x2A, endianness);
	fileHeader.e_phnum = fileData.getWord(0x2C, endianness);
	fileHeader.e_shentsize = fileData.getWord(0x2E, endianness);
	fileHeader.e_shnum = fileData.getWord(0x30, endianness);
	fileHeader.e_shstrndx = fileData.getWord(0x32, endianness);
}

void ElfFile::writeHeader(ByteArray& data, size_t pos, Endianness endianness)
{
	memcpy(&fileData[0], fileHeader.e_ident, sizeof(fileHeader.e_ident));
	data.replaceWord(pos + 0x10, fileHeader.e_type, endianness);
	data.replaceWord(pos + 0x12, fileHeader.e_machine, endianness);
	data.replaceDoubleWord(pos + 0x14, fileHeader.e_version, endianness);
	data.replaceDoubleWord(pos + 0x18, fileHeader.e_entry, endianness);
	data.replaceDoubleWord(pos + 0x1C, fileHeader.e_phoff, endianness);
	data.replaceDoubleWord(pos + 0x20, fileHeader.e_shoff, endianness);
	data.replaceDoubleWord(pos + 0x24, fileHeader.e_flags, endianness);
	data.replaceWord(pos + 0x28, fileHeader.e_ehsize, endianness);
	data.replaceWord(pos + 0x2A, fileHeader.e_phentsize, endianness);
	data.replaceWord(pos + 0x2C, fileHeader.e_phnum, endianness);
	data.replaceWord(pos + 0x2E, fileHeader.e_shentsize, endianness);
	data.replaceWord(pos + 0x30, fileHeader.e_shnum, endianness);
	data.replaceWord(pos + 0x32, fileHeader.e_shstrndx, endianness);
}

void ElfFile::loadProgramHeader(Elf32_Phdr& header, ByteArray& data, size_t pos)
{
	Endianness endianness = getEndianness();
	header.p_type   = data.getDoubleWord(pos + 0x00, endianness);
	header.p_offset = data.getDoubleWord(pos + 0x04, endianness);
	header.p_vaddr  = data.getDoubleWord(pos + 0x08, endianness);
	header.p_paddr  = data.getDoubleWord(pos + 0x0C, endianness);
	header.p_filesz = data.getDoubleWord(pos + 0x10, endianness);
	header.p_memsz  = data.getDoubleWord(pos + 0x14, endianness);
	header.p_flags  = data.getDoubleWord(pos + 0x18, endianness);
	header.p_align  = data.getDoubleWord(pos + 0x1C, endianness);
}

void ElfFile::loadSectionHeader(Elf32_Shdr& header, ByteArray& data, size_t pos)
{
	Endianness endianness = getEndianness();
	header.sh_name      = data.getDoubleWord(pos + 0x00, endianness);
	header.sh_type      = data.getDoubleWord(pos + 0x04, endianness);
	header.sh_flags     = data.getDoubleWord(pos + 0x08, endianness);
	header.sh_addr      = data.getDoubleWord(pos + 0x0C, endianness);
	header.sh_offset    = data.getDoubleWord(pos + 0x10, endianness);
	header.sh_size      = data.getDoubleWord(pos + 0x14, endianness);
	header.sh_link      = data.getDoubleWord(pos + 0x18, endianness);
	header.sh_info      = data.getDoubleWord(pos + 0x1C, endianness);
	header.sh_addralign = data.getDoubleWord(pos + 0x20, endianness);
	header.sh_entsize   = data.getDoubleWord(pos + 0x24, endianness);
}

bool ElfFile::load(const fs::path& fileName, bool sort)
{
	ByteArray data = ByteArray::fromFile(fileName);
	if (data.size() == 0)
		return false;
	return load(data,sort);
}

bool ElfFile::load(ByteArray& data, bool sort)
{
	fileData = data;

	loadElfHeader();
	symTab = nullptr;
	strTab = nullptr;

	// load segments
	for (size_t i = 0; i < fileHeader.e_phnum; i++)
	{
		size_t pos = fileHeader.e_phoff+i*fileHeader.e_phentsize;

		Elf32_Phdr sectionHeader;
		loadProgramHeader(sectionHeader, fileData, pos);

		ByteArray segmentData = fileData.mid(sectionHeader.p_offset,sectionHeader.p_filesz);
		ElfSegment* segment = new ElfSegment(sectionHeader,segmentData);
		segments.push_back(segment);
	}

	// load sections and assign them to segments
	for (int i = 0; i < fileHeader.e_shnum; i++)
	{
		size_t pos = fileHeader.e_shoff+i*fileHeader.e_shentsize;

		Elf32_Shdr sectionHeader;
		loadSectionHeader(sectionHeader, fileData, pos);

		ElfSection* section = new ElfSection(sectionHeader);
		sections.push_back(section);

		// check if the section belongs to a segment
		ElfSegment* owner = nullptr;
		for (int k = 0; k < (int)segments.size(); k++)
		{
			if (segments[k]->isSectionPartOf(section))
			{
				owner = segments[k];
				break;
			}
		}

		if (owner != nullptr)
		{
			owner->addSection(section);
		} else {
			if (section->getType() != SHT_NOBITS && section->getType() != SHT_NULL)
			{
				ByteArray data = fileData.mid(section->getOffset(),section->getSize());
				section->setData(data);
			}

			switch (section->getType())
			{
			case SHT_SYMTAB:
				symTab = section;
				break;
			case SHT_STRTAB:
				if (!strTab || i != fileHeader.e_shstrndx)
				{
					strTab = section;
				}
				break;
			}

			segmentlessSections.push_back(section);
		}
	}

	determinePartOrder();
	loadSectionNames();

	if (sort)
	{
		std::sort(segmentlessSections.begin(),segmentlessSections.end(),compareSection);

		for (int i = 0; i < (int)segments.size(); i++)
		{
			segments[i]->sortSections();
		}
	}

	return true;
}

void ElfFile::save(const fs::path& fileName)
{
	fileData.clear();

	// reserve space for header and table data
	fileData.reserveBytes(sizeof(Elf32_Ehdr));

	for (size_t i = 0; i < 4; i++)
	{
		switch (partsOrder[i])
		{
		case ELFPART_SEGMENTTABLE:
			fileData.alignSize(4);
			fileHeader.e_phoff = (Elf32_Off) fileData.size();
			fileData.reserveBytes(segments.size()*fileHeader.e_phentsize);
			break;
		case ELFPART_SECTIONTABLE:
			fileData.alignSize(4);
			fileHeader.e_shoff = (Elf32_Off) fileData.size();
			fileData.reserveBytes(sections.size()*fileHeader.e_shentsize);
			break;
		case ELFPART_SEGMENTS:
			for (size_t i = 0; i < segments.size(); i++)
			{
				segments[i]->writeData(fileData);
			}
			break;
		case ELFPART_SEGMENTLESSSECTIONS:
			for (size_t i = 0; i < segmentlessSections.size(); i++)
			{
				segmentlessSections[i]->writeData(fileData);
			}
			break;
		}
	}

	// copy data to the tables
	Endianness endianness = getEndianness();
	writeHeader(fileData, 0, endianness);
	for (size_t i = 0; i < segments.size(); i++)
	{
		size_t pos = fileHeader.e_phoff+i*fileHeader.e_phentsize;
		segments[i]->writeHeader(fileData, pos, endianness);
	}

	for (size_t i = 0; i < sections.size(); i++)
	{
		size_t pos = fileHeader.e_shoff+i*fileHeader.e_shentsize;
		sections[i]->writeHeader(fileData, pos, endianness);
	}

	fileData.toFile(fileName);
}

int ElfFile::getSymbolCount()
{
	if (symTab == nullptr)
		return 0;

	return symTab->getSize()/sizeof(Elf32_Sym);
}

bool ElfFile::getSymbol(Elf32_Sym& symbol, size_t index)
{
	if (symTab == nullptr)
		return false;

	ByteArray &data = symTab->getData();
	size_t pos = index*sizeof(Elf32_Sym);
	Endianness endianness = getEndianness();
	symbol.st_name  = data.getDoubleWord(pos + 0x00, endianness);
	symbol.st_value = data.getDoubleWord(pos + 0x04, endianness);
	symbol.st_size  = data.getDoubleWord(pos + 0x08, endianness);
	symbol.st_info  = data[pos + 0x0C];
	symbol.st_other = data[pos + 0x0D];
	symbol.st_shndx = data.getWord(pos + 0x0E, endianness);

	return true;
}

const char* ElfFile::getStrTableString(size_t pos)
{
	if (strTab == nullptr)
		return nullptr;

	return (const char*) &strTab->getData()[pos];
}

#pragma region "File: Core/ELF/ElfRelocator.cpp"


#include <cstring>

struct ArFileHeader
{
	char fileName[16];
	char modifactionTime[12];
	char ownerId[6];
	char groupId[6];
	char fileMode[8];
	char fileSize[10];
	char magic[2];
};

struct ArFileEntry
{
	std::string name;
	ByteArray data;
};

std::vector<ArFileEntry> loadArArchive(const fs::path& inputName)
{
	ByteArray input = ByteArray::fromFile(inputName);
	std::vector<ArFileEntry> result;

	if (input.size() < 8 || memcmp(input.data(),"!<arch>\n",8) != 0)
	{
		if (input.size() < 4 || memcmp(input.data(),"\x7F""ELF",4) != 0)
			return result;

		ArFileEntry entry;
		entry.name = inputName.filename().u8string();
		entry.data = input;
		result.push_back(entry);
		return result;
	}

	size_t pos = 8;
	while (pos < input.size())
	{
		ArFileHeader* header = (ArFileHeader*) input.data(pos);
		pos += sizeof(ArFileHeader);

		// get file size
		int size = 0;
		for (int i = 0; i < 10; i++)
		{
			if (header->fileSize[i] == ' ')
				break;

			size = size*10;
			size += (header->fileSize[i]-'0');
		}

		// only ELF files are actually interesting
		if (memcmp(input.data(pos),"\x7F""ELF",4) == 0)
		{
			// get file name
			char fileName[17];
			fileName[16] = 0;
			for (int i = 0; i < 16; i++)
			{
				if (header->fileName[i] == ' ')
				{
					// remove trailing slashes of file names
					if (i > 0 && fileName[i-1] == '/')
						i--;
					fileName[i] = 0;
					break;
				}

				fileName[i] = header->fileName[i];
			}

			ArFileEntry entry;
			entry.name = fileName;
			entry.data = input.mid(pos,size);
			result.push_back(entry);
		}

		pos += size;
		if (pos % 2)
			pos++;
	}

	return result;
}

bool ElfRelocator::init(const fs::path& inputName)
{
	relocator = Architecture::current().getElfRelocator();
	if (relocator == nullptr)
	{
		Logger::printError(Logger::Error, "Object importing not supported for this architecture");
		return false;
	}

	auto inputFiles = loadArArchive(inputName);
	if (inputFiles.size() == 0)
	{
		Logger::printError(Logger::Error, "Could not load library");
		return false;
	}

	for (ArFileEntry& entry: inputFiles)
	{
		ElfRelocatorFile file;

		ElfFile* elf = new ElfFile();
		if (!elf->load(entry.data,false))
		{
			Logger::printError(Logger::Error, "Could not load object file %s",entry.name);
			return false;
		}

		if (elf->getType() != ET_REL)
		{
			Logger::printError(Logger::Error, "Unexpected ELF type %d in object file %s",elf->getType(),entry.name);
			return false;
		}

		if (elf->getMachine() != relocator->expectedMachine())
		{
			Logger::printError(Logger::Error, "Unexpected ELF machine %d in object file %s",elf->getMachine(),entry.name);
			return false;
		}

		if (elf->getEndianness() != Architecture::current().getEndianness())
		{
			Logger::printError(Logger::Error, "Incorrect endianness in object file %s",entry.name);
			return false;
		}

		if (elf->getSegmentCount() != 0)
		{
			Logger::printError(Logger::Error, "Unexpected segment count %d in object file %s",elf->getSegmentCount(),entry.name);
			return false;
		}


		// load all relevant sections of this file
		for (size_t s = 0; s < elf->getSegmentlessSectionCount(); s++)
		{
			ElfSection* sec = elf->getSegmentlessSection(s);
			if (!(sec->getFlags() & SHF_ALLOC))
				continue;

			if (sec->getType() == SHT_PROGBITS || sec->getType() == SHT_NOBITS || sec->getType() == SHT_INIT_ARRAY)
			{
				ElfRelocatorSection sectionEntry;
				sectionEntry.section = sec;
				sectionEntry.index = s;
				sectionEntry.relSection = nullptr;
				sectionEntry.label = nullptr;

				// search relocation section
				for (size_t k = 0; k < elf->getSegmentlessSectionCount(); k++)
				{
					ElfSection* relSection = elf->getSegmentlessSection(k);
					if (relSection->getType() != SHT_REL && relSection->getType() != SHT_RELA)
						continue;
					if (relSection->getInfo() != s)
						continue;

					// got it
					sectionEntry.relSection = relSection;
					break;
				}

				// keep track of constructor sections
				if (sec->getName() == ".ctors" || sec->getName() == ".init_array")
				{
					ElfRelocatorCtor ctor;
					ctor.symbolName = Global.symbolTable.getUniqueLabelName();
					ctor.size = sec->getSize();

					sectionEntry.label = Global.symbolTable.getLabel(ctor.symbolName,-1,-1);
					sectionEntry.label->setDefined(true);

					ctors.push_back(ctor);
				}

				file.sections.push_back(sectionEntry);
			}
		}

		// init exportable symbols
		for (int i = 0; i < elf->getSymbolCount(); i++)
		{
			Elf32_Sym symbol;
			elf->getSymbol(symbol, i);

			if ((ELF32_ST_BIND(symbol.st_info) == STB_GLOBAL || ELF32_ST_BIND(symbol.st_info) == STB_WEAK) && symbol.st_shndx != 0)
			{
				ElfRelocatorSymbol symEntry;
				symEntry.type = ELF32_ST_TYPE(symbol.st_info);
				symEntry.name = elf->getStrTableString(symbol.st_name);
				symEntry.relativeAddress = symbol.st_value;
				symEntry.section = symbol.st_shndx;
				symEntry.size = symbol.st_size;
				symEntry.label = nullptr;

				file.symbols.push_back(symEntry);
			}
		}

		file.elf = elf;
		file.name = entry.name;
		files.push_back(file);
	}

	return true;
}

bool ElfRelocator::exportSymbols()
{
	bool error = false;

	for (ElfRelocatorFile& file: files)
	{
		for (ElfRelocatorSymbol& sym: file.symbols)
		{
			if (sym.label != nullptr)
				continue;

			std::string lowered = sym.name;
			std::transform(lowered.begin(), lowered.end(), lowered.begin(), ::tolower);

			sym.label = Global.symbolTable.getLabel(Identifier(lowered),-1,-1);
			if (sym.label == nullptr)
			{
				Logger::printError(Logger::Error, "Invalid label name \"%s\"",sym.name);
				error = true;
				continue;
			}

			if (sym.label->isDefined())
			{
				Logger::printError(Logger::Error, "Label \"%s\" already defined",sym.name);
				error = true;
				continue;
			}

			RelocationData data;
			data.symbolAddress = sym.relativeAddress;
			relocator->setSymbolAddress(data,sym.relativeAddress,sym.type);

			sym.relativeAddress = data.symbolAddress;
			sym.label->setInfo(data.targetSymbolInfo);
			sym.label->setIsData(sym.type == STT_OBJECT);
			sym.label->setUpdateInfo(false);

			sym.label->setValue(0);
			sym.label->setDefined(true);
			sym.label->setOriginalName(Identifier(sym.name));
		}
	}

	return !error;
}

std::unique_ptr<CAssemblerCommand> ElfRelocator::generateCtor(const Identifier& ctorName)
{
	std::unique_ptr<CAssemblerCommand> content = relocator->generateCtorStub(ctors);

	auto func = std::make_unique<CDirectiveFunction>(ctorName, ctorName);
	func->setContent(std::move(content));
	return func;
}

void ElfRelocator::loadRelocation(Elf32_Rela& rela, bool addend, ByteArray& data, int offset, Endianness endianness)
{
	rela.r_offset = data.getDoubleWord(offset + 0x00, endianness);
	rela.r_info   = data.getDoubleWord(offset + 0x04, endianness);
	if (addend)
		rela.r_addend = data.getDoubleWord(offset + 0x08, endianness);
	else
		rela.r_addend = 0;
}

bool ElfRelocator::relocateFile(ElfRelocatorFile& file, int64_t& relocationAddress)
{
	ElfFile* elf = file.elf;
	int64_t start = relocationAddress;

	// calculate address for each section
	std::map<int64_t,int64_t> relocationOffsets;
	for (ElfRelocatorSection& entry: file.sections)
	{
		ElfSection* section = entry.section;
		size_t index = entry.index;
		int size = section->getSize();

		if (section->getType() == SHT_NOBITS)
		{
			// these sections should not be relocated...
			relocationOffsets[index] = section->getAddress();

		} else {
			while (relocationAddress % section->getAlignment())
				relocationAddress++;

			if (entry.label != nullptr)
				entry.label->setValue(relocationAddress);

			relocationOffsets[index] = relocationAddress;
			relocationAddress += size;
		}
	}

	size_t dataStart = outputData.size();
	outputData.reserveBytes((size_t)(relocationAddress-start));

	// load sections
	bool error = false;
	for (ElfRelocatorSection& entry: file.sections)
	{
		ElfSection* section = entry.section;
		size_t index = entry.index;

		if (section->getType() == SHT_NOBITS)
		{
			// reserveBytes initialized the data to 0 already
			continue;
		}

		ByteArray sectionData = section->getData();

		// relocate if necessary
		ElfSection* relSection = entry.relSection;
		if (relSection != nullptr)
		{
			bool isRela = relSection->getType() == SHT_RELA;
			int structSize = isRela ? sizeof(Elf32_Rela) : sizeof(Elf32_Rel);

			std::vector<RelocationAction> relocationActions;
			for (unsigned int relOffset = 0; relOffset < relSection->getSize(); relOffset += structSize)
			{
				Elf32_Rela rela;
				loadRelocation(rela, isRela, relSection->getData(), relOffset, elf->getEndianness());
				int pos = rela.r_offset;

				if (relocator->isDummyRelocationType(rela.getType()))
					continue;

				int symNum = rela.getSymbolNum();
				if (symNum <= 0)
				{
					Logger::queueError(Logger::Warning, "Invalid symbol num %06X",symNum);
					error = true;
					continue;
				}

				Elf32_Sym sym;
				elf->getSymbol(sym, symNum);
				int symSection = sym.st_shndx;

				RelocationData relData;
				relData.opcode = sectionData.getDoubleWord(pos, elf->getEndianness());
				relData.opcodeOffset = pos+relocationOffsets[index];
				relData.addend = rela.r_addend;
				relocator->setSymbolAddress(relData,sym.st_value,sym.st_info & 0xF);

				// externs?
				if (sym.st_shndx == 0)
				{
					if (sym.st_name == 0)
					{
						Logger::queueError(Logger::Error, "Symbol without a name");
						error = true;
						continue;
					}

					std::string symName = toLowercase(elf->getStrTableString(sym.st_name));

					std::shared_ptr<Label> label = Global.symbolTable.getLabel(Identifier(symName),-1,-1);
					if (label == nullptr)
					{
						Logger::queueError(Logger::Error, "Invalid external symbol %s",symName);
						error = true;
						continue;
					}
					if (!label->isDefined())
					{
						Logger::queueError(Logger::Error, "Undefined external symbol %s in file %s",symName,file.name);
						error = true;
						continue;
					}

					relData.relocationBase = (unsigned int) label->getValue();
					relData.targetSymbolType = label->isData() ? STT_OBJECT : STT_FUNC;
					relData.targetSymbolInfo = label->getInfo();
				} else {
					relData.relocationBase = relocationOffsets[symSection]+relData.symbolAddress;
				}

				std::vector<std::string> errors;
				if (!relocator->relocateOpcode(rela.getType(), relData, relocationActions, errors))
				{
					for (const std::string& error : errors)
					{
						Logger::queueError(Logger::Error, error);
					}
					error = true;
					continue;
				}
			}

			// finish any dangling relocations
			std::vector<std::string> errors;
			if (!relocator->finish(relocationActions, errors))
			{
				for (const std::string& error : errors)
				{
					Logger::queueError(Logger::Error, error);
				}
				error = true;
			}

			// now actually write the relocated values
			for (const RelocationAction& action : relocationActions)
			{
				sectionData.replaceDoubleWord(action.offset-relocationOffsets[index], action.newValue, elf->getEndianness());
			}
		}

		size_t arrayStart = (size_t) (dataStart+relocationOffsets[index]-start);
		memcpy(outputData.data(arrayStart),sectionData.data(),sectionData.size());
	}

	// now update symbols
	for (ElfRelocatorSymbol& sym: file.symbols)
	{
		int64_t oldAddress = sym.relocatedAddress;

		switch (sym.section)
		{
		case SHN_ABS:		// address does not change
			sym.relocatedAddress = sym.relativeAddress;
			break;
		case SHN_COMMON:	// needs to be allocated. relativeAddress gives alignment constraint
			{
				int64_t start = relocationAddress;

				while (relocationAddress % sym.relativeAddress)
					relocationAddress++;

				sym.relocatedAddress = relocationAddress;
				relocationAddress += sym.size;
				outputData.reserveBytes((size_t)(relocationAddress-start));
			}
			break;
		default:			// normal relocated symbol
			sym.relocatedAddress = sym.relativeAddress+relocationOffsets[sym.section];
			break;
		}

		if (sym.label != nullptr)
			sym.label->setValue(sym.relocatedAddress);

		if (oldAddress != sym.relocatedAddress)
			dataChanged = true;
	}

	return !error;
}

bool ElfRelocator::relocate(int64_t& memoryAddress)
{
	int oldCrc = getCrc32(outputData.data(),outputData.size());
	outputData.clear();
	dataChanged = false;

	bool error = false;
	int64_t start = memoryAddress;

	for (ElfRelocatorFile& file: files)
	{
		if (!relocateFile(file,memoryAddress))
			error = true;
	}

	int newCrc = getCrc32(outputData.data(),outputData.size());
	if (oldCrc != newCrc)
		dataChanged = true;

	memoryAddress -= start;
	return !error;
}

void ElfRelocator::writeSymbols(SymbolData& symData) const
{
	for (const ElfRelocatorFile& file: files)
	{
		for (const ElfRelocatorSymbol& sym: file.symbols)
		{
			symData.addLabel(sym.relocatedAddress, sym.name);

			switch (sym.type)
			{
			case STT_OBJECT:
				symData.addData(sym.relocatedAddress,sym.size,SymbolData::Data8);
				break;
			case STT_FUNC:
				symData.startFunction(sym.relocatedAddress);
				symData.endFunction(sym.relocatedAddress+sym.size);
				break;
			}
		}
	}
}

std::unique_ptr<CAssemblerCommand> IElfRelocator::generateCtorStub(std::vector<ElfRelocatorCtor> &ctors)
{
	return nullptr;
}

#pragma region "File: Core/Assembler.cpp"


#include <thread>

bool encodeAssembly(std::unique_ptr<CAssemblerCommand> content, SymbolData& symData, TempData& tempData)
{
	bool Revalidate;

	Mips.Pass2();

	ValidateState validation;
	do	// loop until everything is constant
	{
		Logger::clearQueue();
		Revalidate = false;

		if (validation.passes >= 100)
		{
			Logger::queueError(Logger::Error, "Stuck in infinite validation loop");
			break;
		}

		g_fileManager->reset();
		Allocations::clearSubAreas();

#ifdef _DEBUG
		if (!Logger::isSilent())
			printf("Validate %d...\n",validation.passes);
#endif

		if (Global.memoryMode)
			g_fileManager->openFile(Global.memoryFile,true);

		Revalidate = content->Validate(validation);

		Mips.Revalidate();

		if (Global.memoryMode)
			g_fileManager->closeFile();

		validation.passes++;
	} while (Revalidate);

	Allocations::validateOverlap();

	Logger::printQueue();
	if (Logger::hasError())
	{
		return false;
	}

#ifdef _DEBUG
	if (!Logger::isSilent())
		printf("Encode...\n");
#endif

	// and finally encode
	if (Global.memoryMode)
		g_fileManager->openFile(Global.memoryFile,false);

	auto writeTempData = [&]()
	{
		tempData.start();
		if (tempData.isOpen())
			content->writeTempData(tempData);
		tempData.end();
	};

	auto writeSymData = [&]()
	{
		content->writeSymData(symData);
		symData.write();
	};

	// writeTempData, writeSymData and encode all access the same
	// memory but never change, so they can run in parallel
	if (Global.multiThreading)
	{
		std::thread tempThread(writeTempData);
		std::thread symThread(writeSymData);

		content->Encode();

		tempThread.join();
		symThread.join();
	} else {
		writeTempData();
		writeSymData();
		content->Encode();
	}

	if (g_fileManager->hasOpenFile())
	{
		if (!Global.memoryMode)
			Logger::printError(Logger::Warning, "File not closed");
		g_fileManager->closeFile();
	}

	if (Logger::hasError())
	{
		return false;
	}

	return true;
}

static void printStats(const AllocationStats &stats)
{
	Logger::printLine("Total areas and regions: %lld / %lld", stats.totalUsage, stats.totalSize);
	Logger::printLine("Total regions: %lld / %lld", stats.sharedUsage, stats.sharedSize);
	Logger::printLine("Largest area or region: 0x%08llX, %lld / %lld", stats.largestPosition, stats.largestUsage, stats.largestSize);

	int64_t startFreePosition = stats.largestFreePosition + stats.largestFreeUsage;
	Logger::printLine("Most free area or region: 0x%08llX, %lld / %lld (free at 0x%08llX)", stats.largestFreePosition, stats.largestFreeUsage, stats.largestFreeSize, startFreePosition);
	int64_t startSharedFreePosition = stats.sharedFreePosition + stats.sharedFreeUsage;
	Logger::printLine("Most free region: 0x%08llX, %lld / %lld (free at 0x%08llX)", stats.sharedFreePosition, stats.sharedFreeUsage, stats.sharedFreeSize, startSharedFreePosition);

	if (stats.totalPoolSize != 0)
	{
		Logger::printLine("Total pool size: %lld", stats.totalPoolSize);
		Logger::printLine("Largest pool: 0x%08llX, %lld", stats.largestPoolPosition, stats.largestPoolSize);
	}
}

bool runArmips(ArmipsArguments& settings)
{
	// initialize and reset global data
	Global.Section = 0;
	Global.nocash = false;
	Global.FileInfo.TotalLineCount = 0;
	Global.relativeInclude = false;
	Global.multiThreading = true;
	Architecture::setCurrent(InvalidArchitecture);

	Tokenizer::clearEquValues();
	Logger::clear();
	Allocations::clear();
	Global.Table.clear();
	Global.symbolTable.clear();

	Global.fileList.clear();
	Global.FileInfo.TotalLineCount = 0;
	Global.FileInfo.LineNumber = 0;
	Global.FileInfo.FileNum = 0;

	ExpressionFunctionHandler::instance().reset();
	registerExpressionFunctions(ExpressionFunctionHandler::instance());

	// process settings
	Parser parser;
	SymbolData symData;
	TempData tempData;

	Logger::setSilent(settings.silent);
	Logger::setErrorOnWarning(settings.errorOnWarning);

	if (!settings.symFileName.empty())
		symData.setNocashSymFileName(settings.symFileName, settings.symFileVersion);

	if (!settings.tempFileName.empty())
		tempData.setFileName(settings.tempFileName);

	Token token;
	for (size_t i = 0; i < settings.equList.size(); i++)
	{
		parser.addEquation(token, settings.equList[i].name, settings.equList[i].value);
	}

	Global.symbolTable.addLabels(settings.labels);
	for (const LabelDefinition& label : settings.labels)
	{
		symData.addLabel(label.value, label.name.string());
	}

	if (Logger::hasError())
		return false;

	// run assembler
	TextFile input;
	switch (settings.mode)
	{
	case ArmipsMode::FILE:
		Global.memoryMode = false;
		if (!input.open(settings.inputFileName,TextFile::Read))
		{
			Logger::printError(Logger::Error, "Could not open file");
			return false;
		}
		break;
	case ArmipsMode::MEMORY:
		Global.memoryMode = true;
		Global.memoryFile = settings.memoryFile;
		input.openMemory(settings.content);
		break;
	}

	std::unique_ptr<CAssemblerCommand> content = parser.parseFile(input);
	Logger::printQueue();

	bool result = !Logger::hasError();
	if (result && content != nullptr)
		result = encodeAssembly(std::move(content), symData, tempData);

	if (g_fileManager->hasOpenFile())
	{
		if (!Global.memoryMode)
			Logger::printError(Logger::Warning, "File not closed");
		g_fileManager->closeFile();
	}

	// return errors
	if (settings.errorsResult != nullptr)
	{
		std::vector<std::string> errors = Logger::getErrors();
		for (size_t i = 0; i < errors.size(); i++)
			settings.errorsResult->push_back(errors[i]);
	}

	if (settings.showStats)
		printStats(Allocations::collectStats());

	if (Logger::hasError())
	{
		return false;
	}

	return result;
}

#pragma region "File: Core/Common.cpp"


#include <sys/stat.h>

FileManager fileManager;
FileManager* g_fileManager = &fileManager;

tGlobal Global;

void FileList::add(const fs::path &path)
{
	_entries.emplace_back(path);
}

const fs::path &FileList::path(int fileIndex) const
{
	return _entries[size_t(fileIndex)].path();
}

const fs::path &FileList::relative_path(int fileIndex) const
{
	return _entries[size_t(fileIndex)].relativePath();
}

const std::string &FileList::string(int fileIndex) const
{
	return _entries[size_t(fileIndex)].string();
}

const std::string &FileList::relativeString(int fileIndex) const
{
	return _entries[size_t(fileIndex)].relativeString();
}

size_t FileList::size() const
{
	return _entries.size();
}

void FileList::clear()
{
	_entries.clear();
}

FileList::Entry::Entry(const fs::path &path) :
	_path(path),
	_relativePath(path.lexically_proximate(fs::current_path())),
	_string(_path.u8string()),
	_relativeString(_relativePath.generic_u8string())
{
}

const fs::path &FileList::Entry::path() const
{
	return _path;
}

const fs::path &FileList::Entry::relativePath() const
{
	return _relativePath;
}

const std::string &FileList::Entry::string() const
{
	return _string;
}

const std::string &FileList::Entry::relativeString() const
{
	return _relativeString;
}

fs::path getFullPathName(const fs::path& path)
{
	if (Global.relativeInclude && !path.is_absolute())
	{
		const fs::path &source = Global.fileList.path(Global.FileInfo.FileNum);
		return fs::absolute(source.parent_path() / path).lexically_normal();
	}
	else
	{
		return fs::absolute(path).lexically_normal();
	}
}

bool checkLabelDefined(const Identifier& labelName, int section)
{
	std::shared_ptr<Label> label = Global.symbolTable.getLabel(labelName,Global.FileInfo.FileNum,section);
	return label->isDefined();
}

bool checkValidLabelName(const Identifier& labelName)
{
	return Global.symbolTable.isValidSymbolName(labelName);
}

bool isPowerOfTwo(int64_t n)
{
	if (n == 0) return false;
	return !(n & (n - 1));
}

#pragma region "File: Core/Expression.cpp"


namespace
{
	std::string to_string(int64_t value)
	{
		return tfm::format("%d", value);
	}

	std::string to_string(double value)
	{
		return tfm::format("%#.17g", value);
	}
}

enum class ExpressionValueCombination
{
	II = (int(ExpressionValueType::Integer) << 2) | (int(ExpressionValueType::Integer) << 0),
	IF = (int(ExpressionValueType::Integer) << 2) | (int(ExpressionValueType::Float)   << 0),
	FI = (int(ExpressionValueType::Float)   << 2) | (int(ExpressionValueType::Integer) << 0),
	FF = (int(ExpressionValueType::Float)   << 2) | (int(ExpressionValueType::Float)   << 0),
	IS = (int(ExpressionValueType::Integer) << 2) | (int(ExpressionValueType::String)  << 0),
	FS = (int(ExpressionValueType::Float)   << 2) | (int(ExpressionValueType::String)  << 0),
	SI = (int(ExpressionValueType::String)  << 2) | (int(ExpressionValueType::Integer) << 0),
	SF = (int(ExpressionValueType::String)  << 2) | (int(ExpressionValueType::Float)   << 0),
	SS = (int(ExpressionValueType::String)  << 2) | (int(ExpressionValueType::String)  << 0),
};

ExpressionValueCombination getValueCombination(ExpressionValueType a, ExpressionValueType b)
{
	return (ExpressionValueCombination) ((int(a) << 2) | (int(b) << 0));
}

ExpressionValue ExpressionValue::operator+(const ExpressionValue& other) const
{
	ExpressionValue result;
	switch (getValueCombination(type,other.type))
	{
	case ExpressionValueCombination::II:
		result.type = ExpressionValueType::Integer;
		result.intValue = intValue + other.intValue;
		break;
	case ExpressionValueCombination::FI:
		result.type = ExpressionValueType::Float;
		result.floatValue = floatValue + other.intValue;
		break;
	case ExpressionValueCombination::IF:
		result.type = ExpressionValueType::Float;
		result.floatValue = intValue + other.floatValue;
		break;
	case ExpressionValueCombination::FF:
		result.type = ExpressionValueType::Float;
		result.floatValue = floatValue + other.floatValue;
		break;
	case ExpressionValueCombination::IS:
		result.type = ExpressionValueType::String;
		result.strValue = StringLiteral(to_string(intValue)) + other.strValue;
		break;
	case ExpressionValueCombination::FS:
		result.type = ExpressionValueType::String;
		result.strValue = StringLiteral(to_string(floatValue)) + other.strValue;
		break;
	case ExpressionValueCombination::SI:
		result.type = ExpressionValueType::String;
		result.strValue = strValue + to_string(other.intValue);
		break;
	case ExpressionValueCombination::SF:
		result.type = ExpressionValueType::String;
		result.strValue = strValue + to_string(other.floatValue);
		break;
	case ExpressionValueCombination::SS:
		result.type = ExpressionValueType::String;
		result.strValue = strValue + other.strValue;
		break;
	}

	return result;
}

ExpressionValue ExpressionValue::operator-(const ExpressionValue& other) const
{
	ExpressionValue result;
	switch (getValueCombination(type,other.type))
	{
	case ExpressionValueCombination::II:
		result.type = ExpressionValueType::Integer;
		result.intValue = intValue - other.intValue;
		break;
	case ExpressionValueCombination::FI:
		result.type = ExpressionValueType::Float;
		result.floatValue = floatValue - other.intValue;
		break;
	case ExpressionValueCombination::IF:
		result.type = ExpressionValueType::Float;
		result.floatValue = intValue - other.floatValue;
		break;
	case ExpressionValueCombination::FF:
		result.type = ExpressionValueType::Float;
		result.floatValue = floatValue - other.floatValue;
		break;
	default:
		break;
	}

	return result;
}

ExpressionValue ExpressionValue::operator*(const ExpressionValue& other) const
{
	ExpressionValue result;
	switch (getValueCombination(type,other.type))
	{
	case ExpressionValueCombination::II:
		result.type = ExpressionValueType::Integer;
		result.intValue = intValue * other.intValue;
		break;
	case ExpressionValueCombination::FI:
		result.type = ExpressionValueType::Float;
		result.floatValue = floatValue * other.intValue;
		break;
	case ExpressionValueCombination::IF:
		result.type = ExpressionValueType::Float;
		result.floatValue = intValue * other.floatValue;
		break;
	case ExpressionValueCombination::FF:
		result.type = ExpressionValueType::Float;
		result.floatValue = floatValue * other.floatValue;
		break;
	default:
		break;
	}

	return result;
}

ExpressionValue ExpressionValue::operator/(const ExpressionValue& other) const
{
	ExpressionValue result;
	switch (getValueCombination(type,other.type))
	{
	case ExpressionValueCombination::II:
		result.type = ExpressionValueType::Integer;
		if (intValue == INT64_MIN && other.intValue == -1){
			result.intValue = INT64_MIN;
			Logger::queueError(Logger::Warning, "Division overflow in expression");
			return result;
		}
		if (other.intValue == 0)
		{
			result.intValue = ~0;
			Logger::queueError(Logger::Warning, "Integer division by zero in expression");
			return result;
		}
		result.intValue = intValue / other.intValue;
		break;
	case ExpressionValueCombination::FI:
		result.type = ExpressionValueType::Float;
		result.floatValue = floatValue / other.intValue;
		break;
	case ExpressionValueCombination::IF:
		result.type = ExpressionValueType::Float;
		result.floatValue = intValue / other.floatValue;
		break;
	case ExpressionValueCombination::FF:
		result.type = ExpressionValueType::Float;
		result.floatValue = floatValue / other.floatValue;
		break;
	default:
		break;
	}

	return result;
}

ExpressionValue ExpressionValue::operator%(const ExpressionValue& other) const
{
	ExpressionValue result;
	switch (getValueCombination(type,other.type))
	{
	case ExpressionValueCombination::II:
		result.type = ExpressionValueType::Integer;
		if (intValue == INT64_MIN && other.intValue == -1){
			result.intValue = 0;
			Logger::queueError(Logger::Warning, "Division overflow in expression");
			return result;
		}
		if (other.intValue == 0)
		{
			result.intValue = intValue;
			Logger::queueError(Logger::Warning, "Integer division by zero in expression");
			return result;
		}
		result.intValue = intValue % other.intValue;
		break;
	default:
		break;
	}

	return result;
}

ExpressionValue ExpressionValue::operator!() const
{
	ExpressionValue result;
	result.type = ExpressionValueType::Integer;

	if (isFloat())
		result.intValue = !floatValue;
	else
		result.intValue = !intValue;

	return result;
}

ExpressionValue ExpressionValue::operator~() const
{
	ExpressionValue result;

	if (isInt())
	{
		result.type = ExpressionValueType::Integer;
		result.intValue = ~intValue;
	}

	return result;
}

ExpressionValue ExpressionValue::operator<<(const ExpressionValue& other) const
{
	ExpressionValue result;
	switch (getValueCombination(type,other.type))
	{
	case ExpressionValueCombination::II:
		result.type = ExpressionValueType::Integer;
		result.intValue = ((uint64_t) intValue) << other.intValue;
		break;
	default:
		break;
	}

	return result;
}

ExpressionValue ExpressionValue::operator>>(const ExpressionValue& other) const
{
	ExpressionValue result;
	switch (getValueCombination(type,other.type))
	{
	case ExpressionValueCombination::II:
		result.type = ExpressionValueType::Integer;
		result.intValue = ((uint64_t) intValue) >> other.intValue;
		break;
	default:
		break;
	}

	return result;
}

bool ExpressionValue::operator<(const ExpressionValue& other) const
{
	switch (getValueCombination(type,other.type))
	{
	case ExpressionValueCombination::II:
		return intValue < other.intValue;
	case ExpressionValueCombination::FI:
		return floatValue < other.intValue;
	case ExpressionValueCombination::IF:
		return intValue < other.floatValue;
	case ExpressionValueCombination::FF:
		return floatValue < other.floatValue;
	case ExpressionValueCombination::SS:
		return strValue < other.strValue;
	default:
		break;
	}

	return false;
}

bool ExpressionValue::operator<=(const ExpressionValue& other) const
{
	switch (getValueCombination(type,other.type))
	{
	case ExpressionValueCombination::II:
		return intValue <= other.intValue;
	case ExpressionValueCombination::FI:
		return floatValue <= other.intValue;
	case ExpressionValueCombination::IF:
		return intValue <= other.floatValue;
	case ExpressionValueCombination::FF:
		return floatValue <= other.floatValue;
	case ExpressionValueCombination::SS:
		return strValue <= other.strValue;
	default:
		break;
	}

	return false;
}

bool ExpressionValue::operator>(const ExpressionValue& other) const
{
	return other < *this;
}

bool ExpressionValue::operator>=(const ExpressionValue& other) const
{
	return other <= *this;
}

bool ExpressionValue::operator==(const ExpressionValue& other) const
{
	switch (getValueCombination(type,other.type))
	{
	case ExpressionValueCombination::II:
		return intValue == other.intValue;
	case ExpressionValueCombination::FI:
		return floatValue == other.intValue;
	case ExpressionValueCombination::IF:
		return intValue == other.floatValue;
	case ExpressionValueCombination::FF:
		return floatValue == other.floatValue;
	case ExpressionValueCombination::IS:
		return StringLiteral(to_string(intValue)) == other.strValue;
	case ExpressionValueCombination::FS:
		return StringLiteral(to_string(floatValue)) == other.strValue;
	case ExpressionValueCombination::SI:
		return strValue == to_string(other.intValue);
	case ExpressionValueCombination::SF:
		return strValue == to_string(other.floatValue);
	case ExpressionValueCombination::SS:
		return strValue == other.strValue;
	}

	return false;
}

bool ExpressionValue::operator!=(const ExpressionValue& other) const
{
	return !(*this == other);
}

ExpressionValue ExpressionValue::operator&(const ExpressionValue& other) const
{
	ExpressionValue result;
	switch (getValueCombination(type,other.type))
	{
	case ExpressionValueCombination::II:
		result.type = ExpressionValueType::Integer;
		result.intValue = intValue & other.intValue;
		break;
	default:
		break;
	}

	return result;
}

ExpressionValue ExpressionValue::operator|(const ExpressionValue& other) const
{
	ExpressionValue result;
	switch (getValueCombination(type,other.type))
	{
	case ExpressionValueCombination::II:
		result.type = ExpressionValueType::Integer;
		result.intValue = intValue | other.intValue;
		break;
	default:
		break;
	}

	return result;
}

ExpressionValue ExpressionValue::operator^(const ExpressionValue& other) const
{
	ExpressionValue result;
	switch (getValueCombination(type,other.type))
	{
	case ExpressionValueCombination::II:
		result.type = ExpressionValueType::Integer;
		result.intValue = intValue ^ other.intValue;
		break;
	default:
		break;
	}

	return result;
}

ExpressionValue ExpressionValue::operator&&(const ExpressionValue& other) const
{
	ExpressionValue result;
	result.type = ExpressionValueType::Integer;

	switch (getValueCombination(type,other.type))
	{
	case ExpressionValueCombination::II:
		result.intValue = intValue && other.intValue;
		break;
	case ExpressionValueCombination::FI:
		result.floatValue = floatValue && other.intValue;
		break;
	case ExpressionValueCombination::IF:
		result.floatValue = intValue && other.floatValue;
		break;
	case ExpressionValueCombination::FF:
		result.floatValue = floatValue && other.floatValue;
		break;
	default:
		break;
	}

	return result;
}

ExpressionValue ExpressionValue::operator||(const ExpressionValue& other) const
{
	ExpressionValue result;
	result.type = ExpressionValueType::Integer;

	switch (getValueCombination(type,other.type))
	{
	case ExpressionValueCombination::II:
		result.intValue = intValue || other.intValue;
		break;
	case ExpressionValueCombination::FI:
		result.floatValue = floatValue || other.intValue;
		break;
	case ExpressionValueCombination::IF:
		result.floatValue = intValue || other.floatValue;
		break;
	case ExpressionValueCombination::FF:
		result.floatValue = floatValue || other.floatValue;
		break;
	default:
		break;
	}

	return result;
}

ExpressionInternal::ExpressionInternal(int64_t value)
	: ExpressionInternal()
{
	type = OperatorType::Integer;
	this->value = value;
}

ExpressionInternal::ExpressionInternal(double value)
	: ExpressionInternal()
{
	type = OperatorType::Float;
	this->value = value;
}

ExpressionInternal::ExpressionInternal(Identifier value)
	: ExpressionInternal()
{
	this->type = OperatorType::Identifier;
	this->value = std::move(value);
	fileNum = Global.FileInfo.FileNum;
	section = Global.Section;
}

ExpressionInternal::ExpressionInternal(StringLiteral value)
	: ExpressionInternal()
{
	this->type = OperatorType::String;
	this->value = std::move(value);
}

ExpressionInternal::ExpressionInternal(const Identifier& name, std::vector<std::unique_ptr<ExpressionInternal>> parameters)
	: ExpressionInternal()
{
	type = OperatorType::FunctionCall;
	this->value = name;
	children = std::move(parameters);
}

void ExpressionInternal::replaceMemoryPos(const Identifier& identifierName)
{
	for (size_t i = 0; i < children.size(); i++)
	{
		if (children[i])
		{
			children[i]->replaceMemoryPos(identifierName);
		}
	}

	if (type == OperatorType::MemoryPos)
	{
		type = OperatorType::Identifier;
		value = identifierName;
		fileNum = Global.FileInfo.FileNum;
		section = Global.Section;
	}
}

ExpressionValue ExpressionInternal::executeFunctionCall()
{
	const auto &functionName = valueAs<Identifier>();

	auto handle = ExpressionFunctionHandler::instance().find(functionName);
	if (!handle)
	{
		Logger::queueError(Logger::Error, "Unknown function \"%s\"", functionName);
		return {};
	}

	if (handle->minParams() > children.size())
	{
		Logger::queueError(Logger::Error, "Not enough parameters for \"%s\" (%d<%d)", functionName, children.size(), handle->minParams());
		return {};
	}

	if (handle->maxParams() < children.size())
	{
		Logger::queueError(Logger::Error, "Too many parameters for \"%s\" (%d>%d)", functionName, children.size(), handle->maxParams());
		return {};
	}

	return handle->execute(children);
}

bool isExpressionFunctionSafe(const Identifier& name, bool inUnknownOrFalseBlock)
{
	auto handle = ExpressionFunctionHandler::instance().find(name);
	if (!handle)
	{
		// well, a function that doesn't exist at least won't change its behavior...
		return true;
	}

	ExpFuncSafety safety = handle->safety();

	if (inUnknownOrFalseBlock && safety == ExpFuncSafety::ConditionalUnsafe)
		return false;

	return safety != ExpFuncSafety::Unsafe;
}

bool ExpressionInternal::simplify(bool inUnknownOrFalseBlock)
{
	// check if this expression can actually be simplified
	// without causing side effects
	switch (type)
	{
	case OperatorType::Identifier:
	case OperatorType::MemoryPos:
	case OperatorType::ToString:
		return false;
	case OperatorType::FunctionCall:
		if (!isExpressionFunctionSafe(valueAs<Identifier>(), inUnknownOrFalseBlock))
			return false;
		break;
	default:
		break;
	}

	// check if the same applies to all children
	bool canSimplify = true;
	for (size_t i = 0; i < children.size(); i++)
	{
		if (children[i] != nullptr && !children[i]->simplify(inUnknownOrFalseBlock))
			canSimplify = false;
	}

	// if so, this expression can be evaluated into a constant
	if (canSimplify)
	{
		ExpressionValue value = evaluate();

		switch (value.type)
		{
		case ExpressionValueType::Integer:
			type = OperatorType::Integer;
			this->value = value.intValue;
			break;
		case ExpressionValueType::Float:
			type = OperatorType::Float;
			this->value = value.floatValue;
			break;
		case ExpressionValueType::String:
			type = OperatorType::String;
			this->value = value.strValue;
			break;
		default:
			type = OperatorType::Invalid;
			break;
		}

		children.clear();
	}

	return canSimplify;
}

ExpressionValue ExpressionInternal::evaluate()
{
	ExpressionValue val;

	std::shared_ptr<Label> label;
	switch (type)
	{
	case OperatorType::Integer:
		val.type = ExpressionValueType::Integer;
		val.intValue = valueAs<int64_t>();
		return val;
	case OperatorType::Float:
		val.type = ExpressionValueType::Float;
		val.floatValue = valueAs<double>();
		return val;
	case OperatorType::Identifier:
		label = Global.symbolTable.getLabel(valueAs<Identifier>(),fileNum,section);
		if (label == nullptr)
		{
			Logger::queueError(Logger::Error, "Invalid label name \"%s\"", valueAs<Identifier>());
			return val;
		}

		if (!label->isDefined())
		{
			Logger::queueError(Logger::Error, "Undefined label \"%s\"",label->getName());
			return val;
		}

		val.type = ExpressionValueType::Integer;
		val.intValue = label->getValue();
		return val;
	case OperatorType::String:
		val.type = ExpressionValueType::String;
		val.strValue = valueAs<StringLiteral>();
		return val;
	case OperatorType::MemoryPos:
		val.type = ExpressionValueType::Integer;
		val.intValue = g_fileManager->getVirtualAddress();
		return val;
	case OperatorType::ToString:
		val.type = ExpressionValueType::String;
		val.strValue = children[0]->toString();
		return val;
	case OperatorType::Add:
		return children[0]->evaluate() + children[1]->evaluate();
	case OperatorType::Sub:
		return children[0]->evaluate() - children[1]->evaluate();
	case OperatorType::Mult:
		return children[0]->evaluate() * children[1]->evaluate();
	case OperatorType::Div:
		return children[0]->evaluate() / children[1]->evaluate();
	case OperatorType::Mod:
		return children[0]->evaluate() % children[1]->evaluate();
	case OperatorType::Neg:
		val.type = ExpressionValueType::Integer;
		val.intValue = 0;
		return val - children[0]->evaluate();
	case OperatorType::LogNot:
		return !children[0]->evaluate();
	case OperatorType::BitNot:
		return ~children[0]->evaluate();
	case OperatorType::LeftShift:
		return children[0]->evaluate() << children[1]->evaluate();
	case OperatorType::RightShift:
		return children[0]->evaluate() >> children[1]->evaluate();
	case OperatorType::Less:
		val.type = ExpressionValueType::Integer;
		val.intValue = children[0]->evaluate() < children[1]->evaluate();
		return val;
	case OperatorType::Greater:
		val.type = ExpressionValueType::Integer;
		val.intValue = children[0]->evaluate() > children[1]->evaluate();
		return val;
	case OperatorType::LessEqual:
		val.type = ExpressionValueType::Integer;
		val.intValue = children[0]->evaluate() <= children[1]->evaluate();
		return val;
	case OperatorType::GreaterEqual:
		val.type = ExpressionValueType::Integer;
		val.intValue = children[0]->evaluate() >= children[1]->evaluate();
		return val;
	case OperatorType::Equal:
		val.type = ExpressionValueType::Integer;
		val.intValue = children[0]->evaluate() == children[1]->evaluate();
		return val;
	case OperatorType::NotEqual:
		val.type = ExpressionValueType::Integer;
		val.intValue = children[0]->evaluate() != children[1]->evaluate();
		return val;
	case OperatorType::BitAnd:
		return children[0]->evaluate() & children[1]->evaluate();
	case OperatorType::BitOr:
		return children[0]->evaluate() | children[1]->evaluate();
	case OperatorType::LogAnd:
		return children[0]->evaluate() && children[1]->evaluate();
	case OperatorType::LogOr:
		return children[0]->evaluate() || children[1]->evaluate();
	case OperatorType::Xor:
		return children[0]->evaluate() ^ children[1]->evaluate();
	case OperatorType::TertiaryIf:
		val.type = ExpressionValueType::Integer;
		val.intValue = 0;
		if (children[0]->evaluate() == val)
			return children[2]->evaluate();
		else
			return children[1]->evaluate();
	case OperatorType::FunctionCall:
		return executeFunctionCall();
	default:
		return val;
	}
}

static std::string escapeString(const std::string& text)
{
	std::string result = text;
	replaceAll(result,R"(\)",R"(\\)");
	replaceAll(result,R"(")",R"(\")");

	return tfm::format(R"("%s")",text);
}

std::string ExpressionInternal::formatFunctionCall()
{
	std::string text = valueAs<StringLiteral>().string() + "(";

	for (size_t i = 0; i < children.size(); i++)
	{
		if (i != 0)
			text += ",";
		text += children[i]->toString();
	}

	return text + ")";
}

std::string ExpressionInternal::toString()
{
	switch (type)
	{
	case OperatorType::Integer:
		return tfm::format("%d",valueAs<int64_t>());
	case OperatorType::Float:
		return tfm::format("%g",valueAs<double>());
	case OperatorType::Identifier:
		return valueAs<Identifier>().string();
	case OperatorType::String:
		return escapeString(valueAs<StringLiteral>().string());
	case OperatorType::MemoryPos:
		return ".";
	case OperatorType::Add:
		return tfm::format("(%s + %s)",children[0]->toString(),children[1]->toString());
	case OperatorType::Sub:
		return tfm::format("(%s - %s)",children[0]->toString(),children[1]->toString());
	case OperatorType::Mult:
		return tfm::format("(%s * %s)",children[0]->toString(),children[1]->toString());
	case OperatorType::Div:
		return tfm::format("(%s / %s)",children[0]->toString(),children[1]->toString());
	case OperatorType::Mod:
		return tfm::format("(%s %% %s)",children[0]->toString(),children[1]->toString());
	case OperatorType::Neg:
		return tfm::format("(-%s)",children[0]->toString());
	case OperatorType::LogNot:
		return tfm::format("(!%s)",children[0]->toString());
	case OperatorType::BitNot:
		return tfm::format("(~%s)",children[0]->toString());
	case OperatorType::LeftShift:
		return tfm::format("(%s << %s)",children[0]->toString(),children[1]->toString());
	case OperatorType::RightShift:
		return tfm::format("(%s >> %s)",children[0]->toString(),children[1]->toString());
	case OperatorType::Less:
		return tfm::format("(%s < %s)",children[0]->toString(),children[1]->toString());
	case OperatorType::Greater:
		return tfm::format("(%s > %s)",children[0]->toString(),children[1]->toString());
	case OperatorType::LessEqual:
		return tfm::format("(%s <= %s)",children[0]->toString(),children[1]->toString());
	case OperatorType::GreaterEqual:
		return tfm::format("(%s >= %s)",children[0]->toString(),children[1]->toString());
	case OperatorType::Equal:
		return tfm::format("(%s == %s)",children[0]->toString(),children[1]->toString());
	case OperatorType::NotEqual:
		return tfm::format("(%s != %s)",children[0]->toString(),children[1]->toString());
	case OperatorType::BitAnd:
		return tfm::format("(%s & %s)",children[0]->toString(),children[1]->toString());
	case OperatorType::BitOr:
		return tfm::format("(%s | %s)",children[0]->toString(),children[1]->toString());
	case OperatorType::LogAnd:
		return tfm::format("(%s && %s)",children[0]->toString(),children[1]->toString());
	case OperatorType::LogOr:
		return tfm::format("(%s || %s)",children[0]->toString(),children[1]->toString());
	case OperatorType::Xor:
		return tfm::format("(%s ^ %s)",children[0]->toString(),children[1]->toString());
	case OperatorType::TertiaryIf:
		return tfm::format("(%s ? %s : %s)",children[0]->toString(),children[1]->toString(),children[2]->toString());
	case OperatorType::ToString:
		return tfm::format("(%s%s)",convertUnicodeCharToUtf8(u'\u00B0'),children[0]->toString());
	case OperatorType::FunctionCall:
		return formatFunctionCall();
	default:
		return "";
	}
}

Expression::Expression(std::unique_ptr<ExpressionInternal> exp, bool inUnknownOrFalseBlock) :
	expression(std::move(exp))
{
	if (expression)
		constExpression = expression->simplify(inUnknownOrFalseBlock);
}

ExpressionValue Expression::evaluate()
{
	if (expression == nullptr)
	{
		ExpressionValue invalid;
		return invalid;
	}

	return expression->evaluate();
}

void Expression::replaceMemoryPos(const Identifier& identifierName)
{
	if (expression != nullptr)
		expression->replaceMemoryPos(identifierName);
}

bool Expression::evaluateString(StringLiteral &dest, bool convert)
{
	if (expression == nullptr)
		return false;

	ExpressionValue value = expression->evaluate();
	if (convert && value.isInt())
	{
		dest = to_string(value.intValue);
		return true;
	}

	if (convert && value.isFloat())
	{
		dest = to_string(value.floatValue);
		return true;
	}

	if (!value.isString())
		return false;

	dest = value.strValue;
	return true;
}

bool Expression::evaluateIdentifier(Identifier &dest)
{
	if (expression == nullptr || !expression->isIdentifier())
		return false;

	dest = expression->getIdentifier();
	return true;
}

std::string Expression::toString()
{
	return expression != nullptr ? expression->toString() : "";
}

Expression createConstExpression(int64_t value)
{
	return Expression(std::make_unique<ExpressionInternal>(value), false);
}

#pragma region "File: Core/ExpressionFunctions.cpp"


#include <cmath>

#if ARMIPS_REGEXP
#include <regex>
#endif

#if defined(__clang__)
#if __has_feature(cxx_exceptions)
#define ARMIPS_EXCEPTIONS 1
#else
#define ARMIPS_EXCEPTIONS 0
#endif
#elif defined(_MSC_VER) && defined(_CPPUNWIND)
#define ARMIPS_EXCEPTIONS 1
#elif defined(__EXCEPTIONS) || defined(__cpp_exceptions)
#define ARMIPS_EXCEPTIONS 1
#else
#define ARMIPS_EXCEPTIONS 0
#endif

bool getExpFuncParameter(const std::vector<ExpressionValue>& parameters, size_t index, int64_t& dest,
	const Identifier &funcName, bool optional)
{
	if (optional && index >= parameters.size())
		return true;

	if (index >= parameters.size() || !parameters[index].isInt())
	{
		Logger::queueError(Logger::Error, "Invalid parameter %d for %s: expecting integer",index+1,funcName);
		return false;
	}

	dest = parameters[index].intValue;
	return true;
}

bool getExpFuncParameter(const std::vector<ExpressionValue>& parameters, size_t index, const StringLiteral*& dest,
	const Identifier &funcName, bool optional)
{
	if (optional && index >= parameters.size())
		return true;

	if (index >= parameters.size() || !parameters[index].isString())
	{
		Logger::queueError(Logger::Error, "Invalid parameter %d for %s: expecting string",index+1,funcName);
		return false;
	}

	dest = &parameters[index].strValue;
	return true;
}

#define GET_PARAM(params,index,dest) \
	if (getExpFuncParameter(params,index,dest,funcName,false) == false) \
		return ExpressionValue();
#define GET_OPTIONAL_PARAM(params,index,dest,defaultValue) \
	dest = defaultValue; \
	if (getExpFuncParameter(params,index,dest,funcName,true) == false) \
		return ExpressionValue();


ExpressionValue expFuncVersion(const Identifier &funcName, const std::vector<ExpressionValue>& parameters)
{
	int64_t value = ARMIPS_VERSION_MAJOR*1000 + ARMIPS_VERSION_MINOR*10 + ARMIPS_VERSION_REVISION;
	return ExpressionValue(value);
}

ExpressionValue expFuncEndianness(const Identifier &funcName, const std::vector<ExpressionValue>& parameters)
{
	ExpressionValue result;
	result.type = ExpressionValueType::String;

	switch (g_fileManager->getEndianness())
	{
	case Endianness::Little:
		return ExpressionValue(StringLiteral("little"));
	case Endianness::Big:
		return ExpressionValue(StringLiteral("big"));
	}

	return ExpressionValue();
}

ExpressionValue expFuncOutputName(const Identifier &funcName, const std::vector<ExpressionValue>& parameters)
{
	std::shared_ptr<AssemblerFile> file = g_fileManager->getOpenFile();
	if (file == nullptr)
	{
		Logger::queueError(Logger::Error, "outputName: no file opened");
		return ExpressionValue();
	}

	std::string value = file->getFileName().u8string();
	return ExpressionValue(value);
}

ExpressionValue expFuncFileExists(const Identifier &funcName, const std::vector<ExpressionValue>& parameters)
{
	const StringLiteral* fileName;
	GET_PARAM(parameters,0,fileName);

	auto fullName = getFullPathName(fileName->path());
	return ExpressionValue(fs::exists(fullName) ? INT64_C(1) : INT64_C(0));
}

ExpressionValue expFuncFileSize(const Identifier &funcName, const std::vector<ExpressionValue>& parameters)
{
	const StringLiteral* fileName;
	GET_PARAM(parameters,0,fileName);

	auto fullName = getFullPathName(fileName->path());

	std::error_code error;
	return ExpressionValue(static_cast<int64_t>(fs::file_size(fullName, error)));
}

ExpressionValue expFuncToString(const Identifier &funcName, const std::vector<ExpressionValue>& parameters)
{
	ExpressionValue result;

	switch (parameters[0].type)
	{
	case ExpressionValueType::String:
		result.strValue = parameters[0].strValue;
		break;
	case ExpressionValueType::Integer:
		result.strValue = tfm::format("%d",parameters[0].intValue);
		break;
	case ExpressionValueType::Float:
		result.strValue = tfm::format("%#.17g",parameters[0].floatValue);
		break;
	default:
		return result;
	}

	result.type = ExpressionValueType::String;
	return result;
}

ExpressionValue expFuncToHex(const Identifier &funcName, const std::vector<ExpressionValue>& parameters)
{
	int64_t value, digits;
	GET_PARAM(parameters,0,value);
	GET_OPTIONAL_PARAM(parameters,1,digits,8);

	return ExpressionValue(tfm::format("%0*X",digits,value));
}

ExpressionValue expFuncInt(const Identifier &funcName, const std::vector<ExpressionValue>& parameters)
{
	ExpressionValue result;

	switch (parameters[0].type)
	{
	case ExpressionValueType::Integer:
		result.intValue = parameters[0].intValue;
		break;
	case ExpressionValueType::Float:
		result.intValue = (int64_t) parameters[0].floatValue;
		break;
	default:
		return result;
	}

	result.type = ExpressionValueType::Integer;
	return result;
}

ExpressionValue expFuncRound(const Identifier &funcName, const std::vector<ExpressionValue>& parameters)
{
	ExpressionValue result;

	switch (parameters[0].type)
	{
	case ExpressionValueType::Integer:
		result.intValue = parameters[0].intValue;
		break;
	case ExpressionValueType::Float:
		result.intValue = llround(parameters[0].floatValue);
		break;
	default:
		return result;
	}

	result.type = ExpressionValueType::Integer;
	return result;
}

ExpressionValue expFuncFloat(const Identifier &funcName, const std::vector<ExpressionValue>& parameters)
{
	ExpressionValue result;

	switch (parameters[0].type)
	{
	case ExpressionValueType::Integer:
		result.floatValue = (double) parameters[0].intValue;
		break;
	case ExpressionValueType::Float:
		result.floatValue = parameters[0].floatValue;
		break;
	default:
		return result;
	}

	result.type = ExpressionValueType::Float;
	return result;
}

ExpressionValue expFuncFrac(const Identifier &funcName, const std::vector<ExpressionValue>& parameters)
{
	ExpressionValue result;
	double intPart;

	switch (parameters[0].type)
	{
	case ExpressionValueType::Float:
		result.floatValue = modf(parameters[0].floatValue,&intPart);
		break;
	default:
		return result;
	}

	result.type = ExpressionValueType::Float;
	return result;
}

ExpressionValue expFuncMin(const Identifier &funcName, const std::vector<ExpressionValue>& parameters)
{
	ExpressionValue result;
	double floatMin, floatCur;
	int64_t intMin, intCur;

	floatCur = floatMin = std::numeric_limits<double>::max();
	intCur = intMin = std::numeric_limits<int64_t>::max();
	bool isInt = true;

	for (size_t i = 0; i < parameters.size(); i++)
	{
		switch (parameters[i].type)
		{
		case ExpressionValueType::Integer:
			intCur = parameters[i].intValue;
			floatCur = (double)parameters[i].intValue;
			break;
		case ExpressionValueType::Float:
			floatCur = parameters[i].floatValue;
			isInt = false;
			break;
		default:
			return result;
		}

		if (intCur < intMin)
			intMin = intCur;
		if (floatCur < floatMin)
			floatMin = floatCur;
	}

	if (isInt)
	{
		result.intValue = intMin;
		result.type = ExpressionValueType::Integer;
	}
	else
	{
		result.floatValue = floatMin;
		result.type = ExpressionValueType::Float;
	}

	return result;
}

ExpressionValue expFuncMax(const Identifier &funcName, const std::vector<ExpressionValue>& parameters)
{
	ExpressionValue result;
	double floatMax, floatCur;
	int64_t intMax, intCur;

	floatCur = floatMax = std::numeric_limits<double>::min();
	intCur = intMax = std::numeric_limits<int64_t>::min();
	bool isInt = true;

	for (size_t i = 0; i < parameters.size(); i++)
	{
		switch (parameters[i].type)
		{
		case ExpressionValueType::Integer:
			intCur = parameters[i].intValue;
			floatCur = (double)parameters[i].intValue;
			break;
		case ExpressionValueType::Float:
			floatCur = parameters[i].floatValue;
			isInt = false;
			break;
		default:
			return result;
		}

		if (intCur > intMax)
			intMax = intCur;
		if (floatCur > floatMax)
			floatMax = floatCur;
	}

	if (isInt)
	{
		result.intValue = intMax;
		result.type = ExpressionValueType::Integer;
	}
	else
	{
		result.floatValue = floatMax;
		result.type = ExpressionValueType::Float;
	}

	return result;
}

ExpressionValue expFuncAbs(const Identifier &funcName, const std::vector<ExpressionValue>& parameters)
{
	ExpressionValue result;

	switch (parameters[0].type)
	{
	case ExpressionValueType::Float:
		result.type = ExpressionValueType::Float;
		result.floatValue = fabs(parameters[0].floatValue);
		break;
	case ExpressionValueType::Integer:
		result.type = ExpressionValueType::Integer;
		result.intValue = parameters[0].intValue >= 0 ?
			parameters[0].intValue : -parameters[0].intValue;
		break;
	default:
		break;
	}

	return result;
}

ExpressionValue expFuncStrlen(const Identifier &funcName, const std::vector<ExpressionValue>& parameters)
{
	const StringLiteral* source;
	GET_PARAM(parameters,0,source);

	return ExpressionValue((int64_t)source->size());
}

ExpressionValue expFuncSubstr(const Identifier &funcName, const std::vector<ExpressionValue>& parameters)
{
	int64_t start, count;
	const StringLiteral* source;

	GET_PARAM(parameters,0,source);
	GET_PARAM(parameters,1,start);
	GET_PARAM(parameters,2,count);

	return ExpressionValue(source->string().substr((size_t)start,(size_t)count));
}

#if ARMIPS_REGEXP
ExpressionValue expFuncRegExMatch(const Identifier &funcName, const std::vector<ExpressionValue>& parameters)
{
	const StringLiteral* source;
	const StringLiteral* regexString;

	GET_PARAM(parameters,0,source);
	GET_PARAM(parameters,1,regexString);

#if ARMIPS_EXCEPTIONS
	try
	{
#endif
		std::regex regex(regexString->string());
		bool found = std::regex_match(source->string(),regex);
		return ExpressionValue(found ? INT64_C(1) : INT64_C(0));
#if ARMIPS_EXCEPTIONS
	} catch (std::regex_error&)
	{
		Logger::queueError(Logger::Error, "Invalid regular expression");
		return ExpressionValue();
	}
#endif
}

ExpressionValue expFuncRegExSearch(const Identifier &funcName, const std::vector<ExpressionValue>& parameters)
{
	const StringLiteral* source;
	const StringLiteral* regexString;

	GET_PARAM(parameters,0,source);
	GET_PARAM(parameters,1,regexString);

#if ARMIPS_EXCEPTIONS
	try
	{
#endif
		std::regex regex(regexString->string());
		bool found = std::regex_search(source->string(),regex);
		return ExpressionValue(found ? INT64_C(1) : INT64_C(0));
#if ARMIPS_EXCEPTIONS
	} catch (std::regex_error&)
	{
		Logger::queueError(Logger::Error, "Invalid regular expression");
		return ExpressionValue();
	}
#endif
}

ExpressionValue expFuncRegExExtract(const Identifier &funcName, const std::vector<ExpressionValue>& parameters)
{
	const StringLiteral* source;
	const StringLiteral* regexString;
	int64_t matchIndex;

	GET_PARAM(parameters,0,source);
	GET_PARAM(parameters,1,regexString);
	GET_OPTIONAL_PARAM(parameters,2,matchIndex,0);

#if ARMIPS_EXCEPTIONS
	try
	{
#endif
		std::regex regex(regexString->string());
		std::smatch result;
		bool found = std::regex_search(source->string(),result,regex);
		if (!found || (size_t)matchIndex >= result.size())
		{
			Logger::queueError(Logger::Error, "Capture group index %d does not exist",matchIndex);
			return ExpressionValue();
		}

		return ExpressionValue(result[(size_t)matchIndex].str());
#if ARMIPS_EXCEPTIONS
	} catch (std::regex_error&)
	{
		Logger::queueError(Logger::Error, "Invalid regular expression");
		return ExpressionValue();
	}
#endif
}
#endif

ExpressionValue expFuncFind(const Identifier &funcName, const std::vector<ExpressionValue>& parameters)
{
	int64_t start;
	const StringLiteral* source;
	const StringLiteral* value;

	GET_PARAM(parameters,0,source);
	GET_PARAM(parameters,1,value);
	GET_OPTIONAL_PARAM(parameters,2,start,0);

	size_t pos = source->string().find(value->string(),(size_t)start);
	return ExpressionValue(pos == std::string::npos ? INT64_C(-1) : (int64_t) pos);
}

ExpressionValue expFuncRFind(const Identifier &funcName, const std::vector<ExpressionValue>& parameters)
{
	int64_t start;
	const StringLiteral* source;
	const StringLiteral* value;

	GET_PARAM(parameters,0,source);
	GET_PARAM(parameters,1,value);
	GET_OPTIONAL_PARAM(parameters,2,start,std::string::npos);

	size_t pos = source->string().rfind(value->string(),(size_t)start);
	return ExpressionValue(pos == std::string::npos ? INT64_C(-1) : (int64_t) pos);
}


template<typename T>
ExpressionValue expFuncRead(const Identifier &funcName, const std::vector<ExpressionValue>& parameters)
{
	const StringLiteral* fileName;
	int64_t pos;

	GET_PARAM(parameters,0,fileName);
	GET_OPTIONAL_PARAM(parameters,1,pos,0);

	auto fullName = getFullPathName(fileName->path());

	fs::ifstream file(fullName, fs::ifstream::in | fs::ifstream::binary);
	if (!file.is_open())
	{
		Logger::queueError(Logger::Error, "Could not open %s",*fileName);
		return ExpressionValue();
	}

	file.seekg(pos);
	if (file.eof() || file.fail())
	{
		Logger::queueError(Logger::Error, "Invalid offset 0x%08X of %s", pos, *fileName);
		return ExpressionValue();
	}

	T buffer;
	file.read(reinterpret_cast<char*>(&buffer), sizeof(T));

	if (file.fail())
	{
		Logger::queueError(Logger::Error, "Failed to read %d byte(s) from offset 0x%08X of %s", sizeof(T), pos, *fileName);
		return ExpressionValue();
	}

	return ExpressionValue((int64_t) buffer);
}

ExpressionValue expFuncReadAscii(const Identifier &funcName, const std::vector<ExpressionValue>& parameters)
{
	const StringLiteral* fileName;
	int64_t start;
	int64_t length;

	GET_PARAM(parameters,0,fileName);
	GET_OPTIONAL_PARAM(parameters,1,start,0);
	GET_OPTIONAL_PARAM(parameters,2,length,0);

	auto fullName = getFullPathName(fileName->path());

	std::error_code error;
	int64_t totalSize = static_cast<int64_t>(fs::file_size(fullName, error));

	if (length == 0 || start+length > totalSize)
		length = totalSize-start;

	fs::ifstream file(fullName, fs::ifstream::in | fs::ifstream::binary);
	if (!file.is_open())
	{
		Logger::queueError(Logger::Error, "Could not open %s", *fileName);
		return ExpressionValue();
	}

	file.seekg(start);
	if (file.eof() || file.fail())
	{
		Logger::queueError(Logger::Error, "Invalid offset 0x%08X of %s", start, *fileName);
		return ExpressionValue();
	}

	char buffer[1024];
	bool stringTerminated = false;
	std::string result;

	for (int64_t progress = 0; !stringTerminated && progress < length; progress += (int64_t) sizeof(buffer))
	{
		auto bytesToRead = (size_t) std::min((int64_t) sizeof(buffer), length - progress);

		file.read(buffer, bytesToRead);
		if (file.fail())
		{
			Logger::queueError(Logger::Error, "Failed to read %d byte(s) from offset 0x%08X of %s", bytesToRead, *fileName);
			return ExpressionValue();
		}

		for (std::streamsize i = 0; i < file.gcount(); i++)
		{
			if (buffer[i] == 0x00)
			{
				stringTerminated = true;
				break;
			}

			if (buffer[i] < 0x20)
			{
				Logger::printError(Logger::Warning, "%s: Non-ASCII character", funcName);
				return ExpressionValue();
			}

			result += buffer[i];
		}
	}

	return ExpressionValue(result);
}

ExpressionValue expLabelFuncDefined(const Identifier &funcName, const std::vector<std::shared_ptr<Label>> &parameters)
{
	if (parameters.empty() || !parameters.front())
	{
		Logger::queueError(Logger::Error, "%s: Invalid parameters", funcName);
		return ExpressionValue();
	}

	return ExpressionValue(parameters.front()->isDefined() ? INT64_C(1) : INT64_C(0));
}

ExpressionValue expLabelFuncOrg(const Identifier &funcName, const std::vector<std::shared_ptr<Label>>& parameters)
{
	// return physical address of label parameter
	if (parameters.size())
	{
		Label* label = parameters.front().get();
		if (!label)
			return ExpressionValue();

		return ExpressionValue(parameters.front()->getValue());
	}

	if(!g_fileManager->hasOpenFile())
	{
		Logger::queueError(Logger::Error, "%s: no file opened", funcName);
		return ExpressionValue();
	}
	return ExpressionValue(g_fileManager->getVirtualAddress());
}

ExpressionValue expLabelFuncOrga(const Identifier &funcName, const std::vector<std::shared_ptr<Label>>& parameters)
{
	// return physical address of label parameter
	if (parameters.size())
	{
		Label* label = parameters.front().get();
		if (!label)
			return ExpressionValue();

		if (!label->hasPhysicalValue())
		{
			Logger::queueError(Logger::Error, "%s: parameter %s has no physical address", funcName, label->getName());
			return ExpressionValue();
		}

		return ExpressionValue(parameters.front()->getPhysicalValue());
	}

	// return current physical address otherwise
	if(!g_fileManager->hasOpenFile())
	{
		Logger::queueError(Logger::Error, "%s: no file opened", funcName);
		return ExpressionValue();
	}
	return ExpressionValue(g_fileManager->getPhysicalAddress());
}

ExpressionValue expLabelFuncHeaderSize(const Identifier &funcName, const std::vector<std::shared_ptr<Label>>& parameters)
{
	// return difference between physical and virtual address of label parameter
	if (parameters.size())
	{
		Label* label = parameters.front().get();
		if (!label)
			return ExpressionValue();

		if (!label->hasPhysicalValue())
		{
			Logger::queueError(Logger::Error, "%s: parameter %s has no physical address", funcName, label->getName());
			return ExpressionValue();
		}

		return ExpressionValue(label->getValue() - label->getPhysicalValue());
	}

	if(!g_fileManager->hasOpenFile())
	{
		Logger::queueError(Logger::Error, "headersize: no file opened");
		return ExpressionValue();
	}
	return ExpressionValue(g_fileManager->getHeaderSize());
}

const ExpressionFunctionEntry expressionFunctions[] = {
	{ "version",       &expFuncVersion,        0, 0,   ExpFuncSafety::Safe},
	{ "endianness",    &expFuncEndianness,     0, 0,   ExpFuncSafety::Unsafe},
	{ "outputname",    &expFuncOutputName,     0, 0,   ExpFuncSafety::Unsafe},
	{ "fileexists",    &expFuncFileExists,     1, 1,   ExpFuncSafety::Safe},
	{ "filesize",      &expFuncFileSize,       1, 1,   ExpFuncSafety::ConditionalUnsafe},
	{ "tostring",      &expFuncToString,       1, 1,   ExpFuncSafety::Safe},
	{ "tohex",         &expFuncToHex,          1, 2,   ExpFuncSafety::Safe},

	{ "int",           &expFuncInt,            1, 1,   ExpFuncSafety::Safe},
	{ "float",         &expFuncFloat,          1, 1,   ExpFuncSafety::Safe},
	{ "frac",          &expFuncFrac,           1, 1,   ExpFuncSafety::Safe},
	{ "abs",           &expFuncAbs,            1, 1,   ExpFuncSafety::Safe},
	{ "round",         &expFuncRound,          1, 1,   ExpFuncSafety::Safe},
	{ "min",           &expFuncMin,            1, 256, ExpFuncSafety::Safe},
	{ "max",           &expFuncMax,            1, 256, ExpFuncSafety::Safe},

	{ "strlen",        &expFuncStrlen,         1, 1,   ExpFuncSafety::Safe},
	{ "substr",        &expFuncSubstr,         3, 3,   ExpFuncSafety::Safe},
#if ARMIPS_REGEXP
	{ "regex_match",   &expFuncRegExMatch,     2, 2,   ExpFuncSafety::Safe},
	{ "regex_search",  &expFuncRegExSearch,    2, 2,   ExpFuncSafety::Safe},
	{ "regex_extract", &expFuncRegExExtract,   2, 3,   ExpFuncSafety::Safe},
#endif
	{ "find",          &expFuncFind,           2, 3,   ExpFuncSafety::Safe},
	{ "rfind",         &expFuncRFind,          2, 3,   ExpFuncSafety::Safe},

	{ "readbyte",      &expFuncRead<uint8_t>,  1, 2,   ExpFuncSafety::ConditionalUnsafe},
	{ "readu8",        &expFuncRead<uint8_t>,  1, 2,   ExpFuncSafety::ConditionalUnsafe},
	{ "readu16",       &expFuncRead<uint16_t>, 1, 2,   ExpFuncSafety::ConditionalUnsafe},
	{ "readu32",       &expFuncRead<uint32_t>, 1, 2,   ExpFuncSafety::ConditionalUnsafe},
	{ "readu64",       &expFuncRead<uint64_t>, 1, 2,   ExpFuncSafety::ConditionalUnsafe},
	{ "reads8",        &expFuncRead<int8_t>,   1, 2,   ExpFuncSafety::ConditionalUnsafe},
	{ "reads16",       &expFuncRead<int16_t>,  1, 2,   ExpFuncSafety::ConditionalUnsafe},
	{ "reads32",       &expFuncRead<int32_t>,  1, 2,   ExpFuncSafety::ConditionalUnsafe},
	{ "reads64",       &expFuncRead<int64_t>,  1, 2,   ExpFuncSafety::ConditionalUnsafe},
	{ "readascii",     &expFuncReadAscii,      1, 3,   ExpFuncSafety::ConditionalUnsafe},
};

const ExpressionLabelFunctionEntry expressionLabelFunctions[] =
{
	{ "defined",    &expLabelFuncDefined,      1, 1, ExpFuncSafety::Unsafe},
	{ "org",        &expLabelFuncOrg,          0, 1, ExpFuncSafety::Unsafe},
	{ "orga",       &expLabelFuncOrga,         0, 1, ExpFuncSafety::Unsafe},
	{ "headersize", &expLabelFuncHeaderSize,   0, 1, ExpFuncSafety::Unsafe},
};

void registerExpressionFunctions(ExpressionFunctionHandler &handler)
{
	for (const auto &func : expressionFunctions)
	{
		handler.addFunction(Identifier(func.name), func.function, func.minParams, func.maxParams, func.safety);
	}

	for (const auto &func : expressionLabelFunctions)
	{
		handler.addLabelFunction(Identifier(func.name), func.function, func.minParams, func.maxParams, func.safety);
	}
}

#pragma region "File: Core/FileManager.cpp"


inline uint64_t swapEndianness64(uint64_t value)
{
	return ((value & 0xFF) << 56) | ((value & 0xFF00) << 40) | ((value & 0xFF0000) << 24) | ((value & 0xFF000000) << 8) |
	((value & 0xFF00000000) >> 8) | ((value & 0xFF0000000000) >> 24) |
	((value & 0xFF000000000000) >> 40) | ((value & 0xFF00000000000000) >> 56);
}

inline uint32_t swapEndianness32(uint32_t value)
{
	return ((value & 0xFF) << 24) | ((value & 0xFF00) << 8) | ((value & 0xFF0000) >> 8) | ((value & 0xFF000000) >> 24);
}

inline uint16_t swapEndianness16(uint16_t value)
{
	return ((value & 0xFF) << 8) | ((value & 0xFF00) >> 8);
}


GenericAssemblerFile::GenericAssemblerFile(const fs::path& fileName, int64_t headerSize, bool overwrite)
	: originalHeaderSize(headerSize), headerSize(headerSize), fileName(fileName)
{
	this->seekPhysical(0);
	mode = overwrite ? Create : Open;
}

GenericAssemblerFile::GenericAssemblerFile(const fs::path& fileName, const fs::path& originalFileName, int64_t headerSize)
	: originalHeaderSize(headerSize), headerSize(headerSize), fileName(fileName), originalName(originalFileName)
{
	this->seekPhysical(0);
	mode = Copy;
}

bool GenericAssemblerFile::open(bool onlyCheck)
{
	std::error_code errorCode;

	headerSize = originalHeaderSize;
	virtualAddress = headerSize;

	auto flagsOpenExisting = fs::ofstream::in | fs::ofstream::out | fs::ofstream::binary;
	auto flagsOverwrite = fs::ofstream::out | fs::ofstream::trunc | fs::ofstream::binary;

	if (!onlyCheck)
	{
		// actually open the file
		switch (mode)
		{
		case Open:
			stream.open(fileName, flagsOpenExisting);
			if (!stream.is_open())
			{
				Logger::printError(Logger::FatalError, "Could not open file %s",fileName.u8string());
				return false;
			}
			return true;

		case Create:
			stream.open(fileName, flagsOverwrite);
			if (!stream.is_open())
			{
				Logger::printError(Logger::FatalError, "Could not create file %s",fileName.u8string());
				return false;
			}
			return true;

		case Copy:
			if (!fs::copy_file(originalName, fileName, fs::copy_options::overwrite_existing, errorCode))
			{
				Logger::printError(Logger::FatalError, "Could not copy file %s",originalName.u8string());
				return false;
			}

			stream.open(fileName, flagsOpenExisting);
			if (!stream.is_open())
			{
				Logger::printError(Logger::FatalError, "Could not create file %s",fileName.u8string());
				return false;
			}
			return true;
		}
	}

	// else only check if it can be done, don't actually do it permanently
	bool exists = false;
	fs::ofstream temp;
	switch (mode)
	{
	case Open:
		temp.open(fileName, flagsOpenExisting);
		if (!temp.is_open())
		{
			Logger::queueError(Logger::FatalError, "Could not open file %s",fileName.u8string());
			return false;
		}
		temp.close();
		return true;

	case Create:
		// open file with writee access. if it didn't exist before, remove it afterwards
		exists = fs::exists(fileName);

		temp.open(fileName, exists ? flagsOpenExisting : flagsOverwrite);
		if (!temp.is_open())
		{
			Logger::queueError(Logger::FatalError, "Could not create file %s",fileName.u8string());
			return false;
		}
		temp.close();

		if (!exists)
			fs::remove(fileName, errorCode);

		return true;

	case Copy:
		// check original file
		temp.open(originalName, flagsOpenExisting);
		if (!temp.is_open())
		{
			Logger::queueError(Logger::FatalError, "Could not copy file %s",originalName.u8string());
			return false;
		}
		temp.close();

		// Check input and output are not the same
		std::error_code ec;
		if (fs::equivalent(originalName, fileName, ec))
		{
			Logger::queueError(Logger::FatalError, "Could not copy file %s", originalName.u8string());
			return false;
		}

		// check new file, same as create
		exists = fs::exists(fileName);

		temp.open(fileName, exists ? flagsOpenExisting : flagsOverwrite);
		if (!temp.is_open())
		{
			Logger::queueError(Logger::FatalError, "Could not create file %s",fileName.u8string());
			return false;
		}
		temp.close();

		if (!exists)
			fs::remove(fileName, errorCode);
		return true;
	}

	return false;
}

bool GenericAssemblerFile::write(void* data, size_t length)
{
	if (!isOpen())
		return false;

	stream.write(reinterpret_cast<const char *>( data ), length);
	virtualAddress += length;
	return !stream.fail();
}

bool GenericAssemblerFile::seekVirtual(int64_t virtualAddress)
{
	if (virtualAddress - headerSize < 0)
	{
		Logger::queueError(Logger::Error, "Seeking to virtual address with negative physical address");
		return false;
	}
	if (virtualAddress < 0)
		Logger::queueError(Logger::Warning, "Seeking to negative virtual address");

	this->virtualAddress = virtualAddress;
	int64_t physicalAddress = virtualAddress-headerSize;

	if (isOpen())
		stream.seekp(physicalAddress);

	return true;
}

bool GenericAssemblerFile::seekPhysical(int64_t physicalAddress)
{
	if (physicalAddress < 0)
	{
		Logger::queueError(Logger::Error, "Seeking to negative physical address");
		return false;
	}
	if (physicalAddress + headerSize < 0)
		Logger::queueError(Logger::Warning, "Seeking to physical address with negative virtual address");

	virtualAddress = physicalAddress+headerSize;

	if (isOpen())
		stream.seekp(physicalAddress);

	return true;
}



FileManager::FileManager()
{
	// detect own endianness
	volatile union
	{
		uint32_t i;
		uint8_t c[4];
	} u;
	u.c[3] = 0xAA;
	u.c[2] = 0xBB;
	u.c[1] = 0xCC;
	u.c[0] = 0xDD;

	if (u.i == 0xDDCCBBAA)
		ownEndianness = Endianness::Big;
	else if (u.i == 0xAABBCCDD)
		ownEndianness = Endianness::Little;
	else
		Logger::printError(Logger::Error, "Running on unknown endianness");

	reset();
}

FileManager::~FileManager() = default;

void FileManager::reset()
{
	activeFile = nullptr;
	setEndianness(Endianness::Little);
}

bool FileManager::checkActiveFile()
{
	if (activeFile == nullptr)
	{
		Logger::queueError(Logger::Error, "No file opened");
		return false;
	}
	return true;
}

bool FileManager::openFile(std::shared_ptr<AssemblerFile> file, bool onlyCheck)
{
	if (activeFile != nullptr)
	{
		Logger::queueError(Logger::Warning, "File not closed before opening a new one");
		activeFile->close();
	}

	activeFile = std::move(file);
	return activeFile->open(onlyCheck);
}

void FileManager::addFile(std::shared_ptr<AssemblerFile> file)
{
	files.push_back(file);
}

void FileManager::closeFile()
{
	if (activeFile == nullptr)
	{
		Logger::queueError(Logger::Warning, "No file opened");
		return;
	}

	activeFile->close();
	activeFile = nullptr;
}

bool FileManager::write(void* data, size_t length)
{
	if (!checkActiveFile())
		return false;

	if (!activeFile->isOpen())
	{
		Logger::queueError(Logger::Error, "No file opened");
		return false;
	}

	return activeFile->write(data,length);
}

bool FileManager::writeU8(uint8_t data)
{
	return write(&data,1);
}

bool FileManager::writeU16(uint16_t data)
{
	if (endianness != ownEndianness)
		data = swapEndianness16(data);

	return write(&data,2);
}

bool FileManager::writeU32(uint32_t data)
{
	if (endianness != ownEndianness)
		data = swapEndianness32(data);

	return write(&data,4);
}

bool FileManager::writeU64(uint64_t data)
{
	if (endianness != ownEndianness)
		data = swapEndianness64(data);

	return write(&data,8);
}

int64_t FileManager::getVirtualAddress()
{
	if (activeFile == nullptr)
		return -1;
	return activeFile->getVirtualAddress();
}

int64_t FileManager::getPhysicalAddress()
{
	if (activeFile == nullptr)
		return -1;
	return activeFile->getPhysicalAddress();
}

int64_t FileManager::getHeaderSize()
{
	if (activeFile == nullptr)
		return -1;
	return activeFile->getHeaderSize();
}

bool FileManager::seekVirtual(int64_t virtualAddress)
{
	if (!checkActiveFile())
		return false;

	bool result = activeFile->seekVirtual(virtualAddress);
	if (result && Global.memoryMode)
	{
		int sec = Global.symbolTable.findSection(virtualAddress);
		if (sec != -1)
			Global.Section = sec;
	}

	return result;
}

bool FileManager::seekPhysical(int64_t virtualAddress)
{
	if (!checkActiveFile())
		return false;
	return activeFile->seekPhysical(virtualAddress);
}

bool FileManager::advanceMemory(size_t bytes)
{
	if (!checkActiveFile())
		return false;

	int64_t pos = activeFile->getVirtualAddress();
	return activeFile->seekVirtual(pos+bytes);
}

int64_t FileManager::getOpenFileID()
{
	if (!checkActiveFile())
		return 0;

	static_assert(sizeof(int64_t) >= sizeof(intptr_t), "Assumes pointers are <= 64 bit");
	return (int64_t)(intptr_t)activeFile.get();
}

#pragma region "File: Core/Misc.cpp"


#include <iostream>

std::vector<Logger::QueueEntry> Logger::queue;
std::vector<std::string> Logger::errors;
bool Logger::error = false;
bool Logger::fatalError = false;
bool Logger::errorOnWarning = false;
bool Logger::silent = false;
int Logger::suppressLevel = 0;

std::string Logger::formatError(ErrorType type, const char* text)
{
	std::string position;

	if (!Global.memoryMode && Global.fileList.size() > 0)
	{
		const auto& fileName = Global.fileList.relativeString(Global.FileInfo.FileNum);
		position = tfm::format("%s(%d) ", fileName, Global.FileInfo.LineNumber);
	}

	switch (type)
	{
	case Warning:
		return tfm::format("%swarning: %s",position,text);
	case Error:
		return tfm::format("%serror: %s",position,text);
	case FatalError:
		return tfm::format("%sfatal error: %s",position,text);
	case Notice:
		return tfm::format("%snotice: %s",position,text);
	}

	return "";
}

void Logger::setFlags(ErrorType type)
{
	switch (type)
	{
	case Warning:
		if (errorOnWarning)
			error = true;
		break;
	case Error:
		error = true;
		break;
	case FatalError:
		error = true;
		fatalError = true;
		break;
	case Notice:
		break;
	}
}

void Logger::clear()
{
	queue.clear();
	errors.clear();
	error = false;
	fatalError = false;
	errorOnWarning = false;
	silent = false;
}

void Logger::printLine(std::string_view text)
{
	if (suppressLevel)
		return;

	std::cout << text << std::endl;
}

void Logger::print(std::string_view text)
{
	if (suppressLevel)
		return;

	std::cout << text;
}

void Logger::printError(ErrorType type, const std::string_view text)
{
	if (suppressLevel)
		return;

	std::string errorText = formatError(type,text.data());
	errors.push_back(errorText);

	if (!silent)
		printLine(errorText);

	setFlags(type);
}

void Logger::queueError(ErrorType type, const std::string_view text)
{
	if (suppressLevel)
		return;

	QueueEntry entry;
	entry.type = type;
	entry.text = formatError(type,text.data());
	queue.push_back(entry);
}

void Logger::printQueue()
{
	for (size_t i = 0; i < queue.size(); i++)
	{
		errors.push_back(queue[i].text);

		if (!silent)
			printLine(queue[i].text);

		setFlags(queue[i].type);
	}
}

void TempData::start()
{
	if (!file.getFileName().empty())
	{
		if (!file.open(TextFile::Write))
		{
			Logger::printError(Logger::Error,"Could not open temp file %s.",file.getFileName().u8string());
			return;
		}

		size_t fileCount = Global.fileList.size();
		size_t lineCount = Global.FileInfo.TotalLineCount;
		size_t labelCount = Global.symbolTable.getLabelCount();
		size_t equCount = Global.symbolTable.getEquationCount();

		file.writeFormat("; %d %S included\n",fileCount,fileCount == 1 ? "file" : "files");
		file.writeFormat("; %d %S\n",lineCount,lineCount == 1 ? "line" : "lines");
		file.writeFormat("; %d %S\n",labelCount,labelCount == 1 ? "label" : "labels");
		file.writeFormat("; %d %S\n\n",equCount,equCount == 1 ? "equation" : "equations");
		for (size_t i = 0; i < fileCount; i++)
		{
			file.writeFormat("; %S\n",Global.fileList.string(int(i)));
		}
		file.writeLine("");
	}
}

void TempData::end()
{
	if (file.isOpen())
		file.close();
}

void TempData::writeLine(int64_t memoryAddress, const std::string& text)
{
	if (file.isOpen())
	{
		char hexbuf[10] = {0};
		snprintf(hexbuf, 10, "%08X ", (int32_t) memoryAddress);
		std::string str = hexbuf + text;
		while (str.size() < 70)
			str += ' ';

		str += tfm::format("; %S line %d",
			Global.fileList.string(Global.FileInfo.FileNum),Global.FileInfo.LineNumber);

		file.writeLine(str);
	}
}

#pragma region "File: Core/SymbolData.cpp"


#include <algorithm>

SymbolData::SymbolData()
{
	clear();
}

void SymbolData::clear()
{
	enabled = true;
	nocashSymFileName.clear();
	modules.clear();
	files.clear();
	currentModule = 0;
	currentFunction = -1;

	SymDataModule defaultModule;
	defaultModule.file = nullptr;
	modules.push_back(defaultModule);
}

struct NocashSymEntry
{
	int64_t address;
	std::string text;

	bool operator<(const NocashSymEntry& other) const
	{
		if (address != other.address)
			return address < other.address;
		return text < other.text;
	}
};

void SymbolData::writeNocashSym()
{
	if (nocashSymFileName.empty())
		return;

	std::vector<NocashSymEntry> entries;
	for (size_t k = 0; k < modules.size(); k++)
	{
		SymDataModule& module = modules[k];
		for (size_t i = 0; i < module.symbols.size(); i++)
		{
			SymDataSymbol& sym = module.symbols[i];

			size_t size = 0;
			for (size_t f = 0; f < module.functions.size(); f++)
			{
				if (module.functions[f].address == sym.address)
				{
					size = module.functions[f].size;
					break;
				}
			}

			NocashSymEntry entry;
			entry.address = sym.address;

			if (size != 0 && nocashSymVersion >= 2)
				entry.text = tfm::format("%s,%08X",sym.name,size);
			else
				entry.text = sym.name;

			if (nocashSymVersion == 1)
				std::transform(entry.text.begin(), entry.text.end(), entry.text.begin(), ::tolower);

			entries.push_back(entry);
		}

		for (const SymDataData& data: module.data)
		{
			NocashSymEntry entry;
			entry.address = data.address;

			switch (data.type)
			{
			case Data8:
				entry.text = tfm::format(".byt:%04X",data.size);
				break;
			case Data16:
				entry.text = tfm::format(".wrd:%04X",data.size);
				break;
			case Data32:
				entry.text = tfm::format(".dbl:%04X",data.size);
				break;
			case Data64:
				entry.text = tfm::format(".dbl:%04X",data.size);
				break;
			case DataAscii:
				entry.text = tfm::format(".asc:%04X",data.size);
				break;
			}

			entries.push_back(entry);
		}
	}

	std::sort(entries.begin(),entries.end());

	TextFile file;
	if (!file.open(nocashSymFileName,TextFile::Write,TextFile::ASCII))
	{
		Logger::printError(Logger::Error, "Could not open sym file %s.",file.getFileName().u8string());
		return;
	}
	file.writeLine("00000000 0");

	for (size_t i = 0; i < entries.size(); i++)
	{
		file.writeFormat("%08X %s\n",entries[i].address,entries[i].text);
	}

	file.write("\x1A");
	file.close();
}

void SymbolData::write()
{
	writeNocashSym();
}

void SymbolData::addLabel(int64_t memoryAddress, const std::string& name)
{
	if (!enabled)
		return;

	SymDataSymbol sym;
	sym.address = memoryAddress;
	sym.name = name;

	for (SymDataSymbol& symbol: modules[currentModule].symbols)
	{
		if (symbol.address == sym.address && symbol.name == sym.name)
			return;
	}

	modules[currentModule].symbols.push_back(sym);
}

void SymbolData::addData(int64_t address, size_t size, DataType type)
{
	if (!enabled)
		return;

	SymDataData data;
	data.address = address;
	data.size = size;
	data.type = type;
	modules[currentModule].data.insert(data);
}

size_t SymbolData::addFileName(const std::string& fileName)
{
	for (size_t i = 0; i < files.size(); i++)
	{
		if (files[i] == fileName)
			return i;
	}

	files.push_back(fileName);
	return files.size()-1;
}

void SymbolData::startModule(AssemblerFile* file)
{
	for (size_t i = 0; i < modules.size(); i++)
	{
		if (modules[i].file == file)
		{
			currentModule = (int)i;
			return;
		}
	}

	SymDataModule module;
	module.file = file;
	modules.push_back(module);
	currentModule = (int)modules.size()-1;
}

void SymbolData::endModule(AssemblerFile* file)
{
	if (modules[currentModule].file != file)
		return;

	if (currentModule == 0)
	{
		Logger::printError(Logger::Error, "No module opened");
		return;
	}

	if (currentFunction != -1)
	{
		Logger::printError(Logger::Error, "Module closed before function end");
		currentFunction = -1;
	}

	currentModule = 0;
}

void SymbolData::startFunction(int64_t address)
{
	if (currentFunction != -1)
	{
		endFunction(address);
	}

	currentFunction = (int)modules[currentModule].functions.size();

	SymDataFunction func;
	func.address = address;
	func.size = 0;
	modules[currentModule].functions.push_back(func);
}

void SymbolData::endFunction(int64_t address)
{
	if (currentFunction == -1)
	{
		Logger::printError(Logger::Error, "Not inside a function");
		return;
	}

	SymDataFunction& func = modules[currentModule].functions[currentFunction];
	func.size = (size_t) (address-func.address);
	currentFunction = -1;
}

#pragma region "File: Core/SymbolTable.cpp"



const char validSymbolCharacters[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_.";

bool operator<(SymbolKey const& lhs, SymbolKey const& rhs)
{
	if (lhs.file != rhs.file)
		return lhs.file < rhs.file;
	if (lhs.section != rhs.section)
		return lhs.section < rhs.section;
	return lhs.name.compare(rhs.name) < 0;
}


Label::Label(const Identifier &name) :
	name(name),
	originalName(name)
{
}


SymbolTable::SymbolTable()
{
	uniqueCount = 0;
}

SymbolTable::~SymbolTable()
{
	clear();
}

void SymbolTable::clear()
{
	symbols.clear();
	labels.clear();
	equationsCount = 0;
	uniqueCount = 0;
}

void SymbolTable::setFileSectionValues(const Identifier& symbol, int& file, int& section)
{
	if (symbol.string()[0] == '@')
	{
		if (symbol.string()[1] != '@')
		{
			// static label, @. the section doesn't matter
			section = -1;
		} else {
			// local label, @@. the file doesn't matter
			file = -1;
		}
	} else {
		// global label. neither file nor section matters
		file = section = -1;
	}
}

std::shared_ptr<Label> SymbolTable::getLabel(const Identifier& symbol, int file, int section)
{
	if (!isValidSymbolName(symbol))
		return nullptr;

	int actualSection = section;
	setFileSectionValues(symbol,file,section);
	SymbolKey key = { symbol, file, section };

	// find label, create new one if it doesn't exist
	auto it = symbols.find(key);
	if (it == symbols.end())
	{
		SymbolInfo value = { LabelSymbol, labels.size() };
		symbols[key] = value;

		std::shared_ptr<Label> result = std::make_shared<Label>(Identifier(symbol));
		if (section == actualSection)
			result->setSection(section);			// local, set section of parent
		else
			result->setSection(actualSection+1);	// global, set section of children
		labels.push_back(result);
		return result;
	}

	// make sure not to match symbols that aren't labels
	if (it->second.type != LabelSymbol)
		return nullptr;

	return labels[it->second.index];
}

bool SymbolTable::symbolExists(const Identifier& symbol, int file, int section)
{
	if (!isValidSymbolName(symbol))
		return false;

	setFileSectionValues(symbol,file,section);

	SymbolKey key = { symbol, file, section };
	auto it = symbols.find(key);
	return it != symbols.end();
}

bool SymbolTable::isValidSymbolName(const Identifier& symbol)
{
	const auto& string = symbol.string();

	size_t size = string.size();
	size_t start = 0;

	// don't match empty names
	if (size == 0 || string.compare("@") == 0 || string.compare("@@") == 0)
		return false;

	if (string[0] == '@')
	{
		start++;
		if (size > 1 && string[1] == '@')
			start++;
	}

	if (string[start] >= '0' && string[start] <= '9')
		return false;

	for (size_t i = start; i < size; i++)
	{
		if (strchr(validSymbolCharacters,string[i]) == nullptr)
			return false;
	}

	return true;
}

bool SymbolTable::isValidSymbolCharacter(char character, bool first)
{
	if ((character >= 'a' && character <= 'z') || (character >= 'A' && character <= 'Z')) return true;
	if (!first && (character >= '0' && character <= '9')) return true;
	if (character == '_' || character == '.') return true;
	if (character == '@') return true;
	return false;
}

bool SymbolTable::addEquation(const Identifier& name, int file, int section, size_t referenceIndex)
{
	if (!isValidSymbolName(name))
		return false;

	if (symbolExists(name,file,section))
		return false;

	setFileSectionValues(name,file,section);

	SymbolKey key = { name, file, section };
	SymbolInfo value = { EquationSymbol, referenceIndex };
	symbols[key] = value;

	equationsCount++;
	return true;
}

bool SymbolTable::findEquation(const Identifier& name, int file, int section, size_t& dest)
{
	setFileSectionValues(name,file,section);

	SymbolKey key = { name, file, section };
	auto it = symbols.find(key);
	if (it == symbols.end() || it->second.type != EquationSymbol)
		return false;

	dest = it->second.index;
	return true;
}

// TODO: better
Identifier SymbolTable::getUniqueLabelName(bool local)
{
	std::string name = tfm::format("__armips_label_%08x__",uniqueCount++);
	if (local)
		name = "@@" + name;

	generatedLabels.insert(Identifier(name));
	return Identifier(name);
}

void SymbolTable::addLabels(const std::vector<LabelDefinition>& labels)
{
	for (const LabelDefinition& def: labels)
	{
		if (!isValidSymbolName(def.name))
			continue;

		std::shared_ptr<Label> label = getLabel(def.name,Global.FileInfo.FileNum,Global.Section);
		if (label == nullptr)
			continue;

		if (!isLocalSymbol(def.name))
			Global.Section++;

		label->setDefined(true);
		label->setValue(def.value);
	}
}

int SymbolTable::findSection(int64_t address)
{
	int64_t smallestBefore = -1;
	int64_t smallestDiff = 0x7FFFFFFF;

	for (auto& lab: labels)
	{
		int64_t diff = address-lab->getValue();
		if (diff >= 0 && diff < smallestDiff)
		{
			smallestDiff = diff;
			smallestBefore = lab->getSection();
		}
	}

	return int(smallestBefore);
}

#pragma region "File: Main/main.cpp"

#include <clocale>

#ifdef _WIN32
#include <Windows.h>
#endif

namespace
{
	int run(const std::vector<std::string> &arguments)
	{
#ifdef ARMIPS_TESTS
		if (arguments.size() < 2)
			return !runTests("Tests", arguments[0]);
		else
			return !runTests(arguments[1], arguments[0]);
#endif
		return runFromCommandLine(arguments);
	}
}

#ifdef _WIN32
int wmain(int argc, wchar_t* argv[])
{
	std::setlocale(LC_CTYPE, "");

	// enable ANSI escape code processing
	HANDLE stdoutHandle = GetStdHandle(STD_OUTPUT_HANDLE);
	DWORD consoleMode = 0;
	GetConsoleMode(stdoutHandle , &consoleMode);
	consoleMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
	SetConsoleMode(stdoutHandle, consoleMode);

	// process parameters
	std::vector<std::string> arguments;
	for (int i = 0; i < argc; ++i)
	{
		arguments.push_back(convertWStringToUtf8(argv[i]));
	}

	return run(arguments);
}
#else
int main(int argc, char* argv[])
{
	std::setlocale(LC_CTYPE, "");

	std::vector<std::string> arguments;
	for (int i = 0; i < argc; i++)
	{
		arguments.push_back(argv[i]);
	}

	return run(arguments);
}
#endif
