// armips assembler v0.11
// https://github.com/Kingcom/armips/
// To simplify compilation, all files have been concatenated into one.
// MIPS only, ARM is 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.

*/

// file: stdafx.h


#define _CRT_SECURE_NO_WARNINGS
#undef __STRICT_ANSI__

#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

#include <cstdio>
#include <vector>
#include <cstdlib>
#include <cstdarg>
#include <cctype>
#include <cstring>
#include <cmath>
#include <clocale>

#include <sstream>
#include <iomanip>
#include <memory>

#define formatString tfm::format

// Custom make_unique so that C++14 support will not be necessary for compilation
template<typename T, typename... Args>
std::unique_ptr<T> make_unique(Args&&... args)
{
    return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
}

// 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::wostream
// * 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::wcout:
//
//   std::wstring weekday = L"Wednesday";
//   const wchar_t* month = L"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::wstring 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, L"%s, %s %d, %.2d:%.2d\n",
//               weekday, month, day, hour, min);
//
// The other returns a std::wstring:
//
//   std::wstring date = tfm::format(L"%s, %s %d, %.2d:%.2d\n",
//                                  weekday, month, day, hour, min);
//   std::wcout << 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::wcout, L"%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

#define TINYFORMAT_ALLOW_WCHAR_STRINGS
#define TINYFORMAT_USE_VARIADIC_TEMPLATES

#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 { wchar_t dummy[2]; };
        struct succeed { wchar_t 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::wostream& /*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::wostream& 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::wostream& /**/, const T& /**/) { return false; }
};
template<typename T>
struct formatZeroIntegerWorkaround<T,true>
{
    static bool invoke(std::wostream& 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 wchar_tacters to the given stream.
template<typename T>
inline void formatTruncated(std::wostream& out, const T& value, int ntrunc)
{
    std::wostringstream tmp;
    tmp << value;
    std::wstring 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::wostream& out, type* value, int ntrunc) \
{                                                           \
    std::streamsize len = 0;                                \
    while(len < ntrunc && value[len] != 0)                  \
        ++len;                                              \
    out.write(value, len);                                  \
}
// Overload for const wchar_t* and wchar_t*.  Could overload for signed & unsigned
// wchar_t too, but these are technically unneeded for printf compatibility.
TINYFORMAT_DEFINE_FORMAT_TRUNCATED_CSTR(const wchar_t)
TINYFORMAT_DEFINE_FORMAT_TRUNCATED_CSTR(wchar_t)
#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::wostream& out, const wchar_t* /*fmtBegin*/,
                        const wchar_t* 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 wchar_t 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 wchar_t*).
    const bool canConvertToChar = detail::is_convertible<T,wchar_t>::value;
    const bool canConvertToVoidPtr = detail::is_convertible<T, const void*>::value;
    if(canConvertToChar && *(fmtEnd-1) == 'c')
        detail::formatValueAsType<T, wchar_t>::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 wchar_tacters may be read.
        detail::formatTruncated(out, value, ntrunc);
    }
    else
        out << value;
}


// Overloaded version for wchar_t types to support printing as an integer
#define TINYFORMAT_DEFINE_FORMATVALUE_CHAR(wchar_tType)                  \
inline void formatValue(std::wostream& out, const wchar_t* /*fmtBegin*/,  \
                        const wchar_t* fmtEnd, int /**/, wchar_tType 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::wostream& out, const wchar_t* fmtBegin,
                    const wchar_t* 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::wostream& out, const wchar_t* fmtBegin,
                        const wchar_t* 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::wostream& out, const wchar_t* fmtBegin,
                             const wchar_t* 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 wchar_t*& 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 wchar_t* printFormatStringLiteral(std::wostream& out, const wchar_t* fmt)
{
    const wchar_t* 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 wchar_tacter after the end of the current format spec.
inline const wchar_t* streamStateFromFormat(std::wostream& out, bool& spacePadPositive,
                                         int& ntrunc, const wchar_t* 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 wchar_t* 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);
            // Falls through
        case 'x': case 'p':
            out.setf(std::ios::hex, std::ios::basefield);
            intConversion = true;
            break;
        case 'E':
            out.setf(std::ios::uppercase);
            // 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);
            // Falls through
        case 'f':
            out.setf(std::ios::fixed, std::ios::floatfield);
            break;
        case 'G':
            out.setf(std::ios::uppercase);
            // 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::wostream& out, const wchar_t* 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();
    wchar_t 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 wchar_t* 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::wostringstream tmpStream;
            tmpStream.copyfmt(out);
            tmpStream.setf(std::ios::showpos);
            arg.format(tmpStream, fmt, fmtEnd, ntrunc);
            std::wstring 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::wostream& out, const wchar_t* 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::wostream& out, const wchar_t* 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::wostream& out, const wchar_t* 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::wstring format(const wchar_t* fmt, const Args&... args)
{
    std::wostringstream oss;
    format(oss, fmt, args...);
    return oss.str();
}

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

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


#else // C++98 version

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

inline std::wstring format(const wchar_t* fmt)
{
    std::wostringstream oss;
    format(oss, fmt);
    return oss.str();
}

inline void printf(const wchar_t* fmt)
{
    format(std::wcout, fmt);
}

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

#define TINYFORMAT_MAKE_FORMAT_FUNCS(n)                                   \
                                                                          \
template<TINYFORMAT_ARGTYPES(n)>                                          \
void format(std::wostream& out, const wchar_t* fmt, TINYFORMAT_VARARGS(n))    \
{                                                                         \
    vformat(out, fmt, makeFormatList(TINYFORMAT_PASSARGS(n)));            \
}                                                                         \
                                                                          \
template<TINYFORMAT_ARGTYPES(n)>                                          \
std::wstring format(const wchar_t* fmt, TINYFORMAT_VARARGS(n))                \
{                                                                         \
    std::wostringstream oss;                                               \
    format(oss, fmt, TINYFORMAT_PASSARGS(n));                             \
    return oss.str();                                                     \
}                                                                         \
                                                                          \
template<TINYFORMAT_ARGTYPES(n)>                                          \
void printf(const wchar_t* fmt, TINYFORMAT_VARARGS(n))                       \
{                                                                         \
    format(std::wcout, fmt, TINYFORMAT_PASSARGS(n));                       \
}                                                                         \
                                                                          \
template<TINYFORMAT_ARGTYPES(n)>                                          \
void printfln(const wchar_t* fmt, TINYFORMAT_VARARGS(n))                     \
{                                                                         \
    format(std::wcout, fmt, TINYFORMAT_PASSARGS(n));                       \
    std::wcout << '\n';                                                    \
}

TINYFORMAT_FOREACH_ARGNUM(TINYFORMAT_MAKE_FORMAT_FUNCS)
#undef TINYFORMAT_MAKE_FORMAT_FUNCS

#endif


} // namespace tinyformat

#endif // TINYFORMAT_H_INCLUDED

// file: Commands/CAssemblerCommand.h

class TempData;
class SymbolData;

class CAssemblerCommand
{
public:
	CAssemblerCommand();
	virtual ~CAssemblerCommand() { };
	virtual bool Validate() = 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:
	virtual bool Validate() { return false; };
	virtual void Encode() const { };
	virtual void writeTempData(TempData& tempData) const { };
	virtual void writeSymData(SymbolData& symData) const { };
};

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

// file: Core/Expression.h
#include <memory>

inline std::wstring to_wstring(int64_t value)
{
	return formatString(L"%d", value);
}

inline std::wstring to_wstring(double value)
{
	return formatString(L"%#.17g", value);
}

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

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

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

	ExpressionValue(const std::wstring& value)
	{
		type = ExpressionValueType::String;
		strValue = value;
	}

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

	struct
	{
		int64_t intValue;
		double floatValue;
	};

	std::wstring 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 Label;

struct ExpressionFunctionEntry;
struct ExpressionLabelFunctionEntry;

class ExpressionInternal
{
public:
	ExpressionInternal();
	~ExpressionInternal();
	ExpressionInternal(int64_t value);
	ExpressionInternal(double value);
	ExpressionInternal(const std::wstring& value, OperatorType type);
	ExpressionInternal(OperatorType op, ExpressionInternal* a = nullptr,
		ExpressionInternal* b = nullptr, ExpressionInternal* c = nullptr);
	ExpressionInternal(const std::wstring& name, const std::vector<ExpressionInternal*>& parameters);
	ExpressionValue evaluate();
	std::wstring toString();
	bool isIdentifier() { return type == OperatorType::Identifier; }
	std::wstring getStringValue() { return strValue; }
	void replaceMemoryPos(const std::wstring& identifierName);
	bool simplify(bool inUnknownOrFalseBlock);
	unsigned int getFileNum() { return fileNum; }
	unsigned int getSection() { return section; }
private:
	void allocate(size_t count);
	void deallocate();
	std::wstring formatFunctionCall();
	ExpressionValue executeExpressionFunctionCall(const ExpressionFunctionEntry& entry);
	ExpressionValue executeExpressionLabelFunctionCall(const ExpressionLabelFunctionEntry& entry);
	ExpressionValue executeFunctionCall();
	bool checkParameterCount(size_t min, size_t max);

	OperatorType type;
	ExpressionInternal** children;
	size_t childrenCount;

	union
	{
		int64_t intValue;
		double floatValue;
	};
	std::wstring strValue;

	unsigned int fileNum, section;
};

class Expression
{
public:
	Expression();
	ExpressionValue evaluate();
	bool isLoaded() const { return expression != nullptr; }
	void setExpression(ExpressionInternal* exp, bool inUnknownOrFalseBlock);
	void replaceMemoryPos(const std::wstring& 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(std::wstring& dest, bool convert)
	{
		if (expression == nullptr)
			return false;

		ExpressionValue value = expression->evaluate();
		if (convert && value.isInt())
		{
			dest = to_wstring(value.intValue);
			return true;
		}

		if (convert && value.isFloat())
		{
			dest = to_wstring(value.floatValue);
			return true;
		}

		if (value.isString() == false)
			return false;

		dest = value.strValue;
		return true;
	}

	bool evaluateIdentifier(std::wstring& dest)
	{
		if (expression == nullptr || expression->isIdentifier() == false)
			return false;

		dest = expression->getStringValue();
		return true;
	}

	std::wstring toString() { return expression != nullptr ? expression->toString() : L""; };
private:
	std::shared_ptr<ExpressionInternal> expression;
	std::wstring originalText;
	bool constExpression;
};

Expression createConstExpression(int64_t value);

// file: Core/ExpressionFunctions.h
#include <map>

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

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

using ExpressionFunction = ExpressionValue (*)(const std::wstring& funcName, const std::vector<ExpressionValue>&);
using ExpressionLabelFunction = ExpressionValue (*)(const std::wstring& 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,
};

struct ExpressionFunctionEntry
{
	ExpressionFunction function;
	size_t minParams;
	size_t maxParams;
	ExpFuncSafety safety;
};

struct ExpressionLabelFunctionEntry
{
	ExpressionLabelFunction function;
	size_t minParams;
	size_t maxParams;
	ExpFuncSafety safety;
};


using ExpressionFunctionMap =  std::map<std::wstring, const ExpressionFunctionEntry>;
using ExpressionLabelFunctionMap =  std::map<std::wstring, const ExpressionLabelFunctionEntry>;

extern const ExpressionFunctionMap expressionFunctions;
extern const ExpressionLabelFunctionMap expressionLabelFunctions;

// file: Core/SymbolData.h
#include <set>

class AssemblerFile;

struct SymDataSymbol
{
	std::wstring 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 std::wstring& name, int version) { nocashSymFileName = name; nocashSymVersion = version; };
	void write();
	void setEnabled(bool b) { enabled = b; };

	void addLabel(int64_t address, const std::wstring& 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::wstring& fileName);

	std::wstring nocashSymFileName;
	bool enabled;
	int nocashSymVersion;

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

// file: Util/Util.h
#include <string>
#include <stdio.h>


typedef std::vector<std::wstring> StringList;

std::wstring convertUtf8ToWString(const char* source);
std::string convertWCharToUtf8(wchar_t character);
;std::string convertWStringToUtf8(const std::wstring& source);

std::wstring intToHexString(unsigned int value, int digits, bool prefix = false);
std::wstring intToString(unsigned int value, int digits);
bool stringToInt(const std::wstring& 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);

StringList getStringListFromArray(wchar_t** source, int count);
StringList splitString(const std::wstring& str, const wchar_t delim, bool skipEmpty);

int64_t fileSize(const std::wstring& fileName);
bool fileExists(const std::wstring& strFilename);
bool copyFile(const std::wstring& existingFile, const std::wstring& newFile);
bool deleteFile(const std::wstring& fileName);;

std::wstring toWLowercase(const std::string& str);
std::wstring getFileNameFromPath(const std::wstring& path);
size_t replaceAll(std::wstring& str, const wchar_t* oldValue,const std::wstring& newValue);
bool startsWith(const std::wstring& str, const wchar_t* value, size_t stringPos = 0);

enum class OpenFileMode { ReadBinary, WriteBinary, ReadWriteBinary };
FILE* openFile(const std::wstring& fileName, OpenFileMode mode);
std::wstring getCurrentDirectory();
bool changeDirectory(const std::wstring& dir);
bool isAbsolutePath(const std::wstring& path);

#ifndef ARRAY_SIZE
#define ARRAY_SIZE(x) (sizeof((x)) / sizeof((x)[0]))
#endif

// file: Util/FileClasses.h
#include <list>

class BinaryFile
{
public:
	enum Mode { Read, Write, ReadWrite };

	BinaryFile();
	~BinaryFile();

	bool open(const std::wstring& fileName, Mode mode);
	bool open(Mode mode);
	bool isOpen() { return handle != nullptr; };
	bool atEnd() { return isOpen() && mode != Write && ftell(handle) == size_; };
	void setPos(long pos) { if (isOpen()) fseek(handle,pos,SEEK_SET); };
	long pos() { return isOpen() ? ftell(handle) : -1; }
	long size() { return size_; };
	void close();

	void setFileName(const std::wstring& name) { fileName = name; };
	const std::wstring& getFileName() { return fileName; };

	size_t read(void* dest, size_t length);
	size_t write(void* source, size_t length);
private:
	FILE* handle;
	std::wstring fileName;
	Mode mode;
	long size_;
};

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

	TextFile();
	~TextFile();
	void openMemory(const std::wstring& content);
	bool open(const std::wstring& fileName, Mode mode, Encoding defaultEncoding = GUESS);
	bool open(Mode mode, Encoding defaultEncoding = GUESS);
	bool isOpen() { return fromMemory || handle != nullptr; };
	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 std::wstring& name) { fileName = name; };
	const std::wstring& getFileName() { return fileName; };

	wchar_t readCharacter();
	std::wstring readLine();
	StringList readAll();
	void writeCharacter(wchar_t character);
	void write(const wchar_t* line);
	void write(const std::wstring& line);
	void write(const char* value);
	void write(const std::string& value);
	void writeLine(const wchar_t* line);
	void writeLine(const std::wstring& line);
	void writeLine(const char* line);
	void writeLine(const std::string& line);
	void writeLines(StringList& list);

	template <typename... Args>
	void writeFormat(const wchar_t* text, const Args&... args)
	{
		std::wstring message = formatString(text,args...);
		write(message);
	}

	bool hasError() { return errorText.size() != 0 && !errorRetrieved; };
	const std::wstring& getErrorText() { errorRetrieved = true; return errorText; };
private:
	long tell();
	void seek(long pos);

	FILE* handle;
	std::wstring fileName;
	Encoding encoding;
	Mode mode;
	bool recursion;
	bool guessedEncoding;
	long size_;
	std::wstring errorText;
	bool errorRetrieved;
	bool fromMemory;
	std::wstring content;
	size_t contentPos;
	int lineCount;

	std::string buf;
	size_t bufPos;

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

wchar_t sjisToUnicode(unsigned short);
TextFile::Encoding getEncodingFromString(const std::wstring& str);

// file: Util/ByteArray.h

#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 std::wstring& fileName, long start = 0, size_t size = 0);
	bool toFile(const std::wstring& fileName);
private:
	void grow(size_t neededSize);
	byte* data_;
	size_t size_;
	size_t allocatedSize_;
};

// file: Core/FileManager.h
#include <vector>

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 std::wstring& getFileName() = 0;
};

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

	virtual bool open(bool onlyCheck);
	virtual void close() { if (handle.isOpen()) handle.close(); };
	virtual bool isOpen() { return handle.isOpen(); };
	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 std::wstring& getFileName() { return fileName; };
	const std::wstring& 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;
	BinaryFile handle;
	std::wstring fileName;
	std::wstring 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; };
	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;
};

// 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,
};

// 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;
};

#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))

// 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 std::wstring&fileName, bool sort);
	bool load(ByteArray& data, bool sort);
	void save(const std::wstring&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, int pos, Endianness endianness);
	void loadProgramHeader(Elf32_Phdr& header, ByteArray& data, int pos);
	void loadSectionHeader(Elf32_Shdr& header, ByteArray& data, int 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, int 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, int 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;

	int64_t symbolAddress;
	int targetSymbolType;
	int targetSymbolInfo;
};

// file: Core/ELF/ElfRelocator.h

struct ElfRelocatorCtor
{
	std::wstring 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 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::wstring>& errors) = 0;
	virtual bool finish(std::vector<RelocationAction>& actions, std::vector<std::wstring>& errors) { return true; }
	virtual void setSymbolAddress(RelocationData& data, int64_t symbolAddress, int symbolType) = 0;

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


class Label;

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

struct ElfRelocatorSymbol
{
	std::shared_ptr<Label> label;
	std::wstring 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::wstring name;
};

class ElfRelocator
{
public:
	bool init(const std::wstring& inputName);
	bool exportSymbols();
	void writeSymbols(SymbolData& symData) const;
	std::unique_ptr<CAssemblerCommand> generateCtor(const std::wstring& 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_Rel& rel, ByteArray& data, int offset, Endianness endianness);

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

// file: Archs/Architecture.h

class Tokenizer;
class Parser;

class CArchitecture
{
public:
	virtual std::unique_ptr<CAssemblerCommand> parseDirective(Parser& parser) { return nullptr; }
	virtual std::unique_ptr<CAssemblerCommand> parseOpcode(Parser& parser) { return nullptr; }
	virtual const ExpressionFunctionMap& getExpressionFunctions() { return emptyMap; }
	virtual void NextSection() = 0;
	virtual void Pass2() = 0;
	virtual void Revalidate() = 0;
	virtual std::unique_ptr<IElfRelocator> getElfRelocator() = 0;
	virtual Endianness getEndianness() = 0;
private:
	const ExpressionFunctionMap emptyMap = {};
};

class ArchitectureCommand: public CAssemblerCommand
{
public:
	ArchitectureCommand(const std::wstring& tempText, const std::wstring& symText);
	virtual bool Validate();
	virtual void Encode() const;
	virtual void writeTempData(TempData& tempData) const;
	virtual void writeSymData(SymbolData& symData) const;
private:
	int64_t position;
	Endianness endianness;
	std::wstring tempText;
	std::wstring symText;
};

class CInvalidArchitecture: public CArchitecture
{
public:
	virtual void NextSection();
	virtual void Pass2();
	virtual void Revalidate();
	virtual std::unique_ptr<IElfRelocator> getElfRelocator();
	virtual Endianness getEndianness() { return Endianness::Little; }
};

extern CInvalidArchitecture InvalidArchitecture;

// file: Archs/MIPS/Mips.h

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

class CMipsArchitecture: public CArchitecture
{
public:
	CMipsArchitecture();
	virtual std::unique_ptr<CAssemblerCommand> parseDirective(Parser& parser);
	virtual std::unique_ptr<CAssemblerCommand> parseOpcode(Parser& parser);
	virtual const ExpressionFunctionMap& getExpressionFunctions();
	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;
	};
	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);


// file: Archs/MIPS/MipsOpcodes.h

#define MA_MIPS1		0x00000001
#define MA_MIPS2		0x00000002
#define MA_MIPS3		0x00000004
#define MA_MIPS4		0x00000008
#define MA_PSX			0x00000010
#define MA_PS2			0x00000040
#define MA_PSP			0x00000080
#define MA_RSP			0x00000100

#define MA_EXPSX		0x00001000
#define MA_EXN64		0x00002000
#define MA_EXPS2		0x00004000
#define MA_EXPSP		0x00008000
#define MA_EXRSP		0x00010000

#define MO_IPCA			0x00000001	// pc >> 2
#define MO_IPCR			0x00000002	// PC, -> difference >> 2
#define MO_RSD			0x00000004	// rs = rd
#define MO_RST			0x00000008	// rs = rt
#define MO_RDT			0x00000010	// rd = rt
#define MO_DELAY		0x00000020	// delay slot follows
#define MO_NODELAYSLOT	0x00000040	// can't be in a delay slot
#define MO_DELAYRT		0x00000080	// rt won't be available for one instruction
#define MO_IGNORERTD	0x00000100	// don't care for rt delay
#define MO_FRSD			0x00000200	// float rs + rd
#define MO_IMMALIGNED	0x00000400	// immediate 4 byte aligned
#define MO_VFPU_MIXED	0x00000800	// mixed mode vfpu register
#define MO_VFPU_6BIT	0x00001000	// vfpu register can have 6 bits max
#define MO_VFPU_SINGLE	0x00002000	// single vfpu reg
#define MO_VFPU_QUAD	0x00004000	// quad vfpu reg
#define MO_VFPU			0x00008000	// vfpu type opcode
#define MO_64BIT		0x00010000	// only available on 64 bit cpus
#define MO_FPU			0x00020000	// only available with an fpu
#define MO_TRANSPOSE_VS	0x00040000	// matrix vs has to be transposed
#define MO_VFPU_PAIR	0x00080000	// pair vfpu reg
#define MO_VFPU_TRIPLE	0x00100000	// triple vfpu reg
#define MO_DFPU			0x00200000	// double-precision fpu opcodes
#define MO_RSPVRSD		0x00400000	// rsp vector rs + rd
#define MO_NEGIMM		0x00800000 	// negated immediate (for subi)
#define MO_RSP_HWOFFSET	0x01000000	// RSP halfword load/store offset
#define MO_RSP_WOFFSET	0x02000000	// RSP word load/store offset
#define MO_RSP_DWOFFSET	0x04000000	// RSP doubleword load/store offset
#define MO_RSP_QWOFFSET	0x08000000	// 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[];

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

extern const tMipsOpcode MipsOpcodes[];

// 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;
	std::wstring 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();
	virtual bool Validate();
	virtual void Encode() const;
	virtual void writeTempData(TempData& tempData) const;
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;
};

// file: Util/EncodingTable.h
#include <map>

class Trie
{
public:
	Trie();
	void insert(const wchar_t* text, size_t value);
	void insert(wchar_t character, size_t value);
	bool findLongestPrefix(const wchar_t* text, size_t& result);
private:
	struct LookupEntry
	{
		size_t node;
		wchar_t 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 std::wstring& fileName, TextFile::Encoding encoding = TextFile::GUESS);
	bool isLoaded() { return entries.size() != 0; };
	void addEntry(unsigned char* hex, size_t hexLength, const std::wstring& value);
	void addEntry(unsigned char* hex, size_t hexLength, wchar_t value);
	void setTerminationEntry(unsigned char* hex, size_t hexLength);
	ByteArray encodeString(const std::wstring& 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;
};

// file: Core/Misc.h
#include <vector>

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

	static void clear();
	static void printLine(const std::wstring& text);
	static void printLine(const std::string& text);

	template <typename... Args>
	static void printLine(const wchar_t* text, const Args&... args)
	{
		std::wstring message = formatString(text,args...);
		printLine(message);
	}

	static void print(const std::wstring& text);

	template <typename... Args>
	static void print(const wchar_t* text, const Args&... args)
	{
		std::wstring message = formatString(text,args...);
		print(message);
	}

	static void printError(ErrorType type, const std::wstring& text);
	static void printError(ErrorType type, const wchar_t* text);
	static void queueError(ErrorType type, const std::wstring& text);
	static void queueError(ErrorType type, const wchar_t* text);

	template <typename... Args>
	static void printError(ErrorType type, const wchar_t* text, const Args&... args)
	{
		std::wstring message = formatString(text,args...);
		printError(type,message);
	}

	template <typename... Args>
	static void queueError(ErrorType type, const wchar_t* text, const Args&... args)
	{
		std::wstring message = formatString(text,args...);
		queueError(type,message);
	}

	static void printQueue();
	static void clearQueue() { queue.clear(); };
	static StringList 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::wstring formatError(ErrorType type, const wchar_t* text);
	static void setFlags(ErrorType type);

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

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

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

// file: Core/Assembler.h

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

enum class ArmipsMode { FILE, MEMORY };

struct LabelDefinition
{
	std::wstring originalName;
	std::wstring name;
	int64_t value;
};

struct EquationDefinition
{
	std::wstring name;
	std::wstring value;
};

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

	// file mode
	std::wstring inputFileName;
	std::wstring tempFileName;
	std::wstring symFileName;
	bool useAbsoluteFileNames;

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

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

bool runArmips(ArmipsArguments& settings);

// file: Core/SymbolTable.h

#include <map>

struct SymbolKey
{
	std::wstring name;
	int file;
	int section;
};

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

class Label
{
public:
	Label(std::wstring name): name(name),defined(false),data(false),updateInfo(true),info(0) { };
	const std::wstring getName() { return name; };
	void setOriginalName(const std::wstring& name) { originalName = name; }
	const std::wstring getOriginalName() { return originalName.empty() ? name : 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:
	std::wstring name, originalName;
	int64_t value;
	int64_t physicalValue;
	bool physicalValueSet = false;
	bool defined;
	bool data;
	bool updateInfo;
	int info;
	int section;
};

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

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

	std::wstring getUniqueLabelName(bool local = false);
	size_t getLabelCount() { return labels.size(); };
	size_t getEquationCount() { return equationsCount; };
	bool isGeneratedLabel(const std::wstring& name) { return generatedLabels.find(name) != generatedLabels.end(); }
private:
	void setFileSectionValues(const std::wstring& 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<std::wstring> generatedLabels;
};

// file: Core/Common.h

#include <vector>

typedef struct {
	std::vector<std::wstring> FileList;
	int FileCount;
	int FileNum;
	int LineNumber;
	int TotalLineCount;
} tFileInfo;

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

extern tGlobal Global;
extern CArchitecture* Arch;

class FileManager;
extern FileManager* g_fileManager;

std::wstring getFolderNameFromPath(const std::wstring& src);
std::wstring getFullPathName(const std::wstring& path);

bool checkLabelDefined(const std::wstring& labelName, int section);
bool checkValidLabelName(const std::wstring& labelName);

bool isPowerOfTwo(int64_t n);

// file: Parser/DirectivesParser.h

#include <unordered_map>

class CAssemblerCommand;
class Parser;

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

struct DirectiveEntry {
	DirectiveFunc function;
	int flags;
};

using DirectiveMap = std::unordered_multimap<std::wstring, 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_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

extern const DirectiveMap directives;

// file: Parser/Tokenizer.h

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;

	Token() : originalText(nullptr), stringValue(nullptr), checked(false)
	{
	}

	Token(Token &&src)
	{
		// Move strings.
		originalText = src.originalText;
		src.originalText = nullptr;
		stringValue = src.stringValue;
		src.stringValue = nullptr;

		// Just copy the rest.
		type = src.type;
		line = src.line;
		column = src.column;
		floatValue = src.floatValue;
		checked = src.checked;
	}

	Token(const Token &src) {
		// Copy strings.
		originalText = nullptr;
		if (src.originalText)
			setOriginalText(src.originalText);
		stringValue = nullptr;
		if (src.stringValue)
			setStringValue(src.stringValue);

		// And copy the rest.
		type = src.type;
		line = src.line;
		column = src.column;
		floatValue = src.floatValue;
		checked = src.checked;
	}

	~Token()
	{
		clearOriginalText();
		clearStringValue();
	}

	Token& operator=(const Token& src)
	{
		// Copy strings.
		originalText = nullptr;
		if (src.originalText)
			setOriginalText(src.originalText);
		stringValue = nullptr;
		if (src.stringValue)
			setStringValue(src.stringValue);

		// And copy the rest.
		type = src.type;
		line = src.line;
		column = src.column;
		floatValue = src.floatValue;
		checked = src.checked;

		return *this;
	}

	void setOriginalText(const std::wstring& t)
	{
		setOriginalText(t, 0, t.length());
	}

	void setOriginalText(const std::wstring& t, const size_t pos, const size_t len)
	{
		clearOriginalText();
		originalText = new wchar_t[len + 1];
		wmemcpy(originalText, t.data() + pos, len);
		originalText[len] = 0;
	}

	std::wstring getOriginalText() const
	{
		return originalText;
	}

	void setStringValue(const std::wstring& t)
	{
		setStringValue(t, 0, t.length());
	}

	void setStringValue(const std::wstring& t, const size_t pos, const size_t len)
	{
		clearStringValue();
		stringValue = new wchar_t[len + 1];
		wmemcpy(stringValue, t.data() + pos, len);
		stringValue[len] = 0;
	}

	void setStringAndOriginalValue(const std::wstring& t)
	{
		setStringAndOriginalValue(t, 0, t.length());
	}

	void setStringAndOriginalValue(const std::wstring& t, const size_t pos, const size_t len)
	{
		setStringValue(t, pos, len);
		clearOriginalText();
		originalText = stringValue;
	}

	std::wstring getStringValue() const
	{
		if (stringValue)
			return stringValue;
		return L"";
	}

	bool stringValueStartsWith(wchar_t c) const
	{
		if (stringValue)
			return stringValue[0] == c;
		return false;
	}

	TokenType type;
	size_t line;
	size_t column;

	union
	{
		int64_t intValue;
		double floatValue;
	};

protected:
	void clearOriginalText()
	{
		if (originalText != stringValue)
			delete [] originalText;
		originalText = nullptr;
	}

	void clearStringValue()
	{
		if (stringValue != originalText)
			delete [] stringValue;
		stringValue = nullptr;
	}

	wchar_t* originalText;
	wchar_t* stringValue;

	bool checked;
};

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 std::wstring& identifier, std::vector<Token>& tokens);
	void registerReplacement(const std::wstring& identifier, const std::wstring& 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
	{
		std::wstring 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() { return linePos >= currentLine.size() && input->atEnd(); };

	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::wstring& value);
	void createToken(TokenType type, size_t length, const std::wstring& 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::wstring 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();
	}
};

// file: Archs/MIPS/MipsMacros.h

#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 wchar_t* name;
	const wchar_t* args;
	MipsMacroFunc function;
	int flags;
};

extern const MipsMacroDefinition mipsMacros[];

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

// file: Archs/MIPS/MipsParser.h
#include <unordered_map>

struct MipsRegisterDescriptor {
	const wchar_t* 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::wstring& text, size_t& pos, int& result);
	bool decodeVfpuType(const std::wstring& name, size_t& pos, int& dest);
	bool decodeOpcode(const std::wstring& name, const tMipsOpcode& opcode);

	void setOmittedRegisters(const tMipsOpcode& opcode);
	bool matchSymbol(Parser& parser, wchar_t 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::wstring& 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::wstring buffer;
};

// 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()
{
	bool Result = false;

	bool previousNop = addNop;
	addNop = false;

	RamPos = g_fileManager->getVirtualAddress();
	if (RamPos % 4)
	{
		Logger::queueError(Logger::Error,L"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) == false)
			{
				Logger::queueError(Logger::Error, L"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,L"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,L"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,L"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,L"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) == false)
			{
				Logger::queueError(Logger::Error, L"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,L"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,L"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 == false)
	{
		bool fix = false;

		if (registerData.grd.num != -1 && registerData.grd.num == Mips.GetLoadDelayRegister())
		{
			Logger::queueError(Logger::Warning,L"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,L"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,L"register %S may not be available due to load delay",registerData.grt.name);
			fix = true;
		}

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

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

	Mips.SetDelaySlot(opcodeData.opcode.flags & MO_DELAY ? true : false);

	// now check if this opcode causes a load delay
	if (Mips.hasLoadDelay())
		Mips.SetLoadDelay(opcodeData.opcode.flags & MO_DELAYRT ? true : false,registerData.grt.num);

	if (previousNop != addNop)
		Result = true;

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

void CMipsInstruction::encodeNormal() const
{
	int 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));
}

// file: Archs/MIPS/MipsExpressionFunctions.h

extern const ExpressionFunctionMap mipsExpressionFunctions;

// 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::wstring>& errors) override;
	bool finish(std::vector<RelocationAction>& actions, std::vector<std::wstring>& 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::wstring>& 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;
};

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

const ExpressionFunctionMap& CMipsArchitecture::getExpressionFunctions()
{
	return mipsExpressionFunctions;
}

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:
		return ::make_unique<MipsElfRelocator>();
	case MARCH_PSX:
	case MARCH_RSP:
	default:
		return nullptr;
	}
}

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

// 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 std::wstring& getFileName() { return fileName; };

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

	int segment;
	int section;
	size_t sectionOffset;
};


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

// file: Util/CRC.h

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

// 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,L"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,L"Not inside a section");
	return -1;
}

int64_t MipsElfFile::getHeaderSize()
{
	// this method is not used
	Logger::queueError(Logger::Error,L"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,L"Found segment, but no containing section");
			return false;
		}
	}

	// segmentless sections don't have a virtual address
	Logger::printError(Logger::Error,L"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,L"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,L"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,L"Not inside a section");
	return false;
}

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

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

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

	if (elf.getType() != 2)
	{
		Logger::printError(Logger::FatalError,L"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::wstring& name)
{
	std::string utf8Name = convertWStringToUtf8(name);

	// look in segments
	for (size_t i = 0; i < elf.getSegmentCount(); i++)
	{
		ElfSegment* seg = elf.getSegment(i);
		int n = seg->findSection(utf8Name);
		if (n != -1)
		{
			segment = (int) i;
			section = n;
			return true;
		}
	}

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

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

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

//
// DirectiveLoadPspElf
//

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

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

	g_fileManager->addFile(file);
}

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

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

	g_fileManager->addFile(file);
}

bool DirectiveLoadMipsElf::Validate()
{
	Arch->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(),formatString(L".loadelf \"%s\"",inputName));
	} else {
		tempData.writeLine(g_fileManager->getVirtualAddress(),formatString(L".loadelf \"%s\",\"%s\"",
			inputName,outputName));
	}
}

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

// file: Commands/CommandSequence.h

class Label;

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

// file: Parser/Parser.h
#include <set>
#include <map>
#include <unordered_map>

struct AssemblyTemplateArgument
{
	const wchar_t* variableName;
	std::wstring value;
};

struct ParserMacro
{
	std::wstring name;
	std::vector<std::wstring> parameters;
	std::set<std::wstring> 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 std::wstring& name, const std::wstring& value);

	Expression parseExpression();
	bool parseExpressionList(std::vector<Expression>& list, int min = -1, int max = -1);
	bool parseIdentifier(std::wstring& dest);
	std::unique_ptr<CAssemblerCommand> parseCommand();
	std::unique_ptr<CAssemblerCommand> parseCommandSequence(wchar_t indicator = 0, const std::initializer_list<const wchar_t*> terminators = {});
	std::unique_ptr<CAssemblerCommand> parseFile(TextFile& file, bool virtualFile = false);
	std::unique_ptr<CAssemblerCommand> parseString(const std::wstring& text);
	std::unique_ptr<CAssemblerCommand> parseTemplate(const std::wstring& 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; }

	template <typename... Args>
	void printError(const Token& token, const wchar_t* text, const Args&... args)
	{
		errorLine = token.line;
		Global.FileInfo.LineNumber = (int) token.line;
		std::wstring errorText = formatString(text,args...);
		Logger::printError(Logger::Error,errorText);
		error = true;
	}

	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 std::wstring& name = L"");
	std::unique_ptr<CAssemblerCommand> parseLabel();
	bool checkEquLabel();
	bool checkMacroDefinition();
	std::unique_ptr<CAssemblerCommand> parseMacroCall();

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

	std::vector<FileEntry> entries;
	std::map<std::wstring,ParserMacro> macros;
	std::set<std::wstring> 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 wchar_t* 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 wchar_t* 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;
};

// 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::wstring>& errors)
{
	bool result = true;

	for (const Hi16Entry &hi16: hi16Entries)
	{
		if (hi16.relocationBase != lo16RelocationBase)
		{
			errors.push_back(formatString(L"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::wstring>& 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(formatString(L"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::wstring>& 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 wchar_t* mipsCtorTemplate = LR"(
	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::wstring table;
		for (size_t i = 0; i < ctors.size(); i++)
		{
			if (i != 0)
				table += ',';
			table += formatString(L"%s,%s+0x%08X",ctors[i].symbolName,ctors[i].symbolName,ctors[i].size);
		}

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

// file: Archs/MIPS/MipsExpressionFunctions.cpp

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

ExpressionValue expFuncHi(const std::wstring& 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 std::wstring& funcName, const std::vector<ExpressionValue>& parameters)
{
	int64_t value;

	GET_PARAM(parameters,0,value);

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

const ExpressionFunctionMap mipsExpressionFunctions = {
	{ L"lo",			{ &expFuncLo,				1,	1,	ExpFuncSafety::Safe } },
	{ L"hi",			{ &expFuncHi,				1,	1,	ExpFuncSafety::Safe } },
};

// 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()
{
	int64_t memoryPos = g_fileManager->getVirtualAddress();
	content->applyFileInfo();
	bool result = content->Validate();
	int64_t newMemoryPos = g_fileManager->getVirtualAddress();

	applyFileInfo();

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

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

	return result;
}

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

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

std::wstring preprocessMacro(const wchar_t* 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
	std::wstring labelName = Global.symbolTable.getUniqueLabelName(true);
	immediates.primary.expression.replaceMemoryPos(labelName);
	immediates.secondary.expression.replaceMemoryPos(labelName);

	return formatString(L"%s: %s",labelName,text);
}

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

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

	std::wstring sraop, subop;

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

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

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

	std::wstring sraop, subop;

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

std::unique_ptr<CAssemblerCommand> generateMipsMacroLi(Parser& parser, MipsRegisterData& registers, MipsImmediateData& immediates, int flags)
{
	const wchar_t* templateLi = LR"(
		.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::wstring macroText = preprocessMacro(templateLi,immediates);
	return createMacro(parser,macroText,flags, {
			{ L"%upper%",	(flags & MIPSM_UPPER) ? L"1" : L"0" },
			{ L"%lower%",	(flags & MIPSM_LOWER) ? L"1" : L"0" },
			{ L"%rs%",		registers.grs.name },
			{ L"%imm%",		immediates.secondary.expression.toString() },
	});
}

std::unique_ptr<CAssemblerCommand> generateMipsMacroLoadStore(Parser& parser, MipsRegisterData& registers, MipsImmediateData& immediates, int flags)
{
	const wchar_t* templateLoadStore = LR"(
		.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 wchar_t* op;
	bool isCop = false;
	switch (flags & (MIPSM_ACCESSMASK|MIPSM_LOAD|MIPSM_STORE))
	{
	case MIPSM_LOAD|MIPSM_B:		op = L"lb"; break;
	case MIPSM_LOAD|MIPSM_BU:		op = L"lbu"; break;
	case MIPSM_LOAD|MIPSM_HW:		op = L"lh"; break;
	case MIPSM_LOAD|MIPSM_HWU:		op = L"lhu"; break;
	case MIPSM_LOAD|MIPSM_W:		op = L"lw"; break;
	case MIPSM_LOAD|MIPSM_WU:		op = L"lwu"; break;
	case MIPSM_LOAD|MIPSM_DW:		op = L"ld"; break;
	case MIPSM_LOAD|MIPSM_LLSCW:	op = L"ll"; break;
	case MIPSM_LOAD|MIPSM_LLSCDW:	op = L"lld"; break;
	case MIPSM_LOAD|MIPSM_COP1:		op = L"lwc1"; isCop = true; break;
	case MIPSM_LOAD|MIPSM_COP2:		op = L"lwc2"; isCop = true; break;
	case MIPSM_LOAD|MIPSM_DCOP1:	op = L"ldc1"; isCop = true; break;
	case MIPSM_LOAD|MIPSM_DCOP2:	op = L"ldc2"; isCop = true; break;
	case MIPSM_STORE|MIPSM_B:		op = L"sb"; break;
	case MIPSM_STORE|MIPSM_HW:		op = L"sh"; break;
	case MIPSM_STORE|MIPSM_W:		op = L"sw"; break;
	case MIPSM_STORE|MIPSM_DW:		op = L"sd"; break;
	case MIPSM_STORE|MIPSM_LLSCW:	op = L"sc"; break;
	case MIPSM_STORE|MIPSM_LLSCDW:	op = L"scd"; break;
	case MIPSM_STORE|MIPSM_COP1:	op = L"swc1"; isCop = true; break;
	case MIPSM_STORE|MIPSM_COP2:	op = L"swc2"; isCop = true; break;
	case MIPSM_STORE|MIPSM_DCOP1:	op = L"sdc1"; isCop = true; break;
	case MIPSM_STORE|MIPSM_DCOP2:	op = L"sdc2"; isCop = true; break;
	default: return nullptr;
	}

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

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

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

	std::wstring op, size;
	int type = flags & MIPSM_ACCESSMASK;
	if (type == MIPSM_HW || type == MIPSM_HWU)
	{
		const wchar_t* templateHalfword = LR"(
			.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 ? L"lbu" : L"lb";
		selectedTemplate = templateHalfword;
	} else if (type == MIPSM_W || type == MIPSM_DW)
	{
		const wchar_t* templateWord = LR"(
			.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,L"Cannot use same register as source and destination");
			return ::make_unique<DummyCommand>();
		}

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

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

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

	std::wstring op, size;
	int type = flags & MIPSM_ACCESSMASK;
	if (type == MIPSM_HW)
	{
		const wchar_t* templateHalfword = LR"(
			.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 wchar_t* templateWord = LR"(
			.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,L"Cannot use same register as source and destination");
			return ::make_unique<DummyCommand>();
		}

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

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

std::unique_ptr<CAssemblerCommand> generateMipsMacroBranch(Parser& parser, MipsRegisterData& registers, MipsImmediateData& immediates, int flags)
{
	const wchar_t* 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::wstring op;
	if (bne || beq)
	{
		const wchar_t* templateNeEq = LR"(
			.if %imm% == 0
				%op%	%rs%,r0,%dest%
			.else
				li		r1,%imm%
				%op%	%rs%,r1,%dest%
			.endif
		)";

		selectedTemplate = templateNeEq;
		if(likely)
			op = bne ? L"bnel" : L"beql";
		else
			op = bne ? L"bne" : L"beq";
	} else if (immediate && (beqz || bnez))
	{
		const wchar_t* templateImmediate = LR"(
			.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 ? L"bnezl" : L"beqzl";
		else
			op = bnez ? L"bnez" : L"beqz";
	} else if (beqz || bnez)
	{
		const wchar_t* templateRegister = LR"(
			.if %revcmp%
				slt%u%	r1,%rt%,%rs%
			.else
				slt%u%	r1,%rs%,%rt%
			.endif
			%op%	r1,%dest%
		)";

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

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

std::unique_ptr<CAssemblerCommand> generateMipsMacroSet(Parser& parser, MipsRegisterData& registers, MipsImmediateData& immediates, int flags)
{
	const wchar_t* 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 wchar_t* templateImmediateEqNe = LR"(
			.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 wchar_t* templateEqNe = LR"(
			xor		%rd%,%rs%,%rt%
			.if %eq%
				sltiu	%rd%,%rd%,1
			.else
				sltu	%rd%,r0,%rd%
			.endif
		)";

		selectedTemplate = templateEqNe;
	} else if (immediate && (ge || lt))
	{
		const wchar_t* templateImmediateGeLt = LR"(
			.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 wchar_t* templateGe = LR"(
			.if %revcmp%
				slt%u%	%rd%,%rt%,%rs%
			.else
				slt%u%	%rd%,%rs%,%rt%
			.endif
			xori	%rd%,%rd%,1
		)";

		selectedTemplate = templateGe;
	} else
	{
		return nullptr;
	}

	std::wstring macroText = preprocessMacro(selectedTemplate,immediates);
	return createMacro(parser,macroText,flags, {
			{ L"%u%",		unsigned_ ? L"u" : L""},
			{ L"%eq%",		eq ? L"1" : L"0" },
			{ L"%ge%",		ge ? L"1" : L"0" },
			{ L"%revcmp%",	revcmp ? L"1" : L"0" },
			{ L"%rd%",		registers.grd.name },
			{ L"%rs%",		registers.grs.name },
			{ L"%rt%",		registers.grt.name },
			{ L"%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 wchar_t* selectedTemplate;
	if (psp && immediate)
	{
		const wchar_t* templatePspImmediate = LR"(
			.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 wchar_t* templatePspRegister = LR"(
			.if %left%
				negu	r1,%rt%
				rotrv	%rd%,%rs%,r1
			.else
				rotrv	%rd%,%rs%,%rt%
			.endif
		)";

		selectedTemplate = templatePspRegister;
	} else if (immediate)
	{
		const wchar_t* templateImmediate = LR"(
			.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 wchar_t* templateRegister = LR"(
			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::wstring macroText = preprocessMacro(selectedTemplate,immediates);
	return createMacro(parser,macroText,flags, {
			{ L"%left%",	left ? L"1" : L"0" },
			{ L"%rd%",		registers.grd.name },
			{ L"%rs%",		registers.grs.name },
			{ L"%rt%",		registers.grt.name },
			{ L"%amount%",	immediates.primary.expression.toString() },
	});
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

	{ nullptr,	nullptr,	nullptr,							0 }
};

// 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_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_RSPVRSD },
	{ "vmulu",	"Rd,Rs,RtRe",	MIPS_RSP_COP2(0x01),		MA_RSP,		0 },
	{ "vmulu",	"Rs,RtRe",		MIPS_RSP_COP2(0x01),		MA_RSP,		MO_RSPVRSD },
	{ "vrndp",	"Rd,Rs,RtRe",	MIPS_RSP_COP2(0x02),		MA_RSP,		0 },
	{ "vrndp",	"Rs,RtRe",		MIPS_RSP_COP2(0x02),		MA_RSP,		MO_RSPVRSD },
	{ "vmulq",	"Rd,Rs,RtRe",	MIPS_RSP_COP2(0x03),		MA_RSP,		0 },
	{ "vmulq",	"Rs,RtRe",		MIPS_RSP_COP2(0x03),		MA_RSP,		MO_RSPVRSD },
	{ "vmudl",	"Rd,Rs,RtRe",	MIPS_RSP_COP2(0x04),		MA_RSP,		0 },
	{ "vmudl",	"Rs,RtRe",		MIPS_RSP_COP2(0x04),		MA_RSP,		MO_RSPVRSD },
	{ "vmudm",	"Rd,Rs,RtRe",	MIPS_RSP_COP2(0x05),		MA_RSP,		0 },
	{ "vmudm",	"Rs,RtRe",		MIPS_RSP_COP2(0x05),		MA_RSP,		MO_RSPVRSD },
	{ "vmudn",	"Rd,Rs,RtRe",	MIPS_RSP_COP2(0x06),		MA_RSP,		0 },
	{ "vmudn",	"Rs,RtRe",		MIPS_RSP_COP2(0x06),		MA_RSP,		MO_RSPVRSD },
	{ "vmudh",	"Rd,Rs,RtRe",	MIPS_RSP_COP2(0x07),		MA_RSP,		0 },
	{ "vmudh",	"Rs,RtRe",		MIPS_RSP_COP2(0x07),		MA_RSP,		MO_RSPVRSD },
	{ "vmacf",	"Rd,Rs,RtRe",	MIPS_RSP_COP2(0x08),		MA_RSP,		0 },
	{ "vmacf",	"Rs,RtRe",		MIPS_RSP_COP2(0x08),		MA_RSP,		MO_RSPVRSD },
	{ "vmacu",	"Rd,Rs,RtRe",	MIPS_RSP_COP2(0x09),		MA_RSP,		0 },
	{ "vmacu",	"Rs,RtRe",		MIPS_RSP_COP2(0x09),		MA_RSP,		MO_RSPVRSD },
	{ "vrndn",	"Rd,Rs,RtRe",	MIPS_RSP_COP2(0x0a),		MA_RSP,		0 },
	{ "vrndn",	"Rs,RtRe",		MIPS_RSP_COP2(0x0a),		MA_RSP,		MO_RSPVRSD },
	{ "vmacq",	"Rd,Rs,RtRe",	MIPS_RSP_COP2(0x0b),		MA_RSP,		0 },
	{ "vmacq",	"Rs,RtRe",		MIPS_RSP_COP2(0x0b),		MA_RSP,		MO_RSPVRSD },
	{ "vmadl",	"Rd,Rs,RtRe",	MIPS_RSP_COP2(0x0c),		MA_RSP,		0 },
	{ "vmadl",	"Rs,RtRe",		MIPS_RSP_COP2(0x0c),		MA_RSP,		MO_RSPVRSD },
	{ "vmadm",	"Rd,Rs,RtRe",	MIPS_RSP_COP2(0x0d),		MA_RSP,		0 },
	{ "vmadm",	"Rs,RtRe",		MIPS_RSP_COP2(0x0d),		MA_RSP,		MO_RSPVRSD },
	{ "vmadn",	"Rd,Rs,RtRe",	MIPS_RSP_COP2(0x0e),		MA_RSP,		0 },
	{ "vmadn",	"Rs,RtRe",		MIPS_RSP_COP2(0x0e),		MA_RSP,		MO_RSPVRSD },
	{ "vmadh",	"Rd,Rs,RtRe",	MIPS_RSP_COP2(0x0f),		MA_RSP,		0 },
	{ "vmadh",	"Rs,RtRe",		MIPS_RSP_COP2(0x0f),		MA_RSP,		MO_RSPVRSD },
	{ "vadd",	"Rd,Rs,RtRe",	MIPS_RSP_COP2(0x10),		MA_RSP,		0 },
	{ "vadd",	"Rs,RtRe",		MIPS_RSP_COP2(0x10),		MA_RSP,		MO_RSPVRSD },
	{ "vsub",	"Rd,Rs,RtRe",	MIPS_RSP_COP2(0x11),		MA_RSP,		0 },
	{ "vsub",	"Rs,RtRe",		MIPS_RSP_COP2(0x11),		MA_RSP,		MO_RSPVRSD },
	{ "vsut",	"Rd,Rs,RtRe",	MIPS_RSP_COP2(0x12),		MA_RSP,		0 },
	{ "vsut",	"Rs,RtRe",		MIPS_RSP_COP2(0x12),		MA_RSP,		MO_RSPVRSD },
	{ "vabs",	"Rd,Rs,RtRe",	MIPS_RSP_COP2(0x13),		MA_RSP,		0 },
	{ "vabs",	"Rs,RtRe",		MIPS_RSP_COP2(0x13),		MA_RSP,		MO_RSPVRSD },
	{ "vaddc",	"Rd,Rs,RtRe",	MIPS_RSP_COP2(0x14),		MA_RSP,		0 },
	{ "vaddc",	"Rs,RtRe",		MIPS_RSP_COP2(0x14),		MA_RSP,		MO_RSPVRSD },
	{ "vsubc",	"Rd,Rs,RtRe",	MIPS_RSP_COP2(0x15),		MA_RSP,		0 },
	{ "vsubc",	"Rs,RtRe",		MIPS_RSP_COP2(0x15),		MA_RSP,		MO_RSPVRSD },
	{ "vaddb",	"Rd,Rs,RtRe",	MIPS_RSP_COP2(0x16),		MA_RSP,		0 },
	{ "vaddb",	"Rs,RtRe",		MIPS_RSP_COP2(0x16),		MA_RSP,		MO_RSPVRSD },
	{ "vsubb",	"Rd,Rs,RtRe",	MIPS_RSP_COP2(0x17),		MA_RSP,		0 },
	{ "vsubb",	"Rs,RtRe",		MIPS_RSP_COP2(0x17),		MA_RSP,		MO_RSPVRSD },
	{ "vaccb",	"Rd,Rs,RtRe",	MIPS_RSP_COP2(0x18),		MA_RSP,		0 },
	{ "vaccb",	"Rs,RtRe",		MIPS_RSP_COP2(0x18),		MA_RSP,		MO_RSPVRSD },
	{ "vsucb",	"Rd,Rs,RtRe",	MIPS_RSP_COP2(0x19),		MA_RSP,		0 },
	{ "vsucb",	"Rs,RtRe",		MIPS_RSP_COP2(0x19),		MA_RSP,		MO_RSPVRSD },
	{ "vsad",	"Rd,Rs,RtRe",	MIPS_RSP_COP2(0x1a),		MA_RSP,		0 },
	{ "vsad",	"Rs,RtRe",		MIPS_RSP_COP2(0x1a),		MA_RSP,		MO_RSPVRSD },
	{ "vsac",	"Rd,Rs,RtRe",	MIPS_RSP_COP2(0x1b),		MA_RSP,		0 },
	{ "vsac",	"Rs,RtRe",		MIPS_RSP_COP2(0x1b),		MA_RSP,		MO_RSPVRSD },
	{ "vsum",	"Rd,Rs,RtRe",	MIPS_RSP_COP2(0x1c),		MA_RSP,		0 },
	{ "vsum",	"Rs,RtRe",		MIPS_RSP_COP2(0x1c),		MA_RSP,		MO_RSPVRSD },
	{ "vsar",	"Rd,Rs,RtRe",	MIPS_RSP_COP2(0x1d),		MA_RSP,		0 },
	{ "vsar",	"Rs,RtRe",		MIPS_RSP_COP2(0x1d),		MA_RSP,		MO_RSPVRSD },
	{ "vacc",	"Rd,Rs,RtRe",	MIPS_RSP_COP2(0x1e),		MA_RSP,		0 },
	{ "vacc",	"Rs,RtRe",		MIPS_RSP_COP2(0x1e),		MA_RSP,		MO_RSPVRSD },
	{ "vsuc",	"Rd,Rs,RtRe",	MIPS_RSP_COP2(0x1f),		MA_RSP,		0 },
	{ "vsuc",	"Rs,RtRe",		MIPS_RSP_COP2(0x1f),		MA_RSP,		MO_RSPVRSD },
	{ "vlt",	"Rd,Rs,RtRe",	MIPS_RSP_COP2(0x20),		MA_RSP,		0 },
	{ "vlt",	"Rs,RtRe",		MIPS_RSP_COP2(0x20),		MA_RSP,		MO_RSPVRSD },
	{ "veq",	"Rd,Rs,RtRe",	MIPS_RSP_COP2(0x21),		MA_RSP,		0 },
	{ "veq",	"Rs,RtRe",		MIPS_RSP_COP2(0x21),		MA_RSP,		MO_RSPVRSD },
	{ "vne",	"Rd,Rs,RtRe",	MIPS_RSP_COP2(0x22),		MA_RSP,		0 },
	{ "vne",	"Rs,RtRe",		MIPS_RSP_COP2(0x22),		MA_RSP,		MO_RSPVRSD },
	{ "vge",	"Rd,Rs,RtRe",	MIPS_RSP_COP2(0x23),		MA_RSP,		0 },
	{ "vge",	"Rs,RtRe",		MIPS_RSP_COP2(0x23),		MA_RSP,		MO_RSPVRSD },
	{ "vcl",	"Rd,Rs,RtRe",	MIPS_RSP_COP2(0x24),		MA_RSP,		0 },
	{ "vcl",	"Rs,RtRe",		MIPS_RSP_COP2(0x24),		MA_RSP,		MO_RSPVRSD },
	{ "vch",	"Rd,Rs,RtRe",	MIPS_RSP_COP2(0x25),		MA_RSP,		0 },
	{ "vch",	"Rs,RtRe",		MIPS_RSP_COP2(0x25),		MA_RSP,		MO_RSPVRSD },
	{ "vcr",	"Rd,Rs,RtRe",	MIPS_RSP_COP2(0x26),		MA_RSP,		0 },
	{ "vcr",	"Rs,RtRe",		MIPS_RSP_COP2(0x26),		MA_RSP,		MO_RSPVRSD },
	{ "vmrg",	"Rd,Rs,RtRe",	MIPS_RSP_COP2(0x27),		MA_RSP,		0 },
	{ "vmrg",	"Rs,RtRe",		MIPS_RSP_COP2(0x27),		MA_RSP,		MO_RSPVRSD },
	{ "vand",	"Rd,Rs,RtRe",	MIPS_RSP_COP2(0x28),		MA_RSP,		0 },
	{ "vand",	"Rs,RtRe",		MIPS_RSP_COP2(0x28),		MA_RSP,		MO_RSPVRSD },
	{ "vnand",	"Rd,Rs,RtRe",	MIPS_RSP_COP2(0x29),		MA_RSP,		0 },
	{ "vnand",	"Rs,RtRe",		MIPS_RSP_COP2(0x29),		MA_RSP,		MO_RSPVRSD },
	{ "vor",	"Rd,Rs,RtRe",	MIPS_RSP_COP2(0x2a),		MA_RSP,		0 },
	{ "vor",	"Rs,RtRe",		MIPS_RSP_COP2(0x2a),		MA_RSP,		MO_RSPVRSD },
	{ "vnor",	"Rd,Rs,RtRe",	MIPS_RSP_COP2(0x2b),		MA_RSP,		0 },
	{ "vnor",	"Rs,RtRe",		MIPS_RSP_COP2(0x2b),		MA_RSP,		MO_RSPVRSD },
	{ "vxor",	"Rd,Rs,RtRe",	MIPS_RSP_COP2(0x2c),		MA_RSP,		0 },
	{ "vxor",	"Rs,RtRe",		MIPS_RSP_COP2(0x2c),		MA_RSP,		MO_RSPVRSD },
	{ "vnxor",	"Rd,Rs,RtRe",	MIPS_RSP_COP2(0x2d),		MA_RSP,		0 },
	{ "vnxor",	"Rs,RtRe",		MIPS_RSP_COP2(0x2d),		MA_RSP,		MO_RSPVRSD },
	{ "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_RSPVRSD },
	{ "vextq",	"Rd,Rs,RtRe",	MIPS_RSP_COP2(0x39),		MA_RSP,		0 },
	{ "vextq",	"Rs,RtRe",		MIPS_RSP_COP2(0x39),		MA_RSP,		MO_RSPVRSD },
	{ "vextn",	"Rd,Rs,RtRe",	MIPS_RSP_COP2(0x3a),		MA_RSP,		0 },
	{ "vextn",	"Rs,RtRe",		MIPS_RSP_COP2(0x3a),		MA_RSP,		MO_RSPVRSD },
	{ "vinst",	"Rd,Rs,RtRe",	MIPS_RSP_COP2(0x3c),		MA_RSP,		0 },
	{ "vinst",	"Rs,RtRe",		MIPS_RSP_COP2(0x3c),		MA_RSP,		MO_RSPVRSD },
	{ "vinsq",	"Rd,Rs,RtRe",	MIPS_RSP_COP2(0x3d),		MA_RSP,		0 },
	{ "vinsq",	"Rs,RtRe",		MIPS_RSP_COP2(0x3d),		MA_RSP,		MO_RSPVRSD },
	{ "vinsn",	"Rd,Rs,RtRe",	MIPS_RSP_COP2(0x3e),		MA_RSP,		0 },
	{ "vinsn",	"Rs,RtRe",		MIPS_RSP_COP2(0x3e),		MA_RSP,		MO_RSPVRSD },
	{ "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 },
	{"lwv",		"RtRo,i7(s)",	MIPS_RSP_LWC2(0x0a),		MA_RSP,		MO_RSP_QWOFFSET },
	{"lwv",		"RtRo,(s)",		MIPS_RSP_LWC2(0x0a),		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 },
};

// file: Parser/ExpressionParser.h

Expression parseExpression(Tokenizer& tokenizer, bool inUnknownOrFalseBlock);
void allowFunctionCallExpression(bool allow);

// file: Archs/MIPS/PsxRelocator.h

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

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

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


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

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

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

class PsxRelocator
{
public:
	bool init(const std::wstring& 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::wstring& 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 std::wstring& fileName);
	~DirectivePsxObjImport() { };
	virtual bool Validate();
	virtual void Encode() const;
	virtual void writeTempData(TempData& tempData) const { };
	virtual void writeSymData(SymbolData& symData) const;
private:
	PsxRelocator rel;
};

// file: Commands/CDirectiveFile.h

class GenericAssemblerFile;

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

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

	virtual bool Validate();
	virtual void Encode() const;
	virtual void writeTempData(TempData& tempData) const;
	virtual void writeSymData(SymbolData& symData) const;
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);
	virtual bool Validate();
	virtual void Encode() const;
	virtual void writeTempData(TempData& tempData) const;
	virtual void writeSymData(SymbolData& symData) const { };
private:
	void exec() const;
	Expression expression;
	Type type;
	int64_t position;
	int64_t virtualAddress;
};

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

	virtual bool Validate();
	virtual void Encode() const;
	virtual void writeTempData(TempData& tempData) const;
	virtual void writeSymData(SymbolData& symData) const;
private:
	std::wstring 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);
	virtual bool Validate();
	virtual void Encode() const;
	virtual void writeTempData(TempData& tempData) const;
	virtual void writeSymData(SymbolData& symData) const;
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);
	virtual bool Validate();
	virtual void Encode() const;
	virtual void writeTempData(TempData& tempData) const;
	virtual void writeSymData(SymbolData& symData) const { };
private:
	Expression expression;
	int64_t value;
	int64_t virtualAddress;
};

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

class DirectiveObjImport: public CAssemblerCommand
{
public:
	DirectiveObjImport(const std::wstring& inputName);
	DirectiveObjImport(const std::wstring& inputName, const std::wstring& ctorName);
	~DirectiveObjImport() { };
	virtual bool Validate();
	virtual void Encode() const;
	virtual void writeTempData(TempData& tempData) const;
	virtual void writeSymData(SymbolData& symData) const;
private:
	ElfRelocator rel;
	std::unique_ptr<CAssemblerCommand> ctor;
};

// file: Archs/MIPS/MipsParser.cpp

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

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

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

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

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

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

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

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

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

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

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

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

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

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

	std::wstring inputName, outputName;
	if (list[0].evaluateString(inputName,true) == false)
		return nullptr;

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

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

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

	std::wstring inputName;
	if (list[0].evaluateString(inputName,true) == false)
		return nullptr;

	if (list.size() == 2)
	{
		std::wstring ctorName;
		if (list[1].evaluateIdentifier(ctorName) == false)
			return nullptr;

		if (Mips.GetVersion() == MARCH_PSX)
		{
			parser.printError(start,L"Constructor not supported for PSX libraries");
			return ::make_unique<InvalidCommand>();
		}

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

	if (Mips.GetVersion() == MARCH_PSX)
		return ::make_unique<DirectivePsxObjImport>(inputName);
	else
		return ::make_unique<DirectiveObjImport>(inputName);
}

const DirectiveMap mipsDirectives = {
	{ L".resetdelay",		{ &parseDirectiveResetDelay,	0 } },
	{ L".fixloaddelay",		{ &parseDirectiveFixLoadDelay,	0 } },
	{ L".loadelf",			{ &parseDirectiveLoadElf,		0 } },
	{ L".importobj",		{ &parseDirectiveImportObj,		0 } },
	{ L".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 = formatString(L"$%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 std::wstring stringValue = token.getStringValue();
	for (size_t i = 0; i < count; i++)
	{
		if (stringValue == table[i].name)
		{
			dest.name = stringValue;
			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,ARRAY_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,ARRAY_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,ARRAY_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,ARRAY_SIZE(mipsCop0Registers));
}

bool MipsParser::parsePs2Cop2Register(Parser& parser, MipsRegisterValue& dest)
{
	dest.type = MipsRegisterType::Ps2Cop2;
	return parseRegisterTable(parser,dest,mipsPs2Cop2FpRegisters,ARRAY_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,ARRAY_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,ARRAY_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,ARRAY_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,ARRAY_SIZE(mipsRspVectorControlRegisters));
}

bool MipsParser::parseRspVectorRegister(Parser& parser, MipsRegisterValue& dest)
{
	dest.type = MipsRegisterType::RspVector;
	return parseRegisterTable(parser,dest,mipsRspVectorRegisters,ARRAY_SIZE(mipsRspVectorRegisters));
}

bool MipsParser::parseRspVectorElement(Parser& parser, MipsRegisterValue& dest)
{
	dest.type = MipsRegisterType::RspVectorElement;

	if (parser.peekToken().type == TokenType::LBrack)
	{
		static const MipsRegisterDescriptor rspElementNames[] = {
			{ L"0q", 2 },		{ L"1q", 3 },		{ L"0h", 4 },		{ L"1h", 5 },
			{ L"2h", 6 },		{ L"3h", 7 },		{ L"0w", 8 },		{ L"0", 8 },
			{ L"1w", 9 },		{ L"1", 9 },		{ L"2w", 10 },		{ L"2", 10 },
			{ L"3w", 11 },		{ L"3", 11 },		{ L"4w", 12 },		{ L"4", 12 },
			{ L"5w", 13 },		{ L"5", 13 },		{ L"6w", 14 },		{ L"6", 14 },
			{ L"7w", 15 },		{ L"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::wstring stringValue = token.getOriginalText();
		if (std::any_of(stringValue.begin(), stringValue.end(), iswupper))
		{
			std::transform(stringValue.begin(), stringValue.end(), stringValue.begin(), towlower);
		}

		for (size_t i = 0; i < ARRAY_SIZE(rspElementNames); i++)
		{
			if (stringValue == rspElementNames[i].name)
			{
				dest.num = rspElementNames[i].num;
				dest.name = rspElementNames[i].name;

				return parser.nextToken().type == TokenType::RBrack;
			}
		}

		return false;
	}

	dest.num = 0;
	dest.name = L"";

	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 = formatString(L"%d", token.intValue);
	dest.num = 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 = formatString(L"%d", token.intValue);
		dest.num = token.intValue;

		return parser.nextToken().type == TokenType::RBrack;
	}

	dest.num = 0;
	dest.name = L"";

	return true;
}

static bool decodeDigit(wchar_t 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();
	const std::wstring stringValue = token.getStringValue();
	if (token.type != TokenType::Identifier || stringValue.size() != 4)
		return false;

	int mtx,col,row;
	if (decodeDigit(stringValue[1],mtx) == false) return false;
	if (decodeDigit(stringValue[2],col) == false) return false;
	if (decodeDigit(stringValue[3],row) == false) return false;
	wchar_t mode = towlower(stringValue[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 = stringValue;
	parser.eatToken();
	return true;
}

bool MipsParser::parseVfpuControlRegister(Parser& parser, MipsRegisterValue& reg)
{
	static const wchar_t* vfpuCtrlNames[16] = {
		L"spfx",	L"tpfx",	L"dpfx",	L"cc",
		L"inf4",	L"rsv5",	L"rsv6",	L"rev",
		L"rcx0",	L"rcx1",	L"rcx2",	L"rcx3",
		L"rcx4",	L"rcx5",	L"rcx6",	L"rcx7",
	};

	const Token& token = parser.peekToken();
	const std::wstring stringValue = token.getStringValue();

	if (token.type == TokenType::Identifier)
	{
		for (int i = 0; i < 16; i++)
		{
			if (stringValue == vfpuCtrlNames[i])
			{
				reg.num = i;
				reg.name = vfpuCtrlNames[i];

				parser.eatToken();
				return true;
			}
		}
	} else if (token.type == TokenType::Integer && token.intValue <= 15)
	{
		reg.num = (int) token.intValue;
		reg.name = 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, wchar_t 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},
			{L"maxfloat"}
		);
		// sqrt(2)
		sequenceParser.addEntry(2,
			{TokenType::Identifier, TokenType::LParen, TokenType::Integer, TokenType::RParen},
			{L"sqrt", INT64_C(2)}
		);
		// sqrt(1/2)
		sequenceParser.addEntry(3,
			{TokenType::Identifier, TokenType::LParen, TokenType::Integer, TokenType::Div, TokenType::Integer, TokenType::RParen},
			{L"sqrt", INT64_C(1), INT64_C(2)}
		);
		// sqrt(0.5)
		sequenceParser.addEntry(3,
			{TokenType::Identifier, TokenType::LParen, TokenType::Float, TokenType::RParen},
			{L"sqrt", 0.5}
		);
		// 2/sqrt(pi)
		sequenceParser.addEntry(4,
			{TokenType::Integer, TokenType::Div, TokenType::Identifier, TokenType::LParen, TokenType::Identifier, TokenType::RParen},
			{INT64_C(2), L"sqrt", L"pi"}
		);
		// 2/pi
		sequenceParser.addEntry(5,
			{TokenType::Integer, TokenType::Div, TokenType::Identifier},
			{INT64_C(2), L"pi"}
		);
		// 1/pi
		sequenceParser.addEntry(6,
			{TokenType::Integer, TokenType::Div, TokenType::Identifier},
			{INT64_C(1), L"pi"}
		);
		// pi/4
		sequenceParser.addEntry(7,
			{TokenType::Identifier, TokenType::Div, TokenType::Integer},
			{L"pi", INT64_C(4)}
		);
		// pi/2
		sequenceParser.addEntry(8,
			{TokenType::Identifier, TokenType::Div, TokenType::Integer},
			{L"pi", INT64_C(2)}
		);
		// pi/6 - early because "pi" is a prefix of it
		sequenceParser.addEntry(16,
			{TokenType::Identifier, TokenType::Div, TokenType::Integer},
			{L"pi", INT64_C(6)}
		);
		// pi
		sequenceParser.addEntry(9,
			{TokenType::Identifier},
			{L"pi"}
		);
		// e
		sequenceParser.addEntry(10,
			{TokenType::Identifier},
			{L"e"}
		);
		// log2(e)
		sequenceParser.addEntry(11,
			{TokenType::Identifier, TokenType::LParen, TokenType::Identifier, TokenType::RParen},
			{L"log2", L"e"}
		);
		// log10(e)
		sequenceParser.addEntry(12,
			{TokenType::Identifier, TokenType::LParen, TokenType::Identifier, TokenType::RParen},
			{L"log10", L"e"}
		);
		// ln(2)
		sequenceParser.addEntry(13,
			{TokenType::Identifier, TokenType::LParen, TokenType::Integer, TokenType::RParen},
			{L"ln", INT64_C(2)}
		);
		// ln(10)
		sequenceParser.addEntry(14,
			{TokenType::Identifier, TokenType::LParen, TokenType::Integer, TokenType::RParen},
			{L"ln", INT64_C(10)}
		);
		// 2*pi
		sequenceParser.addEntry(15,
			{TokenType::Integer, TokenType::Mult, TokenType::Identifier},
			{INT64_C(2), L"pi"}
		);
		// log10(2)
		sequenceParser.addEntry(17,
			{TokenType::Identifier, TokenType::LParen, TokenType::Integer, TokenType::RParen},
			{L"log10", INT64_C(2)}
		);
		// log2(10)
		sequenceParser.addEntry(18,
			{TokenType::Identifier, TokenType::LParen, TokenType::Integer, TokenType::RParen},
			{L"log2", INT64_C(10)}
		);
		// sqrt(3)/2
		sequenceParser.addEntry(19,
			{TokenType::Identifier, TokenType::LParen, TokenType::Integer, TokenType::RParen, TokenType::Div, TokenType::Integer},
			{L"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;

		const std::wstring stringValue = token.getStringValue();
		if (token.type != TokenType::Identifier || stringValue.size() != 1)
			return false;

		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 wchar_t* conditions[] = {
		L"fl", L"eq", L"lt", L"le", L"tr", L"ne", L"ge", L"gt",
		L"ez", L"en", L"ei", L"es", L"nz", L"nn", L"ni", L"ns"
	};

	const Token& token = parser.nextToken();
	if (token.type != TokenType::Identifier)
		return false;

	const std::wstring stringValue = token.getStringValue();
	for (size_t i = 0; i < ARRAY_SIZE(conditions); i++)
	{
		if (stringValue == conditions[i])
		{
			result = 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;

		// check for register
		const wchar_t* reg;
		static const wchar_t* vpfxstRegisters = L"xyzw";
		const std::wstring stringValue = token.getStringValue();
		if (stringValue.size() == 1 && (reg = wcschr(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) == false)
			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), L"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), L"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), L"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), L"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) == false)
			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::wstring& text, size_t& pos, int& result)
{
	if (pos+3 == text.size())
	{
		if (startsWith(text,L"any",pos))
		{
			result = 4;
			pos += 3;
			return true;
		}
		if (startsWith(text,L"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.getStringValue(),pos,result);
}

bool MipsParser::parseWb(Parser& parser)
{
	const Token& token = parser.nextToken();
	if (token.type != TokenType::Identifier)
		return false;

	return token.getStringValue() == L"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::wstring& 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::wstring& 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_RSPVRSD)
		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_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));
			allowFunctionCallExpression(*encoding != '(');
			CHECK(decodeImmediateSize(encoding,immediate.primary.type));
			allowFunctionCallExpression(true);
			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 std::wstring stringValue = token.getStringValue();

	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(stringValue,MipsOpcodes[z]) == true)
		{
			TokenizerPosition tokenPos = parser.getTokenizer()->getPosition();

			if (parseParameters(parser,MipsOpcodes[z]) == true)
			{
				// success, return opcode
				return ::make_unique<CMipsInstruction>(opcodeData,immediate,registers);
			}

			parser.getTokenizer()->setPosition(tokenPos);
			paramFail = true;
		}
	}

	if (paramFail == true)
		parser.printError(token,L"MIPS parameter failure");
	else
		parser.printError(token,L"Invalid MIPS opcode '%s'",stringValue);

	return nullptr;
}

bool MipsParser::parseMacroParameters(Parser& parser, const MipsMacroDefinition& macro)
{
	const wchar_t* encoding = (const wchar_t*) 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
			allowFunctionCallExpression(*encoding != '(');
			CHECK(parseImmediate(parser,immediate.primary.expression));
			allowFunctionCallExpression(true);
			break;
		case 'I':	// secondary immediate
			allowFunctionCallExpression(*encoding != '(');
			CHECK(parseImmediate(parser,immediate.secondary.expression));
			allowFunctionCallExpression(true);
			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)
{
	TokenizerPosition startPos = parser.getTokenizer()->getPosition();

	// Cannot be a reference (we eat below.)
	const Token token = parser.peekToken();
	if (token.type != TokenType::Identifier)
		return nullptr;

	parser.eatToken();
	const std::wstring stringValue = token.getStringValue();
	for (int z = 0; mipsMacros[z].name != nullptr; z++)
	{
		if (stringValue == mipsMacros[z].name)
		{
			TokenizerPosition tokenPos = parser.getTokenizer()->getPosition();

			if (parseMacroParameters(parser,mipsMacros[z]) == true)
			{
				return mipsMacros[z].function(parser,registers,immediate,mipsMacros[z].flags);
			}

			parser.getTokenizer()->setPosition(tokenPos);
		}
	}

	// no matching macro found, restore state
	parser.getTokenizer()->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 += formatString(L"%f", bitsToFloat(originalValue));
		break;
	case MipsImmediateType::Immediate16:
		if (!(opcodeFlags & MO_IPCR) && originalValue & 0x8000)
			buffer += formatString(L"-0x%X", 0x10000-(originalValue & 0xFFFF));
		else
			buffer += formatString(L"0x%X", originalValue);
		break;
	default:
		buffer += formatString(L"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 += formatString(L"r%d",*encoding);
			encoding += 1;
			break;
		case 's':	// register
			buffer += regData.grs.name;
			break;
		case 'd':	// register
			buffer += regData.grd.name;
			break;
		case 't':	// register
			buffer += regData.grt.name;
			break;
		case 'S':	// fpu register
			buffer += regData.frs.name;
			break;
		case 'D':	// fpu register
			buffer += regData.frd.name;
			break;
		case 'T':	// fpu register
			buffer += regData.frt.name;
			break;
		case 'v':	// psp vfpu reg
		case 'm':	// vfpu matrix register
			switch (*encoding++)
			{
			case 'd':
				buffer += regData.vrd.name;
				break;
			case 's':
				buffer += regData.vrs.name;
				break;
			case 't':
				buffer += regData.vrt.name;
				break;
			}
			break;
		case 'V':	// ps2 vector reg
			switch (*encoding++)
			{
			case 'd':
				buffer += regData.ps2vrd.name;
				break;
			case 's':
				buffer += regData.ps2vrs.name;
				break;
			case 't':
				buffer += regData.ps2vrt.name;
				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 += L"wb";
			break;
		default:
			buffer += *(encoding-1);
			break;
		}
	}
}

const std::wstring& MipsOpcodeFormatter::formatOpcode(const MipsOpcodeData& opData, const MipsRegisterData& regData,
	const MipsImmediateData& immData)
{
	buffer = L"   ";
	handleOpcodeName(opData);

	while (buffer.size() < 11)
		buffer += ' ';

	handleOpcodeParameters(opData,regData,immData);
	return buffer;
}

// file: Archs/MIPS/PsxRelocator.cpp
#include <map>

struct PsxLibEntry
{
	std::wstring name;
	ByteArray data;
};

const unsigned char psxObjectFileMagicNum[6] = { 'L', 'N', 'K', '\x02', '\x2E', '\x07' };

std::vector<PsxLibEntry> loadPsxLibrary(const std::wstring& 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 = getFileNameFromPath(inputName);
		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::wstring& dest)
{
	dest = L"";
	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::wstring& 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 std::wstring& inputName)
{
	auto inputFiles = loadPsxLibrary(inputName);
	if (inputFiles.size() == 0)
	{
		Logger::printError(Logger::Error,L"Could not load library");
		return false;
	}

	reloc = new MipsElfRelocator();

	for (PsxLibEntry& entry: inputFiles)
	{
		PsxRelocatorFile file;
		file.name = entry.name;

		if (parseObject(entry.data,file) == false)
		{
			Logger::printError(Logger::Error,L"Could not load object file %s",entry.name);
			return false;
		}

		// init symbols
		for (PsxSymbol& sym: file.symbols)
		{
			std::wstring lowered = sym.name;
			std::transform(lowered.begin(), lowered.end(), lowered.begin(), ::towlower);

			sym.label = Global.symbolTable.getLabel(lowered,-1,-1);
			if (sym.label == nullptr)
			{
				Logger::printError(Logger::Error,L"Invalid label name \"%s\"",sym.name);
				continue;
			}

			if (sym.label->isDefined() && sym.type != PsxSymbolType::External)
			{
				Logger::printError(Logger::Error,L"Label \"%s\" already defined",sym.name);
				continue;
			}

			sym.label->setOriginalName(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() == false)
			{
				Logger::queueError(Logger::Error,L"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::wstring> 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::wstring& error : errors)
				{
					Logger::queueError(Logger::Error, error);
				}
				error = true;
			}
		}

		// finish any dangling relocations
		std::vector<std::wstring> errors;
		if (!reloc->finish(relocationActions, errors))
		{
			for (const std::wstring& 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) == false)
			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.c_str());
		}
	}
}

//
// DirectivePsxObjImport
//

DirectivePsxObjImport::DirectivePsxObjImport(const std::wstring& fileName)
{
	if (rel.init(fileName))
	{
	}
}

bool DirectivePsxObjImport::Validate()
{
	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);
}

// file: Archs/Architecture.cpp

CInvalidArchitecture InvalidArchitecture;

ArchitectureCommand::ArchitectureCommand(const std::wstring& tempText, const std::wstring& symText)
{
	this->tempText = tempText;
	this->symText = symText;
	this->endianness = Arch->getEndianness();
}

bool ArchitectureCommand::Validate()
{
	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::wstringstream stream(tempText);

		std::wstring line;
		while (std::getline(stream,line,L'\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,L"No architecture specified");
}

void CInvalidArchitecture::Pass2()
{
	Logger::printError(Logger::FatalError,L"No architecture specified");
}

void CInvalidArchitecture::Revalidate()
{
	Logger::printError(Logger::FatalError,L"No architecture specified");
}

std::unique_ptr<IElfRelocator> CInvalidArchitecture::getElfRelocator()
{
	Logger::printError(Logger::FatalError,L"No architecture specified");
	return nullptr;
}


// 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;
}

// file: Commands/CAssemblerLabel.h

class Label;

class CAssemblerLabel: public CAssemblerCommand
{
public:
	CAssemblerLabel(const std::wstring& name, const std::wstring& originalName);
	CAssemblerLabel(const std::wstring& name, const std::wstring& originalName, Expression& value);
	virtual bool Validate();
	virtual void Encode() const;
	virtual void writeTempData(TempData& tempData) const;
	virtual void writeSymData(SymbolData& symData) const;
private:
	Expression labelValue;
	std::shared_ptr<Label> label;
	bool defined;
};

class CDirectiveFunction: public CAssemblerCommand
{
public:
	CDirectiveFunction(const std::wstring& name, const std::wstring& originalName);
	virtual bool Validate();
	virtual void Encode() const;
	virtual void writeTempData(TempData& tempData) const;
	virtual void writeSymData(SymbolData& symData) const;
	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;
};

// file: Commands/CAssemblerLabel.cpp

CAssemblerLabel::CAssemblerLabel(const std::wstring& name, const std::wstring& originalName)
{
	this->defined = false;
	this->label = nullptr;

	if (Global.symbolTable.isLocalSymbol(name) == false)
		updateSection(++Global.Section);

	label = Global.symbolTable.getLabel(name, FileNum, getSection());
	if (label == nullptr)
	{
		Logger::printError(Logger::Error, L"Invalid label name \"%s\"", name);
		return;
	}

	label->setOriginalName(originalName);

	// does this need to be in validate?
	if (label->getUpdateInfo())
	{
#ifdef ARMIPS_ARM
		if (Arch == &Arm && Arm.GetThumbMode())
			label->setInfo(1);
		else
#endif
			label->setInfo(0);
	}
}

CAssemblerLabel::CAssemblerLabel(const std::wstring& name, const std::wstring& originalName, Expression& value)
	: CAssemblerLabel(name,originalName)
{
	labelValue = value;
}

bool CAssemblerLabel::Validate()
{
	bool result = false;
	if (defined == false)
	{
		if (label->isDefined())
		{
			Logger::queueError(Logger::Error, L"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) == false)
		{
			Logger::printError(Logger::Error, L"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()) == false)
		tempData.writeLine(label->getValue(),formatString(L"%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());
}




CDirectiveFunction::CDirectiveFunction(const std::wstring& name, const std::wstring& originalName)
{
	this->label = ::make_unique<CAssemblerLabel>(name,originalName);
	this->content = nullptr;
	this->start = this->end = 0;
}

bool CDirectiveFunction::Validate()
{
	start = g_fileManager->getVirtualAddress();

	label->applyFileInfo();
	bool result = label->Validate();

	content->applyFileInfo();
	if (content->Validate())
		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);
}

// file: Commands/CDirectiveArea.h

class CDirectiveArea: public CAssemblerCommand
{
public:
	CDirectiveArea(Expression& size);
	virtual bool Validate();
	virtual void Encode() const;
	virtual void writeTempData(TempData& tempData) const;
	virtual void writeSymData(SymbolData& symData) const;
	void setFillExpression(Expression& exp);
	void setContent(std::unique_ptr<CAssemblerCommand> content) { this->content = std::move(content); }
private:
	int64_t position;
	Expression sizeExpression;
	int64_t areaSize;
	int64_t contentSize;
	Expression fillExpression;
	int8_t fillValue;
	std::unique_ptr<CAssemblerCommand> content;
};

// file: Commands/CDirectiveArea.cpp
#include <algorithm>

CDirectiveArea::CDirectiveArea(Expression& size)
{
	this->areaSize = 0;
	this->contentSize = 0;
	this->fillValue = 0;

	this->sizeExpression = size;
	this->content = nullptr;
}

void CDirectiveArea::setFillExpression(Expression& exp)
{
	fillExpression = exp;
}

bool CDirectiveArea::Validate()
{
	int64_t oldAreaSize = areaSize;
	int64_t oldContentSize = contentSize;

	position = g_fileManager->getVirtualAddress();

	if (sizeExpression.evaluateInteger(areaSize) == false)
	{
		Logger::queueError(Logger::Error,L"Invalid size expression");
		return false;
	}

	if (areaSize < 0)
	{
		Logger::queueError(Logger::Error, L"Negative area size");
		return false;
	}

	if (fillExpression.isLoaded())
	{
		if (fillExpression.evaluateInteger(fillValue) == false)
		{
			Logger::queueError(Logger::Error,L"Invalid fill expression");
			return false;
		}
	}

	content->applyFileInfo();
	bool result = content->Validate();
	contentSize = g_fileManager->getVirtualAddress()-position;

	// restore info of this command
	applyFileInfo();

	if (areaSize < contentSize)
	{
		Logger::queueError(Logger::Error,L"Area overflowed");
	}

	if (fillExpression.isLoaded())
		g_fileManager->advanceMemory(areaSize-contentSize);

	if (areaSize != oldAreaSize || contentSize != oldContentSize)
		result = true;

	return result;
}

void CDirectiveArea::Encode() const
{
	content->Encode();

	if (fillExpression.isLoaded())
	{
		unsigned char buffer[64];
		memset(buffer,fillValue,64);

		size_t writeSize = areaSize-contentSize;
		while (writeSize > 0)
		{
			size_t part = std::min<size_t>(64,writeSize);
			g_fileManager->write(buffer,part);
			writeSize -= part;
		}
	}
}

void CDirectiveArea::writeTempData(TempData& tempData) const
{
	tempData.writeLine(position,formatString(L".area 0x%08X",areaSize));
	content->applyFileInfo();
	content->writeTempData(tempData);

	if (fillExpression.isLoaded())
	{
		std::wstring fillString = formatString(L".fill 0x%08X,0x%02X",areaSize-contentSize,fillValue);
		tempData.writeLine(position+contentSize,fillString);
		tempData.writeLine(position+areaSize,L".endarea");
	} else {
		tempData.writeLine(position+contentSize,L".endarea");
	}
}

void CDirectiveArea::writeSymData(SymbolData& symData) const
{
	content->writeSymData(symData);

	if (fillExpression.isLoaded())
		symData.addData(position+contentSize,areaSize-contentSize,SymbolData::Data8);
}

// 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 std::wstring& name);
	CDirectiveConditional(ConditionType type, const Expression& exp);
	virtual bool Validate();
	virtual void Encode() const;
	virtual void writeTempData(TempData& tempData) const;
	virtual void writeSymData(SymbolData& symData) const;
	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;
};

// file: Commands/CDirectiveConditional.cpp

#ifdef ARMIPS_ARM
extern CArmArchitecture Arm;
#endif

CDirectiveConditional::CDirectiveConditional(ConditionType type)
{
	this->type = type;

	ifBlock = nullptr;
	elseBlock = nullptr;
	previousResult = false;
}

CDirectiveConditional::CDirectiveConditional(ConditionType type, const std::wstring& name)
	: CDirectiveConditional(type)
{
	label = Global.symbolTable.getLabel(name,Global.FileInfo.FileNum,Global.Section);
	if (label == nullptr)
		Logger::printError(Logger::Error,L"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;
	if (expression.isLoaded())
	{
		if (expression.evaluateInteger(value) == false)
		{
			Logger::queueError(Logger::Error,L"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,L"Invalid conditional type");
	return false;
}

bool CDirectiveConditional::Validate()
{
	bool result = evaluate();
	bool returnValue = result != previousResult;
	previousResult = result;

	if (result)
	{
		ifBlock->applyFileInfo();
		if (ifBlock->Validate())
			returnValue = true;
	} else if (elseBlock != nullptr)
	{
		elseBlock->applyFileInfo();
		if (elseBlock->Validate())
			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);
	}
}

// file: Commands/CDirectiveData.h

enum class EncodingMode { Invalid, U8, U16, U32, U64, Ascii, Float, Double, Sjis, Custom };

class TableCommand: public CAssemblerCommand
{
public:
	TableCommand(const std::wstring& fileName, TextFile::Encoding encoding);
	virtual bool Validate();
	virtual void Encode() const { };
	virtual void writeTempData(TempData& tempData) const { };
	virtual void writeSymData(SymbolData& symData) const { };
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);
	virtual bool Validate();
	virtual void Encode() const;
	virtual void writeTempData(TempData& tempData) const;
	virtual void writeSymData(SymbolData& symData) const;
private:
	void encodeCustom(EncodingTable& table);
	void encodeSjis();
	void encodeFloat();
	void encodeDouble();
	void encodeNormal();
	size_t getUnitSize() const;
	size_t getDataSize() const;

	int64_t position;
	EncodingMode mode;
	bool writeTermination;
	std::vector<Expression> entries;
	ByteArray customData;
	std::vector<int64_t> normalData;
	Endianness endianness;
};

// file: Commands/CDirectiveData.cpp

//
// TableCommand
//

TableCommand::TableCommand(const std::wstring& fileName, TextFile::Encoding encoding)
{
	auto fullName = getFullPathName(fileName);

	if (fileExists(fullName) == false)
	{
		Logger::printError(Logger::Error,L"Table file \"%s\" does not exist",fileName);
		return;
	}

	if (table.load(fullName,encoding) == false)
	{
		Logger::printError(Logger::Error,L"Invalid table file \"%s\"",fileName);
		return;
	}
}

bool TableCommand::Validate()
{
	Global.Table = table;
	return false;
}


//
// CDirectiveData
//

CDirectiveData::CDirectiveData()
{
	mode = EncodingMode::Invalid;
	writeTermination = false;
	endianness = Arch->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,L"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,L"Invalid expression");
			continue;
		}

		if (value.isInt())
		{
			customData.appendByte(value.intValue);
		} else if (value.isString())
		{
			ByteArray encoded = table.encodeString(value.strValue,false);
			if (encoded.size() == 0 && value.strValue.size() > 0)
			{
				Logger::queueError(Logger::Error,L"Failed to encode \"%s\"",value.strValue);
			}
			customData.append(encoded);
		} else {
			Logger::queueError(Logger::Error,L"Invalid expression type");
		}
	}

	if (writeTermination)
	{
		ByteArray encoded = table.encodeTermination();
		customData.append(encoded);
	}
}

void CDirectiveData::encodeSjis()
{
	static EncodingTable sjisTable;
	if (sjisTable.isLoaded() == false)
	{
		unsigned char hexBuffer[2];

		sjisTable.setTerminationEntry((unsigned char*)"\0",1);

		for (unsigned short SJISValue = 0x0001; SJISValue < 0x0100; SJISValue++)
		{
			wchar_t unicodeValue = sjisToUnicode(SJISValue);
			if (unicodeValue != 0xFFFF)
			{
				hexBuffer[0] = SJISValue & 0xFF;
				sjisTable.addEntry(hexBuffer, 1, unicodeValue);
			}
		}
		for (unsigned short SJISValue = 0x8100; SJISValue < 0xEF00; SJISValue++)
		{
			wchar_t unicodeValue = sjisToUnicode(SJISValue);
			if (unicodeValue != 0xFFFF)
			{
				hexBuffer[0] = (SJISValue >> 8) & 0xFF;
				hexBuffer[1] = SJISValue & 0xFF;
				sjisTable.addEntry(hexBuffer, 2, 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,L"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,L"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,L"Invalid expression");
			continue;
		}

		if (value.isString())
		{
			bool hadNonAscii = false;
			for (size_t l = 0; l < value.strValue.size(); l++)
			{
				int64_t num = value.strValue[l];
				normalData.push_back(num);

				if (num >= 0x80 && hadNonAscii == false)
				{
					Logger::printError(Logger::Warning,L"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,L"Invalid expression type");
		}
	}

	if (writeTermination)
	{
		normalData.push_back(0);
	}
}

bool CDirectiveData::Validate()
{
	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,L"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;
	wchar_t* str = new wchar_t[size];
	wchar_t* start = str;

	switch (mode)
	{
	case EncodingMode::Sjis:
	case EncodingMode::Custom:
		str += swprintf(str,20,L".byte ");

		for (size_t i = 0; i < customData.size(); i++)
		{
			str += swprintf(str,20,L"0x%02X,",(uint8_t)customData[i]);
		}
		break;
	case EncodingMode::U8:
	case EncodingMode::Ascii:
		str += swprintf(str,20,L".byte ");

		for (size_t i = 0; i < normalData.size(); i++)
		{
			str += swprintf(str,20,L"0x%02X,",(uint8_t)normalData[i]);
		}
		break;
	case EncodingMode::U16:
		str += swprintf(str,20,L".halfword ");

		for (size_t i = 0; i < normalData.size(); i++)
		{
			str += swprintf(str,20,L"0x%04X,",(uint16_t)normalData[i]);
		}
		break;
	case EncodingMode::U32:
	case EncodingMode::Float:
		str += swprintf(str,20,L".word ");

		for (size_t i = 0; i < normalData.size(); i++)
		{
			str += swprintf(str,20,L"0x%08X,",(uint32_t)normalData[i]);
		}
		break;
	case EncodingMode::U64:
	case EncodingMode::Double:
		str += swprintf(str,20,L".doubleword ");

		for (size_t i = 0; i < normalData.size(); i++)
		{
			str += swprintf(str,20,L"0x%16llX,",(uint64_t)normalData[i]);
		}
		break;
	case EncodingMode::Invalid:
		// TODO: Assert?
		break;
	}

	*(str-1) = 0;
	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;
	}
}

// file: Commands/CDirectiveFile.cpp

//
// CDirectiveFile
//

CDirectiveFile::CDirectiveFile()
{
	type = Type::Invalid;
	file = nullptr;
}

void CDirectiveFile::initOpen(const std::wstring& fileName, int64_t memory)
{
	type = Type::Open;
	std::wstring fullName = getFullPathName(fileName);

	file = std::make_shared<GenericAssemblerFile>(fullName,memory,false);
	g_fileManager->addFile(file);

	updateSection(++Global.Section);
}

void CDirectiveFile::initCreate(const std::wstring& fileName, int64_t memory)
{
	type = Type::Create;
	std::wstring fullName = getFullPathName(fileName);

	file = std::make_shared<GenericAssemblerFile>(fullName,memory,true);
	g_fileManager->addFile(file);

	updateSection(++Global.Section);
}

void CDirectiveFile::initCopy(const std::wstring& inputName, const std::wstring& outputName, int64_t memory)
{
	type = Type::Copy;
	std::wstring fullInputName = getFullPathName(inputName);
	std::wstring 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()
{
	virtualAddress = g_fileManager->getVirtualAddress();
	Arch->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::wstring str;

	switch (type)
	{
	case Type::Open:
		str = formatString(L".open \"%s\",0x%08X",file->getFileName(),file->getOriginalHeaderSize());
		break;
	case Type::Create:
		str = formatString(L".create \"%s\",0x%08X",file->getFileName(),file->getOriginalHeaderSize());
		break;
	case Type::Copy:
		str = formatString(L".open \"%s\",\"%s\",0x%08X",file->getOriginalFileName(),
			file->getFileName(),file->getOriginalHeaderSize());
		break;
	case Type::Close:
		str = L".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()
{
	virtualAddress = g_fileManager->getVirtualAddress();

	if (expression.evaluateInteger(position) == false)
	{
		Logger::queueError(Logger::FatalError,L"Invalid position");
		return false;
	}

	Arch->NextSection();
	exec();
	return false;
}

void CDirectivePosition::Encode() const
{
	Arch->NextSection();
	exec();
}

void CDirectivePosition::writeTempData(TempData& tempData) const
{
	switch (type)
	{
	case Physical:
		tempData.writeLine(virtualAddress,formatString(L".orga 0x%08X",position));
		break;
	case Virtual:
		tempData.writeLine(virtualAddress,formatString(L".org 0x%08X",position));
		break;
	}
}

//
// CDirectiveIncbin
//

CDirectiveIncbin::CDirectiveIncbin(const std::wstring& fileName)
	: size(0), start(0)
{
	this->fileName = getFullPathName(fileName);

	if (fileExists(this->fileName) == false)
	{
		Logger::printError(Logger::FatalError,L"File %s not found",this->fileName);
	}

	this->fileSize = ::fileSize(this->fileName);
}

bool CDirectiveIncbin::Validate()
{
	virtualAddress = g_fileManager->getVirtualAddress();

	if (startExpression.isLoaded())
	{
		if (startExpression.evaluateInteger(start) == false)
		{
			Logger::queueError(Logger::Error,L"Invalid position expression");
			return false;
		}

		if (start > fileSize)
		{
			Logger::queueError(Logger::Error,L"Start position past end of file");
			return false;
		}
	} else {
		start = 0;
	}

	if (sizeExpression.isLoaded())
	{
		if (sizeExpression.evaluateInteger(size) == false)
		{
			Logger::queueError(Logger::Error,L"Invalid size expression");
			return false;
		}
	} else {
		size = fileSize-start;
	}

	if (start+size > fileSize)
	{
		Logger::queueError(Logger::Warning,L"Read size truncated due to file size");
		size = fileSize-start;
	}

	Arch->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,L"Could not read file \"%s\"",fileName);
			return;
		}
		g_fileManager->write(data.data(),data.size());
	}
}

void CDirectiveIncbin::writeTempData(TempData& tempData) const
{
	tempData.writeLine(virtualAddress,formatString(L".incbin \"%s\"",fileName));
}

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()
{
	virtualAddress = g_fileManager->getVirtualAddress();

	if (valueExpression.isLoaded())
	{
		if (valueExpression.evaluateInteger(value) == false)
		{
			Logger::queueError(Logger::FatalError,L"Invalid %s",mode == Fill ? L"size" : L"alignment");
			return false;
		}
	}

	if (mode != Fill && isPowerOfTwo(value) == false)
	{
		Logger::queueError(Logger::Error, L"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) == false)
		{
			Logger::printError(Logger::FatalError,L"Invalid fill value");
			return false;
		}
	}

	Arch->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,formatString(L".align 0x%08X",value));
		break;
	case AlignPhysical:
		tempData.writeLine(virtualAddress, formatString(L".aligna 0x%08X", value));
		break;
	case Fill:
		tempData.writeLine(virtualAddress,formatString(L".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()
{
	virtualAddress = g_fileManager->getVirtualAddress();

	if (expression.isLoaded())
	{
		if (expression.evaluateInteger(value) == false)
		{
			Logger::queueError(Logger::FatalError,L"Invalid skip length");
			return false;
		}
	}

	Arch->NextSection();
	g_fileManager->advanceMemory(value);

	return false;
}

void CDirectiveSkip::Encode() const
{
	Arch->NextSection();
	g_fileManager->advanceMemory(value);
}

void CDirectiveSkip::writeTempData(TempData& tempData) const
{
	tempData.writeLine(virtualAddress,formatString(L".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,L"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()
{
	virtualAddress = g_fileManager->getVirtualAddress();

	if (expression.evaluateInteger(headerSize) == false)
	{
		Logger::queueError(Logger::FatalError,L"Invalid header size");
		return false;
	}

	exec();
	return false;
}

void CDirectiveHeaderSize::Encode() const
{
	exec();
}

void CDirectiveHeaderSize::writeTempData(TempData& tempData) const
{
	tempData.writeLine(virtualAddress,formatString(L".headersize %s0x%08X",
		headerSize < 0 ? L"-" : L"", headerSize < 0 ? -headerSize : headerSize));
}


//
// DirectiveObjImport
//

DirectiveObjImport::DirectiveObjImport(const std::wstring& inputName)
{
	ctor = nullptr;
	if (rel.init(inputName))
	{
		rel.exportSymbols();
	}
}

DirectiveObjImport::DirectiveObjImport(const std::wstring& inputName, const std::wstring& ctorName)
{
	if (rel.init(inputName))
	{
		rel.exportSymbols();
		ctor = rel.generateCtor(ctorName);
	}
}

bool DirectiveObjImport::Validate()
{
	bool result = false;
	if (ctor != nullptr && ctor->Validate())
		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);
}

// file: Commands/CDirectiveMessage.h

class CDirectiveMessage: public CAssemblerCommand
{
public:
	enum class Type { Warning, Error, Notice };
	CDirectiveMessage(Type type, Expression exp);
	virtual bool Validate();
	virtual void Encode() const {};
	virtual void writeTempData(TempData& tempData) const { };
private:
	Type errorType;
	Expression exp;
};

class CDirectiveSym: public CAssemblerCommand
{
public:
	CDirectiveSym(bool enable) {enabled = enable; };
	virtual bool Validate() { return false; };
	virtual void Encode() const { };
	virtual void writeTempData(TempData& tempData) const { };
	virtual void writeSymData(SymbolData& symData) const { symData.setEnabled(enabled); }
private:
	bool enabled;
};

// file: Commands/CDirectiveMessage.cpp

CDirectiveMessage::CDirectiveMessage(Type type, Expression exp)
{
	errorType = type;
	this->exp = exp;
}

bool CDirectiveMessage::Validate()
{
	std::wstring text;
	if (exp.evaluateString(text,true) == false)
	{
		Logger::queueError(Logger::Error,L"Invalid expression");
		return false;
	}

	switch (errorType)
	{
	case Type::Warning:
		Logger::queueError(Logger::Warning,text);
		break;
	case Type::Error:
		Logger::queueError(Logger::Error,text);
		break;
	case Type::Notice:
		Logger::queueError(Logger::Notice,text);
		break;
	}
	return false;
}

// file: Commands/CommandSequence.cpp

CommandSequence::CommandSequence()
	: CAssemblerCommand()
{

}

bool CommandSequence::Validate()
{
	bool result = false;

	for (const std::unique_ptr<CAssemblerCommand>& cmd: commands)
	{
		cmd->applyFileInfo();
		if (cmd->Validate())
			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);
	}
}

// file: Parser/DirectivesParser.cpp

#include <initializer_list>
#include <algorithm>

std::unique_ptr<CAssemblerCommand> parseDirectiveOpen(Parser& parser, int flags)
{
	std::vector<Expression> list;
	if (parser.parseExpressionList(list,2,3) == false)
		return nullptr;

	int64_t memoryAddress;
	std::wstring inputName, outputName;

	if (list[0].evaluateString(inputName,false) == false)
		return nullptr;

	if (list.back().evaluateInteger(memoryAddress) == false)
		return nullptr;

	auto file = ::make_unique<CDirectiveFile>();
	if (list.size() == 3)
	{
		if (list[1].evaluateString(outputName,false) == false)
			return nullptr;

		file->initCopy(inputName,outputName,memoryAddress);
		return file;
	} else {
		file->initOpen(inputName,memoryAddress);
		return file;
	}
}

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

	int64_t memoryAddress;
	std::wstring inputName, outputName;

	if (list[0].evaluateString(inputName,false) == false)
		return nullptr;

	if (list.back().evaluateInteger(memoryAddress) == false)
		return nullptr;

	auto file = ::make_unique<CDirectiveFile>();
	file->initCreate(inputName,memoryAddress);
	return file;
}

std::unique_ptr<CAssemblerCommand> parseDirectiveClose(Parser& parser, int flags)
{
	auto file = ::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) == false)
		return nullptr;

	std::wstring fileName;
	if (list[0].evaluateString(fileName,false) == false)
		return nullptr;

	auto incbin = ::make_unique<CDirectiveIncbin>(fileName);
	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() == false)
		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 ::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 ::make_unique<CDirectiveAlignFill>(UINT64_C(4),mode);

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

	if (list.size() == 2)
		return ::make_unique<CDirectiveAlignFill>(list[0],list[1],mode);
	else
		return ::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) == false)
		return nullptr;

	return ::make_unique<CDirectiveSkip>(list[0]);
}

std::unique_ptr<CAssemblerCommand> parseDirectiveHeaderSize(Parser& parser, int flags)
{
	Expression exp = parser.parseExpression();
	if (exp.isLoaded() == false)
		return nullptr;

	return ::make_unique<CDirectiveHeaderSize>(exp);
}

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

	std::wstring fileName;
	if (list[0].evaluateString(fileName,true) == false)
		return nullptr;

	if (list.size() == 2)
	{
		std::wstring ctorName;
		if (list[1].evaluateIdentifier(ctorName) == false)
			return nullptr;

		return ::make_unique<DirectiveObjImport>(fileName,ctorName);
	}

	return ::make_unique<DirectiveObjImport>(fileName);
}

std::unique_ptr<CAssemblerCommand> parseDirectiveConditional(Parser& parser, int flags)
{
	ConditionType type;
	std::wstring 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() == false)
		{
			parser.printError(start,L"Invalid condition");
			return ::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) == false)
			return nullptr;
		break;
	case DIRECTIVE_COND_IFNDEF:
		type = ConditionType::IFNDEF;
		if (parser.parseIdentifier(name) == false)
			return nullptr;
		break;
	}

	if(parser.nextToken().type != TokenType::Separator)
	{
		parser.printError(start,L"Directive not terminated");
		return nullptr;
	}

	parser.pushConditionalResult(condResult);
	std::unique_ptr<CAssemblerCommand> ifBlock = parser.parseCommandSequence(L'.', {L".else", L".elseif", L".elseifdef", L".elseifndef", L".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();
	const std::wstring stringValue = next.getStringValue();

	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 (stringValue == L".else")
	{
		elseBlock = parser.parseCommandSequence(L'.', {L".endif"});

		parser.eatToken();	// eat .endif
	} else if (stringValue == L".elseif")
	{
		elseBlock = parseDirectiveConditional(parser,DIRECTIVE_COND_IF);
	} else if (stringValue == L".elseifdef")
	{
		elseBlock = parseDirectiveConditional(parser,DIRECTIVE_COND_IFDEF);
	} else if (stringValue == L".elseifndef")
	{
		elseBlock = parseDirectiveConditional(parser,DIRECTIVE_COND_IFNDEF);
	} else if (stringValue != L".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 ::make_unique<DummyCommand>();
	}

	std::unique_ptr<CDirectiveConditional> cond;
	if (exp.isLoaded())
		cond = ::make_unique<CDirectiveConditional>(type,exp);
	else if (name.size() != 0)
		cond = ::make_unique<CDirectiveConditional>(type,name);
	else
		cond = ::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) == false)
		return nullptr;

	std::wstring fileName;
	if (list[0].evaluateString(fileName,true) == false)
	{
		parser.printError(start,L"Invalid file name");
		return nullptr;
	}

	TextFile::Encoding encoding = TextFile::GUESS;
	if (list.size() == 2)
	{
		std::wstring encodingName;
		if (list[1].evaluateString(encodingName,true) == false)
		{
			parser.printError(start,L"Invalid encoding name");
			return nullptr;
		}

		encoding = getEncodingFromString(encodingName);
	}

	return ::make_unique<TableCommand>(fileName,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) == false)
		return nullptr;

	auto data = ::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_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)
{
	Arch = &Mips;
	Mips.SetLoadDelay(false, 0);

	switch (flags)
	{
	case DIRECTIVE_MIPS_PSX:
		Mips.SetVersion(MARCH_PSX);
		return ::make_unique<ArchitectureCommand>(L".psx", L"");
	case DIRECTIVE_MIPS_PS2:
		Mips.SetVersion(MARCH_PS2);
		return ::make_unique<ArchitectureCommand>(L".ps2", L"");
	case DIRECTIVE_MIPS_PSP:
		Mips.SetVersion(MARCH_PSP);
		return ::make_unique<ArchitectureCommand>(L".psp", L"");
	case DIRECTIVE_MIPS_N64:
		Mips.SetVersion(MARCH_N64);
		return ::make_unique<ArchitectureCommand>(L".n64", L"");
	case DIRECTIVE_MIPS_RSP:
		Mips.SetVersion(MARCH_RSP);
		return ::make_unique<ArchitectureCommand>(L".rsp", L"");
	}

	return nullptr;
}

std::unique_ptr<CAssemblerCommand> parseDirectiveArmArch(Parser& parser, int flags)
{
#ifdef ARMIPS_ARM
	Arch = &Arm;

	switch (flags)
	{
	case DIRECTIVE_ARM_GBA:
		Arm.SetThumbMode(true);
		Arm.setVersion(AARCH_GBA);
		return ::make_unique<ArchitectureCommand>(L".gba\n.thumb", L".thumb");
	case DIRECTIVE_ARM_NDS:
		Arm.SetThumbMode(false);
		Arm.setVersion(AARCH_NDS);
		return ::make_unique<ArchitectureCommand>(L".nds\n.arm", L".arm");
	case DIRECTIVE_ARM_3DS:
		Arm.SetThumbMode(false);
		Arm.setVersion(AARCH_3DS);
		return ::make_unique<ArchitectureCommand>(L".3ds\n.arm", L".arm");
	case DIRECTIVE_ARM_BIG:
		Arm.SetThumbMode(false);
		Arm.setVersion(AARCH_BIG);
		return ::make_unique<ArchitectureCommand>(L".arm.big\n.arm", L".arm");
	case DIRECTIVE_ARM_LITTLE:
		Arm.SetThumbMode(false);
		Arm.setVersion(AARCH_LITTLE);
		return ::make_unique<ArchitectureCommand>(L".arm.little\n.arm", L".arm");
	}
#endif

	return nullptr;
}

std::unique_ptr<CAssemblerCommand> parseDirectiveArea(Parser& parser, int flags)
{
	std::vector<Expression> parameters;
	if (parser.parseExpressionList(parameters,1,2) == false)
		return nullptr;

	auto area = ::make_unique<CDirectiveArea>(parameters[0]);
	if (parameters.size() == 2)
		area->setFillExpression(parameters[1]);

	std::unique_ptr<CAssemblerCommand> content = parser.parseCommandSequence(L'.', {L".endarea"});
	parser.eatToken();

	area->setContent(std::move(content));
	return area;
}

std::unique_ptr<CAssemblerCommand> parseDirectiveErrorWarning(Parser& parser, int flags)
{
	const Token &tok = parser.nextToken();

	if (tok.type != TokenType::Identifier && tok.type != TokenType::String)
		return nullptr;

	std::wstring stringValue = tok.getStringValue();
	std::transform(stringValue.begin(),stringValue.end(),stringValue.begin(),::towlower);

	if (stringValue == L"on")
	{
		Logger::setErrorOnWarning(true);
		return ::make_unique<DummyCommand>();
	} else if (stringValue == L"off")
	{
		Logger::setErrorOnWarning(false);
		return ::make_unique<DummyCommand>();
	}

	return nullptr;
}

std::unique_ptr<CAssemblerCommand> parseDirectiveRelativeInclude(Parser& parser, int flags)
{
	const Token &tok = parser.nextToken();

	if (tok.type != TokenType::Identifier && tok.type != TokenType::String)
		return nullptr;

	std::wstring stringValue = tok.getStringValue();
	std::transform(stringValue.begin(),stringValue.end(),stringValue.begin(),::towlower);

	if (stringValue == L"on")
	{
		Global.relativeInclude = true;
		return ::make_unique<DummyCommand>();
	} else if (stringValue == L"off")
	{
		Global.relativeInclude = false;
		return ::make_unique<DummyCommand>();
	}

	return nullptr;
}

std::unique_ptr<CAssemblerCommand> parseDirectiveNocash(Parser& parser, int flags)
{
	const Token &tok = parser.nextToken();

	if (tok.type != TokenType::Identifier && tok.type != TokenType::String)
		return nullptr;

	std::wstring stringValue = tok.getStringValue();
	std::transform(stringValue.begin(),stringValue.end(),stringValue.begin(),::towlower);

	if (stringValue == L"on")
	{
		Global.nocash = true;
		return ::make_unique<DummyCommand>();
	} else if (stringValue == L"off")
	{
		Global.nocash = false;
		return ::make_unique<DummyCommand>();
	}

	return nullptr;
}

std::unique_ptr<CAssemblerCommand> parseDirectiveSym(Parser& parser, int flags)
{
	const Token &tok = parser.nextToken();

	if (tok.type != TokenType::Identifier && tok.type != TokenType::String)
		return nullptr;

	std::wstring stringValue = tok.getStringValue();
	std::transform(stringValue.begin(),stringValue.end(),stringValue.begin(),::towlower);

	if (stringValue == L"on")
		return ::make_unique<CDirectiveSym>(true);
	else if (stringValue == L"off")
		return ::make_unique<CDirectiveSym>(false);
	else
		return nullptr;
}

std::unique_ptr<CAssemblerCommand> parseDirectiveDefineLabel(Parser& parser, int flags)
{
	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() == false)
		return nullptr;

	const std::wstring stringValue = tok.getStringValue();
	if (Global.symbolTable.isValidSymbolName(stringValue) == false)
	{
		parser.printError(tok,L"Invalid label name \"%s\"",stringValue);
		return nullptr;
	}

	return ::make_unique<CAssemblerLabel>(stringValue,tok.getOriginalText(),value);
}

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,L"Directive not terminated");
		return nullptr;
	}

	auto func = ::make_unique<CDirectiveFunction>(tok.getStringValue(),tok.getOriginalText());
	std::unique_ptr<CAssemblerCommand> seq = parser.parseCommandSequence(L'.', {L".endfunc",L".endfunction",L".func",L".function"});

	const std::wstring stringValue = parser.peekToken().getStringValue();
	if (stringValue == L".endfunc" ||
		stringValue == L".endfunction")
	{
		parser.eatToken();
		if(parser.nextToken().type != TokenType::Separator)
		{
			parser.printError(tok,L"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 ::make_unique<CDirectiveMessage>(CDirectiveMessage::Type::Warning,exp);
	case DIRECTIVE_MSG_ERROR:
		return ::make_unique<CDirectiveMessage>(CDirectiveMessage::Type::Error,exp);
	case DIRECTIVE_MSG_NOTICE:
		return ::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) == false)
		return nullptr;

	std::wstring fileName;
	if (parameters[0].evaluateString(fileName,true) == false)
		return nullptr;

	fileName = getFullPathName(fileName);

	TextFile::Encoding encoding = TextFile::GUESS;
	if (parameters.size() == 2)
	{
		std::wstring encodingName;
		if (parameters[1].evaluateString(encodingName,true) == false
			&& parameters[1].evaluateIdentifier(encodingName) == false)
			return nullptr;

		encoding = getEncodingFromString(encodingName);
	}

	// don't include the file if it's inside a false block
	if (parser.isInsideTrueBlock() == false)
		return ::make_unique<DummyCommand>();

	if (fileExists(fileName) == false)
	{
		parser.printError(start,L"Included file \"%s\" does not exist",fileName);
		return nullptr;
	}

	TextFile f;
	if (f.open(fileName,TextFile::Read,encoding) == false)
	{
		parser.printError(start,L"Could not open included file \"%s\"",fileName);
		return nullptr;
	}

	return parser.parseFile(f);
}

const DirectiveMap directives = {
	{ L".open",				{ &parseDirectiveOpen,				DIRECTIVE_NOTINMEMORY } },
	{ L".openfile",			{ &parseDirectiveOpen,				DIRECTIVE_NOTINMEMORY } },
	{ L".create",			{ &parseDirectiveCreate,			DIRECTIVE_NOTINMEMORY } },
	{ L".createfile",		{ &parseDirectiveCreate,			DIRECTIVE_NOTINMEMORY } },
	{ L".close",			{ &parseDirectiveClose,				DIRECTIVE_NOTINMEMORY } },
	{ L".closefile",		{ &parseDirectiveClose,				DIRECTIVE_NOTINMEMORY } },
	{ L".incbin",			{ &parseDirectiveIncbin,			0 } },
	{ L".import",			{ &parseDirectiveIncbin,			0 } },
	{ L".org",				{ &parseDirectivePosition,			DIRECTIVE_POS_VIRTUAL } },
	{ L"org",				{ &parseDirectivePosition,			DIRECTIVE_POS_VIRTUAL } },
	{ L".orga",				{ &parseDirectivePosition,			DIRECTIVE_POS_PHYSICAL } },
	{ L"orga",				{ &parseDirectivePosition,			DIRECTIVE_POS_PHYSICAL } },
	{ L".headersize",		{ &parseDirectiveHeaderSize,		0 } },
	{ L".align",			{ &parseDirectiveAlignFill,			DIRECTIVE_ALIGN_VIRTUAL } },
	{ L".aligna",			{ &parseDirectiveAlignFill,			DIRECTIVE_ALIGN_PHYSICAL } },
	{ L".fill",				{ &parseDirectiveAlignFill,			DIRECTIVE_ALIGN_FILL } },
	{ L"defs",				{ &parseDirectiveAlignFill,			DIRECTIVE_ALIGN_FILL } },
	{ L".skip",				{ &parseDirectiveSkip,				0 } },

	{ L".if",				{ &parseDirectiveConditional,		DIRECTIVE_COND_IF } },
	{ L".ifdef",			{ &parseDirectiveConditional,		DIRECTIVE_COND_IFDEF } },
	{ L".ifndef",			{ &parseDirectiveConditional,		DIRECTIVE_COND_IFNDEF } },

	{ L".loadtable",		{ &parseDirectiveTable,				0 } },
	{ L".table",			{ &parseDirectiveTable,				0 } },
	{ L".byte",				{ &parseDirectiveData,				DIRECTIVE_DATA_8 } },
	{ L".halfword",			{ &parseDirectiveData,				DIRECTIVE_DATA_16 } },
	{ L".word",				{ &parseDirectiveData,				DIRECTIVE_DATA_32 } },
	{ L".doubleword",		{ &parseDirectiveData,				DIRECTIVE_DATA_64 } },
	{ L".db",				{ &parseDirectiveData,				DIRECTIVE_DATA_8 } },
	{ L".dh",				{ &parseDirectiveData,				DIRECTIVE_DATA_16|DIRECTIVE_NOCASHOFF } },
	{ L".dw",				{ &parseDirectiveData,				DIRECTIVE_DATA_32|DIRECTIVE_NOCASHOFF } },
	{ L".dd",				{ &parseDirectiveData,				DIRECTIVE_DATA_64|DIRECTIVE_NOCASHOFF } },
	{ L".dw",				{ &parseDirectiveData,				DIRECTIVE_DATA_16|DIRECTIVE_NOCASHON } },
	{ L".dd",				{ &parseDirectiveData,				DIRECTIVE_DATA_32|DIRECTIVE_NOCASHON } },
	{ L".dcb",				{ &parseDirectiveData,				DIRECTIVE_DATA_8 } },
	{ L".dcw",				{ &parseDirectiveData,				DIRECTIVE_DATA_16 } },
	{ L".dcd",				{ &parseDirectiveData,				DIRECTIVE_DATA_32 } },
	{ L".dcq",				{ &parseDirectiveData,				DIRECTIVE_DATA_64 } },
	{ L"db",				{ &parseDirectiveData,				DIRECTIVE_DATA_8 } },
	{ L"dh",				{ &parseDirectiveData,				DIRECTIVE_DATA_16|DIRECTIVE_NOCASHOFF } },
	{ L"dw",				{ &parseDirectiveData,				DIRECTIVE_DATA_32|DIRECTIVE_NOCASHOFF } },
	{ L"dd",				{ &parseDirectiveData,				DIRECTIVE_DATA_64|DIRECTIVE_NOCASHOFF } },
	{ L"dw",				{ &parseDirectiveData,				DIRECTIVE_DATA_16|DIRECTIVE_NOCASHON } },
	{ L"dd",				{ &parseDirectiveData,				DIRECTIVE_DATA_32|DIRECTIVE_NOCASHON } },
	{ L"dcb",				{ &parseDirectiveData,				DIRECTIVE_DATA_8 } },
	{ L"dcw",				{ &parseDirectiveData,				DIRECTIVE_DATA_16 } },
	{ L"dcd",				{ &parseDirectiveData,				DIRECTIVE_DATA_32 } },
	{ L"dcq",				{ &parseDirectiveData,				DIRECTIVE_DATA_64 } },
	{ L".float",			{ &parseDirectiveData,				DIRECTIVE_DATA_FLOAT } },
	{ L".double",			{ &parseDirectiveData,				DIRECTIVE_DATA_DOUBLE } },
	{ L".ascii",			{ &parseDirectiveData,				DIRECTIVE_DATA_ASCII } },
	{ L".asciiz",			{ &parseDirectiveData,				DIRECTIVE_DATA_ASCII|DIRECTIVE_DATA_TERMINATION } },
	{ L".string",			{ &parseDirectiveData,				DIRECTIVE_DATA_CUSTOM|DIRECTIVE_DATA_TERMINATION } },
	{ L".str",				{ &parseDirectiveData,				DIRECTIVE_DATA_CUSTOM|DIRECTIVE_DATA_TERMINATION } },
	{ L".stringn",			{ &parseDirectiveData,				DIRECTIVE_DATA_CUSTOM } },
	{ L".strn",				{ &parseDirectiveData,				DIRECTIVE_DATA_CUSTOM } },
	{ L".sjis",				{ &parseDirectiveData,				DIRECTIVE_DATA_SJIS|DIRECTIVE_DATA_TERMINATION } },
	{ L".sjisn",			{ &parseDirectiveData,				DIRECTIVE_DATA_SJIS } },

	{ L".psx",				{ &parseDirectiveMipsArch,			DIRECTIVE_MIPS_PSX } },
	{ L".ps2",				{ &parseDirectiveMipsArch,			DIRECTIVE_MIPS_PS2 } },
	{ L".psp",				{ &parseDirectiveMipsArch,			DIRECTIVE_MIPS_PSP } },
	{ L".n64",				{ &parseDirectiveMipsArch,			DIRECTIVE_MIPS_N64 } },
	{ L".rsp",				{ &parseDirectiveMipsArch,			DIRECTIVE_MIPS_RSP } },

	{ L".gba",				{ &parseDirectiveArmArch,			DIRECTIVE_ARM_GBA } },
	{ L".nds",				{ &parseDirectiveArmArch,			DIRECTIVE_ARM_NDS } },
	{ L".3ds",				{ &parseDirectiveArmArch,			DIRECTIVE_ARM_3DS } },
	{ L".arm.big",			{ &parseDirectiveArmArch,			DIRECTIVE_ARM_BIG } },
	{ L".arm.little",		{ &parseDirectiveArmArch,			DIRECTIVE_ARM_LITTLE } },

	{ L".area",				{ &parseDirectiveArea,				0 } },

	{ L".importobj",		{ &parseDirectiveObjImport,			0 } },
	{ L".importlib",		{ &parseDirectiveObjImport,			0 } },

	{ L".erroronwarning",	{ &parseDirectiveErrorWarning,		0 } },
	{ L".relativeinclude",	{ &parseDirectiveRelativeInclude,	0 } },
	{ L".nocash",			{ &parseDirectiveNocash,			0 } },
	{ L".sym",				{ &parseDirectiveSym,				0 } },

	{ L".definelabel",		{ &parseDirectiveDefineLabel,		0 } },
	{ L".function",			{ &parseDirectiveFunction,			DIRECTIVE_MANUALSEPARATOR } },
	{ L".func",				{ &parseDirectiveFunction,			DIRECTIVE_MANUALSEPARATOR } },

	{ L".warning",			{ &parseDirectiveMessage,			DIRECTIVE_MSG_WARNING } },
	{ L".error",			{ &parseDirectiveMessage,			DIRECTIVE_MSG_ERROR } },
	{ L".notice",			{ &parseDirectiveMessage,			DIRECTIVE_MSG_NOTICE } },

	{ L".include",			{ &parseDirectiveInclude,			0 } },
};

// file: Parser/ExpressionParser.cpp

static ExpressionInternal* expression(Tokenizer& tokenizer);

static bool allowFunctionCall = true;

void allowFunctionCallExpression(bool allow)
{
	allowFunctionCall = allow;
}

static ExpressionInternal* primaryExpression(Tokenizer& tokenizer)
{
	const Token &tok = tokenizer.peekToken();

	switch (tok.type)
	{
	case TokenType::Float:
		tokenizer.eatToken();
		return new ExpressionInternal(tok.floatValue);
	case TokenType::Identifier:
		{
			const std::wstring stringValue = tok.getStringValue();
			tokenizer.eatToken();
			if (stringValue == L".")
				return new ExpressionInternal(OperatorType::MemoryPos);
			else
				return new ExpressionInternal(stringValue,OperatorType::Identifier);
		}
	case TokenType::String:
		tokenizer.eatToken();
		return new ExpressionInternal(tok.getStringValue(),OperatorType::String);
	case TokenType::Integer:
		tokenizer.eatToken();
		return new ExpressionInternal(tok.intValue);
	case TokenType::LParen:
		{
			tokenizer.eatToken();
			ExpressionInternal* exp = expression(tokenizer);

			if (tokenizer.nextToken().type != TokenType::RParen)
			{
				delete exp;
				return nullptr;
			}

			return exp;
		}
	case TokenType::Invalid:
	default:
		break;
	}

	return nullptr;
}

static ExpressionInternal* postfixExpression(Tokenizer& tokenizer)
{
	if (allowFunctionCall &&
		tokenizer.peekToken(0).type == TokenType::Identifier &&
		tokenizer.peekToken(1).type == TokenType::LParen)
	{
		const std::wstring functionName = tokenizer.nextToken().getStringValue();
		tokenizer.eatToken();

		std::vector<ExpressionInternal*> parameters;
		while (tokenizer.peekToken().type != TokenType::RParen)
		{
			if (parameters.size() != 0 && tokenizer.nextToken().type != TokenType::Comma)
			{
				for (ExpressionInternal* exp: parameters)
					delete exp;
				return nullptr;
			}

			ExpressionInternal* exp = expression(tokenizer);
			if (exp == nullptr)
			{
				for (ExpressionInternal* exp: parameters)
					delete exp;
				return nullptr;
			}

			parameters.push_back(exp);
		}

		tokenizer.eatToken();

		return new ExpressionInternal(functionName,parameters);
	}

	return primaryExpression(tokenizer);
}

static ExpressionInternal* unaryExpression(Tokenizer& tokenizer)
{
	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 new ExpressionInternal(OperatorType::Neg,exp);
	case TokenType::Tilde:
		return new ExpressionInternal(OperatorType::BitNot,exp);
	case TokenType::Exclamation:
		return new ExpressionInternal(OperatorType::LogNot,exp);
	case TokenType::Degree:
		return new ExpressionInternal(OperatorType::ToString,exp);
	default:
		delete exp;
		return nullptr;
	}
}

static ExpressionInternal* multiplicativeExpression(Tokenizer& tokenizer)
{
	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();

		ExpressionInternal* exp2 = unaryExpression(tokenizer);
		if (exp2 == nullptr)
		{
			delete exp;
			return nullptr;
		}

		exp = new ExpressionInternal(op,exp,exp2);
	}

	return exp;
}

static ExpressionInternal* additiveExpression(Tokenizer& tokenizer)
{
	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();

		ExpressionInternal* exp2 = multiplicativeExpression(tokenizer);
		if (exp2 == nullptr)
		{
			delete exp;
			return nullptr;
		}

		exp = new ExpressionInternal(op,exp,exp2);
	}

	return exp;
}

static ExpressionInternal* shiftExpression(Tokenizer& tokenizer)
{
	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();

		ExpressionInternal* exp2 = additiveExpression(tokenizer);
		if (exp2 == nullptr)
		{
			delete exp;
			return nullptr;
		}

		exp = new ExpressionInternal(op,exp,exp2);
	}

	return exp;
}

static ExpressionInternal* relationalExpression(Tokenizer& tokenizer)
{
	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();

		ExpressionInternal* exp2 = shiftExpression(tokenizer);
		if (exp2 == nullptr)
		{
			delete exp;
			return nullptr;
		}

		exp = new ExpressionInternal(op,exp,exp2);
	}

	return exp;
}

static ExpressionInternal* equalityExpression(Tokenizer& tokenizer)
{
	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();

		ExpressionInternal* exp2 = relationalExpression(tokenizer);
		if (exp2 == nullptr)
		{
			delete exp;
			return nullptr;
		}

		exp = new ExpressionInternal(op,exp,exp2);
	}

	return exp;
}

static ExpressionInternal* andExpression(Tokenizer& tokenizer)
{
	ExpressionInternal* exp = equalityExpression(tokenizer);
	if (exp == nullptr)
		return nullptr;

	while (tokenizer.peekToken().type == TokenType::BitAnd)
	{
		tokenizer.eatToken();

		ExpressionInternal* exp2 = equalityExpression(tokenizer);
		if (exp2 == nullptr)
		{
			delete exp;
			return nullptr;
		}

		exp = new ExpressionInternal(OperatorType::BitAnd,exp,exp2);
	}

	return exp;
}

static ExpressionInternal* exclusiveOrExpression(Tokenizer& tokenizer)
{
	ExpressionInternal* exp = andExpression(tokenizer);
	if (exp == nullptr)
		return nullptr;

	while (tokenizer.peekToken().type == TokenType::Caret)
	{
		tokenizer.eatToken();

		ExpressionInternal* exp2 = andExpression(tokenizer);
		if (exp2 == nullptr)
		{
			delete exp;
			return nullptr;
		}

		exp = new ExpressionInternal(OperatorType::Xor,exp,exp2);
	}

	return exp;
}

static ExpressionInternal* inclusiveOrExpression(Tokenizer& tokenizer)
{
	ExpressionInternal* exp = exclusiveOrExpression(tokenizer);
	if (exp == nullptr)
		return nullptr;

	while (tokenizer.peekToken().type == TokenType::BitOr)
	{
		tokenizer.eatToken();

		ExpressionInternal* exp2 = exclusiveOrExpression(tokenizer);
		if (exp2 == nullptr)
		{
			delete exp;
			return nullptr;
		}

		exp = new ExpressionInternal(OperatorType::BitOr,exp,exp2);
	}

	return exp;
}

static ExpressionInternal* logicalAndExpression(Tokenizer& tokenizer)
{
	ExpressionInternal* exp = inclusiveOrExpression(tokenizer);
	if (exp == nullptr)
		return nullptr;

	while (tokenizer.peekToken().type == TokenType::LogAnd)
	{
		tokenizer.eatToken();

		ExpressionInternal* exp2 = inclusiveOrExpression(tokenizer);
		if (exp2 == nullptr)
		{
			delete exp;
			return nullptr;
		}

		exp = new ExpressionInternal(OperatorType::LogAnd,exp,exp2);
	}

	return exp;
}

static ExpressionInternal* logicalOrExpression(Tokenizer& tokenizer)
{
	ExpressionInternal* exp = logicalAndExpression(tokenizer);
	if (exp == nullptr)
		return nullptr;

	while (tokenizer.peekToken().type == TokenType::LogOr)
	{
		tokenizer.eatToken();

		ExpressionInternal* exp2 = logicalAndExpression(tokenizer);
		if (exp2 == nullptr)
		{
			delete exp;
			return nullptr;
		}

		exp = new ExpressionInternal(OperatorType::LogOr,exp,exp2);
	}

	return exp;
}

static ExpressionInternal* conditionalExpression(Tokenizer& tokenizer)
{
	ExpressionInternal* exp = logicalOrExpression(tokenizer);
	if (exp == nullptr)
		return nullptr;

	// check a ? b : c
	if (tokenizer.peekToken().type != TokenType::Question)
		return exp;

	tokenizer.eatToken();
	ExpressionInternal* second = expression(tokenizer);

	if (second != nullptr && tokenizer.nextToken().type == TokenType::Colon)
	{
		ExpressionInternal* third = expression(tokenizer);
		if (third != nullptr)
			return new ExpressionInternal(OperatorType::TertiaryIf,exp,second,third);

		delete third;
	}

	delete second;
	delete exp;
	return nullptr;
}

static 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
	ExpressionInternal* exp = expression(tokenizer);
	if (exp == nullptr)
		tokenizer.setPosition(pos);

	Expression result;
	result.setExpression(exp, inUnknownOrFalseBlock);
	return result;
}

// file: Parser/Parser.cpp

inline bool isPartOfList(const std::wstring& value, const std::initializer_list<const wchar_t*>& terminators)
{
	for (const wchar_t* 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);
}

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() == false)
	{
		printError(start,L"Parameter failure");
		getTokenizer()->skipLookahead();
		valid = false;
	}

	while (peekToken().type == TokenType::Comma)
	{
		eatToken();

		exp = parseExpression();
		list.push_back(exp);

		if (exp.isLoaded() == false)
		{
			printError(start,L"Parameter failure");
			getTokenizer()->skipLookahead();
			valid = false;
		}
	}

	if (list.size() < (size_t) min)
	{
		printError(start,L"Not enough parameters (min %d)",min);
		return false;
	}

	if (max != -1 && (size_t) max < list.size())
	{
		printError(start,L"Too many parameters (max %d)",max);
		return false;
	}

	return valid;
}

bool Parser::parseIdentifier(std::wstring& dest)
{
	const Token& tok = nextToken();
	if (tok.type != TokenType::Identifier)
		return false;

	dest = tok.getStringValue();
	return true;
}

std::unique_ptr<CAssemblerCommand> Parser::parseCommandSequence(wchar_t indicator, const std::initializer_list<const wchar_t*> terminators)
{
	auto sequence = ::make_unique<CommandSequence>();

	bool foundTermination = false;
	while (atEnd() == false)
	{
		const Token &next = peekToken();

		if(next.type == TokenType::Separator)
		{
			eatToken();
			continue;
		}

		if (next.stringValueStartsWith(indicator) && isPartOfList(next.getStringValue(), terminators))
		{
			foundTermination = true;
			break;
		}

		bool foundSomething = false;
		while (checkEquLabel() || checkMacroDefinition())
		{
			// 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() == false)
		{
			continue;
		}

		sequence->addCommand(std::move(cmd));
	}

	if (!foundTermination && terminators.size())
	{
		std::wstring expected;
		for (const wchar_t* terminator : terminators)
		{
			if (!expected.empty())
				expected += L", ";
			expected += terminator;
		}

		Logger::printError(Logger::Error, L"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) == false)
		return nullptr;

	std::unique_ptr<CAssemblerCommand> result = parse(&tokenizer,virtualFile,file.getFileName());

	if (file.isFromMemory() == false)
		Global.FileInfo.TotalLineCount += file.getNumLines();

	return result;
}

std::unique_ptr<CAssemblerCommand> Parser::parseString(const std::wstring& text)
{
	TextFile file;
	file.openMemory(text);
	return parseFile(file,true);
}

std::unique_ptr<CAssemblerCommand> Parser::parseTemplate(const std::wstring& text, std::initializer_list<AssemblyTemplateArgument> variables)
{
	std::wstring 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,L"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::wstring stringValue = tok.getStringValue();

	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 == true)
			continue;
		if ((directive.flags & DIRECTIVE_NOCASHON) && Global.nocash == false)
			continue;
		if ((directive.flags & DIRECTIVE_NOTINMEMORY) && Global.memoryMode == true)
			continue;

		if (directive.flags & DIRECTIVE_MIPSRESETDELAY)
			Arch->NextSection();

		eatToken();
		std::unique_ptr<CAssemblerCommand> result = directive.function(*this,directive.flags);
		if (result == nullptr)
		{
			if (hasError() == false)
				printError(tok,L"Directive parameter failure");
			return nullptr;
		} else if (!(directive.flags & DIRECTIVE_MANUALSEPARATOR) && nextToken().type != TokenType::Separator)
		{
			printError(tok,L"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 std::wstring& name)
{
	if (entries.size() >= 150)
	{
		Logger::queueError(Logger::Error, L"Max include/recursion depth reached");
		return nullptr;
	}

	FileEntry entry;
	entry.tokenizer = tokenizer;
	entry.virtualFile = virtualFile;

	if (virtualFile == false && name.empty() == false)
	{
		entry.fileNum = (int) Global.FileInfo.FileList.size();
		Global.FileInfo.FileList.push_back(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 std::wstring& name, const std::wstring& value)
{
	// parse value string
	TextFile f;
	f.openMemory(value);

	FileTokenizer tok;
	tok.init(&f);

	TokenizerPosition start = tok.getPosition();
	while (tok.atEnd() == false && tok.peekToken().type != TokenType::Separator)
	{
		const Token& token = tok.nextToken();
		if (token.type == TokenType::Identifier && token.getStringValue() == name)
		{
			printError(startToken,L"Recursive equ definition for \"%s\" not allowed",name);
			return;
		}

		if (token.type == TokenType::Equ)
		{
			printError(startToken,L"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)
		{
			std::wstring name = peekToken(0).getStringValue();
			std::wstring value = peekToken(pos+1).getStringValue();
			eatTokens(pos+2);

			// skip the equ if it's inside a false conditional block
			if (isInsideTrueBlock() == false)
				return true;

			// equs can't be inside blocks whose condition can only be
			// evaluated during validation
			if (isInsideUnknownBlock())
			{
				printError(start,L"equ not allowed inside of block with non-trivial condition");
				return true;
			}

			// equs are not allowed in macros
			if (initializingMacro)
			{
				printError(start,L"equ not allowed in macro");
				return true;
			}

			if (Global.symbolTable.isValidSymbolName(name) == false)
			{
				printError(start,L"Invalid equation name \"%s\"",name);
				return true;
			}

			if (Global.symbolTable.symbolExists(name,Global.FileInfo.FileNum,Global.Section))
			{
				printError(start,L"Equation name \"%s\" already defined",name);
				return true;
			}

			addEquation(start,name,value);
			return true;
		}
	}

	return false;
}

bool Parser::checkMacroDefinition()
{
	const Token& first = peekToken();
	if (first.type != TokenType::Identifier)
		return false;

	if (!first.stringValueStartsWith(L'.') || first.getStringValue() != L".macro")
		return false;

	eatToken();

	// nested macro definitions are not allowed
	if (initializingMacro)
	{
		printError(first,L"Nested macro definitions not allowed");
		while (!atEnd())
		{
			const Token& token = nextToken();
			if (token.type == TokenType::Identifier && token.getStringValue() == L".endmacro")
				break;
		}

		return true;
	}

	std::vector<Expression> parameters;
	if (parseExpressionList(parameters,1,-1) == false)
		return false;

	ParserMacro macro;
	macro.counter = 0;

	// load name
	if (parameters[0].evaluateIdentifier(macro.name) == false)
		return false;

	// load parameters
	for (size_t i = 1; i < parameters.size(); i++)
	{
		std::wstring name;
		if (parameters[i].evaluateIdentifier(name) == false)
			return false;

		macro.parameters.push_back(name);
	}

	if(nextToken().type != TokenType::Separator)
	{
		printError(first,L"Macro directive not terminated");
		return false;
	}

	// load macro content

	TokenizerPosition start = getTokenizer()->getPosition();
	bool valid = false;
	while (atEnd() == false)
	{
		const Token& tok = nextToken();
		if (tok.type == TokenType::Identifier && tok.getStringValue() == L".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, L"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, L"Macro \"%s\" already defined", macro.name);
		return false;
	}

	// no .endmacro, not valid
	if (valid == false)
	{
		printError(first, L"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,L"Endmacro directive not terminated");
		return false;
	}

	macros[macro.name] = macro;
	return true;
}

std::unique_ptr<CAssemblerCommand> Parser::parseMacroCall()
{
	const Token& start = peekToken();
	if (start.type != TokenType::Identifier)
		return nullptr;

	auto it = macros.find(start.getStringValue());
	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<std::wstring> identifierParameters;
	for (size_t i = 0; i < macro.parameters.size(); i++)
	{
		if (peekToken().type == TokenType::Separator)
		{
			printError(start,L"Too few macro arguments (%d vs %d)",i,macro.parameters.size());
			return nullptr;
		}

		if (i != 0)
		{
			if (nextToken().type != TokenType::Comma)
			{
				printError(start,L"Macro arguments not comma-separated");
				return nullptr;
			}
		}

		TokenizerPosition startPos = getTokenizer()->getPosition();
		Expression exp = parseExpression();
		if (exp.isLoaded() == false)
		{
			printError(start,L"Invalid macro argument expression");
			return nullptr;
		}

		TokenizerPosition endPos = getTokenizer()->getPosition();
		std::vector<Token> tokens = getTokenizer()->getTokens(startPos,endPos);

		// remember any single identifier parameters for the label replacement
		if (tokens.size() == 1 && tokens[0].type == TokenType::Identifier)
			identifierParameters.insert(tokens[0].getStringValue());

		// 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)
		{
			eatToken();
			parseExpression();
			count++;
		}

		printError(start,L"Too many macro arguments (%d vs %d)",count,macro.parameters.size());
		return nullptr;
	}

	if(nextToken().type != TokenType::Separator)
	{
		printError(start,L"Macro call not terminated");
		return nullptr;
	}

	// skip macro instantiation in known false blocks
	if (!isInsideUnknownBlock() && !isInsideTrueBlock())
		return ::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 ::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 std::wstring& 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::wstring fullName;
		if (Global.symbolTable.isLocalSymbol(label))
			fullName = formatString(L"@@%s_%s_%08X",macro.name,label.substr(2),macro.counter);
		else if (Global.symbolTable.isStaticSymbol(label))
			fullName = formatString(L"@%s_%s_%08X",macro.name,label.substr(1),macro.counter);
		else
			fullName = formatString(L"%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 std::wstring name = start.getStringValue();
		eatTokens(2);

		if (initializingMacro)
			macroLabels.insert(name);

		if (Global.symbolTable.isValidSymbolName(name) == false)
		{
			printError(start,L"Invalid label name \"%s\"",name);
			return nullptr;
		}

		return ::make_unique<CAssemblerLabel>(name,start.getOriginalText());
	}

	return nullptr;
}

std::unique_ptr<CAssemblerCommand> Parser::handleError()
{
	// skip the rest of the statement
	while (!atEnd() && nextToken().type != TokenType::Separator);

	clearError();
	return ::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 == false && 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 ::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 = Arch->parseDirective(*this)) != nullptr)
		return command;
	if (hasError())
		return handleError();

	if ((command = parseDirective(directives)) != nullptr)
		return command;
	if (hasError())
		return handleError();

	if ((command = Arch->parseOpcode(*this)) != nullptr)
		return command;
	if (hasError())
		return handleError();

	const Token& token = peekToken();
	printError(token,L"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.getStringValue())
				{
					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;
}

// file: Parser/Tokenizer.cpp
#include <algorithm>


//
// Tokenizer
//

std::vector<std::vector<Token>> Tokenizer::equValues;

Tokenizer::Tokenizer()
{
	position.it = tokens.begin();
	invalidToken.type = TokenType::Invalid;
	invalidToken.setOriginalText(L"Unexpected end of token stream");
}

bool Tokenizer::processElement(TokenList::iterator& it)
{
	if (it == tokens.end())
		return false;

	while ((*it).checked == false)
	{
		bool replaced = false;
		if ((*it).type == TokenType::Identifier)
		{
			const std::wstring stringValue = (*it).getStringValue();
			for (const Replacement& replacement: replacements)
			{
				// if the identifier matches, add all of its tokens
				if (replacement.identifier == stringValue)
				{
					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 (stringValue != (*it).getStringValue())
						replaced = true;
					break;
				}
			}

			if (replaced)
				continue;

			// check for equs
			size_t index;
			if (Global.symbolTable.findEquation(stringValue,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 == false)
			(*it).checked = true;
	}

	return true;
}

const Token& Tokenizer::nextToken()
{
	if (processElement(position.it) == false)
		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) == false)
			return invalidToken;

		it++;
	}

	if (processElement(it) == false)
		return invalidToken;

	return *it;
}

void Tokenizer::eatTokens(int num)
{
	for (int i = 0; i < num; i++)
	{
		if (processElement(position.it) == false)
			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 std::wstring& identifier, std::vector<Token>& tokens)
{
	Replacement replacement { identifier, tokens };
	replacements.push_back(replacement);
}

void Tokenizer::registerReplacement(const std::wstring& identifier, const std::wstring& newValue)
{
	// Ensure the new identifier is lower case as it would be as a normally parsed string
	std::wstring lowerCase = newValue;
	std::transform(lowerCase.begin(), lowerCase.end(), lowerCase.begin(), ::towlower);

	Token tok;
	tok.type = TokenType::Identifier;
	tok.setStringValue(lowerCase);
	tok.setOriginalText(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::wstring& text, size_t pos)
{
	if (pos >= text.size())
		return false;

	return text[pos] == ' ' || text[pos] == '\t';
}

inline bool isComment(const std::wstring& 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::wstring& text, size_t pos)
{
	if (pos >= text.size())
		return false;

	return text[pos] == '\\';
}

inline bool isBlockComment(const std::wstring& text, size_t pos){
	if (pos+1 < text.size() && text[pos+0] == '/' && text[pos+1] == '*')
		return true;

	return false;
}

inline bool isBlockCommentEnd(const std::wstring& text, size_t pos){
	if (pos+1 < text.size() && text[pos+0] == '*' && text[pos+1] == '/')
		return true;

	return false;
}

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,L"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.setOriginalText(currentLine,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.setOriginalText(currentLine,linePos,length);
	token.intValue = value;

	linePos += length;
}

void FileTokenizer::createToken(TokenType type, size_t length, double value)
{
	token.type = type;
	token.line = lineNumber;
	token.column = linePos+1;
	token.setOriginalText(currentLine,linePos,length);
	token.floatValue = value;

	linePos += length;
}

void FileTokenizer::createToken(TokenType type, size_t length, const std::wstring& value)
{
	createToken(type, length, value, 0, value.length());
}

void FileTokenizer::createToken(TokenType type, size_t length, const std::wstring& value, size_t valuePos, size_t valueLength)
{
	token.type = type;
	token.line = lineNumber;
	token.column = linePos+1;
	token.setOriginalText(currentLine,linePos,length);
	token.setStringValue(value,valuePos,valueLength);

	linePos += length;
}

void FileTokenizer::createTokenCurrentString(TokenType type, size_t length)
{
	token.type = type;
	token.line = lineNumber;
	token.column = linePos+1;
	token.setStringAndOriginalValue(currentLine,linePos,length);

	linePos += length;
}

bool FileTokenizer::parseOperator()
{
	wchar_t first = currentLine[linePos];
	wchar_t 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 L'\U000000B0':	// degree sign
		createToken(TokenType::Degree,1);
		return true;
	}

	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::wstring str = currentLine.substr(start, end - start);
	wchar_t* end_ptr;

	result = wcstod(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);

	wchar_t first = currentLine[pos];

	// character constants
	if (first == '\'' && pos+2 < currentLine.size() && currentLine[pos+2] == '\'')
	{
		createToken(TokenType::Integer,3,(int64_t)currentLine[pos+1]);
		return std::move(token);
	}

	// strings
	if (first == '"')
	{
		std::wstring 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,L"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' && towlower(currentLine[start+1]) == 'x';

		while (end < currentLine.size() && (iswalnum(currentLine[end]) || currentLine[end] == '.'))
		{
			if (currentLine[end] == '.')
			{
				if (foundExp || foundPoint)
					isValid = false;
				foundPoint = true;
			} else if (towlower(currentLine[end]) == 'h' && !foundExpSign) {
				isHex = true;
			} else if (towlower(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() || !iswalnum(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) == false)
			{
				createTokenCurrentString(TokenType::NumberString,end-start);
				return std::move(token);
			}

			createToken(TokenType::Integer,end-start,value);
		} else { // isFloat
			double value;
			if (isValid == false)
			{
				createToken(TokenType::Invalid,end-start,L"Invalid floating point number");
				return std::move(token);
			}

			if (convertFloat(start,end,value) == false)
			{
				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)
	{
		std::wstring text = formatString(L"Invalid input '%c'",currentLine[pos]);
		createToken(TokenType::Invalid,1,text);
		return std::move(token);
	}

	std::wstring 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(), ::towlower);
		textLowered = true;
	}

	if (text == L"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::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,
						L"Unexpected character after line continuation character");
					addToken(token);
				}

				addSeparator = false;
			} else if(linePos < currentLine.size())
			{
				addToken(std::move(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;
}

// file: Util/ByteArray.cpp

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 std::wstring& fileName, long start, size_t size)
{
	ByteArray ret;

	FILE* input = openFile(fileName,OpenFileMode::ReadBinary);
	if (input == nullptr)
		return ret;

	fseek(input,0,SEEK_END);
	long fileSize = ftell(input);

	if (start >= fileSize)
	{
		fclose(input);
		return ret;
	}

	if (size == 0 || start+(long)size > fileSize)
		size = fileSize-start;

	fseek(input,start,SEEK_SET);

	ret.grow(size);
	ret.size_ = fread(ret.data(),1,size,input);
	fclose(input);

	return ret;
}

bool ByteArray::toFile(const std::wstring& fileName)
{
	FILE* output = openFile(fileName,OpenFileMode::WriteBinary);
	if (output == nullptr) return false;
	size_t length = fwrite(data_,1,size_,output);
	fclose(output);
	return length == size_;
}

// 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;
}

// file: Util/EncodingTable.cpp

#define MAXHEXLENGTH 32

Trie::Trie()
{
	Node root { 0, false, 0 };
	nodes.push_back(root);
}

void Trie::insert(const wchar_t* 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;
}

void Trie::insert(wchar_t character, size_t value)
{
	wchar_t str[2];
	str[0] = character;
	str[1] = 0;
	insert(str,value);
}

bool Trie::findLongestPrefix(const wchar_t* 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()
{

}

EncodingTable::~EncodingTable()
{

}

void EncodingTable::clear()
{
	hexData.clear();
	entries.clear();
}

int parseHexString(std::wstring& hex, unsigned char* dest)
{
	for (size_t i = 0; i < hex.size(); i++)
	{
		wchar_t source = towlower(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 std::wstring& fileName, TextFile::Encoding encoding)
{
	unsigned char hexBuffer[MAXHEXLENGTH];

	TextFile input;
	if (input.open(fileName,TextFile::Read,encoding) == false)
		return false;

	hexData.clear();
	entries.clear();
	setTerminationEntry((unsigned char*)"\0",1);

	while (!input.atEnd())
	{
		std::wstring line = input.readLine();
		if (line.empty() || line[0] == '*') continue;

		if (line[0] == '/')
		{
			std::wstring 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::wstring hex = line.substr(0,pos);
			std::wstring 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::wstring& 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::addEntry(unsigned char* hex, size_t hexLength, wchar_t value)
{
	if (value == '\0')
		return;

	// insert into trie
	size_t index = entries.size();
	lookup.insert(value,index);

	// add entry
	TableEntry entry;
	entry.hexPos = hexData.append(hex,hexLength);
	entry.hexLen = hexLength;
	entry.valueLen = 1;

	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::wstring& str, bool writeTermination)
{
	ByteArray result;

	size_t pos = 0;
	while (pos < str.size())
	{
		size_t index;
		if (lookup.findLongestPrefix(str.c_str()+pos,index) == false)
		{
			// 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;
}

// file: Util/FileClasses.cpp

const wchar_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 wchar_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 wchar_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 wchar_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 wchar_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,
};

wchar_t sjisToUnicode(unsigned short SjisCharacter)
{
	if (SjisCharacter < 0x80)
	{
		return SjisCharacter;
	} else if (SjisCharacter < 0x100)
	{
		return SjisToUnicodeTable1[SjisCharacter-0x80];
	}

	if ((SjisCharacter & 0xFF) < 0x40) return 0xFFFF;

	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 0xFFFF;
	}
}

BinaryFile::BinaryFile()
{
	handle = nullptr;
}

BinaryFile::~BinaryFile()
{
	close();
}

bool BinaryFile::open(const std::wstring& fileName, Mode mode)
{
	setFileName(fileName);
	return open(mode);
}

bool BinaryFile::open(Mode mode)
{
	if (isOpen())
		close();

	this->mode = mode;

	// open all files as binary due to unicode
	switch (mode)
	{
	case Read:
		handle = openFile(fileName,OpenFileMode::ReadBinary);
		break;
	case Write:
		handle = openFile(fileName,OpenFileMode::WriteBinary);
		break;
	case ReadWrite:
		handle = openFile(fileName,OpenFileMode::ReadWriteBinary);
		break;
	default:
		return false;
	}

	if (handle == nullptr)
		return false;

	if (mode != Write)
	{
		fseek(handle,0,SEEK_END);
		size_ = ftell(handle);
		fseek(handle,0,SEEK_SET);
	}

	return true;
}

void BinaryFile::close()
{
	if (isOpen())
	{
		fclose(handle);
		handle = nullptr;
	}
}

size_t BinaryFile::read(void* dest, size_t length)
{
	if (isOpen() == false || mode == Write)
		return 0;

	return fread(dest,1,length,handle);
}

size_t BinaryFile::write(void* source, size_t length)
{
	if (isOpen() == false || mode == Read)
		return 0;

	return fwrite(source,1,length,handle);
}

const size_t TEXTFILE_BUF_MAX_SIZE = 4096;

TextFile::TextFile()
{
	handle = nullptr;
	recursion = false;
	errorRetrieved = false;
	fromMemory = false;
	bufPos = 0;
	lineCount = 0;
}

TextFile::~TextFile()
{
	close();
}

void TextFile::openMemory(const std::wstring& content)
{
	fromMemory = true;
	this->content = content;
	contentPos = 0;
	size_ = (long) content.size();
	encoding = UTF16LE;
	mode = Read;
	lineCount = 0;
}

bool TextFile::open(const std::wstring& 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:
		handle = openFile(fileName,OpenFileMode::ReadBinary);
		break;
	case Write:
		handle = openFile(fileName,OpenFileMode::WriteBinary);
		if (handle == nullptr)
			return false;

		buf.resize(TEXTFILE_BUF_MAX_SIZE);
		if (encoding != ASCII)
		{
			encoding = UTF8;
			writeCharacter(0xFEFF);
		}
		break;
	default:
		return false;
	}

	if (handle == nullptr)
		return false;

	// detect encoding
	unsigned short num;
	contentPos = 0;

	if (mode == Read)
	{
		fseek(handle,0,SEEK_END);
		size_ = ftell(handle);
		fseek(handle,0,SEEK_SET);

		if (fread(&num,2,1,handle) == 1)
		{
			switch (num)
			{
			case 0xFFFE:
				encoding = UTF16BE;
				contentPos += 2;
				break;
			case 0xFEFF:
				encoding = UTF16LE;
				contentPos += 2;
				break;
			case 0xBBEF:
				if (fgetc(handle) == 0xBF)
				{
					encoding = UTF8;
					contentPos += 3;
					break;
				} // fallthrough
			default:
				if (defaultEncoding == GUESS)
				{
					encoding = UTF8;
					guessedEncoding = true;
				}
				fseek(handle,0,SEEK_SET);
				break;
			}
		} else {
			if (defaultEncoding == GUESS)
			{
				encoding = UTF8;
				guessedEncoding = true;
			}
		}
	}

	return true;
}

void TextFile::close()
{
	if (isOpen() && !fromMemory)
	{
		bufDrainWrite();
		fclose(handle);
		handle = nullptr;
	}
	bufPos = 0;
}

long TextFile::tell()
{
	return (long) contentPos;
}

void TextFile::seek(long pos)
{
	if (fromMemory)
		contentPos = pos;
	else
		fseek(handle,pos,SEEK_SET);
}

void TextFile::bufFillRead()
{
	assert(mode == Read);

	buf.resize(TEXTFILE_BUF_MAX_SIZE);
	size_t read = fread(&buf[0], 1, TEXTFILE_BUF_MAX_SIZE, handle);
	buf.resize(read);

	bufPos = 0;
}

wchar_t TextFile::readCharacter()
{
	wchar_t value;

	switch (encoding)
	{
	case UTF8:
		{
			value = bufGetChar();
			contentPos++;

			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 = formatString(L"One or more invalid UTF-8 characters in this file");
			}

			for (int i = 0; i < extraBytes; i++)
			{
				int b = bufGetChar();
				contentPos++;

				if ((b & 0xC0) != 0x80)
				{
					errorText = formatString(L"One or more invalid UTF-8 characters in this file");
				}

				value = (value << 6) | (b & 0x3F);
			}
		}
		break;
	case UTF16LE:
		if (fromMemory)
		{
			value = content[contentPos++];
		} else {
			value = bufGet16LE();
			contentPos += 2;
		}
		break;
	case UTF16BE:
		value = bufGet16BE();
		contentPos += 2;
		break;
	case SJIS:
		{
			unsigned short sjis = bufGetChar();
			contentPos++;
			if (sjis >= 0x80)
			{
				sjis = (sjis << 8) | bufGetChar();
				contentPos++;
			}
			value = sjisToUnicode(sjis);
			if (value == (wchar_t)-1)
			{
				errorText = formatString(L"One or more invalid Shift-JIS characters in this file");
			}
		}
		break;
	case ASCII:
		value = bufGetChar();
		contentPos++;
		break;
	case GUESS:
		errorText = formatString(L"Cannot read from GUESS encoding");
		break;
	}

	// convert \r\n to \n
	if (value == L'\r' && recursion == false && atEnd() == false)
	{
		recursion = true;
		long pos = tell();
		wchar_t nextValue = readCharacter();
		recursion = false;

		if (nextValue == L'\n')
			return nextValue;
		seek(pos);
	}

	return value;
}

std::wstring TextFile::readLine()
{
	std::wstring result;
	wchar_t value;

	if (isOpen())
	{
		while (tell() < size() && (value = readCharacter()) != L'\n')
		{
			result += value;
		}
	}

	lineCount++;
	return result;
}

StringList TextFile::readAll()
{
	StringList 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();
		fwrite(p, 1, len, handle);
	}
	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()
{
	fwrite(&buf[0], 1, bufPos, handle);
	bufPos = 0;
}

void TextFile::writeCharacter(wchar_t character)
{
	if (mode != Write) return;

	// only support utf8 for now
	if (character < 0x80)
	{
#ifdef _WIN32
		if (character == L'\n')
		{
			bufPut('\r');
		}
#endif
		bufPut(character & 0x7F);
	} else if (encoding != ASCII)
	{
		if (character < 0x800)
		{
			bufPut(0xC0 | ((character >> 6) & 0x1F));
			bufPut(0x80 | (character & 0x3F));
		} else {
			bufPut(0xE0 | ((character >> 12) & 0xF));
			bufPut(0x80 | ((character >> 6) & 0x3F));
			bufPut(0x80 | (character & 0x3F));
		}
	}
}

void TextFile::write(const wchar_t* line)
{
	if (mode != Write) return;
	while (*line != 0)
	{
		writeCharacter(*line);
		line++;
	}
}

void TextFile::write(const std::wstring& line)
{
	write(line.c_str());
}

void TextFile::write(const char* line)
{
	if (mode != Write) return;
	while (*line != 0)
	{
		writeCharacter(*line);
		line++;
	}
}

void TextFile::write(const std::string& line)
{
	write(line.c_str());
}

void TextFile::writeLine(const wchar_t* line)
{
	if (mode != Write) return;
	write(line);
	writeCharacter(L'\n');
}

void TextFile::writeLine(const std::wstring& line)
{
	writeLine(line.c_str());
}

void TextFile::writeLine(const char* line)
{
	if (mode != Write) return;
	write(line);
	writeCharacter(L'\n');
}

void TextFile::writeLine(const std::string& line)
{
	writeLine(line.c_str());
}

void TextFile::writeLines(StringList& list)
{
	for (size_t i = 0; i < list.size(); i++)
	{
		writeLine(list[i]);
	}
}

struct EncodingValue
{
	const wchar_t* name;
	TextFile::Encoding value;
};

const EncodingValue encodingValues[] = {
	{ L"sjis",			TextFile::SJIS },
	{ L"shift-jis",		TextFile::SJIS },
	{ L"utf8",			TextFile::UTF8 },
	{ L"utf-8",			TextFile::UTF8 },
	{ L"utf16",			TextFile::UTF16LE },
	{ L"utf-16",		TextFile::UTF16LE },
	{ L"utf16-be",		TextFile::UTF16BE },
	{ L"utf-16-be",		TextFile::UTF16BE },
	{ L"ascii",			TextFile::ASCII },
};

TextFile::Encoding getEncodingFromString(const std::wstring& str)
{
	for (size_t i = 0; i < sizeof(encodingValues)/sizeof(EncodingValue); i++)
	{
		if (str.compare(encodingValues[i].name) == 0)
			return encodingValues[i].value;
	}

	return TextFile::GUESS;
}

// file: Util/Util.cpp
#include <sys/stat.h>
#ifdef _WIN32
#include <windows.h>
#include <shlwapi.h>
#if defined(WINAPI_FAMILY) && defined(WINAPI_FAMILY_PARTITION)
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP) && WINAPI_FAMILY != WINAPI_FAMILY_DESKTOP_APP
#define ARMIPS_WINDOWS_UWP
#endif
#endif
#else
#include <unistd.h>
#endif

std::wstring convertUtf8ToWString(const char* source)
{
	std::wstring result;

	int index = 0;
	while (source[index] != 0)
	{
		int extraBytes = 0;
		int value = source[index++];

		if ((value & 0xE0) == 0xC0)
		{
			extraBytes = 1;
			value &= 0x1F;
		} else if ((value & 0xF0) == 0xE0)
		{
			extraBytes = 2;
			value &= 0x0F;
		} else if (value > 0x7F)
		{
			// error
			return std::wstring();
		}

		for (int i = 0; i < extraBytes; i++)
		{
			int b = source[index++];
			if ((b & 0xC0) != 0x80)
			{
			// error
			return std::wstring();
			}

			value = (value << 6) | (b & 0x3F);
		}

		result += value;
	}

	return result;
}

std::string convertWCharToUtf8(wchar_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(const std::wstring& 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;
}

std::wstring intToHexString(unsigned int value, int digits, bool prefix)
{
	std::wstring result;
	result.reserve((digits+prefix) ? 2 : 0);

	if (prefix)
	{
		result += '0';
		result += 'x';
	}

	while (digits > 8)
	{
		result += '0';
		digits--;
	}

	wchar_t buf[9];
	swprintf(buf,9,L"%0*X",digits,value);
	result += buf;

	return result;
}

std::wstring intToString(unsigned int value, int digits)
{
	std::wstring result;
	result.reserve(digits);

	while (digits > 8)
	{
		result += ' ';
		digits--;
	}

	wchar_t buf[9];
	swprintf(buf,9,L"%*d",digits,value);
	result += buf;

	return result;
}

bool stringToInt(const std::wstring& line, size_t start, size_t end, int64_t& result)
{
	// find base of number
	int32_t base = 10;
	if (line[start] == '0')
	{
		if (towlower(line[start+1]) == 'x')
		{
			base = 16;
			start += 2;
		} else if (towlower(line[start+1]) == 'o')
		{
			base = 8;
			start += 2;
		} else if (towlower(line[start+1]) == 'b' && towlower(line[end-1]) != 'h')
		{
			base = 2;
			start += 2;
		}
	}

	if (base == 10)
	{
		if (towlower(line[end-1]) == 'h')
		{
			base = 16;
			end--;
		} else if (towlower(line[end-1]) == 'b')
		{
			base = 2;
			end--;
		} else if (towlower(line[end-1]) == 'o')
		{
			base = 8;
			end--;
		}
	}

	// convert number
	result = 0;
	while (start < end)
	{
		wchar_t c = towlower(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;
}

StringList getStringListFromArray(wchar_t** source, int count)
{
	StringList result;
	for (int i = 0; i < count; i++)
	{
		result.push_back(std::wstring(source[i]));
	}

	return result;
}

StringList splitString(const std::wstring& str, const wchar_t delim, bool skipEmpty)
{
	StringList result;
	std::wstringstream stream(str);
	std::wstring arg;
	while (std::getline(stream,arg,delim))
	{
		if (arg.empty() && skipEmpty) continue;
		result.push_back(arg);
	}

	return result;
}

int64_t fileSize(const std::wstring& fileName)
{
#ifdef _WIN32
	WIN32_FILE_ATTRIBUTE_DATA attr;
	if (!GetFileAttributesEx(fileName.c_str(),GetFileExInfoStandard,&attr)
		|| (attr.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
		return 0;
	return ((int64_t) attr.nFileSizeHigh << 32) | (int64_t) attr.nFileSizeLow;
#else
	std::string utf8 = convertWStringToUtf8(fileName);
	struct stat fileStat;
	int err = stat(utf8.c_str(),&fileStat);
	if (0 != err)
		return 0;
	return fileStat.st_size;
#endif
}

bool fileExists(const std::wstring& strFilename)
{
#ifdef _WIN32
#ifdef ARMIPS_WINDOWS_UWP
	return GetFileAttributes(strFilename.c_str()) != INVALID_FILE_ATTRIBUTES;
#else
	int OldMode = SetErrorMode(SEM_FAILCRITICALERRORS);
	bool success = GetFileAttributes(strFilename.c_str()) != INVALID_FILE_ATTRIBUTES;
	SetErrorMode(OldMode);
	return success;
#endif
#else
	std::string utf8 = convertWStringToUtf8(strFilename);
	struct stat stFileInfo;
	int intStat = stat(utf8.c_str(),&stFileInfo);
	return intStat == 0;
#endif
}

bool copyFile(const std::wstring& existingFile, const std::wstring& newFile)
{
#ifdef _WIN32
	return CopyFileW(existingFile.c_str(),newFile.c_str(),false) != FALSE;
#else
	unsigned char buffer[BUFSIZ];
	bool error = false;

	std::string existingUtf8 = convertWStringToUtf8(existingFile);
	std::string newUtf8 = convertWStringToUtf8(newFile);

	FILE* input = fopen(existingUtf8.c_str(),"rb");
	FILE* output = fopen(newUtf8.c_str(),"wb");

	if (input == nullptr || output == nullptr)
		return false;

	size_t n;
	while ((n = fread(buffer,1,BUFSIZ,input)) > 0)
	{
		if (fwrite(buffer,1,n,output) != n)
			error = true;
	}

	fclose(input);
	fclose(output);
	return !error;
#endif
}

bool deleteFile(const std::wstring& fileName)
{
#ifdef _WIN32
	return DeleteFileW(fileName.c_str()) != FALSE;
#else
	std::string utf8 = convertWStringToUtf8(fileName);
	return unlink(utf8.c_str()) == 0;
#endif
}

FILE* openFile(const std::wstring& fileName, OpenFileMode mode)
{
#ifdef _WIN32
	switch (mode)
	{
	case OpenFileMode::ReadBinary:
		return _wfopen(fileName.c_str(),L"rb");
	case OpenFileMode::WriteBinary:
		return _wfopen(fileName.c_str(),L"wb");
	case OpenFileMode::ReadWriteBinary:
		return _wfopen(fileName.c_str(),L"rb+");
	}
#else
	std::string nameUtf8 = convertWStringToUtf8(fileName);

	switch (mode)
	{
	case OpenFileMode::ReadBinary:
		return fopen(nameUtf8.c_str(),"rb");
	case OpenFileMode::WriteBinary:
		return fopen(nameUtf8.c_str(),"wb");
	case OpenFileMode::ReadWriteBinary:
		return fopen(nameUtf8.c_str(),"rb+");
	}
#endif

	return nullptr;
}

std::wstring getCurrentDirectory()
{
#ifdef _WIN32
	wchar_t dir[MAX_PATH];
	_wgetcwd(dir,MAX_PATH-1);
	return dir;
#else
	char* dir = getcwd(nullptr,0);
	std::wstring result = convertUtf8ToWString(dir);
	free(dir);
	return result;
#endif
}

bool changeDirectory(const std::wstring& dir)
{
#ifdef _WIN32
	return _wchdir(dir.c_str()) == 0;
#else
	std::string utf8 = convertWStringToUtf8(dir);
	return chdir(utf8.c_str()) == 0;
#endif
}

std::wstring toWLowercase(const std::string& str)
{
	std::wstring result;
	for (size_t i = 0; i < str.size(); i++)
	{
		result += tolower(str[i]);
	}

	return result;
}

std::wstring getFileNameFromPath(const std::wstring& path)
{
	size_t n = path.find_last_of(L"/\\");
	if (n == path.npos)
		return path;
	return path.substr(n);
}

size_t replaceAll(std::wstring& str, const wchar_t* oldValue,const std::wstring& newValue)
{
	size_t pos = 0;
	size_t len = wcslen(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::wstring& str, const wchar_t* value, size_t stringPos)
{
	while (*value != 0 && stringPos < str.size())
	{
		if (str[stringPos++] != *value++)
			return false;
	}

	return *value == 0;
}

bool isAbsolutePath(const std::wstring& path)
{
#ifdef _WIN32
	return path.size() > 2 && (path[1] == ':' || (path[0] == '\\' && path[1] == '\\'));
#else
	return path.size() >= 1 && path[0] == '/';
#endif
}

// file: Main/CommandLineInterface.h

int runFromCommandLine(const StringList& arguments, ArmipsArguments settings = {});

// file: Main/CommandLineInterface.cpp

static void printUsage(std::wstring executableName)
{
	Logger::printLine(L"armips assembler v%d.%d.%d (%s %s) by Kingcom",
		ARMIPS_VERSION_MAJOR, ARMIPS_VERSION_MINOR, ARMIPS_VERSION_REVISION, __DATE__, __TIME__);
	Logger::printLine(L"Usage: %s [optional parameters] <FILE>", executableName);
	Logger::printLine(L"");
	Logger::printLine(L"Optional parameters:");
	Logger::printLine(L" -temp <TEMP>              Output temporary assembly data to <TEMP> file");
	Logger::printLine(L" -sym  <SYM>               Output symbol data in the sym format to <SYM> file");
	Logger::printLine(L" -sym2 <SYM2>              Output symbol data in the sym2 format to <SYM2> file");
	Logger::printLine(L" -root <ROOT>              Use <ROOT> as working directory during execution");
	Logger::printLine(L" -equ  <NAME> <VAL>        Equivalent to \'<NAME> equ <VAL>\' in code");
	Logger::printLine(L" -strequ <NAME> <VAL>      Equivalent to \'<NAME> equ \"<VAL>\"\' in code");
	Logger::printLine(L" -definelabel <NAME> <VAL> Equivalent to \'.definelabel <NAME>, <VAL>\' in code");
	Logger::printLine(L" -erroronwarning           Treat all warnings like errors");
	Logger::printLine(L"");
	Logger::printLine(L"File arguments:");
	Logger::printLine(L" <FILE>                    Main assembly code file");
}

static bool parseArguments(const StringList& arguments, ArmipsArguments& settings)
{
	size_t argpos = 1;
	bool readflags = true;
	while (argpos < arguments.size())
	{
		if (readflags && arguments[argpos][0] == L'-')
		{
			if (arguments[argpos] == L"--")
			{
				readflags = false;
				argpos += 1;
			}
			else if (arguments[argpos] == L"-temp" && argpos + 1 < arguments.size())
			{
				settings.tempFileName = arguments[argpos + 1];
				argpos += 2;
			}
			else if (arguments[argpos] == L"-sym" && argpos + 1 < arguments.size())
			{
				settings.symFileName = arguments[argpos + 1];
				settings.symFileVersion = 1;
				argpos += 2;
			}
			else if (arguments[argpos] == L"-sym2" && argpos + 1 < arguments.size())
			{
				settings.symFileName = arguments[argpos + 1];
				settings.symFileVersion = 2;
				argpos += 2;
			}
			else if (arguments[argpos] == L"-erroronwarning")
			{
				settings.errorOnWarning = true;
				argpos += 1;
			}
			else if (arguments[argpos] == L"-equ" && argpos + 2 < arguments.size())
			{
				EquationDefinition def;

				def.name = arguments[argpos+1];
				std::transform(def.name.begin(), def.name.end(), def.name.begin(), ::towlower);

				if (!checkValidLabelName(def.name))
				{
					Logger::printError(Logger::Error, L"Invalid equation name \"%s\"", def.name);
					return false;
				}

				auto it = std::find_if(settings.equList.begin(), settings.equList.end(),
						[&def](EquationDefinition x) -> bool {return def.name == x.name;});
				if(it != settings.equList.end())
				{
					Logger::printError(Logger::Error, L"Equation name \"%s\" already defined", def.name);
					return false;
				}

				def.value = arguments[argpos + 2];
				settings.equList.push_back(def);
				argpos += 3;
			}
			else if (arguments[argpos] == L"-strequ" && argpos + 2 < arguments.size())
			{
				EquationDefinition def;

				def.name = arguments[argpos+1];
				std::transform(def.name.begin(), def.name.end(), def.name.begin(), ::towlower);

				if (!checkValidLabelName(def.name))
				{
					Logger::printError(Logger::Error, L"Invalid equation name \"%s\"", def.name);
					return false;
				}

				auto it = std::find_if(settings.equList.begin(), settings.equList.end(),
						[&def](EquationDefinition x) -> bool {return def.name == x.name;});
				if(it != settings.equList.end())
				{
					Logger::printError(Logger::Error, L"Equation name \"%s\" already defined", def.name);
					return false;
				}

				def.value = formatString(L"\"%s\"", arguments[argpos + 2]);
				settings.equList.push_back(def);
				argpos += 3;
			}
			else if (arguments[argpos] == L"-time")
			{
				Logger::printError(Logger::Warning, L"-time flag is deprecated");
				argpos += 1;
			}
			else if (arguments[argpos] == L"-root" && argpos + 1 < arguments.size())
			{
				if(!changeDirectory(arguments[argpos + 1]))
				{
					Logger::printError(Logger::Error, L"Could not open directory \"%s\"", arguments[argpos + 1]);
					return false;
				}
				argpos += 2;
			}
			else if (arguments[argpos] == L"-definelabel" && argpos + 2 < arguments.size())
			{
				LabelDefinition def;

				def.originalName = arguments[argpos + 1];
				def.name = def.originalName;
				std::transform(def.name.begin(), def.name.end(), def.name.begin(), ::towlower);

				if (!checkValidLabelName(def.name))
				{
					Logger::printError(Logger::Error, L"Invalid label name \"%s\"", def.name);
					return false;
				}

				auto it = std::find_if(settings.labels.begin(), settings.labels.end(),
						[&def](LabelDefinition x) -> bool {return def.name == x.name;});
				if(it != settings.labels.end())
				{
					Logger::printError(Logger::Error, L"Label name \"%s\" already defined", def.name);
					return false;
				}

				int64_t value;
				if (!stringToInt(arguments[argpos + 2], 0, arguments[argpos + 2].size(), value))
				{
					Logger::printError(Logger::Error, L"Invalid label value \"%s\"", arguments[argpos + 2]);
					return false;
				}
				def.value = value;

				settings.labels.push_back(def);
				argpos += 3;
			}
			else {
				Logger::printError(Logger::Error, L"Invalid command line argument \"%s\"\n", arguments[argpos]);
				printUsage(arguments[0]);
				return false;
			}
		}
		else {
			// only allow one input filename
			if (settings.inputFileName == L"")
			{
				settings.inputFileName = arguments[argpos];
				argpos++;
			}
			else {
				Logger::printError(Logger::Error, L"Multiple input assembly files specified\n");
				printUsage(arguments[0]);
				return false;
			}
		}
	}

	// ensure input file was specified
	if (settings.inputFileName == L"")
	{
		if (arguments.size() > 1)
			Logger::printError(Logger::Error, L"Missing input assembly file\n");

		printUsage(arguments[0]);
		return false;
	}

	// turn input filename into an absolute path
	if (settings.useAbsoluteFileNames && isAbsolutePath(settings.inputFileName) == false)
		settings.inputFileName = formatString(L"%s/%s", getCurrentDirectory(), settings.inputFileName);

	if (fileExists(settings.inputFileName) == false)
	{
		Logger::printError(Logger::Error, L"File \"%s\" not found", settings.inputFileName);
		return false;
	}
	return true;
}

int runFromCommandLine(const StringList& arguments, ArmipsArguments settings)
{
	if (parseArguments(arguments, settings) == false)
	{
		if (arguments.size() > 1 && !settings.silent)
			Logger::printLine(L"Cannot parse arguments; aborting.");

		return 1;
	}

	if (runArmips(settings) == false)
	{
		if (!settings.silent)
			Logger::printLine(L"Aborting.");

		return 1;
	}

	return 0;
}

// file: Core/ELF/ElfFile.cpp
#include <vector>
#include <algorithm>

#ifndef _WIN32
#include <strings.h>
#define _stricmp strcasecmp
#endif

static bool stringEqualInsensitive(const std::string& a, const std::string& b)
{
	if (a.size() != b.size())
		return false;
	return _stricmp(a.c_str(),b.c_str()) == 0;
}

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, int 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,L"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, int 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 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 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, int 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, int 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, int 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 std::wstring& 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++)
	{
		int 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++)
	{
		int 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 std::wstring&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++)
	{
		int pos = fileHeader.e_phoff+i*fileHeader.e_phentsize;
		segments[i]->writeHeader(fileData, pos, endianness);
	}

	for (size_t i = 0; i < sections.size(); i++)
	{
		int 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();
	int 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];
}

// file: Core/ELF/ElfRelocator.cpp

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::wstring name;
	ByteArray data;
};

std::vector<ArFileEntry> loadArArchive(const std::wstring& 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 = getFileNameFromPath(inputName);
		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 = convertUtf8ToWString(fileName);
			entry.data = input.mid(pos,size);
			result.push_back(entry);
		}

		pos += size;
		if (pos % 2)
			pos++;
	}

	return result;
}

bool ElfRelocator::init(const std::wstring& inputName)
{
	relocator = Arch->getElfRelocator();
	if (relocator == nullptr)
	{
		Logger::printError(Logger::Error,L"Object importing not supported for this architecture");
		return false;
	}

	auto inputFiles = loadArArchive(inputName);
	if (inputFiles.size() == 0)
	{
		Logger::printError(Logger::Error,L"Could not load library");
		return false;
	}

	for (ArFileEntry& entry: inputFiles)
	{
		ElfRelocatorFile file;

		ElfFile* elf = new ElfFile();
		if (elf->load(entry.data,false) == false)
		{
			Logger::printError(Logger::Error,L"Could not load object file %s",entry.name);
			return false;
		}

		if (elf->getType() != ET_REL)
		{
			Logger::printError(Logger::Error,L"Unexpected ELF type %d in object file %s",elf->getType(),entry.name);
			return false;
		}

		if (elf->getMachine() != relocator->expectedMachine())
		{
			Logger::printError(Logger::Error,L"Unexpected ELF machine %d in object file %s",elf->getMachine(),entry.name);
			return false;
		}

		if (elf->getEndianness() != Arch->getEndianness())
		{
			Logger::printError(Logger::Error,L"Incorrect endianness in object file %s",entry.name);
			return false;
		}

		if (elf->getSegmentCount() != 0)
		{
			Logger::printError(Logger::Error,L"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)
						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 && symbol.st_shndx != 0)
			{
				ElfRelocatorSymbol symEntry;
				symEntry.type = ELF32_ST_TYPE(symbol.st_info);
				symEntry.name = convertUtf8ToWString(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::wstring lowered = sym.name;
			std::transform(lowered.begin(), lowered.end(), lowered.begin(), ::towlower);

			sym.label = Global.symbolTable.getLabel(lowered,-1,-1);
			if (sym.label == nullptr)
			{
				Logger::printError(Logger::Error,L"Invalid label name \"%s\"",sym.name);
				error = true;
				continue;
			}

			if (sym.label->isDefined())
			{
				Logger::printError(Logger::Error,L"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(sym.name);
		}
	}

	return !error;
}

std::unique_ptr<CAssemblerCommand> ElfRelocator::generateCtor(const std::wstring& ctorName)
{
	std::unique_ptr<CAssemblerCommand> content = relocator->generateCtorStub(ctors);

	auto func = ::make_unique<CDirectiveFunction>(ctorName,ctorName);
	func->setContent(std::move(content));
	return func;
}

void ElfRelocator::loadRelocation(Elf32_Rel& rel, ByteArray& data, int offset, Endianness endianness)
{
	rel.r_offset = data.getDoubleWord(offset + 0x00, endianness);
	rel.r_info   = data.getDoubleWord(offset + 0x04, endianness);
}

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();

		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)
		{
			std::vector<RelocationAction> relocationActions;
			for (unsigned int relOffset = 0; relOffset < relSection->getSize(); relOffset += sizeof(Elf32_Rel))
			{
				Elf32_Rel rel;
				loadRelocation(rel, relSection->getData(), relOffset, elf->getEndianness());
				int pos = rel.r_offset;

				if (relocator->isDummyRelocationType(rel.getType()))
					continue;

				int symNum = rel.getSymbolNum();
				if (symNum <= 0)
				{
					Logger::queueError(Logger::Warning,L"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];
				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, L"Symbol without a name");
						error = true;
						continue;
					}

					std::wstring symName = toWLowercase(elf->getStrTableString(sym.st_name));

					std::shared_ptr<Label> label = Global.symbolTable.getLabel(symName,-1,-1);
					if (label == nullptr)
					{
						Logger::queueError(Logger::Error,L"Invalid external symbol %s",symName);
						error = true;
						continue;
					}
					if (label->isDefined() == false)
					{
						Logger::queueError(Logger::Error,L"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::wstring> errors;
				if (!relocator->relocateOpcode(rel.getType(),relData, relocationActions, errors))
				{
					for (const std::wstring& error : errors)
					{
						Logger::queueError(Logger::Error, error);
					}
					error = true;
					continue;
				}
			}

			// finish any dangling relocations
			std::vector<std::wstring> errors;
			if (!relocator->finish(relocationActions, errors))
			{
				for (const std::wstring& 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) == false)
			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;
			}
		}
	}
}

// file: Core/Assembler.cpp
#include <thread>

void AddFileName(const std::wstring& FileName)
{
	Global.FileInfo.FileNum = (int) Global.FileInfo.FileList.size();
	Global.FileInfo.FileList.push_back(FileName);
	Global.FileInfo.LineNumber = 0;
}

bool encodeAssembly(std::unique_ptr<CAssemblerCommand> content, SymbolData& symData, TempData& tempData)
{
	bool Revalidate;

#ifdef ARMIPS_ARM
	Arm.Pass2();
#endif
	Mips.Pass2();

	int validationPasses = 0;
	do	// loop until everything is constant
	{
		Global.validationPasses = validationPasses;
		Logger::clearQueue();
		Revalidate = false;

		if (validationPasses >= 100)
		{
			Logger::queueError(Logger::Error,L"Stuck in infinite validation loop");
			break;
		}

		g_fileManager->reset();

#ifdef _DEBUG
		if (!Logger::isSilent())
			printf("Validate %d...\n",validationPasses);
#endif

		if (Global.memoryMode)
			g_fileManager->openFile(Global.memoryFile,true);

		Revalidate = content->Validate();

#ifdef ARMIPS_ARM
		Arm.Revalidate();
#endif
		Mips.Revalidate();

		if (Global.memoryMode)
			g_fileManager->closeFile();

		validationPasses++;
	} while (Revalidate == true);

	Logger::printQueue();
	if (Logger::hasError() == true)
	{
		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,L"File not closed");
		g_fileManager->closeFile();
	}

	return true;
}

bool runArmips(ArmipsArguments& settings)
{
	// initialize and reset global data
	Global.Section = 0;
	Global.nocash = false;
	Global.FileInfo.FileCount = 0;
	Global.FileInfo.TotalLineCount = 0;
	Global.relativeInclude = false;
	Global.validationPasses = 0;
	Global.multiThreading = true;
	Arch = &InvalidArchitecture;

	Tokenizer::clearEquValues();
	Logger::clear();
	Global.Table.clear();
	Global.symbolTable.clear();

	Global.FileInfo.FileList.clear();
	Global.FileInfo.FileCount = 0;
	Global.FileInfo.TotalLineCount = 0;
	Global.FileInfo.LineNumber = 0;
	Global.FileInfo.FileNum = 0;

#ifdef ARMIPS_ARM
	Arm.clear();
#endif

	// 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.originalName);
	}

	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) == false)
		{
			Logger::printError(Logger::Error,L"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 == true && content != nullptr)
		result = encodeAssembly(std::move(content), symData, tempData);

	if (g_fileManager->hasOpenFile())
	{
		if (!Global.memoryMode)
			Logger::printError(Logger::Warning,L"File not closed");
		g_fileManager->closeFile();
	}

	// return errors
	if (settings.errorsResult != nullptr)
	{
		StringList errors = Logger::getErrors();
		for (size_t i = 0; i < errors.size(); i++)
			settings.errorsResult->push_back(errors[i]);
	}

	return result;
}

// file: Core/Common.cpp
#include <sys/stat.h>

FileManager fileManager;
FileManager* g_fileManager = &fileManager;

tGlobal Global;
CArchitecture* Arch;

std::wstring getFolderNameFromPath(const std::wstring& src)
{
#ifdef _WIN32
	size_t s = src.find_last_of(L"\\/");
#else
	size_t s = src.rfind(L"/");
#endif
	if (s == std::wstring::npos)
	{
		return L".";
	}

	return src.substr(0,s);
}

std::wstring getFullPathName(const std::wstring& path)
{
	if (Global.relativeInclude == true)
	{
		if (isAbsolutePath(path))
		{
			return path;
		} else {
			std::wstring source = Global.FileInfo.FileList[Global.FileInfo.FileNum];
			return getFolderNameFromPath(source) + L"/" + path;
		}
	} else {
		return path;
	}
}

bool checkLabelDefined(const std::wstring& labelName, int section)
{
	std::shared_ptr<Label> label = Global.symbolTable.getLabel(labelName,Global.FileInfo.FileNum,section);
	return label->isDefined();
}

bool checkValidLabelName(const std::wstring& labelName)
{
	return Global.symbolTable.isValidSymbolName(labelName);
}

bool isPowerOfTwo(int64_t n)
{
	if (n == 0) return false;
	return !(n & (n - 1));
}

// file: Core/Expression.cpp

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 = to_wstring(intValue) + other.strValue;
		break;
	case ExpressionValueCombination::FS:
		result.type = ExpressionValueType::String;
		result.strValue = to_wstring(floatValue) + other.strValue;
		break;
	case ExpressionValueCombination::SI:
		result.type = ExpressionValueType::String;
		result.strValue = strValue + to_wstring(other.intValue);
		break;
	case ExpressionValueCombination::SF:
		result.type = ExpressionValueType::String;
		result.strValue = strValue + to_wstring(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,L"Division overflow in expression");
			return result;
		}
		if (other.intValue == 0)
		{
			result.intValue = ~0;
			Logger::queueError(Logger::Warning,L"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,L"Division overflow in expression");
			return result;
		}
		if (other.intValue == 0)
		{
			result.intValue = intValue;
			Logger::queueError(Logger::Warning,L"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 to_wstring(intValue) == other.strValue;
	case ExpressionValueCombination::FS:
		return to_wstring(floatValue) == other.strValue;
	case ExpressionValueCombination::SI:
		return strValue == to_wstring(other.intValue);
	case ExpressionValueCombination::SF:
		return strValue == to_wstring(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()
{
	children = nullptr;
	childrenCount = 0;
}

ExpressionInternal::~ExpressionInternal()
{
	deallocate();
}

ExpressionInternal::ExpressionInternal(int64_t value)
	: ExpressionInternal()
{
	type = OperatorType::Integer;
	intValue = value;
}

ExpressionInternal::ExpressionInternal(double value)
	: ExpressionInternal()
{
	type = OperatorType::Float;
	floatValue = value;
}

ExpressionInternal::ExpressionInternal(const std::wstring& value, OperatorType type)
	: ExpressionInternal()
{
	this->type = type;
	strValue = value;

	switch (type)
	{
	case OperatorType::Identifier:
		fileNum = Global.FileInfo.FileNum;
		section = Global.Section;
		break;
	case OperatorType::String:
		break;
	default:
		break;
	}
}

ExpressionInternal::ExpressionInternal(OperatorType op, ExpressionInternal* a,
	ExpressionInternal* b, ExpressionInternal* c)
	: ExpressionInternal()
{
	type = op;
	allocate(3);

	children[0] = a;
	children[1] = b;
	children[2] = c;
}

ExpressionInternal::ExpressionInternal(const std::wstring& name, const std::vector<ExpressionInternal*>& parameters)
	: ExpressionInternal()
{
	type = OperatorType::FunctionCall;
	allocate(parameters.size());

	strValue = name;
	for (size_t i = 0; i < parameters.size(); i++)
	{
		children[i] = parameters[i];
	}
}

void ExpressionInternal::allocate(size_t count)
{
	deallocate();

	children = new ExpressionInternal*[count];
	childrenCount = count;
}

void ExpressionInternal::deallocate()
{
	for (size_t i = 0; i < childrenCount; i++)
	{
		delete children[i];
	}

	delete[] children;
	children = nullptr;
	childrenCount = 0;
}

void ExpressionInternal::replaceMemoryPos(const std::wstring& identifierName)
{
	for (size_t i = 0; i < childrenCount; i++)
	{
		if (children[i] != nullptr)
		{
			children[i]->replaceMemoryPos(identifierName);
		}
	}

	if (type == OperatorType::MemoryPos)
	{
		type = OperatorType::Identifier;
		strValue = identifierName;
		fileNum = Global.FileInfo.FileNum;
		section = Global.Section;
	}
}

bool ExpressionInternal::checkParameterCount(size_t minParams, size_t maxParams)
{
	if (minParams > childrenCount)
	{
		Logger::queueError(Logger::Error,L"Not enough parameters for \"%s\" (min %d)",strValue,minParams);
		return false;
	}

	if (maxParams < childrenCount)
	{
		Logger::queueError(Logger::Error,L"Too many parameters for \"%s\" (min %d)",strValue,maxParams);
		return false;
	}

	return true;
}

ExpressionValue ExpressionInternal::executeExpressionFunctionCall(const ExpressionFunctionEntry& entry)
{
	// check parameters
	if (!checkParameterCount(entry.minParams, entry.maxParams))
		return {};

	// evaluate parameters
	std::vector<ExpressionValue> params;
	params.reserve(childrenCount);

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

		params.push_back(result);
	}

	// execute
	return entry.function(strValue, params);
}

ExpressionValue ExpressionInternal::executeExpressionLabelFunctionCall(const ExpressionLabelFunctionEntry& entry)
{
	// check parameters
	if (!checkParameterCount(entry.minParams, entry.maxParams))
		return {};

	// evaluate parameters
	std::vector<std::shared_ptr<Label>> params;
	params.reserve(childrenCount);

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

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

	// execute
	return entry.function(strValue, params);
}

ExpressionValue ExpressionInternal::executeFunctionCall()
{
	// try expression functions
	auto expFuncIt = expressionFunctions.find(strValue);
	if (expFuncIt != expressionFunctions.end())
		return executeExpressionFunctionCall(expFuncIt->second);

	// try expression label functions
	auto expLabelFuncIt = expressionLabelFunctions.find(strValue);
	if (expLabelFuncIt != expressionLabelFunctions.end())
		return executeExpressionLabelFunctionCall(expLabelFuncIt->second);

	// try architecture specific expression functions
	auto& archExpressionFunctions = Arch->getExpressionFunctions();
	expFuncIt = archExpressionFunctions.find(strValue);
	if (expFuncIt != archExpressionFunctions.end())
		return executeExpressionFunctionCall(expFuncIt->second);

	// error
	Logger::queueError(Logger::Error, L"Unknown function \"%s\"", strValue);
	return {};
}

bool isExpressionFunctionSafe(const std::wstring& name, bool inUnknownOrFalseBlock)
{
	// expression functions may be unsafe, others are safe
	ExpFuncSafety safety = ExpFuncSafety::Unsafe;
	bool found = false;

	auto it = expressionFunctions.find(name);
	if (it != expressionFunctions.end())
	{
		safety = it->second.safety;
		found = true;
	}

	if (!found)
	{
		auto labelIt = expressionLabelFunctions.find(name);
		if (labelIt != expressionLabelFunctions.end())
		{
			safety = labelIt->second.safety;
			found = true;
		}
	}

	if (!found)
	{
		auto& archExpressionFunctions = Arch->getExpressionFunctions();
		it = archExpressionFunctions.find(name);
		if (it != archExpressionFunctions.end())
		{
			safety = it->second.safety;
			found = true;
		}
	}

	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(strValue, inUnknownOrFalseBlock) == false)
			return false;
		break;
	default:
		break;
	}

	// check if the same applies to all children
	bool canSimplify = true;
	for (size_t i = 0; i < childrenCount; i++)
	{
		if (children[i] != nullptr && children[i]->simplify(inUnknownOrFalseBlock) == false)
			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;
			intValue = value.intValue;
			break;
		case ExpressionValueType::Float:
			type = OperatorType::Float;
			floatValue = value.floatValue;
			break;
		case ExpressionValueType::String:
			type = OperatorType::String;
			strValue = value.strValue;
			break;
		default:
			type = OperatorType::Invalid;
			break;
		}

		deallocate();
	}

	return canSimplify;
}

ExpressionValue ExpressionInternal::evaluate()
{
	ExpressionValue val;

	std::shared_ptr<Label> label;
	switch (type)
	{
	case OperatorType::Integer:
		val.type = ExpressionValueType::Integer;
		val.intValue = intValue;
		return val;
	case OperatorType::Float:
		val.type = ExpressionValueType::Float;
		val.floatValue = floatValue;
		return val;
	case OperatorType::Identifier:
		label = Global.symbolTable.getLabel(strValue,fileNum,section);
		if (label == nullptr)
		{
			Logger::queueError(Logger::Error,L"Invalid label name \"%s\"",strValue);
			return val;
		}

		if (!label->isDefined())
		{
			Logger::queueError(Logger::Error,L"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 = strValue;
		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::wstring escapeString(const std::wstring& text)
{
	std::wstring result = text;
	replaceAll(result,LR"(\)",LR"(\\)");
	replaceAll(result,LR"(")",LR"(\")");

	return formatString(LR"("%s")",text);
}

std::wstring ExpressionInternal::formatFunctionCall()
{
	std::wstring text = strValue + L"(";

	for (size_t i = 0; i < childrenCount; i++)
	{
		if (i != 0)
			text += L",";
		text += children[i]->toString();
	}

	return text + L")";
}

std::wstring ExpressionInternal::toString()
{
	switch (type)
	{
	case OperatorType::Integer:
		return formatString(L"%d",intValue);
	case OperatorType::Float:
		return formatString(L"%g",floatValue);
	case OperatorType::Identifier:
		return strValue;
	case OperatorType::String:
		return escapeString(strValue);
	case OperatorType::MemoryPos:
		return L".";
	case OperatorType::Add:
		return formatString(L"(%s + %s)",children[0]->toString(),children[1]->toString());
	case OperatorType::Sub:
		return formatString(L"(%s - %s)",children[0]->toString(),children[1]->toString());
	case OperatorType::Mult:
		return formatString(L"(%s * %s)",children[0]->toString(),children[1]->toString());
	case OperatorType::Div:
		return formatString(L"(%s / %s)",children[0]->toString(),children[1]->toString());
	case OperatorType::Mod:
		return formatString(L"(%s %% %s)",children[0]->toString(),children[1]->toString());
	case OperatorType::Neg:
		return formatString(L"(-%s)",children[0]->toString());
	case OperatorType::LogNot:
		return formatString(L"(!%s)",children[0]->toString());
	case OperatorType::BitNot:
		return formatString(L"(~%s)",children[0]->toString());
	case OperatorType::LeftShift:
		return formatString(L"(%s << %s)",children[0]->toString(),children[1]->toString());
	case OperatorType::RightShift:
		return formatString(L"(%s >> %s)",children[0]->toString(),children[1]->toString());
	case OperatorType::Less:
		return formatString(L"(%s < %s)",children[0]->toString(),children[1]->toString());
	case OperatorType::Greater:
		return formatString(L"(%s > %s)",children[0]->toString(),children[1]->toString());
	case OperatorType::LessEqual:
		return formatString(L"(%s <= %s)",children[0]->toString(),children[1]->toString());
	case OperatorType::GreaterEqual:
		return formatString(L"(%s >= %s)",children[0]->toString(),children[1]->toString());
	case OperatorType::Equal:
		return formatString(L"(%s == %s)",children[0]->toString(),children[1]->toString());
	case OperatorType::NotEqual:
		return formatString(L"(%s != %s)",children[0]->toString(),children[1]->toString());
	case OperatorType::BitAnd:
		return formatString(L"(%s & %s)",children[0]->toString(),children[1]->toString());
	case OperatorType::BitOr:
		return formatString(L"(%s | %s)",children[0]->toString(),children[1]->toString());
	case OperatorType::LogAnd:
		return formatString(L"(%s && %s)",children[0]->toString(),children[1]->toString());
	case OperatorType::LogOr:
		return formatString(L"(%s || %s)",children[0]->toString(),children[1]->toString());
	case OperatorType::Xor:
		return formatString(L"(%s ^ %s)",children[0]->toString(),children[1]->toString());
	case OperatorType::TertiaryIf:
		return formatString(L"(%s ? %s : %s)",children[0]->toString(),children[1]->toString(),children[2]->toString());
	case OperatorType::ToString:
		return formatString(L"(%c%s)",L'\U000000B0',children[0]->toString());
	case OperatorType::FunctionCall:
		return formatFunctionCall();
	default:
		return L"";
	}
}

Expression::Expression()
{
	expression = nullptr;
	constExpression = true;
}

void Expression::setExpression(ExpressionInternal* exp, bool inUnknownOrFalseBlock)
{
	expression = std::shared_ptr<ExpressionInternal>(exp);
	if (exp != nullptr)
		constExpression = expression->simplify(inUnknownOrFalseBlock);
	else
		constExpression = true;
}

ExpressionValue Expression::evaluate()
{
	if (expression == nullptr)
	{
		ExpressionValue invalid;
		return invalid;
	}

	return expression->evaluate();
}

void Expression::replaceMemoryPos(const std::wstring& identifierName)
{
	if (expression != nullptr)
		expression->replaceMemoryPos(identifierName);
}

Expression createConstExpression(int64_t value)
{
	Expression exp;
	ExpressionInternal* num = new ExpressionInternal(value);
	exp.setExpression(num,false);
	return exp;
}

// file: Core/ExpressionFunctions.cpp
#if ARMIPS_REGEXP
#include <regex>
#endif

bool getExpFuncParameter(const std::vector<ExpressionValue>& parameters, size_t index, int64_t& dest,
	const std::wstring& funcName, bool optional)
{
	if (optional && index >= parameters.size())
		return true;

	if (index >= parameters.size() || parameters[index].isInt() == false)
	{
		Logger::queueError(Logger::Error,L"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 std::wstring*& dest,
	const std::wstring& funcName, bool optional)
{
	if (optional && index >= parameters.size())
		return true;

	if (index >= parameters.size() || parameters[index].isString() == false)
	{
		Logger::queueError(Logger::Error,L"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 std::wstring& 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 std::wstring& funcName, const std::vector<ExpressionValue>& parameters)
{
	ExpressionValue result;
	result.type = ExpressionValueType::String;

	switch (g_fileManager->getEndianness())
	{
	case Endianness::Little:
		return ExpressionValue(L"little");
	case Endianness::Big:
		return ExpressionValue(L"big");
	}

	return ExpressionValue();
}

ExpressionValue expFuncOutputName(const std::wstring& funcName, const std::vector<ExpressionValue>& parameters)
{
	std::shared_ptr<AssemblerFile> file = g_fileManager->getOpenFile();
	if (file == nullptr)
	{
		Logger::queueError(Logger::Error,L"outputName: no file opened");
		return ExpressionValue();
	}

	std::wstring value = file->getFileName();
	return ExpressionValue(value);
}

ExpressionValue expFuncFileExists(const std::wstring& funcName, const std::vector<ExpressionValue>& parameters)
{
	const std::wstring* fileName;
	GET_PARAM(parameters,0,fileName);

	std::wstring fullName = getFullPathName(*fileName);
	return ExpressionValue(fileExists(fullName) ? INT64_C(1) : INT64_C(0));
}

ExpressionValue expFuncFileSize(const std::wstring& funcName, const std::vector<ExpressionValue>& parameters)
{
	const std::wstring* fileName;
	GET_PARAM(parameters,0,fileName);

	std::wstring fullName = getFullPathName(*fileName);
	return ExpressionValue((int64_t) fileSize(fullName));
}

ExpressionValue expFuncToString(const std::wstring& 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 = formatString(L"%d",parameters[0].intValue);
		break;
	case ExpressionValueType::Float:
		result.strValue = formatString(L"%#.17g",parameters[0].floatValue);
		break;
	default:
		return result;
	}

	result.type = ExpressionValueType::String;
	return result;
}

ExpressionValue expFuncToHex(const std::wstring& funcName, const std::vector<ExpressionValue>& parameters)
{
	int64_t value, digits;
	GET_PARAM(parameters,0,value);
	GET_OPTIONAL_PARAM(parameters,1,digits,8);

	return ExpressionValue(formatString(L"%0*X",digits,value));
}

ExpressionValue expFuncInt(const std::wstring& 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 std::wstring& 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 std::wstring& 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 std::wstring& 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 std::wstring& 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 std::wstring& 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 std::wstring& 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 std::wstring& funcName, const std::vector<ExpressionValue>& parameters)
{
	const std::wstring* source;
	GET_PARAM(parameters,0,source);

	return ExpressionValue((int64_t)source->size());
}

ExpressionValue expFuncSubstr(const std::wstring& funcName, const std::vector<ExpressionValue>& parameters)
{
	int64_t start, count;
	const std::wstring* source;

	GET_PARAM(parameters,0,source);
	GET_PARAM(parameters,1,start);
	GET_PARAM(parameters,2,count);

	return ExpressionValue(source->substr((size_t)start,(size_t)count));
}

#if ARMIPS_REGEXP
ExpressionValue expFuncRegExMatch(const std::wstring& funcName, const std::vector<ExpressionValue>& parameters)
{
	const std::wstring* source;
	const std::wstring* regexString;

	GET_PARAM(parameters,0,source);
	GET_PARAM(parameters,1,regexString);

#if ARMIPS_EXCEPTIONS
	try
	{
#endif
		std::wregex regex(*regexString);
		bool found = std::regex_match(*source,regex);
		return ExpressionValue(found ? INT64_C(1) : INT64_C(0));
#if ARMIPS_EXCEPTIONS
	} catch (std::regex_error&)
	{
		Logger::queueError(Logger::Error,L"Invalid regular expression");
		return ExpressionValue();
	}
#endif
}

ExpressionValue expFuncRegExSearch(const std::wstring& funcName, const std::vector<ExpressionValue>& parameters)
{
	const std::wstring* source;
	const std::wstring* regexString;

	GET_PARAM(parameters,0,source);
	GET_PARAM(parameters,1,regexString);

#if ARMIPS_EXCEPTIONS
	try
	{
#endif
		std::wregex regex(*regexString);
		bool found = std::regex_search(*source,regex);
		return ExpressionValue(found ? INT64_C(1) : INT64_C(0));
#if ARMIPS_EXCEPTIONS
	} catch (std::regex_error&)
	{
		Logger::queueError(Logger::Error,L"Invalid regular expression");
		return ExpressionValue();
	}
#endif
}

ExpressionValue expFuncRegExExtract(const std::wstring& funcName, const std::vector<ExpressionValue>& parameters)
{
	const std::wstring* source;
	const std::wstring* 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::wregex regex(*regexString);
		std::wsmatch result;
		bool found = std::regex_search(*source,result,regex);
		if (found == false || (size_t)matchIndex >= result.size())
		{
			Logger::queueError(Logger::Error,L"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,L"Invalid regular expression");
		return ExpressionValue();
	}
#endif
}
#endif

ExpressionValue expFuncFind(const std::wstring& funcName, const std::vector<ExpressionValue>& parameters)
{
	int64_t start;
	const std::wstring* source;
	const std::wstring* value;

	GET_PARAM(parameters,0,source);
	GET_PARAM(parameters,1,value);
	GET_OPTIONAL_PARAM(parameters,2,start,0);

	size_t pos = source->find(*value,(size_t)start);
	return ExpressionValue(pos == std::wstring::npos ? INT64_C(-1) : (int64_t) pos);
}

ExpressionValue expFuncRFind(const std::wstring& funcName, const std::vector<ExpressionValue>& parameters)
{
	int64_t start;
	const std::wstring* source;
	const std::wstring* value;

	GET_PARAM(parameters,0,source);
	GET_PARAM(parameters,1,value);
	GET_OPTIONAL_PARAM(parameters,2,start,std::wstring::npos);

	size_t pos = source->rfind(*value,(size_t)start);
	return ExpressionValue(pos == std::wstring::npos ? INT64_C(-1) : (int64_t) pos);
}


template<typename T>
ExpressionValue expFuncRead(const std::wstring& funcName, const std::vector<ExpressionValue>& parameters)
{
	const std::wstring* fileName;
	int64_t pos;

	GET_PARAM(parameters,0,fileName);
	GET_OPTIONAL_PARAM(parameters,1,pos,0);

	std::wstring fullName = getFullPathName(*fileName);

	BinaryFile file;
	if (file.open(fullName,BinaryFile::Read) == false)
	{
		Logger::queueError(Logger::Error,L"Could not open %s",*fileName);
		return ExpressionValue();
	}

	file.setPos(pos);

	T buffer;
	if (file.read(&buffer, sizeof(T)) != sizeof(T))
	{
		Logger::queueError(Logger::Error, L"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 std::wstring& funcName, const std::vector<ExpressionValue>& parameters)
{
	const std::wstring* 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);

	std::wstring fullName = getFullPathName(*fileName);

	int64_t totalSize = fileSize(fullName);
	if (length == 0 || start+length > totalSize)
		length = totalSize-start;

	BinaryFile file;
	if (file.open(fullName,BinaryFile::Read) == false)
	{
		Logger::queueError(Logger::Error,L"Could not open %s",fileName);
		return ExpressionValue();
	}

	file.setPos((long)start);

	unsigned char* buffer = new unsigned char[length];
	file.read(buffer,(size_t)length);

	std::wstring result;
	for (size_t i = 0; i < (size_t) length; i++)
	{
		if (buffer[i] < 0x20 || buffer[i] > 0x7F)
		{
			Logger::printError(Logger::Warning,L"%s: Non-ASCII character",funcName);
			return ExpressionValue();
		}

		result += (wchar_t) buffer[i];
	}

	delete[] buffer;

	return ExpressionValue(result);
}

ExpressionValue expLabelFuncDefined(const std::wstring &funcName, const std::vector<std::shared_ptr<Label>> &parameters)
{
	if (parameters.empty() || !parameters.front())
	{
		Logger::queueError(Logger::Error,L"%s: Invalid parameters", funcName);
		return ExpressionValue();
	}

	return ExpressionValue(parameters.front()->isDefined() ? INT64_C(1) : INT64_C(0));
}

ExpressionValue expLabelFuncOrg(const std::wstring& 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,L"%s: no file opened", funcName);
		return ExpressionValue();
	}
	return ExpressionValue(g_fileManager->getVirtualAddress());
}

ExpressionValue expLabelFuncOrga(const std::wstring& 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,L"%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,L"%s: no file opened", funcName);
		return ExpressionValue();
	}
	return ExpressionValue(g_fileManager->getPhysicalAddress());
}

ExpressionValue expLabelFuncHeaderSize(const std::wstring& 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,L"%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,L"headersize: no file opened");
		return ExpressionValue();
	}
	return ExpressionValue(g_fileManager->getHeaderSize());
}

const ExpressionFunctionMap expressionFunctions = {
	{ L"version",		{ &expFuncVersion,			0,	0,	ExpFuncSafety::Safe } },
	{ L"endianness",	{ &expFuncEndianness,		0,	0,	ExpFuncSafety::Unsafe } },
	{ L"outputname",	{ &expFuncOutputName,		0,	0,	ExpFuncSafety::Unsafe } },
	{ L"fileexists",	{ &expFuncFileExists,		1,	1,	ExpFuncSafety::Safe } },
	{ L"filesize",		{ &expFuncFileSize,			1,	1,	ExpFuncSafety::ConditionalUnsafe } },
	{ L"tostring",		{ &expFuncToString,			1,	1,	ExpFuncSafety::Safe } },
	{ L"tohex",			{ &expFuncToHex,			1,	2,	ExpFuncSafety::Safe } },

	{ L"int",			{ &expFuncInt,				1,	1,	ExpFuncSafety::Safe } },
	{ L"float",			{ &expFuncFloat,			1,	1,	ExpFuncSafety::Safe } },
	{ L"frac",			{ &expFuncFrac,				1,	1,	ExpFuncSafety::Safe } },
	{ L"abs",			{ &expFuncAbs,				1,	1,	ExpFuncSafety::Safe } },
	{ L"round",			{ &expFuncRound,			1,	1,	ExpFuncSafety::Safe } },
	{ L"min",			{ &expFuncMin,				1,	std::numeric_limits<size_t>::max(),	ExpFuncSafety::Safe } },
	{ L"max",			{ &expFuncMax,				1,	std::numeric_limits<size_t>::max(),	ExpFuncSafety::Safe } },

	{ L"strlen",		{ &expFuncStrlen,			1,	1,	ExpFuncSafety::Safe } },
	{ L"substr",		{ &expFuncSubstr,			3,	3,	ExpFuncSafety::Safe } },
#if ARMIPS_REGEXP
	{ L"regex_match",	{ &expFuncRegExMatch,		2,	2,	ExpFuncSafety::Safe } },
	{ L"regex_search",	{ &expFuncRegExSearch,		2,	2,	ExpFuncSafety::Safe } },
	{ L"regex_extract",	{ &expFuncRegExExtract,		2,	3,	ExpFuncSafety::Safe } },
#endif
	{ L"find",			{ &expFuncFind,				2,	3,	ExpFuncSafety::Safe } },
	{ L"rfind",			{ &expFuncRFind,			2,	3,	ExpFuncSafety::Safe } },

	{ L"readbyte",		{ &expFuncRead<uint8_t>,	1,	2,	ExpFuncSafety::ConditionalUnsafe } },
	{ L"readu8",		{ &expFuncRead<uint8_t>,	1,	2,	ExpFuncSafety::ConditionalUnsafe } },
	{ L"readu16",		{ &expFuncRead<uint16_t>,	1,	2,	ExpFuncSafety::ConditionalUnsafe } },
	{ L"readu32",		{ &expFuncRead<uint32_t>,	1,	2,	ExpFuncSafety::ConditionalUnsafe } },
	{ L"readu64",		{ &expFuncRead<uint64_t>,	1,	2,	ExpFuncSafety::ConditionalUnsafe } },
	{ L"reads8",		{ &expFuncRead<int8_t>,		1,	2,	ExpFuncSafety::ConditionalUnsafe } },
	{ L"reads16",		{ &expFuncRead<int16_t>,	1,	2,	ExpFuncSafety::ConditionalUnsafe } },
	{ L"reads32",		{ &expFuncRead<int32_t>,	1,	2,	ExpFuncSafety::ConditionalUnsafe } },
	{ L"reads64",		{ &expFuncRead<int64_t>,	1,	2,	ExpFuncSafety::ConditionalUnsafe } },
	{ L"readascii",		{ &expFuncReadAscii,		1,	3,	ExpFuncSafety::ConditionalUnsafe } },
};

extern const ExpressionLabelFunctionMap expressionLabelFunctions =
{
	{ L"defined",    { &expLabelFuncDefined,      1, 1, ExpFuncSafety::Unsafe } },
	{ L"org",        { &expLabelFuncOrg,          0, 1, ExpFuncSafety::Unsafe } },
	{ L"orga",       { &expLabelFuncOrga,         0, 1, ExpFuncSafety::Unsafe } },
	{ L"headersize", { &expLabelFuncHeaderSize,   0, 1, ExpFuncSafety::Unsafe } },
};

// 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 std::wstring& fileName, int64_t headerSize, bool overwrite)
{
	this->fileName = fileName;
	this->headerSize = headerSize;
	this->originalHeaderSize = headerSize;
	this->seekPhysical(0);
	mode = overwrite == true ? Create : Open;
}

GenericAssemblerFile::GenericAssemblerFile(const std::wstring& fileName, const std::wstring& originalFileName, int64_t headerSize)
{
	this->fileName = fileName;
	this->originalName = originalFileName;
	this->headerSize = headerSize;
	this->originalHeaderSize = headerSize;
	this->seekPhysical(0);
	mode = Copy;
}

bool GenericAssemblerFile::open(bool onlyCheck)
{
	headerSize = originalHeaderSize;
	virtualAddress = headerSize;

	if (onlyCheck == false)
	{
		// actually open the file
		bool success;
		switch (mode)
		{
		case Open:
			success = handle.open(fileName,BinaryFile::ReadWrite);
			if (success == false)
			{
				Logger::printError(Logger::FatalError,L"Could not open file %s",fileName);
				return false;
			}
			return true;

		case Create:
			success = handle.open(fileName,BinaryFile::Write);
			if (success == false)
			{
				Logger::printError(Logger::FatalError,L"Could not create file %s",fileName);
				return false;
			}
			return true;

		case Copy:
			success = copyFile(originalName,fileName);
			if (success == false)
			{
				Logger::printError(Logger::FatalError,L"Could not copy file %s",originalName);
				return false;
			}

			success = handle.open(fileName,BinaryFile::ReadWrite);
			if (success == false)
			{
				Logger::printError(Logger::FatalError,L"Could not create file %s",fileName);
				return false;
			}
			return true;

		default:
			return false;
		}
	}

	// else only check if it can be done, don't actually do it permanently
	bool success, exists;
	BinaryFile temp;
	switch (mode)
	{
	case Open:
		success = temp.open(fileName,BinaryFile::ReadWrite);
		if (success == false)
		{
			Logger::queueError(Logger::FatalError,L"Could not open file %s",fileName);
			return false;
		}
		temp.close();
		return true;

	case Create:
		// if it exists, check if you can open it with read/write access
		// otherwise open it with write access and remove it afterwards
		exists = fileExists(fileName);
		success = temp.open(fileName,exists ? BinaryFile::ReadWrite : BinaryFile::Write);
		if (success == false)
		{
			Logger::queueError(Logger::FatalError,L"Could not create file %s",fileName);
			return false;
		}
		temp.close();

		if (exists == false)
			deleteFile(fileName);

		return true;

	case Copy:
		// check original file
		success = temp.open(originalName,BinaryFile::ReadWrite);
		if (success == false)
		{
			Logger::queueError(Logger::FatalError,L"Could not open file %s",originalName);
			return false;
		}
		temp.close();

		// check new file, same as create
		exists = fileExists(fileName);
		success = temp.open(fileName,exists ? BinaryFile::ReadWrite : BinaryFile::Write);
		if (success == false)
		{
			Logger::queueError(Logger::FatalError,L"Could not create file %s",fileName);
			return false;
		}
		temp.close();

		if (exists == false)
			deleteFile(fileName);

		return true;

	default:
		return false;
	};

	return false;
}

bool GenericAssemblerFile::write(void* data, size_t length)
{
	if (isOpen() == false)
		return false;

	size_t len = handle.write(data,length);
	virtualAddress += len;
	return len == length;
}

bool GenericAssemblerFile::seekVirtual(int64_t virtualAddress)
{
	if (virtualAddress - headerSize < 0)
	{
		Logger::queueError(Logger::Error,L"Seeking to virtual address with negative physical address");
		return false;
	}
	if (virtualAddress < 0)
		Logger::queueError(Logger::Warning,L"Seeking to negative virtual address");

	this->virtualAddress = virtualAddress;
	int64_t physicalAddress = virtualAddress-headerSize;

	if (isOpen())
		handle.setPos((long)physicalAddress);

	return true;
}

bool GenericAssemblerFile::seekPhysical(int64_t physicalAddress)
{
	if (physicalAddress < 0)
	{
		Logger::queueError(Logger::Error,L"Seeking to negative physical address");
		return false;
	}
	if (physicalAddress + headerSize < 0)
		Logger::queueError(Logger::Warning,L"Seeking to physical address with negative virtual address");

	virtualAddress = physicalAddress+headerSize;

	if (isOpen())
		handle.setPos((long)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,L"Running on unknown endianness");

	reset();
}

FileManager::~FileManager()
{

}

void FileManager::reset()
{
	activeFile = nullptr;
	setEndianness(Endianness::Little);
}

bool FileManager::checkActiveFile()
{
	if (activeFile == nullptr)
	{
		Logger::queueError(Logger::Error,L"No file opened");
		return false;
	}
	return true;
}

bool FileManager::openFile(std::shared_ptr<AssemblerFile> file, bool onlyCheck)
{
	if (activeFile != nullptr)
	{
		Logger::queueError(Logger::Warning,L"File not closed before opening a new one");
		activeFile->close();
	}

	activeFile = 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,L"No file opened");
		return;
	}

	activeFile->close();
	activeFile = nullptr;
}

bool FileManager::write(void* data, size_t length)
{
	if (checkActiveFile() == false)
		return false;

	if (activeFile->isOpen() == false)
	{
		Logger::queueError(Logger::Error,L"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() == false)
		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() == false)
		return false;
	return activeFile->seekPhysical(virtualAddress);
}

bool FileManager::advanceMemory(size_t bytes)
{
	if (checkActiveFile() == false)
		return false;

	int64_t pos = activeFile->getVirtualAddress();
	return activeFile->seekVirtual(pos+bytes);
}

// file: Core/Misc.cpp
#include <iostream>

#ifdef _WIN32
#include <windows.h>
#endif

std::vector<Logger::QueueEntry> Logger::queue;
std::vector<std::wstring> Logger::errors;
bool Logger::error = false;
bool Logger::fatalError = false;
bool Logger::errorOnWarning = false;
bool Logger::silent = false;
int Logger::suppressLevel = 0;

std::wstring Logger::formatError(ErrorType type, const wchar_t* text)
{
	std::wstring position;

	if (Global.memoryMode == false && Global.FileInfo.FileList.size() > 0)
	{
		std::wstring& fileName = Global.FileInfo.FileList[Global.FileInfo.FileNum];
		position = formatString(L"%s(%d) ",fileName,Global.FileInfo.LineNumber);
	}

	switch (type)
	{
	case Warning:
		return formatString(L"%swarning: %s",position,text);
	case Error:
		return formatString(L"%serror: %s",position,text);
	case FatalError:
		return formatString(L"%sfatal error: %s",position,text);
	case Notice:
		return formatString(L"%snotice: %s",position,text);
	}

	return L"";
}

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(const std::wstring& text)
{
	if (suppressLevel)
		return;

	std::wcout << text << std::endl;

#if defined(_MSC_VER) && defined(_DEBUG)
	OutputDebugStringW(text.c_str());
	OutputDebugStringW(L"\n");
#endif
}

void Logger::printLine(const std::string& text)
{
	if (suppressLevel)
		return;

	std::cout << text << std::endl;

#if defined(_MSC_VER) && defined(_DEBUG)
	OutputDebugStringA(text.c_str());
	OutputDebugStringA("\n");
#endif
}

void Logger::print(const std::wstring& text)
{
	if (suppressLevel)
		return;

	std::wcout << text;

#if defined(_MSC_VER) && defined(_DEBUG)
	OutputDebugStringW(text.c_str());
#endif
}

void Logger::printError(ErrorType type, const std::wstring& text)
{
	if (suppressLevel)
		return;

	std::wstring errorText = formatError(type,text.c_str());
	errors.push_back(errorText);

	if (!silent)
		printLine(errorText);

	setFlags(type);
}

void Logger::printError(ErrorType type, const wchar_t* text)
{
	if (suppressLevel)
		return;

	std::wstring errorText = formatError(type,text);
	errors.push_back(errorText);

	if (!silent)
		printLine(errorText);

	setFlags(type);
}

void Logger::queueError(ErrorType type, const std::wstring& text)
{
	if (suppressLevel)
		return;

	QueueEntry entry;
	entry.type = type;
	entry.text = formatError(type,text.c_str());
	queue.push_back(entry);
}

void Logger::queueError(ErrorType type, const wchar_t* text)
{
	if (suppressLevel)
		return;

	QueueEntry entry;
	entry.type = type;
	entry.text = formatError(type,text);
	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() == false)
	{
		if (file.open(TextFile::Write) == false)
		{
			Logger::printError(Logger::Error,L"Could not open temp file %s.",file.getFileName());
			return;
		}

		size_t fileCount = Global.FileInfo.FileList.size();
		size_t lineCount = Global.FileInfo.TotalLineCount;
		size_t labelCount = Global.symbolTable.getLabelCount();
		size_t equCount = Global.symbolTable.getEquationCount();

		file.writeFormat(L"; %d %S included\n",fileCount,fileCount == 1 ? "file" : "files");
		file.writeFormat(L"; %d %S\n",lineCount,lineCount == 1 ? "line" : "lines");
		file.writeFormat(L"; %d %S\n",labelCount,labelCount == 1 ? "label" : "labels");
		file.writeFormat(L"; %d %S\n\n",equCount,equCount == 1 ? "equation" : "equations");
		for (size_t i = 0; i < fileCount; i++)
		{
			file.writeFormat(L"; %S\n",Global.FileInfo.FileList[i]);
		}
		file.writeLine("");
	}
}

void TempData::end()
{
	if (file.isOpen())
		file.close();
}

void TempData::writeLine(int64_t memoryAddress, const std::wstring& text)
{
	if (file.isOpen())
	{
		wchar_t hexbuf[10] = {0};
		swprintf(hexbuf, 10, L"%08X ", (int32_t) memoryAddress);
		std::wstring str = hexbuf + text;
		while (str.size() < 70)
			str += ' ';

		str += formatString(L"; %S line %d",
			Global.FileInfo.FileList[Global.FileInfo.FileNum],Global.FileInfo.LineNumber);

		file.writeLine(str);
	}
}

// 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::wstring 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 = formatString(L"%s,%08X",sym.name,size);
			else
				entry.text = sym.name;

			if (nocashSymVersion == 1)
				std::transform(entry.text.begin(), entry.text.end(), entry.text.begin(), ::towlower);

			entries.push_back(entry);
		}

		for (const SymDataData& data: module.data)
		{
			NocashSymEntry entry;
			entry.address = data.address;

			switch (data.type)
			{
			case Data8:
				entry.text = formatString(L".byt:%04X",data.size);
				break;
			case Data16:
				entry.text = formatString(L".wrd:%04X",data.size);
				break;
			case Data32:
				entry.text = formatString(L".dbl:%04X",data.size);
				break;
			case Data64:
				entry.text = formatString(L".dbl:%04X",data.size);
				break;
			case DataAscii:
				entry.text = formatString(L".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) == false)
	{
		Logger::printError(Logger::Error,L"Could not open sym file %s.",file.getFileName());
		return;
	}
	file.writeLine(L"00000000 0");

	for (size_t i = 0; i < entries.size(); i++)
	{
		file.writeFormat(L"%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::wstring& 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::wstring& 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 = i;
			return;
		}
	}

	SymDataModule module;
	module.file = file;
	modules.push_back(module);
	currentModule = modules.size()-1;
}

void SymbolData::endModule(AssemblerFile* file)
{
	if (modules[currentModule].file != file)
		return;

	if (currentModule == 0)
	{
		Logger::printError(Logger::Error,L"No module opened");
		return;
	}

	if (currentFunction != -1)
	{
		Logger::printError(Logger::Error,L"Module closed before function end");
		currentFunction = -1;
	}

	currentModule = 0;
}

void SymbolData::startFunction(int64_t address)
{
	if (currentFunction != -1)
	{
		endFunction(address);
	}

	currentFunction = 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,L"Not inside a function");
		return;
	}

	SymDataFunction& func = modules[currentModule].functions[currentFunction];
	func.size = (size_t) (address-func.address);
	currentFunction = -1;
}

// file: Core/SymbolTable.cpp

const wchar_t validSymbolCharacters[] = L"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;
}

SymbolTable::SymbolTable()
{
	uniqueCount = 0;
}

SymbolTable::~SymbolTable()
{
	clear();
}

void SymbolTable::clear()
{
	symbols.clear();
	labels.clear();
	equationsCount = 0;
	uniqueCount = 0;
}

void SymbolTable::setFileSectionValues(const std::wstring& symbol, int& file, int& section)
{
	if (symbol[0] == '@')
	{
		if (symbol[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 std::wstring& symbol, int file, int section)
{
	if (isValidSymbolName(symbol) == false)
		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>(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 std::wstring& symbol, int file, int section)
{
	if (isValidSymbolName(symbol) == false)
		return false;

	setFileSectionValues(symbol,file,section);

	SymbolKey key = { symbol, file, section };
	auto it = symbols.find(key);
	return it != symbols.end();
}

bool SymbolTable::isValidSymbolName(const std::wstring& symbol)
{
	size_t size = symbol.size();
	size_t start = 0;

	// don't match empty names
	if (size == 0 || symbol.compare(L"@") == 0 || symbol.compare(L"@@") == 0)
		return false;

	if (symbol[0] == '@')
	{
		start++;
		if (size > 1 && symbol[1] == '@')
			start++;
	}

	if (symbol[start] >= '0' && symbol[start] <= '9')
		return false;

	for (size_t i = start; i < size; i++)
	{
		if (wcschr(validSymbolCharacters,symbol[i]) == nullptr)
			return false;
	}

	return true;
}

bool SymbolTable::isValidSymbolCharacter(wchar_t 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 std::wstring& name, int file, int section, size_t referenceIndex)
{
	if (isValidSymbolName(name) == false)
		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 std::wstring& 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
std::wstring SymbolTable::getUniqueLabelName(bool local)
{
	std::wstring name = formatString(L"__armips_label_%08x__",uniqueCount++);
	if (local)
		name = L"@@" + name;

	generatedLabels.insert(name);
	return 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;

		label->setOriginalName(def.originalName);

		if (isLocalSymbol(def.name) == false)
			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)
	{
		int diff = address-lab->getValue();
		if (diff >= 0 && diff < smallestDiff)
		{
			smallestDiff = diff;
			smallestBefore = lab->getSection();
		}
	}

	return smallestBefore;
}

// file: Main/main.cpp

int wmain(int argc, wchar_t* argv[])
{
	std::setlocale(LC_CTYPE,"");

#ifdef ARMIPS_TESTS
	std::wstring name;

	if (argc < 2)
		return !runTests(L"Tests", argv[0]);
	else
		return !runTests(argv[1], argv[0]);
#endif

	StringList arguments = getStringListFromArray(argv,argc);

	return runFromCommandLine(arguments);
}

#ifndef _WIN32

int main(int argc, char* argv[])
{
	// convert input to wstring
	std::vector<std::wstring> wideStrings;
	for (int i = 0; i < argc; i++)
	{
		std::wstring str = convertUtf8ToWString(argv[i]);
		wideStrings.push_back(str);
	}

	// create argv replacement
	wchar_t** wargv = new wchar_t*[argc];
	for (int i = 0; i < argc; i++)
	{
		wargv[i] = (wchar_t*) wideStrings[i].c_str();
	}

	int result = wmain(argc,wargv);

	delete[] wargv;
	return result;
}

#endif

