diff --git a/.clang-format b/.clang-format new file mode 100644 index 00000000..b05e27ab --- /dev/null +++ b/.clang-format @@ -0,0 +1,22 @@ +--- +Language: Cpp +BasedOnStyle: Google + +MacroBlockBegin: "^\ +BEGIN_EVENT_TABLE.*" +MacroBlockEnd: "^\ +END_EVENT_TABLE()" + +DerivePointerAlignment: false +PointerAlignment: Left + +IncludeCategories: + - Regex: '^"config.h"' + Priority: 1 + - Regex: '^<.*\.h>' + Priority: 2 + - Regex: '^<.*' + Priority: 3 + - Regex: '.*' + Priority: 4 + diff --git a/.gitignore b/.gitignore index a2232d54..5ae5076e 100644 --- a/.gitignore +++ b/.gitignore @@ -77,6 +77,9 @@ CMakeFiles Makefile CMakeScripts/ +# Clang tools +compile_commands.json + # This is for the CMake-generated Visual Studio project *.vcxproj *.vcxproj.user diff --git a/.travis.yml b/.travis.yml index 445c505f..bd9f476a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -44,7 +44,6 @@ before_script: # Don't ask for confirmation when using scp - echo -e "Host armedpineapple.co.uk\n\tStrictHostKeyChecking no\n" >> ~/.ssh/config - echo -e "Host server2.armedpineapple.co.uk\n\tStrictHostKeyChecking no\n" >> ~/.ssh/config - - cd fresh script: # Check if there are trailing whitespaces. - ${TRAVIS_BUILD_DIR}/scripts/check_trailing_whitespaces.py $TRAVIS_BUILD_DIR @@ -52,7 +51,12 @@ script: - ${TRAVIS_BUILD_DIR}/scripts/check_language_files_not_BOM.py $TRAVIS_BUILD_DIR/CorsixTH/Lua/languages # Check if there are lua classes with invalid/improper declarations. - ${TRAVIS_BUILD_DIR}/scripts/check_lua_classes.py + # Check cpp format + - cd ${TRAVIS_BUILD_DIR} + - clang-format -i CorsixTH/Src/*{.cpp,.h} AnimView/*{.cpp,.h} common/*{.cpp,.h} CorsixTH/SrcUnshared/main.cpp + - git diff --exit-code # Build CorsixTH + - cd fresh - make VERBOSE=1 # Validate lua files - find $TRAVIS_BUILD_DIR -path $TRAVIS_BUILD_DIR/CorsixTH/Lua/languages -prune -o -name '*.lua' -print0 | xargs -0 luac -p -- diff --git a/AnimView/app.cpp b/AnimView/app.cpp index 9b58aa1c..57298f0e 100644 --- a/AnimView/app.cpp +++ b/AnimView/app.cpp @@ -20,23 +20,23 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -#include "config.h" #include "app.h" +#include "config.h" #include "frmMain.h" #include "frmSprites.h" IMPLEMENT_APP(ThemeHospitalAnimViewApp) -bool ThemeHospitalAnimViewApp::OnInit() -{ - wxTopLevelWindow *pForm; - if(::wxMessageBox(L"Launch animation viewer? (No -> sprite viewer)", L"AnimView", wxYES_NO) == wxYES) - pForm = new frmMain; - else - pForm = new frmSprites; +bool ThemeHospitalAnimViewApp::OnInit() { + wxTopLevelWindow* pForm; + if (::wxMessageBox(L"Launch animation viewer? (No -> sprite viewer)", + L"AnimView", wxYES_NO) == wxYES) + pForm = new frmMain; + else + pForm = new frmSprites; - pForm->Show(true); - SetTopWindow(pForm); + pForm->Show(true); + SetTopWindow(pForm); - return true; + return true; } diff --git a/AnimView/app.h b/AnimView/app.h index 84d04bf9..a51f4659 100644 --- a/AnimView/app.h +++ b/AnimView/app.h @@ -27,19 +27,18 @@ SOFTWARE. #include "wx/wxprec.h" #ifdef __BORLANDC__ - #pragma hdrstop +#pragma hdrstop #endif // for all others, include the necessary headers (this file is usually all you // need because it includes almost all "standard" wxWidgets headers) #ifndef WX_PRECOMP - #include "wx/wx.h" +#include "wx/wx.h" #endif // ---------------------------- -class ThemeHospitalAnimViewApp : public wxApp -{ - virtual bool OnInit(); +class ThemeHospitalAnimViewApp : public wxApp { + virtual bool OnInit(); }; DECLARE_APP(ThemeHospitalAnimViewApp) diff --git a/AnimView/backdrop.h b/AnimView/backdrop.h index 601b3760..05c1699c 100644 --- a/AnimView/backdrop.h +++ b/AnimView/backdrop.h @@ -1,4 +1,5 @@ /* XPM */ +// clang-format off static const char *const backdrop_xpm[] = { "126 64 12 1", "f c #695D7D", @@ -78,3 +79,4 @@ static const char *const backdrop_xpm[] = { " bfffff bfffff ", " .f .f " }; +// clang-format on diff --git a/AnimView/frmMain.cpp b/AnimView/frmMain.cpp index 394f4893..c2c1de95 100644 --- a/AnimView/frmMain.cpp +++ b/AnimView/frmMain.cpp @@ -20,31 +20,31 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -#include "config.h" #include "frmMain.h" +#include "config.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include #include #include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include #include +#include #include "backdrop.h" BEGIN_EVENT_TABLE(frmMain, wxFrame) - EVT_BUTTON(ID_LOAD , frmMain::_onLoad) - EVT_BUTTON(ID_BROWSE , frmMain::_onBrowse) + EVT_BUTTON(ID_LOAD, frmMain::_onLoad) + EVT_BUTTON(ID_BROWSE, frmMain::_onBrowse) EVT_BUTTON(ID_FIRST_ANIM, frmMain::_onFirstAnim) - EVT_BUTTON(ID_PREV_ANIM , frmMain::_onPrevAnim) - EVT_BUTTON(ID_NEXT_ANIM , frmMain::_onNextAnim) - EVT_BUTTON(ID_LAST_ANIM , frmMain::_onLastAnim) + EVT_BUTTON(ID_PREV_ANIM, frmMain::_onPrevAnim) + EVT_BUTTON(ID_NEXT_ANIM, frmMain::_onNextAnim) + EVT_BUTTON(ID_LAST_ANIM, frmMain::_onLastAnim) EVT_BUTTON(ID_PREV_FRAME, frmMain::_onPrevFrame) EVT_BUTTON(ID_NEXT_FRAME, frmMain::_onNextFrame) EVT_BUTTON(ID_PLAY_PAUSE, frmMain::_onPlayPause) @@ -64,652 +64,727 @@ BEGIN_EVENT_TABLE(frmMain, wxFrame) END_EVENT_TABLE() frmMain::frmMain() - : wxFrame(NULL, wxID_ANY, L"Theme Hospital Animation Viewer") -{ - wxSizer* pMainSizer = new wxBoxSizer(wxHORIZONTAL); + : wxFrame(NULL, wxID_ANY, L"Theme Hospital Animation Viewer") { + wxSizer* pMainSizer = new wxBoxSizer(wxHORIZONTAL); - wxSizer* pSidebarSizer = new wxBoxSizer(wxVERTICAL); + wxSizer* pSidebarSizer = new wxBoxSizer(wxVERTICAL); #define def wxDefaultPosition, wxDefaultSize - wxStaticBoxSizer *pThemeHospital = new wxStaticBoxSizer(wxHORIZONTAL, this, L"Theme Hospital"); - pThemeHospital->Add(new wxStaticText(this, wxID_ANY, L"Directory:"), 0, wxALIGN_CENTER_VERTICAL | wxALL, 1); - pThemeHospital->Add(m_txtTHPath = new wxTextCtrl(this, wxID_ANY, L"", def, wxTE_CENTRE), 1, wxALIGN_CENTER_VERTICAL | wxALL, 1); - pThemeHospital->Add(new wxButton(this, ID_BROWSE, L"Browse..."), 0, wxALIGN_CENTER_VERTICAL | wxALL, 1); - pThemeHospital->Add(new wxButton(this, ID_LOAD, L"Load"), 0, wxALIGN_CENTER_VERTICAL | wxALL, 1); - pSidebarSizer->Add(pThemeHospital, 0, wxEXPAND | wxALL, 0); + wxStaticBoxSizer* pThemeHospital = + new wxStaticBoxSizer(wxHORIZONTAL, this, L"Theme Hospital"); + pThemeHospital->Add(new wxStaticText(this, wxID_ANY, L"Directory:"), 0, + wxALIGN_CENTER_VERTICAL | wxALL, 1); + pThemeHospital->Add( + m_txtTHPath = new wxTextCtrl(this, wxID_ANY, L"", def, wxTE_CENTRE), 1, + wxALIGN_CENTER_VERTICAL | wxALL, 1); + pThemeHospital->Add(new wxButton(this, ID_BROWSE, L"Browse..."), 0, + wxALIGN_CENTER_VERTICAL | wxALL, 1); + pThemeHospital->Add(new wxButton(this, ID_LOAD, L"Load"), 0, + wxALIGN_CENTER_VERTICAL | wxALL, 1); + pSidebarSizer->Add(pThemeHospital, 0, wxEXPAND | wxALL, 0); - wxStaticBoxSizer *pPalette = new wxStaticBoxSizer(wxVERTICAL, this, L"Palette"); - wxBoxSizer *pPaletteTop = new wxBoxSizer(wxHORIZONTAL); - pPaletteTop->Add(new wxRadioButton(this, ID_GHOST_0, L"Standard"), 1); - pPaletteTop->Add(new wxRadioButton(this, ID_GHOST_1, L"Ghost 1"), 1); - pPaletteTop->Add(new wxRadioButton(this, ID_GHOST_2, L"Ghost 2"), 1); - pPaletteTop->Add(new wxRadioButton(this, ID_GHOST_3, L"Ghost 66"), 1); - m_iGhostFile = 0; - m_iGhostIndex = 0; - pPalette->Add(pPaletteTop, 0, wxEXPAND | wxALL, 1); - pPalette->Add(new wxSpinCtrl(this, wxID_ANY, wxEmptyString, def, wxSP_ARROW_KEYS | wxSP_WRAP, 0, 255), 0, wxALIGN_CENTER | wxALL, 1); - pSidebarSizer->Add(pPalette, 0, wxEXPAND | wxALL, 0); + wxStaticBoxSizer* pPalette = + new wxStaticBoxSizer(wxVERTICAL, this, L"Palette"); + wxBoxSizer* pPaletteTop = new wxBoxSizer(wxHORIZONTAL); + pPaletteTop->Add(new wxRadioButton(this, ID_GHOST_0, L"Standard"), 1); + pPaletteTop->Add(new wxRadioButton(this, ID_GHOST_1, L"Ghost 1"), 1); + pPaletteTop->Add(new wxRadioButton(this, ID_GHOST_2, L"Ghost 2"), 1); + pPaletteTop->Add(new wxRadioButton(this, ID_GHOST_3, L"Ghost 66"), 1); + m_iGhostFile = 0; + m_iGhostIndex = 0; + pPalette->Add(pPaletteTop, 0, wxEXPAND | wxALL, 1); + pPalette->Add(new wxSpinCtrl(this, wxID_ANY, wxEmptyString, def, + wxSP_ARROW_KEYS | wxSP_WRAP, 0, 255), + 0, wxALIGN_CENTER | wxALL, 1); + pSidebarSizer->Add(pPalette, 0, wxEXPAND | wxALL, 0); - wxStaticBoxSizer *pAnimation = new wxStaticBoxSizer(wxHORIZONTAL, this, L"Animation"); - pAnimation->Add(new wxButton(this, ID_FIRST_ANIM, L"<<", def, wxBU_EXACTFIT), 0, wxALIGN_CENTER_VERTICAL | wxALL, 1); - pAnimation->Add(new wxButton(this, ID_PREV_ANIM, L"<", def, wxBU_EXACTFIT), 0, wxALIGN_CENTER_VERTICAL | wxALL, 1); - pAnimation->Add(m_txtAnimIndex = new wxTextCtrl(this, ID_ANIM_INDEX, L"0", def, wxTE_CENTRE), 1, wxALIGN_CENTER_VERTICAL | wxALL, 1); - pAnimation->Add(new wxStaticText(this, wxID_ANY, L"of"), 0, wxALIGN_CENTER_VERTICAL | wxALL, 1); - pAnimation->Add(m_txtAnimCount = new wxTextCtrl(this, wxID_ANY, L"?", def, wxTE_CENTRE | wxTE_READONLY), 1, wxALIGN_CENTER_VERTICAL | wxALL, 1); - pAnimation->Add(new wxButton(this, ID_NEXT_ANIM, L">", def, wxBU_EXACTFIT), 0, wxALIGN_CENTER_VERTICAL | wxALL, 1); - pAnimation->Add(new wxButton(this, ID_LAST_ANIM, L">>", def, wxBU_EXACTFIT), 0, wxALIGN_CENTER_VERTICAL | wxALL, 1); - pSidebarSizer->Add(pAnimation, 0, wxEXPAND | wxALL, 0); + wxStaticBoxSizer* pAnimation = + new wxStaticBoxSizer(wxHORIZONTAL, this, L"Animation"); + pAnimation->Add(new wxButton(this, ID_FIRST_ANIM, L"<<", def, wxBU_EXACTFIT), + 0, wxALIGN_CENTER_VERTICAL | wxALL, 1); + pAnimation->Add(new wxButton(this, ID_PREV_ANIM, L"<", def, wxBU_EXACTFIT), 0, + wxALIGN_CENTER_VERTICAL | wxALL, 1); + pAnimation->Add(m_txtAnimIndex = new wxTextCtrl(this, ID_ANIM_INDEX, L"0", + def, wxTE_CENTRE), + 1, wxALIGN_CENTER_VERTICAL | wxALL, 1); + pAnimation->Add(new wxStaticText(this, wxID_ANY, L"of"), 0, + wxALIGN_CENTER_VERTICAL | wxALL, 1); + pAnimation->Add(m_txtAnimCount = new wxTextCtrl(this, wxID_ANY, L"?", def, + wxTE_CENTRE | wxTE_READONLY), + 1, wxALIGN_CENTER_VERTICAL | wxALL, 1); + pAnimation->Add(new wxButton(this, ID_NEXT_ANIM, L">", def, wxBU_EXACTFIT), 0, + wxALIGN_CENTER_VERTICAL | wxALL, 1); + pAnimation->Add(new wxButton(this, ID_LAST_ANIM, L">>", def, wxBU_EXACTFIT), + 0, wxALIGN_CENTER_VERTICAL | wxALL, 1); + pSidebarSizer->Add(pAnimation, 0, wxEXPAND | wxALL, 0); - wxStaticBoxSizer *pFrame = new wxStaticBoxSizer(wxHORIZONTAL, this, L"Frame"); - pFrame->Add(new wxButton(this, ID_PREV_FRAME, L"<", def, wxBU_EXACTFIT), 0, wxALIGN_CENTER_VERTICAL | wxALL, 1); - pFrame->Add(m_txtFrameIndex = new wxTextCtrl(this, wxID_ANY, L"0", def, wxTE_CENTRE), 1, wxALIGN_CENTER_VERTICAL | wxALL, 1); - pFrame->Add(new wxStaticText(this, wxID_ANY, L"of", def, wxALIGN_CENTRE), 0, wxALIGN_CENTER_VERTICAL | wxALL, 1); - pFrame->Add(m_txtFrameCount = new wxTextCtrl(this, wxID_ANY, L"?", def, wxTE_CENTRE | wxTE_READONLY), 1, wxALIGN_CENTER_VERTICAL | wxALL, 1); - pFrame->Add(new wxButton(this, ID_NEXT_FRAME, L">", def, wxBU_EXACTFIT), 0, wxALIGN_CENTER_VERTICAL | wxALL, 1); - pFrame->Add(m_btnPlayPause = new wxButton(this, ID_PLAY_PAUSE, L"Pause"), 1, wxALIGN_CENTER_VERTICAL | wxALL, 1); - m_bPlayingAnimation = true; - //m_bPlayingAnimation = false; - pSidebarSizer->Add(pFrame, 0, wxEXPAND | wxALL, 0); + wxStaticBoxSizer* pFrame = new wxStaticBoxSizer(wxHORIZONTAL, this, L"Frame"); + pFrame->Add(new wxButton(this, ID_PREV_FRAME, L"<", def, wxBU_EXACTFIT), 0, + wxALIGN_CENTER_VERTICAL | wxALL, 1); + pFrame->Add( + m_txtFrameIndex = new wxTextCtrl(this, wxID_ANY, L"0", def, wxTE_CENTRE), + 1, wxALIGN_CENTER_VERTICAL | wxALL, 1); + pFrame->Add(new wxStaticText(this, wxID_ANY, L"of", def, wxALIGN_CENTRE), 0, + wxALIGN_CENTER_VERTICAL | wxALL, 1); + pFrame->Add(m_txtFrameCount = new wxTextCtrl(this, wxID_ANY, L"?", def, + wxTE_CENTRE | wxTE_READONLY), + 1, wxALIGN_CENTER_VERTICAL | wxALL, 1); + pFrame->Add(new wxButton(this, ID_NEXT_FRAME, L">", def, wxBU_EXACTFIT), 0, + wxALIGN_CENTER_VERTICAL | wxALL, 1); + pFrame->Add(m_btnPlayPause = new wxButton(this, ID_PLAY_PAUSE, L"Pause"), 1, + wxALIGN_CENTER_VERTICAL | wxALL, 1); + m_bPlayingAnimation = true; + // m_bPlayingAnimation = false; + pSidebarSizer->Add(pFrame, 0, wxEXPAND | wxALL, 0); -#define ID(layer, id) (ID_LAYER_CHECKS + (layer) * 25 + (id)) - wxStaticBoxSizer *pLayer0 = new wxStaticBoxSizer(wxHORIZONTAL, this, L"Layer 0 (Patient Head)"); - pLayer0->Add(new wxCheckBox(this, ID(0, 0), L"0"), 0, wxALIGN_CENTER | wxALL, 1); - pLayer0->Add(new wxCheckBox(this, ID(0, 2), L"2"), 0, wxALIGN_CENTER | wxALL, 1); - pLayer0->Add(new wxCheckBox(this, ID(0, 4), L"4"), 0, wxALIGN_CENTER | wxALL, 1); - pLayer0->Add(new wxCheckBox(this, ID(0, 6), L"6"), 0, wxALIGN_CENTER | wxALL, 1); - pLayer0->Add(new wxCheckBox(this, ID(0, 8), L"8"), 0, wxALIGN_CENTER | wxALL, 1); - pLayer0->Add(new wxCheckBox(this, ID(0, 10), L"10"), 0, wxALIGN_CENTER | wxALL, 1); - pLayer0->Add(new wxCheckBox(this, ID(0, 12), L"12"), 0, wxALIGN_CENTER | wxALL, 1); - pLayer0->Add(new wxCheckBox(this, ID(0, 14), L"14"), 0, wxALIGN_CENTER | wxALL, 1); - pLayer0->Add(new wxCheckBox(this, ID(0, 16), L"16"), 0, wxALIGN_CENTER | wxALL, 1); - pLayer0->Add(new wxCheckBox(this, ID(0, 18), L"18"), 0, wxALIGN_CENTER | wxALL, 1); - pLayer0->Add(new wxCheckBox(this, ID(0, 20), L"20"), 0, wxALIGN_CENTER | wxALL, 1); - pLayer0->Add(new wxCheckBox(this, ID(0, 22), L"22"), 0, wxALIGN_CENTER | wxALL, 1); - pSidebarSizer->Add(pLayer0, 0, wxEXPAND | wxALL, 0); +#define ID(layer, id) (ID_LAYER_CHECKS + (layer)*25 + (id)) + wxStaticBoxSizer* pLayer0 = + new wxStaticBoxSizer(wxHORIZONTAL, this, L"Layer 0 (Patient Head)"); + pLayer0->Add(new wxCheckBox(this, ID(0, 0), L"0"), 0, wxALIGN_CENTER | wxALL, + 1); + pLayer0->Add(new wxCheckBox(this, ID(0, 2), L"2"), 0, wxALIGN_CENTER | wxALL, + 1); + pLayer0->Add(new wxCheckBox(this, ID(0, 4), L"4"), 0, wxALIGN_CENTER | wxALL, + 1); + pLayer0->Add(new wxCheckBox(this, ID(0, 6), L"6"), 0, wxALIGN_CENTER | wxALL, + 1); + pLayer0->Add(new wxCheckBox(this, ID(0, 8), L"8"), 0, wxALIGN_CENTER | wxALL, + 1); + pLayer0->Add(new wxCheckBox(this, ID(0, 10), L"10"), 0, + wxALIGN_CENTER | wxALL, 1); + pLayer0->Add(new wxCheckBox(this, ID(0, 12), L"12"), 0, + wxALIGN_CENTER | wxALL, 1); + pLayer0->Add(new wxCheckBox(this, ID(0, 14), L"14"), 0, + wxALIGN_CENTER | wxALL, 1); + pLayer0->Add(new wxCheckBox(this, ID(0, 16), L"16"), 0, + wxALIGN_CENTER | wxALL, 1); + pLayer0->Add(new wxCheckBox(this, ID(0, 18), L"18"), 0, + wxALIGN_CENTER | wxALL, 1); + pLayer0->Add(new wxCheckBox(this, ID(0, 20), L"20"), 0, + wxALIGN_CENTER | wxALL, 1); + pLayer0->Add(new wxCheckBox(this, ID(0, 22), L"22"), 0, + wxALIGN_CENTER | wxALL, 1); + pSidebarSizer->Add(pLayer0, 0, wxEXPAND | wxALL, 0); - wxStaticBoxSizer *pLayer1 = new wxStaticBoxSizer(wxHORIZONTAL, this, L"Layer 1 (Patient Clothes)"); - pLayer1->Add(new wxCheckBox(this, ID(1, 0), L"0"), 0, wxALIGN_CENTER | wxALL, 1); - pLayer1->Add(new wxCheckBox(this, ID(1, 2), L"2 (A)"), 0, wxALIGN_CENTER | wxALL, 1); - pLayer1->Add(new wxCheckBox(this, ID(1, 4), L"4 (B)"), 0, wxALIGN_CENTER | wxALL, 1); - pLayer1->Add(new wxCheckBox(this, ID(1, 6), L"6 (C)"), 0, wxALIGN_CENTER | wxALL, 1); - pLayer1->Add(new wxCheckBox(this, ID(1, 8), L"8"), 0, wxALIGN_CENTER | wxALL, 1); - pLayer1->Add(new wxCheckBox(this, ID(1, 10), L"10"), 0, wxALIGN_CENTER | wxALL, 1); - pSidebarSizer->Add(pLayer1, 0, wxEXPAND | wxALL, 0); + wxStaticBoxSizer* pLayer1 = + new wxStaticBoxSizer(wxHORIZONTAL, this, L"Layer 1 (Patient Clothes)"); + pLayer1->Add(new wxCheckBox(this, ID(1, 0), L"0"), 0, wxALIGN_CENTER | wxALL, + 1); + pLayer1->Add(new wxCheckBox(this, ID(1, 2), L"2 (A)"), 0, + wxALIGN_CENTER | wxALL, 1); + pLayer1->Add(new wxCheckBox(this, ID(1, 4), L"4 (B)"), 0, + wxALIGN_CENTER | wxALL, 1); + pLayer1->Add(new wxCheckBox(this, ID(1, 6), L"6 (C)"), 0, + wxALIGN_CENTER | wxALL, 1); + pLayer1->Add(new wxCheckBox(this, ID(1, 8), L"8"), 0, wxALIGN_CENTER | wxALL, + 1); + pLayer1->Add(new wxCheckBox(this, ID(1, 10), L"10"), 0, + wxALIGN_CENTER | wxALL, 1); + pSidebarSizer->Add(pLayer1, 0, wxEXPAND | wxALL, 0); - wxStaticBoxSizer *pLayer2 = new wxStaticBoxSizer(wxHORIZONTAL, this, L"Layer 2 (Bandages / Patient Accessory)"); - pLayer2->Add(new wxCheckBox(this, ID(2, 2), L"2 (Head / Alt Shoes)"), 0, wxALIGN_CENTER | wxALL, 1); - pLayer2->Add(new wxCheckBox(this, ID(2, 4), L"4 (Arm / Hat)"), 0, wxALIGN_CENTER | wxALL, 1); - pLayer2->Add(new wxCheckBox(this, ID(2, 6), L"6"), 0, wxALIGN_CENTER | wxALL, 1); - pSidebarSizer->Add(pLayer2, 0, wxEXPAND | wxALL, 0); + wxStaticBoxSizer* pLayer2 = new wxStaticBoxSizer( + wxHORIZONTAL, this, L"Layer 2 (Bandages / Patient Accessory)"); + pLayer2->Add(new wxCheckBox(this, ID(2, 2), L"2 (Head / Alt Shoes)"), 0, + wxALIGN_CENTER | wxALL, 1); + pLayer2->Add(new wxCheckBox(this, ID(2, 4), L"4 (Arm / Hat)"), 0, + wxALIGN_CENTER | wxALL, 1); + pLayer2->Add(new wxCheckBox(this, ID(2, 6), L"6"), 0, wxALIGN_CENTER | wxALL, + 1); + pSidebarSizer->Add(pLayer2, 0, wxEXPAND | wxALL, 0); - wxStaticBoxSizer *pLayer3 = new wxStaticBoxSizer(wxHORIZONTAL, this, L"Layer 3 (Bandages / Colour)"); - pLayer3->Add(new wxCheckBox(this, ID(3, 0), L"0"), 0, wxALIGN_CENTER | wxALL, 1); - pLayer3->Add(new wxCheckBox(this, ID(3, 2), L"2 (? / Yellow)"), 0, wxALIGN_CENTER | wxALL, 1); - pLayer3->Add(new wxCheckBox(this, ID(3, 4), L"4 (L Foot / Blue)"), 0, wxALIGN_CENTER | wxALL, 1); - pLayer3->Add(new wxCheckBox(this, ID(3, 6), L"6 (? / White)"), 0, wxALIGN_CENTER | wxALL, 1); - pLayer3->Add(new wxCheckBox(this, ID(3, 8), L"8 (R Arm)"), 0, wxALIGN_CENTER | wxALL, 1); - pLayer3->Add(new wxCheckBox(this, ID(3, 10), L"10 (R Foot)"), 0, wxALIGN_CENTER | wxALL, 1); - pSidebarSizer->Add(pLayer3, 0, wxEXPAND | wxALL, 0); + wxStaticBoxSizer* pLayer3 = + new wxStaticBoxSizer(wxHORIZONTAL, this, L"Layer 3 (Bandages / Colour)"); + pLayer3->Add(new wxCheckBox(this, ID(3, 0), L"0"), 0, wxALIGN_CENTER | wxALL, + 1); + pLayer3->Add(new wxCheckBox(this, ID(3, 2), L"2 (? / Yellow)"), 0, + wxALIGN_CENTER | wxALL, 1); + pLayer3->Add(new wxCheckBox(this, ID(3, 4), L"4 (L Foot / Blue)"), 0, + wxALIGN_CENTER | wxALL, 1); + pLayer3->Add(new wxCheckBox(this, ID(3, 6), L"6 (? / White)"), 0, + wxALIGN_CENTER | wxALL, 1); + pLayer3->Add(new wxCheckBox(this, ID(3, 8), L"8 (R Arm)"), 0, + wxALIGN_CENTER | wxALL, 1); + pLayer3->Add(new wxCheckBox(this, ID(3, 10), L"10 (R Foot)"), 0, + wxALIGN_CENTER | wxALL, 1); + pSidebarSizer->Add(pLayer3, 0, wxEXPAND | wxALL, 0); - wxStaticBoxSizer *pLayer4 = new wxStaticBoxSizer(wxHORIZONTAL, this, L"Layer 4 (Bandages / Repair)"); - pLayer4->Add(new wxCheckBox(this, ID(4, 0), L"0"), 0, wxALIGN_CENTER | wxALL, 1); - pLayer4->Add(new wxCheckBox(this, ID(4, 2), L"2 (Head / Repair)"), 0, wxALIGN_CENTER | wxALL, 1); - pLayer4->Add(new wxCheckBox(this, ID(4, 4), L"4 (L Root)"), 0, wxALIGN_CENTER | wxALL, 1); - pLayer4->Add(new wxCheckBox(this, ID(4, 6), L"6"), 0, wxALIGN_CENTER | wxALL, 1); - pLayer4->Add(new wxCheckBox(this, ID(4, 8), L"8 (R Arm)"), 0, wxALIGN_CENTER | wxALL, 1); - pLayer4->Add(new wxCheckBox(this, ID(4, 10), L"10 (R Foot)"), 0, wxALIGN_CENTER | wxALL, 1); - pSidebarSizer->Add(pLayer4, 0, wxEXPAND | wxALL, 0); + wxStaticBoxSizer* pLayer4 = + new wxStaticBoxSizer(wxHORIZONTAL, this, L"Layer 4 (Bandages / Repair)"); + pLayer4->Add(new wxCheckBox(this, ID(4, 0), L"0"), 0, wxALIGN_CENTER | wxALL, + 1); + pLayer4->Add(new wxCheckBox(this, ID(4, 2), L"2 (Head / Repair)"), 0, + wxALIGN_CENTER | wxALL, 1); + pLayer4->Add(new wxCheckBox(this, ID(4, 4), L"4 (L Root)"), 0, + wxALIGN_CENTER | wxALL, 1); + pLayer4->Add(new wxCheckBox(this, ID(4, 6), L"6"), 0, wxALIGN_CENTER | wxALL, + 1); + pLayer4->Add(new wxCheckBox(this, ID(4, 8), L"8 (R Arm)"), 0, + wxALIGN_CENTER | wxALL, 1); + pLayer4->Add(new wxCheckBox(this, ID(4, 10), L"10 (R Foot)"), 0, + wxALIGN_CENTER | wxALL, 1); + pSidebarSizer->Add(pLayer4, 0, wxEXPAND | wxALL, 0); - wxStaticBoxSizer *pLayer5 = new wxStaticBoxSizer(wxHORIZONTAL, this, L"Layer 5 (Staff Head)"); - pLayer5->Add(new wxCheckBox(this, ID(5, 0), L"0"), 0, wxALIGN_CENTER | wxALL, 1); - pLayer5->Add(new wxCheckBox(this, ID(5, 2), L"2 (W1)"), 0, wxALIGN_CENTER | wxALL, 1); - pLayer5->Add(new wxCheckBox(this, ID(5, 4), L"4 (B1)"), 0, wxALIGN_CENTER | wxALL, 1); - pLayer5->Add(new wxCheckBox(this, ID(5, 6), L"6 (W2)"), 0, wxALIGN_CENTER | wxALL, 1); - pLayer5->Add(new wxCheckBox(this, ID(5, 8), L"8 (B2)"), 0, wxALIGN_CENTER | wxALL, 1); - pLayer5->Add(new wxCheckBox(this, ID(5, 10), L"10"), 0, wxALIGN_CENTER | wxALL, 1); - pSidebarSizer->Add(pLayer5, 0, wxEXPAND | wxALL, 0); + wxStaticBoxSizer* pLayer5 = + new wxStaticBoxSizer(wxHORIZONTAL, this, L"Layer 5 (Staff Head)"); + pLayer5->Add(new wxCheckBox(this, ID(5, 0), L"0"), 0, wxALIGN_CENTER | wxALL, + 1); + pLayer5->Add(new wxCheckBox(this, ID(5, 2), L"2 (W1)"), 0, + wxALIGN_CENTER | wxALL, 1); + pLayer5->Add(new wxCheckBox(this, ID(5, 4), L"4 (B1)"), 0, + wxALIGN_CENTER | wxALL, 1); + pLayer5->Add(new wxCheckBox(this, ID(5, 6), L"6 (W2)"), 0, + wxALIGN_CENTER | wxALL, 1); + pLayer5->Add(new wxCheckBox(this, ID(5, 8), L"8 (B2)"), 0, + wxALIGN_CENTER | wxALL, 1); + pLayer5->Add(new wxCheckBox(this, ID(5, 10), L"10"), 0, + wxALIGN_CENTER | wxALL, 1); + pSidebarSizer->Add(pLayer5, 0, wxEXPAND | wxALL, 0); - wxStaticBoxSizer *pLayer10 = new wxStaticBoxSizer(wxHORIZONTAL, this, L"Layer 10 (Wall Colour / Smoke)"); - pLayer10->Add(new wxCheckBox(this, ID(10, 2), L"2 (Yellow / Smoke)"), 0, wxALIGN_CENTER | wxALL, 1); - pLayer10->Add(new wxCheckBox(this, ID(10, 4), L"4 (Blue)"), 0, wxALIGN_CENTER | wxALL, 1); - pLayer10->Add(new wxCheckBox(this, ID(10, 6), L"6 (White)"), 0, wxALIGN_CENTER | wxALL, 1); - pSidebarSizer->Add(pLayer10, 0, wxEXPAND | wxALL, 0); + wxStaticBoxSizer* pLayer10 = new wxStaticBoxSizer( + wxHORIZONTAL, this, L"Layer 10 (Wall Colour / Smoke)"); + pLayer10->Add(new wxCheckBox(this, ID(10, 2), L"2 (Yellow / Smoke)"), 0, + wxALIGN_CENTER | wxALL, 1); + pLayer10->Add(new wxCheckBox(this, ID(10, 4), L"4 (Blue)"), 0, + wxALIGN_CENTER | wxALL, 1); + pLayer10->Add(new wxCheckBox(this, ID(10, 6), L"6 (White)"), 0, + wxALIGN_CENTER | wxALL, 1); + pSidebarSizer->Add(pLayer10, 0, wxEXPAND | wxALL, 0); - wxStaticBoxSizer *pLayer11 = new wxStaticBoxSizer(wxHORIZONTAL, this, L"Layer 11 (Wall Colour / Smoke / Screen)"); - pLayer11->Add(new wxCheckBox(this, ID(11, 2), L"2 (Yellow / Smoke / On)"), 0, wxALIGN_CENTER | wxALL, 1); - pLayer11->Add(new wxCheckBox(this, ID(11, 4), L"4 (Blue)"), 0, wxALIGN_CENTER | wxALL, 1); - pLayer11->Add(new wxCheckBox(this, ID(11, 6), L"6 (Green)"), 0, wxALIGN_CENTER | wxALL, 1); - pSidebarSizer->Add(pLayer11, 0, wxEXPAND | wxALL, 0); + wxStaticBoxSizer* pLayer11 = new wxStaticBoxSizer( + wxHORIZONTAL, this, L"Layer 11 (Wall Colour / Smoke / Screen)"); + pLayer11->Add(new wxCheckBox(this, ID(11, 2), L"2 (Yellow / Smoke / On)"), 0, + wxALIGN_CENTER | wxALL, 1); + pLayer11->Add(new wxCheckBox(this, ID(11, 4), L"4 (Blue)"), 0, + wxALIGN_CENTER | wxALL, 1); + pLayer11->Add(new wxCheckBox(this, ID(11, 6), L"6 (Green)"), 0, + wxALIGN_CENTER | wxALL, 1); + pSidebarSizer->Add(pLayer11, 0, wxEXPAND | wxALL, 0); - wxStaticBoxSizer *pLayer12 = new wxStaticBoxSizer(wxHORIZONTAL, this, L"Layer 12 (Smoke)"); - pLayer12->Add(new wxCheckBox(this, ID(12, 2), L"2 (Smoke)"), 0, wxALIGN_CENTER | wxALL, 1); - pSidebarSizer->Add(pLayer12, 0, wxEXPAND | wxALL, 0); + wxStaticBoxSizer* pLayer12 = + new wxStaticBoxSizer(wxHORIZONTAL, this, L"Layer 12 (Smoke)"); + pLayer12->Add(new wxCheckBox(this, ID(12, 2), L"2 (Smoke)"), 0, + wxALIGN_CENTER | wxALL, 1); + pSidebarSizer->Add(pLayer12, 0, wxEXPAND | wxALL, 0); - wxStaticBoxSizer *pMoodOverlay = new wxStaticBoxSizer(wxVERTICAL, this, L"Overlays"); - pMoodOverlay->Add(new wxCheckBox(this, ID_DRAW_MOOD, L"Draw mood overlay"), 0, wxEXPAND | wxALL, 1); - wxBoxSizer *pMoodRow = new wxBoxSizer(wxHORIZONTAL); - pMoodRow->Add(new wxStaticText(this, wxID_ANY, L"Marker position (click to move it):"), 0, wxEXPAND | wxRIGHT, 2); - pMoodRow->Add(m_txtMoodPosition[0] = new wxTextCtrl(this, wxID_ANY, L"{0, 0}"), 1, wxEXPAND | wxRIGHT, 1); - pMoodRow->Add(m_txtMoodPosition[1] = new wxTextCtrl(this, wxID_ANY, L"{0, 0, \"px\"}"), 1, wxEXPAND); - pMoodOverlay->Add(pMoodRow, 1, wxEXPAND | wxALL, 2); - pMoodOverlay->Add(new wxCheckBox(this, ID_DRAW_COORDINATES, L"Draw tile coodinates"), 0, wxEXPAND | wxALL, 0); - pSidebarSizer->Add(pMoodOverlay, 0, wxEXPAND | wxALL, 0); - m_bDrawMood = false; - m_bDrawCoordinates = false; - m_iMoodDrawX = 0; - m_iMoodDrawY = 0; + wxStaticBoxSizer* pMoodOverlay = + new wxStaticBoxSizer(wxVERTICAL, this, L"Overlays"); + pMoodOverlay->Add(new wxCheckBox(this, ID_DRAW_MOOD, L"Draw mood overlay"), 0, + wxEXPAND | wxALL, 1); + wxBoxSizer* pMoodRow = new wxBoxSizer(wxHORIZONTAL); + pMoodRow->Add( + new wxStaticText(this, wxID_ANY, L"Marker position (click to move it):"), + 0, wxEXPAND | wxRIGHT, 2); + pMoodRow->Add( + m_txtMoodPosition[0] = new wxTextCtrl(this, wxID_ANY, L"{0, 0}"), 1, + wxEXPAND | wxRIGHT, 1); + pMoodRow->Add( + m_txtMoodPosition[1] = new wxTextCtrl(this, wxID_ANY, L"{0, 0, \"px\"}"), + 1, wxEXPAND); + pMoodOverlay->Add(pMoodRow, 1, wxEXPAND | wxALL, 2); + pMoodOverlay->Add( + new wxCheckBox(this, ID_DRAW_COORDINATES, L"Draw tile coodinates"), 0, + wxEXPAND | wxALL, 0); + pSidebarSizer->Add(pMoodOverlay, 0, wxEXPAND | wxALL, 0); + m_bDrawMood = false; + m_bDrawCoordinates = false; + m_iMoodDrawX = 0; + m_iMoodDrawY = 0; - for(int iLayer = 0; iLayer < 13; ++iLayer) - { - wxCheckBox *pCheck = wxDynamicCast(FindWindow(ID(iLayer, 0)), wxCheckBox); - if(pCheck != NULL) - { - pCheck->SetValue(true); - m_mskLayers.set(iLayer, 0); - } + for (int iLayer = 0; iLayer < 13; ++iLayer) { + wxCheckBox* pCheck = wxDynamicCast(FindWindow(ID(iLayer, 0)), wxCheckBox); + if (pCheck != NULL) { + pCheck->SetValue(true); + m_mskLayers.set(iLayer, 0); } + } - Connect(ID(0, 0), ID(12, 24), wxEVT_COMMAND_CHECKBOX_CLICKED, (wxObjectEventFunction)&frmMain::_onToggleMask); + Connect(ID(0, 0), ID(12, 24), wxEVT_COMMAND_CHECKBOX_CLICKED, + (wxObjectEventFunction)&frmMain::_onToggleMask); #undef ID - wxStaticBoxSizer *pSearch = new wxStaticBoxSizer(wxVERTICAL, this, L"Search"); - wxBoxSizer *pSearchButtons = new wxBoxSizer(wxHORIZONTAL); - pSearchButtons->Add(new wxButton(this, ID_SEARCH_LAYER_ID, L"Layer/ID"), 0, wxALL, 1); - pSearchButtons->Add(new wxButton(this, ID_SEARCH_FRAME, L"Frame"), 0, wxALL, 1); - pSearchButtons->Add(new wxButton(this, ID_SEARCH_SOUND, L"Sound"), 0, wxALL, 1); - pSearch->Add(pSearchButtons, 0); - pSearch->Add(m_lstSearchResults = new wxListBox(this, ID_SEARCH_RESULTS), 1, wxEXPAND | wxALL, 1); + wxStaticBoxSizer* pSearch = new wxStaticBoxSizer(wxVERTICAL, this, L"Search"); + wxBoxSizer* pSearchButtons = new wxBoxSizer(wxHORIZONTAL); + pSearchButtons->Add(new wxButton(this, ID_SEARCH_LAYER_ID, L"Layer/ID"), 0, + wxALL, 1); + pSearchButtons->Add(new wxButton(this, ID_SEARCH_FRAME, L"Frame"), 0, wxALL, + 1); + pSearchButtons->Add(new wxButton(this, ID_SEARCH_SOUND, L"Sound"), 0, wxALL, + 1); + pSearch->Add(pSearchButtons, 0); + pSearch->Add(m_lstSearchResults = new wxListBox(this, ID_SEARCH_RESULTS), 1, + wxEXPAND | wxALL, 1); - wxStaticBoxSizer *pFrameFlags = new wxStaticBoxSizer(wxHORIZONTAL, this, L"Frame Flags"); - wxBoxSizer *pFlags1 = new wxBoxSizer(wxVERTICAL); - pFlags1->Add(m_txtFrameFlags[0] = new wxTextCtrl(this, wxID_ANY), 0, wxEXPAND | wxALL, 2); - pFlags1->Add(m_chkFrameFlags[0] = new wxCheckBox(this, wxID_ANY, L"2^0"), 0, wxEXPAND | wxALL, 2); - pFlags1->Add(m_chkFrameFlags[1] = new wxCheckBox(this, wxID_ANY, L"2^1"), 0, wxEXPAND | wxALL, 2); - pFlags1->Add(m_chkFrameFlags[2] = new wxCheckBox(this, wxID_ANY, L"2^2"), 0, wxEXPAND | wxALL, 2); - pFlags1->Add(m_chkFrameFlags[3] = new wxCheckBox(this, wxID_ANY, L"2^3"), 0, wxEXPAND | wxALL, 2); - pFlags1->Add(m_chkFrameFlags[4] = new wxCheckBox(this, wxID_ANY, L"2^4"), 0, wxEXPAND | wxALL, 2); - pFlags1->Add(m_chkFrameFlags[5] = new wxCheckBox(this, wxID_ANY, L"2^5"), 0, wxEXPAND | wxALL, 2); - pFlags1->Add(m_chkFrameFlags[6] = new wxCheckBox(this, wxID_ANY, L"2^6"), 0, wxEXPAND | wxALL, 2); - pFlags1->Add(m_chkFrameFlags[7] = new wxCheckBox(this, wxID_ANY, L"2^7"), 0, wxEXPAND | wxALL, 2); - pFrameFlags->Add(pFlags1, 1, wxEXPAND); - wxBoxSizer *pFlags2 = new wxBoxSizer(wxVERTICAL); - pFlags2->Add(m_txtFrameFlags[1] = new wxTextCtrl(this, wxID_ANY), 0, wxEXPAND | wxALL, 2); - pFlags2->Add(m_chkFrameFlags[8] = new wxCheckBox(this, wxID_ANY, L"2^8 (Animation Start)"), 0, wxEXPAND | wxALL, 2); - pFlags2->Add(m_chkFrameFlags[9] = new wxCheckBox(this, wxID_ANY, L"2^9"), 0, wxEXPAND | wxALL, 2); - pFlags2->Add(m_chkFrameFlags[10] = new wxCheckBox(this, wxID_ANY, L"2^10"), 0, wxEXPAND | wxALL, 2); - pFlags2->Add(m_chkFrameFlags[11] = new wxCheckBox(this, wxID_ANY, L"2^11"), 0, wxEXPAND | wxALL, 2); - pFlags2->Add(m_chkFrameFlags[12] = new wxCheckBox(this, wxID_ANY, L"2^12"), 0, wxEXPAND | wxALL, 2); - pFlags2->Add(m_chkFrameFlags[13] = new wxCheckBox(this, wxID_ANY, L"2^13"), 0, wxEXPAND | wxALL, 2); - pFlags2->Add(m_chkFrameFlags[14] = new wxCheckBox(this, wxID_ANY, L"2^14"), 0, wxEXPAND | wxALL, 2); - pFlags2->Add(m_chkFrameFlags[15] = new wxCheckBox(this, wxID_ANY, L"2^15"), 0, wxEXPAND | wxALL, 2); - pFrameFlags->Add(pFlags2, 1, wxEXPAND); + wxStaticBoxSizer* pFrameFlags = + new wxStaticBoxSizer(wxHORIZONTAL, this, L"Frame Flags"); + wxBoxSizer* pFlags1 = new wxBoxSizer(wxVERTICAL); + pFlags1->Add(m_txtFrameFlags[0] = new wxTextCtrl(this, wxID_ANY), 0, + wxEXPAND | wxALL, 2); + pFlags1->Add(m_chkFrameFlags[0] = new wxCheckBox(this, wxID_ANY, L"2^0"), 0, + wxEXPAND | wxALL, 2); + pFlags1->Add(m_chkFrameFlags[1] = new wxCheckBox(this, wxID_ANY, L"2^1"), 0, + wxEXPAND | wxALL, 2); + pFlags1->Add(m_chkFrameFlags[2] = new wxCheckBox(this, wxID_ANY, L"2^2"), 0, + wxEXPAND | wxALL, 2); + pFlags1->Add(m_chkFrameFlags[3] = new wxCheckBox(this, wxID_ANY, L"2^3"), 0, + wxEXPAND | wxALL, 2); + pFlags1->Add(m_chkFrameFlags[4] = new wxCheckBox(this, wxID_ANY, L"2^4"), 0, + wxEXPAND | wxALL, 2); + pFlags1->Add(m_chkFrameFlags[5] = new wxCheckBox(this, wxID_ANY, L"2^5"), 0, + wxEXPAND | wxALL, 2); + pFlags1->Add(m_chkFrameFlags[6] = new wxCheckBox(this, wxID_ANY, L"2^6"), 0, + wxEXPAND | wxALL, 2); + pFlags1->Add(m_chkFrameFlags[7] = new wxCheckBox(this, wxID_ANY, L"2^7"), 0, + wxEXPAND | wxALL, 2); + pFrameFlags->Add(pFlags1, 1, wxEXPAND); + wxBoxSizer* pFlags2 = new wxBoxSizer(wxVERTICAL); + pFlags2->Add(m_txtFrameFlags[1] = new wxTextCtrl(this, wxID_ANY), 0, + wxEXPAND | wxALL, 2); + pFlags2->Add(m_chkFrameFlags[8] = + new wxCheckBox(this, wxID_ANY, L"2^8 (Animation Start)"), + 0, wxEXPAND | wxALL, 2); + pFlags2->Add(m_chkFrameFlags[9] = new wxCheckBox(this, wxID_ANY, L"2^9"), 0, + wxEXPAND | wxALL, 2); + pFlags2->Add(m_chkFrameFlags[10] = new wxCheckBox(this, wxID_ANY, L"2^10"), 0, + wxEXPAND | wxALL, 2); + pFlags2->Add(m_chkFrameFlags[11] = new wxCheckBox(this, wxID_ANY, L"2^11"), 0, + wxEXPAND | wxALL, 2); + pFlags2->Add(m_chkFrameFlags[12] = new wxCheckBox(this, wxID_ANY, L"2^12"), 0, + wxEXPAND | wxALL, 2); + pFlags2->Add(m_chkFrameFlags[13] = new wxCheckBox(this, wxID_ANY, L"2^13"), 0, + wxEXPAND | wxALL, 2); + pFlags2->Add(m_chkFrameFlags[14] = new wxCheckBox(this, wxID_ANY, L"2^14"), 0, + wxEXPAND | wxALL, 2); + pFlags2->Add(m_chkFrameFlags[15] = new wxCheckBox(this, wxID_ANY, L"2^15"), 0, + wxEXPAND | wxALL, 2); + pFrameFlags->Add(pFlags2, 1, wxEXPAND); - pMainSizer->Add(pSidebarSizer, 0, wxEXPAND | wxALL, 2); + pMainSizer->Add(pSidebarSizer, 0, wxEXPAND | wxALL, 2); - wxSizer* pRightHandSizer = new wxBoxSizer(wxVERTICAL); - pRightHandSizer->AddSpacer(1); + wxSizer* pRightHandSizer = new wxBoxSizer(wxVERTICAL); + pRightHandSizer->AddSpacer(1); - pRightHandSizer->Add(m_panFrame = new wxPanel(this, wxID_ANY, def, wxBORDER_SIMPLE), 0, wxEXPAND | wxALL, 2); - m_panFrame->Connect(wxEVT_PAINT, (wxObjectEventFunction)&frmMain::_onPanelPaint, NULL, this); - m_panFrame->Connect(wxEVT_LEFT_UP, (wxObjectEventFunction)&frmMain::_onPanelClick, NULL, this); - m_panFrame->SetMinSize(m_panFrame->ClientToWindowSize(wxSize(402, 402))); + pRightHandSizer->Add( + m_panFrame = new wxPanel(this, wxID_ANY, def, wxBORDER_SIMPLE), 0, + wxEXPAND | wxALL, 2); + m_panFrame->Connect( + wxEVT_PAINT, (wxObjectEventFunction)&frmMain::_onPanelPaint, NULL, this); + m_panFrame->Connect(wxEVT_LEFT_UP, + (wxObjectEventFunction)&frmMain::_onPanelClick, NULL, + this); + m_panFrame->SetMinSize(m_panFrame->ClientToWindowSize(wxSize(402, 402))); - pRightHandSizer->AddSpacer(1); - pRightHandSizer->Add(pSearch, 1, wxEXPAND | wxALL, 0); - pRightHandSizer->Add(pFrameFlags, 0, wxEXPAND | wxALL, 0); - pMainSizer->Add(pRightHandSizer, 1, wxEXPAND | wxALL, 0); + pRightHandSizer->AddSpacer(1); + pRightHandSizer->Add(pSearch, 1, wxEXPAND | wxALL, 0); + pRightHandSizer->Add(pFrameFlags, 0, wxEXPAND | wxALL, 0); + pMainSizer->Add(pRightHandSizer, 1, wxEXPAND | wxALL, 0); - SetBackgroundColour(m_btnPlayPause->GetBackgroundColour()); - SetSizer(pMainSizer); + SetBackgroundColour(m_btnPlayPause->GetBackgroundColour()); + SetSizer(pMainSizer); - SetMinSize(ClientToWindowSize(pMainSizer->CalcMin())); - SetSize(GetMinSize()); + SetMinSize(ClientToWindowSize(pMainSizer->CalcMin())); + SetSize(GetMinSize()); - load(); + load(); - m_tmrAnimate.SetOwner(this, ID_TIMER_ANIMATE); - m_tmrAnimate.Start(100); + m_tmrAnimate.SetOwner(this, ID_TIMER_ANIMATE); + m_tmrAnimate.Start(100); } -void frmMain::_onBrowse(wxCommandEvent& WXUNUSED(evt)) -{ - m_txtTHPath->SetValue(::wxDirSelector(L"Choose Theme Hospital root folder", - m_txtTHPath->GetValue(), 0, wxDefaultPosition, this)); +void frmMain::_onBrowse(wxCommandEvent& WXUNUSED(evt)) { + m_txtTHPath->SetValue(::wxDirSelector(L"Choose Theme Hospital root folder", + m_txtTHPath->GetValue(), 0, + wxDefaultPosition, this)); } -void frmMain::_onLoad(wxCommandEvent& WXUNUSED(evt)) -{ - ::wxInitAllImageHandlers(); - load(); +void frmMain::_onLoad(wxCommandEvent& WXUNUSED(evt)) { + ::wxInitAllImageHandlers(); + load(); } -void frmMain::load() -{ - wxBusyCursor oBusy; - wxString sPath = m_txtTHPath->GetValue(); - if(sPath.IsEmpty()) - return; - if(sPath.Mid(sPath.Len() - 1) != wxFileName::GetPathSeparator()) - { - sPath += wxFileName::GetPathSeparator(); - } - if(!wxFileName::DirExists(sPath)) - { - ::wxMessageBox(L"Theme Hospital path non-existant", L"Load Animations", wxOK | wxICON_ERROR, this); - return; - } - sPath = _getCaseSensitivePath(L"DATA", sPath); +void frmMain::load() { + wxBusyCursor oBusy; + wxString sPath = m_txtTHPath->GetValue(); + if (sPath.IsEmpty()) return; + if (sPath.Mid(sPath.Len() - 1) != wxFileName::GetPathSeparator()) { sPath += wxFileName::GetPathSeparator(); - wxString aPath = _getCaseSensitivePath(L"VSPR-0", sPath); - aPath += wxFileName::GetPathSeparator(); - m_oAnims.setSpritePath(aPath); + } + if (!wxFileName::DirExists(sPath)) { + ::wxMessageBox(L"Theme Hospital path non-existant", L"Load Animations", + wxOK | wxICON_ERROR, this); + return; + } + sPath = _getCaseSensitivePath(L"DATA", sPath); + sPath += wxFileName::GetPathSeparator(); + wxString aPath = _getCaseSensitivePath(L"VSPR-0", sPath); + aPath += wxFileName::GetPathSeparator(); + m_oAnims.setSpritePath(aPath); - if(!m_oAnims.loadAnimationFile(_getCaseSensitivePath(L"VSTART-1.ANI", sPath)) - ||!m_oAnims.loadFrameFile(_getCaseSensitivePath(L"VFRA-1.ANI", sPath)) - ||!m_oAnims.loadListFile(_getCaseSensitivePath(L"VLIST-1.ANI", sPath)) - ||!m_oAnims.loadElementFile(_getCaseSensitivePath(L"VELE-1.ANI", sPath)) - ||!m_oAnims.loadTableFile(_getCaseSensitivePath(L"VSPR-0.TAB", sPath)) - ||!m_oAnims.loadSpriteFile(_getCaseSensitivePath(L"VSPR-0.DAT", sPath)) - ||!m_oAnims.loadPaletteFile(_getCaseSensitivePath(L"MPALETTE.DAT", sPath)) - ||!m_oAnims.loadGhostFile(_getCaseSensitivePath(L"../QDATA/GHOST1.DAT", sPath), 1) - ||!m_oAnims.loadGhostFile(_getCaseSensitivePath(L"../QDATA/GHOST2.DAT", sPath), 2) - ||!m_oAnims.loadGhostFile(_getCaseSensitivePath(L"../QDATA/GHOST66.DAT", sPath), 3)) - { - ::wxMessageBox(L"Cannot load one or more data files", L"Load Animations", wxOK | wxICON_ERROR, this); + if (!m_oAnims.loadAnimationFile( + _getCaseSensitivePath(L"VSTART-1.ANI", sPath)) || + !m_oAnims.loadFrameFile(_getCaseSensitivePath(L"VFRA-1.ANI", sPath)) || + !m_oAnims.loadListFile(_getCaseSensitivePath(L"VLIST-1.ANI", sPath)) || + !m_oAnims.loadElementFile(_getCaseSensitivePath(L"VELE-1.ANI", sPath)) || + !m_oAnims.loadTableFile(_getCaseSensitivePath(L"VSPR-0.TAB", sPath)) || + !m_oAnims.loadSpriteFile(_getCaseSensitivePath(L"VSPR-0.DAT", sPath)) || + !m_oAnims.loadPaletteFile( + _getCaseSensitivePath(L"MPALETTE.DAT", sPath)) || + !m_oAnims.loadGhostFile( + _getCaseSensitivePath(L"../QDATA/GHOST1.DAT", sPath), 1) || + !m_oAnims.loadGhostFile( + _getCaseSensitivePath(L"../QDATA/GHOST2.DAT", sPath), 2) || + !m_oAnims.loadGhostFile( + _getCaseSensitivePath(L"../QDATA/GHOST66.DAT", sPath), 3)) { + ::wxMessageBox(L"Cannot load one or more data files", L"Load Animations", + wxOK | wxICON_ERROR, this); + } + m_oAnims.markDuplicates(); + + m_txtAnimCount->SetValue( + wxString::Format(L"%u", (int)m_oAnims.getAnimationCount())); + + m_imgBackground.Create(400, 400); + { + unsigned char* pData = m_imgBackground.GetData(); + unsigned char cPrimary = 0xFF; + unsigned char cSecondary = 0xE0; + for (int y = 0; y < 400; ++y) { + for (int x = 0; x < 400; x += 8) { + memset(pData, cPrimary, 4 * 3); + pData += 4 * 3; + memset(pData, cSecondary, 4 * 3); + pData += 4 * 3; + } + if (y % 4 == 3) { + cPrimary ^= cSecondary; + cSecondary ^= cPrimary; + cPrimary ^= cSecondary; + } } - m_oAnims.markDuplicates(); - - m_txtAnimCount->SetValue(wxString::Format(L"%u", (int)m_oAnims.getAnimationCount())); - - m_imgBackground.Create(400, 400); + wxBitmap bmpBackdrop(backdrop_xpm); + wxBitmap bmpBackground(m_imgBackground); { - unsigned char* pData = m_imgBackground.GetData(); - unsigned char cPrimary = 0xFF; - unsigned char cSecondary = 0xE0; - for(int y = 0; y < 400; ++y) - { - for(int x = 0; x < 400; x += 8) - { - memset(pData, cPrimary , 4 * 3); - pData += 4 * 3; - memset(pData, cSecondary, 4 * 3); - pData += 4 * 3; - } - if(y % 4 == 3) - { - cPrimary ^= cSecondary; - cSecondary ^= cPrimary; - cPrimary ^= cSecondary; - } - } - wxBitmap bmpBackdrop(backdrop_xpm); - wxBitmap bmpBackground(m_imgBackground); - { - wxMemoryDC dcBlit; - dcBlit.SelectObject(bmpBackground); - dcBlit.DrawBitmap(bmpBackdrop, 78, 170, true); - } - m_imgBackground = bmpBackground.ConvertToImage(); + wxMemoryDC dcBlit; + dcBlit.SelectObject(bmpBackground); + dcBlit.DrawBitmap(bmpBackdrop, 78, 170, true); } + m_imgBackground = bmpBackground.ConvertToImage(); + } - _onAnimChange(0); + _onAnimChange(0); } -void frmMain::_onToggleMask(wxCommandEvent& evt) -{ - int iID = evt.GetId() - ID_LAYER_CHECKS; - int iLayer = iID / 25; - iID %= 25; +void frmMain::_onToggleMask(wxCommandEvent& evt) { + int iID = evt.GetId() - ID_LAYER_CHECKS; + int iLayer = iID / 25; + iID %= 25; - if(evt.IsChecked()) - m_mskLayers.set(iLayer, iID); - else - m_mskLayers.clear(iLayer, iID); + if (evt.IsChecked()) + m_mskLayers.set(iLayer, iID); + else + m_mskLayers.clear(iLayer, iID); + m_panFrame->Refresh(false); +} + +frmMain::~frmMain() {} + +void frmMain::_onFirstAnim(wxCommandEvent& evt) { + if (m_iCurrentAnim > 0) _onAnimChange(0); +} + +void frmMain::_onPrevAnim(wxCommandEvent& evt) { + size_t iAnim = m_iCurrentAnim; + while (iAnim > 0) { + --iAnim; + if (!m_oAnims.isAnimationDuplicate(iAnim)) { + _onAnimChange(iAnim); + break; + } + } +} + +void frmMain::_onNextAnim(wxCommandEvent& evt) { + size_t iAnim = m_iCurrentAnim + 1; + while (iAnim < m_oAnims.getAnimationCount()) { + if (!m_oAnims.isAnimationDuplicate(iAnim)) { + _onAnimChange(iAnim); + break; + } + iAnim++; + } +} + +void frmMain::_onLastAnim(wxCommandEvent& evt) { + if (m_iCurrentAnim < m_oAnims.getAnimationCount() - 1) + _onAnimChange(m_oAnims.getAnimationCount() - 1); +} + +void frmMain::_onAnimChar(wxCommandEvent& evt) { + long iAnim; + if (evt.GetString().ToLong(&iAnim)) { + if (iAnim >= 0 && iAnim < (long)m_oAnims.getAnimationCount()) { + _onAnimChange((size_t)iAnim); + } + } +} + +void frmMain::_onGhostFileChange(wxCommandEvent& evt) { + m_iGhostFile = evt.GetId() - ID_GHOST_0; + m_oAnims.setGhost(m_iGhostFile, m_iGhostIndex); + m_panFrame->Refresh(false); +} + +void frmMain::_onGhostIndexChange(wxSpinEvent& evt) { + m_iGhostIndex = evt.GetPosition(); + m_oAnims.setGhost(m_iGhostFile, m_iGhostIndex); + m_panFrame->Refresh(false); +} + +void frmMain::_onAnimChange(size_t iIndex) { + m_iCurrentAnim = iIndex; + m_txtAnimIndex->ChangeValue(wxString::Format(L"%u", (int)iIndex)); + m_iCurrentFrame = 0; + + THLayerMask oMask; + m_oAnims.getAnimationMask(iIndex, oMask); + for (int iLayer = 0; iLayer < 13; ++iLayer) { + for (int iId = 0; iId < 32; ++iId) { + wxCheckBox* pCheck = wxDynamicCast( + FindWindow(ID_LAYER_CHECKS + iLayer * 25 + iId), wxCheckBox); + if (pCheck) { + pCheck->Enable(oMask.isSet(iLayer, iId)); + } + } + } + + m_panFrame->Refresh(false); + m_txtFrameIndex->SetValue(wxString::Format(L"0")); + m_txtFrameCount->SetValue( + wxString::Format(L"%u", (int)m_oAnims.getFrameCount(iIndex))); +} + +void frmMain::_onPlayPause(wxCommandEvent& evt) { + m_bPlayingAnimation = !m_bPlayingAnimation; + if (m_bPlayingAnimation) + m_btnPlayPause->SetLabel(L"Pause"); + else + m_btnPlayPause->SetLabel(L"Play"); +} + +void frmMain::_onPrevFrame(wxCommandEvent& evt) { + if (m_oAnims.getAnimationCount() == 0) return; + + if (m_iCurrentFrame == 0) + m_iCurrentFrame = m_oAnims.getFrameCount(m_iCurrentAnim) - 1; + else + m_iCurrentFrame = + (m_iCurrentFrame - 1) % m_oAnims.getFrameCount(m_iCurrentAnim); + m_txtFrameIndex->SetValue(wxString::Format(L"%u", m_iCurrentFrame)); + m_panFrame->Refresh(false); +} + +void frmMain::_onNextFrame(wxCommandEvent& evt) { + if (m_oAnims.getAnimationCount() == 0) return; + + m_iCurrentFrame = + (m_iCurrentFrame + 1) % m_oAnims.getFrameCount(m_iCurrentAnim); + m_txtFrameIndex->SetValue(wxString::Format(L"%u", m_iCurrentFrame)); + m_panFrame->Refresh(false); +} + +void frmMain::_onTimer(wxTimerEvent& evt) { + if (m_bPlayingAnimation) { + if (m_oAnims.getAnimationCount() == 0) return; + + m_iCurrentFrame = + (m_iCurrentFrame + 1) % m_oAnims.getFrameCount(m_iCurrentAnim); + m_txtFrameIndex->SetValue(wxString::Format(L"%u", (int)m_iCurrentFrame)); m_panFrame->Refresh(false); + } } -frmMain::~frmMain() -{ +void frmMain::_onToggleDrawMood(wxCommandEvent& evt) { + m_bDrawMood = evt.IsChecked(); + m_panFrame->Refresh(false); } -void frmMain::_onFirstAnim(wxCommandEvent& evt) -{ - if(m_iCurrentAnim > 0) - _onAnimChange(0); +void frmMain::_onToggleDrawCoordinates(wxCommandEvent& evt) { + m_bDrawCoordinates = evt.IsChecked(); + m_panFrame->Refresh(false); } -void frmMain::_onPrevAnim(wxCommandEvent& evt) -{ - size_t iAnim = m_iCurrentAnim; - while(iAnim > 0) - { - --iAnim; - if(!m_oAnims.isAnimationDuplicate(iAnim)) - { - _onAnimChange(iAnim); - break; - } +void frmMain::_onPanelPaint(wxPaintEvent& evt) { + wxPaintDC DC(m_panFrame); + + wxImage imgCanvas(400, 400, false); + if (m_imgBackground.IsOk()) { + memcpy(imgCanvas.GetData(), m_imgBackground.GetData(), 400 * 400 * 3); + } else { + memset(imgCanvas.GetData(), 0xFF, 400 * 400 * 3); + } + if (!imgCanvas.HasAlpha()) { + imgCanvas.InitAlpha(); + } + for (int iX = 0; iX < 400; ++iX) { + for (int iY = 0; iY < 400; ++iY) { + // set completely opaque + imgCanvas.SetAlpha(iX, iY, (unsigned char)255); } -} + } + wxSize oSize; + m_oAnims.drawFrame(imgCanvas, m_iCurrentAnim, m_iCurrentFrame, &m_mskLayers, + oSize); + if (m_bDrawMood) { + m_oAnims.drawFrame(imgCanvas, 4048, 0, &m_mskLayers, oSize, + m_iMoodDrawX - 1, m_iMoodDrawY - 80); + } + th_frame_t* pFrame = m_oAnims.getFrameStruct(m_iCurrentAnim, m_iCurrentFrame); + uint16_t iFlags = 0; + if (pFrame) { + iFlags = pFrame->flags; + } + int iFlags1 = (int)(iFlags & 0xFF); + int iFlags2 = (int)(iFlags >> 8); + m_txtFrameFlags[0]->SetValue( + wxString::Format(L"0x%02x (%03i)", iFlags1, iFlags1)); + m_txtFrameFlags[1]->SetValue( + wxString::Format(L"0x%02x00 (256 * %03i)", iFlags2, iFlags2)); + for (int i = 0; i < 16; ++i) + m_chkFrameFlags[i]->SetValue((iFlags & (1 << i)) != 0); -void frmMain::_onNextAnim(wxCommandEvent& evt) -{ - size_t iAnim = m_iCurrentAnim + 1; - while(iAnim < m_oAnims.getAnimationCount()) - { - if(!m_oAnims.isAnimationDuplicate(iAnim)) - { - _onAnimChange(iAnim); - break; - } - iAnim++; + wxBitmap bmpCanvas(imgCanvas); + + DC.DrawBitmap(bmpCanvas, 1, 1, false); + + // Draw relative tile coordinates + if (m_bDrawCoordinates) { + for (int i = -1; i <= 1; ++i) { + for (int j = -1; j <= 1; ++j) { + _drawCoordinates(DC, i, j); + } } + } } -void frmMain::_onLastAnim(wxCommandEvent& evt) -{ - if(m_iCurrentAnim < m_oAnims.getAnimationCount() - 1) - _onAnimChange(m_oAnims.getAnimationCount() - 1); +void frmMain::_onPanelClick(wxMouseEvent& evt) { + m_iMoodDrawX = evt.GetX() - 143; + m_iMoodDrawY = evt.GetY() - 203; + { + double fX = (double)m_iMoodDrawX; + double fY = (double)m_iMoodDrawY; + fY = fY / 32.0; + fX = fX / 64.0; + fY -= fX; + fX *= 2.0; + fX += fY; + m_txtMoodPosition[0]->SetValue(wxString::Format(L"{%.2f, %.2f}", fX, fY)); + } + m_txtMoodPosition[1]->SetValue( + wxString::Format(L"{%i, %i, \"px\"}", m_iMoodDrawX, m_iMoodDrawY)); + if (m_bDrawMood) m_panFrame->Refresh(false); } -void frmMain::_onAnimChar(wxCommandEvent& evt) -{ - long iAnim; - if(evt.GetString().ToLong(&iAnim)) - { - if(iAnim >= 0 && iAnim < (long)m_oAnims.getAnimationCount()) - { - _onAnimChange((size_t)iAnim); - } +void frmMain::_onSearchLayerId(wxCommandEvent& evt) { + int iLayer = ::wxGetNumberFromUser( + L"Enter the layer number to search in (0 - 12)", L"Layer:", + L"Search for Layer / ID Combo", 0, 0, 13, this); + if (iLayer == -1) return; + int iID = ::wxGetNumberFromUser(L"Enter the ID number to search for (0 - 24)", + L"ID:", L"Search for Layer / ID Combo", 0, 0, + 24, this); + if (iID == -1) return; + + m_lstSearchResults->Clear(); + wxBusyCursor oBusy; + for (size_t i = 0; i < m_oAnims.getAnimationCount(); ++i) { + if (m_oAnims.isAnimationDuplicate(i)) continue; + + THLayerMask mskAnim; + m_oAnims.getAnimationMask(i, mskAnim); + if (mskAnim.isSet(iLayer, iID)) { + m_lstSearchResults->Append(wxString::Format(L"%i", (int)i)); } + } } -void frmMain::_onGhostFileChange(wxCommandEvent& evt) -{ - m_iGhostFile = evt.GetId() - ID_GHOST_0; - m_oAnims.setGhost(m_iGhostFile, m_iGhostIndex); - m_panFrame->Refresh(false); -} +void frmMain::_onSearchFrame(wxCommandEvent& evt) { + int iFrame = + ::wxGetNumberFromUser(L"Enter the frame number to search for.", L"Frame:", + L"Search for frame", 0, 0, 20000, this); + if (iFrame == -1) return; -void frmMain::_onGhostIndexChange(wxSpinEvent& evt) -{ - m_iGhostIndex = evt.GetPosition(); - m_oAnims.setGhost(m_iGhostFile, m_iGhostIndex); - m_panFrame->Refresh(false); -} - -void frmMain::_onAnimChange(size_t iIndex) -{ - m_iCurrentAnim = iIndex; - m_txtAnimIndex->ChangeValue(wxString::Format(L"%u", (int)iIndex)); - m_iCurrentFrame = 0; - - THLayerMask oMask; - m_oAnims.getAnimationMask(iIndex, oMask); - for(int iLayer = 0; iLayer < 13; ++iLayer) - { - for(int iId = 0; iId < 32; ++iId) - { - wxCheckBox *pCheck = wxDynamicCast(FindWindow(ID_LAYER_CHECKS + iLayer * 25 + iId), wxCheckBox); - if(pCheck) - { - pCheck->Enable(oMask.isSet(iLayer, iId)); - } - } + m_lstSearchResults->Clear(); + wxBusyCursor oBusy; + for (size_t i = 0; i < m_oAnims.getAnimationCount(); ++i) { + if (m_oAnims.isAnimationDuplicate(i)) continue; + if (m_oAnims.doesAnimationIncludeFrame(i, iFrame)) { + m_lstSearchResults->Append(wxString::Format(L"%i", (int)i)); } - - m_panFrame->Refresh(false); - m_txtFrameIndex->SetValue(wxString::Format(L"0")); - m_txtFrameCount->SetValue(wxString::Format(L"%u", (int)m_oAnims.getFrameCount(iIndex))); + } } -void frmMain::_onPlayPause(wxCommandEvent& evt) -{ - m_bPlayingAnimation = !m_bPlayingAnimation; - if(m_bPlayingAnimation) - m_btnPlayPause->SetLabel(L"Pause"); - else - m_btnPlayPause->SetLabel(L"Play"); -} +void frmMain::_onSearchSoundIndex(wxCommandEvent& evt) { + int iFrame = ::wxGetNumberFromUser(L"Enter the sound index to search for.", + L"Sound index:", L"Search for sound", 0, 0, + 256, this); + if (iFrame == -1) return; -void frmMain::_onPrevFrame(wxCommandEvent& evt) -{ - if(m_oAnims.getAnimationCount() == 0) - return; - - if(m_iCurrentFrame == 0) - m_iCurrentFrame = m_oAnims.getFrameCount(m_iCurrentAnim) - 1; - else - m_iCurrentFrame = (m_iCurrentFrame - 1) % m_oAnims.getFrameCount(m_iCurrentAnim); - m_txtFrameIndex->SetValue(wxString::Format(L"%u", m_iCurrentFrame)); - m_panFrame->Refresh(false); -} - -void frmMain::_onNextFrame(wxCommandEvent& evt) -{ - if(m_oAnims.getAnimationCount() == 0) - return; - - m_iCurrentFrame = (m_iCurrentFrame + 1) % m_oAnims.getFrameCount(m_iCurrentAnim); - m_txtFrameIndex->SetValue(wxString::Format(L"%u", m_iCurrentFrame)); - m_panFrame->Refresh(false); -} - -void frmMain::_onTimer(wxTimerEvent& evt) -{ - if(m_bPlayingAnimation) - { - if(m_oAnims.getAnimationCount() == 0) - return; - - m_iCurrentFrame = (m_iCurrentFrame + 1) % m_oAnims.getFrameCount(m_iCurrentAnim); - m_txtFrameIndex->SetValue(wxString::Format(L"%u", (int)m_iCurrentFrame)); - m_panFrame->Refresh(false); + m_lstSearchResults->Clear(); + wxBusyCursor oBusy; + for (size_t i = 0; i < m_oAnims.getAnimationCount(); ++i) { + if (m_oAnims.isAnimationDuplicate(i)) continue; + size_t iCount = m_oAnims.getFrameCount(i); + for (size_t j = 0; j < iCount; ++j) { + if ((m_oAnims.getFrameStruct(i, j)->flags & 0xFF) == iFrame) { + m_lstSearchResults->Append(wxString::Format(L"%i", (int)i)); + break; + } } + } } -void frmMain::_onToggleDrawMood(wxCommandEvent& evt) -{ - m_bDrawMood = evt.IsChecked(); - m_panFrame->Refresh(false); +void frmMain::_onGotoSearchResult(wxCommandEvent& evt) { + long iAnim; + evt.GetString().ToLong(&iAnim); + _onAnimChange(iAnim); } -void frmMain::_onToggleDrawCoordinates(wxCommandEvent& evt) -{ - m_bDrawCoordinates = evt.IsChecked(); - m_panFrame->Refresh(false); +void frmMain::_drawCoordinates(wxPaintDC& DC, int i, int j) { + int x = 122; // tile (0, 0) text start x-coordinate + int y = 226; // tile (0, 0) text start y-coordinate + wxString s; + s.Printf(_T("(%2d,%2d)"), i, j); + DC.DrawText(s, 32 * (i - j) + x, 16 * (i + j - 2) + y); } -void frmMain::_onPanelPaint(wxPaintEvent& evt) -{ - wxPaintDC DC(m_panFrame); - - wxImage imgCanvas(400, 400, false); - if(m_imgBackground.IsOk()) - { - memcpy(imgCanvas.GetData(), m_imgBackground.GetData(), 400 * 400 * 3); - } - else - { - memset(imgCanvas.GetData(), 0xFF, 400 * 400 * 3); - } - if(!imgCanvas.HasAlpha()) - { - imgCanvas.InitAlpha(); - } - for(int iX = 0; iX < 400; ++iX) - { - for(int iY = 0; iY < 400; ++iY) - { - //set completely opaque - imgCanvas.SetAlpha(iX,iY,(unsigned char)255); - } - } - wxSize oSize; - m_oAnims.drawFrame(imgCanvas, m_iCurrentAnim, m_iCurrentFrame, &m_mskLayers, oSize); - if(m_bDrawMood) - { - m_oAnims.drawFrame(imgCanvas, 4048, 0, &m_mskLayers, oSize, m_iMoodDrawX - 1, m_iMoodDrawY - 80); - } - th_frame_t *pFrame = m_oAnims.getFrameStruct(m_iCurrentAnim, m_iCurrentFrame); - uint16_t iFlags = 0; - if(pFrame) { - iFlags = pFrame->flags; - } - int iFlags1 = (int)(iFlags & 0xFF); - int iFlags2 = (int)(iFlags >> 8); - m_txtFrameFlags[0]->SetValue(wxString::Format(L"0x%02x (%03i)", iFlags1, iFlags1)); - m_txtFrameFlags[1]->SetValue(wxString::Format(L"0x%02x00 (256 * %03i)", iFlags2, iFlags2)); - for(int i = 0; i < 16; ++i) - m_chkFrameFlags[i]->SetValue((iFlags & (1 << i)) != 0); - - wxBitmap bmpCanvas(imgCanvas); - - DC.DrawBitmap(bmpCanvas, 1, 1, false); - - // Draw relative tile coordinates - if (m_bDrawCoordinates) { - for (int i = -1; i <= 1; ++i) { - for (int j = -1; j <= 1; ++j) { - _drawCoordinates(DC, i, j); - } - } - } -} - -void frmMain::_onPanelClick(wxMouseEvent& evt) -{ - m_iMoodDrawX = evt.GetX() - 143; - m_iMoodDrawY = evt.GetY() - 203; - { - double fX = (double)m_iMoodDrawX; - double fY = (double)m_iMoodDrawY; - fY = fY / 32.0; - fX = fX / 64.0; - fY -= fX; - fX *= 2.0; - fX += fY; - m_txtMoodPosition[0]->SetValue(wxString::Format(L"{%.2f, %.2f}", fX, fY)); - } - m_txtMoodPosition[1]->SetValue(wxString::Format(L"{%i, %i, \"px\"}", m_iMoodDrawX, m_iMoodDrawY)); - if(m_bDrawMood) - m_panFrame->Refresh(false); -} - -void frmMain::_onSearchLayerId(wxCommandEvent& evt) -{ - int iLayer = ::wxGetNumberFromUser(L"Enter the layer number to search in (0 - 12)", L"Layer:", L"Search for Layer / ID Combo", 0, 0, 13, this); - if(iLayer == -1) - return; - int iID = ::wxGetNumberFromUser(L"Enter the ID number to search for (0 - 24)", L"ID:", L"Search for Layer / ID Combo", 0, 0, 24, this); - if(iID == -1) - return; - - m_lstSearchResults->Clear(); - wxBusyCursor oBusy; - for(size_t i = 0; i < m_oAnims.getAnimationCount(); ++i) - { - if(m_oAnims.isAnimationDuplicate(i)) - continue; - - THLayerMask mskAnim; - m_oAnims.getAnimationMask(i, mskAnim); - if(mskAnim.isSet(iLayer, iID)) - { - m_lstSearchResults->Append(wxString::Format(L"%i", (int)i)); - } - } -} - -void frmMain::_onSearchFrame(wxCommandEvent& evt) -{ - int iFrame = ::wxGetNumberFromUser(L"Enter the frame number to search for.", L"Frame:", L"Search for frame", 0, 0, 20000, this); - if(iFrame == -1) - return; - - m_lstSearchResults->Clear(); - wxBusyCursor oBusy; - for(size_t i = 0; i < m_oAnims.getAnimationCount(); ++i) - { - if(m_oAnims.isAnimationDuplicate(i)) - continue; - if(m_oAnims.doesAnimationIncludeFrame(i, iFrame)) - { - m_lstSearchResults->Append(wxString::Format(L"%i", (int)i)); - } - } -} - -void frmMain::_onSearchSoundIndex(wxCommandEvent& evt) -{ - int iFrame = ::wxGetNumberFromUser(L"Enter the sound index to search for.", L"Sound index:", L"Search for sound", 0, 0, 256, this); - if(iFrame == -1) - return; - - m_lstSearchResults->Clear(); - wxBusyCursor oBusy; - for(size_t i = 0; i < m_oAnims.getAnimationCount(); ++i) - { - if(m_oAnims.isAnimationDuplicate(i)) - continue; - size_t iCount = m_oAnims.getFrameCount(i); - for(size_t j = 0; j < iCount; ++j) - { - if((m_oAnims.getFrameStruct(i, j)->flags & 0xFF) == iFrame) - { - m_lstSearchResults->Append(wxString::Format(L"%i", (int)i)); - break; - } - } - } -} - -void frmMain::_onGotoSearchResult(wxCommandEvent& evt) -{ - long iAnim; - evt.GetString().ToLong(&iAnim); - _onAnimChange(iAnim); -} - -void frmMain::_drawCoordinates(wxPaintDC& DC, int i, int j) -{ - int x = 122; // tile (0, 0) text start x-coordinate - int y = 226; // tile (0, 0) text start y-coordinate - wxString s; - s.Printf(_T("(%2d,%2d)"), i, j); - DC.DrawText(s, 32 * (i - j) + x, 16 * (i + j - 2) + y); -} - -wxString frmMain::_getCaseSensitivePath(const wxString& sInsensitivePathPart, const wxString& sPath) -{ +wxString frmMain::_getCaseSensitivePath(const wxString& sInsensitivePathPart, + const wxString& sPath) { bool found; bool cont; - if(!wxFileName::IsCaseSensitive()) { return sPath + sInsensitivePathPart; } + if (!wxFileName::IsCaseSensitive()) { + return sPath + sInsensitivePathPart; + } wxString retStr(sPath); - wxStringTokenizer pathTokenizer(sInsensitivePathPart, wxFileName::GetPathSeparator()); - while(pathTokenizer.HasMoreTokens()) - { + wxStringTokenizer pathTokenizer(sInsensitivePathPart, + wxFileName::GetPathSeparator()); + while (pathTokenizer.HasMoreTokens()) { wxDir dir(retStr); - if(!dir.IsOpened()) - { + if (!dir.IsOpened()) { break; } wxString pathPart = pathTokenizer.GetNextToken(); wxString realName; - cont = dir.GetFirst(&realName, wxEmptyString, wxDIR_DIRS|wxDIR_FILES|wxDIR_HIDDEN|wxDIR_DOTDOT); + cont = dir.GetFirst(&realName, wxEmptyString, + wxDIR_DIRS | wxDIR_FILES | wxDIR_HIDDEN | wxDIR_DOTDOT); found = false; - while(cont) - { - if(realName.Upper() == pathPart.Upper()) - { - if(retStr.Last() != wxFileName::GetPathSeparator()) - { + while (cont) { + if (realName.Upper() == pathPart.Upper()) { + if (retStr.Last() != wxFileName::GetPathSeparator()) { retStr += wxFileName::GetPathSeparator(); } retStr += realName; @@ -719,19 +794,16 @@ wxString frmMain::_getCaseSensitivePath(const wxString& sInsensitivePathPart, co cont = dir.GetNext(&realName); } - if(!found) - { + if (!found) { retStr += wxFileName::GetPathSeparator(); retStr += pathPart; break; } } - while(pathTokenizer.HasMoreTokens()) - { + while (pathTokenizer.HasMoreTokens()) { wxString pathPart = pathTokenizer.GetNextToken(); - if(retStr.Last() != wxFileName::GetPathSeparator()) - { + if (retStr.Last() != wxFileName::GetPathSeparator()) { retStr += wxFileName::GetPathSeparator(); } retStr += pathPart; diff --git a/AnimView/frmMain.h b/AnimView/frmMain.h index 2a05d4c5..1f8faae5 100644 --- a/AnimView/frmMain.h +++ b/AnimView/frmMain.h @@ -22,112 +22,113 @@ SOFTWARE. #pragma once #include "config.h" -#include #include -#include #include -#include -#include -#include -#include #include +#include +#include +#include +#include +#include +#include #include #include "th.h" //#include -class frmMain : public wxFrame -{ -public: - frmMain(); - ~frmMain(); +class frmMain : public wxFrame { + public: + frmMain(); + ~frmMain(); - enum - { - ID_FIRST_ANIM = wxID_HIGHEST + 1, - ID_PREV_ANIM, - ID_ANIM_INDEX, - ID_NEXT_ANIM, - ID_LAST_ANIM, - ID_PREV_FRAME, - ID_NEXT_FRAME, - ID_PLAY_PAUSE, - ID_TIMER_ANIMATE, - ID_SEARCH_LAYER_ID, - ID_SEARCH_FRAME, - ID_SEARCH_SOUND, - ID_SEARCH_RESULTS, - ID_GHOST_0, - ID_GHOST_1, - ID_GHOST_2, - ID_GHOST_3, - ID_LOAD, - ID_BROWSE, - ID_EXPORT, - ID_DRAW_MOOD, - ID_DRAW_COORDINATES, - ID_LAYER_CHECKS, // Must be last ID - }; + enum { + ID_FIRST_ANIM = wxID_HIGHEST + 1, + ID_PREV_ANIM, + ID_ANIM_INDEX, + ID_NEXT_ANIM, + ID_LAST_ANIM, + ID_PREV_FRAME, + ID_NEXT_FRAME, + ID_PLAY_PAUSE, + ID_TIMER_ANIMATE, + ID_SEARCH_LAYER_ID, + ID_SEARCH_FRAME, + ID_SEARCH_SOUND, + ID_SEARCH_RESULTS, + ID_GHOST_0, + ID_GHOST_1, + ID_GHOST_2, + ID_GHOST_3, + ID_LOAD, + ID_BROWSE, + ID_EXPORT, + ID_DRAW_MOOD, + ID_DRAW_COORDINATES, + ID_LAYER_CHECKS, // Must be last ID + }; - void load(); - void export_png(); - void exportSpritesPage(bool bComplex, wxString sPath, wxString sFilename, wxString spPath=L"", wxString sPalette=L"MPALETTE.DAT"); - //std::vector<_sprite_t> m_vSprites; + void load(); + void export_png(); + void exportSpritesPage(bool bComplex, wxString sPath, wxString sFilename, + wxString spPath = L"", + wxString sPalette = L"MPALETTE.DAT"); + // std::vector<_sprite_t> m_vSprites; -protected: - void _onLoad(wxCommandEvent& evt); - void _onBrowse(wxCommandEvent& evt); - void _onExport(wxCommandEvent& evt); - void _onFirstAnim(wxCommandEvent& evt); - void _onPrevAnim(wxCommandEvent& evt); - void _onNextAnim(wxCommandEvent& evt); - void _onLastAnim(wxCommandEvent& evt); - void _onPrevFrame(wxCommandEvent& evt); - void _onNextFrame(wxCommandEvent& evt); - void _onPlayPause(wxCommandEvent& evt); - void _onToggleMask(wxCommandEvent& evt); - void _onToggleDrawMood(wxCommandEvent& evt); - void _onToggleDrawCoordinates(wxCommandEvent& evt); - void _onSearchLayerId(wxCommandEvent& evt); - void _onSearchFrame(wxCommandEvent& evt); - void _onSearchSoundIndex(wxCommandEvent& evt); - void _onGotoSearchResult(wxCommandEvent& evt); - void _onAnimChar(wxCommandEvent& evt); - void _onGhostFileChange(wxCommandEvent& evt); - void _onGhostIndexChange(wxSpinEvent& evt); - void _onPanelPaint(wxPaintEvent& evt); - void _onPanelClick(wxMouseEvent& evt); - void _onTimer(wxTimerEvent& evt); + protected: + void _onLoad(wxCommandEvent& evt); + void _onBrowse(wxCommandEvent& evt); + void _onExport(wxCommandEvent& evt); + void _onFirstAnim(wxCommandEvent& evt); + void _onPrevAnim(wxCommandEvent& evt); + void _onNextAnim(wxCommandEvent& evt); + void _onLastAnim(wxCommandEvent& evt); + void _onPrevFrame(wxCommandEvent& evt); + void _onNextFrame(wxCommandEvent& evt); + void _onPlayPause(wxCommandEvent& evt); + void _onToggleMask(wxCommandEvent& evt); + void _onToggleDrawMood(wxCommandEvent& evt); + void _onToggleDrawCoordinates(wxCommandEvent& evt); + void _onSearchLayerId(wxCommandEvent& evt); + void _onSearchFrame(wxCommandEvent& evt); + void _onSearchSoundIndex(wxCommandEvent& evt); + void _onGotoSearchResult(wxCommandEvent& evt); + void _onAnimChar(wxCommandEvent& evt); + void _onGhostFileChange(wxCommandEvent& evt); + void _onGhostIndexChange(wxSpinEvent& evt); + void _onPanelPaint(wxPaintEvent& evt); + void _onPanelClick(wxMouseEvent& evt); + void _onTimer(wxTimerEvent& evt); - void _onAnimChange(size_t iIndex); + void _onAnimChange(size_t iIndex); - void _drawCoordinates(wxPaintDC& DC, int i, int j); - wxString _getCaseSensitivePath(const wxString& sInsensitivePathPart, const wxString& sPath); + void _drawCoordinates(wxPaintDC& DC, int i, int j); + wxString _getCaseSensitivePath(const wxString& sInsensitivePathPart, + const wxString& sPath); - THAnimations m_oAnims; - THLayerMask m_mskLayers; - wxImage m_imgBackground; - wxTimer m_tmrAnimate; - size_t m_iCurrentAnim; - size_t m_iCurrentFrame; - int m_iGhostFile; - int m_iGhostIndex; - int m_iMoodDrawX; - int m_iMoodDrawY; - bool m_bPlayingAnimation; - bool m_bDrawMood; - bool m_bDrawCoordinates; + THAnimations m_oAnims; + THLayerMask m_mskLayers; + wxImage m_imgBackground; + wxTimer m_tmrAnimate; + size_t m_iCurrentAnim; + size_t m_iCurrentFrame; + int m_iGhostFile; + int m_iGhostIndex; + int m_iMoodDrawX; + int m_iMoodDrawY; + bool m_bPlayingAnimation; + bool m_bDrawMood; + bool m_bDrawCoordinates; - wxButton* m_btnPlayPause; - wxButton* m_btnExport; - wxTextCtrl* m_txtTHPath; - wxTextCtrl* m_txtAnimIndex; - wxTextCtrl* m_txtAnimCount; - wxTextCtrl* m_txtFrameIndex; - wxTextCtrl* m_txtFrameCount; - wxTextCtrl* m_txtFrameFlags[2]; - wxTextCtrl* m_txtMoodPosition[2]; - wxCheckBox* m_chkFrameFlags[16]; - wxListBox* m_lstSearchResults; - wxPanel* m_panFrame; - DECLARE_EVENT_TABLE() + wxButton* m_btnPlayPause; + wxButton* m_btnExport; + wxTextCtrl* m_txtTHPath; + wxTextCtrl* m_txtAnimIndex; + wxTextCtrl* m_txtAnimCount; + wxTextCtrl* m_txtFrameIndex; + wxTextCtrl* m_txtFrameCount; + wxTextCtrl* m_txtFrameFlags[2]; + wxTextCtrl* m_txtMoodPosition[2]; + wxCheckBox* m_chkFrameFlags[16]; + wxListBox* m_lstSearchResults; + wxPanel* m_panFrame; + DECLARE_EVENT_TABLE() }; diff --git a/AnimView/frmSprites.cpp b/AnimView/frmSprites.cpp index 6ca8cd70..fdfbcddf 100644 --- a/AnimView/frmSprites.cpp +++ b/AnimView/frmSprites.cpp @@ -20,15 +20,15 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -#include "config.h" #include "frmSprites.h" -#include -#include -#include +#include "config.h" +#include #include #include +#include +#include +#include #include -#include BEGIN_EVENT_TABLE(frmSprites, wxFrame) EVT_BUTTON(ID_LOAD, frmSprites::_onLoad) @@ -40,171 +40,177 @@ BEGIN_EVENT_TABLE(frmSprites, wxFrame) END_EVENT_TABLE() frmSprites::frmSprites() - : wxFrame(NULL, wxID_ANY, L"Theme Hospital Sprite Viewer") -{ - wxBoxSizer *pMainSizer = new wxBoxSizer(wxVERTICAL); + : wxFrame(NULL, wxID_ANY, L"Theme Hospital Sprite Viewer") { + wxBoxSizer* pMainSizer = new wxBoxSizer(wxVERTICAL); - wxStaticBoxSizer *pFiles = new wxStaticBoxSizer(wxVERTICAL, this, L"Files"); - wxFlexGridSizer *pFilesGrid = new wxFlexGridSizer(4, 3, 2, 1); - pFilesGrid->AddGrowableCol(1, 1); - pFilesGrid->Add(new wxStaticText(this, wxID_ANY, L"Table:"), 0, wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT); - pFilesGrid->Add(m_txtTable = new wxTextCtrl(this, wxID_ANY, L"X:\\ThemeHospital\\hospital\\QData\\Font00V.tab"), 1, wxALIGN_CENTER_VERTICAL | wxEXPAND); - pFilesGrid->Add(new wxButton(this, ID_BROWSE_TABLE, L"Browse..."), 0, wxALIGN_CENTER_VERTICAL); - pFilesGrid->Add(new wxStaticText(this, wxID_ANY, L"Data:"), 0, wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT); - pFilesGrid->Add(m_txtData = new wxTextCtrl(this, wxID_ANY, L""), 1, wxALIGN_CENTER_VERTICAL | wxEXPAND); - pFilesGrid->Add(new wxButton(this, ID_BROWSE_DATA, L"Browse..."), 0, wxALIGN_CENTER_VERTICAL); - pFilesGrid->Add(new wxStaticText(this, wxID_ANY, L"Palette:"), 0, wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT); - pFilesGrid->Add(m_txtPalette = new wxTextCtrl(this, wxID_ANY, L"X:\\ThemeHospital\\hospital\\Data\\MPalette.dat"), 1, wxALIGN_CENTER_VERTICAL | wxEXPAND); - pFilesGrid->Add(new wxButton(this, ID_BROWSE_PALETTE, L"Browse..."), 0, wxALIGN_CENTER_VERTICAL); - pFiles->Add(pFilesGrid, 0, wxEXPAND | wxALL, 1); - wxButton *pTmp; - pFiles->Add(pTmp = new wxButton(this, ID_LOAD, L"Load Simple"), 0, wxALIGN_CENTER | wxALL, 1); - pFiles->Add(pTmp = new wxButton(this, ID_LOAD_COMPLEX, L"Load Complex"), 0, wxALIGN_CENTER | wxALL, 1); - pFiles->Add(pTmp = new wxButton(this, ID_NEXT, L"Next"), 0, wxALIGN_CENTER | wxALL, 1); - SetBackgroundColour(pTmp->GetBackgroundColour()); - pMainSizer->Add(pFiles, 0, wxEXPAND | wxALL, 2); + wxStaticBoxSizer* pFiles = new wxStaticBoxSizer(wxVERTICAL, this, L"Files"); + wxFlexGridSizer* pFilesGrid = new wxFlexGridSizer(4, 3, 2, 1); + pFilesGrid->AddGrowableCol(1, 1); + pFilesGrid->Add(new wxStaticText(this, wxID_ANY, L"Table:"), 0, + wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT); + pFilesGrid->Add( + m_txtTable = new wxTextCtrl( + this, wxID_ANY, L"X:\\ThemeHospital\\hospital\\QData\\Font00V.tab"), + 1, wxALIGN_CENTER_VERTICAL | wxEXPAND); + pFilesGrid->Add(new wxButton(this, ID_BROWSE_TABLE, L"Browse..."), 0, + wxALIGN_CENTER_VERTICAL); + pFilesGrid->Add(new wxStaticText(this, wxID_ANY, L"Data:"), 0, + wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT); + pFilesGrid->Add(m_txtData = new wxTextCtrl(this, wxID_ANY, L""), 1, + wxALIGN_CENTER_VERTICAL | wxEXPAND); + pFilesGrid->Add(new wxButton(this, ID_BROWSE_DATA, L"Browse..."), 0, + wxALIGN_CENTER_VERTICAL); + pFilesGrid->Add(new wxStaticText(this, wxID_ANY, L"Palette:"), 0, + wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT); + pFilesGrid->Add( + m_txtPalette = new wxTextCtrl( + this, wxID_ANY, L"X:\\ThemeHospital\\hospital\\Data\\MPalette.dat"), + 1, wxALIGN_CENTER_VERTICAL | wxEXPAND); + pFilesGrid->Add(new wxButton(this, ID_BROWSE_PALETTE, L"Browse..."), 0, + wxALIGN_CENTER_VERTICAL); + pFiles->Add(pFilesGrid, 0, wxEXPAND | wxALL, 1); + wxButton* pTmp; + pFiles->Add(pTmp = new wxButton(this, ID_LOAD, L"Load Simple"), 0, + wxALIGN_CENTER | wxALL, 1); + pFiles->Add(pTmp = new wxButton(this, ID_LOAD_COMPLEX, L"Load Complex"), 0, + wxALIGN_CENTER | wxALL, 1); + pFiles->Add(pTmp = new wxButton(this, ID_NEXT, L"Next"), 0, + wxALIGN_CENTER | wxALL, 1); + SetBackgroundColour(pTmp->GetBackgroundColour()); + pMainSizer->Add(pFiles, 0, wxEXPAND | wxALL, 2); - wxStaticBoxSizer *pSprites = new wxStaticBoxSizer(wxVERTICAL, this, L"Sprites"); - pSprites->Add(m_panFrame = new MyVScrolled(this), 1, wxEXPAND); - pMainSizer->Add(pSprites, 1, wxEXPAND | wxALL, 2); - m_panFrame->Connect(wxEVT_PAINT, (wxObjectEventFunction)&frmSprites::_onPanelPaint, NULL, this); + wxStaticBoxSizer* pSprites = + new wxStaticBoxSizer(wxVERTICAL, this, L"Sprites"); + pSprites->Add(m_panFrame = new MyVScrolled(this), 1, wxEXPAND); + pMainSizer->Add(pSprites, 1, wxEXPAND | wxALL, 2); + m_panFrame->Connect(wxEVT_PAINT, + (wxObjectEventFunction)&frmSprites::_onPanelPaint, NULL, + this); - SetSizer(pMainSizer); + SetSizer(pMainSizer); - load(true); + load(true); } -frmSprites::~frmSprites() -{ -} +frmSprites::~frmSprites() {} -void frmSprites::_onLoad(wxCommandEvent& evt) -{ - load(false); -} +void frmSprites::_onLoad(wxCommandEvent& evt) { load(false); } -void frmSprites::_onLoadComplex(wxCommandEvent& evt) -{ - load(true); -} +void frmSprites::_onLoadComplex(wxCommandEvent& evt) { load(true); } -void frmSprites::_onNext(wxCommandEvent& evt) -{ - wxString s = m_txtTable->GetValue(); - while(true) - { - const wxChar* sc = s.c_str(); - for(size_t i = s.Length(); i > 0;) - { - --i; - if('0' <= sc[i] && sc[i] <= '9') - { - s.SetChar(i, sc[i] + 1); - if(sc[i] > '9') - { - s.SetChar(i, '0'); - if(sc[i - 1] == '9') - { - s.SetChar(i - 1, '0'); - return; - } - s.SetChar(i - 1, sc[i - 1] + 1); - } - break; - } - } - if(::wxFileExists(s)) - { - m_txtTable->SetValue(s); +void frmSprites::_onNext(wxCommandEvent& evt) { + wxString s = m_txtTable->GetValue(); + while (true) { + const wxChar* sc = s.c_str(); + for (size_t i = s.Length(); i > 0;) { + --i; + if ('0' <= sc[i] && sc[i] <= '9') { + s.SetChar(i, sc[i] + 1); + if (sc[i] > '9') { + s.SetChar(i, '0'); + if (sc[i - 1] == '9') { + s.SetChar(i - 1, '0'); return; + } + s.SetChar(i - 1, sc[i - 1] + 1); } + break; + } } + if (::wxFileExists(s)) { + m_txtTable->SetValue(s); + return; + } + } } -void frmSprites::load(bool bComplex) -{ - if(!m_oAnims.loadTableFile(m_txtTable->GetValue()) - ||!m_oAnims.loadSpriteFile(m_txtData->GetValue().IsEmpty() ? m_txtTable->GetValue().BeforeLast('.')+L".DAT" : m_txtData->GetValue()) - ||!m_oAnims.loadPaletteFile(m_txtPalette->GetValue())) - { - ::wxMessageBox(L"Cannot load files"); - return; +void frmSprites::load(bool bComplex) { + if (!m_oAnims.loadTableFile(m_txtTable->GetValue()) || + !m_oAnims.loadSpriteFile(m_txtData->GetValue().IsEmpty() + ? m_txtTable->GetValue().BeforeLast('.') + + L".DAT" + : m_txtData->GetValue()) || + !m_oAnims.loadPaletteFile(m_txtPalette->GetValue())) { + ::wxMessageBox(L"Cannot load files"); + return; + } + + m_vSprites.clear(); + for (size_t i = 0; i < m_oAnims.getSpriteCount(); ++i) { + _sprite_t oSprite; + Bitmap* pSpriteBitmap = m_oAnims.getSpriteBitmap(i, bComplex); + oSprite.caption = + wxString::Format(L"#%i (%ix%i)", (int)i, pSpriteBitmap->getWidth(), + pSpriteBitmap->getHeight()); + if (pSpriteBitmap->getWidth() * pSpriteBitmap->getHeight() > 0) { + wxImage imgSprite(pSpriteBitmap->getWidth(), pSpriteBitmap->getHeight(), + false); + pSpriteBitmap->blit(imgSprite, 0, 0, NULL, m_oAnims.getPalette(), 0x8000); + oSprite.bitmap = wxBitmap(imgSprite); + } + m_vSprites.push_back(oSprite); + } + + m_panFrame->Refresh(); +} + +void frmSprites::_onPanelPaint(wxPaintEvent& evt) { + wxPaintDC dc(m_panFrame); + + int iAvailableWidth, iAvailableHeight; + m_panFrame->GetClientSize(&iAvailableWidth, &iAvailableHeight); + int iX = 0; + int iTallest = 0; + int iTotal = 0; + int iY = -m_panFrame->GetVisibleRowsBegin(); + + for (std::vector<_sprite_t>::iterator itr = m_vSprites.begin(), + itrEnd = m_vSprites.end(); + itr != itrEnd; ++itr) { + wxSize szLabel = dc.GetTextExtent(itr->caption); + int iWidth = wxMax(szLabel.GetWidth(), + itr->bitmap.IsOk() ? itr->bitmap.GetWidth() : 0); + int iHeight = (itr->bitmap.IsOk() ? itr->bitmap.GetHeight() : 0) + + szLabel.GetHeight() + 2; + if (iWidth + iX > iAvailableWidth) { + iY += iTallest; + iTotal += iTallest; + iX = iTallest = 0; } - m_vSprites.clear(); - for(size_t i = 0; i < m_oAnims.getSpriteCount(); ++i) - { - _sprite_t oSprite; - Bitmap* pSpriteBitmap = m_oAnims.getSpriteBitmap(i, bComplex); - oSprite.caption = wxString::Format(L"#%i (%ix%i)", (int)i, pSpriteBitmap->getWidth(), pSpriteBitmap->getHeight()); - if(pSpriteBitmap->getWidth() * pSpriteBitmap->getHeight() > 0) - { - wxImage imgSprite(pSpriteBitmap->getWidth(), pSpriteBitmap->getHeight(), false); - pSpriteBitmap->blit(imgSprite, 0, 0, NULL, m_oAnims.getPalette(), 0x8000); - oSprite.bitmap = wxBitmap(imgSprite); - } - m_vSprites.push_back(oSprite); + if (iY + iHeight >= 0 && iY < iAvailableHeight) { + dc.DrawText(itr->caption, iX, iY); + if (itr->bitmap.IsOk()) + dc.DrawBitmap(itr->bitmap, iX, iY + szLabel.GetHeight() + 1); } - m_panFrame->Refresh(); + iTallest = wxMax(iTallest, iHeight); + iX += iWidth + 2; + } + + iTotal += iTallest; // Add last row too. + + // Update the row count if it doesn't match. + if (iTotal != m_panFrame->iMyCount) { + m_panFrame->iMyCount = iTotal; + m_panFrame->SetRowCount(iTotal); + } } -void frmSprites::_onPanelPaint(wxPaintEvent& evt) -{ - wxPaintDC dc(m_panFrame); - - int iAvailableWidth, iAvailableHeight; - m_panFrame->GetClientSize(&iAvailableWidth, &iAvailableHeight); - int iX = 0; - int iTallest = 0; - int iTotal = 0; - int iY = -m_panFrame->GetVisibleRowsBegin(); - - for(std::vector<_sprite_t>::iterator itr = m_vSprites.begin(), itrEnd = m_vSprites.end(); - itr != itrEnd; ++itr) - { - wxSize szLabel = dc.GetTextExtent(itr->caption); - int iWidth = wxMax(szLabel.GetWidth(), itr->bitmap.IsOk() ? itr->bitmap.GetWidth() : 0); - int iHeight = (itr->bitmap.IsOk() ? itr->bitmap.GetHeight() : 0) + szLabel.GetHeight() + 2; - if(iWidth + iX > iAvailableWidth) - { - iY += iTallest; - iTotal += iTallest; - iX = iTallest = 0; - } - - if (iY + iHeight >= 0 && iY < iAvailableHeight) { - dc.DrawText(itr->caption, iX, iY); - if(itr->bitmap.IsOk()) - dc.DrawBitmap(itr->bitmap, iX, iY + szLabel.GetHeight() + 1); - } - - iTallest = wxMax(iTallest, iHeight); - iX += iWidth + 2; - } - - iTotal += iTallest; // Add last row too. - - // Update the row count if it doesn't match. - if (iTotal != m_panFrame->iMyCount) { - m_panFrame->iMyCount = iTotal; - m_panFrame->SetRowCount(iTotal); - } +void frmSprites::_onBrowseTable(wxCommandEvent& WXUNUSED(evt)) { + m_txtTable->SetValue(::wxFileSelector( + L"Select location of Font00V.tab (DATA)", m_txtTable->GetValue(), + L"Font00V.tab", L"tab", L"Tab files (*.tab)|*.[tT][aA][bB]", 0, this)); } - -void frmSprites::_onBrowseTable(wxCommandEvent& WXUNUSED(evt)) -{ - m_txtTable->SetValue(::wxFileSelector(L"Select location of Font00V.tab (DATA)", - m_txtTable->GetValue(),L"Font00V.tab",L"tab",L"Tab files (*.tab)|*.[tT][aA][bB]" - ,0, this)); +void frmSprites::_onBrowseData(wxCommandEvent& WXUNUSED(evt)) { + m_txtData->SetValue(::wxFileSelector( + L"Choose Theme Hospital data file", m_txtData->GetValue(), L"", L"dat", + L"Dat files (*.dat)|*.[dD][aA][tT]", 0, this)); } -void frmSprites::_onBrowseData(wxCommandEvent& WXUNUSED(evt)) -{ - m_txtData->SetValue(::wxFileSelector(L"Choose Theme Hospital data file", - m_txtData->GetValue(),L"",L"dat",L"Dat files (*.dat)|*.[dD][aA][tT]", 0, this)); -} -void frmSprites::_onBrowsePalette(wxCommandEvent& WXUNUSED(evt)) -{ - m_txtPalette->SetValue(::wxFileSelector(L"Select location of MPalette.dat (QDATA)", - m_txtPalette->GetValue(),L"MPalette.dat",L"dat",L"Dat or Pal files (*.dat, *.pal)|*.[dD][aA][tT];*.[pP][aA][lL]", 0, this)); +void frmSprites::_onBrowsePalette(wxCommandEvent& WXUNUSED(evt)) { + m_txtPalette->SetValue(::wxFileSelector( + L"Select location of MPalette.dat (QDATA)", m_txtPalette->GetValue(), + L"MPalette.dat", L"dat", + L"Dat or Pal files (*.dat, *.pal)|*.[dD][aA][tT];*.[pP][aA][lL]", 0, + this)); } diff --git a/AnimView/frmSprites.h b/AnimView/frmSprites.h index 9c66548a..383590dc 100644 --- a/AnimView/frmSprites.h +++ b/AnimView/frmSprites.h @@ -24,69 +24,69 @@ SOFTWARE. #include "config.h" #include -#include #include #include -#include -#include -#include +#include #include +#include +#include +#include #include -#include "th.h" #include +#include "th.h" static const int ROW_COUNT = 1000; // Derived class to add scrollbars to the window. class MyVScrolled : public wxVScrolledWindow { -public: - MyVScrolled(wxWindow *parent) : wxVScrolledWindow(parent, wxID_ANY) { iMyCount = ROW_COUNT; } + public: + MyVScrolled(wxWindow* parent) : wxVScrolledWindow(parent, wxID_ANY) { + iMyCount = ROW_COUNT; + } - wxCoord OnGetRowHeight(size_t row) const { return 1; } + wxCoord OnGetRowHeight(size_t row) const { return 1; } - wxCoord EstimateTotalHeight() const { return iMyCount; } + wxCoord EstimateTotalHeight() const { return iMyCount; } - int iMyCount; + int iMyCount; }; -class frmSprites : public wxFrame -{ -public: - frmSprites(); - ~frmSprites(); +class frmSprites : public wxFrame { + public: + frmSprites(); + ~frmSprites(); - enum - { - ID_LOAD = wxID_HIGHEST + 1, - ID_BROWSE_TABLE, - ID_BROWSE_DATA, - ID_BROWSE_PALETTE, - ID_LOAD_COMPLEX, - ID_NEXT, - }; + enum { + ID_LOAD = wxID_HIGHEST + 1, + ID_BROWSE_TABLE, + ID_BROWSE_DATA, + ID_BROWSE_PALETTE, + ID_LOAD_COMPLEX, + ID_NEXT, + }; - void load(bool bComplex); -protected: - struct _sprite_t - { - wxBitmap bitmap; - wxString caption; - }; + void load(bool bComplex); - void _onNext(wxCommandEvent& evt); - void _onLoad(wxCommandEvent& evt); - void _onLoadComplex(wxCommandEvent& evt); - void _onPanelPaint(wxPaintEvent& evt); - void _onBrowseData(wxCommandEvent& evt); - void _onBrowsePalette(wxCommandEvent& evt); - void _onBrowseTable(wxCommandEvent& evt); + protected: + struct _sprite_t { + wxBitmap bitmap; + wxString caption; + }; - std::vector<_sprite_t> m_vSprites; - THAnimations m_oAnims; + void _onNext(wxCommandEvent& evt); + void _onLoad(wxCommandEvent& evt); + void _onLoadComplex(wxCommandEvent& evt); + void _onPanelPaint(wxPaintEvent& evt); + void _onBrowseData(wxCommandEvent& evt); + void _onBrowsePalette(wxCommandEvent& evt); + void _onBrowseTable(wxCommandEvent& evt); - wxTextCtrl* m_txtTable; - wxTextCtrl* m_txtData; - wxTextCtrl* m_txtPalette; - MyVScrolled* m_panFrame; - DECLARE_EVENT_TABLE() + std::vector<_sprite_t> m_vSprites; + THAnimations m_oAnims; + + wxTextCtrl* m_txtTable; + wxTextCtrl* m_txtData; + wxTextCtrl* m_txtPalette; + MyVScrolled* m_panFrame; + DECLARE_EVENT_TABLE() }; diff --git a/AnimView/th.cpp b/AnimView/th.cpp index 9212a959..b6e6943f 100644 --- a/AnimView/th.cpp +++ b/AnimView/th.cpp @@ -20,617 +20,484 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -#include "config.h" #include "th.h" -#include "../common/rnc.h" +#include "config.h" #include -#include #include +#include #include #include #include #include #include +#include "../common/rnc.h" static const unsigned char palette_upscale_map[0x40] = { - 0x00, 0x04, 0x08, 0x0C, 0x10, 0x14, 0x18, 0x1C, - 0x20, 0x24, 0x28, 0x2D, 0x31, 0x35, 0x39, 0x3D, - 0x41, 0x45, 0x49, 0x4D, 0x51, 0x55, 0x59, 0x5D, - 0x61, 0x65, 0x69, 0x6D, 0x71, 0x75, 0x79, 0x7D, - 0x82, 0x86, 0x8A, 0x8E, 0x92, 0x96, 0x9A, 0x9E, - 0xA2, 0xA6, 0xAA, 0xAE, 0xB2, 0xB6, 0xBA, 0xBE, - 0xC2, 0xC6, 0xCA, 0xCE, 0xD2, 0xD7, 0xDB, 0xDF, - 0xE3, 0xE7, 0xEB, 0xEF, 0xF3, 0xF7, 0xFB, 0xFF, + 0x00, 0x04, 0x08, 0x0C, 0x10, 0x14, 0x18, 0x1C, 0x20, 0x24, 0x28, + 0x2D, 0x31, 0x35, 0x39, 0x3D, 0x41, 0x45, 0x49, 0x4D, 0x51, 0x55, + 0x59, 0x5D, 0x61, 0x65, 0x69, 0x6D, 0x71, 0x75, 0x79, 0x7D, 0x82, + 0x86, 0x8A, 0x8E, 0x92, 0x96, 0x9A, 0x9E, 0xA2, 0xA6, 0xAA, 0xAE, + 0xB2, 0xB6, 0xBA, 0xBE, 0xC2, 0xC6, 0xCA, 0xCE, 0xD2, 0xD7, 0xDB, + 0xDF, 0xE3, 0xE7, 0xEB, 0xEF, 0xF3, 0xF7, 0xFB, 0xFF, }; -class ChunkRenderer -{ -public: - ChunkRenderer(int width, int height, unsigned char *buffer = NULL) - { - m_data = buffer ? buffer : new unsigned char[width * height]; - m_ptr = m_data; - m_end = m_data + width * height; - m_x = 0; - m_y = 0; - m_width = width; - m_height = height; - m_skip_eol = false; - } +class ChunkRenderer { + public: + ChunkRenderer(int width, int height, unsigned char* buffer = NULL) { + m_data = buffer ? buffer : new unsigned char[width * height]; + m_ptr = m_data; + m_end = m_data + width * height; + m_x = 0; + m_y = 0; + m_width = width; + m_height = height; + m_skip_eol = false; + } - ~ChunkRenderer() - { - delete[] m_data; - } + ~ChunkRenderer() { delete[] m_data; } - bool isDone() const - { - return m_ptr == m_end; - } + bool isDone() const { return m_ptr == m_end; } - unsigned char* takeData() - { - unsigned char *buffer = m_data; - m_data = 0; - return buffer; - } + unsigned char* takeData() { + unsigned char* buffer = m_data; + m_data = 0; + return buffer; + } - const unsigned char* getData() const - { - return m_data; - } + const unsigned char* getData() const { return m_data; } - void chunkFillToEndOfLine(unsigned char value) - { - if(m_x != 0 || !m_skip_eol) - { - chunkFill(m_width - m_x, value); - } - m_skip_eol = false; + void chunkFillToEndOfLine(unsigned char value) { + if (m_x != 0 || !m_skip_eol) { + chunkFill(m_width - m_x, value); } + m_skip_eol = false; + } - void chunkFinish(unsigned char value) - { - chunkFill(m_end - m_ptr, value); + void chunkFinish(unsigned char value) { chunkFill(m_end - m_ptr, value); } + + void chunkFill(int npixels, unsigned char value) { + _fixNpixels(npixels); + if (npixels > 0) { + memset(m_ptr, value, npixels); + _incrementPosition(npixels); } + } - void chunkFill(int npixels, unsigned char value) - { - _fixNpixels(npixels); - if(npixels > 0) - { - memset(m_ptr, value, npixels); - _incrementPosition(npixels); - } + void chunkCopy(int npixels, const unsigned char* data) { + _fixNpixels(npixels); + if (npixels > 0) { + memcpy(m_ptr, data, npixels); + _incrementPosition(npixels); } + } - void chunkCopy(int npixels, const unsigned char* data) - { - _fixNpixels(npixels); - if(npixels > 0) - { - memcpy(m_ptr, data, npixels); - _incrementPosition(npixels); - } + protected: + inline void _fixNpixels(int& npixels) const { + if (m_ptr + npixels > m_end) { + npixels = m_end - m_ptr; } + } -protected: - inline void _fixNpixels(int& npixels) const - { - if(m_ptr + npixels > m_end) - { - npixels = m_end - m_ptr; - } - } + inline void _incrementPosition(int npixels) { + m_ptr += npixels; + m_x += npixels; + m_y += m_x / m_width; + m_x = m_x % m_width; + m_skip_eol = true; + } - inline void _incrementPosition(int npixels) - { - m_ptr += npixels; - m_x += npixels; - m_y += m_x / m_width; - m_x = m_x % m_width; - m_skip_eol = true; - } - - unsigned char *m_data, *m_ptr, *m_end; - int m_x, m_y, m_width, m_height; - bool m_skip_eol; + unsigned char *m_data, *m_ptr, *m_end; + int m_x, m_y, m_width, m_height; + bool m_skip_eol; }; -static void decode_chunks(ChunkRenderer& renderer, const unsigned char* data, int datalen, unsigned char transparent) -{ - while(!renderer.isDone() && datalen > 0) - { - unsigned char b = *data; - --datalen; - ++data; - if(b == 0) - { - renderer.chunkFillToEndOfLine(transparent); - } - else if(b < 0x80) - { - int amt = b; - if(datalen < amt) - amt = datalen; - renderer.chunkCopy(amt, data); - data += amt; - datalen -= amt; - } - else - { - renderer.chunkFill(0x100 - b, transparent); - } +static void decode_chunks(ChunkRenderer& renderer, const unsigned char* data, + int datalen, unsigned char transparent) { + while (!renderer.isDone() && datalen > 0) { + unsigned char b = *data; + --datalen; + ++data; + if (b == 0) { + renderer.chunkFillToEndOfLine(transparent); + } else if (b < 0x80) { + int amt = b; + if (datalen < amt) amt = datalen; + renderer.chunkCopy(amt, data); + data += amt; + datalen -= amt; + } else { + renderer.chunkFill(0x100 - b, transparent); } - renderer.chunkFinish(transparent); + } + renderer.chunkFinish(transparent); } -static void decode_chunks_complex(ChunkRenderer& renderer, const unsigned char* data, int datalen, unsigned char transparent) -{ - while(!renderer.isDone() && datalen > 0) - { - unsigned char b = *data; - --datalen; - ++data; - if(b == 0) - { - renderer.chunkFillToEndOfLine(transparent); +static void decode_chunks_complex(ChunkRenderer& renderer, + const unsigned char* data, int datalen, + unsigned char transparent) { + while (!renderer.isDone() && datalen > 0) { + unsigned char b = *data; + --datalen; + ++data; + if (b == 0) { + renderer.chunkFillToEndOfLine(transparent); + } else if (b < 0x40) { + int amt = b; + if (datalen < amt) amt = datalen; + renderer.chunkCopy(amt, data); + data += amt; + datalen -= amt; + } else if ((b & 0xC0) == 0x80) { + renderer.chunkFill(b - 0x80, transparent); + } else { + int amt; + unsigned char colour = 0; + if (b == 0xFF) { + if (datalen < 2) { + break; } - else if(b < 0x40) - { - int amt = b; - if(datalen < amt) - amt = datalen; - renderer.chunkCopy(amt, data); - data += amt; - datalen -= amt; - } - else if((b & 0xC0) == 0x80) - { - renderer.chunkFill(b - 0x80, transparent); - } - else - { - int amt; - unsigned char colour = 0; - if(b == 0xFF) - { - if(datalen < 2) - { - break; - } - amt = (int)data[0]; - colour = data[1]; - data += 2; - datalen -= 2; - } - else - { - amt = b - 60 - (b & 0x80) / 2; - if(datalen > 0) - { - colour = *data; - ++data; - --datalen; - } - } - renderer.chunkFill(amt, colour); + amt = (int)data[0]; + colour = data[1]; + data += 2; + datalen -= 2; + } else { + amt = b - 60 - (b & 0x80) / 2; + if (datalen > 0) { + colour = *data; + ++data; + --datalen; } + } + renderer.chunkFill(amt, colour); } - renderer.chunkFinish(transparent); + } + renderer.chunkFinish(transparent); } -THLayerMask::THLayerMask() -{ - clear(); +THLayerMask::THLayerMask() { clear(); } + +void THLayerMask::clear() { + for (int i = 0; i < 13; ++i) m_iMask[i] = 0; } -void THLayerMask::clear() -{ - for(int i = 0; i < 13; ++i) - m_iMask[i] = 0; -} - -THAnimations::THAnimations() -{ - anims = std::vector(); - frames = std::vector(); - elementList = std::vector(); - elements = std::vector(); - sprites = std::vector(); - spriteBitmaps = std::vector(); - chunks = std::vector(); - colours = std::vector(); - ghostMaps = std::array(); - for(int iMap = 0; iMap < 256 * 4; ++iMap) - { - for(int iCol = 0; iCol < 256; ++iCol) - { - ghostMaps[iMap * 256 + iCol] = iCol; - } +THAnimations::THAnimations() { + anims = std::vector(); + frames = std::vector(); + elementList = std::vector(); + elements = std::vector(); + sprites = std::vector(); + spriteBitmaps = std::vector(); + chunks = std::vector(); + colours = std::vector(); + ghostMaps = std::array(); + for (int iMap = 0; iMap < 256 * 4; ++iMap) { + for (int iCol = 0; iCol < 256; ++iCol) { + ghostMaps[iMap * 256 + iCol] = iCol; } - m_iGhostMapOffset = 0; + } + m_iGhostMapOffset = 0; } -THAnimations::~THAnimations() -{ -} - -bool THAnimations::isAnimationDuplicate(size_t iAnimation) -{ - if(iAnimation < anims.size()) - return anims.at(iAnimation).unknown == 1; - else - return true; -} - -size_t THAnimations::markDuplicates() -{ - size_t iNonDuplicateCount = 0; - - std::set seen; - for(th_anim_t& anim : anims) - { - uint16_t iFrame = anim.frame; - uint16_t iFirstFrame = iFrame; - do - { - if(seen.find(iFrame) != seen.end()) { - anim.unknown = 1; - } else { - seen.insert(iFrame); - } - iFrame = frames.at(iFrame).next; - } while(iFrame != iFirstFrame); - - if(anim.unknown == 0) - { - ++iNonDuplicateCount; - } - } - - return iNonDuplicateCount; -} - -bool THAnimations::loadFrameFile(wxString sFilename) -{ - if(!loadVector(frames, sFilename)) - return false; - - /* - 256 is a common flag - could be x-flip. - The lower byte can also take non-zero values - could be ghost palette - indices. - */ +THAnimations::~THAnimations() {} +bool THAnimations::isAnimationDuplicate(size_t iAnimation) { + if (iAnimation < anims.size()) + return anims.at(iAnimation).unknown == 1; + else return true; } -bool THAnimations::loadTableFile(wxString sFilename) -{ - spriteBitmaps.clear(); - if(!loadVector(sprites, sFilename)) - return false; - spriteBitmaps.resize(sprites.size()); - return true; -} +size_t THAnimations::markDuplicates() { + size_t iNonDuplicateCount = 0; -bool THAnimations::loadPaletteFile(wxString sFilename) -{ - if (!loadVector(colours, sFilename)) - return false; - for (th_colour_t& colour : colours) - { - colour.r = palette_upscale_map[colour.r & 0x3F]; - colour.g = palette_upscale_map[colour.g & 0x3F]; - colour.b = palette_upscale_map[colour.b & 0x3F]; + std::set seen; + for (th_anim_t& anim : anims) { + uint16_t iFrame = anim.frame; + uint16_t iFirstFrame = iFrame; + do { + if (seen.find(iFrame) != seen.end()) { + anim.unknown = 1; + } else { + seen.insert(iFrame); + } + iFrame = frames.at(iFrame).next; + } while (iFrame != iFirstFrame); + + if (anim.unknown == 0) { + ++iNonDuplicateCount; } - return true; + } + + return iNonDuplicateCount; } -bool THAnimations::loadGhostFile(wxString sFilename, int iIndex) -{ - if(iIndex < 0 || iIndex >= 4) - return false; +bool THAnimations::loadFrameFile(wxString sFilename) { + if (!loadVector(frames, sFilename)) return false; - std::vector data; + /* + 256 is a common flag - could be x-flip. + The lower byte can also take non-zero values - could be ghost palette + indices. + */ - if (!loadVector(data, sFilename)) - return false; - - if (data.size() != 256 * 256) { - return false; - } - - std::copy(data.begin(), data.end(), ghostMaps.begin() + iIndex * 256 * 256); - return true; + return true; } -void THAnimations::setGhost(int iFile, int iIndex) -{ - m_iGhostMapOffset = iFile * 256 * 256 + iIndex * 256; +bool THAnimations::loadTableFile(wxString sFilename) { + spriteBitmaps.clear(); + if (!loadVector(sprites, sFilename)) return false; + spriteBitmaps.resize(sprites.size()); + return true; } -size_t THAnimations::getAnimationCount() -{ - return anims.size(); +bool THAnimations::loadPaletteFile(wxString sFilename) { + if (!loadVector(colours, sFilename)) return false; + for (th_colour_t& colour : colours) { + colour.r = palette_upscale_map[colour.r & 0x3F]; + colour.g = palette_upscale_map[colour.g & 0x3F]; + colour.b = palette_upscale_map[colour.b & 0x3F]; + } + return true; } -size_t THAnimations::getSpriteCount() -{ - return sprites.size(); -} +bool THAnimations::loadGhostFile(wxString sFilename, int iIndex) { + if (iIndex < 0 || iIndex >= 4) return false; -void THAnimations::setSpritePath(wxString aPath) -{ - m_sSpritePath = aPath; -} + std::vector data; -void THAnimations::getAnimationMask(size_t iAnimation, THLayerMask& mskLayers) -{ - mskLayers.clear(); - if(iAnimation >= anims.size()) - return; + if (!loadVector(data, sFilename)) return false; - uint16_t iFrameIndex = anims.at(iAnimation).frame; - if(iFrameIndex >= frames.size()) - return; - uint16_t iFirstFrameIndex = iFrameIndex; - - do - { - th_frame_t* pFrame = &(frames.at(iFrameIndex)); - uint32_t iListIndex = pFrame->list_index; - th_element_t* pElement; - while((pElement = _getElement(iListIndex++))) - { - mskLayers.set(pElement->flags >> 4, pElement->layerid); - } - iFrameIndex = frames.at(iFrameIndex).next; - } while(iFrameIndex < frames.size() && iFrameIndex != iFirstFrameIndex); -} - -size_t THAnimations::getFrameCount(size_t iAnimation) -{ - if(iAnimation >= anims.size()) - return 0; - size_t iCount = 0; - uint16_t iFirstFrame = anims.at(iAnimation).frame; - if(iFirstFrame < frames.size()) - { - ++iCount; - uint16_t iFrame = frames.at(iFirstFrame).next; - while(iFrame != iFirstFrame && iFrame < frames.size() && iCount < 1024) - { - ++iCount; - iFrame = frames.at(iFrame).next; - } - } - return iCount; -} - -bool THAnimations::doesAnimationIncludeFrame(size_t iAnimation, size_t iFrame) -{ - if(iAnimation >= anims.size() || iFrame >= frames.size()) - return 0; - uint16_t iFirstFrame = anims.at(iAnimation).frame; - uint16_t iFrameNow = iFirstFrame; - do - { - if(iFrameNow >= frames.size()) - break; - if(iFrame == iFrameNow) - return true; - iFrameNow = frames.at(iFrameNow).next; - } while(iFrameNow != iFirstFrame); + if (data.size() != 256 * 256) { return false; + } + + std::copy(data.begin(), data.end(), ghostMaps.begin() + iIndex * 256 * 256); + return true; } -Bitmap* THAnimations::getSpriteBitmap(size_t iSprite, bool bComplex) -{ - if(iSprite >= sprites.size()) - return nullptr; - - if (!spriteBitmaps.at(iSprite).IsOk()) - { - wxString spriteFile = m_sSpritePath + wxString::Format(L"a%04ue.png", (int)iSprite); - th_sprite_t* pSprite = &(sprites.at(iSprite)); - - ChunkRenderer oRenderer(pSprite->width, pSprite->height); - (bComplex ? decode_chunks_complex : decode_chunks)(oRenderer, (const unsigned char*)chunks.data() + pSprite->offset, chunks.size() - pSprite->offset, 0xFF); - spriteBitmaps[iSprite].create(pSprite->width, pSprite->height, oRenderer.getData()); - } - - return &(spriteBitmaps.at(iSprite)); +void THAnimations::setGhost(int iFile, int iIndex) { + m_iGhostMapOffset = iFile * 256 * 256 + iIndex * 256; } -th_frame_t* THAnimations::getFrameStruct(size_t iAnimation, size_t iFrame) -{ - if(iAnimation >= anims.size()) - return 0; - uint16_t iFrameIndex = anims.at(iAnimation).frame; - while(iFrame--) - { - iFrameIndex = frames.at(iFrameIndex).next; - } - return &(frames.at(iFrameIndex)); -} +size_t THAnimations::getAnimationCount() { return anims.size(); } -void THAnimations::drawFrame(wxImage& imgCanvas, size_t iAnimation, size_t iFrame, const THLayerMask* pMask, wxSize& size, int iXOffset, int iYOffset) -{ - if(iAnimation >= anims.size()) - return; - uint16_t iFrameIndex = anims.at(iAnimation).frame; - while(iFrame--) - { - iFrameIndex = frames.at(iFrameIndex).next; - } +size_t THAnimations::getSpriteCount() { return sprites.size(); } +void THAnimations::setSpritePath(wxString aPath) { m_sSpritePath = aPath; } + +void THAnimations::getAnimationMask(size_t iAnimation, THLayerMask& mskLayers) { + mskLayers.clear(); + if (iAnimation >= anims.size()) return; + + uint16_t iFrameIndex = anims.at(iAnimation).frame; + if (iFrameIndex >= frames.size()) return; + uint16_t iFirstFrameIndex = iFrameIndex; + + do { th_frame_t* pFrame = &(frames.at(iFrameIndex)); - th_element_t* pElement; uint32_t iListIndex = pFrame->list_index; - int iFarX = 0; - int iFarY = 0; - while((pElement = _getElement(iListIndex++))) - { - if(pMask != NULL && !pMask->isSet(pElement->flags >> 4, pElement->layerid)) - continue; - uint16_t iSpriteIndex = pElement->table_position / sizeof(th_sprite_t); - th_sprite_t* pSprite = &(sprites.at(iSpriteIndex)); - int iRight = pElement->offx + pSprite->width; - int iBottom = pElement->offy + pSprite->height; - if(iRight > iFarX) - iFarX = iRight; - if(iBottom > iFarY) - iFarY = iBottom; - - getSpriteBitmap(iSpriteIndex)->blit(imgCanvas, pElement->offx + iXOffset, pElement->offy + iYOffset, ghostMaps.data() + m_iGhostMapOffset, colours.data(), pElement->flags & 0xF); + th_element_t* pElement; + while ((pElement = _getElement(iListIndex++))) { + mskLayers.set(pElement->flags >> 4, pElement->layerid); } - size.x = iFarX; - size.y = iFarY; + iFrameIndex = frames.at(iFrameIndex).next; + } while (iFrameIndex < frames.size() && iFrameIndex != iFirstFrameIndex); } -th_element_t* THAnimations::_getElement(uint32_t iListIndex) -{ - if(iListIndex >= elementList.size()) - return nullptr; - uint16_t iElementIndex = elementList.at(iListIndex); - if(iElementIndex >= elements.size()) - return nullptr; - return &(elements.at(iElementIndex)); -} - -unsigned char* THAnimations::Decompress(unsigned char* pData, size_t& iLength) -{ - if (rnc_input_size(pData) != iLength) { - throw std::length_error("rnc data does not match the expected length"); +size_t THAnimations::getFrameCount(size_t iAnimation) { + if (iAnimation >= anims.size()) return 0; + size_t iCount = 0; + uint16_t iFirstFrame = anims.at(iAnimation).frame; + if (iFirstFrame < frames.size()) { + ++iCount; + uint16_t iFrame = frames.at(iFirstFrame).next; + while (iFrame != iFirstFrame && iFrame < frames.size() && iCount < 1024) { + ++iCount; + iFrame = frames.at(iFrame).next; } + } + return iCount; +} - unsigned long outlen = rnc_output_size(pData); - unsigned char* outbuf = new unsigned char[outlen]; +bool THAnimations::doesAnimationIncludeFrame(size_t iAnimation, size_t iFrame) { + if (iAnimation >= anims.size() || iFrame >= frames.size()) return 0; + uint16_t iFirstFrame = anims.at(iAnimation).frame; + uint16_t iFrameNow = iFirstFrame; + do { + if (iFrameNow >= frames.size()) break; + if (iFrame == iFrameNow) return true; + iFrameNow = frames.at(iFrameNow).next; + } while (iFrameNow != iFirstFrame); + return false; +} - if(rnc_unpack(pData, outbuf) == rnc_status::ok) - { - delete[] pData; - iLength = outlen; - return outbuf; - } - else - { - delete[] pData; - delete[] outbuf; - iLength = 0; - return nullptr; +Bitmap* THAnimations::getSpriteBitmap(size_t iSprite, bool bComplex) { + if (iSprite >= sprites.size()) return nullptr; + + if (!spriteBitmaps.at(iSprite).IsOk()) { + wxString spriteFile = + m_sSpritePath + wxString::Format(L"a%04ue.png", (int)iSprite); + th_sprite_t* pSprite = &(sprites.at(iSprite)); + + ChunkRenderer oRenderer(pSprite->width, pSprite->height); + (bComplex ? decode_chunks_complex : decode_chunks)( + oRenderer, (const unsigned char*)chunks.data() + pSprite->offset, + chunks.size() - pSprite->offset, 0xFF); + spriteBitmaps[iSprite].create(pSprite->width, pSprite->height, + oRenderer.getData()); + } + + return &(spriteBitmaps.at(iSprite)); +} + +th_frame_t* THAnimations::getFrameStruct(size_t iAnimation, size_t iFrame) { + if (iAnimation >= anims.size()) return 0; + uint16_t iFrameIndex = anims.at(iAnimation).frame; + while (iFrame--) { + iFrameIndex = frames.at(iFrameIndex).next; + } + return &(frames.at(iFrameIndex)); +} + +void THAnimations::drawFrame(wxImage& imgCanvas, size_t iAnimation, + size_t iFrame, const THLayerMask* pMask, + wxSize& size, int iXOffset, int iYOffset) { + if (iAnimation >= anims.size()) return; + uint16_t iFrameIndex = anims.at(iAnimation).frame; + while (iFrame--) { + iFrameIndex = frames.at(iFrameIndex).next; + } + + th_frame_t* pFrame = &(frames.at(iFrameIndex)); + th_element_t* pElement; + uint32_t iListIndex = pFrame->list_index; + int iFarX = 0; + int iFarY = 0; + while ((pElement = _getElement(iListIndex++))) { + if (pMask != NULL && !pMask->isSet(pElement->flags >> 4, pElement->layerid)) + continue; + uint16_t iSpriteIndex = pElement->table_position / sizeof(th_sprite_t); + th_sprite_t* pSprite = &(sprites.at(iSpriteIndex)); + int iRight = pElement->offx + pSprite->width; + int iBottom = pElement->offy + pSprite->height; + if (iRight > iFarX) iFarX = iRight; + if (iBottom > iFarY) iFarY = iBottom; + + getSpriteBitmap(iSpriteIndex) + ->blit(imgCanvas, pElement->offx + iXOffset, pElement->offy + iYOffset, + ghostMaps.data() + m_iGhostMapOffset, colours.data(), + pElement->flags & 0xF); + } + size.x = iFarX; + size.y = iFarY; +} + +th_element_t* THAnimations::_getElement(uint32_t iListIndex) { + if (iListIndex >= elementList.size()) return nullptr; + uint16_t iElementIndex = elementList.at(iListIndex); + if (iElementIndex >= elements.size()) return nullptr; + return &(elements.at(iElementIndex)); +} + +unsigned char* THAnimations::Decompress(unsigned char* pData, size_t& iLength) { + if (rnc_input_size(pData) != iLength) { + throw std::length_error("rnc data does not match the expected length"); + } + + unsigned long outlen = rnc_output_size(pData); + unsigned char* outbuf = new unsigned char[outlen]; + + if (rnc_unpack(pData, outbuf) == rnc_status::ok) { + delete[] pData; + iLength = outlen; + return outbuf; + } else { + delete[] pData; + delete[] outbuf; + iLength = 0; + return nullptr; + } +} + +Bitmap::Bitmap() : m_iWidth(0), m_iHeight(0), m_pData(nullptr) {} + +Bitmap::~Bitmap() { delete[] m_pData; } + +void Bitmap::create(int iWidth, int iHeight) { + delete[] m_pData; + m_pData = new uint8_t[iWidth * iHeight]; + m_iWidth = iWidth; + m_iHeight = iHeight; + memset(m_pData, 0xFF, iWidth * iHeight); +} + +void Bitmap::create(int iWidth, int iHeight, const uint8_t* pData) { + delete[] m_pData; + m_pData = new uint8_t[iWidth * iHeight]; + m_iWidth = iWidth; + m_iHeight = iHeight; + memcpy(m_pData, pData, iWidth * iHeight); +} + +void Bitmap::blit(Bitmap& bmpCanvas, int iX, int iY, int iFlags) const { + for (int y = 0; y < m_iHeight; ++y) { + for (int x = 0; x < m_iWidth; ++x) { + uint8_t src = pixel(x, y); + if (src == 0xFF) continue; + int iDstX = iX + x; + int iDstY = iY + y; + if (iFlags & 0x2) iDstY = iY + m_iHeight - 1 - y; + if (iFlags & 0x1) iDstX = iX + m_iWidth - 1 - x; + bmpCanvas.pixel(iDstX, iDstY) = src; } + } } -Bitmap::Bitmap() : - m_iWidth(0), - m_iHeight(0), - m_pData(nullptr) -{ +static inline void _merge(th_colour_t& dst, const th_colour_t& src) { + dst.r = (uint8_t)(((unsigned int)dst.r + (unsigned int)src.r) / 2); + dst.g = (uint8_t)(((unsigned int)dst.g + (unsigned int)src.g) / 2); + dst.b = (uint8_t)(((unsigned int)dst.b + (unsigned int)src.b) / 2); } -Bitmap::~Bitmap() -{ - delete[] m_pData; -} +void Bitmap::blit(wxImage& imgCanvas, int iX, int iY, + const unsigned char* pColourTranslate, + const th_colour_t* pPalette, int iFlags) const { + if (m_iHeight == 0 || m_iWidth == 0) return; -void Bitmap::create(int iWidth, int iHeight) -{ - delete[] m_pData; - m_pData = new uint8_t[iWidth * iHeight]; - m_iWidth = iWidth; - m_iHeight = iHeight; - memset(m_pData, 0xFF, iWidth * iHeight); -} - -void Bitmap::create(int iWidth, int iHeight, const uint8_t* pData) -{ - delete[] m_pData; - m_pData = new uint8_t[iWidth * iHeight]; - m_iWidth = iWidth; - m_iHeight = iHeight; - memcpy(m_pData, pData, iWidth * iHeight); -} - -void Bitmap::blit(Bitmap& bmpCanvas, int iX, int iY, int iFlags) const -{ - for(int y = 0; y < m_iHeight; ++y) - { - for(int x = 0; x < m_iWidth; ++x) - { - uint8_t src = pixel(x, y); - if(src == 0xFF) - continue; - int iDstX = iX + x; - int iDstY = iY + y; - if(iFlags & 0x2) - iDstY = iY + m_iHeight - 1 - y; - if(iFlags & 0x1) - iDstX = iX + m_iWidth - 1 - x; - bmpCanvas.pixel(iDstX, iDstY) = src; - } - } -} - -static inline void _merge(th_colour_t& dst, const th_colour_t& src) -{ - dst.r = (uint8_t)(((unsigned int)dst.r + (unsigned int)src.r)/2); - dst.g = (uint8_t)(((unsigned int)dst.g + (unsigned int)src.g)/2); - dst.b = (uint8_t)(((unsigned int)dst.b + (unsigned int)src.b)/2); -} - -void Bitmap::blit(wxImage& imgCanvas, int iX, int iY, const unsigned char* pColourTranslate, const th_colour_t* pPalette, int iFlags) const -{ - if(m_iHeight == 0 || m_iWidth == 0) - return; - - th_colour_t* pCanvas = (th_colour_t*)imgCanvas.GetData(); - int iCanvasWidth = imgCanvas.GetWidth(); - if(m_iHeight > 256 || m_iWidth > 256) - { - return; - } - for(int y = 0; y < m_iHeight; ++y) - { - for(int x = 0; x < m_iWidth; ++x) - { - uint8_t src = pixel(x, y); - if(src == 0xFF && (iFlags & 0x8000) == 0) - continue; - if(pColourTranslate != NULL) - { - src = pColourTranslate[src]; - if(src == 0xFF && (iFlags & 0x8000) == 0) - continue; - } - int iDstX = iX + x; - int iDstY = iY + y; - if(iFlags & 0x2) - iDstY = iY + m_iHeight - 1 - y; - if(iFlags & 0x1) - iDstX = iX + m_iWidth - 1 - x; - th_colour_t srcc = pPalette[src]; - if(iFlags & 0xC) - { - th_colour_t dstc = pCanvas[iDstY * iCanvasWidth + iDstX]; - switch(iFlags & 0xC) - { - case 0x8: - _merge(srcc, dstc); - // fall-through - case 0x4: - _merge(srcc, dstc); - break; - } - } - pCanvas[iDstY * iCanvasWidth + iDstX] = srcc; - if(imgCanvas.HasAlpha()) - { - //set completely opaque - imgCanvas.SetAlpha(iDstX,iDstY,(unsigned char)255); - } + th_colour_t* pCanvas = (th_colour_t*)imgCanvas.GetData(); + int iCanvasWidth = imgCanvas.GetWidth(); + if (m_iHeight > 256 || m_iWidth > 256) { + return; + } + for (int y = 0; y < m_iHeight; ++y) { + for (int x = 0; x < m_iWidth; ++x) { + uint8_t src = pixel(x, y); + if (src == 0xFF && (iFlags & 0x8000) == 0) continue; + if (pColourTranslate != NULL) { + src = pColourTranslate[src]; + if (src == 0xFF && (iFlags & 0x8000) == 0) continue; + } + int iDstX = iX + x; + int iDstY = iY + y; + if (iFlags & 0x2) iDstY = iY + m_iHeight - 1 - y; + if (iFlags & 0x1) iDstX = iX + m_iWidth - 1 - x; + th_colour_t srcc = pPalette[src]; + if (iFlags & 0xC) { + th_colour_t dstc = pCanvas[iDstY * iCanvasWidth + iDstX]; + switch (iFlags & 0xC) { + case 0x8: + _merge(srcc, dstc); + // fall-through + case 0x4: + _merge(srcc, dstc); + break; } + } + pCanvas[iDstY * iCanvasWidth + iDstX] = srcc; + if (imgCanvas.HasAlpha()) { + // set completely opaque + imgCanvas.SetAlpha(iDstX, iDstY, (unsigned char)255); + } } + } } diff --git a/AnimView/th.h b/AnimView/th.h index f2653593..13b3ff5f 100644 --- a/AnimView/th.h +++ b/AnimView/th.h @@ -36,214 +36,210 @@ SOFTWARE. #pragma once #include "config.h" -#include +#include #include #include +#include #include #include -#include #include #pragma pack(push) #pragma pack(1) -struct th_anim_t -{ - uint16_t frame; - uint16_t unknown; +struct th_anim_t { + uint16_t frame; + uint16_t unknown; }; -struct th_frame_t -{ - uint32_t list_index; - uint8_t width; - uint8_t height; - uint16_t flags; - uint16_t next; +struct th_frame_t { + uint32_t list_index; + uint8_t width; + uint8_t height; + uint16_t flags; + uint16_t next; }; -struct th_element_t -{ - uint16_t table_position; - uint8_t offx; - uint8_t offy; - uint8_t flags; - uint8_t layerid; +struct th_element_t { + uint16_t table_position; + uint8_t offx; + uint8_t offy; + uint8_t flags; + uint8_t layerid; }; -struct th_sprite_t -{ - uint32_t offset; - uint8_t width; - uint8_t height; +struct th_sprite_t { + uint32_t offset; + uint8_t width; + uint8_t height; }; -struct th_colour_t -{ - uint8_t r; - uint8_t g; - uint8_t b; +struct th_colour_t { + uint8_t r; + uint8_t g; + uint8_t b; }; #pragma pack(pop) -class THLayerMask -{ -public: - THLayerMask(); +class THLayerMask { + public: + THLayerMask(); - inline void set(int iLayer, int iID) - { - if(0 <= iLayer && iLayer < 13 && 0 <= iID && iID < 32) - m_iMask[iLayer] |= (1 << iID); - } + inline void set(int iLayer, int iID) { + if (0 <= iLayer && iLayer < 13 && 0 <= iID && iID < 32) + m_iMask[iLayer] |= (1 << iID); + } - void clear(); + void clear(); - inline void clear(int iLayer, int iID) - { - if(0 <= iLayer && iLayer < 13 && 0 <= iID && iID < 32) - m_iMask[iLayer] &= ~(1 << iID); - } + inline void clear(int iLayer, int iID) { + if (0 <= iLayer && iLayer < 13 && 0 <= iID && iID < 32) + m_iMask[iLayer] &= ~(1 << iID); + } - inline bool isSet(int iLayer, int iID) const - { - if(0 <= iLayer && iLayer < 13 && 0 <= iID && iID < 32) - return (m_iMask[iLayer] & (1 << iID)) != 0; - else - return false; - } - inline bool isSet(int iLayer) const - { - if(0 <= iLayer && iLayer < 13) - for(int iId = 0; iId < 32; ++iId) - { - if((m_iMask[iLayer] & (static_cast(1) << iId)) != 0) - return true; - } + inline bool isSet(int iLayer, int iID) const { + if (0 <= iLayer && iLayer < 13 && 0 <= iID && iID < 32) + return (m_iMask[iLayer] & (1 << iID)) != 0; + else + return false; + } + inline bool isSet(int iLayer) const { + if (0 <= iLayer && iLayer < 13) + for (int iId = 0; iId < 32; ++iId) { + if ((m_iMask[iLayer] & (static_cast(1) << iId)) != 0) + return true; + } + return false; + } + + protected: + uint32_t m_iMask[13]; +}; + +class Bitmap { + public: + Bitmap(); + ~Bitmap(); + + void create(int iWidth, int iHeight); + void create(int iWidth, int iHeight, const uint8_t* pData); + + inline uint8_t pixel(int iX, int iY) const { + return m_pData[iY * m_iWidth + iX]; + } + inline uint8_t& pixel(int iX, int iY) { return m_pData[iY * m_iWidth + iX]; } + + int getWidth() const { return m_iWidth; } + int getHeight() const { return m_iHeight; } + + void blit(Bitmap& bmpCanvas, int iX, int iY, int iFlags = 0) const; + void blit(wxImage& imgCanvas, int iX, int iY, + const unsigned char* pColourTranslate, const th_colour_t* pPalette, + int iFlags = 0) const; + + bool IsOk() { return m_pData != nullptr; } + + protected: + int m_iWidth; + int m_iHeight; + uint8_t* m_pData; +}; + +class THAnimations { + public: + THAnimations(); + ~THAnimations(); + + bool loadAnimationFile(wxString sFilename) { + return loadVector(anims, sFilename); + } + + bool loadFrameFile(wxString sFilename); + + bool loadListFile(wxString sFilename) { + return loadVector(elementList, sFilename); + } + + bool loadElementFile(wxString sFilename) { + return loadVector(elements, sFilename); + } + + bool loadTableFile(wxString sFilename); + + bool loadSpriteFile(wxString sFilename) { + return loadVector(chunks, sFilename); + } + + bool loadPaletteFile(wxString sFilename); + + bool loadGhostFile(wxString sFilename, int iIndex); + + size_t markDuplicates(); + + size_t getAnimationCount(); + size_t getSpriteCount(); + size_t getFrameCount(size_t iAnimation); + uint16_t getUnknownField(size_t iAnimation) { + return anims.at(iAnimation).unknown; + } + uint16_t getFrameField(size_t iAnimation) { + return anims.at(iAnimation).frame; + } + th_frame_t* getFrameStruct(size_t iAnimation, size_t iFrame); + bool isAnimationDuplicate(size_t iAnimation); + bool doesAnimationIncludeFrame(size_t iAnimation, size_t iFrame); + void getAnimationMask(size_t iAnimation, THLayerMask& mskLayers); + void setSpritePath(wxString aPath); + + Bitmap* getSpriteBitmap(size_t iSprite, bool bComplex = false); + th_colour_t* getPalette() { return colours.data(); } + + void setGhost(int iFile, int iIndex); + void drawFrame(wxImage& imgCanvas, size_t iAnimation, size_t iFrame, + const THLayerMask* pMask, wxSize& size, int iXOffset = 0, + int iYOffset = 0); + void copySpriteToCanvas(wxString spriteFile, int iSpriteIndex, + wxImage& imgCanvas, int iX, int iY, int iFlags = 0); + + static unsigned char* Decompress(unsigned char* pData, size_t& iLength); + + protected: + template + bool loadVector(std::vector& vector, wxString sFilename) { + vector.clear(); + + wxFile oFile(sFilename); + if (!oFile.IsOpened()) return false; + + size_t iLen = oFile.Length(); + unsigned char* pBuffer = new unsigned char[iLen]; + oFile.Read(pBuffer, iLen); + if (memcmp(pBuffer, "RNC\001", 4) == 0) { + pBuffer = Decompress(pBuffer, iLen); + if (!pBuffer) { return false; + } } -protected: - uint32_t m_iMask[13]; -}; - -class Bitmap -{ -public: - Bitmap(); - ~Bitmap(); - - void create(int iWidth, int iHeight); - void create(int iWidth, int iHeight, const uint8_t* pData); - - inline uint8_t pixel(int iX, int iY) const {return m_pData[iY * m_iWidth + iX];} - inline uint8_t& pixel(int iX, int iY) {return m_pData[iY * m_iWidth + iX];} - - int getWidth() const {return m_iWidth;} - int getHeight() const {return m_iHeight;} - - void blit(Bitmap& bmpCanvas, int iX, int iY, int iFlags = 0) const; - void blit(wxImage& imgCanvas, int iX, int iY, const unsigned char* pColourTranslate, const th_colour_t* pPalette, int iFlags = 0) const; - - bool IsOk() {return m_pData != nullptr;} - -protected: - int m_iWidth; - int m_iHeight; - uint8_t* m_pData; -}; - -class THAnimations -{ -public: - THAnimations(); - ~THAnimations(); - - bool loadAnimationFile(wxString sFilename) { - return loadVector(anims, sFilename); - } - - bool loadFrameFile(wxString sFilename); - - bool loadListFile(wxString sFilename) { - return loadVector(elementList, sFilename); - } - - bool loadElementFile(wxString sFilename) { - return loadVector(elements, sFilename); - } - - bool loadTableFile(wxString sFilename); - - bool loadSpriteFile(wxString sFilename) { - return loadVector(chunks, sFilename); - } - - bool loadPaletteFile(wxString sFilename); - - bool loadGhostFile(wxString sFilename, int iIndex); - - size_t markDuplicates(); - - size_t getAnimationCount(); - size_t getSpriteCount(); - size_t getFrameCount(size_t iAnimation); - uint16_t getUnknownField(size_t iAnimation) {return anims.at(iAnimation).unknown; } - uint16_t getFrameField(size_t iAnimation) {return anims.at(iAnimation).frame; } - th_frame_t* getFrameStruct(size_t iAnimation, size_t iFrame); - bool isAnimationDuplicate(size_t iAnimation); - bool doesAnimationIncludeFrame(size_t iAnimation, size_t iFrame); - void getAnimationMask(size_t iAnimation, THLayerMask& mskLayers); - void setSpritePath(wxString aPath); - - Bitmap* getSpriteBitmap(size_t iSprite, bool bComplex = false); - th_colour_t* getPalette() { return colours.data(); } - - void setGhost(int iFile, int iIndex); - void drawFrame(wxImage& imgCanvas, size_t iAnimation, size_t iFrame, const THLayerMask* pMask, wxSize& size, int iXOffset = 0, int iYOffset = 0); - void copySpriteToCanvas(wxString spriteFile, int iSpriteIndex, wxImage& imgCanvas, int iX, int iY, int iFlags = 0); - - static unsigned char* Decompress(unsigned char* pData, size_t& iLength); -protected: - template - bool loadVector(std::vector& vector, wxString sFilename) { - vector.clear(); - - wxFile oFile(sFilename); - if (!oFile.IsOpened()) - return false; - - size_t iLen = oFile.Length(); - unsigned char* pBuffer = new unsigned char[iLen]; - oFile.Read(pBuffer, iLen); - if(memcmp(pBuffer, "RNC\001", 4) == 0) - { - pBuffer = Decompress(pBuffer, iLen); - if(!pBuffer) - { - return false; - } - } - - for (int offset = 0; offset < iLen; offset += sizeof(T)) { - vector.push_back(*(reinterpret_cast(pBuffer + offset))); - } - - return true; - } - - th_element_t* _getElement(uint32_t iListIndex); - - std::vector anims; - std::vector frames; - std::vector elementList; - std::vector elements; - std::vector sprites; - std::vector spriteBitmaps; - std::vector chunks; - std::vector colours; - std::array ghostMaps; - size_t m_iGhostMapOffset; - wxString m_sSpritePath; + for (int offset = 0; offset < iLen; offset += sizeof(T)) { + vector.push_back(*(reinterpret_cast(pBuffer + offset))); + } + + return true; + } + + th_element_t* _getElement(uint32_t iListIndex); + + std::vector anims; + std::vector frames; + std::vector elementList; + std::vector elements; + std::vector sprites; + std::vector spriteBitmaps; + std::vector chunks; + std::vector colours; + std::array ghostMaps; + size_t m_iGhostMapOffset; + wxString m_sSpritePath; }; diff --git a/CorsixTH/Src/bootstrap.cpp b/CorsixTH/Src/bootstrap.cpp index 5a4b0d02..68e27332 100644 --- a/CorsixTH/Src/bootstrap.cpp +++ b/CorsixTH/Src/bootstrap.cpp @@ -19,11 +19,11 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +#include "config.h" +#include +#include #include "lua.hpp" #include "th_lua.h" -#include "config.h" -#include -#include /* Often, an error occurs during the CorsixTH startup process. Examples of such errors include: @@ -42,281 +42,281 @@ homemade bitmap font, as we cannot rely on TH fonts being present). namespace { +// clang-format off constexpr int first_bootstrap_code_line_number = __LINE__ + 2; -constexpr std::array bootstrap_code {{ - "local lines, dat, tab, pal, err = {}, ...", - "local function t(s) return s:gsub('\\t', ' ') end", - "for s in tostring(err):gmatch'[^\\r\\n]+' do lines[#lines+1] = t(s) end", - "local TH, SDL, rnc = require'TH', require'sdl', require'rnc'.decompress", - "if not SDL.init('video') then error'Unable to initialise video' end", - "local w, h = 640, 480", - "local function dernc(x) return x:match'^RNC' and assert(rnc(x)) or x end", - "local video = TheApp and TheApp.video or TH.surface(w, h)", - "video:setCaption('CorsixTH - Error during startup')", - "local palette, sheet, font = TH.palette(), TH.sheet(), TH.bitmap_font()", - "if not palette:load(dernc(pal)) then error'Unable to load palette' end", - "sheet:setPalette(palette)", - "if not sheet:load(dernc(tab), dernc(dat), true, video) then error'Unable to load sheet' end", - "font:setSheet(sheet):setSeparation(1, 0)", - "local bx, by, bw, bh = 20, 0, 100, 16", // Print message and draw button: - "local function draw()", - " video:startFrame()", - " video:fillBlack()", - " local y = 20", - " for _, s in ipairs(lines) do", - " y = font:drawWrapped(video, s, 20, y, w - 40)", - " end", - " by = y + 20", - " video:drawRect(video:mapRGB(151, 23, 23), bx, by, bw, bh)", - " video:drawRect(video:mapRGB(171, 53, 53), bx + 1, by + 1, bw - 2, bh - 2)", - " font:draw(video, 'Exit', bx, by + 1, bw, bh)", - " video:endFrame()", - "end", - "SDL.wm.showCursor(true)", - "draw()", - "local running = true", // Minimal event handler: - "repeat", - " local e, where = SDL.mainloop(coroutine.create(function()", - " while running do", - " local e, _, x, y = coroutine.yield(true)", - " if e == 'frame' then draw()", - " elseif e == 'buttonup' then", - " x, y = x - bx, y - by", - " running = x < 0 or bw <= x or y < 0 or bh <= y", - " end", - " end", - " end))", - " if running then print(e) end", - "until where ~= 'callback'", - nullptr -}}; +constexpr std::array bootstrap_code{ + {"local lines, dat, tab, pal, err = {}, ...", + "local function t(s) return s:gsub('\\t', ' ') end", + "for s in tostring(err):gmatch'[^\\r\\n]+' do lines[#lines+1] = t(s) end", + "local TH, SDL, rnc = require'TH', require'sdl', require'rnc'.decompress", + "if not SDL.init('video') then error'Unable to initialise video' end", + "local w, h = 640, 480", + "local function dernc(x) return x:match'^RNC' and assert(rnc(x)) or x end", + "local video = TheApp and TheApp.video or TH.surface(w, h)", + "video:setCaption('CorsixTH - Error during startup')", + "local palette, sheet, font = TH.palette(), TH.sheet(), TH.bitmap_font()", + "if not palette:load(dernc(pal)) then error'Unable to load palette' end", + "sheet:setPalette(palette)", + "if not sheet:load(dernc(tab), dernc(dat), true, video) then error'Unable to load sheet' end", + "font:setSheet(sheet):setSeparation(1, 0)", + "local bx, by, bw, bh = 20, 0, 100, 16", // Print message and draw button: + "local function draw()", + " video:startFrame()", + " video:fillBlack()", + " local y = 20", + " for _, s in ipairs(lines) do", + " y = font:drawWrapped(video, s, 20, y, w - 40)", + " end", + " by = y + 20", + " video:drawRect(video:mapRGB(151, 23, 23), bx, by, bw, bh)", + " video:drawRect(video:mapRGB(171, 53, 53), bx + 1, by + 1, bw - 2, bh - 2)", + " font:draw(video, 'Exit', bx, by + 1, bw, bh)", + " video:endFrame()", + "end", + "SDL.wm.showCursor(true)", + "draw()", + "local running = true", // Minimal event handler: + "repeat", + " local e, where = SDL.mainloop(coroutine.create(function()", + " while running do", + " local e, _, x, y = coroutine.yield(true)", + " if e == 'frame' then draw()", + " elseif e == 'buttonup' then", + " x, y = x - bx, y - by", + " running = x < 0 or bw <= x or y < 0 or bh <= y", + " end", + " end", + " end))", + " if running then print(e) end", + "until where ~= 'callback'", + nullptr}}; +// clang-format on /* Start autogenerated content */ /* Data from bootstrap_font.tab inserted by mkbootstrap.lua: */ -constexpr std::array bootstrap_font_tab { -0x52, 0x4E, 0x43, 0x01, 0x00, 0x00, 0x05, 0x46, 0x00, 0x00, 0x01, 0xE1, 0xFB, -0xF2, 0x66, 0x51, 0xBE, 0xEF, 0x0C, 0x09, 0x59, 0x60, 0x10, 0x34, 0x43, 0x54, -0xA6, 0x46, 0xA2, 0x08, 0x00, 0xA0, 0xC4, 0x01, 0xF6, 0x3D, 0x00, 0x00, 0xAF, -0xAE, 0x05, 0x01, 0x03, 0x01, 0x0C, 0x0B, 0xAE, 0x9E, 0x03, 0x0C, 0x16, 0x08, -0x0C, 0x47, 0x63, 0xEA, 0x0C, 0x74, 0x07, 0x0C, 0x9A, 0xEA, 0xEA, 0x06, 0x0C, -0xC8, 0x02, 0x0C, 0xD1, 0x1D, 0x92, 0xEE, 0x4B, 0x43, 0x09, 0x01, 0x3B, 0xD5, -0x34, 0x4F, 0xAA, 0xF4, 0xCC, 0xA4, 0x56, 0x60, 0xDA, 0x66, 0x54, 0x1D, 0x66, -0xA3, 0x3E, 0x7C, 0x04, 0x0C, 0x9B, 0xD3, 0x4C, 0xB5, 0xD5, 0x52, 0x93, 0xF2, -0x13, 0x24, 0x12, 0x02, 0x31, 0x52, 0x49, 0x92, 0x71, 0x97, 0x54, 0x7B, 0xB9, -0x8C, 0x6A, 0xC3, 0xB3, 0x51, 0xCD, 0xB5, 0x4E, 0xE5, 0x4E, 0x33, 0xF6, 0x1D, -0xAD, 0x0F, 0x34, 0xAA, 0x29, 0xDB, 0x8C, 0x4E, 0xAA, 0xBB, 0x12, 0x9A, 0x91, -0x49, 0x4A, 0xB1, 0xD7, 0x48, 0x2E, 0xF5, 0x10, 0x04, 0x01, 0x49, 0x34, 0x58, -0x92, 0x54, 0x76, 0x97, 0xE9, 0x91, 0xB9, 0x12, 0x4E, 0xDB, 0x03, 0x05, 0x50, -0x6D, 0x2B, 0x36, 0xAA, 0x50, 0xDC, 0xA8, 0x6E, 0x2E, 0x3D, 0x9C, 0x33, 0xA5, -0xBD, 0x25, 0xC9, 0xDD, 0xF8, 0x09, 0x92, 0x1F, 0x06, 0x45, 0x6D, 0xAA, 0x69, -0x66, 0x92, 0x97, 0xB7, 0x54, 0xB7, 0xDB, 0x1F, 0x39, 0xF8, 0x12, 0x07, 0x41, -0x95, 0x2F, 0x1B, 0x49, 0x41, 0x4B, 0x75, 0xDD, 0x99, 0x49, 0x55, 0x70, 0x75, -0xBF, 0x92, 0x24, 0xA7, 0xC4, 0x49, 0x92, 0xD9, 0xF5, 0x6B, 0x82, 0x14, 0x08, -0x38, 0xEA, 0x65, 0x8C, 0x6A, 0x43, 0x9B, 0x51, 0x5D, 0xE5, 0x46, 0x7F, 0xB5, -0xC7, 0xA4, 0x95, 0x91, 0xAE, 0xD0, 0x4C, 0xC6, 0xE2, 0x33, 0xC9, 0xFF, 0x09, -0x92, 0x1F, 0x09, 0x34, 0x47, 0x24, 0xA9, 0x65, 0x7D, 0x9A, 0x46, 0x99, 0x92, -0x72, 0xB6, 0xD4, 0x9A, 0xB9, 0xF4, 0x10, 0x0A, 0x19, 0x54, 0x32, 0x1B, 0xCF, -0x3E, 0x4C, 0xAA, 0x5E, 0x97, 0x31, 0x6D, 0xAA, 0xF4, 0x48, 0x39, 0x87, 0xB0, -0x49, 0x09, 0xCF, 0xE9, 0xFB, 0x99, 0x90, 0x46, 0x2C, 0x92, 0x54, 0x4B, 0x6D, -0xE9, 0x91, 0x8A, 0x24, 0x49, 0xA6, 0xC0, 0xD9, 0x75, 0xC5, 0x91, 0xBD, 0xF0, -0x9C, 0x75, 0x45, 0xA8, 0x19, 0xFA, 0x46, 0x3A, 0x52, 0x75, 0x5A, 0x8C, 0x2A, -0x75, 0x3B, 0xAA, 0x95, 0xBD, 0x27, 0xBF, 0x27, 0x39, 0xE3, 0x04, 0x0D, 0x41, -0xD5, 0x24, 0x31, 0x92, 0x42, 0x5E, 0xA4, 0x6A, 0x85, 0x9A, 0xB4, 0xAA, 0x12, -0x9A, 0xD0, 0xF3, 0x39, 0x41, 0x15, 0x0E, 0x37, 0x92, 0x24, 0x5F, 0x84, 0xA5, -0xD5, 0xDE, 0x33, 0x93, 0xB6, 0xD9, 0xAA, 0x6F, 0xE4, 0x2E, 0xF6, 0x14, 0x0F, -0x61, 0x92, 0x52, 0xA4, 0xCA, 0x6C, 0xCD, 0x4C, 0x86, 0x94, 0x92, 0xEA, 0xA5, -0x7F, 0x39, 0xD9, 0x72, 0xAF, 0x11, 0x10, 0x04, 0xAA, 0x1C, 0xED, 0x47, 0x3D, -0xF6, 0x97, 0x3D, 0xF4, 0x7D, 0x0A, 0x5E, 0xAF, 0x44, 0x80, 0xAA, 0x57, 0x35, -0xAA, 0xA1, 0xF7, 0x34, 0xBA, 0x92, 0xA4, 0xD6, 0xF7, 0x7A, 0xF7, 0x5B, 0x1A, -0x18, 0x11, 0x35, 0xAA, 0xC4, 0x48, 0xAA, 0x4C, 0x6E, 0xD6, 0x91, 0x93, 0x12, -0x52, 0xBA, 0xE0, 0x35, 0x8D, 0xFF, 0x5C, 0x1F, 0x27, 0x12, 0x54, 0xE9, 0x44, -0x51, 0xA5, 0x67, 0x47, 0x92, 0x7F, 0x99, 0x64, 0x37, 0xBA, 0x38, 0x49, 0xE2, -0xA8, 0xCF, 0xF2, 0x75, 0x53, 0x0D, 0x13, 0xFA, 0x24, 0x1E, 0x51, 0xB9, 0xC9, -0x29, 0x40, 0x52, 0x7D, 0x0F -}; +constexpr std::array bootstrap_font_tab{ + 0x52, 0x4E, 0x43, 0x01, 0x00, 0x00, 0x05, 0x46, 0x00, 0x00, 0x01, 0xE1, + 0xFB, 0xF2, 0x66, 0x51, 0xBE, 0xEF, 0x0C, 0x09, 0x59, 0x60, 0x10, 0x34, + 0x43, 0x54, 0xA6, 0x46, 0xA2, 0x08, 0x00, 0xA0, 0xC4, 0x01, 0xF6, 0x3D, + 0x00, 0x00, 0xAF, 0xAE, 0x05, 0x01, 0x03, 0x01, 0x0C, 0x0B, 0xAE, 0x9E, + 0x03, 0x0C, 0x16, 0x08, 0x0C, 0x47, 0x63, 0xEA, 0x0C, 0x74, 0x07, 0x0C, + 0x9A, 0xEA, 0xEA, 0x06, 0x0C, 0xC8, 0x02, 0x0C, 0xD1, 0x1D, 0x92, 0xEE, + 0x4B, 0x43, 0x09, 0x01, 0x3B, 0xD5, 0x34, 0x4F, 0xAA, 0xF4, 0xCC, 0xA4, + 0x56, 0x60, 0xDA, 0x66, 0x54, 0x1D, 0x66, 0xA3, 0x3E, 0x7C, 0x04, 0x0C, + 0x9B, 0xD3, 0x4C, 0xB5, 0xD5, 0x52, 0x93, 0xF2, 0x13, 0x24, 0x12, 0x02, + 0x31, 0x52, 0x49, 0x92, 0x71, 0x97, 0x54, 0x7B, 0xB9, 0x8C, 0x6A, 0xC3, + 0xB3, 0x51, 0xCD, 0xB5, 0x4E, 0xE5, 0x4E, 0x33, 0xF6, 0x1D, 0xAD, 0x0F, + 0x34, 0xAA, 0x29, 0xDB, 0x8C, 0x4E, 0xAA, 0xBB, 0x12, 0x9A, 0x91, 0x49, + 0x4A, 0xB1, 0xD7, 0x48, 0x2E, 0xF5, 0x10, 0x04, 0x01, 0x49, 0x34, 0x58, + 0x92, 0x54, 0x76, 0x97, 0xE9, 0x91, 0xB9, 0x12, 0x4E, 0xDB, 0x03, 0x05, + 0x50, 0x6D, 0x2B, 0x36, 0xAA, 0x50, 0xDC, 0xA8, 0x6E, 0x2E, 0x3D, 0x9C, + 0x33, 0xA5, 0xBD, 0x25, 0xC9, 0xDD, 0xF8, 0x09, 0x92, 0x1F, 0x06, 0x45, + 0x6D, 0xAA, 0x69, 0x66, 0x92, 0x97, 0xB7, 0x54, 0xB7, 0xDB, 0x1F, 0x39, + 0xF8, 0x12, 0x07, 0x41, 0x95, 0x2F, 0x1B, 0x49, 0x41, 0x4B, 0x75, 0xDD, + 0x99, 0x49, 0x55, 0x70, 0x75, 0xBF, 0x92, 0x24, 0xA7, 0xC4, 0x49, 0x92, + 0xD9, 0xF5, 0x6B, 0x82, 0x14, 0x08, 0x38, 0xEA, 0x65, 0x8C, 0x6A, 0x43, + 0x9B, 0x51, 0x5D, 0xE5, 0x46, 0x7F, 0xB5, 0xC7, 0xA4, 0x95, 0x91, 0xAE, + 0xD0, 0x4C, 0xC6, 0xE2, 0x33, 0xC9, 0xFF, 0x09, 0x92, 0x1F, 0x09, 0x34, + 0x47, 0x24, 0xA9, 0x65, 0x7D, 0x9A, 0x46, 0x99, 0x92, 0x72, 0xB6, 0xD4, + 0x9A, 0xB9, 0xF4, 0x10, 0x0A, 0x19, 0x54, 0x32, 0x1B, 0xCF, 0x3E, 0x4C, + 0xAA, 0x5E, 0x97, 0x31, 0x6D, 0xAA, 0xF4, 0x48, 0x39, 0x87, 0xB0, 0x49, + 0x09, 0xCF, 0xE9, 0xFB, 0x99, 0x90, 0x46, 0x2C, 0x92, 0x54, 0x4B, 0x6D, + 0xE9, 0x91, 0x8A, 0x24, 0x49, 0xA6, 0xC0, 0xD9, 0x75, 0xC5, 0x91, 0xBD, + 0xF0, 0x9C, 0x75, 0x45, 0xA8, 0x19, 0xFA, 0x46, 0x3A, 0x52, 0x75, 0x5A, + 0x8C, 0x2A, 0x75, 0x3B, 0xAA, 0x95, 0xBD, 0x27, 0xBF, 0x27, 0x39, 0xE3, + 0x04, 0x0D, 0x41, 0xD5, 0x24, 0x31, 0x92, 0x42, 0x5E, 0xA4, 0x6A, 0x85, + 0x9A, 0xB4, 0xAA, 0x12, 0x9A, 0xD0, 0xF3, 0x39, 0x41, 0x15, 0x0E, 0x37, + 0x92, 0x24, 0x5F, 0x84, 0xA5, 0xD5, 0xDE, 0x33, 0x93, 0xB6, 0xD9, 0xAA, + 0x6F, 0xE4, 0x2E, 0xF6, 0x14, 0x0F, 0x61, 0x92, 0x52, 0xA4, 0xCA, 0x6C, + 0xCD, 0x4C, 0x86, 0x94, 0x92, 0xEA, 0xA5, 0x7F, 0x39, 0xD9, 0x72, 0xAF, + 0x11, 0x10, 0x04, 0xAA, 0x1C, 0xED, 0x47, 0x3D, 0xF6, 0x97, 0x3D, 0xF4, + 0x7D, 0x0A, 0x5E, 0xAF, 0x44, 0x80, 0xAA, 0x57, 0x35, 0xAA, 0xA1, 0xF7, + 0x34, 0xBA, 0x92, 0xA4, 0xD6, 0xF7, 0x7A, 0xF7, 0x5B, 0x1A, 0x18, 0x11, + 0x35, 0xAA, 0xC4, 0x48, 0xAA, 0x4C, 0x6E, 0xD6, 0x91, 0x93, 0x12, 0x52, + 0xBA, 0xE0, 0x35, 0x8D, 0xFF, 0x5C, 0x1F, 0x27, 0x12, 0x54, 0xE9, 0x44, + 0x51, 0xA5, 0x67, 0x47, 0x92, 0x7F, 0x99, 0x64, 0x37, 0xBA, 0x38, 0x49, + 0xE2, 0xA8, 0xCF, 0xF2, 0x75, 0x53, 0x0D, 0x13, 0xFA, 0x24, 0x1E, 0x51, + 0xB9, 0xC9, 0x29, 0x40, 0x52, 0x7D, 0x0F}; /* Data from bootstrap_font.dat inserted by mkbootstrap.lua: */ -constexpr std::array bootstrap_font_dat { -0x52, 0x4E, 0x43, 0x01, 0x00, 0x00, 0x13, 0x68, 0x00, 0x00, 0x05, 0xEC, 0xD3, -0x6C, 0x7E, 0xAB, 0xBE, 0xEF, 0x94, 0x90, 0x81, 0x61, 0x50, 0x34, 0x44, 0x33, -0x33, 0x53, 0x66, 0x64, 0x64, 0x26, 0x20, 0x60, 0x47, 0x1E, 0x01, 0xFF, 0x84, -0x41, 0x01, 0x02, 0xFF, 0x01, 0x84, 0x09, 0xFF, 0xFF, 0x14, 0x2F, 0x06, 0xCD, -0x9B, 0xCC, 0x8C, 0x89, 0x04, 0x1B, 0x42, 0x06, 0x65, 0x64, 0x71, 0x3B, 0x44, -0x01, 0x06, 0x78, 0xE0, 0xE9, 0xD9, 0x9A, 0x2B, 0x36, 0x72, 0xB0, 0x43, 0x84, -0x59, 0x1B, 0x4D, 0x0A, 0xB6, 0x75, 0x87, 0x91, 0x32, 0xAB, 0x86, 0x02, 0xCC, -0xDE, 0x84, 0x07, 0x82, 0x31, 0x85, 0xCC, 0x45, 0x90, 0x4A, 0x84, 0xE7, 0xC4, -0x9C, 0x42, 0xFC, 0x29, 0x66, 0x0C, 0xA4, 0x94, 0x9D, 0xD9, 0x38, 0x75, 0x35, -0x42, 0x98, 0x07, 0x6D, 0x70, 0x91, 0x1B, 0x03, 0x8E, 0x54, 0x40, 0x9B, 0xED, -0x89, 0x19, 0x44, 0x87, 0x02, 0x9D, 0x8B, 0x5C, 0x3A, 0x15, 0x19, 0xF8, 0xB7, -0xAC, 0x43, 0x01, 0x15, 0x93, 0x0B, 0x5D, 0x03, 0x83, 0x40, 0x1A, 0x56, 0xA5, -0x1D, 0x03, 0xF2, 0x93, 0xA2, 0x50, 0x03, 0xB6, 0x11, 0x1C, 0xF2, 0x8E, 0x8E, -0x60, 0x87, 0x42, 0x89, 0x93, 0x40, 0x93, 0x64, 0xA2, 0x81, 0x04, 0x29, 0x23, -0x11, 0x4F, 0x36, 0xA0, 0xBA, 0x8E, 0xA2, 0xA4, 0x1A, 0x08, 0x75, 0x65, 0x6E, -0x9C, 0xAC, 0x14, 0xC3, 0x18, 0x67, 0x33, 0x16, 0x4E, 0x10, 0x31, 0x8C, 0x11, -0x9B, 0x17, 0x7C, 0xCC, 0x44, 0x57, 0x8F, 0xF7, 0x74, 0x0E, 0x3F, 0x71, 0x80, -0x64, 0x08, 0x39, 0x90, 0xBC, 0x28, 0x87, 0x3A, 0x15, 0x8B, 0x70, 0x30, 0x9D, -0x04, 0x14, 0x0B, 0x89, 0x1C, 0x95, 0x21, 0x8F, 0x50, 0xB5, 0x2D, 0xD9, 0x85, -0x1B, 0x80, 0x72, 0xEC, 0x0A, 0xCE, 0x6B, 0xA8, 0xDB, 0x86, 0x05, 0x35, 0x93, -0x84, 0x12, 0x8B, 0xE6, 0xEC, 0x84, 0xBF, 0x79, 0x5F, 0xDA, 0xA4, 0xC8, 0x97, -0xF8, 0x84, 0x42, 0x9B, 0x19, 0xB4, 0x96, 0xCB, 0xD4, 0x85, 0x21, 0x84, 0x08, -0xB3, 0x5B, 0x1F, 0x88, 0x60, 0xCE, 0x85, 0x0F, 0x56, 0x92, 0x92, 0xA4, 0x05, -0xA6, 0x43, 0x46, 0x9E, 0x94, 0x08, 0x8B, 0x58, 0x17, 0x3B, 0x87, 0xC9, 0xAA, -0x86, 0x48, 0x83, 0x23, 0x12, 0x83, 0x52, 0x90, 0x89, 0x38, 0x4A, 0x50, 0x89, -0x52, 0x32, 0x98, 0x45, 0xB9, 0x84, 0x48, 0x6D, 0x98, 0x17, 0x9E, 0xA2, 0x37, -0x66, 0xC2, 0xA2, 0xB4, 0x5A, 0x2C, 0x54, 0xE5, 0xC5, 0x15, 0x74, 0x44, 0x67, -0x06, 0x50, 0x6E, 0xE1, 0x06, 0x2F, 0x2D, 0xC8, 0x99, 0x0D, 0xB6, 0x4D, 0x79, -0x24, 0x0E, 0x07, 0x1D, 0xE8, 0x91, 0x94, 0x0F, 0xDE, 0x62, 0x3F, 0x60, 0x0F, -0x27, 0x63, 0xE5, 0x40, 0x78, 0x85, 0xB8, 0x15, 0x09, 0xCA, 0x22, 0x2B, 0x19, -0x09, 0x53, 0xF9, 0x84, 0x69, 0x6D, 0x32, 0x6B, 0xAE, 0x45, 0xCA, 0x90, 0xE6, -0x12, 0x6A, 0xB4, 0xA2, 0xD8, 0x4C, 0xFA, 0xA1, 0x5D, 0xEF, 0xC2, 0xE4, 0x02, -0x00, 0xF9, 0x5B, 0x05, 0x13, 0x6B, 0xC2, 0xB9, 0xAA, 0x17, 0xD8, 0xBA, 0xB1, -0xF9, 0xA0, 0x05, 0x4C, 0x2F, 0x9A, 0x61, 0xBA, 0x52, 0x94, 0x63, 0xB9, 0x16, -0x9D, 0xA3, 0x65, 0x84, 0x3D, 0x4A, 0x64, 0x9C, 0x96, 0x20, 0xF6, 0x30, 0x85, -0x68, 0x5A, 0x50, 0x95, 0xE5, 0x30, 0xFD, 0x90, 0x1C, 0xE2, 0xE6, 0x43, 0x0B, -0x6A, 0x9F, 0x23, 0x12, 0x27, 0xC2, 0x81, 0x9D, 0x55, 0x85, 0xC7, 0x01, 0xA5, -0x58, 0x01, 0xC7, 0xD3, 0xF3, 0xFD, 0x87, 0x0B, 0x58, 0x7C, 0x52, 0xCA, 0x6C, -0x47, 0xF1, 0x39, 0x19, 0x8A, 0x9A, 0xF7, 0x21, 0x0A, 0x67, 0x06, 0xBE, 0xD3, -0x88, 0xCF, 0x47, 0x34, 0x94, 0x09, 0x04, 0x40, 0x64, 0x09, 0xE6, 0x4F, 0xEA, -0xA5, 0x55, 0x4E, 0xA5, 0x95, 0xE5, 0x50, 0x0D, 0x06, 0x46, 0x61, 0x85, 0x23, -0x08, 0xAB, 0xF9, 0x48, 0x09, 0x33, 0x44, 0x50, 0x07, 0x21, 0x3C, 0xA8, 0x61, -0x9D, 0x34, 0x84, 0x22, 0x5C, 0x44, 0xFD, 0xA0, 0x6D, 0xE2, 0x6E, 0x56, 0x3A, -0xA3, 0x19, 0x62, 0x4A, 0x82, 0x0C, 0x98, 0x25, 0xCF, 0x24, 0xC8, 0x35, 0x35, -0x1A, 0x12, 0x59, 0x5D, 0x98, 0xD6, 0x2E, 0x76, 0x7F, 0x2B, 0xF4, 0x30, 0x24, -0xAF, 0x4C, 0x5C, 0x28, 0x2E, 0x28, 0xAA, 0x24, 0xE5, 0x17, 0xFF, 0x55, 0x42, -0xC9, 0x54, 0x1B, 0x75, 0xDD, 0xBE, 0x47, 0x9B, 0x11, 0x22, 0x0E, 0xCC, 0xAA, -0x88, 0x88, 0x66, 0x64, 0xCA, 0xD0, 0x88, 0x08, 0x41, 0x06, 0xF0, 0x17, 0x45, -0x82, 0x07, 0xAE, 0x80, 0x9F, 0xB2, 0x23, 0x23, 0xAB, 0x8C, 0xE9, 0xFA, 0xC3, -0x53, 0xD0, 0x06, 0x88, 0x67, 0x59, 0xB3, 0x92, 0xD2, 0xE9, 0xA8, 0x82, 0x0A, -0xA7, 0xC9, 0x44, 0x8E, 0x47, 0x15, 0x82, 0x37, 0x80, 0x9B, 0x88, 0xF9, 0x08, -0x27, 0xDC, 0x05, 0xC2, 0xD3, 0x94, 0x59, 0x55, 0x41, 0x7F, 0x91, 0xFF, 0xDB, -0x16, 0x8C, 0x0C, 0x27, 0x76, 0x6E, 0x26, 0x81, 0xAA, 0xA1, 0x8F, 0xC1, 0x20, -0x63, 0x0B, 0xC3, 0x89, 0x49, 0x31, 0x42, 0x93, 0x8C, 0x69, 0x07, 0x7B, 0x3F, -0x9E, 0x0E, 0x45, 0x56, 0x85, 0x91, 0xE9, 0x78, 0xCC, 0xF9, 0x08, 0xA4, 0xD5, -0x92, 0x70, 0x89, 0x16, 0xC3, 0x0C, 0x92, 0x18, 0xA7, 0x59, 0xF2, 0x08, 0xCD, -0xE0, 0xF2, 0xA8, 0x97, 0x4C, 0xF3, 0x83, 0xA7, 0x1C, 0x68, 0x54, 0x17, 0x03, -0x8A, 0x20, 0x57, 0x50, 0x58, 0x31, 0x11, 0x72, 0x87, 0x71, 0x25, 0x2E, 0xE7, -0xE4, 0x79, 0x90, 0x10, 0x50, 0x1D, 0x54, 0x88, 0xE1, 0xB7, 0x8F, 0x18, 0x02, -0x77, 0xA8, 0x0C, 0x64, 0x11, 0x8C, 0xC9, 0x21, 0xE6, 0x07, 0xCB, 0x5E, 0x8F, -0xB2, 0x25, 0x0B, 0x7B, 0x0A, 0x25, 0x8B, 0x95, 0xD0, 0x82, 0xF4, 0xE2, 0x34, -0x20, 0xEA, 0x5F, 0x36, 0x8C, 0xA1, 0x94, 0x50, 0xFD, 0x69, 0x1B, 0xCB, 0xB0, -0x8B, 0x4F, 0xBF, 0xB5, 0xC7, 0x0B, 0xC2, 0xC5, 0x17, 0xEE, 0x8C, 0x91, 0xFB, -0xD3, 0x0D, 0xD1, 0xE1, 0x98, 0x3A, 0xC9, 0x4D, 0x08, 0xF4, 0xB8, 0x59, 0x11, -0x78, 0x48, 0x20, 0x8B, 0x25, 0x08, 0xBF, 0xF2, 0x89, 0x59, 0xDB, 0x31, 0xD6, -0x17, 0x9E, 0xCA, 0x74, 0x20, 0x8E, 0x0E, 0xD5, 0x07, 0x3B, 0x5D, 0x51, 0x8E, -0x86, 0x71, 0xCE, 0xE6, 0x2C, 0x54, 0xB2, 0xC6, 0x8B, 0x02, 0x7D, 0x7F, 0xC2, -0x61, 0xC2, 0x0B, 0xF0, 0x85, 0x84, 0xCC, 0x98, 0x11, 0x4A, 0x8D, 0x66, 0x03, -0xF8, 0x54, 0x57, 0xB0, 0xF0, 0x45, 0x8C, 0xFF, 0xD3, 0x61, 0xA8, 0x82, 0x09, -0xFF, 0x00, 0xCA, 0x0B, 0x8C, 0x9B, 0x75, 0x24, 0x09, 0x07, 0x00, 0xF9, 0xAC, -0xC1, 0xBB, 0xB7, 0x9E, 0xE8, 0x41, 0xE4, 0x2B, 0xCE, 0xAB, 0x2D, 0x9D, 0xEA, -0xFD, 0x41, 0x00, 0x42, 0x8F, 0xC9, 0xF4, 0x87, 0x54, 0x61, 0x2D, 0x42, 0x51, -0x3F, 0x52, 0x29, 0xCB, 0x87, 0x0F, 0x1F, 0xE4, 0x11, 0x33, 0x8E, 0xE5, 0xCD, -0x86, 0x6F, 0x96, 0x30, 0x40, 0x06, 0xF5, 0x7F, 0x42, 0xB2, 0x61, 0x93, 0x55, -0x87, 0xFE, 0x86, 0xE3, 0x67, 0x49, 0xA2, 0xC3, 0x1F, 0x6F, 0xEF, 0x76, 0x4E, -0xE5, 0x12, 0x89, 0x3E, 0x10, 0xFF, 0x90, 0x99, 0xBF, 0x40, 0x45, 0xAA, 0xFC, -0x10, 0xDC, 0xC1, 0xF1, 0x44, 0x13, 0x50, 0x58, 0x29, 0x61, 0x28, 0x95, 0x70, -0x05, 0x7E, 0x21, 0x8D, 0xE3, 0x83, 0xB2, 0x8F, 0xB2, 0xA2, 0x10, 0xE0, 0x93, -0xAC, 0xFD, 0x88, 0x38, 0x38, 0xC4, 0x46, 0x23, 0x4A, 0x41, 0xF8, 0x0F, 0xA8, -0x12, 0xD5, 0x88, 0x7F, 0x92, 0xA2, 0x21, 0xBE, 0xF1, 0x0A, 0x11, 0x92, 0x11, -0x99, 0xAB, 0xEA, 0xC3, 0x85, 0x22, 0x1B, 0xA2, 0xDF, 0xE9, 0x7E, 0x18, 0x95, -0xB6, 0x48, 0xD1, 0x9F, 0x50, 0xCF, 0x11, 0xBE, 0xF9, 0xB9, 0x1E, 0xC7, 0x9B, -0x00, 0xE3, 0x62, 0xAA, 0x98, 0x0E, 0x01, 0x87, 0xC0, 0x27, 0x59, 0x53, 0x83, -0x47, 0xBA, 0x96, 0xEA, 0xFF, 0x88, 0x68, 0xEA, 0x08, 0xD3, 0x58, 0xCE, 0x0F, -0x05, 0x8A, 0xDA, 0x27, 0x6E, 0x15, 0x18, 0xF2, 0xAF, 0x38, 0xEA, 0x54, 0xA0, -0x89, 0x74, 0xA1, 0x4B, 0x16, 0x9A, 0x14, 0x4D, 0x06, 0x85, 0x1D, 0xEC, 0xEF, -0x52, 0x54, 0x93, 0x2C, 0x09, 0xA3, 0xE9, 0xC6, 0x1C, 0x75, 0xAD, 0xD8, 0x14, -0x2E, 0x31, 0x58, 0x15, 0xCF, 0xB8, 0x44, 0x04, 0xAC, 0x1C, 0xF0, 0x22, 0xA9, -0x3B, 0x99, 0xD9, 0x44, 0x9D, 0x8E, 0xF8, 0x05, 0xE1, 0x04, 0x7B, 0x0B, 0x32, -0x13, 0x85, 0x22, 0xDE, 0x87, 0xAC, 0x27, 0xAB, 0x2A, 0x66, 0x75, 0xA1, 0x9B, -0xC0, 0x51, 0xE1, 0x59, 0x43, 0x5B, 0xC2, 0x37, 0xDF, 0x00, 0xD5, 0x98, 0x53, -0xE3, 0x95, 0xD0, 0xD2, 0x88, 0xF6, 0x8F, 0xFD, 0x28, 0x5D, 0xFE, 0x50, 0x34, -0xF4, 0x85, 0xEA, 0xA1, 0xC9, 0x0F, 0xA6, 0x72, 0xA8, 0x57, 0xF8, 0xEA, 0x50, -0x96, 0xC8, 0x84, 0x1E, 0xF8, 0x1F, 0x2C, 0xEB, 0xA9, 0xA3, 0x64, 0xE1, 0xAC, -0xE4, 0x2B, 0x50, 0xA4, 0x66, 0x54, 0x87, 0x63, 0xA2, 0x87, 0x00, 0x04, 0x29, -0x2F, 0x1E, 0x89, 0x51, 0xC1, 0xC0, 0xD0, 0xA4, 0xC7, 0x14, 0x52, 0xBA, 0x58, -0xF1, 0xC4, 0x41, 0x40, 0x57, 0xD5, 0xF2, 0xA6, 0x80, 0xEC, 0xB0, 0x30, 0xB1, -0x71, 0xDB, 0x45, 0xA4, 0x86, 0xAC, 0xA7, 0x98, 0xD4, 0xB3, 0x26, 0x42, 0xF1, -0x24, 0x31, 0x0C, 0x54, 0x4F, 0x68, 0x03, 0xB3, 0x0E, 0xD8, 0xB7, 0xE5, 0x8D, -0x1C, 0x8B, 0xD5, 0x08, 0x2D, 0x9E, 0xD0, 0x9C, 0x06, 0x62, 0xA1, 0xD5, 0x41, -0x3B, 0xA0, 0x5C, 0x7E, 0x92, 0x75, 0x84, 0x57, 0x38, 0x41, 0xA0, 0x25, 0xB3, -0xA6, 0xA3, 0x89, 0x33, 0xC2, 0xD1, 0x60, 0x72, 0x57, 0xB1, 0xC2, 0xC0, 0x2A, -0x79, 0xB0, 0xC2, 0x6C, 0x13, 0x01, 0x51, 0x21, 0xCB, 0x13, 0xA3, 0x32, 0x3E, -0x16, 0x49, 0xEF, 0x6C, 0xF1, 0x30, 0x90, 0x05, 0x5D, 0x85, 0x27, 0x1B, 0xAA, -0xC0, 0x74, 0x7A, 0x5C, 0x37, 0x01, 0x27, 0x05, 0xEA, 0x3C, 0x4A, 0x4B, 0x9F, -0x0D, 0xC8, 0x18, 0xA2, 0xD4, 0x20, 0x59, 0xCB, 0x19, 0x7D, 0x9C, 0x5F, 0x6C, -0x04, 0xCA, 0x28, 0x6E, 0x6B, 0xAC, 0x7B, 0x0C, 0xCE, 0xE5, 0x9E, 0x49, 0x84, -0x18, 0xDD, 0x1D, 0xAA, 0x39, 0x29, 0x9F, 0x30, 0xFA, 0x22, 0x67, 0x7C, 0x93, -0x92, 0x09, 0x13, 0x52, 0x2F, 0x1E, 0x4E, 0x6E, 0x61, 0x05, 0x18, 0x85, 0x07, -0xA9, 0x33, 0x1E, 0x68, 0xBD, 0x95, 0x31, 0x20, 0x8B, 0x2A, 0xA6, 0x70, 0x96, -0xC4, 0x5E, 0x3D, 0x95, 0x50, 0x79, 0x67, 0x46, 0xCA, 0x50, 0x20, 0xDF, 0x10, -0x2F, 0x08, 0xA0, 0x03, 0xBD, 0x2D, 0x50, 0xEE, 0x55, 0x2C, 0xEC, 0xC9, 0x5F, -0xB9, 0x11, 0x44, 0xEF, 0x4A, 0x21, 0x74, 0x74, 0x75, 0xF3, 0x86, 0xA2, 0x8A, -0x88, 0xA3, 0x77, 0x21, 0xC6, 0xC3, 0x21, 0x27, 0x8A, 0x42, 0x2C, 0x16, 0x3A, -0x88, 0x8A, 0x7F, 0x73, 0xEA, 0xD8, 0x79, 0x46, 0x1C, 0x67, 0x9B, 0x79, 0x61, -0x95, 0xF3, 0x44, 0x63, 0x65, 0x12, 0x8C, 0xA5, 0xBB, 0x88, 0x12, 0x67, 0x9F, -0x90, 0x5D, 0x95, 0x66, 0xA1, 0x9C, 0x57, 0x2A, 0x09, 0x34, 0x07, 0x7B, 0xD6, -0x02, 0xAC, 0x9A, 0x43, 0x10, 0xB5, 0x0A, 0xA0, 0x17, 0x1C, 0xA6, 0x00, 0x9B -}; +constexpr std::array bootstrap_font_dat{ + 0x52, 0x4E, 0x43, 0x01, 0x00, 0x00, 0x13, 0x68, 0x00, 0x00, 0x05, 0xEC, + 0xD3, 0x6C, 0x7E, 0xAB, 0xBE, 0xEF, 0x94, 0x90, 0x81, 0x61, 0x50, 0x34, + 0x44, 0x33, 0x33, 0x53, 0x66, 0x64, 0x64, 0x26, 0x20, 0x60, 0x47, 0x1E, + 0x01, 0xFF, 0x84, 0x41, 0x01, 0x02, 0xFF, 0x01, 0x84, 0x09, 0xFF, 0xFF, + 0x14, 0x2F, 0x06, 0xCD, 0x9B, 0xCC, 0x8C, 0x89, 0x04, 0x1B, 0x42, 0x06, + 0x65, 0x64, 0x71, 0x3B, 0x44, 0x01, 0x06, 0x78, 0xE0, 0xE9, 0xD9, 0x9A, + 0x2B, 0x36, 0x72, 0xB0, 0x43, 0x84, 0x59, 0x1B, 0x4D, 0x0A, 0xB6, 0x75, + 0x87, 0x91, 0x32, 0xAB, 0x86, 0x02, 0xCC, 0xDE, 0x84, 0x07, 0x82, 0x31, + 0x85, 0xCC, 0x45, 0x90, 0x4A, 0x84, 0xE7, 0xC4, 0x9C, 0x42, 0xFC, 0x29, + 0x66, 0x0C, 0xA4, 0x94, 0x9D, 0xD9, 0x38, 0x75, 0x35, 0x42, 0x98, 0x07, + 0x6D, 0x70, 0x91, 0x1B, 0x03, 0x8E, 0x54, 0x40, 0x9B, 0xED, 0x89, 0x19, + 0x44, 0x87, 0x02, 0x9D, 0x8B, 0x5C, 0x3A, 0x15, 0x19, 0xF8, 0xB7, 0xAC, + 0x43, 0x01, 0x15, 0x93, 0x0B, 0x5D, 0x03, 0x83, 0x40, 0x1A, 0x56, 0xA5, + 0x1D, 0x03, 0xF2, 0x93, 0xA2, 0x50, 0x03, 0xB6, 0x11, 0x1C, 0xF2, 0x8E, + 0x8E, 0x60, 0x87, 0x42, 0x89, 0x93, 0x40, 0x93, 0x64, 0xA2, 0x81, 0x04, + 0x29, 0x23, 0x11, 0x4F, 0x36, 0xA0, 0xBA, 0x8E, 0xA2, 0xA4, 0x1A, 0x08, + 0x75, 0x65, 0x6E, 0x9C, 0xAC, 0x14, 0xC3, 0x18, 0x67, 0x33, 0x16, 0x4E, + 0x10, 0x31, 0x8C, 0x11, 0x9B, 0x17, 0x7C, 0xCC, 0x44, 0x57, 0x8F, 0xF7, + 0x74, 0x0E, 0x3F, 0x71, 0x80, 0x64, 0x08, 0x39, 0x90, 0xBC, 0x28, 0x87, + 0x3A, 0x15, 0x8B, 0x70, 0x30, 0x9D, 0x04, 0x14, 0x0B, 0x89, 0x1C, 0x95, + 0x21, 0x8F, 0x50, 0xB5, 0x2D, 0xD9, 0x85, 0x1B, 0x80, 0x72, 0xEC, 0x0A, + 0xCE, 0x6B, 0xA8, 0xDB, 0x86, 0x05, 0x35, 0x93, 0x84, 0x12, 0x8B, 0xE6, + 0xEC, 0x84, 0xBF, 0x79, 0x5F, 0xDA, 0xA4, 0xC8, 0x97, 0xF8, 0x84, 0x42, + 0x9B, 0x19, 0xB4, 0x96, 0xCB, 0xD4, 0x85, 0x21, 0x84, 0x08, 0xB3, 0x5B, + 0x1F, 0x88, 0x60, 0xCE, 0x85, 0x0F, 0x56, 0x92, 0x92, 0xA4, 0x05, 0xA6, + 0x43, 0x46, 0x9E, 0x94, 0x08, 0x8B, 0x58, 0x17, 0x3B, 0x87, 0xC9, 0xAA, + 0x86, 0x48, 0x83, 0x23, 0x12, 0x83, 0x52, 0x90, 0x89, 0x38, 0x4A, 0x50, + 0x89, 0x52, 0x32, 0x98, 0x45, 0xB9, 0x84, 0x48, 0x6D, 0x98, 0x17, 0x9E, + 0xA2, 0x37, 0x66, 0xC2, 0xA2, 0xB4, 0x5A, 0x2C, 0x54, 0xE5, 0xC5, 0x15, + 0x74, 0x44, 0x67, 0x06, 0x50, 0x6E, 0xE1, 0x06, 0x2F, 0x2D, 0xC8, 0x99, + 0x0D, 0xB6, 0x4D, 0x79, 0x24, 0x0E, 0x07, 0x1D, 0xE8, 0x91, 0x94, 0x0F, + 0xDE, 0x62, 0x3F, 0x60, 0x0F, 0x27, 0x63, 0xE5, 0x40, 0x78, 0x85, 0xB8, + 0x15, 0x09, 0xCA, 0x22, 0x2B, 0x19, 0x09, 0x53, 0xF9, 0x84, 0x69, 0x6D, + 0x32, 0x6B, 0xAE, 0x45, 0xCA, 0x90, 0xE6, 0x12, 0x6A, 0xB4, 0xA2, 0xD8, + 0x4C, 0xFA, 0xA1, 0x5D, 0xEF, 0xC2, 0xE4, 0x02, 0x00, 0xF9, 0x5B, 0x05, + 0x13, 0x6B, 0xC2, 0xB9, 0xAA, 0x17, 0xD8, 0xBA, 0xB1, 0xF9, 0xA0, 0x05, + 0x4C, 0x2F, 0x9A, 0x61, 0xBA, 0x52, 0x94, 0x63, 0xB9, 0x16, 0x9D, 0xA3, + 0x65, 0x84, 0x3D, 0x4A, 0x64, 0x9C, 0x96, 0x20, 0xF6, 0x30, 0x85, 0x68, + 0x5A, 0x50, 0x95, 0xE5, 0x30, 0xFD, 0x90, 0x1C, 0xE2, 0xE6, 0x43, 0x0B, + 0x6A, 0x9F, 0x23, 0x12, 0x27, 0xC2, 0x81, 0x9D, 0x55, 0x85, 0xC7, 0x01, + 0xA5, 0x58, 0x01, 0xC7, 0xD3, 0xF3, 0xFD, 0x87, 0x0B, 0x58, 0x7C, 0x52, + 0xCA, 0x6C, 0x47, 0xF1, 0x39, 0x19, 0x8A, 0x9A, 0xF7, 0x21, 0x0A, 0x67, + 0x06, 0xBE, 0xD3, 0x88, 0xCF, 0x47, 0x34, 0x94, 0x09, 0x04, 0x40, 0x64, + 0x09, 0xE6, 0x4F, 0xEA, 0xA5, 0x55, 0x4E, 0xA5, 0x95, 0xE5, 0x50, 0x0D, + 0x06, 0x46, 0x61, 0x85, 0x23, 0x08, 0xAB, 0xF9, 0x48, 0x09, 0x33, 0x44, + 0x50, 0x07, 0x21, 0x3C, 0xA8, 0x61, 0x9D, 0x34, 0x84, 0x22, 0x5C, 0x44, + 0xFD, 0xA0, 0x6D, 0xE2, 0x6E, 0x56, 0x3A, 0xA3, 0x19, 0x62, 0x4A, 0x82, + 0x0C, 0x98, 0x25, 0xCF, 0x24, 0xC8, 0x35, 0x35, 0x1A, 0x12, 0x59, 0x5D, + 0x98, 0xD6, 0x2E, 0x76, 0x7F, 0x2B, 0xF4, 0x30, 0x24, 0xAF, 0x4C, 0x5C, + 0x28, 0x2E, 0x28, 0xAA, 0x24, 0xE5, 0x17, 0xFF, 0x55, 0x42, 0xC9, 0x54, + 0x1B, 0x75, 0xDD, 0xBE, 0x47, 0x9B, 0x11, 0x22, 0x0E, 0xCC, 0xAA, 0x88, + 0x88, 0x66, 0x64, 0xCA, 0xD0, 0x88, 0x08, 0x41, 0x06, 0xF0, 0x17, 0x45, + 0x82, 0x07, 0xAE, 0x80, 0x9F, 0xB2, 0x23, 0x23, 0xAB, 0x8C, 0xE9, 0xFA, + 0xC3, 0x53, 0xD0, 0x06, 0x88, 0x67, 0x59, 0xB3, 0x92, 0xD2, 0xE9, 0xA8, + 0x82, 0x0A, 0xA7, 0xC9, 0x44, 0x8E, 0x47, 0x15, 0x82, 0x37, 0x80, 0x9B, + 0x88, 0xF9, 0x08, 0x27, 0xDC, 0x05, 0xC2, 0xD3, 0x94, 0x59, 0x55, 0x41, + 0x7F, 0x91, 0xFF, 0xDB, 0x16, 0x8C, 0x0C, 0x27, 0x76, 0x6E, 0x26, 0x81, + 0xAA, 0xA1, 0x8F, 0xC1, 0x20, 0x63, 0x0B, 0xC3, 0x89, 0x49, 0x31, 0x42, + 0x93, 0x8C, 0x69, 0x07, 0x7B, 0x3F, 0x9E, 0x0E, 0x45, 0x56, 0x85, 0x91, + 0xE9, 0x78, 0xCC, 0xF9, 0x08, 0xA4, 0xD5, 0x92, 0x70, 0x89, 0x16, 0xC3, + 0x0C, 0x92, 0x18, 0xA7, 0x59, 0xF2, 0x08, 0xCD, 0xE0, 0xF2, 0xA8, 0x97, + 0x4C, 0xF3, 0x83, 0xA7, 0x1C, 0x68, 0x54, 0x17, 0x03, 0x8A, 0x20, 0x57, + 0x50, 0x58, 0x31, 0x11, 0x72, 0x87, 0x71, 0x25, 0x2E, 0xE7, 0xE4, 0x79, + 0x90, 0x10, 0x50, 0x1D, 0x54, 0x88, 0xE1, 0xB7, 0x8F, 0x18, 0x02, 0x77, + 0xA8, 0x0C, 0x64, 0x11, 0x8C, 0xC9, 0x21, 0xE6, 0x07, 0xCB, 0x5E, 0x8F, + 0xB2, 0x25, 0x0B, 0x7B, 0x0A, 0x25, 0x8B, 0x95, 0xD0, 0x82, 0xF4, 0xE2, + 0x34, 0x20, 0xEA, 0x5F, 0x36, 0x8C, 0xA1, 0x94, 0x50, 0xFD, 0x69, 0x1B, + 0xCB, 0xB0, 0x8B, 0x4F, 0xBF, 0xB5, 0xC7, 0x0B, 0xC2, 0xC5, 0x17, 0xEE, + 0x8C, 0x91, 0xFB, 0xD3, 0x0D, 0xD1, 0xE1, 0x98, 0x3A, 0xC9, 0x4D, 0x08, + 0xF4, 0xB8, 0x59, 0x11, 0x78, 0x48, 0x20, 0x8B, 0x25, 0x08, 0xBF, 0xF2, + 0x89, 0x59, 0xDB, 0x31, 0xD6, 0x17, 0x9E, 0xCA, 0x74, 0x20, 0x8E, 0x0E, + 0xD5, 0x07, 0x3B, 0x5D, 0x51, 0x8E, 0x86, 0x71, 0xCE, 0xE6, 0x2C, 0x54, + 0xB2, 0xC6, 0x8B, 0x02, 0x7D, 0x7F, 0xC2, 0x61, 0xC2, 0x0B, 0xF0, 0x85, + 0x84, 0xCC, 0x98, 0x11, 0x4A, 0x8D, 0x66, 0x03, 0xF8, 0x54, 0x57, 0xB0, + 0xF0, 0x45, 0x8C, 0xFF, 0xD3, 0x61, 0xA8, 0x82, 0x09, 0xFF, 0x00, 0xCA, + 0x0B, 0x8C, 0x9B, 0x75, 0x24, 0x09, 0x07, 0x00, 0xF9, 0xAC, 0xC1, 0xBB, + 0xB7, 0x9E, 0xE8, 0x41, 0xE4, 0x2B, 0xCE, 0xAB, 0x2D, 0x9D, 0xEA, 0xFD, + 0x41, 0x00, 0x42, 0x8F, 0xC9, 0xF4, 0x87, 0x54, 0x61, 0x2D, 0x42, 0x51, + 0x3F, 0x52, 0x29, 0xCB, 0x87, 0x0F, 0x1F, 0xE4, 0x11, 0x33, 0x8E, 0xE5, + 0xCD, 0x86, 0x6F, 0x96, 0x30, 0x40, 0x06, 0xF5, 0x7F, 0x42, 0xB2, 0x61, + 0x93, 0x55, 0x87, 0xFE, 0x86, 0xE3, 0x67, 0x49, 0xA2, 0xC3, 0x1F, 0x6F, + 0xEF, 0x76, 0x4E, 0xE5, 0x12, 0x89, 0x3E, 0x10, 0xFF, 0x90, 0x99, 0xBF, + 0x40, 0x45, 0xAA, 0xFC, 0x10, 0xDC, 0xC1, 0xF1, 0x44, 0x13, 0x50, 0x58, + 0x29, 0x61, 0x28, 0x95, 0x70, 0x05, 0x7E, 0x21, 0x8D, 0xE3, 0x83, 0xB2, + 0x8F, 0xB2, 0xA2, 0x10, 0xE0, 0x93, 0xAC, 0xFD, 0x88, 0x38, 0x38, 0xC4, + 0x46, 0x23, 0x4A, 0x41, 0xF8, 0x0F, 0xA8, 0x12, 0xD5, 0x88, 0x7F, 0x92, + 0xA2, 0x21, 0xBE, 0xF1, 0x0A, 0x11, 0x92, 0x11, 0x99, 0xAB, 0xEA, 0xC3, + 0x85, 0x22, 0x1B, 0xA2, 0xDF, 0xE9, 0x7E, 0x18, 0x95, 0xB6, 0x48, 0xD1, + 0x9F, 0x50, 0xCF, 0x11, 0xBE, 0xF9, 0xB9, 0x1E, 0xC7, 0x9B, 0x00, 0xE3, + 0x62, 0xAA, 0x98, 0x0E, 0x01, 0x87, 0xC0, 0x27, 0x59, 0x53, 0x83, 0x47, + 0xBA, 0x96, 0xEA, 0xFF, 0x88, 0x68, 0xEA, 0x08, 0xD3, 0x58, 0xCE, 0x0F, + 0x05, 0x8A, 0xDA, 0x27, 0x6E, 0x15, 0x18, 0xF2, 0xAF, 0x38, 0xEA, 0x54, + 0xA0, 0x89, 0x74, 0xA1, 0x4B, 0x16, 0x9A, 0x14, 0x4D, 0x06, 0x85, 0x1D, + 0xEC, 0xEF, 0x52, 0x54, 0x93, 0x2C, 0x09, 0xA3, 0xE9, 0xC6, 0x1C, 0x75, + 0xAD, 0xD8, 0x14, 0x2E, 0x31, 0x58, 0x15, 0xCF, 0xB8, 0x44, 0x04, 0xAC, + 0x1C, 0xF0, 0x22, 0xA9, 0x3B, 0x99, 0xD9, 0x44, 0x9D, 0x8E, 0xF8, 0x05, + 0xE1, 0x04, 0x7B, 0x0B, 0x32, 0x13, 0x85, 0x22, 0xDE, 0x87, 0xAC, 0x27, + 0xAB, 0x2A, 0x66, 0x75, 0xA1, 0x9B, 0xC0, 0x51, 0xE1, 0x59, 0x43, 0x5B, + 0xC2, 0x37, 0xDF, 0x00, 0xD5, 0x98, 0x53, 0xE3, 0x95, 0xD0, 0xD2, 0x88, + 0xF6, 0x8F, 0xFD, 0x28, 0x5D, 0xFE, 0x50, 0x34, 0xF4, 0x85, 0xEA, 0xA1, + 0xC9, 0x0F, 0xA6, 0x72, 0xA8, 0x57, 0xF8, 0xEA, 0x50, 0x96, 0xC8, 0x84, + 0x1E, 0xF8, 0x1F, 0x2C, 0xEB, 0xA9, 0xA3, 0x64, 0xE1, 0xAC, 0xE4, 0x2B, + 0x50, 0xA4, 0x66, 0x54, 0x87, 0x63, 0xA2, 0x87, 0x00, 0x04, 0x29, 0x2F, + 0x1E, 0x89, 0x51, 0xC1, 0xC0, 0xD0, 0xA4, 0xC7, 0x14, 0x52, 0xBA, 0x58, + 0xF1, 0xC4, 0x41, 0x40, 0x57, 0xD5, 0xF2, 0xA6, 0x80, 0xEC, 0xB0, 0x30, + 0xB1, 0x71, 0xDB, 0x45, 0xA4, 0x86, 0xAC, 0xA7, 0x98, 0xD4, 0xB3, 0x26, + 0x42, 0xF1, 0x24, 0x31, 0x0C, 0x54, 0x4F, 0x68, 0x03, 0xB3, 0x0E, 0xD8, + 0xB7, 0xE5, 0x8D, 0x1C, 0x8B, 0xD5, 0x08, 0x2D, 0x9E, 0xD0, 0x9C, 0x06, + 0x62, 0xA1, 0xD5, 0x41, 0x3B, 0xA0, 0x5C, 0x7E, 0x92, 0x75, 0x84, 0x57, + 0x38, 0x41, 0xA0, 0x25, 0xB3, 0xA6, 0xA3, 0x89, 0x33, 0xC2, 0xD1, 0x60, + 0x72, 0x57, 0xB1, 0xC2, 0xC0, 0x2A, 0x79, 0xB0, 0xC2, 0x6C, 0x13, 0x01, + 0x51, 0x21, 0xCB, 0x13, 0xA3, 0x32, 0x3E, 0x16, 0x49, 0xEF, 0x6C, 0xF1, + 0x30, 0x90, 0x05, 0x5D, 0x85, 0x27, 0x1B, 0xAA, 0xC0, 0x74, 0x7A, 0x5C, + 0x37, 0x01, 0x27, 0x05, 0xEA, 0x3C, 0x4A, 0x4B, 0x9F, 0x0D, 0xC8, 0x18, + 0xA2, 0xD4, 0x20, 0x59, 0xCB, 0x19, 0x7D, 0x9C, 0x5F, 0x6C, 0x04, 0xCA, + 0x28, 0x6E, 0x6B, 0xAC, 0x7B, 0x0C, 0xCE, 0xE5, 0x9E, 0x49, 0x84, 0x18, + 0xDD, 0x1D, 0xAA, 0x39, 0x29, 0x9F, 0x30, 0xFA, 0x22, 0x67, 0x7C, 0x93, + 0x92, 0x09, 0x13, 0x52, 0x2F, 0x1E, 0x4E, 0x6E, 0x61, 0x05, 0x18, 0x85, + 0x07, 0xA9, 0x33, 0x1E, 0x68, 0xBD, 0x95, 0x31, 0x20, 0x8B, 0x2A, 0xA6, + 0x70, 0x96, 0xC4, 0x5E, 0x3D, 0x95, 0x50, 0x79, 0x67, 0x46, 0xCA, 0x50, + 0x20, 0xDF, 0x10, 0x2F, 0x08, 0xA0, 0x03, 0xBD, 0x2D, 0x50, 0xEE, 0x55, + 0x2C, 0xEC, 0xC9, 0x5F, 0xB9, 0x11, 0x44, 0xEF, 0x4A, 0x21, 0x74, 0x74, + 0x75, 0xF3, 0x86, 0xA2, 0x8A, 0x88, 0xA3, 0x77, 0x21, 0xC6, 0xC3, 0x21, + 0x27, 0x8A, 0x42, 0x2C, 0x16, 0x3A, 0x88, 0x8A, 0x7F, 0x73, 0xEA, 0xD8, + 0x79, 0x46, 0x1C, 0x67, 0x9B, 0x79, 0x61, 0x95, 0xF3, 0x44, 0x63, 0x65, + 0x12, 0x8C, 0xA5, 0xBB, 0x88, 0x12, 0x67, 0x9F, 0x90, 0x5D, 0x95, 0x66, + 0xA1, 0x9C, 0x57, 0x2A, 0x09, 0x34, 0x07, 0x7B, 0xD6, 0x02, 0xAC, 0x9A, + 0x43, 0x10, 0xB5, 0x0A, 0xA0, 0x17, 0x1C, 0xA6, 0x00, 0x9B}; + /* Data from bootstrap_font.pal inserted by mkbootstrap.lua: */ -constexpr std::array bootstrap_font_pal { -0x52, 0x4E, 0x43, 0x01, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x1D, 0xB7, -0xFF, 0x76, 0x79, 0xBE, 0xEF, 0x90, 0x10, 0x90, 0x05, 0x01, 0x02, 0x00, 0x00, -0x20, 0x2B, 0x04, 0x00, 0x00, 0x00, 0x40, 0x08, 0x00, 0xB6, 0x00, 0x00, 0x00, -0x3F, 0x3F, 0x3F, 0x62, 0x79, 0xBE, 0x27, 0x3F -}; +constexpr std::array bootstrap_font_pal{ + 0x52, 0x4E, 0x43, 0x01, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x1D, + 0xB7, 0xFF, 0x76, 0x79, 0xBE, 0xEF, 0x90, 0x10, 0x90, 0x05, 0x01, 0x02, + 0x00, 0x00, 0x20, 0x2B, 0x04, 0x00, 0x00, 0x00, 0x40, 0x08, 0x00, 0xB6, + 0x00, 0x00, 0x00, 0x3F, 0x3F, 0x3F, 0x62, 0x79, 0xBE, 0x27, 0x3F}; /* End autogenerated content */ // Lua reader function for loading bootstrap_code -const char* read_bootstrap_line(lua_State *L, void *data, size_t *size) -{ - int& iLine = *reinterpret_cast(data); - ++iLine; - if(iLine < 0 || (iLine & 1)) - { - *size = 1; - return "\n"; - } - else - { - const char *s = bootstrap_code[iLine / 2]; - if(s == nullptr) - { - *size = 0; - return nullptr; - } - else - { - *size = std::strlen(s); - return s; - } +const char* read_bootstrap_line(lua_State* L, void* data, size_t* size) { + int& iLine = *reinterpret_cast(data); + ++iLine; + if (iLine < 0 || (iLine & 1)) { + *size = 1; + return "\n"; + } else { + const char* s = bootstrap_code[iLine / 2]; + if (s == nullptr) { + *size = 0; + return nullptr; + } else { + *size = std::strlen(s); + return s; } + } } -template -inline void push(lua_State *L, Array data) -{ - lua_pushlstring(L, reinterpret_cast(data.data()), data.size()); +template +inline void push(lua_State* L, Array data) { + lua_pushlstring(L, reinterpret_cast(data.data()), data.size()); } -} // namespace +} // namespace -int bootstrap_lua_resources(lua_State *L) -{ - push(L, bootstrap_font_dat); - push(L, bootstrap_font_tab); - push(L, bootstrap_font_pal); - return 3; +int bootstrap_lua_resources(lua_State* L) { + push(L, bootstrap_font_dat); + push(L, bootstrap_font_tab); + push(L, bootstrap_font_pal); + return 3; } -int bootstrap_lua_error_report(lua_State *L) -{ - int iLine = -first_bootstrap_code_line_number; - if(luaT_load(L, read_bootstrap_line, &iLine, "@bootstrap.cpp", "t") == 0) - { - bootstrap_lua_resources(L); - lua_pushvalue(L, 1); - lua_call(L, 4, 0); - return 0; - } - else - return lua_error(L); +int bootstrap_lua_error_report(lua_State* L) { + int iLine = -first_bootstrap_code_line_number; + if (luaT_load(L, read_bootstrap_line, &iLine, "@bootstrap.cpp", "t") == 0) { + bootstrap_lua_resources(L); + lua_pushvalue(L, 1); + lua_call(L, 4, 0); + return 0; + } else + return lua_error(L); } diff --git a/CorsixTH/Src/bootstrap.h b/CorsixTH/Src/bootstrap.h index 69f3313c..4d9270ca 100644 --- a/CorsixTH/Src/bootstrap.h +++ b/CorsixTH/Src/bootstrap.h @@ -25,9 +25,9 @@ SOFTWARE. #include "lua.hpp" //! Push onto the stack the bootstrap font data file, table file and palette. -int bootstrap_lua_resources(lua_State *L); +int bootstrap_lua_resources(lua_State* L); //! Provide an onscreen report of the error message on the top of the stack. -int bootstrap_lua_error_report(lua_State *L); +int bootstrap_lua_error_report(lua_State* L); -#endif // CORSIX_TH_BOOTSTRAP_H_ +#endif // CORSIX_TH_BOOTSTRAP_H_ diff --git a/CorsixTH/Src/config.h.in b/CorsixTH/Src/config.h.in index b7ff2382..f29201d6 100644 --- a/CorsixTH/Src/config.h.in +++ b/CorsixTH/Src/config.h.in @@ -63,7 +63,7 @@ SOFTWARE. /** Standard includes **/ #ifndef __STDC_CONSTANT_MACROS -# define __STDC_CONSTANT_MACROS +#define __STDC_CONSTANT_MACROS #endif #include #include @@ -74,18 +74,20 @@ SOFTWARE. #endif // We bring in the most common stddef and stdint types to avoid typing +// clang-format off +using std::int8_t; +using std::int16_t; +using std::int32_t; +using std::int64_t; using std::size_t; using std::uint8_t; using std::uint16_t; using std::uint32_t; using std::uint64_t; -using std::int8_t; -using std::int16_t; -using std::int32_t; -using std::int64_t; +// clang-format on /** Visual Leak Detector **/ // In Visual Studio, Visual Leak Detector can be used to find memory leaks. #cmakedefine CORSIX_TH_USE_VLD -#endif // CORSIX_TH_CONFIG_H_ +#endif // CORSIX_TH_CONFIG_H_ diff --git a/CorsixTH/Src/cp437_table.h b/CorsixTH/Src/cp437_table.h index c6a87cdd..b236700f 100644 --- a/CorsixTH/Src/cp437_table.h +++ b/CorsixTH/Src/cp437_table.h @@ -3,7 +3,8 @@ #include -constexpr std::array cp437_to_unicode_table { +// clang-format off +constexpr std::array cp437_to_unicode_table{ /* 0x00 through 0x7F need no translation */ 0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7, 0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x00EC, 0x00C4, 0x00C5, @@ -20,7 +21,6 @@ constexpr std::array cp437_to_unicode_table { 0x03B1, 0x00DF, 0x0393, 0x03C0, 0x03A3, 0x03C3, 0x03BC, 0x03C4, 0x03A6, 0x0398, 0x03A9, 0x03B4, 0x221E, 0x03C6, 0x03B5, 0x2229, 0x2261, 0x00B1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00F7, 0x2248, - 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0 -}; - + 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0}; +// clang-format on #endif diff --git a/CorsixTH/Src/cp936_table.h b/CorsixTH/Src/cp936_table.h index b0339e63..64620049 100644 --- a/CorsixTH/Src/cp936_table.h +++ b/CorsixTH/Src/cp936_table.h @@ -8,3283 +8,2778 @@ // 1st byte is between 0x81 and 0xFE (subtract 0x81 for table lookup) // 2nd byte is between 0x40 and 0xFE (subtract 0x40 for table lookup) // Unspecified codes are mapped to 0x003F -constexpr std::array, 126> cp936_to_unicode_table {{ - { - 0x4E02, 0x4E04, 0x4E05, 0x4E06, 0x4E0F, 0x4E12, 0x4E17, 0x4E1F, - 0x4E20, 0x4E21, 0x4E23, 0x4E26, 0x4E29, 0x4E2E, 0x4E2F, 0x4E31, - 0x4E33, 0x4E35, 0x4E37, 0x4E3C, 0x4E40, 0x4E41, 0x4E42, 0x4E44, - 0x4E46, 0x4E4A, 0x4E51, 0x4E55, 0x4E57, 0x4E5A, 0x4E5B, 0x4E62, - 0x4E63, 0x4E64, 0x4E65, 0x4E67, 0x4E68, 0x4E6A, 0x4E6B, 0x4E6C, - 0x4E6D, 0x4E6E, 0x4E6F, 0x4E72, 0x4E74, 0x4E75, 0x4E76, 0x4E77, - 0x4E78, 0x4E79, 0x4E7A, 0x4E7B, 0x4E7C, 0x4E7D, 0x4E7F, 0x4E80, - 0x4E81, 0x4E82, 0x4E83, 0x4E84, 0x4E85, 0x4E87, 0x4E8A, 0x003F, - 0x4E90, 0x4E96, 0x4E97, 0x4E99, 0x4E9C, 0x4E9D, 0x4E9E, 0x4EA3, - 0x4EAA, 0x4EAF, 0x4EB0, 0x4EB1, 0x4EB4, 0x4EB6, 0x4EB7, 0x4EB8, - 0x4EB9, 0x4EBC, 0x4EBD, 0x4EBE, 0x4EC8, 0x4ECC, 0x4ECF, 0x4ED0, - 0x4ED2, 0x4EDA, 0x4EDB, 0x4EDC, 0x4EE0, 0x4EE2, 0x4EE6, 0x4EE7, - 0x4EE9, 0x4EED, 0x4EEE, 0x4EEF, 0x4EF1, 0x4EF4, 0x4EF8, 0x4EF9, - 0x4EFA, 0x4EFC, 0x4EFE, 0x4F00, 0x4F02, 0x4F03, 0x4F04, 0x4F05, - 0x4F06, 0x4F07, 0x4F08, 0x4F0B, 0x4F0C, 0x4F12, 0x4F13, 0x4F14, - 0x4F15, 0x4F16, 0x4F1C, 0x4F1D, 0x4F21, 0x4F23, 0x4F28, 0x4F29, - 0x4F2C, 0x4F2D, 0x4F2E, 0x4F31, 0x4F33, 0x4F35, 0x4F37, 0x4F39, - 0x4F3B, 0x4F3E, 0x4F3F, 0x4F40, 0x4F41, 0x4F42, 0x4F44, 0x4F45, - 0x4F47, 0x4F48, 0x4F49, 0x4F4A, 0x4F4B, 0x4F4C, 0x4F52, 0x4F54, - 0x4F56, 0x4F61, 0x4F62, 0x4F66, 0x4F68, 0x4F6A, 0x4F6B, 0x4F6D, - 0x4F6E, 0x4F71, 0x4F72, 0x4F75, 0x4F77, 0x4F78, 0x4F79, 0x4F7A, - 0x4F7D, 0x4F80, 0x4F81, 0x4F82, 0x4F85, 0x4F86, 0x4F87, 0x4F8A, - 0x4F8C, 0x4F8E, 0x4F90, 0x4F92, 0x4F93, 0x4F95, 0x4F96, 0x4F98, - 0x4F99, 0x4F9A, 0x4F9C, 0x4F9E, 0x4F9F, 0x4FA1, 0x4FA2 - }, - { - 0x4FA4, 0x4FAB, 0x4FAD, 0x4FB0, 0x4FB1, 0x4FB2, 0x4FB3, 0x4FB4, - 0x4FB6, 0x4FB7, 0x4FB8, 0x4FB9, 0x4FBA, 0x4FBB, 0x4FBC, 0x4FBD, - 0x4FBE, 0x4FC0, 0x4FC1, 0x4FC2, 0x4FC6, 0x4FC7, 0x4FC8, 0x4FC9, - 0x4FCB, 0x4FCC, 0x4FCD, 0x4FD2, 0x4FD3, 0x4FD4, 0x4FD5, 0x4FD6, - 0x4FD9, 0x4FDB, 0x4FE0, 0x4FE2, 0x4FE4, 0x4FE5, 0x4FE7, 0x4FEB, - 0x4FEC, 0x4FF0, 0x4FF2, 0x4FF4, 0x4FF5, 0x4FF6, 0x4FF7, 0x4FF9, - 0x4FFB, 0x4FFC, 0x4FFD, 0x4FFF, 0x5000, 0x5001, 0x5002, 0x5003, - 0x5004, 0x5005, 0x5006, 0x5007, 0x5008, 0x5009, 0x500A, 0x003F, - 0x500B, 0x500E, 0x5010, 0x5011, 0x5013, 0x5015, 0x5016, 0x5017, - 0x501B, 0x501D, 0x501E, 0x5020, 0x5022, 0x5023, 0x5024, 0x5027, - 0x502B, 0x502F, 0x5030, 0x5031, 0x5032, 0x5033, 0x5034, 0x5035, - 0x5036, 0x5037, 0x5038, 0x5039, 0x503B, 0x503D, 0x503F, 0x5040, - 0x5041, 0x5042, 0x5044, 0x5045, 0x5046, 0x5049, 0x504A, 0x504B, - 0x504D, 0x5050, 0x5051, 0x5052, 0x5053, 0x5054, 0x5056, 0x5057, - 0x5058, 0x5059, 0x505B, 0x505D, 0x505E, 0x505F, 0x5060, 0x5061, - 0x5062, 0x5063, 0x5064, 0x5066, 0x5067, 0x5068, 0x5069, 0x506A, - 0x506B, 0x506D, 0x506E, 0x506F, 0x5070, 0x5071, 0x5072, 0x5073, - 0x5074, 0x5075, 0x5078, 0x5079, 0x507A, 0x507C, 0x507D, 0x5081, - 0x5082, 0x5083, 0x5084, 0x5086, 0x5087, 0x5089, 0x508A, 0x508B, - 0x508C, 0x508E, 0x508F, 0x5090, 0x5091, 0x5092, 0x5093, 0x5094, - 0x5095, 0x5096, 0x5097, 0x5098, 0x5099, 0x509A, 0x509B, 0x509C, - 0x509D, 0x509E, 0x509F, 0x50A0, 0x50A1, 0x50A2, 0x50A4, 0x50A6, - 0x50AA, 0x50AB, 0x50AD, 0x50AE, 0x50AF, 0x50B0, 0x50B1, 0x50B3, - 0x50B4, 0x50B5, 0x50B6, 0x50B7, 0x50B8, 0x50B9, 0x50BC - }, - { - 0x50BD, 0x50BE, 0x50BF, 0x50C0, 0x50C1, 0x50C2, 0x50C3, 0x50C4, - 0x50C5, 0x50C6, 0x50C7, 0x50C8, 0x50C9, 0x50CA, 0x50CB, 0x50CC, - 0x50CD, 0x50CE, 0x50D0, 0x50D1, 0x50D2, 0x50D3, 0x50D4, 0x50D5, - 0x50D7, 0x50D8, 0x50D9, 0x50DB, 0x50DC, 0x50DD, 0x50DE, 0x50DF, - 0x50E0, 0x50E1, 0x50E2, 0x50E3, 0x50E4, 0x50E5, 0x50E8, 0x50E9, - 0x50EA, 0x50EB, 0x50EF, 0x50F0, 0x50F1, 0x50F2, 0x50F4, 0x50F6, - 0x50F7, 0x50F8, 0x50F9, 0x50FA, 0x50FC, 0x50FD, 0x50FE, 0x50FF, - 0x5100, 0x5101, 0x5102, 0x5103, 0x5104, 0x5105, 0x5108, 0x003F, - 0x5109, 0x510A, 0x510C, 0x510D, 0x510E, 0x510F, 0x5110, 0x5111, - 0x5113, 0x5114, 0x5115, 0x5116, 0x5117, 0x5118, 0x5119, 0x511A, - 0x511B, 0x511C, 0x511D, 0x511E, 0x511F, 0x5120, 0x5122, 0x5123, - 0x5124, 0x5125, 0x5126, 0x5127, 0x5128, 0x5129, 0x512A, 0x512B, - 0x512C, 0x512D, 0x512E, 0x512F, 0x5130, 0x5131, 0x5132, 0x5133, - 0x5134, 0x5135, 0x5136, 0x5137, 0x5138, 0x5139, 0x513A, 0x513B, - 0x513C, 0x513D, 0x513E, 0x5142, 0x5147, 0x514A, 0x514C, 0x514E, - 0x514F, 0x5150, 0x5152, 0x5153, 0x5157, 0x5158, 0x5159, 0x515B, - 0x515D, 0x515E, 0x515F, 0x5160, 0x5161, 0x5163, 0x5164, 0x5166, - 0x5167, 0x5169, 0x516A, 0x516F, 0x5172, 0x517A, 0x517E, 0x517F, - 0x5183, 0x5184, 0x5186, 0x5187, 0x518A, 0x518B, 0x518E, 0x518F, - 0x5190, 0x5191, 0x5193, 0x5194, 0x5198, 0x519A, 0x519D, 0x519E, - 0x519F, 0x51A1, 0x51A3, 0x51A6, 0x51A7, 0x51A8, 0x51A9, 0x51AA, - 0x51AD, 0x51AE, 0x51B4, 0x51B8, 0x51B9, 0x51BA, 0x51BE, 0x51BF, - 0x51C1, 0x51C2, 0x51C3, 0x51C5, 0x51C8, 0x51CA, 0x51CD, 0x51CE, - 0x51D0, 0x51D2, 0x51D3, 0x51D4, 0x51D5, 0x51D6, 0x51D7 - }, - { - 0x51D8, 0x51D9, 0x51DA, 0x51DC, 0x51DE, 0x51DF, 0x51E2, 0x51E3, - 0x51E5, 0x51E6, 0x51E7, 0x51E8, 0x51E9, 0x51EA, 0x51EC, 0x51EE, - 0x51F1, 0x51F2, 0x51F4, 0x51F7, 0x51FE, 0x5204, 0x5205, 0x5209, - 0x520B, 0x520C, 0x520F, 0x5210, 0x5213, 0x5214, 0x5215, 0x521C, - 0x521E, 0x521F, 0x5221, 0x5222, 0x5223, 0x5225, 0x5226, 0x5227, - 0x522A, 0x522C, 0x522F, 0x5231, 0x5232, 0x5234, 0x5235, 0x523C, - 0x523E, 0x5244, 0x5245, 0x5246, 0x5247, 0x5248, 0x5249, 0x524B, - 0x524E, 0x524F, 0x5252, 0x5253, 0x5255, 0x5257, 0x5258, 0x003F, - 0x5259, 0x525A, 0x525B, 0x525D, 0x525F, 0x5260, 0x5262, 0x5263, - 0x5264, 0x5266, 0x5268, 0x526B, 0x526C, 0x526D, 0x526E, 0x5270, - 0x5271, 0x5273, 0x5274, 0x5275, 0x5276, 0x5277, 0x5278, 0x5279, - 0x527A, 0x527B, 0x527C, 0x527E, 0x5280, 0x5283, 0x5284, 0x5285, - 0x5286, 0x5287, 0x5289, 0x528A, 0x528B, 0x528C, 0x528D, 0x528E, - 0x528F, 0x5291, 0x5292, 0x5294, 0x5295, 0x5296, 0x5297, 0x5298, - 0x5299, 0x529A, 0x529C, 0x52A4, 0x52A5, 0x52A6, 0x52A7, 0x52AE, - 0x52AF, 0x52B0, 0x52B4, 0x52B5, 0x52B6, 0x52B7, 0x52B8, 0x52B9, - 0x52BA, 0x52BB, 0x52BC, 0x52BD, 0x52C0, 0x52C1, 0x52C2, 0x52C4, - 0x52C5, 0x52C6, 0x52C8, 0x52CA, 0x52CC, 0x52CD, 0x52CE, 0x52CF, - 0x52D1, 0x52D3, 0x52D4, 0x52D5, 0x52D7, 0x52D9, 0x52DA, 0x52DB, - 0x52DC, 0x52DD, 0x52DE, 0x52E0, 0x52E1, 0x52E2, 0x52E3, 0x52E5, - 0x52E6, 0x52E7, 0x52E8, 0x52E9, 0x52EA, 0x52EB, 0x52EC, 0x52ED, - 0x52EE, 0x52EF, 0x52F1, 0x52F2, 0x52F3, 0x52F4, 0x52F5, 0x52F6, - 0x52F7, 0x52F8, 0x52FB, 0x52FC, 0x52FD, 0x5301, 0x5302, 0x5303, - 0x5304, 0x5307, 0x5309, 0x530A, 0x530B, 0x530C, 0x530E - }, - { - 0x5311, 0x5312, 0x5313, 0x5314, 0x5318, 0x531B, 0x531C, 0x531E, - 0x531F, 0x5322, 0x5324, 0x5325, 0x5327, 0x5328, 0x5329, 0x532B, - 0x532C, 0x532D, 0x532F, 0x5330, 0x5331, 0x5332, 0x5333, 0x5334, - 0x5335, 0x5336, 0x5337, 0x5338, 0x533C, 0x533D, 0x5340, 0x5342, - 0x5344, 0x5346, 0x534B, 0x534C, 0x534D, 0x5350, 0x5354, 0x5358, - 0x5359, 0x535B, 0x535D, 0x5365, 0x5368, 0x536A, 0x536C, 0x536D, - 0x5372, 0x5376, 0x5379, 0x537B, 0x537C, 0x537D, 0x537E, 0x5380, - 0x5381, 0x5383, 0x5387, 0x5388, 0x538A, 0x538E, 0x538F, 0x003F, - 0x5390, 0x5391, 0x5392, 0x5393, 0x5394, 0x5396, 0x5397, 0x5399, - 0x539B, 0x539C, 0x539E, 0x53A0, 0x53A1, 0x53A4, 0x53A7, 0x53AA, - 0x53AB, 0x53AC, 0x53AD, 0x53AF, 0x53B0, 0x53B1, 0x53B2, 0x53B3, - 0x53B4, 0x53B5, 0x53B7, 0x53B8, 0x53B9, 0x53BA, 0x53BC, 0x53BD, - 0x53BE, 0x53C0, 0x53C3, 0x53C4, 0x53C5, 0x53C6, 0x53C7, 0x53CE, - 0x53CF, 0x53D0, 0x53D2, 0x53D3, 0x53D5, 0x53DA, 0x53DC, 0x53DD, - 0x53DE, 0x53E1, 0x53E2, 0x53E7, 0x53F4, 0x53FA, 0x53FE, 0x53FF, - 0x5400, 0x5402, 0x5405, 0x5407, 0x540B, 0x5414, 0x5418, 0x5419, - 0x541A, 0x541C, 0x5422, 0x5424, 0x5425, 0x542A, 0x5430, 0x5433, - 0x5436, 0x5437, 0x543A, 0x543D, 0x543F, 0x5441, 0x5442, 0x5444, - 0x5445, 0x5447, 0x5449, 0x544C, 0x544D, 0x544E, 0x544F, 0x5451, - 0x545A, 0x545D, 0x545E, 0x545F, 0x5460, 0x5461, 0x5463, 0x5465, - 0x5467, 0x5469, 0x546A, 0x546B, 0x546C, 0x546D, 0x546E, 0x546F, - 0x5470, 0x5474, 0x5479, 0x547A, 0x547E, 0x547F, 0x5481, 0x5483, - 0x5485, 0x5487, 0x5488, 0x5489, 0x548A, 0x548D, 0x5491, 0x5493, - 0x5497, 0x5498, 0x549C, 0x549E, 0x549F, 0x54A0, 0x54A1 - }, - { - 0x54A2, 0x54A5, 0x54AE, 0x54B0, 0x54B2, 0x54B5, 0x54B6, 0x54B7, - 0x54B9, 0x54BA, 0x54BC, 0x54BE, 0x54C3, 0x54C5, 0x54CA, 0x54CB, - 0x54D6, 0x54D8, 0x54DB, 0x54E0, 0x54E1, 0x54E2, 0x54E3, 0x54E4, - 0x54EB, 0x54EC, 0x54EF, 0x54F0, 0x54F1, 0x54F4, 0x54F5, 0x54F6, - 0x54F7, 0x54F8, 0x54F9, 0x54FB, 0x54FE, 0x5500, 0x5502, 0x5503, - 0x5504, 0x5505, 0x5508, 0x550A, 0x550B, 0x550C, 0x550D, 0x550E, - 0x5512, 0x5513, 0x5515, 0x5516, 0x5517, 0x5518, 0x5519, 0x551A, - 0x551C, 0x551D, 0x551E, 0x551F, 0x5521, 0x5525, 0x5526, 0x003F, - 0x5528, 0x5529, 0x552B, 0x552D, 0x5532, 0x5534, 0x5535, 0x5536, - 0x5538, 0x5539, 0x553A, 0x553B, 0x553D, 0x5540, 0x5542, 0x5545, - 0x5547, 0x5548, 0x554B, 0x554C, 0x554D, 0x554E, 0x554F, 0x5551, - 0x5552, 0x5553, 0x5554, 0x5557, 0x5558, 0x5559, 0x555A, 0x555B, - 0x555D, 0x555E, 0x555F, 0x5560, 0x5562, 0x5563, 0x5568, 0x5569, - 0x556B, 0x556F, 0x5570, 0x5571, 0x5572, 0x5573, 0x5574, 0x5579, - 0x557A, 0x557D, 0x557F, 0x5585, 0x5586, 0x558C, 0x558D, 0x558E, - 0x5590, 0x5592, 0x5593, 0x5595, 0x5596, 0x5597, 0x559A, 0x559B, - 0x559E, 0x55A0, 0x55A1, 0x55A2, 0x55A3, 0x55A4, 0x55A5, 0x55A6, - 0x55A8, 0x55A9, 0x55AA, 0x55AB, 0x55AC, 0x55AD, 0x55AE, 0x55AF, - 0x55B0, 0x55B2, 0x55B4, 0x55B6, 0x55B8, 0x55BA, 0x55BC, 0x55BF, - 0x55C0, 0x55C1, 0x55C2, 0x55C3, 0x55C6, 0x55C7, 0x55C8, 0x55CA, - 0x55CB, 0x55CE, 0x55CF, 0x55D0, 0x55D5, 0x55D7, 0x55D8, 0x55D9, - 0x55DA, 0x55DB, 0x55DE, 0x55E0, 0x55E2, 0x55E7, 0x55E9, 0x55ED, - 0x55EE, 0x55F0, 0x55F1, 0x55F4, 0x55F6, 0x55F8, 0x55F9, 0x55FA, - 0x55FB, 0x55FC, 0x55FF, 0x5602, 0x5603, 0x5604, 0x5605 - }, - { - 0x5606, 0x5607, 0x560A, 0x560B, 0x560D, 0x5610, 0x5611, 0x5612, - 0x5613, 0x5614, 0x5615, 0x5616, 0x5617, 0x5619, 0x561A, 0x561C, - 0x561D, 0x5620, 0x5621, 0x5622, 0x5625, 0x5626, 0x5628, 0x5629, - 0x562A, 0x562B, 0x562E, 0x562F, 0x5630, 0x5633, 0x5635, 0x5637, - 0x5638, 0x563A, 0x563C, 0x563D, 0x563E, 0x5640, 0x5641, 0x5642, - 0x5643, 0x5644, 0x5645, 0x5646, 0x5647, 0x5648, 0x5649, 0x564A, - 0x564B, 0x564F, 0x5650, 0x5651, 0x5652, 0x5653, 0x5655, 0x5656, - 0x565A, 0x565B, 0x565D, 0x565E, 0x565F, 0x5660, 0x5661, 0x003F, - 0x5663, 0x5665, 0x5666, 0x5667, 0x566D, 0x566E, 0x566F, 0x5670, - 0x5672, 0x5673, 0x5674, 0x5675, 0x5677, 0x5678, 0x5679, 0x567A, - 0x567D, 0x567E, 0x567F, 0x5680, 0x5681, 0x5682, 0x5683, 0x5684, - 0x5687, 0x5688, 0x5689, 0x568A, 0x568B, 0x568C, 0x568D, 0x5690, - 0x5691, 0x5692, 0x5694, 0x5695, 0x5696, 0x5697, 0x5698, 0x5699, - 0x569A, 0x569B, 0x569C, 0x569D, 0x569E, 0x569F, 0x56A0, 0x56A1, - 0x56A2, 0x56A4, 0x56A5, 0x56A6, 0x56A7, 0x56A8, 0x56A9, 0x56AA, - 0x56AB, 0x56AC, 0x56AD, 0x56AE, 0x56B0, 0x56B1, 0x56B2, 0x56B3, - 0x56B4, 0x56B5, 0x56B6, 0x56B8, 0x56B9, 0x56BA, 0x56BB, 0x56BD, - 0x56BE, 0x56BF, 0x56C0, 0x56C1, 0x56C2, 0x56C3, 0x56C4, 0x56C5, - 0x56C6, 0x56C7, 0x56C8, 0x56C9, 0x56CB, 0x56CC, 0x56CD, 0x56CE, - 0x56CF, 0x56D0, 0x56D1, 0x56D2, 0x56D3, 0x56D5, 0x56D6, 0x56D8, - 0x56D9, 0x56DC, 0x56E3, 0x56E5, 0x56E6, 0x56E7, 0x56E8, 0x56E9, - 0x56EA, 0x56EC, 0x56EE, 0x56EF, 0x56F2, 0x56F3, 0x56F6, 0x56F7, - 0x56F8, 0x56FB, 0x56FC, 0x5700, 0x5701, 0x5702, 0x5705, 0x5707, - 0x570B, 0x570C, 0x570D, 0x570E, 0x570F, 0x5710, 0x5711 - }, - { - 0x5712, 0x5713, 0x5714, 0x5715, 0x5716, 0x5717, 0x5718, 0x5719, - 0x571A, 0x571B, 0x571D, 0x571E, 0x5720, 0x5721, 0x5722, 0x5724, - 0x5725, 0x5726, 0x5727, 0x572B, 0x5731, 0x5732, 0x5734, 0x5735, - 0x5736, 0x5737, 0x5738, 0x573C, 0x573D, 0x573F, 0x5741, 0x5743, - 0x5744, 0x5745, 0x5746, 0x5748, 0x5749, 0x574B, 0x5752, 0x5753, - 0x5754, 0x5755, 0x5756, 0x5758, 0x5759, 0x5762, 0x5763, 0x5765, - 0x5767, 0x576C, 0x576E, 0x5770, 0x5771, 0x5772, 0x5774, 0x5775, - 0x5778, 0x5779, 0x577A, 0x577D, 0x577E, 0x577F, 0x5780, 0x003F, - 0x5781, 0x5787, 0x5788, 0x5789, 0x578A, 0x578D, 0x578E, 0x578F, - 0x5790, 0x5791, 0x5794, 0x5795, 0x5796, 0x5797, 0x5798, 0x5799, - 0x579A, 0x579C, 0x579D, 0x579E, 0x579F, 0x57A5, 0x57A8, 0x57AA, - 0x57AC, 0x57AF, 0x57B0, 0x57B1, 0x57B3, 0x57B5, 0x57B6, 0x57B7, - 0x57B9, 0x57BA, 0x57BB, 0x57BC, 0x57BD, 0x57BE, 0x57BF, 0x57C0, - 0x57C1, 0x57C4, 0x57C5, 0x57C6, 0x57C7, 0x57C8, 0x57C9, 0x57CA, - 0x57CC, 0x57CD, 0x57D0, 0x57D1, 0x57D3, 0x57D6, 0x57D7, 0x57DB, - 0x57DC, 0x57DE, 0x57E1, 0x57E2, 0x57E3, 0x57E5, 0x57E6, 0x57E7, - 0x57E8, 0x57E9, 0x57EA, 0x57EB, 0x57EC, 0x57EE, 0x57F0, 0x57F1, - 0x57F2, 0x57F3, 0x57F5, 0x57F6, 0x57F7, 0x57FB, 0x57FC, 0x57FE, - 0x57FF, 0x5801, 0x5803, 0x5804, 0x5805, 0x5808, 0x5809, 0x580A, - 0x580C, 0x580E, 0x580F, 0x5810, 0x5812, 0x5813, 0x5814, 0x5816, - 0x5817, 0x5818, 0x581A, 0x581B, 0x581C, 0x581D, 0x581F, 0x5822, - 0x5823, 0x5825, 0x5826, 0x5827, 0x5828, 0x5829, 0x582B, 0x582C, - 0x582D, 0x582E, 0x582F, 0x5831, 0x5832, 0x5833, 0x5834, 0x5836, - 0x5837, 0x5838, 0x5839, 0x583A, 0x583B, 0x583C, 0x583D - }, - { - 0x583E, 0x583F, 0x5840, 0x5841, 0x5842, 0x5843, 0x5845, 0x5846, - 0x5847, 0x5848, 0x5849, 0x584A, 0x584B, 0x584E, 0x584F, 0x5850, - 0x5852, 0x5853, 0x5855, 0x5856, 0x5857, 0x5859, 0x585A, 0x585B, - 0x585C, 0x585D, 0x585F, 0x5860, 0x5861, 0x5862, 0x5863, 0x5864, - 0x5866, 0x5867, 0x5868, 0x5869, 0x586A, 0x586D, 0x586E, 0x586F, - 0x5870, 0x5871, 0x5872, 0x5873, 0x5874, 0x5875, 0x5876, 0x5877, - 0x5878, 0x5879, 0x587A, 0x587B, 0x587C, 0x587D, 0x587F, 0x5882, - 0x5884, 0x5886, 0x5887, 0x5888, 0x588A, 0x588B, 0x588C, 0x003F, - 0x588D, 0x588E, 0x588F, 0x5890, 0x5891, 0x5894, 0x5895, 0x5896, - 0x5897, 0x5898, 0x589B, 0x589C, 0x589D, 0x58A0, 0x58A1, 0x58A2, - 0x58A3, 0x58A4, 0x58A5, 0x58A6, 0x58A7, 0x58AA, 0x58AB, 0x58AC, - 0x58AD, 0x58AE, 0x58AF, 0x58B0, 0x58B1, 0x58B2, 0x58B3, 0x58B4, - 0x58B5, 0x58B6, 0x58B7, 0x58B8, 0x58B9, 0x58BA, 0x58BB, 0x58BD, - 0x58BE, 0x58BF, 0x58C0, 0x58C2, 0x58C3, 0x58C4, 0x58C6, 0x58C7, - 0x58C8, 0x58C9, 0x58CA, 0x58CB, 0x58CC, 0x58CD, 0x58CE, 0x58CF, - 0x58D0, 0x58D2, 0x58D3, 0x58D4, 0x58D6, 0x58D7, 0x58D8, 0x58D9, - 0x58DA, 0x58DB, 0x58DC, 0x58DD, 0x58DE, 0x58DF, 0x58E0, 0x58E1, - 0x58E2, 0x58E3, 0x58E5, 0x58E6, 0x58E7, 0x58E8, 0x58E9, 0x58EA, - 0x58ED, 0x58EF, 0x58F1, 0x58F2, 0x58F4, 0x58F5, 0x58F7, 0x58F8, - 0x58FA, 0x58FB, 0x58FC, 0x58FD, 0x58FE, 0x58FF, 0x5900, 0x5901, - 0x5903, 0x5905, 0x5906, 0x5908, 0x5909, 0x590A, 0x590B, 0x590C, - 0x590E, 0x5910, 0x5911, 0x5912, 0x5913, 0x5917, 0x5918, 0x591B, - 0x591D, 0x591E, 0x5920, 0x5921, 0x5922, 0x5923, 0x5926, 0x5928, - 0x592C, 0x5930, 0x5932, 0x5933, 0x5935, 0x5936, 0x593B - }, - { - 0x593D, 0x593E, 0x593F, 0x5940, 0x5943, 0x5945, 0x5946, 0x594A, - 0x594C, 0x594D, 0x5950, 0x5952, 0x5953, 0x5959, 0x595B, 0x595C, - 0x595D, 0x595E, 0x595F, 0x5961, 0x5963, 0x5964, 0x5966, 0x5967, - 0x5968, 0x5969, 0x596A, 0x596B, 0x596C, 0x596D, 0x596E, 0x596F, - 0x5970, 0x5971, 0x5972, 0x5975, 0x5977, 0x597A, 0x597B, 0x597C, - 0x597E, 0x597F, 0x5980, 0x5985, 0x5989, 0x598B, 0x598C, 0x598E, - 0x598F, 0x5990, 0x5991, 0x5994, 0x5995, 0x5998, 0x599A, 0x599B, - 0x599C, 0x599D, 0x599F, 0x59A0, 0x59A1, 0x59A2, 0x59A6, 0x003F, - 0x59A7, 0x59AC, 0x59AD, 0x59B0, 0x59B1, 0x59B3, 0x59B4, 0x59B5, - 0x59B6, 0x59B7, 0x59B8, 0x59BA, 0x59BC, 0x59BD, 0x59BF, 0x59C0, - 0x59C1, 0x59C2, 0x59C3, 0x59C4, 0x59C5, 0x59C7, 0x59C8, 0x59C9, - 0x59CC, 0x59CD, 0x59CE, 0x59CF, 0x59D5, 0x59D6, 0x59D9, 0x59DB, - 0x59DE, 0x59DF, 0x59E0, 0x59E1, 0x59E2, 0x59E4, 0x59E6, 0x59E7, - 0x59E9, 0x59EA, 0x59EB, 0x59ED, 0x59EE, 0x59EF, 0x59F0, 0x59F1, - 0x59F2, 0x59F3, 0x59F4, 0x59F5, 0x59F6, 0x59F7, 0x59F8, 0x59FA, - 0x59FC, 0x59FD, 0x59FE, 0x5A00, 0x5A02, 0x5A0A, 0x5A0B, 0x5A0D, - 0x5A0E, 0x5A0F, 0x5A10, 0x5A12, 0x5A14, 0x5A15, 0x5A16, 0x5A17, - 0x5A19, 0x5A1A, 0x5A1B, 0x5A1D, 0x5A1E, 0x5A21, 0x5A22, 0x5A24, - 0x5A26, 0x5A27, 0x5A28, 0x5A2A, 0x5A2B, 0x5A2C, 0x5A2D, 0x5A2E, - 0x5A2F, 0x5A30, 0x5A33, 0x5A35, 0x5A37, 0x5A38, 0x5A39, 0x5A3A, - 0x5A3B, 0x5A3D, 0x5A3E, 0x5A3F, 0x5A41, 0x5A42, 0x5A43, 0x5A44, - 0x5A45, 0x5A47, 0x5A48, 0x5A4B, 0x5A4C, 0x5A4D, 0x5A4E, 0x5A4F, - 0x5A50, 0x5A51, 0x5A52, 0x5A53, 0x5A54, 0x5A56, 0x5A57, 0x5A58, - 0x5A59, 0x5A5B, 0x5A5C, 0x5A5D, 0x5A5E, 0x5A5F, 0x5A60 - }, - { - 0x5A61, 0x5A63, 0x5A64, 0x5A65, 0x5A66, 0x5A68, 0x5A69, 0x5A6B, - 0x5A6C, 0x5A6D, 0x5A6E, 0x5A6F, 0x5A70, 0x5A71, 0x5A72, 0x5A73, - 0x5A78, 0x5A79, 0x5A7B, 0x5A7C, 0x5A7D, 0x5A7E, 0x5A80, 0x5A81, - 0x5A82, 0x5A83, 0x5A84, 0x5A85, 0x5A86, 0x5A87, 0x5A88, 0x5A89, - 0x5A8A, 0x5A8B, 0x5A8C, 0x5A8D, 0x5A8E, 0x5A8F, 0x5A90, 0x5A91, - 0x5A93, 0x5A94, 0x5A95, 0x5A96, 0x5A97, 0x5A98, 0x5A99, 0x5A9C, - 0x5A9D, 0x5A9E, 0x5A9F, 0x5AA0, 0x5AA1, 0x5AA2, 0x5AA3, 0x5AA4, - 0x5AA5, 0x5AA6, 0x5AA7, 0x5AA8, 0x5AA9, 0x5AAB, 0x5AAC, 0x003F, - 0x5AAD, 0x5AAE, 0x5AAF, 0x5AB0, 0x5AB1, 0x5AB4, 0x5AB6, 0x5AB7, - 0x5AB9, 0x5ABA, 0x5ABB, 0x5ABC, 0x5ABD, 0x5ABF, 0x5AC0, 0x5AC3, - 0x5AC4, 0x5AC5, 0x5AC6, 0x5AC7, 0x5AC8, 0x5ACA, 0x5ACB, 0x5ACD, - 0x5ACE, 0x5ACF, 0x5AD0, 0x5AD1, 0x5AD3, 0x5AD5, 0x5AD7, 0x5AD9, - 0x5ADA, 0x5ADB, 0x5ADD, 0x5ADE, 0x5ADF, 0x5AE2, 0x5AE4, 0x5AE5, - 0x5AE7, 0x5AE8, 0x5AEA, 0x5AEC, 0x5AED, 0x5AEE, 0x5AEF, 0x5AF0, - 0x5AF2, 0x5AF3, 0x5AF4, 0x5AF5, 0x5AF6, 0x5AF7, 0x5AF8, 0x5AF9, - 0x5AFA, 0x5AFB, 0x5AFC, 0x5AFD, 0x5AFE, 0x5AFF, 0x5B00, 0x5B01, - 0x5B02, 0x5B03, 0x5B04, 0x5B05, 0x5B06, 0x5B07, 0x5B08, 0x5B0A, - 0x5B0B, 0x5B0C, 0x5B0D, 0x5B0E, 0x5B0F, 0x5B10, 0x5B11, 0x5B12, - 0x5B13, 0x5B14, 0x5B15, 0x5B18, 0x5B19, 0x5B1A, 0x5B1B, 0x5B1C, - 0x5B1D, 0x5B1E, 0x5B1F, 0x5B20, 0x5B21, 0x5B22, 0x5B23, 0x5B24, - 0x5B25, 0x5B26, 0x5B27, 0x5B28, 0x5B29, 0x5B2A, 0x5B2B, 0x5B2C, - 0x5B2D, 0x5B2E, 0x5B2F, 0x5B30, 0x5B31, 0x5B33, 0x5B35, 0x5B36, - 0x5B38, 0x5B39, 0x5B3A, 0x5B3B, 0x5B3C, 0x5B3D, 0x5B3E, 0x5B3F, - 0x5B41, 0x5B42, 0x5B43, 0x5B44, 0x5B45, 0x5B46, 0x5B47 - }, - { - 0x5B48, 0x5B49, 0x5B4A, 0x5B4B, 0x5B4C, 0x5B4D, 0x5B4E, 0x5B4F, - 0x5B52, 0x5B56, 0x5B5E, 0x5B60, 0x5B61, 0x5B67, 0x5B68, 0x5B6B, - 0x5B6D, 0x5B6E, 0x5B6F, 0x5B72, 0x5B74, 0x5B76, 0x5B77, 0x5B78, - 0x5B79, 0x5B7B, 0x5B7C, 0x5B7E, 0x5B7F, 0x5B82, 0x5B86, 0x5B8A, - 0x5B8D, 0x5B8E, 0x5B90, 0x5B91, 0x5B92, 0x5B94, 0x5B96, 0x5B9F, - 0x5BA7, 0x5BA8, 0x5BA9, 0x5BAC, 0x5BAD, 0x5BAE, 0x5BAF, 0x5BB1, - 0x5BB2, 0x5BB7, 0x5BBA, 0x5BBB, 0x5BBC, 0x5BC0, 0x5BC1, 0x5BC3, - 0x5BC8, 0x5BC9, 0x5BCA, 0x5BCB, 0x5BCD, 0x5BCE, 0x5BCF, 0x003F, - 0x5BD1, 0x5BD4, 0x5BD5, 0x5BD6, 0x5BD7, 0x5BD8, 0x5BD9, 0x5BDA, - 0x5BDB, 0x5BDC, 0x5BE0, 0x5BE2, 0x5BE3, 0x5BE6, 0x5BE7, 0x5BE9, - 0x5BEA, 0x5BEB, 0x5BEC, 0x5BED, 0x5BEF, 0x5BF1, 0x5BF2, 0x5BF3, - 0x5BF4, 0x5BF5, 0x5BF6, 0x5BF7, 0x5BFD, 0x5BFE, 0x5C00, 0x5C02, - 0x5C03, 0x5C05, 0x5C07, 0x5C08, 0x5C0B, 0x5C0C, 0x5C0D, 0x5C0E, - 0x5C10, 0x5C12, 0x5C13, 0x5C17, 0x5C19, 0x5C1B, 0x5C1E, 0x5C1F, - 0x5C20, 0x5C21, 0x5C23, 0x5C26, 0x5C28, 0x5C29, 0x5C2A, 0x5C2B, - 0x5C2D, 0x5C2E, 0x5C2F, 0x5C30, 0x5C32, 0x5C33, 0x5C35, 0x5C36, - 0x5C37, 0x5C43, 0x5C44, 0x5C46, 0x5C47, 0x5C4C, 0x5C4D, 0x5C52, - 0x5C53, 0x5C54, 0x5C56, 0x5C57, 0x5C58, 0x5C5A, 0x5C5B, 0x5C5C, - 0x5C5D, 0x5C5F, 0x5C62, 0x5C64, 0x5C67, 0x5C68, 0x5C69, 0x5C6A, - 0x5C6B, 0x5C6C, 0x5C6D, 0x5C70, 0x5C72, 0x5C73, 0x5C74, 0x5C75, - 0x5C76, 0x5C77, 0x5C78, 0x5C7B, 0x5C7C, 0x5C7D, 0x5C7E, 0x5C80, - 0x5C83, 0x5C84, 0x5C85, 0x5C86, 0x5C87, 0x5C89, 0x5C8A, 0x5C8B, - 0x5C8E, 0x5C8F, 0x5C92, 0x5C93, 0x5C95, 0x5C9D, 0x5C9E, 0x5C9F, - 0x5CA0, 0x5CA1, 0x5CA4, 0x5CA5, 0x5CA6, 0x5CA7, 0x5CA8 - }, - { - 0x5CAA, 0x5CAE, 0x5CAF, 0x5CB0, 0x5CB2, 0x5CB4, 0x5CB6, 0x5CB9, - 0x5CBA, 0x5CBB, 0x5CBC, 0x5CBE, 0x5CC0, 0x5CC2, 0x5CC3, 0x5CC5, - 0x5CC6, 0x5CC7, 0x5CC8, 0x5CC9, 0x5CCA, 0x5CCC, 0x5CCD, 0x5CCE, - 0x5CCF, 0x5CD0, 0x5CD1, 0x5CD3, 0x5CD4, 0x5CD5, 0x5CD6, 0x5CD7, - 0x5CD8, 0x5CDA, 0x5CDB, 0x5CDC, 0x5CDD, 0x5CDE, 0x5CDF, 0x5CE0, - 0x5CE2, 0x5CE3, 0x5CE7, 0x5CE9, 0x5CEB, 0x5CEC, 0x5CEE, 0x5CEF, - 0x5CF1, 0x5CF2, 0x5CF3, 0x5CF4, 0x5CF5, 0x5CF6, 0x5CF7, 0x5CF8, - 0x5CF9, 0x5CFA, 0x5CFC, 0x5CFD, 0x5CFE, 0x5CFF, 0x5D00, 0x003F, - 0x5D01, 0x5D04, 0x5D05, 0x5D08, 0x5D09, 0x5D0A, 0x5D0B, 0x5D0C, - 0x5D0D, 0x5D0F, 0x5D10, 0x5D11, 0x5D12, 0x5D13, 0x5D15, 0x5D17, - 0x5D18, 0x5D19, 0x5D1A, 0x5D1C, 0x5D1D, 0x5D1F, 0x5D20, 0x5D21, - 0x5D22, 0x5D23, 0x5D25, 0x5D28, 0x5D2A, 0x5D2B, 0x5D2C, 0x5D2F, - 0x5D30, 0x5D31, 0x5D32, 0x5D33, 0x5D35, 0x5D36, 0x5D37, 0x5D38, - 0x5D39, 0x5D3A, 0x5D3B, 0x5D3C, 0x5D3F, 0x5D40, 0x5D41, 0x5D42, - 0x5D43, 0x5D44, 0x5D45, 0x5D46, 0x5D48, 0x5D49, 0x5D4D, 0x5D4E, - 0x5D4F, 0x5D50, 0x5D51, 0x5D52, 0x5D53, 0x5D54, 0x5D55, 0x5D56, - 0x5D57, 0x5D59, 0x5D5A, 0x5D5C, 0x5D5E, 0x5D5F, 0x5D60, 0x5D61, - 0x5D62, 0x5D63, 0x5D64, 0x5D65, 0x5D66, 0x5D67, 0x5D68, 0x5D6A, - 0x5D6D, 0x5D6E, 0x5D70, 0x5D71, 0x5D72, 0x5D73, 0x5D75, 0x5D76, - 0x5D77, 0x5D78, 0x5D79, 0x5D7A, 0x5D7B, 0x5D7C, 0x5D7D, 0x5D7E, - 0x5D7F, 0x5D80, 0x5D81, 0x5D83, 0x5D84, 0x5D85, 0x5D86, 0x5D87, - 0x5D88, 0x5D89, 0x5D8A, 0x5D8B, 0x5D8C, 0x5D8D, 0x5D8E, 0x5D8F, - 0x5D90, 0x5D91, 0x5D92, 0x5D93, 0x5D94, 0x5D95, 0x5D96, 0x5D97, - 0x5D98, 0x5D9A, 0x5D9B, 0x5D9C, 0x5D9E, 0x5D9F, 0x5DA0 - }, - { - 0x5DA1, 0x5DA2, 0x5DA3, 0x5DA4, 0x5DA5, 0x5DA6, 0x5DA7, 0x5DA8, - 0x5DA9, 0x5DAA, 0x5DAB, 0x5DAC, 0x5DAD, 0x5DAE, 0x5DAF, 0x5DB0, - 0x5DB1, 0x5DB2, 0x5DB3, 0x5DB4, 0x5DB5, 0x5DB6, 0x5DB8, 0x5DB9, - 0x5DBA, 0x5DBB, 0x5DBC, 0x5DBD, 0x5DBE, 0x5DBF, 0x5DC0, 0x5DC1, - 0x5DC2, 0x5DC3, 0x5DC4, 0x5DC6, 0x5DC7, 0x5DC8, 0x5DC9, 0x5DCA, - 0x5DCB, 0x5DCC, 0x5DCE, 0x5DCF, 0x5DD0, 0x5DD1, 0x5DD2, 0x5DD3, - 0x5DD4, 0x5DD5, 0x5DD6, 0x5DD7, 0x5DD8, 0x5DD9, 0x5DDA, 0x5DDC, - 0x5DDF, 0x5DE0, 0x5DE3, 0x5DE4, 0x5DEA, 0x5DEC, 0x5DED, 0x003F, - 0x5DF0, 0x5DF5, 0x5DF6, 0x5DF8, 0x5DF9, 0x5DFA, 0x5DFB, 0x5DFC, - 0x5DFF, 0x5E00, 0x5E04, 0x5E07, 0x5E09, 0x5E0A, 0x5E0B, 0x5E0D, - 0x5E0E, 0x5E12, 0x5E13, 0x5E17, 0x5E1E, 0x5E1F, 0x5E20, 0x5E21, - 0x5E22, 0x5E23, 0x5E24, 0x5E25, 0x5E28, 0x5E29, 0x5E2A, 0x5E2B, - 0x5E2C, 0x5E2F, 0x5E30, 0x5E32, 0x5E33, 0x5E34, 0x5E35, 0x5E36, - 0x5E39, 0x5E3A, 0x5E3E, 0x5E3F, 0x5E40, 0x5E41, 0x5E43, 0x5E46, - 0x5E47, 0x5E48, 0x5E49, 0x5E4A, 0x5E4B, 0x5E4D, 0x5E4E, 0x5E4F, - 0x5E50, 0x5E51, 0x5E52, 0x5E53, 0x5E56, 0x5E57, 0x5E58, 0x5E59, - 0x5E5A, 0x5E5C, 0x5E5D, 0x5E5F, 0x5E60, 0x5E63, 0x5E64, 0x5E65, - 0x5E66, 0x5E67, 0x5E68, 0x5E69, 0x5E6A, 0x5E6B, 0x5E6C, 0x5E6D, - 0x5E6E, 0x5E6F, 0x5E70, 0x5E71, 0x5E75, 0x5E77, 0x5E79, 0x5E7E, - 0x5E81, 0x5E82, 0x5E83, 0x5E85, 0x5E88, 0x5E89, 0x5E8C, 0x5E8D, - 0x5E8E, 0x5E92, 0x5E98, 0x5E9B, 0x5E9D, 0x5EA1, 0x5EA2, 0x5EA3, - 0x5EA4, 0x5EA8, 0x5EA9, 0x5EAA, 0x5EAB, 0x5EAC, 0x5EAE, 0x5EAF, - 0x5EB0, 0x5EB1, 0x5EB2, 0x5EB4, 0x5EBA, 0x5EBB, 0x5EBC, 0x5EBD, - 0x5EBF, 0x5EC0, 0x5EC1, 0x5EC2, 0x5EC3, 0x5EC4, 0x5EC5 - }, - { - 0x5EC6, 0x5EC7, 0x5EC8, 0x5ECB, 0x5ECC, 0x5ECD, 0x5ECE, 0x5ECF, - 0x5ED0, 0x5ED4, 0x5ED5, 0x5ED7, 0x5ED8, 0x5ED9, 0x5EDA, 0x5EDC, - 0x5EDD, 0x5EDE, 0x5EDF, 0x5EE0, 0x5EE1, 0x5EE2, 0x5EE3, 0x5EE4, - 0x5EE5, 0x5EE6, 0x5EE7, 0x5EE9, 0x5EEB, 0x5EEC, 0x5EED, 0x5EEE, - 0x5EEF, 0x5EF0, 0x5EF1, 0x5EF2, 0x5EF3, 0x5EF5, 0x5EF8, 0x5EF9, - 0x5EFB, 0x5EFC, 0x5EFD, 0x5F05, 0x5F06, 0x5F07, 0x5F09, 0x5F0C, - 0x5F0D, 0x5F0E, 0x5F10, 0x5F12, 0x5F14, 0x5F16, 0x5F19, 0x5F1A, - 0x5F1C, 0x5F1D, 0x5F1E, 0x5F21, 0x5F22, 0x5F23, 0x5F24, 0x003F, - 0x5F28, 0x5F2B, 0x5F2C, 0x5F2E, 0x5F30, 0x5F32, 0x5F33, 0x5F34, - 0x5F35, 0x5F36, 0x5F37, 0x5F38, 0x5F3B, 0x5F3D, 0x5F3E, 0x5F3F, - 0x5F41, 0x5F42, 0x5F43, 0x5F44, 0x5F45, 0x5F46, 0x5F47, 0x5F48, - 0x5F49, 0x5F4A, 0x5F4B, 0x5F4C, 0x5F4D, 0x5F4E, 0x5F4F, 0x5F51, - 0x5F54, 0x5F59, 0x5F5A, 0x5F5B, 0x5F5C, 0x5F5E, 0x5F5F, 0x5F60, - 0x5F63, 0x5F65, 0x5F67, 0x5F68, 0x5F6B, 0x5F6E, 0x5F6F, 0x5F72, - 0x5F74, 0x5F75, 0x5F76, 0x5F78, 0x5F7A, 0x5F7D, 0x5F7E, 0x5F7F, - 0x5F83, 0x5F86, 0x5F8D, 0x5F8E, 0x5F8F, 0x5F91, 0x5F93, 0x5F94, - 0x5F96, 0x5F9A, 0x5F9B, 0x5F9D, 0x5F9E, 0x5F9F, 0x5FA0, 0x5FA2, - 0x5FA3, 0x5FA4, 0x5FA5, 0x5FA6, 0x5FA7, 0x5FA9, 0x5FAB, 0x5FAC, - 0x5FAF, 0x5FB0, 0x5FB1, 0x5FB2, 0x5FB3, 0x5FB4, 0x5FB6, 0x5FB8, - 0x5FB9, 0x5FBA, 0x5FBB, 0x5FBE, 0x5FBF, 0x5FC0, 0x5FC1, 0x5FC2, - 0x5FC7, 0x5FC8, 0x5FCA, 0x5FCB, 0x5FCE, 0x5FD3, 0x5FD4, 0x5FD5, - 0x5FDA, 0x5FDB, 0x5FDC, 0x5FDE, 0x5FDF, 0x5FE2, 0x5FE3, 0x5FE5, - 0x5FE6, 0x5FE8, 0x5FE9, 0x5FEC, 0x5FEF, 0x5FF0, 0x5FF2, 0x5FF3, - 0x5FF4, 0x5FF6, 0x5FF7, 0x5FF9, 0x5FFA, 0x5FFC, 0x6007 - }, - { - 0x6008, 0x6009, 0x600B, 0x600C, 0x6010, 0x6011, 0x6013, 0x6017, - 0x6018, 0x601A, 0x601E, 0x601F, 0x6022, 0x6023, 0x6024, 0x602C, - 0x602D, 0x602E, 0x6030, 0x6031, 0x6032, 0x6033, 0x6034, 0x6036, - 0x6037, 0x6038, 0x6039, 0x603A, 0x603D, 0x603E, 0x6040, 0x6044, - 0x6045, 0x6046, 0x6047, 0x6048, 0x6049, 0x604A, 0x604C, 0x604E, - 0x604F, 0x6051, 0x6053, 0x6054, 0x6056, 0x6057, 0x6058, 0x605B, - 0x605C, 0x605E, 0x605F, 0x6060, 0x6061, 0x6065, 0x6066, 0x606E, - 0x6071, 0x6072, 0x6074, 0x6075, 0x6077, 0x607E, 0x6080, 0x003F, - 0x6081, 0x6082, 0x6085, 0x6086, 0x6087, 0x6088, 0x608A, 0x608B, - 0x608E, 0x608F, 0x6090, 0x6091, 0x6093, 0x6095, 0x6097, 0x6098, - 0x6099, 0x609C, 0x609E, 0x60A1, 0x60A2, 0x60A4, 0x60A5, 0x60A7, - 0x60A9, 0x60AA, 0x60AE, 0x60B0, 0x60B3, 0x60B5, 0x60B6, 0x60B7, - 0x60B9, 0x60BA, 0x60BD, 0x60BE, 0x60BF, 0x60C0, 0x60C1, 0x60C2, - 0x60C3, 0x60C4, 0x60C7, 0x60C8, 0x60C9, 0x60CC, 0x60CD, 0x60CE, - 0x60CF, 0x60D0, 0x60D2, 0x60D3, 0x60D4, 0x60D6, 0x60D7, 0x60D9, - 0x60DB, 0x60DE, 0x60E1, 0x60E2, 0x60E3, 0x60E4, 0x60E5, 0x60EA, - 0x60F1, 0x60F2, 0x60F5, 0x60F7, 0x60F8, 0x60FB, 0x60FC, 0x60FD, - 0x60FE, 0x60FF, 0x6102, 0x6103, 0x6104, 0x6105, 0x6107, 0x610A, - 0x610B, 0x610C, 0x6110, 0x6111, 0x6112, 0x6113, 0x6114, 0x6116, - 0x6117, 0x6118, 0x6119, 0x611B, 0x611C, 0x611D, 0x611E, 0x6121, - 0x6122, 0x6125, 0x6128, 0x6129, 0x612A, 0x612C, 0x612D, 0x612E, - 0x612F, 0x6130, 0x6131, 0x6132, 0x6133, 0x6134, 0x6135, 0x6136, - 0x6137, 0x6138, 0x6139, 0x613A, 0x613B, 0x613C, 0x613D, 0x613E, - 0x6140, 0x6141, 0x6142, 0x6143, 0x6144, 0x6145, 0x6146 - }, - { - 0x6147, 0x6149, 0x614B, 0x614D, 0x614F, 0x6150, 0x6152, 0x6153, - 0x6154, 0x6156, 0x6157, 0x6158, 0x6159, 0x615A, 0x615B, 0x615C, - 0x615E, 0x615F, 0x6160, 0x6161, 0x6163, 0x6164, 0x6165, 0x6166, - 0x6169, 0x616A, 0x616B, 0x616C, 0x616D, 0x616E, 0x616F, 0x6171, - 0x6172, 0x6173, 0x6174, 0x6176, 0x6178, 0x6179, 0x617A, 0x617B, - 0x617C, 0x617D, 0x617E, 0x617F, 0x6180, 0x6181, 0x6182, 0x6183, - 0x6184, 0x6185, 0x6186, 0x6187, 0x6188, 0x6189, 0x618A, 0x618C, - 0x618D, 0x618F, 0x6190, 0x6191, 0x6192, 0x6193, 0x6195, 0x003F, - 0x6196, 0x6197, 0x6198, 0x6199, 0x619A, 0x619B, 0x619C, 0x619E, - 0x619F, 0x61A0, 0x61A1, 0x61A2, 0x61A3, 0x61A4, 0x61A5, 0x61A6, - 0x61AA, 0x61AB, 0x61AD, 0x61AE, 0x61AF, 0x61B0, 0x61B1, 0x61B2, - 0x61B3, 0x61B4, 0x61B5, 0x61B6, 0x61B8, 0x61B9, 0x61BA, 0x61BB, - 0x61BC, 0x61BD, 0x61BF, 0x61C0, 0x61C1, 0x61C3, 0x61C4, 0x61C5, - 0x61C6, 0x61C7, 0x61C9, 0x61CC, 0x61CD, 0x61CE, 0x61CF, 0x61D0, - 0x61D3, 0x61D5, 0x61D6, 0x61D7, 0x61D8, 0x61D9, 0x61DA, 0x61DB, - 0x61DC, 0x61DD, 0x61DE, 0x61DF, 0x61E0, 0x61E1, 0x61E2, 0x61E3, - 0x61E4, 0x61E5, 0x61E7, 0x61E8, 0x61E9, 0x61EA, 0x61EB, 0x61EC, - 0x61ED, 0x61EE, 0x61EF, 0x61F0, 0x61F1, 0x61F2, 0x61F3, 0x61F4, - 0x61F6, 0x61F7, 0x61F8, 0x61F9, 0x61FA, 0x61FB, 0x61FC, 0x61FD, - 0x61FE, 0x6200, 0x6201, 0x6202, 0x6203, 0x6204, 0x6205, 0x6207, - 0x6209, 0x6213, 0x6214, 0x6219, 0x621C, 0x621D, 0x621E, 0x6220, - 0x6223, 0x6226, 0x6227, 0x6228, 0x6229, 0x622B, 0x622D, 0x622F, - 0x6230, 0x6231, 0x6232, 0x6235, 0x6236, 0x6238, 0x6239, 0x623A, - 0x623B, 0x623C, 0x6242, 0x6244, 0x6245, 0x6246, 0x624A - }, - { - 0x624F, 0x6250, 0x6255, 0x6256, 0x6257, 0x6259, 0x625A, 0x625C, - 0x625D, 0x625E, 0x625F, 0x6260, 0x6261, 0x6262, 0x6264, 0x6265, - 0x6268, 0x6271, 0x6272, 0x6274, 0x6275, 0x6277, 0x6278, 0x627A, - 0x627B, 0x627D, 0x6281, 0x6282, 0x6283, 0x6285, 0x6286, 0x6287, - 0x6288, 0x628B, 0x628C, 0x628D, 0x628E, 0x628F, 0x6290, 0x6294, - 0x6299, 0x629C, 0x629D, 0x629E, 0x62A3, 0x62A6, 0x62A7, 0x62A9, - 0x62AA, 0x62AD, 0x62AE, 0x62AF, 0x62B0, 0x62B2, 0x62B3, 0x62B4, - 0x62B6, 0x62B7, 0x62B8, 0x62BA, 0x62BE, 0x62C0, 0x62C1, 0x003F, - 0x62C3, 0x62CB, 0x62CF, 0x62D1, 0x62D5, 0x62DD, 0x62DE, 0x62E0, - 0x62E1, 0x62E4, 0x62EA, 0x62EB, 0x62F0, 0x62F2, 0x62F5, 0x62F8, - 0x62F9, 0x62FA, 0x62FB, 0x6300, 0x6303, 0x6304, 0x6305, 0x6306, - 0x630A, 0x630B, 0x630C, 0x630D, 0x630F, 0x6310, 0x6312, 0x6313, - 0x6314, 0x6315, 0x6317, 0x6318, 0x6319, 0x631C, 0x6326, 0x6327, - 0x6329, 0x632C, 0x632D, 0x632E, 0x6330, 0x6331, 0x6333, 0x6334, - 0x6335, 0x6336, 0x6337, 0x6338, 0x633B, 0x633C, 0x633E, 0x633F, - 0x6340, 0x6341, 0x6344, 0x6347, 0x6348, 0x634A, 0x6351, 0x6352, - 0x6353, 0x6354, 0x6356, 0x6357, 0x6358, 0x6359, 0x635A, 0x635B, - 0x635C, 0x635D, 0x6360, 0x6364, 0x6365, 0x6366, 0x6368, 0x636A, - 0x636B, 0x636C, 0x636F, 0x6370, 0x6372, 0x6373, 0x6374, 0x6375, - 0x6378, 0x6379, 0x637C, 0x637D, 0x637E, 0x637F, 0x6381, 0x6383, - 0x6384, 0x6385, 0x6386, 0x638B, 0x638D, 0x6391, 0x6393, 0x6394, - 0x6395, 0x6397, 0x6399, 0x639A, 0x639B, 0x639C, 0x639D, 0x639E, - 0x639F, 0x63A1, 0x63A4, 0x63A6, 0x63AB, 0x63AF, 0x63B1, 0x63B2, - 0x63B5, 0x63B6, 0x63B9, 0x63BB, 0x63BD, 0x63BF, 0x63C0 - }, - { - 0x63C1, 0x63C2, 0x63C3, 0x63C5, 0x63C7, 0x63C8, 0x63CA, 0x63CB, - 0x63CC, 0x63D1, 0x63D3, 0x63D4, 0x63D5, 0x63D7, 0x63D8, 0x63D9, - 0x63DA, 0x63DB, 0x63DC, 0x63DD, 0x63DF, 0x63E2, 0x63E4, 0x63E5, - 0x63E6, 0x63E7, 0x63E8, 0x63EB, 0x63EC, 0x63EE, 0x63EF, 0x63F0, - 0x63F1, 0x63F3, 0x63F5, 0x63F7, 0x63F9, 0x63FA, 0x63FB, 0x63FC, - 0x63FE, 0x6403, 0x6404, 0x6406, 0x6407, 0x6408, 0x6409, 0x640A, - 0x640D, 0x640E, 0x6411, 0x6412, 0x6415, 0x6416, 0x6417, 0x6418, - 0x6419, 0x641A, 0x641D, 0x641F, 0x6422, 0x6423, 0x6424, 0x003F, - 0x6425, 0x6427, 0x6428, 0x6429, 0x642B, 0x642E, 0x642F, 0x6430, - 0x6431, 0x6432, 0x6433, 0x6435, 0x6436, 0x6437, 0x6438, 0x6439, - 0x643B, 0x643C, 0x643E, 0x6440, 0x6442, 0x6443, 0x6449, 0x644B, - 0x644C, 0x644D, 0x644E, 0x644F, 0x6450, 0x6451, 0x6453, 0x6455, - 0x6456, 0x6457, 0x6459, 0x645A, 0x645B, 0x645C, 0x645D, 0x645F, - 0x6460, 0x6461, 0x6462, 0x6463, 0x6464, 0x6465, 0x6466, 0x6468, - 0x646A, 0x646B, 0x646C, 0x646E, 0x646F, 0x6470, 0x6471, 0x6472, - 0x6473, 0x6474, 0x6475, 0x6476, 0x6477, 0x647B, 0x647C, 0x647D, - 0x647E, 0x647F, 0x6480, 0x6481, 0x6483, 0x6486, 0x6488, 0x6489, - 0x648A, 0x648B, 0x648C, 0x648D, 0x648E, 0x648F, 0x6490, 0x6493, - 0x6494, 0x6497, 0x6498, 0x649A, 0x649B, 0x649C, 0x649D, 0x649F, - 0x64A0, 0x64A1, 0x64A2, 0x64A3, 0x64A5, 0x64A6, 0x64A7, 0x64A8, - 0x64AA, 0x64AB, 0x64AF, 0x64B1, 0x64B2, 0x64B3, 0x64B4, 0x64B6, - 0x64B9, 0x64BB, 0x64BD, 0x64BE, 0x64BF, 0x64C1, 0x64C3, 0x64C4, - 0x64C6, 0x64C7, 0x64C8, 0x64C9, 0x64CA, 0x64CB, 0x64CC, 0x64CF, - 0x64D1, 0x64D3, 0x64D4, 0x64D5, 0x64D6, 0x64D9, 0x64DA - }, - { - 0x64DB, 0x64DC, 0x64DD, 0x64DF, 0x64E0, 0x64E1, 0x64E3, 0x64E5, - 0x64E7, 0x64E8, 0x64E9, 0x64EA, 0x64EB, 0x64EC, 0x64ED, 0x64EE, - 0x64EF, 0x64F0, 0x64F1, 0x64F2, 0x64F3, 0x64F4, 0x64F5, 0x64F6, - 0x64F7, 0x64F8, 0x64F9, 0x64FA, 0x64FB, 0x64FC, 0x64FD, 0x64FE, - 0x64FF, 0x6501, 0x6502, 0x6503, 0x6504, 0x6505, 0x6506, 0x6507, - 0x6508, 0x650A, 0x650B, 0x650C, 0x650D, 0x650E, 0x650F, 0x6510, - 0x6511, 0x6513, 0x6514, 0x6515, 0x6516, 0x6517, 0x6519, 0x651A, - 0x651B, 0x651C, 0x651D, 0x651E, 0x651F, 0x6520, 0x6521, 0x003F, - 0x6522, 0x6523, 0x6524, 0x6526, 0x6527, 0x6528, 0x6529, 0x652A, - 0x652C, 0x652D, 0x6530, 0x6531, 0x6532, 0x6533, 0x6537, 0x653A, - 0x653C, 0x653D, 0x6540, 0x6541, 0x6542, 0x6543, 0x6544, 0x6546, - 0x6547, 0x654A, 0x654B, 0x654D, 0x654E, 0x6550, 0x6552, 0x6553, - 0x6554, 0x6557, 0x6558, 0x655A, 0x655C, 0x655F, 0x6560, 0x6561, - 0x6564, 0x6565, 0x6567, 0x6568, 0x6569, 0x656A, 0x656D, 0x656E, - 0x656F, 0x6571, 0x6573, 0x6575, 0x6576, 0x6578, 0x6579, 0x657A, - 0x657B, 0x657C, 0x657D, 0x657E, 0x657F, 0x6580, 0x6581, 0x6582, - 0x6583, 0x6584, 0x6585, 0x6586, 0x6588, 0x6589, 0x658A, 0x658D, - 0x658E, 0x658F, 0x6592, 0x6594, 0x6595, 0x6596, 0x6598, 0x659A, - 0x659D, 0x659E, 0x65A0, 0x65A2, 0x65A3, 0x65A6, 0x65A8, 0x65AA, - 0x65AC, 0x65AE, 0x65B1, 0x65B2, 0x65B3, 0x65B4, 0x65B5, 0x65B6, - 0x65B7, 0x65B8, 0x65BA, 0x65BB, 0x65BE, 0x65BF, 0x65C0, 0x65C2, - 0x65C7, 0x65C8, 0x65C9, 0x65CA, 0x65CD, 0x65D0, 0x65D1, 0x65D3, - 0x65D4, 0x65D5, 0x65D8, 0x65D9, 0x65DA, 0x65DB, 0x65DC, 0x65DD, - 0x65DE, 0x65DF, 0x65E1, 0x65E3, 0x65E4, 0x65EA, 0x65EB - }, - { - 0x65F2, 0x65F3, 0x65F4, 0x65F5, 0x65F8, 0x65F9, 0x65FB, 0x65FC, - 0x65FD, 0x65FE, 0x65FF, 0x6601, 0x6604, 0x6605, 0x6607, 0x6608, - 0x6609, 0x660B, 0x660D, 0x6610, 0x6611, 0x6612, 0x6616, 0x6617, - 0x6618, 0x661A, 0x661B, 0x661C, 0x661E, 0x6621, 0x6622, 0x6623, - 0x6624, 0x6626, 0x6629, 0x662A, 0x662B, 0x662C, 0x662E, 0x6630, - 0x6632, 0x6633, 0x6637, 0x6638, 0x6639, 0x663A, 0x663B, 0x663D, - 0x663F, 0x6640, 0x6642, 0x6644, 0x6645, 0x6646, 0x6647, 0x6648, - 0x6649, 0x664A, 0x664D, 0x664E, 0x6650, 0x6651, 0x6658, 0x003F, - 0x6659, 0x665B, 0x665C, 0x665D, 0x665E, 0x6660, 0x6662, 0x6663, - 0x6665, 0x6667, 0x6669, 0x666A, 0x666B, 0x666C, 0x666D, 0x6671, - 0x6672, 0x6673, 0x6675, 0x6678, 0x6679, 0x667B, 0x667C, 0x667D, - 0x667F, 0x6680, 0x6681, 0x6683, 0x6685, 0x6686, 0x6688, 0x6689, - 0x668A, 0x668B, 0x668D, 0x668E, 0x668F, 0x6690, 0x6692, 0x6693, - 0x6694, 0x6695, 0x6698, 0x6699, 0x669A, 0x669B, 0x669C, 0x669E, - 0x669F, 0x66A0, 0x66A1, 0x66A2, 0x66A3, 0x66A4, 0x66A5, 0x66A6, - 0x66A9, 0x66AA, 0x66AB, 0x66AC, 0x66AD, 0x66AF, 0x66B0, 0x66B1, - 0x66B2, 0x66B3, 0x66B5, 0x66B6, 0x66B7, 0x66B8, 0x66BA, 0x66BB, - 0x66BC, 0x66BD, 0x66BF, 0x66C0, 0x66C1, 0x66C2, 0x66C3, 0x66C4, - 0x66C5, 0x66C6, 0x66C7, 0x66C8, 0x66C9, 0x66CA, 0x66CB, 0x66CC, - 0x66CD, 0x66CE, 0x66CF, 0x66D0, 0x66D1, 0x66D2, 0x66D3, 0x66D4, - 0x66D5, 0x66D6, 0x66D7, 0x66D8, 0x66DA, 0x66DE, 0x66DF, 0x66E0, - 0x66E1, 0x66E2, 0x66E3, 0x66E4, 0x66E5, 0x66E7, 0x66E8, 0x66EA, - 0x66EB, 0x66EC, 0x66ED, 0x66EE, 0x66EF, 0x66F1, 0x66F5, 0x66F6, - 0x66F8, 0x66FA, 0x66FB, 0x66FD, 0x6701, 0x6702, 0x6703 - }, - { - 0x6704, 0x6705, 0x6706, 0x6707, 0x670C, 0x670E, 0x670F, 0x6711, - 0x6712, 0x6713, 0x6716, 0x6718, 0x6719, 0x671A, 0x671C, 0x671E, - 0x6720, 0x6721, 0x6722, 0x6723, 0x6724, 0x6725, 0x6727, 0x6729, - 0x672E, 0x6730, 0x6732, 0x6733, 0x6736, 0x6737, 0x6738, 0x6739, - 0x673B, 0x673C, 0x673E, 0x673F, 0x6741, 0x6744, 0x6745, 0x6747, - 0x674A, 0x674B, 0x674D, 0x6752, 0x6754, 0x6755, 0x6757, 0x6758, - 0x6759, 0x675A, 0x675B, 0x675D, 0x6762, 0x6763, 0x6764, 0x6766, - 0x6767, 0x676B, 0x676C, 0x676E, 0x6771, 0x6774, 0x6776, 0x003F, - 0x6778, 0x6779, 0x677A, 0x677B, 0x677D, 0x6780, 0x6782, 0x6783, - 0x6785, 0x6786, 0x6788, 0x678A, 0x678C, 0x678D, 0x678E, 0x678F, - 0x6791, 0x6792, 0x6793, 0x6794, 0x6796, 0x6799, 0x679B, 0x679F, - 0x67A0, 0x67A1, 0x67A4, 0x67A6, 0x67A9, 0x67AC, 0x67AE, 0x67B1, - 0x67B2, 0x67B4, 0x67B9, 0x67BA, 0x67BB, 0x67BC, 0x67BD, 0x67BE, - 0x67BF, 0x67C0, 0x67C2, 0x67C5, 0x67C6, 0x67C7, 0x67C8, 0x67C9, - 0x67CA, 0x67CB, 0x67CC, 0x67CD, 0x67CE, 0x67D5, 0x67D6, 0x67D7, - 0x67DB, 0x67DF, 0x67E1, 0x67E3, 0x67E4, 0x67E6, 0x67E7, 0x67E8, - 0x67EA, 0x67EB, 0x67ED, 0x67EE, 0x67F2, 0x67F5, 0x67F6, 0x67F7, - 0x67F8, 0x67F9, 0x67FA, 0x67FB, 0x67FC, 0x67FE, 0x6801, 0x6802, - 0x6803, 0x6804, 0x6806, 0x680D, 0x6810, 0x6812, 0x6814, 0x6815, - 0x6818, 0x6819, 0x681A, 0x681B, 0x681C, 0x681E, 0x681F, 0x6820, - 0x6822, 0x6823, 0x6824, 0x6825, 0x6826, 0x6827, 0x6828, 0x682B, - 0x682C, 0x682D, 0x682E, 0x682F, 0x6830, 0x6831, 0x6834, 0x6835, - 0x6836, 0x683A, 0x683B, 0x683F, 0x6847, 0x684B, 0x684D, 0x684F, - 0x6852, 0x6856, 0x6857, 0x6858, 0x6859, 0x685A, 0x685B - }, - { - 0x685C, 0x685D, 0x685E, 0x685F, 0x686A, 0x686C, 0x686D, 0x686E, - 0x686F, 0x6870, 0x6871, 0x6872, 0x6873, 0x6875, 0x6878, 0x6879, - 0x687A, 0x687B, 0x687C, 0x687D, 0x687E, 0x687F, 0x6880, 0x6882, - 0x6884, 0x6887, 0x6888, 0x6889, 0x688A, 0x688B, 0x688C, 0x688D, - 0x688E, 0x6890, 0x6891, 0x6892, 0x6894, 0x6895, 0x6896, 0x6898, - 0x6899, 0x689A, 0x689B, 0x689C, 0x689D, 0x689E, 0x689F, 0x68A0, - 0x68A1, 0x68A3, 0x68A4, 0x68A5, 0x68A9, 0x68AA, 0x68AB, 0x68AC, - 0x68AE, 0x68B1, 0x68B2, 0x68B4, 0x68B6, 0x68B7, 0x68B8, 0x003F, - 0x68B9, 0x68BA, 0x68BB, 0x68BC, 0x68BD, 0x68BE, 0x68BF, 0x68C1, - 0x68C3, 0x68C4, 0x68C5, 0x68C6, 0x68C7, 0x68C8, 0x68CA, 0x68CC, - 0x68CE, 0x68CF, 0x68D0, 0x68D1, 0x68D3, 0x68D4, 0x68D6, 0x68D7, - 0x68D9, 0x68DB, 0x68DC, 0x68DD, 0x68DE, 0x68DF, 0x68E1, 0x68E2, - 0x68E4, 0x68E5, 0x68E6, 0x68E7, 0x68E8, 0x68E9, 0x68EA, 0x68EB, - 0x68EC, 0x68ED, 0x68EF, 0x68F2, 0x68F3, 0x68F4, 0x68F6, 0x68F7, - 0x68F8, 0x68FB, 0x68FD, 0x68FE, 0x68FF, 0x6900, 0x6902, 0x6903, - 0x6904, 0x6906, 0x6907, 0x6908, 0x6909, 0x690A, 0x690C, 0x690F, - 0x6911, 0x6913, 0x6914, 0x6915, 0x6916, 0x6917, 0x6918, 0x6919, - 0x691A, 0x691B, 0x691C, 0x691D, 0x691E, 0x6921, 0x6922, 0x6923, - 0x6925, 0x6926, 0x6927, 0x6928, 0x6929, 0x692A, 0x692B, 0x692C, - 0x692E, 0x692F, 0x6931, 0x6932, 0x6933, 0x6935, 0x6936, 0x6937, - 0x6938, 0x693A, 0x693B, 0x693C, 0x693E, 0x6940, 0x6941, 0x6943, - 0x6944, 0x6945, 0x6946, 0x6947, 0x6948, 0x6949, 0x694A, 0x694B, - 0x694C, 0x694D, 0x694E, 0x694F, 0x6950, 0x6951, 0x6952, 0x6953, - 0x6955, 0x6956, 0x6958, 0x6959, 0x695B, 0x695C, 0x695F - }, - { - 0x6961, 0x6962, 0x6964, 0x6965, 0x6967, 0x6968, 0x6969, 0x696A, - 0x696C, 0x696D, 0x696F, 0x6970, 0x6972, 0x6973, 0x6974, 0x6975, - 0x6976, 0x697A, 0x697B, 0x697D, 0x697E, 0x697F, 0x6981, 0x6983, - 0x6985, 0x698A, 0x698B, 0x698C, 0x698E, 0x698F, 0x6990, 0x6991, - 0x6992, 0x6993, 0x6996, 0x6997, 0x6999, 0x699A, 0x699D, 0x699E, - 0x699F, 0x69A0, 0x69A1, 0x69A2, 0x69A3, 0x69A4, 0x69A5, 0x69A6, - 0x69A9, 0x69AA, 0x69AC, 0x69AE, 0x69AF, 0x69B0, 0x69B2, 0x69B3, - 0x69B5, 0x69B6, 0x69B8, 0x69B9, 0x69BA, 0x69BC, 0x69BD, 0x003F, - 0x69BE, 0x69BF, 0x69C0, 0x69C2, 0x69C3, 0x69C4, 0x69C5, 0x69C6, - 0x69C7, 0x69C8, 0x69C9, 0x69CB, 0x69CD, 0x69CF, 0x69D1, 0x69D2, - 0x69D3, 0x69D5, 0x69D6, 0x69D7, 0x69D8, 0x69D9, 0x69DA, 0x69DC, - 0x69DD, 0x69DE, 0x69E1, 0x69E2, 0x69E3, 0x69E4, 0x69E5, 0x69E6, - 0x69E7, 0x69E8, 0x69E9, 0x69EA, 0x69EB, 0x69EC, 0x69EE, 0x69EF, - 0x69F0, 0x69F1, 0x69F3, 0x69F4, 0x69F5, 0x69F6, 0x69F7, 0x69F8, - 0x69F9, 0x69FA, 0x69FB, 0x69FC, 0x69FE, 0x6A00, 0x6A01, 0x6A02, - 0x6A03, 0x6A04, 0x6A05, 0x6A06, 0x6A07, 0x6A08, 0x6A09, 0x6A0B, - 0x6A0C, 0x6A0D, 0x6A0E, 0x6A0F, 0x6A10, 0x6A11, 0x6A12, 0x6A13, - 0x6A14, 0x6A15, 0x6A16, 0x6A19, 0x6A1A, 0x6A1B, 0x6A1C, 0x6A1D, - 0x6A1E, 0x6A20, 0x6A22, 0x6A23, 0x6A24, 0x6A25, 0x6A26, 0x6A27, - 0x6A29, 0x6A2B, 0x6A2C, 0x6A2D, 0x6A2E, 0x6A30, 0x6A32, 0x6A33, - 0x6A34, 0x6A36, 0x6A37, 0x6A38, 0x6A39, 0x6A3A, 0x6A3B, 0x6A3C, - 0x6A3F, 0x6A40, 0x6A41, 0x6A42, 0x6A43, 0x6A45, 0x6A46, 0x6A48, - 0x6A49, 0x6A4A, 0x6A4B, 0x6A4C, 0x6A4D, 0x6A4E, 0x6A4F, 0x6A51, - 0x6A52, 0x6A53, 0x6A54, 0x6A55, 0x6A56, 0x6A57, 0x6A5A - }, - { - 0x6A5C, 0x6A5D, 0x6A5E, 0x6A5F, 0x6A60, 0x6A62, 0x6A63, 0x6A64, - 0x6A66, 0x6A67, 0x6A68, 0x6A69, 0x6A6A, 0x6A6B, 0x6A6C, 0x6A6D, - 0x6A6E, 0x6A6F, 0x6A70, 0x6A72, 0x6A73, 0x6A74, 0x6A75, 0x6A76, - 0x6A77, 0x6A78, 0x6A7A, 0x6A7B, 0x6A7D, 0x6A7E, 0x6A7F, 0x6A81, - 0x6A82, 0x6A83, 0x6A85, 0x6A86, 0x6A87, 0x6A88, 0x6A89, 0x6A8A, - 0x6A8B, 0x6A8C, 0x6A8D, 0x6A8F, 0x6A92, 0x6A93, 0x6A94, 0x6A95, - 0x6A96, 0x6A98, 0x6A99, 0x6A9A, 0x6A9B, 0x6A9C, 0x6A9D, 0x6A9E, - 0x6A9F, 0x6AA1, 0x6AA2, 0x6AA3, 0x6AA4, 0x6AA5, 0x6AA6, 0x003F, - 0x6AA7, 0x6AA8, 0x6AAA, 0x6AAD, 0x6AAE, 0x6AAF, 0x6AB0, 0x6AB1, - 0x6AB2, 0x6AB3, 0x6AB4, 0x6AB5, 0x6AB6, 0x6AB7, 0x6AB8, 0x6AB9, - 0x6ABA, 0x6ABB, 0x6ABC, 0x6ABD, 0x6ABE, 0x6ABF, 0x6AC0, 0x6AC1, - 0x6AC2, 0x6AC3, 0x6AC4, 0x6AC5, 0x6AC6, 0x6AC7, 0x6AC8, 0x6AC9, - 0x6ACA, 0x6ACB, 0x6ACC, 0x6ACD, 0x6ACE, 0x6ACF, 0x6AD0, 0x6AD1, - 0x6AD2, 0x6AD3, 0x6AD4, 0x6AD5, 0x6AD6, 0x6AD7, 0x6AD8, 0x6AD9, - 0x6ADA, 0x6ADB, 0x6ADC, 0x6ADD, 0x6ADE, 0x6ADF, 0x6AE0, 0x6AE1, - 0x6AE2, 0x6AE3, 0x6AE4, 0x6AE5, 0x6AE6, 0x6AE7, 0x6AE8, 0x6AE9, - 0x6AEA, 0x6AEB, 0x6AEC, 0x6AED, 0x6AEE, 0x6AEF, 0x6AF0, 0x6AF1, - 0x6AF2, 0x6AF3, 0x6AF4, 0x6AF5, 0x6AF6, 0x6AF7, 0x6AF8, 0x6AF9, - 0x6AFA, 0x6AFB, 0x6AFC, 0x6AFD, 0x6AFE, 0x6AFF, 0x6B00, 0x6B01, - 0x6B02, 0x6B03, 0x6B04, 0x6B05, 0x6B06, 0x6B07, 0x6B08, 0x6B09, - 0x6B0A, 0x6B0B, 0x6B0C, 0x6B0D, 0x6B0E, 0x6B0F, 0x6B10, 0x6B11, - 0x6B12, 0x6B13, 0x6B14, 0x6B15, 0x6B16, 0x6B17, 0x6B18, 0x6B19, - 0x6B1A, 0x6B1B, 0x6B1C, 0x6B1D, 0x6B1E, 0x6B1F, 0x6B25, 0x6B26, - 0x6B28, 0x6B29, 0x6B2A, 0x6B2B, 0x6B2C, 0x6B2D, 0x6B2E - }, - { - 0x6B2F, 0x6B30, 0x6B31, 0x6B33, 0x6B34, 0x6B35, 0x6B36, 0x6B38, - 0x6B3B, 0x6B3C, 0x6B3D, 0x6B3F, 0x6B40, 0x6B41, 0x6B42, 0x6B44, - 0x6B45, 0x6B48, 0x6B4A, 0x6B4B, 0x6B4D, 0x6B4E, 0x6B4F, 0x6B50, - 0x6B51, 0x6B52, 0x6B53, 0x6B54, 0x6B55, 0x6B56, 0x6B57, 0x6B58, - 0x6B5A, 0x6B5B, 0x6B5C, 0x6B5D, 0x6B5E, 0x6B5F, 0x6B60, 0x6B61, - 0x6B68, 0x6B69, 0x6B6B, 0x6B6C, 0x6B6D, 0x6B6E, 0x6B6F, 0x6B70, - 0x6B71, 0x6B72, 0x6B73, 0x6B74, 0x6B75, 0x6B76, 0x6B77, 0x6B78, - 0x6B7A, 0x6B7D, 0x6B7E, 0x6B7F, 0x6B80, 0x6B85, 0x6B88, 0x003F, - 0x6B8C, 0x6B8E, 0x6B8F, 0x6B90, 0x6B91, 0x6B94, 0x6B95, 0x6B97, - 0x6B98, 0x6B99, 0x6B9C, 0x6B9D, 0x6B9E, 0x6B9F, 0x6BA0, 0x6BA2, - 0x6BA3, 0x6BA4, 0x6BA5, 0x6BA6, 0x6BA7, 0x6BA8, 0x6BA9, 0x6BAB, - 0x6BAC, 0x6BAD, 0x6BAE, 0x6BAF, 0x6BB0, 0x6BB1, 0x6BB2, 0x6BB6, - 0x6BB8, 0x6BB9, 0x6BBA, 0x6BBB, 0x6BBC, 0x6BBD, 0x6BBE, 0x6BC0, - 0x6BC3, 0x6BC4, 0x6BC6, 0x6BC7, 0x6BC8, 0x6BC9, 0x6BCA, 0x6BCC, - 0x6BCE, 0x6BD0, 0x6BD1, 0x6BD8, 0x6BDA, 0x6BDC, 0x6BDD, 0x6BDE, - 0x6BDF, 0x6BE0, 0x6BE2, 0x6BE3, 0x6BE4, 0x6BE5, 0x6BE6, 0x6BE7, - 0x6BE8, 0x6BE9, 0x6BEC, 0x6BED, 0x6BEE, 0x6BF0, 0x6BF1, 0x6BF2, - 0x6BF4, 0x6BF6, 0x6BF7, 0x6BF8, 0x6BFA, 0x6BFB, 0x6BFC, 0x6BFE, - 0x6BFF, 0x6C00, 0x6C01, 0x6C02, 0x6C03, 0x6C04, 0x6C08, 0x6C09, - 0x6C0A, 0x6C0B, 0x6C0C, 0x6C0E, 0x6C12, 0x6C17, 0x6C1C, 0x6C1D, - 0x6C1E, 0x6C20, 0x6C23, 0x6C25, 0x6C2B, 0x6C2C, 0x6C2D, 0x6C31, - 0x6C33, 0x6C36, 0x6C37, 0x6C39, 0x6C3A, 0x6C3B, 0x6C3C, 0x6C3E, - 0x6C3F, 0x6C43, 0x6C44, 0x6C45, 0x6C48, 0x6C4B, 0x6C4C, 0x6C4D, - 0x6C4E, 0x6C4F, 0x6C51, 0x6C52, 0x6C53, 0x6C56, 0x6C58 - }, - { - 0x6C59, 0x6C5A, 0x6C62, 0x6C63, 0x6C65, 0x6C66, 0x6C67, 0x6C6B, - 0x6C6C, 0x6C6D, 0x6C6E, 0x6C6F, 0x6C71, 0x6C73, 0x6C75, 0x6C77, - 0x6C78, 0x6C7A, 0x6C7B, 0x6C7C, 0x6C7F, 0x6C80, 0x6C84, 0x6C87, - 0x6C8A, 0x6C8B, 0x6C8D, 0x6C8E, 0x6C91, 0x6C92, 0x6C95, 0x6C96, - 0x6C97, 0x6C98, 0x6C9A, 0x6C9C, 0x6C9D, 0x6C9E, 0x6CA0, 0x6CA2, - 0x6CA8, 0x6CAC, 0x6CAF, 0x6CB0, 0x6CB4, 0x6CB5, 0x6CB6, 0x6CB7, - 0x6CBA, 0x6CC0, 0x6CC1, 0x6CC2, 0x6CC3, 0x6CC6, 0x6CC7, 0x6CC8, - 0x6CCB, 0x6CCD, 0x6CCE, 0x6CCF, 0x6CD1, 0x6CD2, 0x6CD8, 0x003F, - 0x6CD9, 0x6CDA, 0x6CDC, 0x6CDD, 0x6CDF, 0x6CE4, 0x6CE6, 0x6CE7, - 0x6CE9, 0x6CEC, 0x6CED, 0x6CF2, 0x6CF4, 0x6CF9, 0x6CFF, 0x6D00, - 0x6D02, 0x6D03, 0x6D05, 0x6D06, 0x6D08, 0x6D09, 0x6D0A, 0x6D0D, - 0x6D0F, 0x6D10, 0x6D11, 0x6D13, 0x6D14, 0x6D15, 0x6D16, 0x6D18, - 0x6D1C, 0x6D1D, 0x6D1F, 0x6D20, 0x6D21, 0x6D22, 0x6D23, 0x6D24, - 0x6D26, 0x6D28, 0x6D29, 0x6D2C, 0x6D2D, 0x6D2F, 0x6D30, 0x6D34, - 0x6D36, 0x6D37, 0x6D38, 0x6D3A, 0x6D3F, 0x6D40, 0x6D42, 0x6D44, - 0x6D49, 0x6D4C, 0x6D50, 0x6D55, 0x6D56, 0x6D57, 0x6D58, 0x6D5B, - 0x6D5D, 0x6D5F, 0x6D61, 0x6D62, 0x6D64, 0x6D65, 0x6D67, 0x6D68, - 0x6D6B, 0x6D6C, 0x6D6D, 0x6D70, 0x6D71, 0x6D72, 0x6D73, 0x6D75, - 0x6D76, 0x6D79, 0x6D7A, 0x6D7B, 0x6D7D, 0x6D7E, 0x6D7F, 0x6D80, - 0x6D81, 0x6D83, 0x6D84, 0x6D86, 0x6D87, 0x6D8A, 0x6D8B, 0x6D8D, - 0x6D8F, 0x6D90, 0x6D92, 0x6D96, 0x6D97, 0x6D98, 0x6D99, 0x6D9A, - 0x6D9C, 0x6DA2, 0x6DA5, 0x6DAC, 0x6DAD, 0x6DB0, 0x6DB1, 0x6DB3, - 0x6DB4, 0x6DB6, 0x6DB7, 0x6DB9, 0x6DBA, 0x6DBB, 0x6DBC, 0x6DBD, - 0x6DBE, 0x6DC1, 0x6DC2, 0x6DC3, 0x6DC8, 0x6DC9, 0x6DCA - }, - { - 0x6DCD, 0x6DCE, 0x6DCF, 0x6DD0, 0x6DD2, 0x6DD3, 0x6DD4, 0x6DD5, - 0x6DD7, 0x6DDA, 0x6DDB, 0x6DDC, 0x6DDF, 0x6DE2, 0x6DE3, 0x6DE5, - 0x6DE7, 0x6DE8, 0x6DE9, 0x6DEA, 0x6DED, 0x6DEF, 0x6DF0, 0x6DF2, - 0x6DF4, 0x6DF5, 0x6DF6, 0x6DF8, 0x6DFA, 0x6DFD, 0x6DFE, 0x6DFF, - 0x6E00, 0x6E01, 0x6E02, 0x6E03, 0x6E04, 0x6E06, 0x6E07, 0x6E08, - 0x6E09, 0x6E0B, 0x6E0F, 0x6E12, 0x6E13, 0x6E15, 0x6E18, 0x6E19, - 0x6E1B, 0x6E1C, 0x6E1E, 0x6E1F, 0x6E22, 0x6E26, 0x6E27, 0x6E28, - 0x6E2A, 0x6E2C, 0x6E2E, 0x6E30, 0x6E31, 0x6E33, 0x6E35, 0x003F, - 0x6E36, 0x6E37, 0x6E39, 0x6E3B, 0x6E3C, 0x6E3D, 0x6E3E, 0x6E3F, - 0x6E40, 0x6E41, 0x6E42, 0x6E45, 0x6E46, 0x6E47, 0x6E48, 0x6E49, - 0x6E4A, 0x6E4B, 0x6E4C, 0x6E4F, 0x6E50, 0x6E51, 0x6E52, 0x6E55, - 0x6E57, 0x6E59, 0x6E5A, 0x6E5C, 0x6E5D, 0x6E5E, 0x6E60, 0x6E61, - 0x6E62, 0x6E63, 0x6E64, 0x6E65, 0x6E66, 0x6E67, 0x6E68, 0x6E69, - 0x6E6A, 0x6E6C, 0x6E6D, 0x6E6F, 0x6E70, 0x6E71, 0x6E72, 0x6E73, - 0x6E74, 0x6E75, 0x6E76, 0x6E77, 0x6E78, 0x6E79, 0x6E7A, 0x6E7B, - 0x6E7C, 0x6E7D, 0x6E80, 0x6E81, 0x6E82, 0x6E84, 0x6E87, 0x6E88, - 0x6E8A, 0x6E8B, 0x6E8C, 0x6E8D, 0x6E8E, 0x6E91, 0x6E92, 0x6E93, - 0x6E94, 0x6E95, 0x6E96, 0x6E97, 0x6E99, 0x6E9A, 0x6E9B, 0x6E9D, - 0x6E9E, 0x6EA0, 0x6EA1, 0x6EA3, 0x6EA4, 0x6EA6, 0x6EA8, 0x6EA9, - 0x6EAB, 0x6EAC, 0x6EAD, 0x6EAE, 0x6EB0, 0x6EB3, 0x6EB5, 0x6EB8, - 0x6EB9, 0x6EBC, 0x6EBE, 0x6EBF, 0x6EC0, 0x6EC3, 0x6EC4, 0x6EC5, - 0x6EC6, 0x6EC8, 0x6EC9, 0x6ECA, 0x6ECC, 0x6ECD, 0x6ECE, 0x6ED0, - 0x6ED2, 0x6ED6, 0x6ED8, 0x6ED9, 0x6EDB, 0x6EDC, 0x6EDD, 0x6EE3, - 0x6EE7, 0x6EEA, 0x6EEB, 0x6EEC, 0x6EED, 0x6EEE, 0x6EEF - }, - { - 0x6EF0, 0x6EF1, 0x6EF2, 0x6EF3, 0x6EF5, 0x6EF6, 0x6EF7, 0x6EF8, - 0x6EFA, 0x6EFB, 0x6EFC, 0x6EFD, 0x6EFE, 0x6EFF, 0x6F00, 0x6F01, - 0x6F03, 0x6F04, 0x6F05, 0x6F07, 0x6F08, 0x6F0A, 0x6F0B, 0x6F0C, - 0x6F0D, 0x6F0E, 0x6F10, 0x6F11, 0x6F12, 0x6F16, 0x6F17, 0x6F18, - 0x6F19, 0x6F1A, 0x6F1B, 0x6F1C, 0x6F1D, 0x6F1E, 0x6F1F, 0x6F21, - 0x6F22, 0x6F23, 0x6F25, 0x6F26, 0x6F27, 0x6F28, 0x6F2C, 0x6F2E, - 0x6F30, 0x6F32, 0x6F34, 0x6F35, 0x6F37, 0x6F38, 0x6F39, 0x6F3A, - 0x6F3B, 0x6F3C, 0x6F3D, 0x6F3F, 0x6F40, 0x6F41, 0x6F42, 0x003F, - 0x6F43, 0x6F44, 0x6F45, 0x6F48, 0x6F49, 0x6F4A, 0x6F4C, 0x6F4E, - 0x6F4F, 0x6F50, 0x6F51, 0x6F52, 0x6F53, 0x6F54, 0x6F55, 0x6F56, - 0x6F57, 0x6F59, 0x6F5A, 0x6F5B, 0x6F5D, 0x6F5F, 0x6F60, 0x6F61, - 0x6F63, 0x6F64, 0x6F65, 0x6F67, 0x6F68, 0x6F69, 0x6F6A, 0x6F6B, - 0x6F6C, 0x6F6F, 0x6F70, 0x6F71, 0x6F73, 0x6F75, 0x6F76, 0x6F77, - 0x6F79, 0x6F7B, 0x6F7D, 0x6F7E, 0x6F7F, 0x6F80, 0x6F81, 0x6F82, - 0x6F83, 0x6F85, 0x6F86, 0x6F87, 0x6F8A, 0x6F8B, 0x6F8F, 0x6F90, - 0x6F91, 0x6F92, 0x6F93, 0x6F94, 0x6F95, 0x6F96, 0x6F97, 0x6F98, - 0x6F99, 0x6F9A, 0x6F9B, 0x6F9D, 0x6F9E, 0x6F9F, 0x6FA0, 0x6FA2, - 0x6FA3, 0x6FA4, 0x6FA5, 0x6FA6, 0x6FA8, 0x6FA9, 0x6FAA, 0x6FAB, - 0x6FAC, 0x6FAD, 0x6FAE, 0x6FAF, 0x6FB0, 0x6FB1, 0x6FB2, 0x6FB4, - 0x6FB5, 0x6FB7, 0x6FB8, 0x6FBA, 0x6FBB, 0x6FBC, 0x6FBD, 0x6FBE, - 0x6FBF, 0x6FC1, 0x6FC3, 0x6FC4, 0x6FC5, 0x6FC6, 0x6FC7, 0x6FC8, - 0x6FCA, 0x6FCB, 0x6FCC, 0x6FCD, 0x6FCE, 0x6FCF, 0x6FD0, 0x6FD3, - 0x6FD4, 0x6FD5, 0x6FD6, 0x6FD7, 0x6FD8, 0x6FD9, 0x6FDA, 0x6FDB, - 0x6FDC, 0x6FDD, 0x6FDF, 0x6FE2, 0x6FE3, 0x6FE4, 0x6FE5 - }, - { - 0x6FE6, 0x6FE7, 0x6FE8, 0x6FE9, 0x6FEA, 0x6FEB, 0x6FEC, 0x6FED, - 0x6FF0, 0x6FF1, 0x6FF2, 0x6FF3, 0x6FF4, 0x6FF5, 0x6FF6, 0x6FF7, - 0x6FF8, 0x6FF9, 0x6FFA, 0x6FFB, 0x6FFC, 0x6FFD, 0x6FFE, 0x6FFF, - 0x7000, 0x7001, 0x7002, 0x7003, 0x7004, 0x7005, 0x7006, 0x7007, - 0x7008, 0x7009, 0x700A, 0x700B, 0x700C, 0x700D, 0x700E, 0x700F, - 0x7010, 0x7012, 0x7013, 0x7014, 0x7015, 0x7016, 0x7017, 0x7018, - 0x7019, 0x701C, 0x701D, 0x701E, 0x701F, 0x7020, 0x7021, 0x7022, - 0x7024, 0x7025, 0x7026, 0x7027, 0x7028, 0x7029, 0x702A, 0x003F, - 0x702B, 0x702C, 0x702D, 0x702E, 0x702F, 0x7030, 0x7031, 0x7032, - 0x7033, 0x7034, 0x7036, 0x7037, 0x7038, 0x703A, 0x703B, 0x703C, - 0x703D, 0x703E, 0x703F, 0x7040, 0x7041, 0x7042, 0x7043, 0x7044, - 0x7045, 0x7046, 0x7047, 0x7048, 0x7049, 0x704A, 0x704B, 0x704D, - 0x704E, 0x7050, 0x7051, 0x7052, 0x7053, 0x7054, 0x7055, 0x7056, - 0x7057, 0x7058, 0x7059, 0x705A, 0x705B, 0x705C, 0x705D, 0x705F, - 0x7060, 0x7061, 0x7062, 0x7063, 0x7064, 0x7065, 0x7066, 0x7067, - 0x7068, 0x7069, 0x706A, 0x706E, 0x7071, 0x7072, 0x7073, 0x7074, - 0x7077, 0x7079, 0x707A, 0x707B, 0x707D, 0x7081, 0x7082, 0x7083, - 0x7084, 0x7086, 0x7087, 0x7088, 0x708B, 0x708C, 0x708D, 0x708F, - 0x7090, 0x7091, 0x7093, 0x7097, 0x7098, 0x709A, 0x709B, 0x709E, - 0x709F, 0x70A0, 0x70A1, 0x70A2, 0x70A3, 0x70A4, 0x70A5, 0x70A6, - 0x70A7, 0x70A8, 0x70A9, 0x70AA, 0x70B0, 0x70B2, 0x70B4, 0x70B5, - 0x70B6, 0x70BA, 0x70BE, 0x70BF, 0x70C4, 0x70C5, 0x70C6, 0x70C7, - 0x70C9, 0x70CB, 0x70CC, 0x70CD, 0x70CE, 0x70CF, 0x70D0, 0x70D1, - 0x70D2, 0x70D3, 0x70D4, 0x70D5, 0x70D6, 0x70D7, 0x70DA - }, - { - 0x70DC, 0x70DD, 0x70DE, 0x70E0, 0x70E1, 0x70E2, 0x70E3, 0x70E5, - 0x70EA, 0x70EE, 0x70F0, 0x70F1, 0x70F2, 0x70F3, 0x70F4, 0x70F5, - 0x70F6, 0x70F8, 0x70FA, 0x70FB, 0x70FC, 0x70FE, 0x70FF, 0x7100, - 0x7101, 0x7102, 0x7103, 0x7104, 0x7105, 0x7106, 0x7107, 0x7108, - 0x710B, 0x710C, 0x710D, 0x710E, 0x710F, 0x7111, 0x7112, 0x7114, - 0x7117, 0x711B, 0x711C, 0x711D, 0x711E, 0x711F, 0x7120, 0x7121, - 0x7122, 0x7123, 0x7124, 0x7125, 0x7127, 0x7128, 0x7129, 0x712A, - 0x712B, 0x712C, 0x712D, 0x712E, 0x7132, 0x7133, 0x7134, 0x003F, - 0x7135, 0x7137, 0x7138, 0x7139, 0x713A, 0x713B, 0x713C, 0x713D, - 0x713E, 0x713F, 0x7140, 0x7141, 0x7142, 0x7143, 0x7144, 0x7146, - 0x7147, 0x7148, 0x7149, 0x714B, 0x714D, 0x714F, 0x7150, 0x7151, - 0x7152, 0x7153, 0x7154, 0x7155, 0x7156, 0x7157, 0x7158, 0x7159, - 0x715A, 0x715B, 0x715D, 0x715F, 0x7160, 0x7161, 0x7162, 0x7163, - 0x7165, 0x7169, 0x716A, 0x716B, 0x716C, 0x716D, 0x716F, 0x7170, - 0x7171, 0x7174, 0x7175, 0x7176, 0x7177, 0x7179, 0x717B, 0x717C, - 0x717E, 0x717F, 0x7180, 0x7181, 0x7182, 0x7183, 0x7185, 0x7186, - 0x7187, 0x7188, 0x7189, 0x718B, 0x718C, 0x718D, 0x718E, 0x7190, - 0x7191, 0x7192, 0x7193, 0x7195, 0x7196, 0x7197, 0x719A, 0x719B, - 0x719C, 0x719D, 0x719E, 0x71A1, 0x71A2, 0x71A3, 0x71A4, 0x71A5, - 0x71A6, 0x71A7, 0x71A9, 0x71AA, 0x71AB, 0x71AD, 0x71AE, 0x71AF, - 0x71B0, 0x71B1, 0x71B2, 0x71B4, 0x71B6, 0x71B7, 0x71B8, 0x71BA, - 0x71BB, 0x71BC, 0x71BD, 0x71BE, 0x71BF, 0x71C0, 0x71C1, 0x71C2, - 0x71C4, 0x71C5, 0x71C6, 0x71C7, 0x71C8, 0x71C9, 0x71CA, 0x71CB, - 0x71CC, 0x71CD, 0x71CF, 0x71D0, 0x71D1, 0x71D2, 0x71D3 - }, - { - 0x71D6, 0x71D7, 0x71D8, 0x71D9, 0x71DA, 0x71DB, 0x71DC, 0x71DD, - 0x71DE, 0x71DF, 0x71E1, 0x71E2, 0x71E3, 0x71E4, 0x71E6, 0x71E8, - 0x71E9, 0x71EA, 0x71EB, 0x71EC, 0x71ED, 0x71EF, 0x71F0, 0x71F1, - 0x71F2, 0x71F3, 0x71F4, 0x71F5, 0x71F6, 0x71F7, 0x71F8, 0x71FA, - 0x71FB, 0x71FC, 0x71FD, 0x71FE, 0x71FF, 0x7200, 0x7201, 0x7202, - 0x7203, 0x7204, 0x7205, 0x7207, 0x7208, 0x7209, 0x720A, 0x720B, - 0x720C, 0x720D, 0x720E, 0x720F, 0x7210, 0x7211, 0x7212, 0x7213, - 0x7214, 0x7215, 0x7216, 0x7217, 0x7218, 0x7219, 0x721A, 0x003F, - 0x721B, 0x721C, 0x721E, 0x721F, 0x7220, 0x7221, 0x7222, 0x7223, - 0x7224, 0x7225, 0x7226, 0x7227, 0x7229, 0x722B, 0x722D, 0x722E, - 0x722F, 0x7232, 0x7233, 0x7234, 0x723A, 0x723C, 0x723E, 0x7240, - 0x7241, 0x7242, 0x7243, 0x7244, 0x7245, 0x7246, 0x7249, 0x724A, - 0x724B, 0x724E, 0x724F, 0x7250, 0x7251, 0x7253, 0x7254, 0x7255, - 0x7257, 0x7258, 0x725A, 0x725C, 0x725E, 0x7260, 0x7263, 0x7264, - 0x7265, 0x7268, 0x726A, 0x726B, 0x726C, 0x726D, 0x7270, 0x7271, - 0x7273, 0x7274, 0x7276, 0x7277, 0x7278, 0x727B, 0x727C, 0x727D, - 0x7282, 0x7283, 0x7285, 0x7286, 0x7287, 0x7288, 0x7289, 0x728C, - 0x728E, 0x7290, 0x7291, 0x7293, 0x7294, 0x7295, 0x7296, 0x7297, - 0x7298, 0x7299, 0x729A, 0x729B, 0x729C, 0x729D, 0x729E, 0x72A0, - 0x72A1, 0x72A2, 0x72A3, 0x72A4, 0x72A5, 0x72A6, 0x72A7, 0x72A8, - 0x72A9, 0x72AA, 0x72AB, 0x72AE, 0x72B1, 0x72B2, 0x72B3, 0x72B5, - 0x72BA, 0x72BB, 0x72BC, 0x72BD, 0x72BE, 0x72BF, 0x72C0, 0x72C5, - 0x72C6, 0x72C7, 0x72C9, 0x72CA, 0x72CB, 0x72CC, 0x72CF, 0x72D1, - 0x72D3, 0x72D4, 0x72D5, 0x72D6, 0x72D8, 0x72DA, 0x72DB - }, - { - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x3000, 0x3001, 0x3002, 0x00B7, 0x02C9, 0x02C7, 0x00A8, - 0x3003, 0x3005, 0x2014, 0xFF5E, 0x2016, 0x2026, 0x2018, 0x2019, - 0x201C, 0x201D, 0x3014, 0x3015, 0x3008, 0x3009, 0x300A, 0x300B, - 0x300C, 0x300D, 0x300E, 0x300F, 0x3016, 0x3017, 0x3010, 0x3011, - 0x00B1, 0x00D7, 0x00F7, 0x2236, 0x2227, 0x2228, 0x2211, 0x220F, - 0x222A, 0x2229, 0x2208, 0x2237, 0x221A, 0x22A5, 0x2225, 0x2220, - 0x2312, 0x2299, 0x222B, 0x222E, 0x2261, 0x224C, 0x2248, 0x223D, - 0x221D, 0x2260, 0x226E, 0x226F, 0x2264, 0x2265, 0x221E, 0x2235, - 0x2234, 0x2642, 0x2640, 0x00B0, 0x2032, 0x2033, 0x2103, 0xFF04, - 0x00A4, 0xFFE0, 0xFFE1, 0x2030, 0x00A7, 0x2116, 0x2606, 0x2605, - 0x25CB, 0x25CF, 0x25CE, 0x25C7, 0x25C6, 0x25A1, 0x25A0, 0x25B3, - 0x25B2, 0x203B, 0x2192, 0x2190, 0x2191, 0x2193, 0x3013 - }, - { - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x2170, 0x2171, 0x2172, 0x2173, 0x2174, 0x2175, 0x2176, - 0x2177, 0x2178, 0x2179, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x2488, 0x2489, 0x248A, 0x248B, 0x248C, 0x248D, 0x248E, - 0x248F, 0x2490, 0x2491, 0x2492, 0x2493, 0x2494, 0x2495, 0x2496, - 0x2497, 0x2498, 0x2499, 0x249A, 0x249B, 0x2474, 0x2475, 0x2476, - 0x2477, 0x2478, 0x2479, 0x247A, 0x247B, 0x247C, 0x247D, 0x247E, - 0x247F, 0x2480, 0x2481, 0x2482, 0x2483, 0x2484, 0x2485, 0x2486, - 0x2487, 0x2460, 0x2461, 0x2462, 0x2463, 0x2464, 0x2465, 0x2466, - 0x2467, 0x2468, 0x2469, 0x003F, 0x003F, 0x3220, 0x3221, 0x3222, - 0x3223, 0x3224, 0x3225, 0x3226, 0x3227, 0x3228, 0x3229, 0x003F, - 0x003F, 0x2160, 0x2161, 0x2162, 0x2163, 0x2164, 0x2165, 0x2166, - 0x2167, 0x2168, 0x2169, 0x216A, 0x216B, 0x003F, 0x003F - }, - { - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0xFF01, 0xFF02, 0xFF03, 0xFFE5, 0xFF05, 0xFF06, 0xFF07, - 0xFF08, 0xFF09, 0xFF0A, 0xFF0B, 0xFF0C, 0xFF0D, 0xFF0E, 0xFF0F, - 0xFF10, 0xFF11, 0xFF12, 0xFF13, 0xFF14, 0xFF15, 0xFF16, 0xFF17, - 0xFF18, 0xFF19, 0xFF1A, 0xFF1B, 0xFF1C, 0xFF1D, 0xFF1E, 0xFF1F, - 0xFF20, 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, 0xFF3B, 0xFF3C, 0xFF3D, 0xFF3E, 0xFF3F, - 0xFF40, 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, 0xFF5B, 0xFF5C, 0xFF5D, 0xFFE3 - }, - { - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 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, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F - }, - { - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 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, - 0x30E0, 0x30E1, 0x30E2, 0x30E3, 0x30E4, 0x30E5, 0x30E6, 0x30E7, - 0x30E8, 0x30E9, 0x30EA, 0x30EB, 0x30EC, 0x30ED, 0x30EE, 0x30EF, - 0x30F0, 0x30F1, 0x30F2, 0x30F3, 0x30F4, 0x30F5, 0x30F6, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F - }, - { - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397, - 0x0398, 0x0399, 0x039A, 0x039B, 0x039C, 0x039D, 0x039E, 0x039F, - 0x03A0, 0x03A1, 0x03A3, 0x03A4, 0x03A5, 0x03A6, 0x03A7, 0x03A8, - 0x03A9, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x03B1, 0x03B2, 0x03B3, 0x03B4, 0x03B5, 0x03B6, 0x03B7, - 0x03B8, 0x03B9, 0x03BA, 0x03BB, 0x03BC, 0x03BD, 0x03BE, 0x03BF, - 0x03C0, 0x03C1, 0x03C3, 0x03C4, 0x03C5, 0x03C6, 0x03C7, 0x03C8, - 0x03C9, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0xFE35, 0xFE36, 0xFE39, 0xFE3A, 0xFE3F, 0xFE40, 0xFE3D, 0xFE3E, - 0xFE41, 0xFE42, 0xFE43, 0xFE44, 0x003F, 0x003F, 0xFE3B, 0xFE3C, - 0xFE37, 0xFE38, 0xFE31, 0x003F, 0xFE33, 0xFE34, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F - }, - { - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 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, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0451, - 0x0436, 0x0437, 0x0438, 0x0439, 0x043A, 0x043B, 0x043C, 0x043D, - 0x043E, 0x043F, 0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, - 0x0446, 0x0447, 0x0448, 0x0449, 0x044A, 0x044B, 0x044C, 0x044D, - 0x044E, 0x044F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F - }, - { - 0x02CA, 0x02CB, 0x02D9, 0x2013, 0x2015, 0x2025, 0x2035, 0x2105, - 0x2109, 0x2196, 0x2197, 0x2198, 0x2199, 0x2215, 0x221F, 0x2223, - 0x2252, 0x2266, 0x2267, 0x22BF, 0x2550, 0x2551, 0x2552, 0x2553, - 0x2554, 0x2555, 0x2556, 0x2557, 0x2558, 0x2559, 0x255A, 0x255B, - 0x255C, 0x255D, 0x255E, 0x255F, 0x2560, 0x2561, 0x2562, 0x2563, - 0x2564, 0x2565, 0x2566, 0x2567, 0x2568, 0x2569, 0x256A, 0x256B, - 0x256C, 0x256D, 0x256E, 0x256F, 0x2570, 0x2571, 0x2572, 0x2573, - 0x2581, 0x2582, 0x2583, 0x2584, 0x2585, 0x2586, 0x2587, 0x003F, - 0x2588, 0x2589, 0x258A, 0x258B, 0x258C, 0x258D, 0x258E, 0x258F, - 0x2593, 0x2594, 0x2595, 0x25BC, 0x25BD, 0x25E2, 0x25E3, 0x25E4, - 0x25E5, 0x2609, 0x2295, 0x3012, 0x301D, 0x301E, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x0101, 0x00E1, 0x01CE, 0x00E0, 0x0113, 0x00E9, 0x011B, - 0x00E8, 0x012B, 0x00ED, 0x01D0, 0x00EC, 0x014D, 0x00F3, 0x01D2, - 0x00F2, 0x016B, 0x00FA, 0x01D4, 0x00F9, 0x01D6, 0x01D8, 0x01DA, - 0x01DC, 0x00FC, 0x00EA, 0x0251, 0x003F, 0x0144, 0x0148, 0x003F, - 0x0261, 0x003F, 0x003F, 0x003F, 0x003F, 0x3105, 0x3106, 0x3107, - 0x3108, 0x3109, 0x310A, 0x310B, 0x310C, 0x310D, 0x310E, 0x310F, - 0x3110, 0x3111, 0x3112, 0x3113, 0x3114, 0x3115, 0x3116, 0x3117, - 0x3118, 0x3119, 0x311A, 0x311B, 0x311C, 0x311D, 0x311E, 0x311F, - 0x3120, 0x3121, 0x3122, 0x3123, 0x3124, 0x3125, 0x3126, 0x3127, - 0x3128, 0x3129, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F - }, - { - 0x3021, 0x3022, 0x3023, 0x3024, 0x3025, 0x3026, 0x3027, 0x3028, - 0x3029, 0x32A3, 0x338E, 0x338F, 0x339C, 0x339D, 0x339E, 0x33A1, - 0x33C4, 0x33CE, 0x33D1, 0x33D2, 0x33D5, 0xFE30, 0xFFE2, 0xFFE4, - 0x003F, 0x2121, 0x3231, 0x003F, 0x2010, 0x003F, 0x003F, 0x003F, - 0x30FC, 0x309B, 0x309C, 0x30FD, 0x30FE, 0x3006, 0x309D, 0x309E, - 0xFE49, 0xFE4A, 0xFE4B, 0xFE4C, 0xFE4D, 0xFE4E, 0xFE4F, 0xFE50, - 0xFE51, 0xFE52, 0xFE54, 0xFE55, 0xFE56, 0xFE57, 0xFE59, 0xFE5A, - 0xFE5B, 0xFE5C, 0xFE5D, 0xFE5E, 0xFE5F, 0xFE60, 0xFE61, 0x003F, - 0xFE62, 0xFE63, 0xFE64, 0xFE65, 0xFE66, 0xFE68, 0xFE69, 0xFE6A, - 0xFE6B, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x3007, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x2500, 0x2501, 0x2502, 0x2503, - 0x2504, 0x2505, 0x2506, 0x2507, 0x2508, 0x2509, 0x250A, 0x250B, - 0x250C, 0x250D, 0x250E, 0x250F, 0x2510, 0x2511, 0x2512, 0x2513, - 0x2514, 0x2515, 0x2516, 0x2517, 0x2518, 0x2519, 0x251A, 0x251B, - 0x251C, 0x251D, 0x251E, 0x251F, 0x2520, 0x2521, 0x2522, 0x2523, - 0x2524, 0x2525, 0x2526, 0x2527, 0x2528, 0x2529, 0x252A, 0x252B, - 0x252C, 0x252D, 0x252E, 0x252F, 0x2530, 0x2531, 0x2532, 0x2533, - 0x2534, 0x2535, 0x2536, 0x2537, 0x2538, 0x2539, 0x253A, 0x253B, - 0x253C, 0x253D, 0x253E, 0x253F, 0x2540, 0x2541, 0x2542, 0x2543, - 0x2544, 0x2545, 0x2546, 0x2547, 0x2548, 0x2549, 0x254A, 0x254B, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F - }, - { - 0x72DC, 0x72DD, 0x72DF, 0x72E2, 0x72E3, 0x72E4, 0x72E5, 0x72E6, - 0x72E7, 0x72EA, 0x72EB, 0x72F5, 0x72F6, 0x72F9, 0x72FD, 0x72FE, - 0x72FF, 0x7300, 0x7302, 0x7304, 0x7305, 0x7306, 0x7307, 0x7308, - 0x7309, 0x730B, 0x730C, 0x730D, 0x730F, 0x7310, 0x7311, 0x7312, - 0x7314, 0x7318, 0x7319, 0x731A, 0x731F, 0x7320, 0x7323, 0x7324, - 0x7326, 0x7327, 0x7328, 0x732D, 0x732F, 0x7330, 0x7332, 0x7333, - 0x7335, 0x7336, 0x733A, 0x733B, 0x733C, 0x733D, 0x7340, 0x7341, - 0x7342, 0x7343, 0x7344, 0x7345, 0x7346, 0x7347, 0x7348, 0x003F, - 0x7349, 0x734A, 0x734B, 0x734C, 0x734E, 0x734F, 0x7351, 0x7353, - 0x7354, 0x7355, 0x7356, 0x7358, 0x7359, 0x735A, 0x735B, 0x735C, - 0x735D, 0x735E, 0x735F, 0x7361, 0x7362, 0x7363, 0x7364, 0x7365, - 0x7366, 0x7367, 0x7368, 0x7369, 0x736A, 0x736B, 0x736E, 0x7370, - 0x7371, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F - }, - { - 0x7372, 0x7373, 0x7374, 0x7375, 0x7376, 0x7377, 0x7378, 0x7379, - 0x737A, 0x737B, 0x737C, 0x737D, 0x737F, 0x7380, 0x7381, 0x7382, - 0x7383, 0x7385, 0x7386, 0x7388, 0x738A, 0x738C, 0x738D, 0x738F, - 0x7390, 0x7392, 0x7393, 0x7394, 0x7395, 0x7397, 0x7398, 0x7399, - 0x739A, 0x739C, 0x739D, 0x739E, 0x73A0, 0x73A1, 0x73A3, 0x73A4, - 0x73A5, 0x73A6, 0x73A7, 0x73A8, 0x73AA, 0x73AC, 0x73AD, 0x73B1, - 0x73B4, 0x73B5, 0x73B6, 0x73B8, 0x73B9, 0x73BC, 0x73BD, 0x73BE, - 0x73BF, 0x73C1, 0x73C3, 0x73C4, 0x73C5, 0x73C6, 0x73C7, 0x003F, - 0x73CB, 0x73CC, 0x73CE, 0x73D2, 0x73D3, 0x73D4, 0x73D5, 0x73D6, - 0x73D7, 0x73D8, 0x73DA, 0x73DB, 0x73DC, 0x73DD, 0x73DF, 0x73E1, - 0x73E2, 0x73E3, 0x73E4, 0x73E6, 0x73E8, 0x73EA, 0x73EB, 0x73EC, - 0x73EE, 0x73EF, 0x73F0, 0x73F1, 0x73F3, 0x73F4, 0x73F5, 0x73F6, - 0x73F7, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F - }, - { - 0x73F8, 0x73F9, 0x73FA, 0x73FB, 0x73FC, 0x73FD, 0x73FE, 0x73FF, - 0x7400, 0x7401, 0x7402, 0x7404, 0x7407, 0x7408, 0x740B, 0x740C, - 0x740D, 0x740E, 0x7411, 0x7412, 0x7413, 0x7414, 0x7415, 0x7416, - 0x7417, 0x7418, 0x7419, 0x741C, 0x741D, 0x741E, 0x741F, 0x7420, - 0x7421, 0x7423, 0x7424, 0x7427, 0x7429, 0x742B, 0x742D, 0x742F, - 0x7431, 0x7432, 0x7437, 0x7438, 0x7439, 0x743A, 0x743B, 0x743D, - 0x743E, 0x743F, 0x7440, 0x7442, 0x7443, 0x7444, 0x7445, 0x7446, - 0x7447, 0x7448, 0x7449, 0x744A, 0x744B, 0x744C, 0x744D, 0x003F, - 0x744E, 0x744F, 0x7450, 0x7451, 0x7452, 0x7453, 0x7454, 0x7456, - 0x7458, 0x745D, 0x7460, 0x7461, 0x7462, 0x7463, 0x7464, 0x7465, - 0x7466, 0x7467, 0x7468, 0x7469, 0x746A, 0x746B, 0x746C, 0x746E, - 0x746F, 0x7471, 0x7472, 0x7473, 0x7474, 0x7475, 0x7478, 0x7479, - 0x747A, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F - }, - { - 0x747B, 0x747C, 0x747D, 0x747F, 0x7482, 0x7484, 0x7485, 0x7486, - 0x7488, 0x7489, 0x748A, 0x748C, 0x748D, 0x748F, 0x7491, 0x7492, - 0x7493, 0x7494, 0x7495, 0x7496, 0x7497, 0x7498, 0x7499, 0x749A, - 0x749B, 0x749D, 0x749F, 0x74A0, 0x74A1, 0x74A2, 0x74A3, 0x74A4, - 0x74A5, 0x74A6, 0x74AA, 0x74AB, 0x74AC, 0x74AD, 0x74AE, 0x74AF, - 0x74B0, 0x74B1, 0x74B2, 0x74B3, 0x74B4, 0x74B5, 0x74B6, 0x74B7, - 0x74B8, 0x74B9, 0x74BB, 0x74BC, 0x74BD, 0x74BE, 0x74BF, 0x74C0, - 0x74C1, 0x74C2, 0x74C3, 0x74C4, 0x74C5, 0x74C6, 0x74C7, 0x003F, - 0x74C8, 0x74C9, 0x74CA, 0x74CB, 0x74CC, 0x74CD, 0x74CE, 0x74CF, - 0x74D0, 0x74D1, 0x74D3, 0x74D4, 0x74D5, 0x74D6, 0x74D7, 0x74D8, - 0x74D9, 0x74DA, 0x74DB, 0x74DD, 0x74DF, 0x74E1, 0x74E5, 0x74E7, - 0x74E8, 0x74E9, 0x74EA, 0x74EB, 0x74EC, 0x74ED, 0x74F0, 0x74F1, - 0x74F2, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F - }, - { - 0x74F3, 0x74F5, 0x74F8, 0x74F9, 0x74FA, 0x74FB, 0x74FC, 0x74FD, - 0x74FE, 0x7500, 0x7501, 0x7502, 0x7503, 0x7505, 0x7506, 0x7507, - 0x7508, 0x7509, 0x750A, 0x750B, 0x750C, 0x750E, 0x7510, 0x7512, - 0x7514, 0x7515, 0x7516, 0x7517, 0x751B, 0x751D, 0x751E, 0x7520, - 0x7521, 0x7522, 0x7523, 0x7524, 0x7526, 0x7527, 0x752A, 0x752E, - 0x7534, 0x7536, 0x7539, 0x753C, 0x753D, 0x753F, 0x7541, 0x7542, - 0x7543, 0x7544, 0x7546, 0x7547, 0x7549, 0x754A, 0x754D, 0x7550, - 0x7551, 0x7552, 0x7553, 0x7555, 0x7556, 0x7557, 0x7558, 0x003F, - 0x755D, 0x755E, 0x755F, 0x7560, 0x7561, 0x7562, 0x7563, 0x7564, - 0x7567, 0x7568, 0x7569, 0x756B, 0x756C, 0x756D, 0x756E, 0x756F, - 0x7570, 0x7571, 0x7573, 0x7575, 0x7576, 0x7577, 0x757A, 0x757B, - 0x757C, 0x757D, 0x757E, 0x7580, 0x7581, 0x7582, 0x7584, 0x7585, - 0x7587, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F - }, - { - 0x7588, 0x7589, 0x758A, 0x758C, 0x758D, 0x758E, 0x7590, 0x7593, - 0x7595, 0x7598, 0x759B, 0x759C, 0x759E, 0x75A2, 0x75A6, 0x75A7, - 0x75A8, 0x75A9, 0x75AA, 0x75AD, 0x75B6, 0x75B7, 0x75BA, 0x75BB, - 0x75BF, 0x75C0, 0x75C1, 0x75C6, 0x75CB, 0x75CC, 0x75CE, 0x75CF, - 0x75D0, 0x75D1, 0x75D3, 0x75D7, 0x75D9, 0x75DA, 0x75DC, 0x75DD, - 0x75DF, 0x75E0, 0x75E1, 0x75E5, 0x75E9, 0x75EC, 0x75ED, 0x75EE, - 0x75EF, 0x75F2, 0x75F3, 0x75F5, 0x75F6, 0x75F7, 0x75F8, 0x75FA, - 0x75FB, 0x75FD, 0x75FE, 0x7602, 0x7604, 0x7606, 0x7607, 0x003F, - 0x7608, 0x7609, 0x760B, 0x760D, 0x760E, 0x760F, 0x7611, 0x7612, - 0x7613, 0x7614, 0x7616, 0x761A, 0x761C, 0x761D, 0x761E, 0x7621, - 0x7623, 0x7627, 0x7628, 0x762C, 0x762E, 0x762F, 0x7631, 0x7632, - 0x7636, 0x7637, 0x7639, 0x763A, 0x763B, 0x763D, 0x7641, 0x7642, - 0x7644, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F - }, - { - 0x7645, 0x7646, 0x7647, 0x7648, 0x7649, 0x764A, 0x764B, 0x764E, - 0x764F, 0x7650, 0x7651, 0x7652, 0x7653, 0x7655, 0x7657, 0x7658, - 0x7659, 0x765A, 0x765B, 0x765D, 0x765F, 0x7660, 0x7661, 0x7662, - 0x7664, 0x7665, 0x7666, 0x7667, 0x7668, 0x7669, 0x766A, 0x766C, - 0x766D, 0x766E, 0x7670, 0x7671, 0x7672, 0x7673, 0x7674, 0x7675, - 0x7676, 0x7677, 0x7679, 0x767A, 0x767C, 0x767F, 0x7680, 0x7681, - 0x7683, 0x7685, 0x7689, 0x768A, 0x768C, 0x768D, 0x768F, 0x7690, - 0x7692, 0x7694, 0x7695, 0x7697, 0x7698, 0x769A, 0x769B, 0x003F, - 0x769C, 0x769D, 0x769E, 0x769F, 0x76A0, 0x76A1, 0x76A2, 0x76A3, - 0x76A5, 0x76A6, 0x76A7, 0x76A8, 0x76A9, 0x76AA, 0x76AB, 0x76AC, - 0x76AD, 0x76AF, 0x76B0, 0x76B3, 0x76B5, 0x76B6, 0x76B7, 0x76B8, - 0x76B9, 0x76BA, 0x76BB, 0x76BC, 0x76BD, 0x76BE, 0x76C0, 0x76C1, - 0x76C3, 0x554A, 0x963F, 0x57C3, 0x6328, 0x54CE, 0x5509, 0x54C0, - 0x7691, 0x764C, 0x853C, 0x77EE, 0x827E, 0x788D, 0x7231, 0x9698, - 0x978D, 0x6C28, 0x5B89, 0x4FFA, 0x6309, 0x6697, 0x5CB8, 0x80FA, - 0x6848, 0x80AE, 0x6602, 0x76CE, 0x51F9, 0x6556, 0x71AC, 0x7FF1, - 0x8884, 0x50B2, 0x5965, 0x61CA, 0x6FB3, 0x82AD, 0x634C, 0x6252, - 0x53ED, 0x5427, 0x7B06, 0x516B, 0x75A4, 0x5DF4, 0x62D4, 0x8DCB, - 0x9776, 0x628A, 0x8019, 0x575D, 0x9738, 0x7F62, 0x7238, 0x767D, - 0x67CF, 0x767E, 0x6446, 0x4F70, 0x8D25, 0x62DC, 0x7A17, 0x6591, - 0x73ED, 0x642C, 0x6273, 0x822C, 0x9881, 0x677F, 0x7248, 0x626E, - 0x62CC, 0x4F34, 0x74E3, 0x534A, 0x529E, 0x7ECA, 0x90A6, 0x5E2E, - 0x6886, 0x699C, 0x8180, 0x7ED1, 0x68D2, 0x78C5, 0x868C, 0x9551, - 0x508D, 0x8C24, 0x82DE, 0x80DE, 0x5305, 0x8912, 0x5265 - }, - { - 0x76C4, 0x76C7, 0x76C9, 0x76CB, 0x76CC, 0x76D3, 0x76D5, 0x76D9, - 0x76DA, 0x76DC, 0x76DD, 0x76DE, 0x76E0, 0x76E1, 0x76E2, 0x76E3, - 0x76E4, 0x76E6, 0x76E7, 0x76E8, 0x76E9, 0x76EA, 0x76EB, 0x76EC, - 0x76ED, 0x76F0, 0x76F3, 0x76F5, 0x76F6, 0x76F7, 0x76FA, 0x76FB, - 0x76FD, 0x76FF, 0x7700, 0x7702, 0x7703, 0x7705, 0x7706, 0x770A, - 0x770C, 0x770E, 0x770F, 0x7710, 0x7711, 0x7712, 0x7713, 0x7714, - 0x7715, 0x7716, 0x7717, 0x7718, 0x771B, 0x771C, 0x771D, 0x771E, - 0x7721, 0x7723, 0x7724, 0x7725, 0x7727, 0x772A, 0x772B, 0x003F, - 0x772C, 0x772E, 0x7730, 0x7731, 0x7732, 0x7733, 0x7734, 0x7739, - 0x773B, 0x773D, 0x773E, 0x773F, 0x7742, 0x7744, 0x7745, 0x7746, - 0x7748, 0x7749, 0x774A, 0x774B, 0x774C, 0x774D, 0x774E, 0x774F, - 0x7752, 0x7753, 0x7754, 0x7755, 0x7756, 0x7757, 0x7758, 0x7759, - 0x775C, 0x8584, 0x96F9, 0x4FDD, 0x5821, 0x9971, 0x5B9D, 0x62B1, - 0x62A5, 0x66B4, 0x8C79, 0x9C8D, 0x7206, 0x676F, 0x7891, 0x60B2, - 0x5351, 0x5317, 0x8F88, 0x80CC, 0x8D1D, 0x94A1, 0x500D, 0x72C8, - 0x5907, 0x60EB, 0x7119, 0x88AB, 0x5954, 0x82EF, 0x672C, 0x7B28, - 0x5D29, 0x7EF7, 0x752D, 0x6CF5, 0x8E66, 0x8FF8, 0x903C, 0x9F3B, - 0x6BD4, 0x9119, 0x7B14, 0x5F7C, 0x78A7, 0x84D6, 0x853D, 0x6BD5, - 0x6BD9, 0x6BD6, 0x5E01, 0x5E87, 0x75F9, 0x95ED, 0x655D, 0x5F0A, - 0x5FC5, 0x8F9F, 0x58C1, 0x81C2, 0x907F, 0x965B, 0x97AD, 0x8FB9, - 0x7F16, 0x8D2C, 0x6241, 0x4FBF, 0x53D8, 0x535E, 0x8FA8, 0x8FA9, - 0x8FAB, 0x904D, 0x6807, 0x5F6A, 0x8198, 0x8868, 0x9CD6, 0x618B, - 0x522B, 0x762A, 0x5F6C, 0x658C, 0x6FD2, 0x6EE8, 0x5BBE, 0x6448, - 0x5175, 0x51B0, 0x67C4, 0x4E19, 0x79C9, 0x997C, 0x70B3 - }, - { - 0x775D, 0x775E, 0x775F, 0x7760, 0x7764, 0x7767, 0x7769, 0x776A, - 0x776D, 0x776E, 0x776F, 0x7770, 0x7771, 0x7772, 0x7773, 0x7774, - 0x7775, 0x7776, 0x7777, 0x7778, 0x777A, 0x777B, 0x777C, 0x7781, - 0x7782, 0x7783, 0x7786, 0x7787, 0x7788, 0x7789, 0x778A, 0x778B, - 0x778F, 0x7790, 0x7793, 0x7794, 0x7795, 0x7796, 0x7797, 0x7798, - 0x7799, 0x779A, 0x779B, 0x779C, 0x779D, 0x779E, 0x77A1, 0x77A3, - 0x77A4, 0x77A6, 0x77A8, 0x77AB, 0x77AD, 0x77AE, 0x77AF, 0x77B1, - 0x77B2, 0x77B4, 0x77B6, 0x77B7, 0x77B8, 0x77B9, 0x77BA, 0x003F, - 0x77BC, 0x77BE, 0x77C0, 0x77C1, 0x77C2, 0x77C3, 0x77C4, 0x77C5, - 0x77C6, 0x77C7, 0x77C8, 0x77C9, 0x77CA, 0x77CB, 0x77CC, 0x77CE, - 0x77CF, 0x77D0, 0x77D1, 0x77D2, 0x77D3, 0x77D4, 0x77D5, 0x77D6, - 0x77D8, 0x77D9, 0x77DA, 0x77DD, 0x77DE, 0x77DF, 0x77E0, 0x77E1, - 0x77E4, 0x75C5, 0x5E76, 0x73BB, 0x83E0, 0x64AD, 0x62E8, 0x94B5, - 0x6CE2, 0x535A, 0x52C3, 0x640F, 0x94C2, 0x7B94, 0x4F2F, 0x5E1B, - 0x8236, 0x8116, 0x818A, 0x6E24, 0x6CCA, 0x9A73, 0x6355, 0x535C, - 0x54FA, 0x8865, 0x57E0, 0x4E0D, 0x5E03, 0x6B65, 0x7C3F, 0x90E8, - 0x6016, 0x64E6, 0x731C, 0x88C1, 0x6750, 0x624D, 0x8D22, 0x776C, - 0x8E29, 0x91C7, 0x5F69, 0x83DC, 0x8521, 0x9910, 0x53C2, 0x8695, - 0x6B8B, 0x60ED, 0x60E8, 0x707F, 0x82CD, 0x8231, 0x4ED3, 0x6CA7, - 0x85CF, 0x64CD, 0x7CD9, 0x69FD, 0x66F9, 0x8349, 0x5395, 0x7B56, - 0x4FA7, 0x518C, 0x6D4B, 0x5C42, 0x8E6D, 0x63D2, 0x53C9, 0x832C, - 0x8336, 0x67E5, 0x78B4, 0x643D, 0x5BDF, 0x5C94, 0x5DEE, 0x8BE7, - 0x62C6, 0x67F4, 0x8C7A, 0x6400, 0x63BA, 0x8749, 0x998B, 0x8C17, - 0x7F20, 0x94F2, 0x4EA7, 0x9610, 0x98A4, 0x660C, 0x7316 - }, - { - 0x77E6, 0x77E8, 0x77EA, 0x77EF, 0x77F0, 0x77F1, 0x77F2, 0x77F4, - 0x77F5, 0x77F7, 0x77F9, 0x77FA, 0x77FB, 0x77FC, 0x7803, 0x7804, - 0x7805, 0x7806, 0x7807, 0x7808, 0x780A, 0x780B, 0x780E, 0x780F, - 0x7810, 0x7813, 0x7815, 0x7819, 0x781B, 0x781E, 0x7820, 0x7821, - 0x7822, 0x7824, 0x7828, 0x782A, 0x782B, 0x782E, 0x782F, 0x7831, - 0x7832, 0x7833, 0x7835, 0x7836, 0x783D, 0x783F, 0x7841, 0x7842, - 0x7843, 0x7844, 0x7846, 0x7848, 0x7849, 0x784A, 0x784B, 0x784D, - 0x784F, 0x7851, 0x7853, 0x7854, 0x7858, 0x7859, 0x785A, 0x003F, - 0x785B, 0x785C, 0x785E, 0x785F, 0x7860, 0x7861, 0x7862, 0x7863, - 0x7864, 0x7865, 0x7866, 0x7867, 0x7868, 0x7869, 0x786F, 0x7870, - 0x7871, 0x7872, 0x7873, 0x7874, 0x7875, 0x7876, 0x7878, 0x7879, - 0x787A, 0x787B, 0x787D, 0x787E, 0x787F, 0x7880, 0x7881, 0x7882, - 0x7883, 0x573A, 0x5C1D, 0x5E38, 0x957F, 0x507F, 0x80A0, 0x5382, - 0x655E, 0x7545, 0x5531, 0x5021, 0x8D85, 0x6284, 0x949E, 0x671D, - 0x5632, 0x6F6E, 0x5DE2, 0x5435, 0x7092, 0x8F66, 0x626F, 0x64A4, - 0x63A3, 0x5F7B, 0x6F88, 0x90F4, 0x81E3, 0x8FB0, 0x5C18, 0x6668, - 0x5FF1, 0x6C89, 0x9648, 0x8D81, 0x886C, 0x6491, 0x79F0, 0x57CE, - 0x6A59, 0x6210, 0x5448, 0x4E58, 0x7A0B, 0x60E9, 0x6F84, 0x8BDA, - 0x627F, 0x901E, 0x9A8B, 0x79E4, 0x5403, 0x75F4, 0x6301, 0x5319, - 0x6C60, 0x8FDF, 0x5F1B, 0x9A70, 0x803B, 0x9F7F, 0x4F88, 0x5C3A, - 0x8D64, 0x7FC5, 0x65A5, 0x70BD, 0x5145, 0x51B2, 0x866B, 0x5D07, - 0x5BA0, 0x62BD, 0x916C, 0x7574, 0x8E0C, 0x7A20, 0x6101, 0x7B79, - 0x4EC7, 0x7EF8, 0x7785, 0x4E11, 0x81ED, 0x521D, 0x51FA, 0x6A71, - 0x53A8, 0x8E87, 0x9504, 0x96CF, 0x6EC1, 0x9664, 0x695A - }, - { - 0x7884, 0x7885, 0x7886, 0x7888, 0x788A, 0x788B, 0x788F, 0x7890, - 0x7892, 0x7894, 0x7895, 0x7896, 0x7899, 0x789D, 0x789E, 0x78A0, - 0x78A2, 0x78A4, 0x78A6, 0x78A8, 0x78A9, 0x78AA, 0x78AB, 0x78AC, - 0x78AD, 0x78AE, 0x78AF, 0x78B5, 0x78B6, 0x78B7, 0x78B8, 0x78BA, - 0x78BB, 0x78BC, 0x78BD, 0x78BF, 0x78C0, 0x78C2, 0x78C3, 0x78C4, - 0x78C6, 0x78C7, 0x78C8, 0x78CC, 0x78CD, 0x78CE, 0x78CF, 0x78D1, - 0x78D2, 0x78D3, 0x78D6, 0x78D7, 0x78D8, 0x78DA, 0x78DB, 0x78DC, - 0x78DD, 0x78DE, 0x78DF, 0x78E0, 0x78E1, 0x78E2, 0x78E3, 0x003F, - 0x78E4, 0x78E5, 0x78E6, 0x78E7, 0x78E9, 0x78EA, 0x78EB, 0x78ED, - 0x78EE, 0x78EF, 0x78F0, 0x78F1, 0x78F3, 0x78F5, 0x78F6, 0x78F8, - 0x78F9, 0x78FB, 0x78FC, 0x78FD, 0x78FE, 0x78FF, 0x7900, 0x7902, - 0x7903, 0x7904, 0x7906, 0x7907, 0x7908, 0x7909, 0x790A, 0x790B, - 0x790C, 0x7840, 0x50A8, 0x77D7, 0x6410, 0x89E6, 0x5904, 0x63E3, - 0x5DDD, 0x7A7F, 0x693D, 0x4F20, 0x8239, 0x5598, 0x4E32, 0x75AE, - 0x7A97, 0x5E62, 0x5E8A, 0x95EF, 0x521B, 0x5439, 0x708A, 0x6376, - 0x9524, 0x5782, 0x6625, 0x693F, 0x9187, 0x5507, 0x6DF3, 0x7EAF, - 0x8822, 0x6233, 0x7EF0, 0x75B5, 0x8328, 0x78C1, 0x96CC, 0x8F9E, - 0x6148, 0x74F7, 0x8BCD, 0x6B64, 0x523A, 0x8D50, 0x6B21, 0x806A, - 0x8471, 0x56F1, 0x5306, 0x4ECE, 0x4E1B, 0x51D1, 0x7C97, 0x918B, - 0x7C07, 0x4FC3, 0x8E7F, 0x7BE1, 0x7A9C, 0x6467, 0x5D14, 0x50AC, - 0x8106, 0x7601, 0x7CB9, 0x6DEC, 0x7FE0, 0x6751, 0x5B58, 0x5BF8, - 0x78CB, 0x64AE, 0x6413, 0x63AA, 0x632B, 0x9519, 0x642D, 0x8FBE, - 0x7B54, 0x7629, 0x6253, 0x5927, 0x5446, 0x6B79, 0x50A3, 0x6234, - 0x5E26, 0x6B86, 0x4EE3, 0x8D37, 0x888B, 0x5F85, 0x902E - }, - { - 0x790D, 0x790E, 0x790F, 0x7910, 0x7911, 0x7912, 0x7914, 0x7915, - 0x7916, 0x7917, 0x7918, 0x7919, 0x791A, 0x791B, 0x791C, 0x791D, - 0x791F, 0x7920, 0x7921, 0x7922, 0x7923, 0x7925, 0x7926, 0x7927, - 0x7928, 0x7929, 0x792A, 0x792B, 0x792C, 0x792D, 0x792E, 0x792F, - 0x7930, 0x7931, 0x7932, 0x7933, 0x7935, 0x7936, 0x7937, 0x7938, - 0x7939, 0x793D, 0x793F, 0x7942, 0x7943, 0x7944, 0x7945, 0x7947, - 0x794A, 0x794B, 0x794C, 0x794D, 0x794E, 0x794F, 0x7950, 0x7951, - 0x7952, 0x7954, 0x7955, 0x7958, 0x7959, 0x7961, 0x7963, 0x003F, - 0x7964, 0x7966, 0x7969, 0x796A, 0x796B, 0x796C, 0x796E, 0x7970, - 0x7971, 0x7972, 0x7973, 0x7974, 0x7975, 0x7976, 0x7979, 0x797B, - 0x797C, 0x797D, 0x797E, 0x797F, 0x7982, 0x7983, 0x7986, 0x7987, - 0x7988, 0x7989, 0x798B, 0x798C, 0x798D, 0x798E, 0x7990, 0x7991, - 0x7992, 0x6020, 0x803D, 0x62C5, 0x4E39, 0x5355, 0x90F8, 0x63B8, - 0x80C6, 0x65E6, 0x6C2E, 0x4F46, 0x60EE, 0x6DE1, 0x8BDE, 0x5F39, - 0x86CB, 0x5F53, 0x6321, 0x515A, 0x8361, 0x6863, 0x5200, 0x6363, - 0x8E48, 0x5012, 0x5C9B, 0x7977, 0x5BFC, 0x5230, 0x7A3B, 0x60BC, - 0x9053, 0x76D7, 0x5FB7, 0x5F97, 0x7684, 0x8E6C, 0x706F, 0x767B, - 0x7B49, 0x77AA, 0x51F3, 0x9093, 0x5824, 0x4F4E, 0x6EF4, 0x8FEA, - 0x654C, 0x7B1B, 0x72C4, 0x6DA4, 0x7FDF, 0x5AE1, 0x62B5, 0x5E95, - 0x5730, 0x8482, 0x7B2C, 0x5E1D, 0x5F1F, 0x9012, 0x7F14, 0x98A0, - 0x6382, 0x6EC7, 0x7898, 0x70B9, 0x5178, 0x975B, 0x57AB, 0x7535, - 0x4F43, 0x7538, 0x5E97, 0x60E6, 0x5960, 0x6DC0, 0x6BBF, 0x7889, - 0x53FC, 0x96D5, 0x51CB, 0x5201, 0x6389, 0x540A, 0x9493, 0x8C03, - 0x8DCC, 0x7239, 0x789F, 0x8776, 0x8FED, 0x8C0D, 0x53E0 - }, - { - 0x7993, 0x7994, 0x7995, 0x7996, 0x7997, 0x7998, 0x7999, 0x799B, - 0x799C, 0x799D, 0x799E, 0x799F, 0x79A0, 0x79A1, 0x79A2, 0x79A3, - 0x79A4, 0x79A5, 0x79A6, 0x79A8, 0x79A9, 0x79AA, 0x79AB, 0x79AC, - 0x79AD, 0x79AE, 0x79AF, 0x79B0, 0x79B1, 0x79B2, 0x79B4, 0x79B5, - 0x79B6, 0x79B7, 0x79B8, 0x79BC, 0x79BF, 0x79C2, 0x79C4, 0x79C5, - 0x79C7, 0x79C8, 0x79CA, 0x79CC, 0x79CE, 0x79CF, 0x79D0, 0x79D3, - 0x79D4, 0x79D6, 0x79D7, 0x79D9, 0x79DA, 0x79DB, 0x79DC, 0x79DD, - 0x79DE, 0x79E0, 0x79E1, 0x79E2, 0x79E5, 0x79E8, 0x79EA, 0x003F, - 0x79EC, 0x79EE, 0x79F1, 0x79F2, 0x79F3, 0x79F4, 0x79F5, 0x79F6, - 0x79F7, 0x79F9, 0x79FA, 0x79FC, 0x79FE, 0x79FF, 0x7A01, 0x7A04, - 0x7A05, 0x7A07, 0x7A08, 0x7A09, 0x7A0A, 0x7A0C, 0x7A0F, 0x7A10, - 0x7A11, 0x7A12, 0x7A13, 0x7A15, 0x7A16, 0x7A18, 0x7A19, 0x7A1B, - 0x7A1C, 0x4E01, 0x76EF, 0x53EE, 0x9489, 0x9876, 0x9F0E, 0x952D, - 0x5B9A, 0x8BA2, 0x4E22, 0x4E1C, 0x51AC, 0x8463, 0x61C2, 0x52A8, - 0x680B, 0x4F97, 0x606B, 0x51BB, 0x6D1E, 0x515C, 0x6296, 0x6597, - 0x9661, 0x8C46, 0x9017, 0x75D8, 0x90FD, 0x7763, 0x6BD2, 0x728A, - 0x72EC, 0x8BFB, 0x5835, 0x7779, 0x8D4C, 0x675C, 0x9540, 0x809A, - 0x5EA6, 0x6E21, 0x5992, 0x7AEF, 0x77ED, 0x953B, 0x6BB5, 0x65AD, - 0x7F0E, 0x5806, 0x5151, 0x961F, 0x5BF9, 0x58A9, 0x5428, 0x8E72, - 0x6566, 0x987F, 0x56E4, 0x949D, 0x76FE, 0x9041, 0x6387, 0x54C6, - 0x591A, 0x593A, 0x579B, 0x8EB2, 0x6735, 0x8DFA, 0x8235, 0x5241, - 0x60F0, 0x5815, 0x86FE, 0x5CE8, 0x9E45, 0x4FC4, 0x989D, 0x8BB9, - 0x5A25, 0x6076, 0x5384, 0x627C, 0x904F, 0x9102, 0x997F, 0x6069, - 0x800C, 0x513F, 0x8033, 0x5C14, 0x9975, 0x6D31, 0x4E8C - }, - { - 0x7A1D, 0x7A1F, 0x7A21, 0x7A22, 0x7A24, 0x7A25, 0x7A26, 0x7A27, - 0x7A28, 0x7A29, 0x7A2A, 0x7A2B, 0x7A2C, 0x7A2D, 0x7A2E, 0x7A2F, - 0x7A30, 0x7A31, 0x7A32, 0x7A34, 0x7A35, 0x7A36, 0x7A38, 0x7A3A, - 0x7A3E, 0x7A40, 0x7A41, 0x7A42, 0x7A43, 0x7A44, 0x7A45, 0x7A47, - 0x7A48, 0x7A49, 0x7A4A, 0x7A4B, 0x7A4C, 0x7A4D, 0x7A4E, 0x7A4F, - 0x7A50, 0x7A52, 0x7A53, 0x7A54, 0x7A55, 0x7A56, 0x7A58, 0x7A59, - 0x7A5A, 0x7A5B, 0x7A5C, 0x7A5D, 0x7A5E, 0x7A5F, 0x7A60, 0x7A61, - 0x7A62, 0x7A63, 0x7A64, 0x7A65, 0x7A66, 0x7A67, 0x7A68, 0x003F, - 0x7A69, 0x7A6A, 0x7A6B, 0x7A6C, 0x7A6D, 0x7A6E, 0x7A6F, 0x7A71, - 0x7A72, 0x7A73, 0x7A75, 0x7A7B, 0x7A7C, 0x7A7D, 0x7A7E, 0x7A82, - 0x7A85, 0x7A87, 0x7A89, 0x7A8A, 0x7A8B, 0x7A8C, 0x7A8E, 0x7A8F, - 0x7A90, 0x7A93, 0x7A94, 0x7A99, 0x7A9A, 0x7A9B, 0x7A9E, 0x7AA1, - 0x7AA2, 0x8D30, 0x53D1, 0x7F5A, 0x7B4F, 0x4F10, 0x4E4F, 0x9600, - 0x6CD5, 0x73D0, 0x85E9, 0x5E06, 0x756A, 0x7FFB, 0x6A0A, 0x77FE, - 0x9492, 0x7E41, 0x51E1, 0x70E6, 0x53CD, 0x8FD4, 0x8303, 0x8D29, - 0x72AF, 0x996D, 0x6CDB, 0x574A, 0x82B3, 0x65B9, 0x80AA, 0x623F, - 0x9632, 0x59A8, 0x4EFF, 0x8BBF, 0x7EBA, 0x653E, 0x83F2, 0x975E, - 0x5561, 0x98DE, 0x80A5, 0x532A, 0x8BFD, 0x5420, 0x80BA, 0x5E9F, - 0x6CB8, 0x8D39, 0x82AC, 0x915A, 0x5429, 0x6C1B, 0x5206, 0x7EB7, - 0x575F, 0x711A, 0x6C7E, 0x7C89, 0x594B, 0x4EFD, 0x5FFF, 0x6124, - 0x7CAA, 0x4E30, 0x5C01, 0x67AB, 0x8702, 0x5CF0, 0x950B, 0x98CE, - 0x75AF, 0x70FD, 0x9022, 0x51AF, 0x7F1D, 0x8BBD, 0x5949, 0x51E4, - 0x4F5B, 0x5426, 0x592B, 0x6577, 0x80A4, 0x5B75, 0x6276, 0x62C2, - 0x8F90, 0x5E45, 0x6C1F, 0x7B26, 0x4F0F, 0x4FD8, 0x670D - }, - { - 0x7AA3, 0x7AA4, 0x7AA7, 0x7AA9, 0x7AAA, 0x7AAB, 0x7AAE, 0x7AAF, - 0x7AB0, 0x7AB1, 0x7AB2, 0x7AB4, 0x7AB5, 0x7AB6, 0x7AB7, 0x7AB8, - 0x7AB9, 0x7ABA, 0x7ABB, 0x7ABC, 0x7ABD, 0x7ABE, 0x7AC0, 0x7AC1, - 0x7AC2, 0x7AC3, 0x7AC4, 0x7AC5, 0x7AC6, 0x7AC7, 0x7AC8, 0x7AC9, - 0x7ACA, 0x7ACC, 0x7ACD, 0x7ACE, 0x7ACF, 0x7AD0, 0x7AD1, 0x7AD2, - 0x7AD3, 0x7AD4, 0x7AD5, 0x7AD7, 0x7AD8, 0x7ADA, 0x7ADB, 0x7ADC, - 0x7ADD, 0x7AE1, 0x7AE2, 0x7AE4, 0x7AE7, 0x7AE8, 0x7AE9, 0x7AEA, - 0x7AEB, 0x7AEC, 0x7AEE, 0x7AF0, 0x7AF1, 0x7AF2, 0x7AF3, 0x003F, - 0x7AF4, 0x7AF5, 0x7AF6, 0x7AF7, 0x7AF8, 0x7AFB, 0x7AFC, 0x7AFE, - 0x7B00, 0x7B01, 0x7B02, 0x7B05, 0x7B07, 0x7B09, 0x7B0C, 0x7B0D, - 0x7B0E, 0x7B10, 0x7B12, 0x7B13, 0x7B16, 0x7B17, 0x7B18, 0x7B1A, - 0x7B1C, 0x7B1D, 0x7B1F, 0x7B21, 0x7B22, 0x7B23, 0x7B27, 0x7B29, - 0x7B2D, 0x6D6E, 0x6DAA, 0x798F, 0x88B1, 0x5F17, 0x752B, 0x629A, - 0x8F85, 0x4FEF, 0x91DC, 0x65A7, 0x812F, 0x8151, 0x5E9C, 0x8150, - 0x8D74, 0x526F, 0x8986, 0x8D4B, 0x590D, 0x5085, 0x4ED8, 0x961C, - 0x7236, 0x8179, 0x8D1F, 0x5BCC, 0x8BA3, 0x9644, 0x5987, 0x7F1A, - 0x5490, 0x5676, 0x560E, 0x8BE5, 0x6539, 0x6982, 0x9499, 0x76D6, - 0x6E89, 0x5E72, 0x7518, 0x6746, 0x67D1, 0x7AFF, 0x809D, 0x8D76, - 0x611F, 0x79C6, 0x6562, 0x8D63, 0x5188, 0x521A, 0x94A2, 0x7F38, - 0x809B, 0x7EB2, 0x5C97, 0x6E2F, 0x6760, 0x7BD9, 0x768B, 0x9AD8, - 0x818F, 0x7F94, 0x7CD5, 0x641E, 0x9550, 0x7A3F, 0x544A, 0x54E5, - 0x6B4C, 0x6401, 0x6208, 0x9E3D, 0x80F3, 0x7599, 0x5272, 0x9769, - 0x845B, 0x683C, 0x86E4, 0x9601, 0x9694, 0x94EC, 0x4E2A, 0x5404, - 0x7ED9, 0x6839, 0x8DDF, 0x8015, 0x66F4, 0x5E9A, 0x7FB9 - }, - { - 0x7B2F, 0x7B30, 0x7B32, 0x7B34, 0x7B35, 0x7B36, 0x7B37, 0x7B39, - 0x7B3B, 0x7B3D, 0x7B3F, 0x7B40, 0x7B41, 0x7B42, 0x7B43, 0x7B44, - 0x7B46, 0x7B48, 0x7B4A, 0x7B4D, 0x7B4E, 0x7B53, 0x7B55, 0x7B57, - 0x7B59, 0x7B5C, 0x7B5E, 0x7B5F, 0x7B61, 0x7B63, 0x7B64, 0x7B65, - 0x7B66, 0x7B67, 0x7B68, 0x7B69, 0x7B6A, 0x7B6B, 0x7B6C, 0x7B6D, - 0x7B6F, 0x7B70, 0x7B73, 0x7B74, 0x7B76, 0x7B78, 0x7B7A, 0x7B7C, - 0x7B7D, 0x7B7F, 0x7B81, 0x7B82, 0x7B83, 0x7B84, 0x7B86, 0x7B87, - 0x7B88, 0x7B89, 0x7B8A, 0x7B8B, 0x7B8C, 0x7B8E, 0x7B8F, 0x003F, - 0x7B91, 0x7B92, 0x7B93, 0x7B96, 0x7B98, 0x7B99, 0x7B9A, 0x7B9B, - 0x7B9E, 0x7B9F, 0x7BA0, 0x7BA3, 0x7BA4, 0x7BA5, 0x7BAE, 0x7BAF, - 0x7BB0, 0x7BB2, 0x7BB3, 0x7BB5, 0x7BB6, 0x7BB7, 0x7BB9, 0x7BBA, - 0x7BBB, 0x7BBC, 0x7BBD, 0x7BBE, 0x7BBF, 0x7BC0, 0x7BC2, 0x7BC3, - 0x7BC4, 0x57C2, 0x803F, 0x6897, 0x5DE5, 0x653B, 0x529F, 0x606D, - 0x9F9A, 0x4F9B, 0x8EAC, 0x516C, 0x5BAB, 0x5F13, 0x5DE9, 0x6C5E, - 0x62F1, 0x8D21, 0x5171, 0x94A9, 0x52FE, 0x6C9F, 0x82DF, 0x72D7, - 0x57A2, 0x6784, 0x8D2D, 0x591F, 0x8F9C, 0x83C7, 0x5495, 0x7B8D, - 0x4F30, 0x6CBD, 0x5B64, 0x59D1, 0x9F13, 0x53E4, 0x86CA, 0x9AA8, - 0x8C37, 0x80A1, 0x6545, 0x987E, 0x56FA, 0x96C7, 0x522E, 0x74DC, - 0x5250, 0x5BE1, 0x6302, 0x8902, 0x4E56, 0x62D0, 0x602A, 0x68FA, - 0x5173, 0x5B98, 0x51A0, 0x89C2, 0x7BA1, 0x9986, 0x7F50, 0x60EF, - 0x704C, 0x8D2F, 0x5149, 0x5E7F, 0x901B, 0x7470, 0x89C4, 0x572D, - 0x7845, 0x5F52, 0x9F9F, 0x95FA, 0x8F68, 0x9B3C, 0x8BE1, 0x7678, - 0x6842, 0x67DC, 0x8DEA, 0x8D35, 0x523D, 0x8F8A, 0x6EDA, 0x68CD, - 0x9505, 0x90ED, 0x56FD, 0x679C, 0x88F9, 0x8FC7, 0x54C8 - }, - { - 0x7BC5, 0x7BC8, 0x7BC9, 0x7BCA, 0x7BCB, 0x7BCD, 0x7BCE, 0x7BCF, - 0x7BD0, 0x7BD2, 0x7BD4, 0x7BD5, 0x7BD6, 0x7BD7, 0x7BD8, 0x7BDB, - 0x7BDC, 0x7BDE, 0x7BDF, 0x7BE0, 0x7BE2, 0x7BE3, 0x7BE4, 0x7BE7, - 0x7BE8, 0x7BE9, 0x7BEB, 0x7BEC, 0x7BED, 0x7BEF, 0x7BF0, 0x7BF2, - 0x7BF3, 0x7BF4, 0x7BF5, 0x7BF6, 0x7BF8, 0x7BF9, 0x7BFA, 0x7BFB, - 0x7BFD, 0x7BFF, 0x7C00, 0x7C01, 0x7C02, 0x7C03, 0x7C04, 0x7C05, - 0x7C06, 0x7C08, 0x7C09, 0x7C0A, 0x7C0D, 0x7C0E, 0x7C10, 0x7C11, - 0x7C12, 0x7C13, 0x7C14, 0x7C15, 0x7C17, 0x7C18, 0x7C19, 0x003F, - 0x7C1A, 0x7C1B, 0x7C1C, 0x7C1D, 0x7C1E, 0x7C20, 0x7C21, 0x7C22, - 0x7C23, 0x7C24, 0x7C25, 0x7C28, 0x7C29, 0x7C2B, 0x7C2C, 0x7C2D, - 0x7C2E, 0x7C2F, 0x7C30, 0x7C31, 0x7C32, 0x7C33, 0x7C34, 0x7C35, - 0x7C36, 0x7C37, 0x7C39, 0x7C3A, 0x7C3B, 0x7C3C, 0x7C3D, 0x7C3E, - 0x7C42, 0x9AB8, 0x5B69, 0x6D77, 0x6C26, 0x4EA5, 0x5BB3, 0x9A87, - 0x9163, 0x61A8, 0x90AF, 0x97E9, 0x542B, 0x6DB5, 0x5BD2, 0x51FD, - 0x558A, 0x7F55, 0x7FF0, 0x64BC, 0x634D, 0x65F1, 0x61BE, 0x608D, - 0x710A, 0x6C57, 0x6C49, 0x592F, 0x676D, 0x822A, 0x58D5, 0x568E, - 0x8C6A, 0x6BEB, 0x90DD, 0x597D, 0x8017, 0x53F7, 0x6D69, 0x5475, - 0x559D, 0x8377, 0x83CF, 0x6838, 0x79BE, 0x548C, 0x4F55, 0x5408, - 0x76D2, 0x8C89, 0x9602, 0x6CB3, 0x6DB8, 0x8D6B, 0x8910, 0x9E64, - 0x8D3A, 0x563F, 0x9ED1, 0x75D5, 0x5F88, 0x72E0, 0x6068, 0x54FC, - 0x4EA8, 0x6A2A, 0x8861, 0x6052, 0x8F70, 0x54C4, 0x70D8, 0x8679, - 0x9E3F, 0x6D2A, 0x5B8F, 0x5F18, 0x7EA2, 0x5589, 0x4FAF, 0x7334, - 0x543C, 0x539A, 0x5019, 0x540E, 0x547C, 0x4E4E, 0x5FFD, 0x745A, - 0x58F6, 0x846B, 0x80E1, 0x8774, 0x72D0, 0x7CCA, 0x6E56 - }, - { - 0x7C43, 0x7C44, 0x7C45, 0x7C46, 0x7C47, 0x7C48, 0x7C49, 0x7C4A, - 0x7C4B, 0x7C4C, 0x7C4E, 0x7C4F, 0x7C50, 0x7C51, 0x7C52, 0x7C53, - 0x7C54, 0x7C55, 0x7C56, 0x7C57, 0x7C58, 0x7C59, 0x7C5A, 0x7C5B, - 0x7C5C, 0x7C5D, 0x7C5E, 0x7C5F, 0x7C60, 0x7C61, 0x7C62, 0x7C63, - 0x7C64, 0x7C65, 0x7C66, 0x7C67, 0x7C68, 0x7C69, 0x7C6A, 0x7C6B, - 0x7C6C, 0x7C6D, 0x7C6E, 0x7C6F, 0x7C70, 0x7C71, 0x7C72, 0x7C75, - 0x7C76, 0x7C77, 0x7C78, 0x7C79, 0x7C7A, 0x7C7E, 0x7C7F, 0x7C80, - 0x7C81, 0x7C82, 0x7C83, 0x7C84, 0x7C85, 0x7C86, 0x7C87, 0x003F, - 0x7C88, 0x7C8A, 0x7C8B, 0x7C8C, 0x7C8D, 0x7C8E, 0x7C8F, 0x7C90, - 0x7C93, 0x7C94, 0x7C96, 0x7C99, 0x7C9A, 0x7C9B, 0x7CA0, 0x7CA1, - 0x7CA3, 0x7CA6, 0x7CA7, 0x7CA8, 0x7CA9, 0x7CAB, 0x7CAC, 0x7CAD, - 0x7CAF, 0x7CB0, 0x7CB4, 0x7CB5, 0x7CB6, 0x7CB7, 0x7CB8, 0x7CBA, - 0x7CBB, 0x5F27, 0x864E, 0x552C, 0x62A4, 0x4E92, 0x6CAA, 0x6237, - 0x82B1, 0x54D7, 0x534E, 0x733E, 0x6ED1, 0x753B, 0x5212, 0x5316, - 0x8BDD, 0x69D0, 0x5F8A, 0x6000, 0x6DEE, 0x574F, 0x6B22, 0x73AF, - 0x6853, 0x8FD8, 0x7F13, 0x6362, 0x60A3, 0x5524, 0x75EA, 0x8C62, - 0x7115, 0x6DA3, 0x5BA6, 0x5E7B, 0x8352, 0x614C, 0x9EC4, 0x78FA, - 0x8757, 0x7C27, 0x7687, 0x51F0, 0x60F6, 0x714C, 0x6643, 0x5E4C, - 0x604D, 0x8C0E, 0x7070, 0x6325, 0x8F89, 0x5FBD, 0x6062, 0x86D4, - 0x56DE, 0x6BC1, 0x6094, 0x6167, 0x5349, 0x60E0, 0x6666, 0x8D3F, - 0x79FD, 0x4F1A, 0x70E9, 0x6C47, 0x8BB3, 0x8BF2, 0x7ED8, 0x8364, - 0x660F, 0x5A5A, 0x9B42, 0x6D51, 0x6DF7, 0x8C41, 0x6D3B, 0x4F19, - 0x706B, 0x83B7, 0x6216, 0x60D1, 0x970D, 0x8D27, 0x7978, 0x51FB, - 0x573E, 0x57FA, 0x673A, 0x7578, 0x7A3D, 0x79EF, 0x7B95 - }, - { - 0x7CBF, 0x7CC0, 0x7CC2, 0x7CC3, 0x7CC4, 0x7CC6, 0x7CC9, 0x7CCB, - 0x7CCE, 0x7CCF, 0x7CD0, 0x7CD1, 0x7CD2, 0x7CD3, 0x7CD4, 0x7CD8, - 0x7CDA, 0x7CDB, 0x7CDD, 0x7CDE, 0x7CE1, 0x7CE2, 0x7CE3, 0x7CE4, - 0x7CE5, 0x7CE6, 0x7CE7, 0x7CE9, 0x7CEA, 0x7CEB, 0x7CEC, 0x7CED, - 0x7CEE, 0x7CF0, 0x7CF1, 0x7CF2, 0x7CF3, 0x7CF4, 0x7CF5, 0x7CF6, - 0x7CF7, 0x7CF9, 0x7CFA, 0x7CFC, 0x7CFD, 0x7CFE, 0x7CFF, 0x7D00, - 0x7D01, 0x7D02, 0x7D03, 0x7D04, 0x7D05, 0x7D06, 0x7D07, 0x7D08, - 0x7D09, 0x7D0B, 0x7D0C, 0x7D0D, 0x7D0E, 0x7D0F, 0x7D10, 0x003F, - 0x7D11, 0x7D12, 0x7D13, 0x7D14, 0x7D15, 0x7D16, 0x7D17, 0x7D18, - 0x7D19, 0x7D1A, 0x7D1B, 0x7D1C, 0x7D1D, 0x7D1E, 0x7D1F, 0x7D21, - 0x7D23, 0x7D24, 0x7D25, 0x7D26, 0x7D28, 0x7D29, 0x7D2A, 0x7D2C, - 0x7D2D, 0x7D2E, 0x7D30, 0x7D31, 0x7D32, 0x7D33, 0x7D34, 0x7D35, - 0x7D36, 0x808C, 0x9965, 0x8FF9, 0x6FC0, 0x8BA5, 0x9E21, 0x59EC, - 0x7EE9, 0x7F09, 0x5409, 0x6781, 0x68D8, 0x8F91, 0x7C4D, 0x96C6, - 0x53CA, 0x6025, 0x75BE, 0x6C72, 0x5373, 0x5AC9, 0x7EA7, 0x6324, - 0x51E0, 0x810A, 0x5DF1, 0x84DF, 0x6280, 0x5180, 0x5B63, 0x4F0E, - 0x796D, 0x5242, 0x60B8, 0x6D4E, 0x5BC4, 0x5BC2, 0x8BA1, 0x8BB0, - 0x65E2, 0x5FCC, 0x9645, 0x5993, 0x7EE7, 0x7EAA, 0x5609, 0x67B7, - 0x5939, 0x4F73, 0x5BB6, 0x52A0, 0x835A, 0x988A, 0x8D3E, 0x7532, - 0x94BE, 0x5047, 0x7A3C, 0x4EF7, 0x67B6, 0x9A7E, 0x5AC1, 0x6B7C, - 0x76D1, 0x575A, 0x5C16, 0x7B3A, 0x95F4, 0x714E, 0x517C, 0x80A9, - 0x8270, 0x5978, 0x7F04, 0x8327, 0x68C0, 0x67EC, 0x78B1, 0x7877, - 0x62E3, 0x6361, 0x7B80, 0x4FED, 0x526A, 0x51CF, 0x8350, 0x69DB, - 0x9274, 0x8DF5, 0x8D31, 0x89C1, 0x952E, 0x7BAD, 0x4EF6 - }, - { - 0x7D37, 0x7D38, 0x7D39, 0x7D3A, 0x7D3B, 0x7D3C, 0x7D3D, 0x7D3E, - 0x7D3F, 0x7D40, 0x7D41, 0x7D42, 0x7D43, 0x7D44, 0x7D45, 0x7D46, - 0x7D47, 0x7D48, 0x7D49, 0x7D4A, 0x7D4B, 0x7D4C, 0x7D4D, 0x7D4E, - 0x7D4F, 0x7D50, 0x7D51, 0x7D52, 0x7D53, 0x7D54, 0x7D55, 0x7D56, - 0x7D57, 0x7D58, 0x7D59, 0x7D5A, 0x7D5B, 0x7D5C, 0x7D5D, 0x7D5E, - 0x7D5F, 0x7D60, 0x7D61, 0x7D62, 0x7D63, 0x7D64, 0x7D65, 0x7D66, - 0x7D67, 0x7D68, 0x7D69, 0x7D6A, 0x7D6B, 0x7D6C, 0x7D6D, 0x7D6F, - 0x7D70, 0x7D71, 0x7D72, 0x7D73, 0x7D74, 0x7D75, 0x7D76, 0x003F, - 0x7D78, 0x7D79, 0x7D7A, 0x7D7B, 0x7D7C, 0x7D7D, 0x7D7E, 0x7D7F, - 0x7D80, 0x7D81, 0x7D82, 0x7D83, 0x7D84, 0x7D85, 0x7D86, 0x7D87, - 0x7D88, 0x7D89, 0x7D8A, 0x7D8B, 0x7D8C, 0x7D8D, 0x7D8E, 0x7D8F, - 0x7D90, 0x7D91, 0x7D92, 0x7D93, 0x7D94, 0x7D95, 0x7D96, 0x7D97, - 0x7D98, 0x5065, 0x8230, 0x5251, 0x996F, 0x6E10, 0x6E85, 0x6DA7, - 0x5EFA, 0x50F5, 0x59DC, 0x5C06, 0x6D46, 0x6C5F, 0x7586, 0x848B, - 0x6868, 0x5956, 0x8BB2, 0x5320, 0x9171, 0x964D, 0x8549, 0x6912, - 0x7901, 0x7126, 0x80F6, 0x4EA4, 0x90CA, 0x6D47, 0x9A84, 0x5A07, - 0x56BC, 0x6405, 0x94F0, 0x77EB, 0x4FA5, 0x811A, 0x72E1, 0x89D2, - 0x997A, 0x7F34, 0x7EDE, 0x527F, 0x6559, 0x9175, 0x8F7F, 0x8F83, - 0x53EB, 0x7A96, 0x63ED, 0x63A5, 0x7686, 0x79F8, 0x8857, 0x9636, - 0x622A, 0x52AB, 0x8282, 0x6854, 0x6770, 0x6377, 0x776B, 0x7AED, - 0x6D01, 0x7ED3, 0x89E3, 0x59D0, 0x6212, 0x85C9, 0x82A5, 0x754C, - 0x501F, 0x4ECB, 0x75A5, 0x8BEB, 0x5C4A, 0x5DFE, 0x7B4B, 0x65A4, - 0x91D1, 0x4ECA, 0x6D25, 0x895F, 0x7D27, 0x9526, 0x4EC5, 0x8C28, - 0x8FDB, 0x9773, 0x664B, 0x7981, 0x8FD1, 0x70EC, 0x6D78 - }, - { - 0x7D99, 0x7D9A, 0x7D9B, 0x7D9C, 0x7D9D, 0x7D9E, 0x7D9F, 0x7DA0, - 0x7DA1, 0x7DA2, 0x7DA3, 0x7DA4, 0x7DA5, 0x7DA7, 0x7DA8, 0x7DA9, - 0x7DAA, 0x7DAB, 0x7DAC, 0x7DAD, 0x7DAF, 0x7DB0, 0x7DB1, 0x7DB2, - 0x7DB3, 0x7DB4, 0x7DB5, 0x7DB6, 0x7DB7, 0x7DB8, 0x7DB9, 0x7DBA, - 0x7DBB, 0x7DBC, 0x7DBD, 0x7DBE, 0x7DBF, 0x7DC0, 0x7DC1, 0x7DC2, - 0x7DC3, 0x7DC4, 0x7DC5, 0x7DC6, 0x7DC7, 0x7DC8, 0x7DC9, 0x7DCA, - 0x7DCB, 0x7DCC, 0x7DCD, 0x7DCE, 0x7DCF, 0x7DD0, 0x7DD1, 0x7DD2, - 0x7DD3, 0x7DD4, 0x7DD5, 0x7DD6, 0x7DD7, 0x7DD8, 0x7DD9, 0x003F, - 0x7DDA, 0x7DDB, 0x7DDC, 0x7DDD, 0x7DDE, 0x7DDF, 0x7DE0, 0x7DE1, - 0x7DE2, 0x7DE3, 0x7DE4, 0x7DE5, 0x7DE6, 0x7DE7, 0x7DE8, 0x7DE9, - 0x7DEA, 0x7DEB, 0x7DEC, 0x7DED, 0x7DEE, 0x7DEF, 0x7DF0, 0x7DF1, - 0x7DF2, 0x7DF3, 0x7DF4, 0x7DF5, 0x7DF6, 0x7DF7, 0x7DF8, 0x7DF9, - 0x7DFA, 0x5C3D, 0x52B2, 0x8346, 0x5162, 0x830E, 0x775B, 0x6676, - 0x9CB8, 0x4EAC, 0x60CA, 0x7CBE, 0x7CB3, 0x7ECF, 0x4E95, 0x8B66, - 0x666F, 0x9888, 0x9759, 0x5883, 0x656C, 0x955C, 0x5F84, 0x75C9, - 0x9756, 0x7ADF, 0x7ADE, 0x51C0, 0x70AF, 0x7A98, 0x63EA, 0x7A76, - 0x7EA0, 0x7396, 0x97ED, 0x4E45, 0x7078, 0x4E5D, 0x9152, 0x53A9, - 0x6551, 0x65E7, 0x81FC, 0x8205, 0x548E, 0x5C31, 0x759A, 0x97A0, - 0x62D8, 0x72D9, 0x75BD, 0x5C45, 0x9A79, 0x83CA, 0x5C40, 0x5480, - 0x77E9, 0x4E3E, 0x6CAE, 0x805A, 0x62D2, 0x636E, 0x5DE8, 0x5177, - 0x8DDD, 0x8E1E, 0x952F, 0x4FF1, 0x53E5, 0x60E7, 0x70AC, 0x5267, - 0x6350, 0x9E43, 0x5A1F, 0x5026, 0x7737, 0x5377, 0x7EE2, 0x6485, - 0x652B, 0x6289, 0x6398, 0x5014, 0x7235, 0x89C9, 0x51B3, 0x8BC0, - 0x7EDD, 0x5747, 0x83CC, 0x94A7, 0x519B, 0x541B, 0x5CFB - }, - { - 0x7DFB, 0x7DFC, 0x7DFD, 0x7DFE, 0x7DFF, 0x7E00, 0x7E01, 0x7E02, - 0x7E03, 0x7E04, 0x7E05, 0x7E06, 0x7E07, 0x7E08, 0x7E09, 0x7E0A, - 0x7E0B, 0x7E0C, 0x7E0D, 0x7E0E, 0x7E0F, 0x7E10, 0x7E11, 0x7E12, - 0x7E13, 0x7E14, 0x7E15, 0x7E16, 0x7E17, 0x7E18, 0x7E19, 0x7E1A, - 0x7E1B, 0x7E1C, 0x7E1D, 0x7E1E, 0x7E1F, 0x7E20, 0x7E21, 0x7E22, - 0x7E23, 0x7E24, 0x7E25, 0x7E26, 0x7E27, 0x7E28, 0x7E29, 0x7E2A, - 0x7E2B, 0x7E2C, 0x7E2D, 0x7E2E, 0x7E2F, 0x7E30, 0x7E31, 0x7E32, - 0x7E33, 0x7E34, 0x7E35, 0x7E36, 0x7E37, 0x7E38, 0x7E39, 0x003F, - 0x7E3A, 0x7E3C, 0x7E3D, 0x7E3E, 0x7E3F, 0x7E40, 0x7E42, 0x7E43, - 0x7E44, 0x7E45, 0x7E46, 0x7E48, 0x7E49, 0x7E4A, 0x7E4B, 0x7E4C, - 0x7E4D, 0x7E4E, 0x7E4F, 0x7E50, 0x7E51, 0x7E52, 0x7E53, 0x7E54, - 0x7E55, 0x7E56, 0x7E57, 0x7E58, 0x7E59, 0x7E5A, 0x7E5B, 0x7E5C, - 0x7E5D, 0x4FCA, 0x7AE3, 0x6D5A, 0x90E1, 0x9A8F, 0x5580, 0x5496, - 0x5361, 0x54AF, 0x5F00, 0x63E9, 0x6977, 0x51EF, 0x6168, 0x520A, - 0x582A, 0x52D8, 0x574E, 0x780D, 0x770B, 0x5EB7, 0x6177, 0x7CE0, - 0x625B, 0x6297, 0x4EA2, 0x7095, 0x8003, 0x62F7, 0x70E4, 0x9760, - 0x5777, 0x82DB, 0x67EF, 0x68F5, 0x78D5, 0x9897, 0x79D1, 0x58F3, - 0x54B3, 0x53EF, 0x6E34, 0x514B, 0x523B, 0x5BA2, 0x8BFE, 0x80AF, - 0x5543, 0x57A6, 0x6073, 0x5751, 0x542D, 0x7A7A, 0x6050, 0x5B54, - 0x63A7, 0x62A0, 0x53E3, 0x6263, 0x5BC7, 0x67AF, 0x54ED, 0x7A9F, - 0x82E6, 0x9177, 0x5E93, 0x88E4, 0x5938, 0x57AE, 0x630E, 0x8DE8, - 0x80EF, 0x5757, 0x7B77, 0x4FA9, 0x5FEB, 0x5BBD, 0x6B3E, 0x5321, - 0x7B50, 0x72C2, 0x6846, 0x77FF, 0x7736, 0x65F7, 0x51B5, 0x4E8F, - 0x76D4, 0x5CBF, 0x7AA5, 0x8475, 0x594E, 0x9B41, 0x5080 - }, - { - 0x7E5E, 0x7E5F, 0x7E60, 0x7E61, 0x7E62, 0x7E63, 0x7E64, 0x7E65, - 0x7E66, 0x7E67, 0x7E68, 0x7E69, 0x7E6A, 0x7E6B, 0x7E6C, 0x7E6D, - 0x7E6E, 0x7E6F, 0x7E70, 0x7E71, 0x7E72, 0x7E73, 0x7E74, 0x7E75, - 0x7E76, 0x7E77, 0x7E78, 0x7E79, 0x7E7A, 0x7E7B, 0x7E7C, 0x7E7D, - 0x7E7E, 0x7E7F, 0x7E80, 0x7E81, 0x7E83, 0x7E84, 0x7E85, 0x7E86, - 0x7E87, 0x7E88, 0x7E89, 0x7E8A, 0x7E8B, 0x7E8C, 0x7E8D, 0x7E8E, - 0x7E8F, 0x7E90, 0x7E91, 0x7E92, 0x7E93, 0x7E94, 0x7E95, 0x7E96, - 0x7E97, 0x7E98, 0x7E99, 0x7E9A, 0x7E9C, 0x7E9D, 0x7E9E, 0x003F, - 0x7EAE, 0x7EB4, 0x7EBB, 0x7EBC, 0x7ED6, 0x7EE4, 0x7EEC, 0x7EF9, - 0x7F0A, 0x7F10, 0x7F1E, 0x7F37, 0x7F39, 0x7F3B, 0x7F3C, 0x7F3D, - 0x7F3E, 0x7F3F, 0x7F40, 0x7F41, 0x7F43, 0x7F46, 0x7F47, 0x7F48, - 0x7F49, 0x7F4A, 0x7F4B, 0x7F4C, 0x7F4D, 0x7F4E, 0x7F4F, 0x7F52, - 0x7F53, 0x9988, 0x6127, 0x6E83, 0x5764, 0x6606, 0x6346, 0x56F0, - 0x62EC, 0x6269, 0x5ED3, 0x9614, 0x5783, 0x62C9, 0x5587, 0x8721, - 0x814A, 0x8FA3, 0x5566, 0x83B1, 0x6765, 0x8D56, 0x84DD, 0x5A6A, - 0x680F, 0x62E6, 0x7BEE, 0x9611, 0x5170, 0x6F9C, 0x8C30, 0x63FD, - 0x89C8, 0x61D2, 0x7F06, 0x70C2, 0x6EE5, 0x7405, 0x6994, 0x72FC, - 0x5ECA, 0x90CE, 0x6717, 0x6D6A, 0x635E, 0x52B3, 0x7262, 0x8001, - 0x4F6C, 0x59E5, 0x916A, 0x70D9, 0x6D9D, 0x52D2, 0x4E50, 0x96F7, - 0x956D, 0x857E, 0x78CA, 0x7D2F, 0x5121, 0x5792, 0x64C2, 0x808B, - 0x7C7B, 0x6CEA, 0x68F1, 0x695E, 0x51B7, 0x5398, 0x68A8, 0x7281, - 0x9ECE, 0x7BF1, 0x72F8, 0x79BB, 0x6F13, 0x7406, 0x674E, 0x91CC, - 0x9CA4, 0x793C, 0x8389, 0x8354, 0x540F, 0x6817, 0x4E3D, 0x5389, - 0x52B1, 0x783E, 0x5386, 0x5229, 0x5088, 0x4F8B, 0x4FD0 - }, - { - 0x7F56, 0x7F59, 0x7F5B, 0x7F5C, 0x7F5D, 0x7F5E, 0x7F60, 0x7F63, - 0x7F64, 0x7F65, 0x7F66, 0x7F67, 0x7F6B, 0x7F6C, 0x7F6D, 0x7F6F, - 0x7F70, 0x7F73, 0x7F75, 0x7F76, 0x7F77, 0x7F78, 0x7F7A, 0x7F7B, - 0x7F7C, 0x7F7D, 0x7F7F, 0x7F80, 0x7F82, 0x7F83, 0x7F84, 0x7F85, - 0x7F86, 0x7F87, 0x7F88, 0x7F89, 0x7F8B, 0x7F8D, 0x7F8F, 0x7F90, - 0x7F91, 0x7F92, 0x7F93, 0x7F95, 0x7F96, 0x7F97, 0x7F98, 0x7F99, - 0x7F9B, 0x7F9C, 0x7FA0, 0x7FA2, 0x7FA3, 0x7FA5, 0x7FA6, 0x7FA8, - 0x7FA9, 0x7FAA, 0x7FAB, 0x7FAC, 0x7FAD, 0x7FAE, 0x7FB1, 0x003F, - 0x7FB3, 0x7FB4, 0x7FB5, 0x7FB6, 0x7FB7, 0x7FBA, 0x7FBB, 0x7FBE, - 0x7FC0, 0x7FC2, 0x7FC3, 0x7FC4, 0x7FC6, 0x7FC7, 0x7FC8, 0x7FC9, - 0x7FCB, 0x7FCD, 0x7FCF, 0x7FD0, 0x7FD1, 0x7FD2, 0x7FD3, 0x7FD6, - 0x7FD7, 0x7FD9, 0x7FDA, 0x7FDB, 0x7FDC, 0x7FDD, 0x7FDE, 0x7FE2, - 0x7FE3, 0x75E2, 0x7ACB, 0x7C92, 0x6CA5, 0x96B6, 0x529B, 0x7483, - 0x54E9, 0x4FE9, 0x8054, 0x83B2, 0x8FDE, 0x9570, 0x5EC9, 0x601C, - 0x6D9F, 0x5E18, 0x655B, 0x8138, 0x94FE, 0x604B, 0x70BC, 0x7EC3, - 0x7CAE, 0x51C9, 0x6881, 0x7CB1, 0x826F, 0x4E24, 0x8F86, 0x91CF, - 0x667E, 0x4EAE, 0x8C05, 0x64A9, 0x804A, 0x50DA, 0x7597, 0x71CE, - 0x5BE5, 0x8FBD, 0x6F66, 0x4E86, 0x6482, 0x9563, 0x5ED6, 0x6599, - 0x5217, 0x88C2, 0x70C8, 0x52A3, 0x730E, 0x7433, 0x6797, 0x78F7, - 0x9716, 0x4E34, 0x90BB, 0x9CDE, 0x6DCB, 0x51DB, 0x8D41, 0x541D, - 0x62CE, 0x73B2, 0x83F1, 0x96F6, 0x9F84, 0x94C3, 0x4F36, 0x7F9A, - 0x51CC, 0x7075, 0x9675, 0x5CAD, 0x9886, 0x53E6, 0x4EE4, 0x6E9C, - 0x7409, 0x69B4, 0x786B, 0x998F, 0x7559, 0x5218, 0x7624, 0x6D41, - 0x67F3, 0x516D, 0x9F99, 0x804B, 0x5499, 0x7B3C, 0x7ABF - }, - { - 0x7FE4, 0x7FE7, 0x7FE8, 0x7FEA, 0x7FEB, 0x7FEC, 0x7FED, 0x7FEF, - 0x7FF2, 0x7FF4, 0x7FF5, 0x7FF6, 0x7FF7, 0x7FF8, 0x7FF9, 0x7FFA, - 0x7FFD, 0x7FFE, 0x7FFF, 0x8002, 0x8007, 0x8008, 0x8009, 0x800A, - 0x800E, 0x800F, 0x8011, 0x8013, 0x801A, 0x801B, 0x801D, 0x801E, - 0x801F, 0x8021, 0x8023, 0x8024, 0x802B, 0x802C, 0x802D, 0x802E, - 0x802F, 0x8030, 0x8032, 0x8034, 0x8039, 0x803A, 0x803C, 0x803E, - 0x8040, 0x8041, 0x8044, 0x8045, 0x8047, 0x8048, 0x8049, 0x804E, - 0x804F, 0x8050, 0x8051, 0x8053, 0x8055, 0x8056, 0x8057, 0x003F, - 0x8059, 0x805B, 0x805C, 0x805D, 0x805E, 0x805F, 0x8060, 0x8061, - 0x8062, 0x8063, 0x8064, 0x8065, 0x8066, 0x8067, 0x8068, 0x806B, - 0x806C, 0x806D, 0x806E, 0x806F, 0x8070, 0x8072, 0x8073, 0x8074, - 0x8075, 0x8076, 0x8077, 0x8078, 0x8079, 0x807A, 0x807B, 0x807C, - 0x807D, 0x9686, 0x5784, 0x62E2, 0x9647, 0x697C, 0x5A04, 0x6402, - 0x7BD3, 0x6F0F, 0x964B, 0x82A6, 0x5362, 0x9885, 0x5E90, 0x7089, - 0x63B3, 0x5364, 0x864F, 0x9C81, 0x9E93, 0x788C, 0x9732, 0x8DEF, - 0x8D42, 0x9E7F, 0x6F5E, 0x7984, 0x5F55, 0x9646, 0x622E, 0x9A74, - 0x5415, 0x94DD, 0x4FA3, 0x65C5, 0x5C65, 0x5C61, 0x7F15, 0x8651, - 0x6C2F, 0x5F8B, 0x7387, 0x6EE4, 0x7EFF, 0x5CE6, 0x631B, 0x5B6A, - 0x6EE6, 0x5375, 0x4E71, 0x63A0, 0x7565, 0x62A1, 0x8F6E, 0x4F26, - 0x4ED1, 0x6CA6, 0x7EB6, 0x8BBA, 0x841D, 0x87BA, 0x7F57, 0x903B, - 0x9523, 0x7BA9, 0x9AA1, 0x88F8, 0x843D, 0x6D1B, 0x9A86, 0x7EDC, - 0x5988, 0x9EBB, 0x739B, 0x7801, 0x8682, 0x9A6C, 0x9A82, 0x561B, - 0x5417, 0x57CB, 0x4E70, 0x9EA6, 0x5356, 0x8FC8, 0x8109, 0x7792, - 0x9992, 0x86EE, 0x6EE1, 0x8513, 0x66FC, 0x6162, 0x6F2B - }, - { - 0x807E, 0x8081, 0x8082, 0x8085, 0x8088, 0x808A, 0x808D, 0x808E, - 0x808F, 0x8090, 0x8091, 0x8092, 0x8094, 0x8095, 0x8097, 0x8099, - 0x809E, 0x80A3, 0x80A6, 0x80A7, 0x80A8, 0x80AC, 0x80B0, 0x80B3, - 0x80B5, 0x80B6, 0x80B8, 0x80B9, 0x80BB, 0x80C5, 0x80C7, 0x80C8, - 0x80C9, 0x80CA, 0x80CB, 0x80CF, 0x80D0, 0x80D1, 0x80D2, 0x80D3, - 0x80D4, 0x80D5, 0x80D8, 0x80DF, 0x80E0, 0x80E2, 0x80E3, 0x80E6, - 0x80EE, 0x80F5, 0x80F7, 0x80F9, 0x80FB, 0x80FE, 0x80FF, 0x8100, - 0x8101, 0x8103, 0x8104, 0x8105, 0x8107, 0x8108, 0x810B, 0x003F, - 0x810C, 0x8115, 0x8117, 0x8119, 0x811B, 0x811C, 0x811D, 0x811F, - 0x8120, 0x8121, 0x8122, 0x8123, 0x8124, 0x8125, 0x8126, 0x8127, - 0x8128, 0x8129, 0x812A, 0x812B, 0x812D, 0x812E, 0x8130, 0x8133, - 0x8134, 0x8135, 0x8137, 0x8139, 0x813A, 0x813B, 0x813C, 0x813D, - 0x813F, 0x8C29, 0x8292, 0x832B, 0x76F2, 0x6C13, 0x5FD9, 0x83BD, - 0x732B, 0x8305, 0x951A, 0x6BDB, 0x77DB, 0x94C6, 0x536F, 0x8302, - 0x5192, 0x5E3D, 0x8C8C, 0x8D38, 0x4E48, 0x73AB, 0x679A, 0x6885, - 0x9176, 0x9709, 0x7164, 0x6CA1, 0x7709, 0x5A92, 0x9541, 0x6BCF, - 0x7F8E, 0x6627, 0x5BD0, 0x59B9, 0x5A9A, 0x95E8, 0x95F7, 0x4EEC, - 0x840C, 0x8499, 0x6AAC, 0x76DF, 0x9530, 0x731B, 0x68A6, 0x5B5F, - 0x772F, 0x919A, 0x9761, 0x7CDC, 0x8FF7, 0x8C1C, 0x5F25, 0x7C73, - 0x79D8, 0x89C5, 0x6CCC, 0x871C, 0x5BC6, 0x5E42, 0x68C9, 0x7720, - 0x7EF5, 0x5195, 0x514D, 0x52C9, 0x5A29, 0x7F05, 0x9762, 0x82D7, - 0x63CF, 0x7784, 0x85D0, 0x79D2, 0x6E3A, 0x5E99, 0x5999, 0x8511, - 0x706D, 0x6C11, 0x62BF, 0x76BF, 0x654F, 0x60AF, 0x95FD, 0x660E, - 0x879F, 0x9E23, 0x94ED, 0x540D, 0x547D, 0x8C2C, 0x6478 - }, - { - 0x8140, 0x8141, 0x8142, 0x8143, 0x8144, 0x8145, 0x8147, 0x8149, - 0x814D, 0x814E, 0x814F, 0x8152, 0x8156, 0x8157, 0x8158, 0x815B, - 0x815C, 0x815D, 0x815E, 0x815F, 0x8161, 0x8162, 0x8163, 0x8164, - 0x8166, 0x8168, 0x816A, 0x816B, 0x816C, 0x816F, 0x8172, 0x8173, - 0x8175, 0x8176, 0x8177, 0x8178, 0x8181, 0x8183, 0x8184, 0x8185, - 0x8186, 0x8187, 0x8189, 0x818B, 0x818C, 0x818D, 0x818E, 0x8190, - 0x8192, 0x8193, 0x8194, 0x8195, 0x8196, 0x8197, 0x8199, 0x819A, - 0x819E, 0x819F, 0x81A0, 0x81A1, 0x81A2, 0x81A4, 0x81A5, 0x003F, - 0x81A7, 0x81A9, 0x81AB, 0x81AC, 0x81AD, 0x81AE, 0x81AF, 0x81B0, - 0x81B1, 0x81B2, 0x81B4, 0x81B5, 0x81B6, 0x81B7, 0x81B8, 0x81B9, - 0x81BC, 0x81BD, 0x81BE, 0x81BF, 0x81C4, 0x81C5, 0x81C7, 0x81C8, - 0x81C9, 0x81CB, 0x81CD, 0x81CE, 0x81CF, 0x81D0, 0x81D1, 0x81D2, - 0x81D3, 0x6479, 0x8611, 0x6A21, 0x819C, 0x78E8, 0x6469, 0x9B54, - 0x62B9, 0x672B, 0x83AB, 0x58A8, 0x9ED8, 0x6CAB, 0x6F20, 0x5BDE, - 0x964C, 0x8C0B, 0x725F, 0x67D0, 0x62C7, 0x7261, 0x4EA9, 0x59C6, - 0x6BCD, 0x5893, 0x66AE, 0x5E55, 0x52DF, 0x6155, 0x6728, 0x76EE, - 0x7766, 0x7267, 0x7A46, 0x62FF, 0x54EA, 0x5450, 0x94A0, 0x90A3, - 0x5A1C, 0x7EB3, 0x6C16, 0x4E43, 0x5976, 0x8010, 0x5948, 0x5357, - 0x7537, 0x96BE, 0x56CA, 0x6320, 0x8111, 0x607C, 0x95F9, 0x6DD6, - 0x5462, 0x9981, 0x5185, 0x5AE9, 0x80FD, 0x59AE, 0x9713, 0x502A, - 0x6CE5, 0x5C3C, 0x62DF, 0x4F60, 0x533F, 0x817B, 0x9006, 0x6EBA, - 0x852B, 0x62C8, 0x5E74, 0x78BE, 0x64B5, 0x637B, 0x5FF5, 0x5A18, - 0x917F, 0x9E1F, 0x5C3F, 0x634F, 0x8042, 0x5B7D, 0x556E, 0x954A, - 0x954D, 0x6D85, 0x60A8, 0x67E0, 0x72DE, 0x51DD, 0x5B81 - }, - { - 0x81D4, 0x81D5, 0x81D6, 0x81D7, 0x81D8, 0x81D9, 0x81DA, 0x81DB, - 0x81DC, 0x81DD, 0x81DE, 0x81DF, 0x81E0, 0x81E1, 0x81E2, 0x81E4, - 0x81E5, 0x81E6, 0x81E8, 0x81E9, 0x81EB, 0x81EE, 0x81EF, 0x81F0, - 0x81F1, 0x81F2, 0x81F5, 0x81F6, 0x81F7, 0x81F8, 0x81F9, 0x81FA, - 0x81FD, 0x81FF, 0x8203, 0x8207, 0x8208, 0x8209, 0x820A, 0x820B, - 0x820E, 0x820F, 0x8211, 0x8213, 0x8215, 0x8216, 0x8217, 0x8218, - 0x8219, 0x821A, 0x821D, 0x8220, 0x8224, 0x8225, 0x8226, 0x8227, - 0x8229, 0x822E, 0x8232, 0x823A, 0x823C, 0x823D, 0x823F, 0x003F, - 0x8240, 0x8241, 0x8242, 0x8243, 0x8245, 0x8246, 0x8248, 0x824A, - 0x824C, 0x824D, 0x824E, 0x8250, 0x8251, 0x8252, 0x8253, 0x8254, - 0x8255, 0x8256, 0x8257, 0x8259, 0x825B, 0x825C, 0x825D, 0x825E, - 0x8260, 0x8261, 0x8262, 0x8263, 0x8264, 0x8265, 0x8266, 0x8267, - 0x8269, 0x62E7, 0x6CDE, 0x725B, 0x626D, 0x94AE, 0x7EBD, 0x8113, - 0x6D53, 0x519C, 0x5F04, 0x5974, 0x52AA, 0x6012, 0x5973, 0x6696, - 0x8650, 0x759F, 0x632A, 0x61E6, 0x7CEF, 0x8BFA, 0x54E6, 0x6B27, - 0x9E25, 0x6BB4, 0x85D5, 0x5455, 0x5076, 0x6CA4, 0x556A, 0x8DB4, - 0x722C, 0x5E15, 0x6015, 0x7436, 0x62CD, 0x6392, 0x724C, 0x5F98, - 0x6E43, 0x6D3E, 0x6500, 0x6F58, 0x76D8, 0x78D0, 0x76FC, 0x7554, - 0x5224, 0x53DB, 0x4E53, 0x5E9E, 0x65C1, 0x802A, 0x80D6, 0x629B, - 0x5486, 0x5228, 0x70AE, 0x888D, 0x8DD1, 0x6CE1, 0x5478, 0x80DA, - 0x57F9, 0x88F4, 0x8D54, 0x966A, 0x914D, 0x4F69, 0x6C9B, 0x55B7, - 0x76C6, 0x7830, 0x62A8, 0x70F9, 0x6F8E, 0x5F6D, 0x84EC, 0x68DA, - 0x787C, 0x7BF7, 0x81A8, 0x670B, 0x9E4F, 0x6367, 0x78B0, 0x576F, - 0x7812, 0x9739, 0x6279, 0x62AB, 0x5288, 0x7435, 0x6BD7 - }, - { - 0x826A, 0x826B, 0x826C, 0x826D, 0x8271, 0x8275, 0x8276, 0x8277, - 0x8278, 0x827B, 0x827C, 0x8280, 0x8281, 0x8283, 0x8285, 0x8286, - 0x8287, 0x8289, 0x828C, 0x8290, 0x8293, 0x8294, 0x8295, 0x8296, - 0x829A, 0x829B, 0x829E, 0x82A0, 0x82A2, 0x82A3, 0x82A7, 0x82B2, - 0x82B5, 0x82B6, 0x82BA, 0x82BB, 0x82BC, 0x82BF, 0x82C0, 0x82C2, - 0x82C3, 0x82C5, 0x82C6, 0x82C9, 0x82D0, 0x82D6, 0x82D9, 0x82DA, - 0x82DD, 0x82E2, 0x82E7, 0x82E8, 0x82E9, 0x82EA, 0x82EC, 0x82ED, - 0x82EE, 0x82F0, 0x82F2, 0x82F3, 0x82F5, 0x82F6, 0x82F8, 0x003F, - 0x82FA, 0x82FC, 0x82FD, 0x82FE, 0x82FF, 0x8300, 0x830A, 0x830B, - 0x830D, 0x8310, 0x8312, 0x8313, 0x8316, 0x8318, 0x8319, 0x831D, - 0x831E, 0x831F, 0x8320, 0x8321, 0x8322, 0x8323, 0x8324, 0x8325, - 0x8326, 0x8329, 0x832A, 0x832E, 0x8330, 0x8332, 0x8337, 0x833B, - 0x833D, 0x5564, 0x813E, 0x75B2, 0x76AE, 0x5339, 0x75DE, 0x50FB, - 0x5C41, 0x8B6C, 0x7BC7, 0x504F, 0x7247, 0x9A97, 0x98D8, 0x6F02, - 0x74E2, 0x7968, 0x6487, 0x77A5, 0x62FC, 0x9891, 0x8D2B, 0x54C1, - 0x8058, 0x4E52, 0x576A, 0x82F9, 0x840D, 0x5E73, 0x51ED, 0x74F6, - 0x8BC4, 0x5C4F, 0x5761, 0x6CFC, 0x9887, 0x5A46, 0x7834, 0x9B44, - 0x8FEB, 0x7C95, 0x5256, 0x6251, 0x94FA, 0x4EC6, 0x8386, 0x8461, - 0x83E9, 0x84B2, 0x57D4, 0x6734, 0x5703, 0x666E, 0x6D66, 0x8C31, - 0x66DD, 0x7011, 0x671F, 0x6B3A, 0x6816, 0x621A, 0x59BB, 0x4E03, - 0x51C4, 0x6F06, 0x67D2, 0x6C8F, 0x5176, 0x68CB, 0x5947, 0x6B67, - 0x7566, 0x5D0E, 0x8110, 0x9F50, 0x65D7, 0x7948, 0x7941, 0x9A91, - 0x8D77, 0x5C82, 0x4E5E, 0x4F01, 0x542F, 0x5951, 0x780C, 0x5668, - 0x6C14, 0x8FC4, 0x5F03, 0x6C7D, 0x6CE3, 0x8BAB, 0x6390 - }, - { - 0x833E, 0x833F, 0x8341, 0x8342, 0x8344, 0x8345, 0x8348, 0x834A, - 0x834B, 0x834C, 0x834D, 0x834E, 0x8353, 0x8355, 0x8356, 0x8357, - 0x8358, 0x8359, 0x835D, 0x8362, 0x8370, 0x8371, 0x8372, 0x8373, - 0x8374, 0x8375, 0x8376, 0x8379, 0x837A, 0x837E, 0x837F, 0x8380, - 0x8381, 0x8382, 0x8383, 0x8384, 0x8387, 0x8388, 0x838A, 0x838B, - 0x838C, 0x838D, 0x838F, 0x8390, 0x8391, 0x8394, 0x8395, 0x8396, - 0x8397, 0x8399, 0x839A, 0x839D, 0x839F, 0x83A1, 0x83A2, 0x83A3, - 0x83A4, 0x83A5, 0x83A6, 0x83A7, 0x83AC, 0x83AD, 0x83AE, 0x003F, - 0x83AF, 0x83B5, 0x83BB, 0x83BE, 0x83BF, 0x83C2, 0x83C3, 0x83C4, - 0x83C6, 0x83C8, 0x83C9, 0x83CB, 0x83CD, 0x83CE, 0x83D0, 0x83D1, - 0x83D2, 0x83D3, 0x83D5, 0x83D7, 0x83D9, 0x83DA, 0x83DB, 0x83DE, - 0x83E2, 0x83E3, 0x83E4, 0x83E6, 0x83E7, 0x83E8, 0x83EB, 0x83EC, - 0x83ED, 0x6070, 0x6D3D, 0x7275, 0x6266, 0x948E, 0x94C5, 0x5343, - 0x8FC1, 0x7B7E, 0x4EDF, 0x8C26, 0x4E7E, 0x9ED4, 0x94B1, 0x94B3, - 0x524D, 0x6F5C, 0x9063, 0x6D45, 0x8C34, 0x5811, 0x5D4C, 0x6B20, - 0x6B49, 0x67AA, 0x545B, 0x8154, 0x7F8C, 0x5899, 0x8537, 0x5F3A, - 0x62A2, 0x6A47, 0x9539, 0x6572, 0x6084, 0x6865, 0x77A7, 0x4E54, - 0x4FA8, 0x5DE7, 0x9798, 0x64AC, 0x7FD8, 0x5CED, 0x4FCF, 0x7A8D, - 0x5207, 0x8304, 0x4E14, 0x602F, 0x7A83, 0x94A6, 0x4FB5, 0x4EB2, - 0x79E6, 0x7434, 0x52E4, 0x82B9, 0x64D2, 0x79BD, 0x5BDD, 0x6C81, - 0x9752, 0x8F7B, 0x6C22, 0x503E, 0x537F, 0x6E05, 0x64CE, 0x6674, - 0x6C30, 0x60C5, 0x9877, 0x8BF7, 0x5E86, 0x743C, 0x7A77, 0x79CB, - 0x4E18, 0x90B1, 0x7403, 0x6C42, 0x56DA, 0x914B, 0x6CC5, 0x8D8B, - 0x533A, 0x86C6, 0x66F2, 0x8EAF, 0x5C48, 0x9A71, 0x6E20 - }, - { - 0x83EE, 0x83EF, 0x83F3, 0x83F4, 0x83F5, 0x83F6, 0x83F7, 0x83FA, - 0x83FB, 0x83FC, 0x83FE, 0x83FF, 0x8400, 0x8402, 0x8405, 0x8407, - 0x8408, 0x8409, 0x840A, 0x8410, 0x8412, 0x8413, 0x8414, 0x8415, - 0x8416, 0x8417, 0x8419, 0x841A, 0x841B, 0x841E, 0x841F, 0x8420, - 0x8421, 0x8422, 0x8423, 0x8429, 0x842A, 0x842B, 0x842C, 0x842D, - 0x842E, 0x842F, 0x8430, 0x8432, 0x8433, 0x8434, 0x8435, 0x8436, - 0x8437, 0x8439, 0x843A, 0x843B, 0x843E, 0x843F, 0x8440, 0x8441, - 0x8442, 0x8443, 0x8444, 0x8445, 0x8447, 0x8448, 0x8449, 0x003F, - 0x844A, 0x844B, 0x844C, 0x844D, 0x844E, 0x844F, 0x8450, 0x8452, - 0x8453, 0x8454, 0x8455, 0x8456, 0x8458, 0x845D, 0x845E, 0x845F, - 0x8460, 0x8462, 0x8464, 0x8465, 0x8466, 0x8467, 0x8468, 0x846A, - 0x846E, 0x846F, 0x8470, 0x8472, 0x8474, 0x8477, 0x8479, 0x847B, - 0x847C, 0x53D6, 0x5A36, 0x9F8B, 0x8DA3, 0x53BB, 0x5708, 0x98A7, - 0x6743, 0x919B, 0x6CC9, 0x5168, 0x75CA, 0x62F3, 0x72AC, 0x5238, - 0x529D, 0x7F3A, 0x7094, 0x7638, 0x5374, 0x9E4A, 0x69B7, 0x786E, - 0x96C0, 0x88D9, 0x7FA4, 0x7136, 0x71C3, 0x5189, 0x67D3, 0x74E4, - 0x58E4, 0x6518, 0x56B7, 0x8BA9, 0x9976, 0x6270, 0x7ED5, 0x60F9, - 0x70ED, 0x58EC, 0x4EC1, 0x4EBA, 0x5FCD, 0x97E7, 0x4EFB, 0x8BA4, - 0x5203, 0x598A, 0x7EAB, 0x6254, 0x4ECD, 0x65E5, 0x620E, 0x8338, - 0x84C9, 0x8363, 0x878D, 0x7194, 0x6EB6, 0x5BB9, 0x7ED2, 0x5197, - 0x63C9, 0x67D4, 0x8089, 0x8339, 0x8815, 0x5112, 0x5B7A, 0x5982, - 0x8FB1, 0x4E73, 0x6C5D, 0x5165, 0x8925, 0x8F6F, 0x962E, 0x854A, - 0x745E, 0x9510, 0x95F0, 0x6DA6, 0x82E5, 0x5F31, 0x6492, 0x6D12, - 0x8428, 0x816E, 0x9CC3, 0x585E, 0x8D5B, 0x4E09, 0x53C1 - }, - { - 0x847D, 0x847E, 0x847F, 0x8480, 0x8481, 0x8483, 0x8484, 0x8485, - 0x8486, 0x848A, 0x848D, 0x848F, 0x8490, 0x8491, 0x8492, 0x8493, - 0x8494, 0x8495, 0x8496, 0x8498, 0x849A, 0x849B, 0x849D, 0x849E, - 0x849F, 0x84A0, 0x84A2, 0x84A3, 0x84A4, 0x84A5, 0x84A6, 0x84A7, - 0x84A8, 0x84A9, 0x84AA, 0x84AB, 0x84AC, 0x84AD, 0x84AE, 0x84B0, - 0x84B1, 0x84B3, 0x84B5, 0x84B6, 0x84B7, 0x84BB, 0x84BC, 0x84BE, - 0x84C0, 0x84C2, 0x84C3, 0x84C5, 0x84C6, 0x84C7, 0x84C8, 0x84CB, - 0x84CC, 0x84CE, 0x84CF, 0x84D2, 0x84D4, 0x84D5, 0x84D7, 0x003F, - 0x84D8, 0x84D9, 0x84DA, 0x84DB, 0x84DC, 0x84DE, 0x84E1, 0x84E2, - 0x84E4, 0x84E7, 0x84E8, 0x84E9, 0x84EA, 0x84EB, 0x84ED, 0x84EE, - 0x84EF, 0x84F1, 0x84F2, 0x84F3, 0x84F4, 0x84F5, 0x84F6, 0x84F7, - 0x84F8, 0x84F9, 0x84FA, 0x84FB, 0x84FD, 0x84FE, 0x8500, 0x8501, - 0x8502, 0x4F1E, 0x6563, 0x6851, 0x55D3, 0x4E27, 0x6414, 0x9A9A, - 0x626B, 0x5AC2, 0x745F, 0x8272, 0x6DA9, 0x68EE, 0x50E7, 0x838E, - 0x7802, 0x6740, 0x5239, 0x6C99, 0x7EB1, 0x50BB, 0x5565, 0x715E, - 0x7B5B, 0x6652, 0x73CA, 0x82EB, 0x6749, 0x5C71, 0x5220, 0x717D, - 0x886B, 0x95EA, 0x9655, 0x64C5, 0x8D61, 0x81B3, 0x5584, 0x6C55, - 0x6247, 0x7F2E, 0x5892, 0x4F24, 0x5546, 0x8D4F, 0x664C, 0x4E0A, - 0x5C1A, 0x88F3, 0x68A2, 0x634E, 0x7A0D, 0x70E7, 0x828D, 0x52FA, - 0x97F6, 0x5C11, 0x54E8, 0x90B5, 0x7ECD, 0x5962, 0x8D4A, 0x86C7, - 0x820C, 0x820D, 0x8D66, 0x6444, 0x5C04, 0x6151, 0x6D89, 0x793E, - 0x8BBE, 0x7837, 0x7533, 0x547B, 0x4F38, 0x8EAB, 0x6DF1, 0x5A20, - 0x7EC5, 0x795E, 0x6C88, 0x5BA1, 0x5A76, 0x751A, 0x80BE, 0x614E, - 0x6E17, 0x58F0, 0x751F, 0x7525, 0x7272, 0x5347, 0x7EF3 - }, - { - 0x8503, 0x8504, 0x8505, 0x8506, 0x8507, 0x8508, 0x8509, 0x850A, - 0x850B, 0x850D, 0x850E, 0x850F, 0x8510, 0x8512, 0x8514, 0x8515, - 0x8516, 0x8518, 0x8519, 0x851B, 0x851C, 0x851D, 0x851E, 0x8520, - 0x8522, 0x8523, 0x8524, 0x8525, 0x8526, 0x8527, 0x8528, 0x8529, - 0x852A, 0x852D, 0x852E, 0x852F, 0x8530, 0x8531, 0x8532, 0x8533, - 0x8534, 0x8535, 0x8536, 0x853E, 0x853F, 0x8540, 0x8541, 0x8542, - 0x8544, 0x8545, 0x8546, 0x8547, 0x854B, 0x854C, 0x854D, 0x854E, - 0x854F, 0x8550, 0x8551, 0x8552, 0x8553, 0x8554, 0x8555, 0x003F, - 0x8557, 0x8558, 0x855A, 0x855B, 0x855C, 0x855D, 0x855F, 0x8560, - 0x8561, 0x8562, 0x8563, 0x8565, 0x8566, 0x8567, 0x8569, 0x856A, - 0x856B, 0x856C, 0x856D, 0x856E, 0x856F, 0x8570, 0x8571, 0x8573, - 0x8575, 0x8576, 0x8577, 0x8578, 0x857C, 0x857D, 0x857F, 0x8580, - 0x8581, 0x7701, 0x76DB, 0x5269, 0x80DC, 0x5723, 0x5E08, 0x5931, - 0x72EE, 0x65BD, 0x6E7F, 0x8BD7, 0x5C38, 0x8671, 0x5341, 0x77F3, - 0x62FE, 0x65F6, 0x4EC0, 0x98DF, 0x8680, 0x5B9E, 0x8BC6, 0x53F2, - 0x77E2, 0x4F7F, 0x5C4E, 0x9A76, 0x59CB, 0x5F0F, 0x793A, 0x58EB, - 0x4E16, 0x67FF, 0x4E8B, 0x62ED, 0x8A93, 0x901D, 0x52BF, 0x662F, - 0x55DC, 0x566C, 0x9002, 0x4ED5, 0x4F8D, 0x91CA, 0x9970, 0x6C0F, - 0x5E02, 0x6043, 0x5BA4, 0x89C6, 0x8BD5, 0x6536, 0x624B, 0x9996, - 0x5B88, 0x5BFF, 0x6388, 0x552E, 0x53D7, 0x7626, 0x517D, 0x852C, - 0x67A2, 0x68B3, 0x6B8A, 0x6292, 0x8F93, 0x53D4, 0x8212, 0x6DD1, - 0x758F, 0x4E66, 0x8D4E, 0x5B70, 0x719F, 0x85AF, 0x6691, 0x66D9, - 0x7F72, 0x8700, 0x9ECD, 0x9F20, 0x5C5E, 0x672F, 0x8FF0, 0x6811, - 0x675F, 0x620D, 0x7AD6, 0x5885, 0x5EB6, 0x6570, 0x6F31 - }, - { - 0x8582, 0x8583, 0x8586, 0x8588, 0x8589, 0x858A, 0x858B, 0x858C, - 0x858D, 0x858E, 0x8590, 0x8591, 0x8592, 0x8593, 0x8594, 0x8595, - 0x8596, 0x8597, 0x8598, 0x8599, 0x859A, 0x859D, 0x859E, 0x859F, - 0x85A0, 0x85A1, 0x85A2, 0x85A3, 0x85A5, 0x85A6, 0x85A7, 0x85A9, - 0x85AB, 0x85AC, 0x85AD, 0x85B1, 0x85B2, 0x85B3, 0x85B4, 0x85B5, - 0x85B6, 0x85B8, 0x85BA, 0x85BB, 0x85BC, 0x85BD, 0x85BE, 0x85BF, - 0x85C0, 0x85C2, 0x85C3, 0x85C4, 0x85C5, 0x85C6, 0x85C7, 0x85C8, - 0x85CA, 0x85CB, 0x85CC, 0x85CD, 0x85CE, 0x85D1, 0x85D2, 0x003F, - 0x85D4, 0x85D6, 0x85D7, 0x85D8, 0x85D9, 0x85DA, 0x85DB, 0x85DD, - 0x85DE, 0x85DF, 0x85E0, 0x85E1, 0x85E2, 0x85E3, 0x85E5, 0x85E6, - 0x85E7, 0x85E8, 0x85EA, 0x85EB, 0x85EC, 0x85ED, 0x85EE, 0x85EF, - 0x85F0, 0x85F1, 0x85F2, 0x85F3, 0x85F4, 0x85F5, 0x85F6, 0x85F7, - 0x85F8, 0x6055, 0x5237, 0x800D, 0x6454, 0x8870, 0x7529, 0x5E05, - 0x6813, 0x62F4, 0x971C, 0x53CC, 0x723D, 0x8C01, 0x6C34, 0x7761, - 0x7A0E, 0x542E, 0x77AC, 0x987A, 0x821C, 0x8BF4, 0x7855, 0x6714, - 0x70C1, 0x65AF, 0x6495, 0x5636, 0x601D, 0x79C1, 0x53F8, 0x4E1D, - 0x6B7B, 0x8086, 0x5BFA, 0x55E3, 0x56DB, 0x4F3A, 0x4F3C, 0x9972, - 0x5DF3, 0x677E, 0x8038, 0x6002, 0x9882, 0x9001, 0x5B8B, 0x8BBC, - 0x8BF5, 0x641C, 0x8258, 0x64DE, 0x55FD, 0x82CF, 0x9165, 0x4FD7, - 0x7D20, 0x901F, 0x7C9F, 0x50F3, 0x5851, 0x6EAF, 0x5BBF, 0x8BC9, - 0x8083, 0x9178, 0x849C, 0x7B97, 0x867D, 0x968B, 0x968F, 0x7EE5, - 0x9AD3, 0x788E, 0x5C81, 0x7A57, 0x9042, 0x96A7, 0x795F, 0x5B59, - 0x635F, 0x7B0B, 0x84D1, 0x68AD, 0x5506, 0x7F29, 0x7410, 0x7D22, - 0x9501, 0x6240, 0x584C, 0x4ED6, 0x5B83, 0x5979, 0x5854 - }, - { - 0x85F9, 0x85FA, 0x85FC, 0x85FD, 0x85FE, 0x8600, 0x8601, 0x8602, - 0x8603, 0x8604, 0x8606, 0x8607, 0x8608, 0x8609, 0x860A, 0x860B, - 0x860C, 0x860D, 0x860E, 0x860F, 0x8610, 0x8612, 0x8613, 0x8614, - 0x8615, 0x8617, 0x8618, 0x8619, 0x861A, 0x861B, 0x861C, 0x861D, - 0x861E, 0x861F, 0x8620, 0x8621, 0x8622, 0x8623, 0x8624, 0x8625, - 0x8626, 0x8628, 0x862A, 0x862B, 0x862C, 0x862D, 0x862E, 0x862F, - 0x8630, 0x8631, 0x8632, 0x8633, 0x8634, 0x8635, 0x8636, 0x8637, - 0x8639, 0x863A, 0x863B, 0x863D, 0x863E, 0x863F, 0x8640, 0x003F, - 0x8641, 0x8642, 0x8643, 0x8644, 0x8645, 0x8646, 0x8647, 0x8648, - 0x8649, 0x864A, 0x864B, 0x864C, 0x8652, 0x8653, 0x8655, 0x8656, - 0x8657, 0x8658, 0x8659, 0x865B, 0x865C, 0x865D, 0x865F, 0x8660, - 0x8661, 0x8663, 0x8664, 0x8665, 0x8666, 0x8667, 0x8668, 0x8669, - 0x866A, 0x736D, 0x631E, 0x8E4B, 0x8E0F, 0x80CE, 0x82D4, 0x62AC, - 0x53F0, 0x6CF0, 0x915E, 0x592A, 0x6001, 0x6C70, 0x574D, 0x644A, - 0x8D2A, 0x762B, 0x6EE9, 0x575B, 0x6A80, 0x75F0, 0x6F6D, 0x8C2D, - 0x8C08, 0x5766, 0x6BEF, 0x8892, 0x78B3, 0x63A2, 0x53F9, 0x70AD, - 0x6C64, 0x5858, 0x642A, 0x5802, 0x68E0, 0x819B, 0x5510, 0x7CD6, - 0x5018, 0x8EBA, 0x6DCC, 0x8D9F, 0x70EB, 0x638F, 0x6D9B, 0x6ED4, - 0x7EE6, 0x8404, 0x6843, 0x9003, 0x6DD8, 0x9676, 0x8BA8, 0x5957, - 0x7279, 0x85E4, 0x817E, 0x75BC, 0x8A8A, 0x68AF, 0x5254, 0x8E22, - 0x9511, 0x63D0, 0x9898, 0x8E44, 0x557C, 0x4F53, 0x66FF, 0x568F, - 0x60D5, 0x6D95, 0x5243, 0x5C49, 0x5929, 0x6DFB, 0x586B, 0x7530, - 0x751C, 0x606C, 0x8214, 0x8146, 0x6311, 0x6761, 0x8FE2, 0x773A, - 0x8DF3, 0x8D34, 0x94C1, 0x5E16, 0x5385, 0x542C, 0x70C3 - }, - { - 0x866D, 0x866F, 0x8670, 0x8672, 0x8673, 0x8674, 0x8675, 0x8676, - 0x8677, 0x8678, 0x8683, 0x8684, 0x8685, 0x8686, 0x8687, 0x8688, - 0x8689, 0x868E, 0x868F, 0x8690, 0x8691, 0x8692, 0x8694, 0x8696, - 0x8697, 0x8698, 0x8699, 0x869A, 0x869B, 0x869E, 0x869F, 0x86A0, - 0x86A1, 0x86A2, 0x86A5, 0x86A6, 0x86AB, 0x86AD, 0x86AE, 0x86B2, - 0x86B3, 0x86B7, 0x86B8, 0x86B9, 0x86BB, 0x86BC, 0x86BD, 0x86BE, - 0x86BF, 0x86C1, 0x86C2, 0x86C3, 0x86C5, 0x86C8, 0x86CC, 0x86CD, - 0x86D2, 0x86D3, 0x86D5, 0x86D6, 0x86D7, 0x86DA, 0x86DC, 0x003F, - 0x86DD, 0x86E0, 0x86E1, 0x86E2, 0x86E3, 0x86E5, 0x86E6, 0x86E7, - 0x86E8, 0x86EA, 0x86EB, 0x86EC, 0x86EF, 0x86F5, 0x86F6, 0x86F7, - 0x86FA, 0x86FB, 0x86FC, 0x86FD, 0x86FF, 0x8701, 0x8704, 0x8705, - 0x8706, 0x870B, 0x870C, 0x870E, 0x870F, 0x8710, 0x8711, 0x8714, - 0x8716, 0x6C40, 0x5EF7, 0x505C, 0x4EAD, 0x5EAD, 0x633A, 0x8247, - 0x901A, 0x6850, 0x916E, 0x77B3, 0x540C, 0x94DC, 0x5F64, 0x7AE5, - 0x6876, 0x6345, 0x7B52, 0x7EDF, 0x75DB, 0x5077, 0x6295, 0x5934, - 0x900F, 0x51F8, 0x79C3, 0x7A81, 0x56FE, 0x5F92, 0x9014, 0x6D82, - 0x5C60, 0x571F, 0x5410, 0x5154, 0x6E4D, 0x56E2, 0x63A8, 0x9893, - 0x817F, 0x8715, 0x892A, 0x9000, 0x541E, 0x5C6F, 0x81C0, 0x62D6, - 0x6258, 0x8131, 0x9E35, 0x9640, 0x9A6E, 0x9A7C, 0x692D, 0x59A5, - 0x62D3, 0x553E, 0x6316, 0x54C7, 0x86D9, 0x6D3C, 0x5A03, 0x74E6, - 0x889C, 0x6B6A, 0x5916, 0x8C4C, 0x5F2F, 0x6E7E, 0x73A9, 0x987D, - 0x4E38, 0x70F7, 0x5B8C, 0x7897, 0x633D, 0x665A, 0x7696, 0x60CB, - 0x5B9B, 0x5A49, 0x4E07, 0x8155, 0x6C6A, 0x738B, 0x4EA1, 0x6789, - 0x7F51, 0x5F80, 0x65FA, 0x671B, 0x5FD8, 0x5984, 0x5A01 - }, - { - 0x8719, 0x871B, 0x871D, 0x871F, 0x8720, 0x8724, 0x8726, 0x8727, - 0x8728, 0x872A, 0x872B, 0x872C, 0x872D, 0x872F, 0x8730, 0x8732, - 0x8733, 0x8735, 0x8736, 0x8738, 0x8739, 0x873A, 0x873C, 0x873D, - 0x8740, 0x8741, 0x8742, 0x8743, 0x8744, 0x8745, 0x8746, 0x874A, - 0x874B, 0x874D, 0x874F, 0x8750, 0x8751, 0x8752, 0x8754, 0x8755, - 0x8756, 0x8758, 0x875A, 0x875B, 0x875C, 0x875D, 0x875E, 0x875F, - 0x8761, 0x8762, 0x8766, 0x8767, 0x8768, 0x8769, 0x876A, 0x876B, - 0x876C, 0x876D, 0x876F, 0x8771, 0x8772, 0x8773, 0x8775, 0x003F, - 0x8777, 0x8778, 0x8779, 0x877A, 0x877F, 0x8780, 0x8781, 0x8784, - 0x8786, 0x8787, 0x8789, 0x878A, 0x878C, 0x878E, 0x878F, 0x8790, - 0x8791, 0x8792, 0x8794, 0x8795, 0x8796, 0x8798, 0x8799, 0x879A, - 0x879B, 0x879C, 0x879D, 0x879E, 0x87A0, 0x87A1, 0x87A2, 0x87A3, - 0x87A4, 0x5DCD, 0x5FAE, 0x5371, 0x97E6, 0x8FDD, 0x6845, 0x56F4, - 0x552F, 0x60DF, 0x4E3A, 0x6F4D, 0x7EF4, 0x82C7, 0x840E, 0x59D4, - 0x4F1F, 0x4F2A, 0x5C3E, 0x7EAC, 0x672A, 0x851A, 0x5473, 0x754F, - 0x80C3, 0x5582, 0x9B4F, 0x4F4D, 0x6E2D, 0x8C13, 0x5C09, 0x6170, - 0x536B, 0x761F, 0x6E29, 0x868A, 0x6587, 0x95FB, 0x7EB9, 0x543B, - 0x7A33, 0x7D0A, 0x95EE, 0x55E1, 0x7FC1, 0x74EE, 0x631D, 0x8717, - 0x6DA1, 0x7A9D, 0x6211, 0x65A1, 0x5367, 0x63E1, 0x6C83, 0x5DEB, - 0x545C, 0x94A8, 0x4E4C, 0x6C61, 0x8BEC, 0x5C4B, 0x65E0, 0x829C, - 0x68A7, 0x543E, 0x5434, 0x6BCB, 0x6B66, 0x4E94, 0x6342, 0x5348, - 0x821E, 0x4F0D, 0x4FAE, 0x575E, 0x620A, 0x96FE, 0x6664, 0x7269, - 0x52FF, 0x52A1, 0x609F, 0x8BEF, 0x6614, 0x7199, 0x6790, 0x897F, - 0x7852, 0x77FD, 0x6670, 0x563B, 0x5438, 0x9521, 0x727A - }, - { - 0x87A5, 0x87A6, 0x87A7, 0x87A9, 0x87AA, 0x87AE, 0x87B0, 0x87B1, - 0x87B2, 0x87B4, 0x87B6, 0x87B7, 0x87B8, 0x87B9, 0x87BB, 0x87BC, - 0x87BE, 0x87BF, 0x87C1, 0x87C2, 0x87C3, 0x87C4, 0x87C5, 0x87C7, - 0x87C8, 0x87C9, 0x87CC, 0x87CD, 0x87CE, 0x87CF, 0x87D0, 0x87D4, - 0x87D5, 0x87D6, 0x87D7, 0x87D8, 0x87D9, 0x87DA, 0x87DC, 0x87DD, - 0x87DE, 0x87DF, 0x87E1, 0x87E2, 0x87E3, 0x87E4, 0x87E6, 0x87E7, - 0x87E8, 0x87E9, 0x87EB, 0x87EC, 0x87ED, 0x87EF, 0x87F0, 0x87F1, - 0x87F2, 0x87F3, 0x87F4, 0x87F5, 0x87F6, 0x87F7, 0x87F8, 0x003F, - 0x87FA, 0x87FB, 0x87FC, 0x87FD, 0x87FF, 0x8800, 0x8801, 0x8802, - 0x8804, 0x8805, 0x8806, 0x8807, 0x8808, 0x8809, 0x880B, 0x880C, - 0x880D, 0x880E, 0x880F, 0x8810, 0x8811, 0x8812, 0x8814, 0x8817, - 0x8818, 0x8819, 0x881A, 0x881C, 0x881D, 0x881E, 0x881F, 0x8820, - 0x8823, 0x7A00, 0x606F, 0x5E0C, 0x6089, 0x819D, 0x5915, 0x60DC, - 0x7184, 0x70EF, 0x6EAA, 0x6C50, 0x7280, 0x6A84, 0x88AD, 0x5E2D, - 0x4E60, 0x5AB3, 0x559C, 0x94E3, 0x6D17, 0x7CFB, 0x9699, 0x620F, - 0x7EC6, 0x778E, 0x867E, 0x5323, 0x971E, 0x8F96, 0x6687, 0x5CE1, - 0x4FA0, 0x72ED, 0x4E0B, 0x53A6, 0x590F, 0x5413, 0x6380, 0x9528, - 0x5148, 0x4ED9, 0x9C9C, 0x7EA4, 0x54B8, 0x8D24, 0x8854, 0x8237, - 0x95F2, 0x6D8E, 0x5F26, 0x5ACC, 0x663E, 0x9669, 0x73B0, 0x732E, - 0x53BF, 0x817A, 0x9985, 0x7FA1, 0x5BAA, 0x9677, 0x9650, 0x7EBF, - 0x76F8, 0x53A2, 0x9576, 0x9999, 0x7BB1, 0x8944, 0x6E58, 0x4E61, - 0x7FD4, 0x7965, 0x8BE6, 0x60F3, 0x54CD, 0x4EAB, 0x9879, 0x5DF7, - 0x6A61, 0x50CF, 0x5411, 0x8C61, 0x8427, 0x785D, 0x9704, 0x524A, - 0x54EE, 0x56A3, 0x9500, 0x6D88, 0x5BB5, 0x6DC6, 0x6653 - }, - { - 0x8824, 0x8825, 0x8826, 0x8827, 0x8828, 0x8829, 0x882A, 0x882B, - 0x882C, 0x882D, 0x882E, 0x882F, 0x8830, 0x8831, 0x8833, 0x8834, - 0x8835, 0x8836, 0x8837, 0x8838, 0x883A, 0x883B, 0x883D, 0x883E, - 0x883F, 0x8841, 0x8842, 0x8843, 0x8846, 0x8847, 0x8848, 0x8849, - 0x884A, 0x884B, 0x884E, 0x884F, 0x8850, 0x8851, 0x8852, 0x8853, - 0x8855, 0x8856, 0x8858, 0x885A, 0x885B, 0x885C, 0x885D, 0x885E, - 0x885F, 0x8860, 0x8866, 0x8867, 0x886A, 0x886D, 0x886F, 0x8871, - 0x8873, 0x8874, 0x8875, 0x8876, 0x8878, 0x8879, 0x887A, 0x003F, - 0x887B, 0x887C, 0x8880, 0x8883, 0x8886, 0x8887, 0x8889, 0x888A, - 0x888C, 0x888E, 0x888F, 0x8890, 0x8891, 0x8893, 0x8894, 0x8895, - 0x8897, 0x8898, 0x8899, 0x889A, 0x889B, 0x889D, 0x889E, 0x889F, - 0x88A0, 0x88A1, 0x88A3, 0x88A5, 0x88A6, 0x88A7, 0x88A8, 0x88A9, - 0x88AA, 0x5C0F, 0x5B5D, 0x6821, 0x8096, 0x5578, 0x7B11, 0x6548, - 0x6954, 0x4E9B, 0x6B47, 0x874E, 0x978B, 0x534F, 0x631F, 0x643A, - 0x90AA, 0x659C, 0x80C1, 0x8C10, 0x5199, 0x68B0, 0x5378, 0x87F9, - 0x61C8, 0x6CC4, 0x6CFB, 0x8C22, 0x5C51, 0x85AA, 0x82AF, 0x950C, - 0x6B23, 0x8F9B, 0x65B0, 0x5FFB, 0x5FC3, 0x4FE1, 0x8845, 0x661F, - 0x8165, 0x7329, 0x60FA, 0x5174, 0x5211, 0x578B, 0x5F62, 0x90A2, - 0x884C, 0x9192, 0x5E78, 0x674F, 0x6027, 0x59D3, 0x5144, 0x51F6, - 0x80F8, 0x5308, 0x6C79, 0x96C4, 0x718A, 0x4F11, 0x4FEE, 0x7F9E, - 0x673D, 0x55C5, 0x9508, 0x79C0, 0x8896, 0x7EE3, 0x589F, 0x620C, - 0x9700, 0x865A, 0x5618, 0x987B, 0x5F90, 0x8BB8, 0x84C4, 0x9157, - 0x53D9, 0x65ED, 0x5E8F, 0x755C, 0x6064, 0x7D6E, 0x5A7F, 0x7EEA, - 0x7EED, 0x8F69, 0x55A7, 0x5BA3, 0x60AC, 0x65CB, 0x7384 - }, - { - 0x88AC, 0x88AE, 0x88AF, 0x88B0, 0x88B2, 0x88B3, 0x88B4, 0x88B5, - 0x88B6, 0x88B8, 0x88B9, 0x88BA, 0x88BB, 0x88BD, 0x88BE, 0x88BF, - 0x88C0, 0x88C3, 0x88C4, 0x88C7, 0x88C8, 0x88CA, 0x88CB, 0x88CC, - 0x88CD, 0x88CF, 0x88D0, 0x88D1, 0x88D3, 0x88D6, 0x88D7, 0x88DA, - 0x88DB, 0x88DC, 0x88DD, 0x88DE, 0x88E0, 0x88E1, 0x88E6, 0x88E7, - 0x88E9, 0x88EA, 0x88EB, 0x88EC, 0x88ED, 0x88EE, 0x88EF, 0x88F2, - 0x88F5, 0x88F6, 0x88F7, 0x88FA, 0x88FB, 0x88FD, 0x88FF, 0x8900, - 0x8901, 0x8903, 0x8904, 0x8905, 0x8906, 0x8907, 0x8908, 0x003F, - 0x8909, 0x890B, 0x890C, 0x890D, 0x890E, 0x890F, 0x8911, 0x8914, - 0x8915, 0x8916, 0x8917, 0x8918, 0x891C, 0x891D, 0x891E, 0x891F, - 0x8920, 0x8922, 0x8923, 0x8924, 0x8926, 0x8927, 0x8928, 0x8929, - 0x892C, 0x892D, 0x892E, 0x892F, 0x8931, 0x8932, 0x8933, 0x8935, - 0x8937, 0x9009, 0x7663, 0x7729, 0x7EDA, 0x9774, 0x859B, 0x5B66, - 0x7A74, 0x96EA, 0x8840, 0x52CB, 0x718F, 0x5FAA, 0x65EC, 0x8BE2, - 0x5BFB, 0x9A6F, 0x5DE1, 0x6B89, 0x6C5B, 0x8BAD, 0x8BAF, 0x900A, - 0x8FC5, 0x538B, 0x62BC, 0x9E26, 0x9E2D, 0x5440, 0x4E2B, 0x82BD, - 0x7259, 0x869C, 0x5D16, 0x8859, 0x6DAF, 0x96C5, 0x54D1, 0x4E9A, - 0x8BB6, 0x7109, 0x54BD, 0x9609, 0x70DF, 0x6DF9, 0x76D0, 0x4E25, - 0x7814, 0x8712, 0x5CA9, 0x5EF6, 0x8A00, 0x989C, 0x960E, 0x708E, - 0x6CBF, 0x5944, 0x63A9, 0x773C, 0x884D, 0x6F14, 0x8273, 0x5830, - 0x71D5, 0x538C, 0x781A, 0x96C1, 0x5501, 0x5F66, 0x7130, 0x5BB4, - 0x8C1A, 0x9A8C, 0x6B83, 0x592E, 0x9E2F, 0x79E7, 0x6768, 0x626C, - 0x4F6F, 0x75A1, 0x7F8A, 0x6D0B, 0x9633, 0x6C27, 0x4EF0, 0x75D2, - 0x517B, 0x6837, 0x6F3E, 0x9080, 0x8170, 0x5996, 0x7476 - }, - { - 0x8938, 0x8939, 0x893A, 0x893B, 0x893C, 0x893D, 0x893E, 0x893F, - 0x8940, 0x8942, 0x8943, 0x8945, 0x8946, 0x8947, 0x8948, 0x8949, - 0x894A, 0x894B, 0x894C, 0x894D, 0x894E, 0x894F, 0x8950, 0x8951, - 0x8952, 0x8953, 0x8954, 0x8955, 0x8956, 0x8957, 0x8958, 0x8959, - 0x895A, 0x895B, 0x895C, 0x895D, 0x8960, 0x8961, 0x8962, 0x8963, - 0x8964, 0x8965, 0x8967, 0x8968, 0x8969, 0x896A, 0x896B, 0x896C, - 0x896D, 0x896E, 0x896F, 0x8970, 0x8971, 0x8972, 0x8973, 0x8974, - 0x8975, 0x8976, 0x8977, 0x8978, 0x8979, 0x897A, 0x897C, 0x003F, - 0x897D, 0x897E, 0x8980, 0x8982, 0x8984, 0x8985, 0x8987, 0x8988, - 0x8989, 0x898A, 0x898B, 0x898C, 0x898D, 0x898E, 0x898F, 0x8990, - 0x8991, 0x8992, 0x8993, 0x8994, 0x8995, 0x8996, 0x8997, 0x8998, - 0x8999, 0x899A, 0x899B, 0x899C, 0x899D, 0x899E, 0x899F, 0x89A0, - 0x89A1, 0x6447, 0x5C27, 0x9065, 0x7A91, 0x8C23, 0x59DA, 0x54AC, - 0x8200, 0x836F, 0x8981, 0x8000, 0x6930, 0x564E, 0x8036, 0x7237, - 0x91CE, 0x51B6, 0x4E5F, 0x9875, 0x6396, 0x4E1A, 0x53F6, 0x66F3, - 0x814B, 0x591C, 0x6DB2, 0x4E00, 0x58F9, 0x533B, 0x63D6, 0x94F1, - 0x4F9D, 0x4F0A, 0x8863, 0x9890, 0x5937, 0x9057, 0x79FB, 0x4EEA, - 0x80F0, 0x7591, 0x6C82, 0x5B9C, 0x59E8, 0x5F5D, 0x6905, 0x8681, - 0x501A, 0x5DF2, 0x4E59, 0x77E3, 0x4EE5, 0x827A, 0x6291, 0x6613, - 0x9091, 0x5C79, 0x4EBF, 0x5F79, 0x81C6, 0x9038, 0x8084, 0x75AB, - 0x4EA6, 0x88D4, 0x610F, 0x6BC5, 0x5FC6, 0x4E49, 0x76CA, 0x6EA2, - 0x8BE3, 0x8BAE, 0x8C0A, 0x8BD1, 0x5F02, 0x7FFC, 0x7FCC, 0x7ECE, - 0x8335, 0x836B, 0x56E0, 0x6BB7, 0x97F3, 0x9634, 0x59FB, 0x541F, - 0x94F6, 0x6DEB, 0x5BC5, 0x996E, 0x5C39, 0x5F15, 0x9690 - }, - { - 0x89A2, 0x89A3, 0x89A4, 0x89A5, 0x89A6, 0x89A7, 0x89A8, 0x89A9, - 0x89AA, 0x89AB, 0x89AC, 0x89AD, 0x89AE, 0x89AF, 0x89B0, 0x89B1, - 0x89B2, 0x89B3, 0x89B4, 0x89B5, 0x89B6, 0x89B7, 0x89B8, 0x89B9, - 0x89BA, 0x89BB, 0x89BC, 0x89BD, 0x89BE, 0x89BF, 0x89C0, 0x89C3, - 0x89CD, 0x89D3, 0x89D4, 0x89D5, 0x89D7, 0x89D8, 0x89D9, 0x89DB, - 0x89DD, 0x89DF, 0x89E0, 0x89E1, 0x89E2, 0x89E4, 0x89E7, 0x89E8, - 0x89E9, 0x89EA, 0x89EC, 0x89ED, 0x89EE, 0x89F0, 0x89F1, 0x89F2, - 0x89F4, 0x89F5, 0x89F6, 0x89F7, 0x89F8, 0x89F9, 0x89FA, 0x003F, - 0x89FB, 0x89FC, 0x89FD, 0x89FE, 0x89FF, 0x8A01, 0x8A02, 0x8A03, - 0x8A04, 0x8A05, 0x8A06, 0x8A08, 0x8A09, 0x8A0A, 0x8A0B, 0x8A0C, - 0x8A0D, 0x8A0E, 0x8A0F, 0x8A10, 0x8A11, 0x8A12, 0x8A13, 0x8A14, - 0x8A15, 0x8A16, 0x8A17, 0x8A18, 0x8A19, 0x8A1A, 0x8A1B, 0x8A1C, - 0x8A1D, 0x5370, 0x82F1, 0x6A31, 0x5A74, 0x9E70, 0x5E94, 0x7F28, - 0x83B9, 0x8424, 0x8425, 0x8367, 0x8747, 0x8FCE, 0x8D62, 0x76C8, - 0x5F71, 0x9896, 0x786C, 0x6620, 0x54DF, 0x62E5, 0x4F63, 0x81C3, - 0x75C8, 0x5EB8, 0x96CD, 0x8E0A, 0x86F9, 0x548F, 0x6CF3, 0x6D8C, - 0x6C38, 0x607F, 0x52C7, 0x7528, 0x5E7D, 0x4F18, 0x60A0, 0x5FE7, - 0x5C24, 0x7531, 0x90AE, 0x94C0, 0x72B9, 0x6CB9, 0x6E38, 0x9149, - 0x6709, 0x53CB, 0x53F3, 0x4F51, 0x91C9, 0x8BF1, 0x53C8, 0x5E7C, - 0x8FC2, 0x6DE4, 0x4E8E, 0x76C2, 0x6986, 0x865E, 0x611A, 0x8206, - 0x4F59, 0x4FDE, 0x903E, 0x9C7C, 0x6109, 0x6E1D, 0x6E14, 0x9685, - 0x4E88, 0x5A31, 0x96E8, 0x4E0E, 0x5C7F, 0x79B9, 0x5B87, 0x8BED, - 0x7FBD, 0x7389, 0x57DF, 0x828B, 0x90C1, 0x5401, 0x9047, 0x55BB, - 0x5CEA, 0x5FA1, 0x6108, 0x6B32, 0x72F1, 0x80B2, 0x8A89 - }, - { - 0x8A1E, 0x8A1F, 0x8A20, 0x8A21, 0x8A22, 0x8A23, 0x8A24, 0x8A25, - 0x8A26, 0x8A27, 0x8A28, 0x8A29, 0x8A2A, 0x8A2B, 0x8A2C, 0x8A2D, - 0x8A2E, 0x8A2F, 0x8A30, 0x8A31, 0x8A32, 0x8A33, 0x8A34, 0x8A35, - 0x8A36, 0x8A37, 0x8A38, 0x8A39, 0x8A3A, 0x8A3B, 0x8A3C, 0x8A3D, - 0x8A3F, 0x8A40, 0x8A41, 0x8A42, 0x8A43, 0x8A44, 0x8A45, 0x8A46, - 0x8A47, 0x8A49, 0x8A4A, 0x8A4B, 0x8A4C, 0x8A4D, 0x8A4E, 0x8A4F, - 0x8A50, 0x8A51, 0x8A52, 0x8A53, 0x8A54, 0x8A55, 0x8A56, 0x8A57, - 0x8A58, 0x8A59, 0x8A5A, 0x8A5B, 0x8A5C, 0x8A5D, 0x8A5E, 0x003F, - 0x8A5F, 0x8A60, 0x8A61, 0x8A62, 0x8A63, 0x8A64, 0x8A65, 0x8A66, - 0x8A67, 0x8A68, 0x8A69, 0x8A6A, 0x8A6B, 0x8A6C, 0x8A6D, 0x8A6E, - 0x8A6F, 0x8A70, 0x8A71, 0x8A72, 0x8A73, 0x8A74, 0x8A75, 0x8A76, - 0x8A77, 0x8A78, 0x8A7A, 0x8A7B, 0x8A7C, 0x8A7D, 0x8A7E, 0x8A7F, - 0x8A80, 0x6D74, 0x5BD3, 0x88D5, 0x9884, 0x8C6B, 0x9A6D, 0x9E33, - 0x6E0A, 0x51A4, 0x5143, 0x57A3, 0x8881, 0x539F, 0x63F4, 0x8F95, - 0x56ED, 0x5458, 0x5706, 0x733F, 0x6E90, 0x7F18, 0x8FDC, 0x82D1, - 0x613F, 0x6028, 0x9662, 0x66F0, 0x7EA6, 0x8D8A, 0x8DC3, 0x94A5, - 0x5CB3, 0x7CA4, 0x6708, 0x60A6, 0x9605, 0x8018, 0x4E91, 0x90E7, - 0x5300, 0x9668, 0x5141, 0x8FD0, 0x8574, 0x915D, 0x6655, 0x97F5, - 0x5B55, 0x531D, 0x7838, 0x6742, 0x683D, 0x54C9, 0x707E, 0x5BB0, - 0x8F7D, 0x518D, 0x5728, 0x54B1, 0x6512, 0x6682, 0x8D5E, 0x8D43, - 0x810F, 0x846C, 0x906D, 0x7CDF, 0x51FF, 0x85FB, 0x67A3, 0x65E9, - 0x6FA1, 0x86A4, 0x8E81, 0x566A, 0x9020, 0x7682, 0x7076, 0x71E5, - 0x8D23, 0x62E9, 0x5219, 0x6CFD, 0x8D3C, 0x600E, 0x589E, 0x618E, - 0x66FE, 0x8D60, 0x624E, 0x55B3, 0x6E23, 0x672D, 0x8F67 - }, - { - 0x8A81, 0x8A82, 0x8A83, 0x8A84, 0x8A85, 0x8A86, 0x8A87, 0x8A88, - 0x8A8B, 0x8A8C, 0x8A8D, 0x8A8E, 0x8A8F, 0x8A90, 0x8A91, 0x8A92, - 0x8A94, 0x8A95, 0x8A96, 0x8A97, 0x8A98, 0x8A99, 0x8A9A, 0x8A9B, - 0x8A9C, 0x8A9D, 0x8A9E, 0x8A9F, 0x8AA0, 0x8AA1, 0x8AA2, 0x8AA3, - 0x8AA4, 0x8AA5, 0x8AA6, 0x8AA7, 0x8AA8, 0x8AA9, 0x8AAA, 0x8AAB, - 0x8AAC, 0x8AAD, 0x8AAE, 0x8AAF, 0x8AB0, 0x8AB1, 0x8AB2, 0x8AB3, - 0x8AB4, 0x8AB5, 0x8AB6, 0x8AB7, 0x8AB8, 0x8AB9, 0x8ABA, 0x8ABB, - 0x8ABC, 0x8ABD, 0x8ABE, 0x8ABF, 0x8AC0, 0x8AC1, 0x8AC2, 0x003F, - 0x8AC3, 0x8AC4, 0x8AC5, 0x8AC6, 0x8AC7, 0x8AC8, 0x8AC9, 0x8ACA, - 0x8ACB, 0x8ACC, 0x8ACD, 0x8ACE, 0x8ACF, 0x8AD0, 0x8AD1, 0x8AD2, - 0x8AD3, 0x8AD4, 0x8AD5, 0x8AD6, 0x8AD7, 0x8AD8, 0x8AD9, 0x8ADA, - 0x8ADB, 0x8ADC, 0x8ADD, 0x8ADE, 0x8ADF, 0x8AE0, 0x8AE1, 0x8AE2, - 0x8AE3, 0x94E1, 0x95F8, 0x7728, 0x6805, 0x69A8, 0x548B, 0x4E4D, - 0x70B8, 0x8BC8, 0x6458, 0x658B, 0x5B85, 0x7A84, 0x503A, 0x5BE8, - 0x77BB, 0x6BE1, 0x8A79, 0x7C98, 0x6CBE, 0x76CF, 0x65A9, 0x8F97, - 0x5D2D, 0x5C55, 0x8638, 0x6808, 0x5360, 0x6218, 0x7AD9, 0x6E5B, - 0x7EFD, 0x6A1F, 0x7AE0, 0x5F70, 0x6F33, 0x5F20, 0x638C, 0x6DA8, - 0x6756, 0x4E08, 0x5E10, 0x8D26, 0x4ED7, 0x80C0, 0x7634, 0x969C, - 0x62DB, 0x662D, 0x627E, 0x6CBC, 0x8D75, 0x7167, 0x7F69, 0x5146, - 0x8087, 0x53EC, 0x906E, 0x6298, 0x54F2, 0x86F0, 0x8F99, 0x8005, - 0x9517, 0x8517, 0x8FD9, 0x6D59, 0x73CD, 0x659F, 0x771F, 0x7504, - 0x7827, 0x81FB, 0x8D1E, 0x9488, 0x4FA6, 0x6795, 0x75B9, 0x8BCA, - 0x9707, 0x632F, 0x9547, 0x9635, 0x84B8, 0x6323, 0x7741, 0x5F81, - 0x72F0, 0x4E89, 0x6014, 0x6574, 0x62EF, 0x6B63, 0x653F - }, - { - 0x8AE4, 0x8AE5, 0x8AE6, 0x8AE7, 0x8AE8, 0x8AE9, 0x8AEA, 0x8AEB, - 0x8AEC, 0x8AED, 0x8AEE, 0x8AEF, 0x8AF0, 0x8AF1, 0x8AF2, 0x8AF3, - 0x8AF4, 0x8AF5, 0x8AF6, 0x8AF7, 0x8AF8, 0x8AF9, 0x8AFA, 0x8AFB, - 0x8AFC, 0x8AFD, 0x8AFE, 0x8AFF, 0x8B00, 0x8B01, 0x8B02, 0x8B03, - 0x8B04, 0x8B05, 0x8B06, 0x8B08, 0x8B09, 0x8B0A, 0x8B0B, 0x8B0C, - 0x8B0D, 0x8B0E, 0x8B0F, 0x8B10, 0x8B11, 0x8B12, 0x8B13, 0x8B14, - 0x8B15, 0x8B16, 0x8B17, 0x8B18, 0x8B19, 0x8B1A, 0x8B1B, 0x8B1C, - 0x8B1D, 0x8B1E, 0x8B1F, 0x8B20, 0x8B21, 0x8B22, 0x8B23, 0x003F, - 0x8B24, 0x8B25, 0x8B27, 0x8B28, 0x8B29, 0x8B2A, 0x8B2B, 0x8B2C, - 0x8B2D, 0x8B2E, 0x8B2F, 0x8B30, 0x8B31, 0x8B32, 0x8B33, 0x8B34, - 0x8B35, 0x8B36, 0x8B37, 0x8B38, 0x8B39, 0x8B3A, 0x8B3B, 0x8B3C, - 0x8B3D, 0x8B3E, 0x8B3F, 0x8B40, 0x8B41, 0x8B42, 0x8B43, 0x8B44, - 0x8B45, 0x5E27, 0x75C7, 0x90D1, 0x8BC1, 0x829D, 0x679D, 0x652F, - 0x5431, 0x8718, 0x77E5, 0x80A2, 0x8102, 0x6C41, 0x4E4B, 0x7EC7, - 0x804C, 0x76F4, 0x690D, 0x6B96, 0x6267, 0x503C, 0x4F84, 0x5740, - 0x6307, 0x6B62, 0x8DBE, 0x53EA, 0x65E8, 0x7EB8, 0x5FD7, 0x631A, - 0x63B7, 0x81F3, 0x81F4, 0x7F6E, 0x5E1C, 0x5CD9, 0x5236, 0x667A, - 0x79E9, 0x7A1A, 0x8D28, 0x7099, 0x75D4, 0x6EDE, 0x6CBB, 0x7A92, - 0x4E2D, 0x76C5, 0x5FE0, 0x949F, 0x8877, 0x7EC8, 0x79CD, 0x80BF, - 0x91CD, 0x4EF2, 0x4F17, 0x821F, 0x5468, 0x5DDE, 0x6D32, 0x8BCC, - 0x7CA5, 0x8F74, 0x8098, 0x5E1A, 0x5492, 0x76B1, 0x5B99, 0x663C, - 0x9AA4, 0x73E0, 0x682A, 0x86DB, 0x6731, 0x732A, 0x8BF8, 0x8BDB, - 0x9010, 0x7AF9, 0x70DB, 0x716E, 0x62C4, 0x77A9, 0x5631, 0x4E3B, - 0x8457, 0x67F1, 0x52A9, 0x86C0, 0x8D2E, 0x94F8, 0x7B51 - }, - { - 0x8B46, 0x8B47, 0x8B48, 0x8B49, 0x8B4A, 0x8B4B, 0x8B4C, 0x8B4D, - 0x8B4E, 0x8B4F, 0x8B50, 0x8B51, 0x8B52, 0x8B53, 0x8B54, 0x8B55, - 0x8B56, 0x8B57, 0x8B58, 0x8B59, 0x8B5A, 0x8B5B, 0x8B5C, 0x8B5D, - 0x8B5E, 0x8B5F, 0x8B60, 0x8B61, 0x8B62, 0x8B63, 0x8B64, 0x8B65, - 0x8B67, 0x8B68, 0x8B69, 0x8B6A, 0x8B6B, 0x8B6D, 0x8B6E, 0x8B6F, - 0x8B70, 0x8B71, 0x8B72, 0x8B73, 0x8B74, 0x8B75, 0x8B76, 0x8B77, - 0x8B78, 0x8B79, 0x8B7A, 0x8B7B, 0x8B7C, 0x8B7D, 0x8B7E, 0x8B7F, - 0x8B80, 0x8B81, 0x8B82, 0x8B83, 0x8B84, 0x8B85, 0x8B86, 0x003F, - 0x8B87, 0x8B88, 0x8B89, 0x8B8A, 0x8B8B, 0x8B8C, 0x8B8D, 0x8B8E, - 0x8B8F, 0x8B90, 0x8B91, 0x8B92, 0x8B93, 0x8B94, 0x8B95, 0x8B96, - 0x8B97, 0x8B98, 0x8B99, 0x8B9A, 0x8B9B, 0x8B9C, 0x8B9D, 0x8B9E, - 0x8B9F, 0x8BAC, 0x8BB1, 0x8BBB, 0x8BC7, 0x8BD0, 0x8BEA, 0x8C09, - 0x8C1E, 0x4F4F, 0x6CE8, 0x795D, 0x9A7B, 0x6293, 0x722A, 0x62FD, - 0x4E13, 0x7816, 0x8F6C, 0x64B0, 0x8D5A, 0x7BC6, 0x6869, 0x5E84, - 0x88C5, 0x5986, 0x649E, 0x58EE, 0x72B6, 0x690E, 0x9525, 0x8FFD, - 0x8D58, 0x5760, 0x7F00, 0x8C06, 0x51C6, 0x6349, 0x62D9, 0x5353, - 0x684C, 0x7422, 0x8301, 0x914C, 0x5544, 0x7740, 0x707C, 0x6D4A, - 0x5179, 0x54A8, 0x8D44, 0x59FF, 0x6ECB, 0x6DC4, 0x5B5C, 0x7D2B, - 0x4ED4, 0x7C7D, 0x6ED3, 0x5B50, 0x81EA, 0x6E0D, 0x5B57, 0x9B03, - 0x68D5, 0x8E2A, 0x5B97, 0x7EFC, 0x603B, 0x7EB5, 0x90B9, 0x8D70, - 0x594F, 0x63CD, 0x79DF, 0x8DB3, 0x5352, 0x65CF, 0x7956, 0x8BC5, - 0x963B, 0x7EC4, 0x94BB, 0x7E82, 0x5634, 0x9189, 0x6700, 0x7F6A, - 0x5C0A, 0x9075, 0x6628, 0x5DE6, 0x4F50, 0x67DE, 0x505A, 0x4F5C, - 0x5750, 0x5EA7, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F - }, - { - 0x8C38, 0x8C39, 0x8C3A, 0x8C3B, 0x8C3C, 0x8C3D, 0x8C3E, 0x8C3F, - 0x8C40, 0x8C42, 0x8C43, 0x8C44, 0x8C45, 0x8C48, 0x8C4A, 0x8C4B, - 0x8C4D, 0x8C4E, 0x8C4F, 0x8C50, 0x8C51, 0x8C52, 0x8C53, 0x8C54, - 0x8C56, 0x8C57, 0x8C58, 0x8C59, 0x8C5B, 0x8C5C, 0x8C5D, 0x8C5E, - 0x8C5F, 0x8C60, 0x8C63, 0x8C64, 0x8C65, 0x8C66, 0x8C67, 0x8C68, - 0x8C69, 0x8C6C, 0x8C6D, 0x8C6E, 0x8C6F, 0x8C70, 0x8C71, 0x8C72, - 0x8C74, 0x8C75, 0x8C76, 0x8C77, 0x8C7B, 0x8C7C, 0x8C7D, 0x8C7E, - 0x8C7F, 0x8C80, 0x8C81, 0x8C83, 0x8C84, 0x8C86, 0x8C87, 0x003F, - 0x8C88, 0x8C8B, 0x8C8D, 0x8C8E, 0x8C8F, 0x8C90, 0x8C91, 0x8C92, - 0x8C93, 0x8C95, 0x8C96, 0x8C97, 0x8C99, 0x8C9A, 0x8C9B, 0x8C9C, - 0x8C9D, 0x8C9E, 0x8C9F, 0x8CA0, 0x8CA1, 0x8CA2, 0x8CA3, 0x8CA4, - 0x8CA5, 0x8CA6, 0x8CA7, 0x8CA8, 0x8CA9, 0x8CAA, 0x8CAB, 0x8CAC, - 0x8CAD, 0x4E8D, 0x4E0C, 0x5140, 0x4E10, 0x5EFF, 0x5345, 0x4E15, - 0x4E98, 0x4E1E, 0x9B32, 0x5B6C, 0x5669, 0x4E28, 0x79BA, 0x4E3F, - 0x5315, 0x4E47, 0x592D, 0x723B, 0x536E, 0x6C10, 0x56DF, 0x80E4, - 0x9997, 0x6BD3, 0x777E, 0x9F17, 0x4E36, 0x4E9F, 0x9F10, 0x4E5C, - 0x4E69, 0x4E93, 0x8288, 0x5B5B, 0x556C, 0x560F, 0x4EC4, 0x538D, - 0x539D, 0x53A3, 0x53A5, 0x53AE, 0x9765, 0x8D5D, 0x531A, 0x53F5, - 0x5326, 0x532E, 0x533E, 0x8D5C, 0x5366, 0x5363, 0x5202, 0x5208, - 0x520E, 0x522D, 0x5233, 0x523F, 0x5240, 0x524C, 0x525E, 0x5261, - 0x525C, 0x84AF, 0x527D, 0x5282, 0x5281, 0x5290, 0x5293, 0x5182, - 0x7F54, 0x4EBB, 0x4EC3, 0x4EC9, 0x4EC2, 0x4EE8, 0x4EE1, 0x4EEB, - 0x4EDE, 0x4F1B, 0x4EF3, 0x4F22, 0x4F64, 0x4EF5, 0x4F25, 0x4F27, - 0x4F09, 0x4F2B, 0x4F5E, 0x4F67, 0x6538, 0x4F5A, 0x4F5D - }, - { - 0x8CAE, 0x8CAF, 0x8CB0, 0x8CB1, 0x8CB2, 0x8CB3, 0x8CB4, 0x8CB5, - 0x8CB6, 0x8CB7, 0x8CB8, 0x8CB9, 0x8CBA, 0x8CBB, 0x8CBC, 0x8CBD, - 0x8CBE, 0x8CBF, 0x8CC0, 0x8CC1, 0x8CC2, 0x8CC3, 0x8CC4, 0x8CC5, - 0x8CC6, 0x8CC7, 0x8CC8, 0x8CC9, 0x8CCA, 0x8CCB, 0x8CCC, 0x8CCD, - 0x8CCE, 0x8CCF, 0x8CD0, 0x8CD1, 0x8CD2, 0x8CD3, 0x8CD4, 0x8CD5, - 0x8CD6, 0x8CD7, 0x8CD8, 0x8CD9, 0x8CDA, 0x8CDB, 0x8CDC, 0x8CDD, - 0x8CDE, 0x8CDF, 0x8CE0, 0x8CE1, 0x8CE2, 0x8CE3, 0x8CE4, 0x8CE5, - 0x8CE6, 0x8CE7, 0x8CE8, 0x8CE9, 0x8CEA, 0x8CEB, 0x8CEC, 0x003F, - 0x8CED, 0x8CEE, 0x8CEF, 0x8CF0, 0x8CF1, 0x8CF2, 0x8CF3, 0x8CF4, - 0x8CF5, 0x8CF6, 0x8CF7, 0x8CF8, 0x8CF9, 0x8CFA, 0x8CFB, 0x8CFC, - 0x8CFD, 0x8CFE, 0x8CFF, 0x8D00, 0x8D01, 0x8D02, 0x8D03, 0x8D04, - 0x8D05, 0x8D06, 0x8D07, 0x8D08, 0x8D09, 0x8D0A, 0x8D0B, 0x8D0C, - 0x8D0D, 0x4F5F, 0x4F57, 0x4F32, 0x4F3D, 0x4F76, 0x4F74, 0x4F91, - 0x4F89, 0x4F83, 0x4F8F, 0x4F7E, 0x4F7B, 0x4FAA, 0x4F7C, 0x4FAC, - 0x4F94, 0x4FE6, 0x4FE8, 0x4FEA, 0x4FC5, 0x4FDA, 0x4FE3, 0x4FDC, - 0x4FD1, 0x4FDF, 0x4FF8, 0x5029, 0x504C, 0x4FF3, 0x502C, 0x500F, - 0x502E, 0x502D, 0x4FFE, 0x501C, 0x500C, 0x5025, 0x5028, 0x507E, - 0x5043, 0x5055, 0x5048, 0x504E, 0x506C, 0x507B, 0x50A5, 0x50A7, - 0x50A9, 0x50BA, 0x50D6, 0x5106, 0x50ED, 0x50EC, 0x50E6, 0x50EE, - 0x5107, 0x510B, 0x4EDD, 0x6C3D, 0x4F58, 0x4F65, 0x4FCE, 0x9FA0, - 0x6C46, 0x7C74, 0x516E, 0x5DFD, 0x9EC9, 0x9998, 0x5181, 0x5914, - 0x52F9, 0x530D, 0x8A07, 0x5310, 0x51EB, 0x5919, 0x5155, 0x4EA0, - 0x5156, 0x4EB3, 0x886E, 0x88A4, 0x4EB5, 0x8114, 0x88D2, 0x7980, - 0x5B34, 0x8803, 0x7FB8, 0x51AB, 0x51B1, 0x51BD, 0x51BC - }, - { - 0x8D0E, 0x8D0F, 0x8D10, 0x8D11, 0x8D12, 0x8D13, 0x8D14, 0x8D15, - 0x8D16, 0x8D17, 0x8D18, 0x8D19, 0x8D1A, 0x8D1B, 0x8D1C, 0x8D20, - 0x8D51, 0x8D52, 0x8D57, 0x8D5F, 0x8D65, 0x8D68, 0x8D69, 0x8D6A, - 0x8D6C, 0x8D6E, 0x8D6F, 0x8D71, 0x8D72, 0x8D78, 0x8D79, 0x8D7A, - 0x8D7B, 0x8D7C, 0x8D7D, 0x8D7E, 0x8D7F, 0x8D80, 0x8D82, 0x8D83, - 0x8D86, 0x8D87, 0x8D88, 0x8D89, 0x8D8C, 0x8D8D, 0x8D8E, 0x8D8F, - 0x8D90, 0x8D92, 0x8D93, 0x8D95, 0x8D96, 0x8D97, 0x8D98, 0x8D99, - 0x8D9A, 0x8D9B, 0x8D9C, 0x8D9D, 0x8D9E, 0x8DA0, 0x8DA1, 0x003F, - 0x8DA2, 0x8DA4, 0x8DA5, 0x8DA6, 0x8DA7, 0x8DA8, 0x8DA9, 0x8DAA, - 0x8DAB, 0x8DAC, 0x8DAD, 0x8DAE, 0x8DAF, 0x8DB0, 0x8DB2, 0x8DB6, - 0x8DB7, 0x8DB9, 0x8DBB, 0x8DBD, 0x8DC0, 0x8DC1, 0x8DC2, 0x8DC5, - 0x8DC7, 0x8DC8, 0x8DC9, 0x8DCA, 0x8DCD, 0x8DD0, 0x8DD2, 0x8DD3, - 0x8DD4, 0x51C7, 0x5196, 0x51A2, 0x51A5, 0x8BA0, 0x8BA6, 0x8BA7, - 0x8BAA, 0x8BB4, 0x8BB5, 0x8BB7, 0x8BC2, 0x8BC3, 0x8BCB, 0x8BCF, - 0x8BCE, 0x8BD2, 0x8BD3, 0x8BD4, 0x8BD6, 0x8BD8, 0x8BD9, 0x8BDC, - 0x8BDF, 0x8BE0, 0x8BE4, 0x8BE8, 0x8BE9, 0x8BEE, 0x8BF0, 0x8BF3, - 0x8BF6, 0x8BF9, 0x8BFC, 0x8BFF, 0x8C00, 0x8C02, 0x8C04, 0x8C07, - 0x8C0C, 0x8C0F, 0x8C11, 0x8C12, 0x8C14, 0x8C15, 0x8C16, 0x8C19, - 0x8C1B, 0x8C18, 0x8C1D, 0x8C1F, 0x8C20, 0x8C21, 0x8C25, 0x8C27, - 0x8C2A, 0x8C2B, 0x8C2E, 0x8C2F, 0x8C32, 0x8C33, 0x8C35, 0x8C36, - 0x5369, 0x537A, 0x961D, 0x9622, 0x9621, 0x9631, 0x962A, 0x963D, - 0x963C, 0x9642, 0x9649, 0x9654, 0x965F, 0x9667, 0x966C, 0x9672, - 0x9674, 0x9688, 0x968D, 0x9697, 0x96B0, 0x9097, 0x909B, 0x909D, - 0x9099, 0x90AC, 0x90A1, 0x90B4, 0x90B3, 0x90B6, 0x90BA - }, - { - 0x8DD5, 0x8DD8, 0x8DD9, 0x8DDC, 0x8DE0, 0x8DE1, 0x8DE2, 0x8DE5, - 0x8DE6, 0x8DE7, 0x8DE9, 0x8DED, 0x8DEE, 0x8DF0, 0x8DF1, 0x8DF2, - 0x8DF4, 0x8DF6, 0x8DFC, 0x8DFE, 0x8DFF, 0x8E00, 0x8E01, 0x8E02, - 0x8E03, 0x8E04, 0x8E06, 0x8E07, 0x8E08, 0x8E0B, 0x8E0D, 0x8E0E, - 0x8E10, 0x8E11, 0x8E12, 0x8E13, 0x8E15, 0x8E16, 0x8E17, 0x8E18, - 0x8E19, 0x8E1A, 0x8E1B, 0x8E1C, 0x8E20, 0x8E21, 0x8E24, 0x8E25, - 0x8E26, 0x8E27, 0x8E28, 0x8E2B, 0x8E2D, 0x8E30, 0x8E32, 0x8E33, - 0x8E34, 0x8E36, 0x8E37, 0x8E38, 0x8E3B, 0x8E3C, 0x8E3E, 0x003F, - 0x8E3F, 0x8E43, 0x8E45, 0x8E46, 0x8E4C, 0x8E4D, 0x8E4E, 0x8E4F, - 0x8E50, 0x8E53, 0x8E54, 0x8E55, 0x8E56, 0x8E57, 0x8E58, 0x8E5A, - 0x8E5B, 0x8E5C, 0x8E5D, 0x8E5E, 0x8E5F, 0x8E60, 0x8E61, 0x8E62, - 0x8E63, 0x8E64, 0x8E65, 0x8E67, 0x8E68, 0x8E6A, 0x8E6B, 0x8E6E, - 0x8E71, 0x90B8, 0x90B0, 0x90CF, 0x90C5, 0x90BE, 0x90D0, 0x90C4, - 0x90C7, 0x90D3, 0x90E6, 0x90E2, 0x90DC, 0x90D7, 0x90DB, 0x90EB, - 0x90EF, 0x90FE, 0x9104, 0x9122, 0x911E, 0x9123, 0x9131, 0x912F, - 0x9139, 0x9143, 0x9146, 0x520D, 0x5942, 0x52A2, 0x52AC, 0x52AD, - 0x52BE, 0x54FF, 0x52D0, 0x52D6, 0x52F0, 0x53DF, 0x71EE, 0x77CD, - 0x5EF4, 0x51F5, 0x51FC, 0x9B2F, 0x53B6, 0x5F01, 0x755A, 0x5DEF, - 0x574C, 0x57A9, 0x57A1, 0x587E, 0x58BC, 0x58C5, 0x58D1, 0x5729, - 0x572C, 0x572A, 0x5733, 0x5739, 0x572E, 0x572F, 0x575C, 0x573B, - 0x5742, 0x5769, 0x5785, 0x576B, 0x5786, 0x577C, 0x577B, 0x5768, - 0x576D, 0x5776, 0x5773, 0x57AD, 0x57A4, 0x578C, 0x57B2, 0x57CF, - 0x57A7, 0x57B4, 0x5793, 0x57A0, 0x57D5, 0x57D8, 0x57DA, 0x57D9, - 0x57D2, 0x57B8, 0x57F4, 0x57EF, 0x57F8, 0x57E4, 0x57DD - }, - { - 0x8E73, 0x8E75, 0x8E77, 0x8E78, 0x8E79, 0x8E7A, 0x8E7B, 0x8E7D, - 0x8E7E, 0x8E80, 0x8E82, 0x8E83, 0x8E84, 0x8E86, 0x8E88, 0x8E89, - 0x8E8A, 0x8E8B, 0x8E8C, 0x8E8D, 0x8E8E, 0x8E91, 0x8E92, 0x8E93, - 0x8E95, 0x8E96, 0x8E97, 0x8E98, 0x8E99, 0x8E9A, 0x8E9B, 0x8E9D, - 0x8E9F, 0x8EA0, 0x8EA1, 0x8EA2, 0x8EA3, 0x8EA4, 0x8EA5, 0x8EA6, - 0x8EA7, 0x8EA8, 0x8EA9, 0x8EAA, 0x8EAD, 0x8EAE, 0x8EB0, 0x8EB1, - 0x8EB3, 0x8EB4, 0x8EB5, 0x8EB6, 0x8EB7, 0x8EB8, 0x8EB9, 0x8EBB, - 0x8EBC, 0x8EBD, 0x8EBE, 0x8EBF, 0x8EC0, 0x8EC1, 0x8EC2, 0x003F, - 0x8EC3, 0x8EC4, 0x8EC5, 0x8EC6, 0x8EC7, 0x8EC8, 0x8EC9, 0x8ECA, - 0x8ECB, 0x8ECC, 0x8ECD, 0x8ECF, 0x8ED0, 0x8ED1, 0x8ED2, 0x8ED3, - 0x8ED4, 0x8ED5, 0x8ED6, 0x8ED7, 0x8ED8, 0x8ED9, 0x8EDA, 0x8EDB, - 0x8EDC, 0x8EDD, 0x8EDE, 0x8EDF, 0x8EE0, 0x8EE1, 0x8EE2, 0x8EE3, - 0x8EE4, 0x580B, 0x580D, 0x57FD, 0x57ED, 0x5800, 0x581E, 0x5819, - 0x5844, 0x5820, 0x5865, 0x586C, 0x5881, 0x5889, 0x589A, 0x5880, - 0x99A8, 0x9F19, 0x61FF, 0x8279, 0x827D, 0x827F, 0x828F, 0x828A, - 0x82A8, 0x8284, 0x828E, 0x8291, 0x8297, 0x8299, 0x82AB, 0x82B8, - 0x82BE, 0x82B0, 0x82C8, 0x82CA, 0x82E3, 0x8298, 0x82B7, 0x82AE, - 0x82CB, 0x82CC, 0x82C1, 0x82A9, 0x82B4, 0x82A1, 0x82AA, 0x829F, - 0x82C4, 0x82CE, 0x82A4, 0x82E1, 0x8309, 0x82F7, 0x82E4, 0x830F, - 0x8307, 0x82DC, 0x82F4, 0x82D2, 0x82D8, 0x830C, 0x82FB, 0x82D3, - 0x8311, 0x831A, 0x8306, 0x8314, 0x8315, 0x82E0, 0x82D5, 0x831C, - 0x8351, 0x835B, 0x835C, 0x8308, 0x8392, 0x833C, 0x8334, 0x8331, - 0x839B, 0x835E, 0x832F, 0x834F, 0x8347, 0x8343, 0x835F, 0x8340, - 0x8317, 0x8360, 0x832D, 0x833A, 0x8333, 0x8366, 0x8365 - }, - { - 0x8EE5, 0x8EE6, 0x8EE7, 0x8EE8, 0x8EE9, 0x8EEA, 0x8EEB, 0x8EEC, - 0x8EED, 0x8EEE, 0x8EEF, 0x8EF0, 0x8EF1, 0x8EF2, 0x8EF3, 0x8EF4, - 0x8EF5, 0x8EF6, 0x8EF7, 0x8EF8, 0x8EF9, 0x8EFA, 0x8EFB, 0x8EFC, - 0x8EFD, 0x8EFE, 0x8EFF, 0x8F00, 0x8F01, 0x8F02, 0x8F03, 0x8F04, - 0x8F05, 0x8F06, 0x8F07, 0x8F08, 0x8F09, 0x8F0A, 0x8F0B, 0x8F0C, - 0x8F0D, 0x8F0E, 0x8F0F, 0x8F10, 0x8F11, 0x8F12, 0x8F13, 0x8F14, - 0x8F15, 0x8F16, 0x8F17, 0x8F18, 0x8F19, 0x8F1A, 0x8F1B, 0x8F1C, - 0x8F1D, 0x8F1E, 0x8F1F, 0x8F20, 0x8F21, 0x8F22, 0x8F23, 0x003F, - 0x8F24, 0x8F25, 0x8F26, 0x8F27, 0x8F28, 0x8F29, 0x8F2A, 0x8F2B, - 0x8F2C, 0x8F2D, 0x8F2E, 0x8F2F, 0x8F30, 0x8F31, 0x8F32, 0x8F33, - 0x8F34, 0x8F35, 0x8F36, 0x8F37, 0x8F38, 0x8F39, 0x8F3A, 0x8F3B, - 0x8F3C, 0x8F3D, 0x8F3E, 0x8F3F, 0x8F40, 0x8F41, 0x8F42, 0x8F43, - 0x8F44, 0x8368, 0x831B, 0x8369, 0x836C, 0x836A, 0x836D, 0x836E, - 0x83B0, 0x8378, 0x83B3, 0x83B4, 0x83A0, 0x83AA, 0x8393, 0x839C, - 0x8385, 0x837C, 0x83B6, 0x83A9, 0x837D, 0x83B8, 0x837B, 0x8398, - 0x839E, 0x83A8, 0x83BA, 0x83BC, 0x83C1, 0x8401, 0x83E5, 0x83D8, - 0x5807, 0x8418, 0x840B, 0x83DD, 0x83FD, 0x83D6, 0x841C, 0x8438, - 0x8411, 0x8406, 0x83D4, 0x83DF, 0x840F, 0x8403, 0x83F8, 0x83F9, - 0x83EA, 0x83C5, 0x83C0, 0x8426, 0x83F0, 0x83E1, 0x845C, 0x8451, - 0x845A, 0x8459, 0x8473, 0x8487, 0x8488, 0x847A, 0x8489, 0x8478, - 0x843C, 0x8446, 0x8469, 0x8476, 0x848C, 0x848E, 0x8431, 0x846D, - 0x84C1, 0x84CD, 0x84D0, 0x84E6, 0x84BD, 0x84D3, 0x84CA, 0x84BF, - 0x84BA, 0x84E0, 0x84A1, 0x84B9, 0x84B4, 0x8497, 0x84E5, 0x84E3, - 0x850C, 0x750D, 0x8538, 0x84F0, 0x8539, 0x851F, 0x853A - }, - { - 0x8F45, 0x8F46, 0x8F47, 0x8F48, 0x8F49, 0x8F4A, 0x8F4B, 0x8F4C, - 0x8F4D, 0x8F4E, 0x8F4F, 0x8F50, 0x8F51, 0x8F52, 0x8F53, 0x8F54, - 0x8F55, 0x8F56, 0x8F57, 0x8F58, 0x8F59, 0x8F5A, 0x8F5B, 0x8F5C, - 0x8F5D, 0x8F5E, 0x8F5F, 0x8F60, 0x8F61, 0x8F62, 0x8F63, 0x8F64, - 0x8F65, 0x8F6A, 0x8F80, 0x8F8C, 0x8F92, 0x8F9D, 0x8FA0, 0x8FA1, - 0x8FA2, 0x8FA4, 0x8FA5, 0x8FA6, 0x8FA7, 0x8FAA, 0x8FAC, 0x8FAD, - 0x8FAE, 0x8FAF, 0x8FB2, 0x8FB3, 0x8FB4, 0x8FB5, 0x8FB7, 0x8FB8, - 0x8FBA, 0x8FBB, 0x8FBC, 0x8FBF, 0x8FC0, 0x8FC3, 0x8FC6, 0x003F, - 0x8FC9, 0x8FCA, 0x8FCB, 0x8FCC, 0x8FCD, 0x8FCF, 0x8FD2, 0x8FD6, - 0x8FD7, 0x8FDA, 0x8FE0, 0x8FE1, 0x8FE3, 0x8FE7, 0x8FEC, 0x8FEF, - 0x8FF1, 0x8FF2, 0x8FF4, 0x8FF5, 0x8FF6, 0x8FFA, 0x8FFB, 0x8FFC, - 0x8FFE, 0x8FFF, 0x9007, 0x9008, 0x900C, 0x900E, 0x9013, 0x9015, - 0x9018, 0x8556, 0x853B, 0x84FF, 0x84FC, 0x8559, 0x8548, 0x8568, - 0x8564, 0x855E, 0x857A, 0x77A2, 0x8543, 0x8572, 0x857B, 0x85A4, - 0x85A8, 0x8587, 0x858F, 0x8579, 0x85AE, 0x859C, 0x8585, 0x85B9, - 0x85B7, 0x85B0, 0x85D3, 0x85C1, 0x85DC, 0x85FF, 0x8627, 0x8605, - 0x8629, 0x8616, 0x863C, 0x5EFE, 0x5F08, 0x593C, 0x5941, 0x8037, - 0x5955, 0x595A, 0x5958, 0x530F, 0x5C22, 0x5C25, 0x5C2C, 0x5C34, - 0x624C, 0x626A, 0x629F, 0x62BB, 0x62CA, 0x62DA, 0x62D7, 0x62EE, - 0x6322, 0x62F6, 0x6339, 0x634B, 0x6343, 0x63AD, 0x63F6, 0x6371, - 0x637A, 0x638E, 0x63B4, 0x636D, 0x63AC, 0x638A, 0x6369, 0x63AE, - 0x63BC, 0x63F2, 0x63F8, 0x63E0, 0x63FF, 0x63C4, 0x63DE, 0x63CE, - 0x6452, 0x63C6, 0x63BE, 0x6445, 0x6441, 0x640B, 0x641B, 0x6420, - 0x640C, 0x6426, 0x6421, 0x645E, 0x6484, 0x646D, 0x6496 - }, - { - 0x9019, 0x901C, 0x9023, 0x9024, 0x9025, 0x9027, 0x9028, 0x9029, - 0x902A, 0x902B, 0x902C, 0x9030, 0x9031, 0x9032, 0x9033, 0x9034, - 0x9037, 0x9039, 0x903A, 0x903D, 0x903F, 0x9040, 0x9043, 0x9045, - 0x9046, 0x9048, 0x9049, 0x904A, 0x904B, 0x904C, 0x904E, 0x9054, - 0x9055, 0x9056, 0x9059, 0x905A, 0x905C, 0x905D, 0x905E, 0x905F, - 0x9060, 0x9061, 0x9064, 0x9066, 0x9067, 0x9069, 0x906A, 0x906B, - 0x906C, 0x906F, 0x9070, 0x9071, 0x9072, 0x9073, 0x9076, 0x9077, - 0x9078, 0x9079, 0x907A, 0x907B, 0x907C, 0x907E, 0x9081, 0x003F, - 0x9084, 0x9085, 0x9086, 0x9087, 0x9089, 0x908A, 0x908C, 0x908D, - 0x908E, 0x908F, 0x9090, 0x9092, 0x9094, 0x9096, 0x9098, 0x909A, - 0x909C, 0x909E, 0x909F, 0x90A0, 0x90A4, 0x90A5, 0x90A7, 0x90A8, - 0x90A9, 0x90AB, 0x90AD, 0x90B2, 0x90B7, 0x90BC, 0x90BD, 0x90BF, - 0x90C0, 0x647A, 0x64B7, 0x64B8, 0x6499, 0x64BA, 0x64C0, 0x64D0, - 0x64D7, 0x64E4, 0x64E2, 0x6509, 0x6525, 0x652E, 0x5F0B, 0x5FD2, - 0x7519, 0x5F11, 0x535F, 0x53F1, 0x53FD, 0x53E9, 0x53E8, 0x53FB, - 0x5412, 0x5416, 0x5406, 0x544B, 0x5452, 0x5453, 0x5454, 0x5456, - 0x5443, 0x5421, 0x5457, 0x5459, 0x5423, 0x5432, 0x5482, 0x5494, - 0x5477, 0x5471, 0x5464, 0x549A, 0x549B, 0x5484, 0x5476, 0x5466, - 0x549D, 0x54D0, 0x54AD, 0x54C2, 0x54B4, 0x54D2, 0x54A7, 0x54A6, - 0x54D3, 0x54D4, 0x5472, 0x54A3, 0x54D5, 0x54BB, 0x54BF, 0x54CC, - 0x54D9, 0x54DA, 0x54DC, 0x54A9, 0x54AA, 0x54A4, 0x54DD, 0x54CF, - 0x54DE, 0x551B, 0x54E7, 0x5520, 0x54FD, 0x5514, 0x54F3, 0x5522, - 0x5523, 0x550F, 0x5511, 0x5527, 0x552A, 0x5567, 0x558F, 0x55B5, - 0x5549, 0x556D, 0x5541, 0x5555, 0x553F, 0x5550, 0x553C - }, - { - 0x90C2, 0x90C3, 0x90C6, 0x90C8, 0x90C9, 0x90CB, 0x90CC, 0x90CD, - 0x90D2, 0x90D4, 0x90D5, 0x90D6, 0x90D8, 0x90D9, 0x90DA, 0x90DE, - 0x90DF, 0x90E0, 0x90E3, 0x90E4, 0x90E5, 0x90E9, 0x90EA, 0x90EC, - 0x90EE, 0x90F0, 0x90F1, 0x90F2, 0x90F3, 0x90F5, 0x90F6, 0x90F7, - 0x90F9, 0x90FA, 0x90FB, 0x90FC, 0x90FF, 0x9100, 0x9101, 0x9103, - 0x9105, 0x9106, 0x9107, 0x9108, 0x9109, 0x910A, 0x910B, 0x910C, - 0x910D, 0x910E, 0x910F, 0x9110, 0x9111, 0x9112, 0x9113, 0x9114, - 0x9115, 0x9116, 0x9117, 0x9118, 0x911A, 0x911B, 0x911C, 0x003F, - 0x911D, 0x911F, 0x9120, 0x9121, 0x9124, 0x9125, 0x9126, 0x9127, - 0x9128, 0x9129, 0x912A, 0x912B, 0x912C, 0x912D, 0x912E, 0x9130, - 0x9132, 0x9133, 0x9134, 0x9135, 0x9136, 0x9137, 0x9138, 0x913A, - 0x913B, 0x913C, 0x913D, 0x913E, 0x913F, 0x9140, 0x9141, 0x9142, - 0x9144, 0x5537, 0x5556, 0x5575, 0x5576, 0x5577, 0x5533, 0x5530, - 0x555C, 0x558B, 0x55D2, 0x5583, 0x55B1, 0x55B9, 0x5588, 0x5581, - 0x559F, 0x557E, 0x55D6, 0x5591, 0x557B, 0x55DF, 0x55BD, 0x55BE, - 0x5594, 0x5599, 0x55EA, 0x55F7, 0x55C9, 0x561F, 0x55D1, 0x55EB, - 0x55EC, 0x55D4, 0x55E6, 0x55DD, 0x55C4, 0x55EF, 0x55E5, 0x55F2, - 0x55F3, 0x55CC, 0x55CD, 0x55E8, 0x55F5, 0x55E4, 0x8F94, 0x561E, - 0x5608, 0x560C, 0x5601, 0x5624, 0x5623, 0x55FE, 0x5600, 0x5627, - 0x562D, 0x5658, 0x5639, 0x5657, 0x562C, 0x564D, 0x5662, 0x5659, - 0x565C, 0x564C, 0x5654, 0x5686, 0x5664, 0x5671, 0x566B, 0x567B, - 0x567C, 0x5685, 0x5693, 0x56AF, 0x56D4, 0x56D7, 0x56DD, 0x56E1, - 0x56F5, 0x56EB, 0x56F9, 0x56FF, 0x5704, 0x570A, 0x5709, 0x571C, - 0x5E0F, 0x5E19, 0x5E14, 0x5E11, 0x5E31, 0x5E3B, 0x5E3C - }, - { - 0x9145, 0x9147, 0x9148, 0x9151, 0x9153, 0x9154, 0x9155, 0x9156, - 0x9158, 0x9159, 0x915B, 0x915C, 0x915F, 0x9160, 0x9166, 0x9167, - 0x9168, 0x916B, 0x916D, 0x9173, 0x917A, 0x917B, 0x917C, 0x9180, - 0x9181, 0x9182, 0x9183, 0x9184, 0x9186, 0x9188, 0x918A, 0x918E, - 0x918F, 0x9193, 0x9194, 0x9195, 0x9196, 0x9197, 0x9198, 0x9199, - 0x919C, 0x919D, 0x919E, 0x919F, 0x91A0, 0x91A1, 0x91A4, 0x91A5, - 0x91A6, 0x91A7, 0x91A8, 0x91A9, 0x91AB, 0x91AC, 0x91B0, 0x91B1, - 0x91B2, 0x91B3, 0x91B6, 0x91B7, 0x91B8, 0x91B9, 0x91BB, 0x003F, - 0x91BC, 0x91BD, 0x91BE, 0x91BF, 0x91C0, 0x91C1, 0x91C2, 0x91C3, - 0x91C4, 0x91C5, 0x91C6, 0x91C8, 0x91CB, 0x91D0, 0x91D2, 0x91D3, - 0x91D4, 0x91D5, 0x91D6, 0x91D7, 0x91D8, 0x91D9, 0x91DA, 0x91DB, - 0x91DD, 0x91DE, 0x91DF, 0x91E0, 0x91E1, 0x91E2, 0x91E3, 0x91E4, - 0x91E5, 0x5E37, 0x5E44, 0x5E54, 0x5E5B, 0x5E5E, 0x5E61, 0x5C8C, - 0x5C7A, 0x5C8D, 0x5C90, 0x5C96, 0x5C88, 0x5C98, 0x5C99, 0x5C91, - 0x5C9A, 0x5C9C, 0x5CB5, 0x5CA2, 0x5CBD, 0x5CAC, 0x5CAB, 0x5CB1, - 0x5CA3, 0x5CC1, 0x5CB7, 0x5CC4, 0x5CD2, 0x5CE4, 0x5CCB, 0x5CE5, - 0x5D02, 0x5D03, 0x5D27, 0x5D26, 0x5D2E, 0x5D24, 0x5D1E, 0x5D06, - 0x5D1B, 0x5D58, 0x5D3E, 0x5D34, 0x5D3D, 0x5D6C, 0x5D5B, 0x5D6F, - 0x5D5D, 0x5D6B, 0x5D4B, 0x5D4A, 0x5D69, 0x5D74, 0x5D82, 0x5D99, - 0x5D9D, 0x8C73, 0x5DB7, 0x5DC5, 0x5F73, 0x5F77, 0x5F82, 0x5F87, - 0x5F89, 0x5F8C, 0x5F95, 0x5F99, 0x5F9C, 0x5FA8, 0x5FAD, 0x5FB5, - 0x5FBC, 0x8862, 0x5F61, 0x72AD, 0x72B0, 0x72B4, 0x72B7, 0x72B8, - 0x72C3, 0x72C1, 0x72CE, 0x72CD, 0x72D2, 0x72E8, 0x72EF, 0x72E9, - 0x72F2, 0x72F4, 0x72F7, 0x7301, 0x72F3, 0x7303, 0x72FA - }, - { - 0x91E6, 0x91E7, 0x91E8, 0x91E9, 0x91EA, 0x91EB, 0x91EC, 0x91ED, - 0x91EE, 0x91EF, 0x91F0, 0x91F1, 0x91F2, 0x91F3, 0x91F4, 0x91F5, - 0x91F6, 0x91F7, 0x91F8, 0x91F9, 0x91FA, 0x91FB, 0x91FC, 0x91FD, - 0x91FE, 0x91FF, 0x9200, 0x9201, 0x9202, 0x9203, 0x9204, 0x9205, - 0x9206, 0x9207, 0x9208, 0x9209, 0x920A, 0x920B, 0x920C, 0x920D, - 0x920E, 0x920F, 0x9210, 0x9211, 0x9212, 0x9213, 0x9214, 0x9215, - 0x9216, 0x9217, 0x9218, 0x9219, 0x921A, 0x921B, 0x921C, 0x921D, - 0x921E, 0x921F, 0x9220, 0x9221, 0x9222, 0x9223, 0x9224, 0x003F, - 0x9225, 0x9226, 0x9227, 0x9228, 0x9229, 0x922A, 0x922B, 0x922C, - 0x922D, 0x922E, 0x922F, 0x9230, 0x9231, 0x9232, 0x9233, 0x9234, - 0x9235, 0x9236, 0x9237, 0x9238, 0x9239, 0x923A, 0x923B, 0x923C, - 0x923D, 0x923E, 0x923F, 0x9240, 0x9241, 0x9242, 0x9243, 0x9244, - 0x9245, 0x72FB, 0x7317, 0x7313, 0x7321, 0x730A, 0x731E, 0x731D, - 0x7315, 0x7322, 0x7339, 0x7325, 0x732C, 0x7338, 0x7331, 0x7350, - 0x734D, 0x7357, 0x7360, 0x736C, 0x736F, 0x737E, 0x821B, 0x5925, - 0x98E7, 0x5924, 0x5902, 0x9963, 0x9967, 0x9968, 0x9969, 0x996A, - 0x996B, 0x996C, 0x9974, 0x9977, 0x997D, 0x9980, 0x9984, 0x9987, - 0x998A, 0x998D, 0x9990, 0x9991, 0x9993, 0x9994, 0x9995, 0x5E80, - 0x5E91, 0x5E8B, 0x5E96, 0x5EA5, 0x5EA0, 0x5EB9, 0x5EB5, 0x5EBE, - 0x5EB3, 0x8D53, 0x5ED2, 0x5ED1, 0x5EDB, 0x5EE8, 0x5EEA, 0x81BA, - 0x5FC4, 0x5FC9, 0x5FD6, 0x5FCF, 0x6003, 0x5FEE, 0x6004, 0x5FE1, - 0x5FE4, 0x5FFE, 0x6005, 0x6006, 0x5FEA, 0x5FED, 0x5FF8, 0x6019, - 0x6035, 0x6026, 0x601B, 0x600F, 0x600D, 0x6029, 0x602B, 0x600A, - 0x603F, 0x6021, 0x6078, 0x6079, 0x607B, 0x607A, 0x6042 - }, - { - 0x9246, 0x9247, 0x9248, 0x9249, 0x924A, 0x924B, 0x924C, 0x924D, - 0x924E, 0x924F, 0x9250, 0x9251, 0x9252, 0x9253, 0x9254, 0x9255, - 0x9256, 0x9257, 0x9258, 0x9259, 0x925A, 0x925B, 0x925C, 0x925D, - 0x925E, 0x925F, 0x9260, 0x9261, 0x9262, 0x9263, 0x9264, 0x9265, - 0x9266, 0x9267, 0x9268, 0x9269, 0x926A, 0x926B, 0x926C, 0x926D, - 0x926E, 0x926F, 0x9270, 0x9271, 0x9272, 0x9273, 0x9275, 0x9276, - 0x9277, 0x9278, 0x9279, 0x927A, 0x927B, 0x927C, 0x927D, 0x927E, - 0x927F, 0x9280, 0x9281, 0x9282, 0x9283, 0x9284, 0x9285, 0x003F, - 0x9286, 0x9287, 0x9288, 0x9289, 0x928A, 0x928B, 0x928C, 0x928D, - 0x928F, 0x9290, 0x9291, 0x9292, 0x9293, 0x9294, 0x9295, 0x9296, - 0x9297, 0x9298, 0x9299, 0x929A, 0x929B, 0x929C, 0x929D, 0x929E, - 0x929F, 0x92A0, 0x92A1, 0x92A2, 0x92A3, 0x92A4, 0x92A5, 0x92A6, - 0x92A7, 0x606A, 0x607D, 0x6096, 0x609A, 0x60AD, 0x609D, 0x6083, - 0x6092, 0x608C, 0x609B, 0x60EC, 0x60BB, 0x60B1, 0x60DD, 0x60D8, - 0x60C6, 0x60DA, 0x60B4, 0x6120, 0x6126, 0x6115, 0x6123, 0x60F4, - 0x6100, 0x610E, 0x612B, 0x614A, 0x6175, 0x61AC, 0x6194, 0x61A7, - 0x61B7, 0x61D4, 0x61F5, 0x5FDD, 0x96B3, 0x95E9, 0x95EB, 0x95F1, - 0x95F3, 0x95F5, 0x95F6, 0x95FC, 0x95FE, 0x9603, 0x9604, 0x9606, - 0x9608, 0x960A, 0x960B, 0x960C, 0x960D, 0x960F, 0x9612, 0x9615, - 0x9616, 0x9617, 0x9619, 0x961A, 0x4E2C, 0x723F, 0x6215, 0x6C35, - 0x6C54, 0x6C5C, 0x6C4A, 0x6CA3, 0x6C85, 0x6C90, 0x6C94, 0x6C8C, - 0x6C68, 0x6C69, 0x6C74, 0x6C76, 0x6C86, 0x6CA9, 0x6CD0, 0x6CD4, - 0x6CAD, 0x6CF7, 0x6CF8, 0x6CF1, 0x6CD7, 0x6CB2, 0x6CE0, 0x6CD6, - 0x6CFA, 0x6CEB, 0x6CEE, 0x6CB1, 0x6CD3, 0x6CEF, 0x6CFE - }, - { - 0x92A8, 0x92A9, 0x92AA, 0x92AB, 0x92AC, 0x92AD, 0x92AF, 0x92B0, - 0x92B1, 0x92B2, 0x92B3, 0x92B4, 0x92B5, 0x92B6, 0x92B7, 0x92B8, - 0x92B9, 0x92BA, 0x92BB, 0x92BC, 0x92BD, 0x92BE, 0x92BF, 0x92C0, - 0x92C1, 0x92C2, 0x92C3, 0x92C4, 0x92C5, 0x92C6, 0x92C7, 0x92C9, - 0x92CA, 0x92CB, 0x92CC, 0x92CD, 0x92CE, 0x92CF, 0x92D0, 0x92D1, - 0x92D2, 0x92D3, 0x92D4, 0x92D5, 0x92D6, 0x92D7, 0x92D8, 0x92D9, - 0x92DA, 0x92DB, 0x92DC, 0x92DD, 0x92DE, 0x92DF, 0x92E0, 0x92E1, - 0x92E2, 0x92E3, 0x92E4, 0x92E5, 0x92E6, 0x92E7, 0x92E8, 0x003F, - 0x92E9, 0x92EA, 0x92EB, 0x92EC, 0x92ED, 0x92EE, 0x92EF, 0x92F0, - 0x92F1, 0x92F2, 0x92F3, 0x92F4, 0x92F5, 0x92F6, 0x92F7, 0x92F8, - 0x92F9, 0x92FA, 0x92FB, 0x92FC, 0x92FD, 0x92FE, 0x92FF, 0x9300, - 0x9301, 0x9302, 0x9303, 0x9304, 0x9305, 0x9306, 0x9307, 0x9308, - 0x9309, 0x6D39, 0x6D27, 0x6D0C, 0x6D43, 0x6D48, 0x6D07, 0x6D04, - 0x6D19, 0x6D0E, 0x6D2B, 0x6D4D, 0x6D2E, 0x6D35, 0x6D1A, 0x6D4F, - 0x6D52, 0x6D54, 0x6D33, 0x6D91, 0x6D6F, 0x6D9E, 0x6DA0, 0x6D5E, - 0x6D93, 0x6D94, 0x6D5C, 0x6D60, 0x6D7C, 0x6D63, 0x6E1A, 0x6DC7, - 0x6DC5, 0x6DDE, 0x6E0E, 0x6DBF, 0x6DE0, 0x6E11, 0x6DE6, 0x6DDD, - 0x6DD9, 0x6E16, 0x6DAB, 0x6E0C, 0x6DAE, 0x6E2B, 0x6E6E, 0x6E4E, - 0x6E6B, 0x6EB2, 0x6E5F, 0x6E86, 0x6E53, 0x6E54, 0x6E32, 0x6E25, - 0x6E44, 0x6EDF, 0x6EB1, 0x6E98, 0x6EE0, 0x6F2D, 0x6EE2, 0x6EA5, - 0x6EA7, 0x6EBD, 0x6EBB, 0x6EB7, 0x6ED7, 0x6EB4, 0x6ECF, 0x6E8F, - 0x6EC2, 0x6E9F, 0x6F62, 0x6F46, 0x6F47, 0x6F24, 0x6F15, 0x6EF9, - 0x6F2F, 0x6F36, 0x6F4B, 0x6F74, 0x6F2A, 0x6F09, 0x6F29, 0x6F89, - 0x6F8D, 0x6F8C, 0x6F78, 0x6F72, 0x6F7C, 0x6F7A, 0x6FD1 - }, - { - 0x930A, 0x930B, 0x930C, 0x930D, 0x930E, 0x930F, 0x9310, 0x9311, - 0x9312, 0x9313, 0x9314, 0x9315, 0x9316, 0x9317, 0x9318, 0x9319, - 0x931A, 0x931B, 0x931C, 0x931D, 0x931E, 0x931F, 0x9320, 0x9321, - 0x9322, 0x9323, 0x9324, 0x9325, 0x9326, 0x9327, 0x9328, 0x9329, - 0x932A, 0x932B, 0x932C, 0x932D, 0x932E, 0x932F, 0x9330, 0x9331, - 0x9332, 0x9333, 0x9334, 0x9335, 0x9336, 0x9337, 0x9338, 0x9339, - 0x933A, 0x933B, 0x933C, 0x933D, 0x933F, 0x9340, 0x9341, 0x9342, - 0x9343, 0x9344, 0x9345, 0x9346, 0x9347, 0x9348, 0x9349, 0x003F, - 0x934A, 0x934B, 0x934C, 0x934D, 0x934E, 0x934F, 0x9350, 0x9351, - 0x9352, 0x9353, 0x9354, 0x9355, 0x9356, 0x9357, 0x9358, 0x9359, - 0x935A, 0x935B, 0x935C, 0x935D, 0x935E, 0x935F, 0x9360, 0x9361, - 0x9362, 0x9363, 0x9364, 0x9365, 0x9366, 0x9367, 0x9368, 0x9369, - 0x936B, 0x6FC9, 0x6FA7, 0x6FB9, 0x6FB6, 0x6FC2, 0x6FE1, 0x6FEE, - 0x6FDE, 0x6FE0, 0x6FEF, 0x701A, 0x7023, 0x701B, 0x7039, 0x7035, - 0x704F, 0x705E, 0x5B80, 0x5B84, 0x5B95, 0x5B93, 0x5BA5, 0x5BB8, - 0x752F, 0x9A9E, 0x6434, 0x5BE4, 0x5BEE, 0x8930, 0x5BF0, 0x8E47, - 0x8B07, 0x8FB6, 0x8FD3, 0x8FD5, 0x8FE5, 0x8FEE, 0x8FE4, 0x8FE9, - 0x8FE6, 0x8FF3, 0x8FE8, 0x9005, 0x9004, 0x900B, 0x9026, 0x9011, - 0x900D, 0x9016, 0x9021, 0x9035, 0x9036, 0x902D, 0x902F, 0x9044, - 0x9051, 0x9052, 0x9050, 0x9068, 0x9058, 0x9062, 0x905B, 0x66B9, - 0x9074, 0x907D, 0x9082, 0x9088, 0x9083, 0x908B, 0x5F50, 0x5F57, - 0x5F56, 0x5F58, 0x5C3B, 0x54AB, 0x5C50, 0x5C59, 0x5B71, 0x5C63, - 0x5C66, 0x7FBC, 0x5F2A, 0x5F29, 0x5F2D, 0x8274, 0x5F3C, 0x9B3B, - 0x5C6E, 0x5981, 0x5983, 0x598D, 0x59A9, 0x59AA, 0x59A3 - }, - { - 0x936C, 0x936D, 0x936E, 0x936F, 0x9370, 0x9371, 0x9372, 0x9373, - 0x9374, 0x9375, 0x9376, 0x9377, 0x9378, 0x9379, 0x937A, 0x937B, - 0x937C, 0x937D, 0x937E, 0x937F, 0x9380, 0x9381, 0x9382, 0x9383, - 0x9384, 0x9385, 0x9386, 0x9387, 0x9388, 0x9389, 0x938A, 0x938B, - 0x938C, 0x938D, 0x938E, 0x9390, 0x9391, 0x9392, 0x9393, 0x9394, - 0x9395, 0x9396, 0x9397, 0x9398, 0x9399, 0x939A, 0x939B, 0x939C, - 0x939D, 0x939E, 0x939F, 0x93A0, 0x93A1, 0x93A2, 0x93A3, 0x93A4, - 0x93A5, 0x93A6, 0x93A7, 0x93A8, 0x93A9, 0x93AA, 0x93AB, 0x003F, - 0x93AC, 0x93AD, 0x93AE, 0x93AF, 0x93B0, 0x93B1, 0x93B2, 0x93B3, - 0x93B4, 0x93B5, 0x93B6, 0x93B7, 0x93B8, 0x93B9, 0x93BA, 0x93BB, - 0x93BC, 0x93BD, 0x93BE, 0x93BF, 0x93C0, 0x93C1, 0x93C2, 0x93C3, - 0x93C4, 0x93C5, 0x93C6, 0x93C7, 0x93C8, 0x93C9, 0x93CB, 0x93CC, - 0x93CD, 0x5997, 0x59CA, 0x59AB, 0x599E, 0x59A4, 0x59D2, 0x59B2, - 0x59AF, 0x59D7, 0x59BE, 0x5A05, 0x5A06, 0x59DD, 0x5A08, 0x59E3, - 0x59D8, 0x59F9, 0x5A0C, 0x5A09, 0x5A32, 0x5A34, 0x5A11, 0x5A23, - 0x5A13, 0x5A40, 0x5A67, 0x5A4A, 0x5A55, 0x5A3C, 0x5A62, 0x5A75, - 0x80EC, 0x5AAA, 0x5A9B, 0x5A77, 0x5A7A, 0x5ABE, 0x5AEB, 0x5AB2, - 0x5AD2, 0x5AD4, 0x5AB8, 0x5AE0, 0x5AE3, 0x5AF1, 0x5AD6, 0x5AE6, - 0x5AD8, 0x5ADC, 0x5B09, 0x5B17, 0x5B16, 0x5B32, 0x5B37, 0x5B40, - 0x5C15, 0x5C1C, 0x5B5A, 0x5B65, 0x5B73, 0x5B51, 0x5B53, 0x5B62, - 0x9A75, 0x9A77, 0x9A78, 0x9A7A, 0x9A7F, 0x9A7D, 0x9A80, 0x9A81, - 0x9A85, 0x9A88, 0x9A8A, 0x9A90, 0x9A92, 0x9A93, 0x9A96, 0x9A98, - 0x9A9B, 0x9A9C, 0x9A9D, 0x9A9F, 0x9AA0, 0x9AA2, 0x9AA3, 0x9AA5, - 0x9AA7, 0x7E9F, 0x7EA1, 0x7EA3, 0x7EA5, 0x7EA8, 0x7EA9 - }, - { - 0x93CE, 0x93CF, 0x93D0, 0x93D1, 0x93D2, 0x93D3, 0x93D4, 0x93D5, - 0x93D7, 0x93D8, 0x93D9, 0x93DA, 0x93DB, 0x93DC, 0x93DD, 0x93DE, - 0x93DF, 0x93E0, 0x93E1, 0x93E2, 0x93E3, 0x93E4, 0x93E5, 0x93E6, - 0x93E7, 0x93E8, 0x93E9, 0x93EA, 0x93EB, 0x93EC, 0x93ED, 0x93EE, - 0x93EF, 0x93F0, 0x93F1, 0x93F2, 0x93F3, 0x93F4, 0x93F5, 0x93F6, - 0x93F7, 0x93F8, 0x93F9, 0x93FA, 0x93FB, 0x93FC, 0x93FD, 0x93FE, - 0x93FF, 0x9400, 0x9401, 0x9402, 0x9403, 0x9404, 0x9405, 0x9406, - 0x9407, 0x9408, 0x9409, 0x940A, 0x940B, 0x940C, 0x940D, 0x003F, - 0x940E, 0x940F, 0x9410, 0x9411, 0x9412, 0x9413, 0x9414, 0x9415, - 0x9416, 0x9417, 0x9418, 0x9419, 0x941A, 0x941B, 0x941C, 0x941D, - 0x941E, 0x941F, 0x9420, 0x9421, 0x9422, 0x9423, 0x9424, 0x9425, - 0x9426, 0x9427, 0x9428, 0x9429, 0x942A, 0x942B, 0x942C, 0x942D, - 0x942E, 0x7EAD, 0x7EB0, 0x7EBE, 0x7EC0, 0x7EC1, 0x7EC2, 0x7EC9, - 0x7ECB, 0x7ECC, 0x7ED0, 0x7ED4, 0x7ED7, 0x7EDB, 0x7EE0, 0x7EE1, - 0x7EE8, 0x7EEB, 0x7EEE, 0x7EEF, 0x7EF1, 0x7EF2, 0x7F0D, 0x7EF6, - 0x7EFA, 0x7EFB, 0x7EFE, 0x7F01, 0x7F02, 0x7F03, 0x7F07, 0x7F08, - 0x7F0B, 0x7F0C, 0x7F0F, 0x7F11, 0x7F12, 0x7F17, 0x7F19, 0x7F1C, - 0x7F1B, 0x7F1F, 0x7F21, 0x7F22, 0x7F23, 0x7F24, 0x7F25, 0x7F26, - 0x7F27, 0x7F2A, 0x7F2B, 0x7F2C, 0x7F2D, 0x7F2F, 0x7F30, 0x7F31, - 0x7F32, 0x7F33, 0x7F35, 0x5E7A, 0x757F, 0x5DDB, 0x753E, 0x9095, - 0x738E, 0x7391, 0x73AE, 0x73A2, 0x739F, 0x73CF, 0x73C2, 0x73D1, - 0x73B7, 0x73B3, 0x73C0, 0x73C9, 0x73C8, 0x73E5, 0x73D9, 0x987C, - 0x740A, 0x73E9, 0x73E7, 0x73DE, 0x73BA, 0x73F2, 0x740F, 0x742A, - 0x745B, 0x7426, 0x7425, 0x7428, 0x7430, 0x742E, 0x742C - }, - { - 0x942F, 0x9430, 0x9431, 0x9432, 0x9433, 0x9434, 0x9435, 0x9436, - 0x9437, 0x9438, 0x9439, 0x943A, 0x943B, 0x943C, 0x943D, 0x943F, - 0x9440, 0x9441, 0x9442, 0x9443, 0x9444, 0x9445, 0x9446, 0x9447, - 0x9448, 0x9449, 0x944A, 0x944B, 0x944C, 0x944D, 0x944E, 0x944F, - 0x9450, 0x9451, 0x9452, 0x9453, 0x9454, 0x9455, 0x9456, 0x9457, - 0x9458, 0x9459, 0x945A, 0x945B, 0x945C, 0x945D, 0x945E, 0x945F, - 0x9460, 0x9461, 0x9462, 0x9463, 0x9464, 0x9465, 0x9466, 0x9467, - 0x9468, 0x9469, 0x946A, 0x946C, 0x946D, 0x946E, 0x946F, 0x003F, - 0x9470, 0x9471, 0x9472, 0x9473, 0x9474, 0x9475, 0x9476, 0x9477, - 0x9478, 0x9479, 0x947A, 0x947B, 0x947C, 0x947D, 0x947E, 0x947F, - 0x9480, 0x9481, 0x9482, 0x9483, 0x9484, 0x9491, 0x9496, 0x9498, - 0x94C7, 0x94CF, 0x94D3, 0x94D4, 0x94DA, 0x94E6, 0x94FB, 0x951C, - 0x9520, 0x741B, 0x741A, 0x7441, 0x745C, 0x7457, 0x7455, 0x7459, - 0x7477, 0x746D, 0x747E, 0x749C, 0x748E, 0x7480, 0x7481, 0x7487, - 0x748B, 0x749E, 0x74A8, 0x74A9, 0x7490, 0x74A7, 0x74D2, 0x74BA, - 0x97EA, 0x97EB, 0x97EC, 0x674C, 0x6753, 0x675E, 0x6748, 0x6769, - 0x67A5, 0x6787, 0x676A, 0x6773, 0x6798, 0x67A7, 0x6775, 0x67A8, - 0x679E, 0x67AD, 0x678B, 0x6777, 0x677C, 0x67F0, 0x6809, 0x67D8, - 0x680A, 0x67E9, 0x67B0, 0x680C, 0x67D9, 0x67B5, 0x67DA, 0x67B3, - 0x67DD, 0x6800, 0x67C3, 0x67B8, 0x67E2, 0x680E, 0x67C1, 0x67FD, - 0x6832, 0x6833, 0x6860, 0x6861, 0x684E, 0x6862, 0x6844, 0x6864, - 0x6883, 0x681D, 0x6855, 0x6866, 0x6841, 0x6867, 0x6840, 0x683E, - 0x684A, 0x6849, 0x6829, 0x68B5, 0x688F, 0x6874, 0x6877, 0x6893, - 0x686B, 0x68C2, 0x696E, 0x68FC, 0x691F, 0x6920, 0x68F9 - }, - { - 0x9527, 0x9533, 0x953D, 0x9543, 0x9548, 0x954B, 0x9555, 0x955A, - 0x9560, 0x956E, 0x9574, 0x9575, 0x9577, 0x9578, 0x9579, 0x957A, - 0x957B, 0x957C, 0x957D, 0x957E, 0x9580, 0x9581, 0x9582, 0x9583, - 0x9584, 0x9585, 0x9586, 0x9587, 0x9588, 0x9589, 0x958A, 0x958B, - 0x958C, 0x958D, 0x958E, 0x958F, 0x9590, 0x9591, 0x9592, 0x9593, - 0x9594, 0x9595, 0x9596, 0x9597, 0x9598, 0x9599, 0x959A, 0x959B, - 0x959C, 0x959D, 0x959E, 0x959F, 0x95A0, 0x95A1, 0x95A2, 0x95A3, - 0x95A4, 0x95A5, 0x95A6, 0x95A7, 0x95A8, 0x95A9, 0x95AA, 0x003F, - 0x95AB, 0x95AC, 0x95AD, 0x95AE, 0x95AF, 0x95B0, 0x95B1, 0x95B2, - 0x95B3, 0x95B4, 0x95B5, 0x95B6, 0x95B7, 0x95B8, 0x95B9, 0x95BA, - 0x95BB, 0x95BC, 0x95BD, 0x95BE, 0x95BF, 0x95C0, 0x95C1, 0x95C2, - 0x95C3, 0x95C4, 0x95C5, 0x95C6, 0x95C7, 0x95C8, 0x95C9, 0x95CA, - 0x95CB, 0x6924, 0x68F0, 0x690B, 0x6901, 0x6957, 0x68E3, 0x6910, - 0x6971, 0x6939, 0x6960, 0x6942, 0x695D, 0x6984, 0x696B, 0x6980, - 0x6998, 0x6978, 0x6934, 0x69CC, 0x6987, 0x6988, 0x69CE, 0x6989, - 0x6966, 0x6963, 0x6979, 0x699B, 0x69A7, 0x69BB, 0x69AB, 0x69AD, - 0x69D4, 0x69B1, 0x69C1, 0x69CA, 0x69DF, 0x6995, 0x69E0, 0x698D, - 0x69FF, 0x6A2F, 0x69ED, 0x6A17, 0x6A18, 0x6A65, 0x69F2, 0x6A44, - 0x6A3E, 0x6AA0, 0x6A50, 0x6A5B, 0x6A35, 0x6A8E, 0x6A79, 0x6A3D, - 0x6A28, 0x6A58, 0x6A7C, 0x6A91, 0x6A90, 0x6AA9, 0x6A97, 0x6AAB, - 0x7337, 0x7352, 0x6B81, 0x6B82, 0x6B87, 0x6B84, 0x6B92, 0x6B93, - 0x6B8D, 0x6B9A, 0x6B9B, 0x6BA1, 0x6BAA, 0x8F6B, 0x8F6D, 0x8F71, - 0x8F72, 0x8F73, 0x8F75, 0x8F76, 0x8F78, 0x8F77, 0x8F79, 0x8F7A, - 0x8F7C, 0x8F7E, 0x8F81, 0x8F82, 0x8F84, 0x8F87, 0x8F8B - }, - { - 0x95CC, 0x95CD, 0x95CE, 0x95CF, 0x95D0, 0x95D1, 0x95D2, 0x95D3, - 0x95D4, 0x95D5, 0x95D6, 0x95D7, 0x95D8, 0x95D9, 0x95DA, 0x95DB, - 0x95DC, 0x95DD, 0x95DE, 0x95DF, 0x95E0, 0x95E1, 0x95E2, 0x95E3, - 0x95E4, 0x95E5, 0x95E6, 0x95E7, 0x95EC, 0x95FF, 0x9607, 0x9613, - 0x9618, 0x961B, 0x961E, 0x9620, 0x9623, 0x9624, 0x9625, 0x9626, - 0x9627, 0x9628, 0x9629, 0x962B, 0x962C, 0x962D, 0x962F, 0x9630, - 0x9637, 0x9638, 0x9639, 0x963A, 0x963E, 0x9641, 0x9643, 0x964A, - 0x964E, 0x964F, 0x9651, 0x9652, 0x9653, 0x9656, 0x9657, 0x003F, - 0x9658, 0x9659, 0x965A, 0x965C, 0x965D, 0x965E, 0x9660, 0x9663, - 0x9665, 0x9666, 0x966B, 0x966D, 0x966E, 0x966F, 0x9670, 0x9671, - 0x9673, 0x9678, 0x9679, 0x967A, 0x967B, 0x967C, 0x967D, 0x967E, - 0x967F, 0x9680, 0x9681, 0x9682, 0x9683, 0x9684, 0x9687, 0x9689, - 0x968A, 0x8F8D, 0x8F8E, 0x8F8F, 0x8F98, 0x8F9A, 0x8ECE, 0x620B, - 0x6217, 0x621B, 0x621F, 0x6222, 0x6221, 0x6225, 0x6224, 0x622C, - 0x81E7, 0x74EF, 0x74F4, 0x74FF, 0x750F, 0x7511, 0x7513, 0x6534, - 0x65EE, 0x65EF, 0x65F0, 0x660A, 0x6619, 0x6772, 0x6603, 0x6615, - 0x6600, 0x7085, 0x66F7, 0x661D, 0x6634, 0x6631, 0x6636, 0x6635, - 0x8006, 0x665F, 0x6654, 0x6641, 0x664F, 0x6656, 0x6661, 0x6657, - 0x6677, 0x6684, 0x668C, 0x66A7, 0x669D, 0x66BE, 0x66DB, 0x66DC, - 0x66E6, 0x66E9, 0x8D32, 0x8D33, 0x8D36, 0x8D3B, 0x8D3D, 0x8D40, - 0x8D45, 0x8D46, 0x8D48, 0x8D49, 0x8D47, 0x8D4D, 0x8D55, 0x8D59, - 0x89C7, 0x89CA, 0x89CB, 0x89CC, 0x89CE, 0x89CF, 0x89D0, 0x89D1, - 0x726E, 0x729F, 0x725D, 0x7266, 0x726F, 0x727E, 0x727F, 0x7284, - 0x728B, 0x728D, 0x728F, 0x7292, 0x6308, 0x6332, 0x63B0 - }, - { - 0x968C, 0x968E, 0x9691, 0x9692, 0x9693, 0x9695, 0x9696, 0x969A, - 0x969B, 0x969D, 0x969E, 0x969F, 0x96A0, 0x96A1, 0x96A2, 0x96A3, - 0x96A4, 0x96A5, 0x96A6, 0x96A8, 0x96A9, 0x96AA, 0x96AB, 0x96AC, - 0x96AD, 0x96AE, 0x96AF, 0x96B1, 0x96B2, 0x96B4, 0x96B5, 0x96B7, - 0x96B8, 0x96BA, 0x96BB, 0x96BF, 0x96C2, 0x96C3, 0x96C8, 0x96CA, - 0x96CB, 0x96D0, 0x96D1, 0x96D3, 0x96D4, 0x96D6, 0x96D7, 0x96D8, - 0x96D9, 0x96DA, 0x96DB, 0x96DC, 0x96DD, 0x96DE, 0x96DF, 0x96E1, - 0x96E2, 0x96E3, 0x96E4, 0x96E5, 0x96E6, 0x96E7, 0x96EB, 0x003F, - 0x96EC, 0x96ED, 0x96EE, 0x96F0, 0x96F1, 0x96F2, 0x96F4, 0x96F5, - 0x96F8, 0x96FA, 0x96FB, 0x96FC, 0x96FD, 0x96FF, 0x9702, 0x9703, - 0x9705, 0x970A, 0x970B, 0x970C, 0x9710, 0x9711, 0x9712, 0x9714, - 0x9715, 0x9717, 0x9718, 0x9719, 0x971A, 0x971B, 0x971D, 0x971F, - 0x9720, 0x643F, 0x64D8, 0x8004, 0x6BEA, 0x6BF3, 0x6BFD, 0x6BF5, - 0x6BF9, 0x6C05, 0x6C07, 0x6C06, 0x6C0D, 0x6C15, 0x6C18, 0x6C19, - 0x6C1A, 0x6C21, 0x6C29, 0x6C24, 0x6C2A, 0x6C32, 0x6535, 0x6555, - 0x656B, 0x724D, 0x7252, 0x7256, 0x7230, 0x8662, 0x5216, 0x809F, - 0x809C, 0x8093, 0x80BC, 0x670A, 0x80BD, 0x80B1, 0x80AB, 0x80AD, - 0x80B4, 0x80B7, 0x80E7, 0x80E8, 0x80E9, 0x80EA, 0x80DB, 0x80C2, - 0x80C4, 0x80D9, 0x80CD, 0x80D7, 0x6710, 0x80DD, 0x80EB, 0x80F1, - 0x80F4, 0x80ED, 0x810D, 0x810E, 0x80F2, 0x80FC, 0x6715, 0x8112, - 0x8C5A, 0x8136, 0x811E, 0x812C, 0x8118, 0x8132, 0x8148, 0x814C, - 0x8153, 0x8174, 0x8159, 0x815A, 0x8171, 0x8160, 0x8169, 0x817C, - 0x817D, 0x816D, 0x8167, 0x584D, 0x5AB5, 0x8188, 0x8182, 0x8191, - 0x6ED5, 0x81A3, 0x81AA, 0x81CC, 0x6726, 0x81CA, 0x81BB - }, - { - 0x9721, 0x9722, 0x9723, 0x9724, 0x9725, 0x9726, 0x9727, 0x9728, - 0x9729, 0x972B, 0x972C, 0x972E, 0x972F, 0x9731, 0x9733, 0x9734, - 0x9735, 0x9736, 0x9737, 0x973A, 0x973B, 0x973C, 0x973D, 0x973F, - 0x9740, 0x9741, 0x9742, 0x9743, 0x9744, 0x9745, 0x9746, 0x9747, - 0x9748, 0x9749, 0x974A, 0x974B, 0x974C, 0x974D, 0x974E, 0x974F, - 0x9750, 0x9751, 0x9754, 0x9755, 0x9757, 0x9758, 0x975A, 0x975C, - 0x975D, 0x975F, 0x9763, 0x9764, 0x9766, 0x9767, 0x9768, 0x976A, - 0x976B, 0x976C, 0x976D, 0x976E, 0x976F, 0x9770, 0x9771, 0x003F, - 0x9772, 0x9775, 0x9777, 0x9778, 0x9779, 0x977A, 0x977B, 0x977D, - 0x977E, 0x977F, 0x9780, 0x9781, 0x9782, 0x9783, 0x9784, 0x9786, - 0x9787, 0x9788, 0x9789, 0x978A, 0x978C, 0x978E, 0x978F, 0x9790, - 0x9793, 0x9795, 0x9796, 0x9797, 0x9799, 0x979A, 0x979B, 0x979C, - 0x979D, 0x81C1, 0x81A6, 0x6B24, 0x6B37, 0x6B39, 0x6B43, 0x6B46, - 0x6B59, 0x98D1, 0x98D2, 0x98D3, 0x98D5, 0x98D9, 0x98DA, 0x6BB3, - 0x5F40, 0x6BC2, 0x89F3, 0x6590, 0x9F51, 0x6593, 0x65BC, 0x65C6, - 0x65C4, 0x65C3, 0x65CC, 0x65CE, 0x65D2, 0x65D6, 0x7080, 0x709C, - 0x7096, 0x709D, 0x70BB, 0x70C0, 0x70B7, 0x70AB, 0x70B1, 0x70E8, - 0x70CA, 0x7110, 0x7113, 0x7116, 0x712F, 0x7131, 0x7173, 0x715C, - 0x7168, 0x7145, 0x7172, 0x714A, 0x7178, 0x717A, 0x7198, 0x71B3, - 0x71B5, 0x71A8, 0x71A0, 0x71E0, 0x71D4, 0x71E7, 0x71F9, 0x721D, - 0x7228, 0x706C, 0x7118, 0x7166, 0x71B9, 0x623E, 0x623D, 0x6243, - 0x6248, 0x6249, 0x793B, 0x7940, 0x7946, 0x7949, 0x795B, 0x795C, - 0x7953, 0x795A, 0x7962, 0x7957, 0x7960, 0x796F, 0x7967, 0x797A, - 0x7985, 0x798A, 0x799A, 0x79A7, 0x79B3, 0x5FD1, 0x5FD0 - }, - { - 0x979E, 0x979F, 0x97A1, 0x97A2, 0x97A4, 0x97A5, 0x97A6, 0x97A7, - 0x97A8, 0x97A9, 0x97AA, 0x97AC, 0x97AE, 0x97B0, 0x97B1, 0x97B3, - 0x97B5, 0x97B6, 0x97B7, 0x97B8, 0x97B9, 0x97BA, 0x97BB, 0x97BC, - 0x97BD, 0x97BE, 0x97BF, 0x97C0, 0x97C1, 0x97C2, 0x97C3, 0x97C4, - 0x97C5, 0x97C6, 0x97C7, 0x97C8, 0x97C9, 0x97CA, 0x97CB, 0x97CC, - 0x97CD, 0x97CE, 0x97CF, 0x97D0, 0x97D1, 0x97D2, 0x97D3, 0x97D4, - 0x97D5, 0x97D6, 0x97D7, 0x97D8, 0x97D9, 0x97DA, 0x97DB, 0x97DC, - 0x97DD, 0x97DE, 0x97DF, 0x97E0, 0x97E1, 0x97E2, 0x97E3, 0x003F, - 0x97E4, 0x97E5, 0x97E8, 0x97EE, 0x97EF, 0x97F0, 0x97F1, 0x97F2, - 0x97F4, 0x97F7, 0x97F8, 0x97F9, 0x97FA, 0x97FB, 0x97FC, 0x97FD, - 0x97FE, 0x97FF, 0x9800, 0x9801, 0x9802, 0x9803, 0x9804, 0x9805, - 0x9806, 0x9807, 0x9808, 0x9809, 0x980A, 0x980B, 0x980C, 0x980D, - 0x980E, 0x603C, 0x605D, 0x605A, 0x6067, 0x6041, 0x6059, 0x6063, - 0x60AB, 0x6106, 0x610D, 0x615D, 0x61A9, 0x619D, 0x61CB, 0x61D1, - 0x6206, 0x8080, 0x807F, 0x6C93, 0x6CF6, 0x6DFC, 0x77F6, 0x77F8, - 0x7800, 0x7809, 0x7817, 0x7818, 0x7811, 0x65AB, 0x782D, 0x781C, - 0x781D, 0x7839, 0x783A, 0x783B, 0x781F, 0x783C, 0x7825, 0x782C, - 0x7823, 0x7829, 0x784E, 0x786D, 0x7856, 0x7857, 0x7826, 0x7850, - 0x7847, 0x784C, 0x786A, 0x789B, 0x7893, 0x789A, 0x7887, 0x789C, - 0x78A1, 0x78A3, 0x78B2, 0x78B9, 0x78A5, 0x78D4, 0x78D9, 0x78C9, - 0x78EC, 0x78F2, 0x7905, 0x78F4, 0x7913, 0x7924, 0x791E, 0x7934, - 0x9F9B, 0x9EF9, 0x9EFB, 0x9EFC, 0x76F1, 0x7704, 0x770D, 0x76F9, - 0x7707, 0x7708, 0x771A, 0x7722, 0x7719, 0x772D, 0x7726, 0x7735, - 0x7738, 0x7750, 0x7751, 0x7747, 0x7743, 0x775A, 0x7768 - }, - { - 0x980F, 0x9810, 0x9811, 0x9812, 0x9813, 0x9814, 0x9815, 0x9816, - 0x9817, 0x9818, 0x9819, 0x981A, 0x981B, 0x981C, 0x981D, 0x981E, - 0x981F, 0x9820, 0x9821, 0x9822, 0x9823, 0x9824, 0x9825, 0x9826, - 0x9827, 0x9828, 0x9829, 0x982A, 0x982B, 0x982C, 0x982D, 0x982E, - 0x982F, 0x9830, 0x9831, 0x9832, 0x9833, 0x9834, 0x9835, 0x9836, - 0x9837, 0x9838, 0x9839, 0x983A, 0x983B, 0x983C, 0x983D, 0x983E, - 0x983F, 0x9840, 0x9841, 0x9842, 0x9843, 0x9844, 0x9845, 0x9846, - 0x9847, 0x9848, 0x9849, 0x984A, 0x984B, 0x984C, 0x984D, 0x003F, - 0x984E, 0x984F, 0x9850, 0x9851, 0x9852, 0x9853, 0x9854, 0x9855, - 0x9856, 0x9857, 0x9858, 0x9859, 0x985A, 0x985B, 0x985C, 0x985D, - 0x985E, 0x985F, 0x9860, 0x9861, 0x9862, 0x9863, 0x9864, 0x9865, - 0x9866, 0x9867, 0x9868, 0x9869, 0x986A, 0x986B, 0x986C, 0x986D, - 0x986E, 0x7762, 0x7765, 0x777F, 0x778D, 0x777D, 0x7780, 0x778C, - 0x7791, 0x779F, 0x77A0, 0x77B0, 0x77B5, 0x77BD, 0x753A, 0x7540, - 0x754E, 0x754B, 0x7548, 0x755B, 0x7572, 0x7579, 0x7583, 0x7F58, - 0x7F61, 0x7F5F, 0x8A48, 0x7F68, 0x7F74, 0x7F71, 0x7F79, 0x7F81, - 0x7F7E, 0x76CD, 0x76E5, 0x8832, 0x9485, 0x9486, 0x9487, 0x948B, - 0x948A, 0x948C, 0x948D, 0x948F, 0x9490, 0x9494, 0x9497, 0x9495, - 0x949A, 0x949B, 0x949C, 0x94A3, 0x94A4, 0x94AB, 0x94AA, 0x94AD, - 0x94AC, 0x94AF, 0x94B0, 0x94B2, 0x94B4, 0x94B6, 0x94B7, 0x94B8, - 0x94B9, 0x94BA, 0x94BC, 0x94BD, 0x94BF, 0x94C4, 0x94C8, 0x94C9, - 0x94CA, 0x94CB, 0x94CC, 0x94CD, 0x94CE, 0x94D0, 0x94D1, 0x94D2, - 0x94D5, 0x94D6, 0x94D7, 0x94D9, 0x94D8, 0x94DB, 0x94DE, 0x94DF, - 0x94E0, 0x94E2, 0x94E4, 0x94E5, 0x94E7, 0x94E8, 0x94EA - }, - { - 0x986F, 0x9870, 0x9871, 0x9872, 0x9873, 0x9874, 0x988B, 0x988E, - 0x9892, 0x9895, 0x9899, 0x98A3, 0x98A8, 0x98A9, 0x98AA, 0x98AB, - 0x98AC, 0x98AD, 0x98AE, 0x98AF, 0x98B0, 0x98B1, 0x98B2, 0x98B3, - 0x98B4, 0x98B5, 0x98B6, 0x98B7, 0x98B8, 0x98B9, 0x98BA, 0x98BB, - 0x98BC, 0x98BD, 0x98BE, 0x98BF, 0x98C0, 0x98C1, 0x98C2, 0x98C3, - 0x98C4, 0x98C5, 0x98C6, 0x98C7, 0x98C8, 0x98C9, 0x98CA, 0x98CB, - 0x98CC, 0x98CD, 0x98CF, 0x98D0, 0x98D4, 0x98D6, 0x98D7, 0x98DB, - 0x98DC, 0x98DD, 0x98E0, 0x98E1, 0x98E2, 0x98E3, 0x98E4, 0x003F, - 0x98E5, 0x98E6, 0x98E9, 0x98EA, 0x98EB, 0x98EC, 0x98ED, 0x98EE, - 0x98EF, 0x98F0, 0x98F1, 0x98F2, 0x98F3, 0x98F4, 0x98F5, 0x98F6, - 0x98F7, 0x98F8, 0x98F9, 0x98FA, 0x98FB, 0x98FC, 0x98FD, 0x98FE, - 0x98FF, 0x9900, 0x9901, 0x9902, 0x9903, 0x9904, 0x9905, 0x9906, - 0x9907, 0x94E9, 0x94EB, 0x94EE, 0x94EF, 0x94F3, 0x94F4, 0x94F5, - 0x94F7, 0x94F9, 0x94FC, 0x94FD, 0x94FF, 0x9503, 0x9502, 0x9506, - 0x9507, 0x9509, 0x950A, 0x950D, 0x950E, 0x950F, 0x9512, 0x9513, - 0x9514, 0x9515, 0x9516, 0x9518, 0x951B, 0x951D, 0x951E, 0x951F, - 0x9522, 0x952A, 0x952B, 0x9529, 0x952C, 0x9531, 0x9532, 0x9534, - 0x9536, 0x9537, 0x9538, 0x953C, 0x953E, 0x953F, 0x9542, 0x9535, - 0x9544, 0x9545, 0x9546, 0x9549, 0x954C, 0x954E, 0x954F, 0x9552, - 0x9553, 0x9554, 0x9556, 0x9557, 0x9558, 0x9559, 0x955B, 0x955E, - 0x955F, 0x955D, 0x9561, 0x9562, 0x9564, 0x9565, 0x9566, 0x9567, - 0x9568, 0x9569, 0x956A, 0x956B, 0x956C, 0x956F, 0x9571, 0x9572, - 0x9573, 0x953A, 0x77E7, 0x77EC, 0x96C9, 0x79D5, 0x79ED, 0x79E3, - 0x79EB, 0x7A06, 0x5D47, 0x7A03, 0x7A02, 0x7A1E, 0x7A14 - }, - { - 0x9908, 0x9909, 0x990A, 0x990B, 0x990C, 0x990E, 0x990F, 0x9911, - 0x9912, 0x9913, 0x9914, 0x9915, 0x9916, 0x9917, 0x9918, 0x9919, - 0x991A, 0x991B, 0x991C, 0x991D, 0x991E, 0x991F, 0x9920, 0x9921, - 0x9922, 0x9923, 0x9924, 0x9925, 0x9926, 0x9927, 0x9928, 0x9929, - 0x992A, 0x992B, 0x992C, 0x992D, 0x992F, 0x9930, 0x9931, 0x9932, - 0x9933, 0x9934, 0x9935, 0x9936, 0x9937, 0x9938, 0x9939, 0x993A, - 0x993B, 0x993C, 0x993D, 0x993E, 0x993F, 0x9940, 0x9941, 0x9942, - 0x9943, 0x9944, 0x9945, 0x9946, 0x9947, 0x9948, 0x9949, 0x003F, - 0x994A, 0x994B, 0x994C, 0x994D, 0x994E, 0x994F, 0x9950, 0x9951, - 0x9952, 0x9953, 0x9956, 0x9957, 0x9958, 0x9959, 0x995A, 0x995B, - 0x995C, 0x995D, 0x995E, 0x995F, 0x9960, 0x9961, 0x9962, 0x9964, - 0x9966, 0x9973, 0x9978, 0x9979, 0x997B, 0x997E, 0x9982, 0x9983, - 0x9989, 0x7A39, 0x7A37, 0x7A51, 0x9ECF, 0x99A5, 0x7A70, 0x7688, - 0x768E, 0x7693, 0x7699, 0x76A4, 0x74DE, 0x74E0, 0x752C, 0x9E20, - 0x9E22, 0x9E28, 0x9E29, 0x9E2A, 0x9E2B, 0x9E2C, 0x9E32, 0x9E31, - 0x9E36, 0x9E38, 0x9E37, 0x9E39, 0x9E3A, 0x9E3E, 0x9E41, 0x9E42, - 0x9E44, 0x9E46, 0x9E47, 0x9E48, 0x9E49, 0x9E4B, 0x9E4C, 0x9E4E, - 0x9E51, 0x9E55, 0x9E57, 0x9E5A, 0x9E5B, 0x9E5C, 0x9E5E, 0x9E63, - 0x9E66, 0x9E67, 0x9E68, 0x9E69, 0x9E6A, 0x9E6B, 0x9E6C, 0x9E71, - 0x9E6D, 0x9E73, 0x7592, 0x7594, 0x7596, 0x75A0, 0x759D, 0x75AC, - 0x75A3, 0x75B3, 0x75B4, 0x75B8, 0x75C4, 0x75B1, 0x75B0, 0x75C3, - 0x75C2, 0x75D6, 0x75CD, 0x75E3, 0x75E8, 0x75E6, 0x75E4, 0x75EB, - 0x75E7, 0x7603, 0x75F1, 0x75FC, 0x75FF, 0x7610, 0x7600, 0x7605, - 0x760C, 0x7617, 0x760A, 0x7625, 0x7618, 0x7615, 0x7619 - }, - { - 0x998C, 0x998E, 0x999A, 0x999B, 0x999C, 0x999D, 0x999E, 0x999F, - 0x99A0, 0x99A1, 0x99A2, 0x99A3, 0x99A4, 0x99A6, 0x99A7, 0x99A9, - 0x99AA, 0x99AB, 0x99AC, 0x99AD, 0x99AE, 0x99AF, 0x99B0, 0x99B1, - 0x99B2, 0x99B3, 0x99B4, 0x99B5, 0x99B6, 0x99B7, 0x99B8, 0x99B9, - 0x99BA, 0x99BB, 0x99BC, 0x99BD, 0x99BE, 0x99BF, 0x99C0, 0x99C1, - 0x99C2, 0x99C3, 0x99C4, 0x99C5, 0x99C6, 0x99C7, 0x99C8, 0x99C9, - 0x99CA, 0x99CB, 0x99CC, 0x99CD, 0x99CE, 0x99CF, 0x99D0, 0x99D1, - 0x99D2, 0x99D3, 0x99D4, 0x99D5, 0x99D6, 0x99D7, 0x99D8, 0x003F, - 0x99D9, 0x99DA, 0x99DB, 0x99DC, 0x99DD, 0x99DE, 0x99DF, 0x99E0, - 0x99E1, 0x99E2, 0x99E3, 0x99E4, 0x99E5, 0x99E6, 0x99E7, 0x99E8, - 0x99E9, 0x99EA, 0x99EB, 0x99EC, 0x99ED, 0x99EE, 0x99EF, 0x99F0, - 0x99F1, 0x99F2, 0x99F3, 0x99F4, 0x99F5, 0x99F6, 0x99F7, 0x99F8, - 0x99F9, 0x761B, 0x763C, 0x7622, 0x7620, 0x7640, 0x762D, 0x7630, - 0x763F, 0x7635, 0x7643, 0x763E, 0x7633, 0x764D, 0x765E, 0x7654, - 0x765C, 0x7656, 0x766B, 0x766F, 0x7FCA, 0x7AE6, 0x7A78, 0x7A79, - 0x7A80, 0x7A86, 0x7A88, 0x7A95, 0x7AA6, 0x7AA0, 0x7AAC, 0x7AA8, - 0x7AAD, 0x7AB3, 0x8864, 0x8869, 0x8872, 0x887D, 0x887F, 0x8882, - 0x88A2, 0x88C6, 0x88B7, 0x88BC, 0x88C9, 0x88E2, 0x88CE, 0x88E3, - 0x88E5, 0x88F1, 0x891A, 0x88FC, 0x88E8, 0x88FE, 0x88F0, 0x8921, - 0x8919, 0x8913, 0x891B, 0x890A, 0x8934, 0x892B, 0x8936, 0x8941, - 0x8966, 0x897B, 0x758B, 0x80E5, 0x76B2, 0x76B4, 0x77DC, 0x8012, - 0x8014, 0x8016, 0x801C, 0x8020, 0x8022, 0x8025, 0x8026, 0x8027, - 0x8029, 0x8028, 0x8031, 0x800B, 0x8035, 0x8043, 0x8046, 0x804D, - 0x8052, 0x8069, 0x8071, 0x8983, 0x9878, 0x9880, 0x9883 - }, - { - 0x99FA, 0x99FB, 0x99FC, 0x99FD, 0x99FE, 0x99FF, 0x9A00, 0x9A01, - 0x9A02, 0x9A03, 0x9A04, 0x9A05, 0x9A06, 0x9A07, 0x9A08, 0x9A09, - 0x9A0A, 0x9A0B, 0x9A0C, 0x9A0D, 0x9A0E, 0x9A0F, 0x9A10, 0x9A11, - 0x9A12, 0x9A13, 0x9A14, 0x9A15, 0x9A16, 0x9A17, 0x9A18, 0x9A19, - 0x9A1A, 0x9A1B, 0x9A1C, 0x9A1D, 0x9A1E, 0x9A1F, 0x9A20, 0x9A21, - 0x9A22, 0x9A23, 0x9A24, 0x9A25, 0x9A26, 0x9A27, 0x9A28, 0x9A29, - 0x9A2A, 0x9A2B, 0x9A2C, 0x9A2D, 0x9A2E, 0x9A2F, 0x9A30, 0x9A31, - 0x9A32, 0x9A33, 0x9A34, 0x9A35, 0x9A36, 0x9A37, 0x9A38, 0x003F, - 0x9A39, 0x9A3A, 0x9A3B, 0x9A3C, 0x9A3D, 0x9A3E, 0x9A3F, 0x9A40, - 0x9A41, 0x9A42, 0x9A43, 0x9A44, 0x9A45, 0x9A46, 0x9A47, 0x9A48, - 0x9A49, 0x9A4A, 0x9A4B, 0x9A4C, 0x9A4D, 0x9A4E, 0x9A4F, 0x9A50, - 0x9A51, 0x9A52, 0x9A53, 0x9A54, 0x9A55, 0x9A56, 0x9A57, 0x9A58, - 0x9A59, 0x9889, 0x988C, 0x988D, 0x988F, 0x9894, 0x989A, 0x989B, - 0x989E, 0x989F, 0x98A1, 0x98A2, 0x98A5, 0x98A6, 0x864D, 0x8654, - 0x866C, 0x866E, 0x867F, 0x867A, 0x867C, 0x867B, 0x86A8, 0x868D, - 0x868B, 0x86AC, 0x869D, 0x86A7, 0x86A3, 0x86AA, 0x8693, 0x86A9, - 0x86B6, 0x86C4, 0x86B5, 0x86CE, 0x86B0, 0x86BA, 0x86B1, 0x86AF, - 0x86C9, 0x86CF, 0x86B4, 0x86E9, 0x86F1, 0x86F2, 0x86ED, 0x86F3, - 0x86D0, 0x8713, 0x86DE, 0x86F4, 0x86DF, 0x86D8, 0x86D1, 0x8703, - 0x8707, 0x86F8, 0x8708, 0x870A, 0x870D, 0x8709, 0x8723, 0x873B, - 0x871E, 0x8725, 0x872E, 0x871A, 0x873E, 0x8748, 0x8734, 0x8731, - 0x8729, 0x8737, 0x873F, 0x8782, 0x8722, 0x877D, 0x877E, 0x877B, - 0x8760, 0x8770, 0x874C, 0x876E, 0x878B, 0x8753, 0x8763, 0x877C, - 0x8764, 0x8759, 0x8765, 0x8793, 0x87AF, 0x87A8, 0x87D2 - }, - { - 0x9A5A, 0x9A5B, 0x9A5C, 0x9A5D, 0x9A5E, 0x9A5F, 0x9A60, 0x9A61, - 0x9A62, 0x9A63, 0x9A64, 0x9A65, 0x9A66, 0x9A67, 0x9A68, 0x9A69, - 0x9A6A, 0x9A6B, 0x9A72, 0x9A83, 0x9A89, 0x9A8D, 0x9A8E, 0x9A94, - 0x9A95, 0x9A99, 0x9AA6, 0x9AA9, 0x9AAA, 0x9AAB, 0x9AAC, 0x9AAD, - 0x9AAE, 0x9AAF, 0x9AB2, 0x9AB3, 0x9AB4, 0x9AB5, 0x9AB9, 0x9ABB, - 0x9ABD, 0x9ABE, 0x9ABF, 0x9AC3, 0x9AC4, 0x9AC6, 0x9AC7, 0x9AC8, - 0x9AC9, 0x9ACA, 0x9ACD, 0x9ACE, 0x9ACF, 0x9AD0, 0x9AD2, 0x9AD4, - 0x9AD5, 0x9AD6, 0x9AD7, 0x9AD9, 0x9ADA, 0x9ADB, 0x9ADC, 0x003F, - 0x9ADD, 0x9ADE, 0x9AE0, 0x9AE2, 0x9AE3, 0x9AE4, 0x9AE5, 0x9AE7, - 0x9AE8, 0x9AE9, 0x9AEA, 0x9AEC, 0x9AEE, 0x9AF0, 0x9AF1, 0x9AF2, - 0x9AF3, 0x9AF4, 0x9AF5, 0x9AF6, 0x9AF7, 0x9AF8, 0x9AFA, 0x9AFC, - 0x9AFD, 0x9AFE, 0x9AFF, 0x9B00, 0x9B01, 0x9B02, 0x9B04, 0x9B05, - 0x9B06, 0x87C6, 0x8788, 0x8785, 0x87AD, 0x8797, 0x8783, 0x87AB, - 0x87E5, 0x87AC, 0x87B5, 0x87B3, 0x87CB, 0x87D3, 0x87BD, 0x87D1, - 0x87C0, 0x87CA, 0x87DB, 0x87EA, 0x87E0, 0x87EE, 0x8816, 0x8813, - 0x87FE, 0x880A, 0x881B, 0x8821, 0x8839, 0x883C, 0x7F36, 0x7F42, - 0x7F44, 0x7F45, 0x8210, 0x7AFA, 0x7AFD, 0x7B08, 0x7B03, 0x7B04, - 0x7B15, 0x7B0A, 0x7B2B, 0x7B0F, 0x7B47, 0x7B38, 0x7B2A, 0x7B19, - 0x7B2E, 0x7B31, 0x7B20, 0x7B25, 0x7B24, 0x7B33, 0x7B3E, 0x7B1E, - 0x7B58, 0x7B5A, 0x7B45, 0x7B75, 0x7B4C, 0x7B5D, 0x7B60, 0x7B6E, - 0x7B7B, 0x7B62, 0x7B72, 0x7B71, 0x7B90, 0x7BA6, 0x7BA7, 0x7BB8, - 0x7BAC, 0x7B9D, 0x7BA8, 0x7B85, 0x7BAA, 0x7B9C, 0x7BA2, 0x7BAB, - 0x7BB4, 0x7BD1, 0x7BC1, 0x7BCC, 0x7BDD, 0x7BDA, 0x7BE5, 0x7BE6, - 0x7BEA, 0x7C0C, 0x7BFE, 0x7BFC, 0x7C0F, 0x7C16, 0x7C0B - }, - { - 0x9B07, 0x9B09, 0x9B0A, 0x9B0B, 0x9B0C, 0x9B0D, 0x9B0E, 0x9B10, - 0x9B11, 0x9B12, 0x9B14, 0x9B15, 0x9B16, 0x9B17, 0x9B18, 0x9B19, - 0x9B1A, 0x9B1B, 0x9B1C, 0x9B1D, 0x9B1E, 0x9B20, 0x9B21, 0x9B22, - 0x9B24, 0x9B25, 0x9B26, 0x9B27, 0x9B28, 0x9B29, 0x9B2A, 0x9B2B, - 0x9B2C, 0x9B2D, 0x9B2E, 0x9B30, 0x9B31, 0x9B33, 0x9B34, 0x9B35, - 0x9B36, 0x9B37, 0x9B38, 0x9B39, 0x9B3A, 0x9B3D, 0x9B3E, 0x9B3F, - 0x9B40, 0x9B46, 0x9B4A, 0x9B4B, 0x9B4C, 0x9B4E, 0x9B50, 0x9B52, - 0x9B53, 0x9B55, 0x9B56, 0x9B57, 0x9B58, 0x9B59, 0x9B5A, 0x003F, - 0x9B5B, 0x9B5C, 0x9B5D, 0x9B5E, 0x9B5F, 0x9B60, 0x9B61, 0x9B62, - 0x9B63, 0x9B64, 0x9B65, 0x9B66, 0x9B67, 0x9B68, 0x9B69, 0x9B6A, - 0x9B6B, 0x9B6C, 0x9B6D, 0x9B6E, 0x9B6F, 0x9B70, 0x9B71, 0x9B72, - 0x9B73, 0x9B74, 0x9B75, 0x9B76, 0x9B77, 0x9B78, 0x9B79, 0x9B7A, - 0x9B7B, 0x7C1F, 0x7C2A, 0x7C26, 0x7C38, 0x7C41, 0x7C40, 0x81FE, - 0x8201, 0x8202, 0x8204, 0x81EC, 0x8844, 0x8221, 0x8222, 0x8223, - 0x822D, 0x822F, 0x8228, 0x822B, 0x8238, 0x823B, 0x8233, 0x8234, - 0x823E, 0x8244, 0x8249, 0x824B, 0x824F, 0x825A, 0x825F, 0x8268, - 0x887E, 0x8885, 0x8888, 0x88D8, 0x88DF, 0x895E, 0x7F9D, 0x7F9F, - 0x7FA7, 0x7FAF, 0x7FB0, 0x7FB2, 0x7C7C, 0x6549, 0x7C91, 0x7C9D, - 0x7C9C, 0x7C9E, 0x7CA2, 0x7CB2, 0x7CBC, 0x7CBD, 0x7CC1, 0x7CC7, - 0x7CCC, 0x7CCD, 0x7CC8, 0x7CC5, 0x7CD7, 0x7CE8, 0x826E, 0x66A8, - 0x7FBF, 0x7FCE, 0x7FD5, 0x7FE5, 0x7FE1, 0x7FE6, 0x7FE9, 0x7FEE, - 0x7FF3, 0x7CF8, 0x7D77, 0x7DA6, 0x7DAE, 0x7E47, 0x7E9B, 0x9EB8, - 0x9EB4, 0x8D73, 0x8D84, 0x8D94, 0x8D91, 0x8DB1, 0x8D67, 0x8D6D, - 0x8C47, 0x8C49, 0x914A, 0x9150, 0x914E, 0x914F, 0x9164 - }, - { - 0x9B7C, 0x9B7D, 0x9B7E, 0x9B7F, 0x9B80, 0x9B81, 0x9B82, 0x9B83, - 0x9B84, 0x9B85, 0x9B86, 0x9B87, 0x9B88, 0x9B89, 0x9B8A, 0x9B8B, - 0x9B8C, 0x9B8D, 0x9B8E, 0x9B8F, 0x9B90, 0x9B91, 0x9B92, 0x9B93, - 0x9B94, 0x9B95, 0x9B96, 0x9B97, 0x9B98, 0x9B99, 0x9B9A, 0x9B9B, - 0x9B9C, 0x9B9D, 0x9B9E, 0x9B9F, 0x9BA0, 0x9BA1, 0x9BA2, 0x9BA3, - 0x9BA4, 0x9BA5, 0x9BA6, 0x9BA7, 0x9BA8, 0x9BA9, 0x9BAA, 0x9BAB, - 0x9BAC, 0x9BAD, 0x9BAE, 0x9BAF, 0x9BB0, 0x9BB1, 0x9BB2, 0x9BB3, - 0x9BB4, 0x9BB5, 0x9BB6, 0x9BB7, 0x9BB8, 0x9BB9, 0x9BBA, 0x003F, - 0x9BBB, 0x9BBC, 0x9BBD, 0x9BBE, 0x9BBF, 0x9BC0, 0x9BC1, 0x9BC2, - 0x9BC3, 0x9BC4, 0x9BC5, 0x9BC6, 0x9BC7, 0x9BC8, 0x9BC9, 0x9BCA, - 0x9BCB, 0x9BCC, 0x9BCD, 0x9BCE, 0x9BCF, 0x9BD0, 0x9BD1, 0x9BD2, - 0x9BD3, 0x9BD4, 0x9BD5, 0x9BD6, 0x9BD7, 0x9BD8, 0x9BD9, 0x9BDA, - 0x9BDB, 0x9162, 0x9161, 0x9170, 0x9169, 0x916F, 0x917D, 0x917E, - 0x9172, 0x9174, 0x9179, 0x918C, 0x9185, 0x9190, 0x918D, 0x9191, - 0x91A2, 0x91A3, 0x91AA, 0x91AD, 0x91AE, 0x91AF, 0x91B5, 0x91B4, - 0x91BA, 0x8C55, 0x9E7E, 0x8DB8, 0x8DEB, 0x8E05, 0x8E59, 0x8E69, - 0x8DB5, 0x8DBF, 0x8DBC, 0x8DBA, 0x8DC4, 0x8DD6, 0x8DD7, 0x8DDA, - 0x8DDE, 0x8DCE, 0x8DCF, 0x8DDB, 0x8DC6, 0x8DEC, 0x8DF7, 0x8DF8, - 0x8DE3, 0x8DF9, 0x8DFB, 0x8DE4, 0x8E09, 0x8DFD, 0x8E14, 0x8E1D, - 0x8E1F, 0x8E2C, 0x8E2E, 0x8E23, 0x8E2F, 0x8E3A, 0x8E40, 0x8E39, - 0x8E35, 0x8E3D, 0x8E31, 0x8E49, 0x8E41, 0x8E42, 0x8E51, 0x8E52, - 0x8E4A, 0x8E70, 0x8E76, 0x8E7C, 0x8E6F, 0x8E74, 0x8E85, 0x8E8F, - 0x8E94, 0x8E90, 0x8E9C, 0x8E9E, 0x8C78, 0x8C82, 0x8C8A, 0x8C85, - 0x8C98, 0x8C94, 0x659B, 0x89D6, 0x89DE, 0x89DA, 0x89DC - }, - { - 0x9BDC, 0x9BDD, 0x9BDE, 0x9BDF, 0x9BE0, 0x9BE1, 0x9BE2, 0x9BE3, - 0x9BE4, 0x9BE5, 0x9BE6, 0x9BE7, 0x9BE8, 0x9BE9, 0x9BEA, 0x9BEB, - 0x9BEC, 0x9BED, 0x9BEE, 0x9BEF, 0x9BF0, 0x9BF1, 0x9BF2, 0x9BF3, - 0x9BF4, 0x9BF5, 0x9BF6, 0x9BF7, 0x9BF8, 0x9BF9, 0x9BFA, 0x9BFB, - 0x9BFC, 0x9BFD, 0x9BFE, 0x9BFF, 0x9C00, 0x9C01, 0x9C02, 0x9C03, - 0x9C04, 0x9C05, 0x9C06, 0x9C07, 0x9C08, 0x9C09, 0x9C0A, 0x9C0B, - 0x9C0C, 0x9C0D, 0x9C0E, 0x9C0F, 0x9C10, 0x9C11, 0x9C12, 0x9C13, - 0x9C14, 0x9C15, 0x9C16, 0x9C17, 0x9C18, 0x9C19, 0x9C1A, 0x003F, - 0x9C1B, 0x9C1C, 0x9C1D, 0x9C1E, 0x9C1F, 0x9C20, 0x9C21, 0x9C22, - 0x9C23, 0x9C24, 0x9C25, 0x9C26, 0x9C27, 0x9C28, 0x9C29, 0x9C2A, - 0x9C2B, 0x9C2C, 0x9C2D, 0x9C2E, 0x9C2F, 0x9C30, 0x9C31, 0x9C32, - 0x9C33, 0x9C34, 0x9C35, 0x9C36, 0x9C37, 0x9C38, 0x9C39, 0x9C3A, - 0x9C3B, 0x89E5, 0x89EB, 0x89EF, 0x8A3E, 0x8B26, 0x9753, 0x96E9, - 0x96F3, 0x96EF, 0x9706, 0x9701, 0x9708, 0x970F, 0x970E, 0x972A, - 0x972D, 0x9730, 0x973E, 0x9F80, 0x9F83, 0x9F85, 0x9F86, 0x9F87, - 0x9F88, 0x9F89, 0x9F8A, 0x9F8C, 0x9EFE, 0x9F0B, 0x9F0D, 0x96B9, - 0x96BC, 0x96BD, 0x96CE, 0x96D2, 0x77BF, 0x96E0, 0x928E, 0x92AE, - 0x92C8, 0x933E, 0x936A, 0x93CA, 0x938F, 0x943E, 0x946B, 0x9C7F, - 0x9C82, 0x9C85, 0x9C86, 0x9C87, 0x9C88, 0x7A23, 0x9C8B, 0x9C8E, - 0x9C90, 0x9C91, 0x9C92, 0x9C94, 0x9C95, 0x9C9A, 0x9C9B, 0x9C9E, - 0x9C9F, 0x9CA0, 0x9CA1, 0x9CA2, 0x9CA3, 0x9CA5, 0x9CA6, 0x9CA7, - 0x9CA8, 0x9CA9, 0x9CAB, 0x9CAD, 0x9CAE, 0x9CB0, 0x9CB1, 0x9CB2, - 0x9CB3, 0x9CB4, 0x9CB5, 0x9CB6, 0x9CB7, 0x9CBA, 0x9CBB, 0x9CBC, - 0x9CBD, 0x9CC4, 0x9CC5, 0x9CC6, 0x9CC7, 0x9CCA, 0x9CCB - }, - { - 0x9C3C, 0x9C3D, 0x9C3E, 0x9C3F, 0x9C40, 0x9C41, 0x9C42, 0x9C43, - 0x9C44, 0x9C45, 0x9C46, 0x9C47, 0x9C48, 0x9C49, 0x9C4A, 0x9C4B, - 0x9C4C, 0x9C4D, 0x9C4E, 0x9C4F, 0x9C50, 0x9C51, 0x9C52, 0x9C53, - 0x9C54, 0x9C55, 0x9C56, 0x9C57, 0x9C58, 0x9C59, 0x9C5A, 0x9C5B, - 0x9C5C, 0x9C5D, 0x9C5E, 0x9C5F, 0x9C60, 0x9C61, 0x9C62, 0x9C63, - 0x9C64, 0x9C65, 0x9C66, 0x9C67, 0x9C68, 0x9C69, 0x9C6A, 0x9C6B, - 0x9C6C, 0x9C6D, 0x9C6E, 0x9C6F, 0x9C70, 0x9C71, 0x9C72, 0x9C73, - 0x9C74, 0x9C75, 0x9C76, 0x9C77, 0x9C78, 0x9C79, 0x9C7A, 0x003F, - 0x9C7B, 0x9C7D, 0x9C7E, 0x9C80, 0x9C83, 0x9C84, 0x9C89, 0x9C8A, - 0x9C8C, 0x9C8F, 0x9C93, 0x9C96, 0x9C97, 0x9C98, 0x9C99, 0x9C9D, - 0x9CAA, 0x9CAC, 0x9CAF, 0x9CB9, 0x9CBE, 0x9CBF, 0x9CC0, 0x9CC1, - 0x9CC2, 0x9CC8, 0x9CC9, 0x9CD1, 0x9CD2, 0x9CDA, 0x9CDB, 0x9CE0, - 0x9CE1, 0x9CCC, 0x9CCD, 0x9CCE, 0x9CCF, 0x9CD0, 0x9CD3, 0x9CD4, - 0x9CD5, 0x9CD7, 0x9CD8, 0x9CD9, 0x9CDC, 0x9CDD, 0x9CDF, 0x9CE2, - 0x977C, 0x9785, 0x9791, 0x9792, 0x9794, 0x97AF, 0x97AB, 0x97A3, - 0x97B2, 0x97B4, 0x9AB1, 0x9AB0, 0x9AB7, 0x9E58, 0x9AB6, 0x9ABA, - 0x9ABC, 0x9AC1, 0x9AC0, 0x9AC5, 0x9AC2, 0x9ACB, 0x9ACC, 0x9AD1, - 0x9B45, 0x9B43, 0x9B47, 0x9B49, 0x9B48, 0x9B4D, 0x9B51, 0x98E8, - 0x990D, 0x992E, 0x9955, 0x9954, 0x9ADF, 0x9AE1, 0x9AE6, 0x9AEF, - 0x9AEB, 0x9AFB, 0x9AED, 0x9AF9, 0x9B08, 0x9B0F, 0x9B13, 0x9B1F, - 0x9B23, 0x9EBD, 0x9EBE, 0x7E3B, 0x9E82, 0x9E87, 0x9E88, 0x9E8B, - 0x9E92, 0x93D6, 0x9E9D, 0x9E9F, 0x9EDB, 0x9EDC, 0x9EDD, 0x9EE0, - 0x9EDF, 0x9EE2, 0x9EE9, 0x9EE7, 0x9EE5, 0x9EEA, 0x9EEF, 0x9F22, - 0x9F2C, 0x9F2F, 0x9F39, 0x9F37, 0x9F3D, 0x9F3E, 0x9F44 - }, - { - 0x9CE3, 0x9CE4, 0x9CE5, 0x9CE6, 0x9CE7, 0x9CE8, 0x9CE9, 0x9CEA, - 0x9CEB, 0x9CEC, 0x9CED, 0x9CEE, 0x9CEF, 0x9CF0, 0x9CF1, 0x9CF2, - 0x9CF3, 0x9CF4, 0x9CF5, 0x9CF6, 0x9CF7, 0x9CF8, 0x9CF9, 0x9CFA, - 0x9CFB, 0x9CFC, 0x9CFD, 0x9CFE, 0x9CFF, 0x9D00, 0x9D01, 0x9D02, - 0x9D03, 0x9D04, 0x9D05, 0x9D06, 0x9D07, 0x9D08, 0x9D09, 0x9D0A, - 0x9D0B, 0x9D0C, 0x9D0D, 0x9D0E, 0x9D0F, 0x9D10, 0x9D11, 0x9D12, - 0x9D13, 0x9D14, 0x9D15, 0x9D16, 0x9D17, 0x9D18, 0x9D19, 0x9D1A, - 0x9D1B, 0x9D1C, 0x9D1D, 0x9D1E, 0x9D1F, 0x9D20, 0x9D21, 0x003F, - 0x9D22, 0x9D23, 0x9D24, 0x9D25, 0x9D26, 0x9D27, 0x9D28, 0x9D29, - 0x9D2A, 0x9D2B, 0x9D2C, 0x9D2D, 0x9D2E, 0x9D2F, 0x9D30, 0x9D31, - 0x9D32, 0x9D33, 0x9D34, 0x9D35, 0x9D36, 0x9D37, 0x9D38, 0x9D39, - 0x9D3A, 0x9D3B, 0x9D3C, 0x9D3D, 0x9D3E, 0x9D3F, 0x9D40, 0x9D41, - 0x9D42, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F - }, - { - 0x9D43, 0x9D44, 0x9D45, 0x9D46, 0x9D47, 0x9D48, 0x9D49, 0x9D4A, - 0x9D4B, 0x9D4C, 0x9D4D, 0x9D4E, 0x9D4F, 0x9D50, 0x9D51, 0x9D52, - 0x9D53, 0x9D54, 0x9D55, 0x9D56, 0x9D57, 0x9D58, 0x9D59, 0x9D5A, - 0x9D5B, 0x9D5C, 0x9D5D, 0x9D5E, 0x9D5F, 0x9D60, 0x9D61, 0x9D62, - 0x9D63, 0x9D64, 0x9D65, 0x9D66, 0x9D67, 0x9D68, 0x9D69, 0x9D6A, - 0x9D6B, 0x9D6C, 0x9D6D, 0x9D6E, 0x9D6F, 0x9D70, 0x9D71, 0x9D72, - 0x9D73, 0x9D74, 0x9D75, 0x9D76, 0x9D77, 0x9D78, 0x9D79, 0x9D7A, - 0x9D7B, 0x9D7C, 0x9D7D, 0x9D7E, 0x9D7F, 0x9D80, 0x9D81, 0x003F, - 0x9D82, 0x9D83, 0x9D84, 0x9D85, 0x9D86, 0x9D87, 0x9D88, 0x9D89, - 0x9D8A, 0x9D8B, 0x9D8C, 0x9D8D, 0x9D8E, 0x9D8F, 0x9D90, 0x9D91, - 0x9D92, 0x9D93, 0x9D94, 0x9D95, 0x9D96, 0x9D97, 0x9D98, 0x9D99, - 0x9D9A, 0x9D9B, 0x9D9C, 0x9D9D, 0x9D9E, 0x9D9F, 0x9DA0, 0x9DA1, - 0x9DA2, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F - }, - { - 0x9DA3, 0x9DA4, 0x9DA5, 0x9DA6, 0x9DA7, 0x9DA8, 0x9DA9, 0x9DAA, - 0x9DAB, 0x9DAC, 0x9DAD, 0x9DAE, 0x9DAF, 0x9DB0, 0x9DB1, 0x9DB2, - 0x9DB3, 0x9DB4, 0x9DB5, 0x9DB6, 0x9DB7, 0x9DB8, 0x9DB9, 0x9DBA, - 0x9DBB, 0x9DBC, 0x9DBD, 0x9DBE, 0x9DBF, 0x9DC0, 0x9DC1, 0x9DC2, - 0x9DC3, 0x9DC4, 0x9DC5, 0x9DC6, 0x9DC7, 0x9DC8, 0x9DC9, 0x9DCA, - 0x9DCB, 0x9DCC, 0x9DCD, 0x9DCE, 0x9DCF, 0x9DD0, 0x9DD1, 0x9DD2, - 0x9DD3, 0x9DD4, 0x9DD5, 0x9DD6, 0x9DD7, 0x9DD8, 0x9DD9, 0x9DDA, - 0x9DDB, 0x9DDC, 0x9DDD, 0x9DDE, 0x9DDF, 0x9DE0, 0x9DE1, 0x003F, - 0x9DE2, 0x9DE3, 0x9DE4, 0x9DE5, 0x9DE6, 0x9DE7, 0x9DE8, 0x9DE9, - 0x9DEA, 0x9DEB, 0x9DEC, 0x9DED, 0x9DEE, 0x9DEF, 0x9DF0, 0x9DF1, - 0x9DF2, 0x9DF3, 0x9DF4, 0x9DF5, 0x9DF6, 0x9DF7, 0x9DF8, 0x9DF9, - 0x9DFA, 0x9DFB, 0x9DFC, 0x9DFD, 0x9DFE, 0x9DFF, 0x9E00, 0x9E01, - 0x9E02, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F - }, - { - 0x9E03, 0x9E04, 0x9E05, 0x9E06, 0x9E07, 0x9E08, 0x9E09, 0x9E0A, - 0x9E0B, 0x9E0C, 0x9E0D, 0x9E0E, 0x9E0F, 0x9E10, 0x9E11, 0x9E12, - 0x9E13, 0x9E14, 0x9E15, 0x9E16, 0x9E17, 0x9E18, 0x9E19, 0x9E1A, - 0x9E1B, 0x9E1C, 0x9E1D, 0x9E1E, 0x9E24, 0x9E27, 0x9E2E, 0x9E30, - 0x9E34, 0x9E3B, 0x9E3C, 0x9E40, 0x9E4D, 0x9E50, 0x9E52, 0x9E53, - 0x9E54, 0x9E56, 0x9E59, 0x9E5D, 0x9E5F, 0x9E60, 0x9E61, 0x9E62, - 0x9E65, 0x9E6E, 0x9E6F, 0x9E72, 0x9E74, 0x9E75, 0x9E76, 0x9E77, - 0x9E78, 0x9E79, 0x9E7A, 0x9E7B, 0x9E7C, 0x9E7D, 0x9E80, 0x003F, - 0x9E81, 0x9E83, 0x9E84, 0x9E85, 0x9E86, 0x9E89, 0x9E8A, 0x9E8C, - 0x9E8D, 0x9E8E, 0x9E8F, 0x9E90, 0x9E91, 0x9E94, 0x9E95, 0x9E96, - 0x9E97, 0x9E98, 0x9E99, 0x9E9A, 0x9E9B, 0x9E9C, 0x9E9E, 0x9EA0, - 0x9EA1, 0x9EA2, 0x9EA3, 0x9EA4, 0x9EA5, 0x9EA7, 0x9EA8, 0x9EA9, - 0x9EAA, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F - }, - { - 0x9EAB, 0x9EAC, 0x9EAD, 0x9EAE, 0x9EAF, 0x9EB0, 0x9EB1, 0x9EB2, - 0x9EB3, 0x9EB5, 0x9EB6, 0x9EB7, 0x9EB9, 0x9EBA, 0x9EBC, 0x9EBF, - 0x9EC0, 0x9EC1, 0x9EC2, 0x9EC3, 0x9EC5, 0x9EC6, 0x9EC7, 0x9EC8, - 0x9ECA, 0x9ECB, 0x9ECC, 0x9ED0, 0x9ED2, 0x9ED3, 0x9ED5, 0x9ED6, - 0x9ED7, 0x9ED9, 0x9EDA, 0x9EDE, 0x9EE1, 0x9EE3, 0x9EE4, 0x9EE6, - 0x9EE8, 0x9EEB, 0x9EEC, 0x9EED, 0x9EEE, 0x9EF0, 0x9EF1, 0x9EF2, - 0x9EF3, 0x9EF4, 0x9EF5, 0x9EF6, 0x9EF7, 0x9EF8, 0x9EFA, 0x9EFD, - 0x9EFF, 0x9F00, 0x9F01, 0x9F02, 0x9F03, 0x9F04, 0x9F05, 0x003F, - 0x9F06, 0x9F07, 0x9F08, 0x9F09, 0x9F0A, 0x9F0C, 0x9F0F, 0x9F11, - 0x9F12, 0x9F14, 0x9F15, 0x9F16, 0x9F18, 0x9F1A, 0x9F1B, 0x9F1C, - 0x9F1D, 0x9F1E, 0x9F1F, 0x9F21, 0x9F23, 0x9F24, 0x9F25, 0x9F26, - 0x9F27, 0x9F28, 0x9F29, 0x9F2A, 0x9F2B, 0x9F2D, 0x9F2E, 0x9F30, - 0x9F31, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F - }, - { - 0x9F32, 0x9F33, 0x9F34, 0x9F35, 0x9F36, 0x9F38, 0x9F3A, 0x9F3C, - 0x9F3F, 0x9F40, 0x9F41, 0x9F42, 0x9F43, 0x9F45, 0x9F46, 0x9F47, - 0x9F48, 0x9F49, 0x9F4A, 0x9F4B, 0x9F4C, 0x9F4D, 0x9F4E, 0x9F4F, - 0x9F52, 0x9F53, 0x9F54, 0x9F55, 0x9F56, 0x9F57, 0x9F58, 0x9F59, - 0x9F5A, 0x9F5B, 0x9F5C, 0x9F5D, 0x9F5E, 0x9F5F, 0x9F60, 0x9F61, - 0x9F62, 0x9F63, 0x9F64, 0x9F65, 0x9F66, 0x9F67, 0x9F68, 0x9F69, - 0x9F6A, 0x9F6B, 0x9F6C, 0x9F6D, 0x9F6E, 0x9F6F, 0x9F70, 0x9F71, - 0x9F72, 0x9F73, 0x9F74, 0x9F75, 0x9F76, 0x9F77, 0x9F78, 0x003F, - 0x9F79, 0x9F7A, 0x9F7B, 0x9F7C, 0x9F7D, 0x9F7E, 0x9F81, 0x9F82, - 0x9F8D, 0x9F8E, 0x9F8F, 0x9F90, 0x9F91, 0x9F92, 0x9F93, 0x9F94, - 0x9F95, 0x9F96, 0x9F97, 0x9F98, 0x9F9C, 0x9F9D, 0x9F9E, 0x9FA1, - 0x9FA2, 0x9FA3, 0x9FA4, 0x9FA5, 0xF92C, 0xF979, 0xF995, 0xF9E7, - 0xF9F1, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F - }, - { - 0xFA0C, 0xFA0D, 0xFA0E, 0xFA0F, 0xFA11, 0xFA13, 0xFA14, 0xFA18, - 0xFA1F, 0xFA20, 0xFA21, 0xFA23, 0xFA24, 0xFA27, 0xFA28, 0xFA29, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, - 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F - } -}}; +constexpr std::array, 126> cp936_to_unicode_table{ + {{0x4E02, 0x4E04, 0x4E05, 0x4E06, 0x4E0F, 0x4E12, 0x4E17, 0x4E1F, 0x4E20, + 0x4E21, 0x4E23, 0x4E26, 0x4E29, 0x4E2E, 0x4E2F, 0x4E31, 0x4E33, 0x4E35, + 0x4E37, 0x4E3C, 0x4E40, 0x4E41, 0x4E42, 0x4E44, 0x4E46, 0x4E4A, 0x4E51, + 0x4E55, 0x4E57, 0x4E5A, 0x4E5B, 0x4E62, 0x4E63, 0x4E64, 0x4E65, 0x4E67, + 0x4E68, 0x4E6A, 0x4E6B, 0x4E6C, 0x4E6D, 0x4E6E, 0x4E6F, 0x4E72, 0x4E74, + 0x4E75, 0x4E76, 0x4E77, 0x4E78, 0x4E79, 0x4E7A, 0x4E7B, 0x4E7C, 0x4E7D, + 0x4E7F, 0x4E80, 0x4E81, 0x4E82, 0x4E83, 0x4E84, 0x4E85, 0x4E87, 0x4E8A, + 0x003F, 0x4E90, 0x4E96, 0x4E97, 0x4E99, 0x4E9C, 0x4E9D, 0x4E9E, 0x4EA3, + 0x4EAA, 0x4EAF, 0x4EB0, 0x4EB1, 0x4EB4, 0x4EB6, 0x4EB7, 0x4EB8, 0x4EB9, + 0x4EBC, 0x4EBD, 0x4EBE, 0x4EC8, 0x4ECC, 0x4ECF, 0x4ED0, 0x4ED2, 0x4EDA, + 0x4EDB, 0x4EDC, 0x4EE0, 0x4EE2, 0x4EE6, 0x4EE7, 0x4EE9, 0x4EED, 0x4EEE, + 0x4EEF, 0x4EF1, 0x4EF4, 0x4EF8, 0x4EF9, 0x4EFA, 0x4EFC, 0x4EFE, 0x4F00, + 0x4F02, 0x4F03, 0x4F04, 0x4F05, 0x4F06, 0x4F07, 0x4F08, 0x4F0B, 0x4F0C, + 0x4F12, 0x4F13, 0x4F14, 0x4F15, 0x4F16, 0x4F1C, 0x4F1D, 0x4F21, 0x4F23, + 0x4F28, 0x4F29, 0x4F2C, 0x4F2D, 0x4F2E, 0x4F31, 0x4F33, 0x4F35, 0x4F37, + 0x4F39, 0x4F3B, 0x4F3E, 0x4F3F, 0x4F40, 0x4F41, 0x4F42, 0x4F44, 0x4F45, + 0x4F47, 0x4F48, 0x4F49, 0x4F4A, 0x4F4B, 0x4F4C, 0x4F52, 0x4F54, 0x4F56, + 0x4F61, 0x4F62, 0x4F66, 0x4F68, 0x4F6A, 0x4F6B, 0x4F6D, 0x4F6E, 0x4F71, + 0x4F72, 0x4F75, 0x4F77, 0x4F78, 0x4F79, 0x4F7A, 0x4F7D, 0x4F80, 0x4F81, + 0x4F82, 0x4F85, 0x4F86, 0x4F87, 0x4F8A, 0x4F8C, 0x4F8E, 0x4F90, 0x4F92, + 0x4F93, 0x4F95, 0x4F96, 0x4F98, 0x4F99, 0x4F9A, 0x4F9C, 0x4F9E, 0x4F9F, + 0x4FA1, 0x4FA2}, + {0x4FA4, 0x4FAB, 0x4FAD, 0x4FB0, 0x4FB1, 0x4FB2, 0x4FB3, 0x4FB4, 0x4FB6, + 0x4FB7, 0x4FB8, 0x4FB9, 0x4FBA, 0x4FBB, 0x4FBC, 0x4FBD, 0x4FBE, 0x4FC0, + 0x4FC1, 0x4FC2, 0x4FC6, 0x4FC7, 0x4FC8, 0x4FC9, 0x4FCB, 0x4FCC, 0x4FCD, + 0x4FD2, 0x4FD3, 0x4FD4, 0x4FD5, 0x4FD6, 0x4FD9, 0x4FDB, 0x4FE0, 0x4FE2, + 0x4FE4, 0x4FE5, 0x4FE7, 0x4FEB, 0x4FEC, 0x4FF0, 0x4FF2, 0x4FF4, 0x4FF5, + 0x4FF6, 0x4FF7, 0x4FF9, 0x4FFB, 0x4FFC, 0x4FFD, 0x4FFF, 0x5000, 0x5001, + 0x5002, 0x5003, 0x5004, 0x5005, 0x5006, 0x5007, 0x5008, 0x5009, 0x500A, + 0x003F, 0x500B, 0x500E, 0x5010, 0x5011, 0x5013, 0x5015, 0x5016, 0x5017, + 0x501B, 0x501D, 0x501E, 0x5020, 0x5022, 0x5023, 0x5024, 0x5027, 0x502B, + 0x502F, 0x5030, 0x5031, 0x5032, 0x5033, 0x5034, 0x5035, 0x5036, 0x5037, + 0x5038, 0x5039, 0x503B, 0x503D, 0x503F, 0x5040, 0x5041, 0x5042, 0x5044, + 0x5045, 0x5046, 0x5049, 0x504A, 0x504B, 0x504D, 0x5050, 0x5051, 0x5052, + 0x5053, 0x5054, 0x5056, 0x5057, 0x5058, 0x5059, 0x505B, 0x505D, 0x505E, + 0x505F, 0x5060, 0x5061, 0x5062, 0x5063, 0x5064, 0x5066, 0x5067, 0x5068, + 0x5069, 0x506A, 0x506B, 0x506D, 0x506E, 0x506F, 0x5070, 0x5071, 0x5072, + 0x5073, 0x5074, 0x5075, 0x5078, 0x5079, 0x507A, 0x507C, 0x507D, 0x5081, + 0x5082, 0x5083, 0x5084, 0x5086, 0x5087, 0x5089, 0x508A, 0x508B, 0x508C, + 0x508E, 0x508F, 0x5090, 0x5091, 0x5092, 0x5093, 0x5094, 0x5095, 0x5096, + 0x5097, 0x5098, 0x5099, 0x509A, 0x509B, 0x509C, 0x509D, 0x509E, 0x509F, + 0x50A0, 0x50A1, 0x50A2, 0x50A4, 0x50A6, 0x50AA, 0x50AB, 0x50AD, 0x50AE, + 0x50AF, 0x50B0, 0x50B1, 0x50B3, 0x50B4, 0x50B5, 0x50B6, 0x50B7, 0x50B8, + 0x50B9, 0x50BC}, + {0x50BD, 0x50BE, 0x50BF, 0x50C0, 0x50C1, 0x50C2, 0x50C3, 0x50C4, 0x50C5, + 0x50C6, 0x50C7, 0x50C8, 0x50C9, 0x50CA, 0x50CB, 0x50CC, 0x50CD, 0x50CE, + 0x50D0, 0x50D1, 0x50D2, 0x50D3, 0x50D4, 0x50D5, 0x50D7, 0x50D8, 0x50D9, + 0x50DB, 0x50DC, 0x50DD, 0x50DE, 0x50DF, 0x50E0, 0x50E1, 0x50E2, 0x50E3, + 0x50E4, 0x50E5, 0x50E8, 0x50E9, 0x50EA, 0x50EB, 0x50EF, 0x50F0, 0x50F1, + 0x50F2, 0x50F4, 0x50F6, 0x50F7, 0x50F8, 0x50F9, 0x50FA, 0x50FC, 0x50FD, + 0x50FE, 0x50FF, 0x5100, 0x5101, 0x5102, 0x5103, 0x5104, 0x5105, 0x5108, + 0x003F, 0x5109, 0x510A, 0x510C, 0x510D, 0x510E, 0x510F, 0x5110, 0x5111, + 0x5113, 0x5114, 0x5115, 0x5116, 0x5117, 0x5118, 0x5119, 0x511A, 0x511B, + 0x511C, 0x511D, 0x511E, 0x511F, 0x5120, 0x5122, 0x5123, 0x5124, 0x5125, + 0x5126, 0x5127, 0x5128, 0x5129, 0x512A, 0x512B, 0x512C, 0x512D, 0x512E, + 0x512F, 0x5130, 0x5131, 0x5132, 0x5133, 0x5134, 0x5135, 0x5136, 0x5137, + 0x5138, 0x5139, 0x513A, 0x513B, 0x513C, 0x513D, 0x513E, 0x5142, 0x5147, + 0x514A, 0x514C, 0x514E, 0x514F, 0x5150, 0x5152, 0x5153, 0x5157, 0x5158, + 0x5159, 0x515B, 0x515D, 0x515E, 0x515F, 0x5160, 0x5161, 0x5163, 0x5164, + 0x5166, 0x5167, 0x5169, 0x516A, 0x516F, 0x5172, 0x517A, 0x517E, 0x517F, + 0x5183, 0x5184, 0x5186, 0x5187, 0x518A, 0x518B, 0x518E, 0x518F, 0x5190, + 0x5191, 0x5193, 0x5194, 0x5198, 0x519A, 0x519D, 0x519E, 0x519F, 0x51A1, + 0x51A3, 0x51A6, 0x51A7, 0x51A8, 0x51A9, 0x51AA, 0x51AD, 0x51AE, 0x51B4, + 0x51B8, 0x51B9, 0x51BA, 0x51BE, 0x51BF, 0x51C1, 0x51C2, 0x51C3, 0x51C5, + 0x51C8, 0x51CA, 0x51CD, 0x51CE, 0x51D0, 0x51D2, 0x51D3, 0x51D4, 0x51D5, + 0x51D6, 0x51D7}, + {0x51D8, 0x51D9, 0x51DA, 0x51DC, 0x51DE, 0x51DF, 0x51E2, 0x51E3, 0x51E5, + 0x51E6, 0x51E7, 0x51E8, 0x51E9, 0x51EA, 0x51EC, 0x51EE, 0x51F1, 0x51F2, + 0x51F4, 0x51F7, 0x51FE, 0x5204, 0x5205, 0x5209, 0x520B, 0x520C, 0x520F, + 0x5210, 0x5213, 0x5214, 0x5215, 0x521C, 0x521E, 0x521F, 0x5221, 0x5222, + 0x5223, 0x5225, 0x5226, 0x5227, 0x522A, 0x522C, 0x522F, 0x5231, 0x5232, + 0x5234, 0x5235, 0x523C, 0x523E, 0x5244, 0x5245, 0x5246, 0x5247, 0x5248, + 0x5249, 0x524B, 0x524E, 0x524F, 0x5252, 0x5253, 0x5255, 0x5257, 0x5258, + 0x003F, 0x5259, 0x525A, 0x525B, 0x525D, 0x525F, 0x5260, 0x5262, 0x5263, + 0x5264, 0x5266, 0x5268, 0x526B, 0x526C, 0x526D, 0x526E, 0x5270, 0x5271, + 0x5273, 0x5274, 0x5275, 0x5276, 0x5277, 0x5278, 0x5279, 0x527A, 0x527B, + 0x527C, 0x527E, 0x5280, 0x5283, 0x5284, 0x5285, 0x5286, 0x5287, 0x5289, + 0x528A, 0x528B, 0x528C, 0x528D, 0x528E, 0x528F, 0x5291, 0x5292, 0x5294, + 0x5295, 0x5296, 0x5297, 0x5298, 0x5299, 0x529A, 0x529C, 0x52A4, 0x52A5, + 0x52A6, 0x52A7, 0x52AE, 0x52AF, 0x52B0, 0x52B4, 0x52B5, 0x52B6, 0x52B7, + 0x52B8, 0x52B9, 0x52BA, 0x52BB, 0x52BC, 0x52BD, 0x52C0, 0x52C1, 0x52C2, + 0x52C4, 0x52C5, 0x52C6, 0x52C8, 0x52CA, 0x52CC, 0x52CD, 0x52CE, 0x52CF, + 0x52D1, 0x52D3, 0x52D4, 0x52D5, 0x52D7, 0x52D9, 0x52DA, 0x52DB, 0x52DC, + 0x52DD, 0x52DE, 0x52E0, 0x52E1, 0x52E2, 0x52E3, 0x52E5, 0x52E6, 0x52E7, + 0x52E8, 0x52E9, 0x52EA, 0x52EB, 0x52EC, 0x52ED, 0x52EE, 0x52EF, 0x52F1, + 0x52F2, 0x52F3, 0x52F4, 0x52F5, 0x52F6, 0x52F7, 0x52F8, 0x52FB, 0x52FC, + 0x52FD, 0x5301, 0x5302, 0x5303, 0x5304, 0x5307, 0x5309, 0x530A, 0x530B, + 0x530C, 0x530E}, + {0x5311, 0x5312, 0x5313, 0x5314, 0x5318, 0x531B, 0x531C, 0x531E, 0x531F, + 0x5322, 0x5324, 0x5325, 0x5327, 0x5328, 0x5329, 0x532B, 0x532C, 0x532D, + 0x532F, 0x5330, 0x5331, 0x5332, 0x5333, 0x5334, 0x5335, 0x5336, 0x5337, + 0x5338, 0x533C, 0x533D, 0x5340, 0x5342, 0x5344, 0x5346, 0x534B, 0x534C, + 0x534D, 0x5350, 0x5354, 0x5358, 0x5359, 0x535B, 0x535D, 0x5365, 0x5368, + 0x536A, 0x536C, 0x536D, 0x5372, 0x5376, 0x5379, 0x537B, 0x537C, 0x537D, + 0x537E, 0x5380, 0x5381, 0x5383, 0x5387, 0x5388, 0x538A, 0x538E, 0x538F, + 0x003F, 0x5390, 0x5391, 0x5392, 0x5393, 0x5394, 0x5396, 0x5397, 0x5399, + 0x539B, 0x539C, 0x539E, 0x53A0, 0x53A1, 0x53A4, 0x53A7, 0x53AA, 0x53AB, + 0x53AC, 0x53AD, 0x53AF, 0x53B0, 0x53B1, 0x53B2, 0x53B3, 0x53B4, 0x53B5, + 0x53B7, 0x53B8, 0x53B9, 0x53BA, 0x53BC, 0x53BD, 0x53BE, 0x53C0, 0x53C3, + 0x53C4, 0x53C5, 0x53C6, 0x53C7, 0x53CE, 0x53CF, 0x53D0, 0x53D2, 0x53D3, + 0x53D5, 0x53DA, 0x53DC, 0x53DD, 0x53DE, 0x53E1, 0x53E2, 0x53E7, 0x53F4, + 0x53FA, 0x53FE, 0x53FF, 0x5400, 0x5402, 0x5405, 0x5407, 0x540B, 0x5414, + 0x5418, 0x5419, 0x541A, 0x541C, 0x5422, 0x5424, 0x5425, 0x542A, 0x5430, + 0x5433, 0x5436, 0x5437, 0x543A, 0x543D, 0x543F, 0x5441, 0x5442, 0x5444, + 0x5445, 0x5447, 0x5449, 0x544C, 0x544D, 0x544E, 0x544F, 0x5451, 0x545A, + 0x545D, 0x545E, 0x545F, 0x5460, 0x5461, 0x5463, 0x5465, 0x5467, 0x5469, + 0x546A, 0x546B, 0x546C, 0x546D, 0x546E, 0x546F, 0x5470, 0x5474, 0x5479, + 0x547A, 0x547E, 0x547F, 0x5481, 0x5483, 0x5485, 0x5487, 0x5488, 0x5489, + 0x548A, 0x548D, 0x5491, 0x5493, 0x5497, 0x5498, 0x549C, 0x549E, 0x549F, + 0x54A0, 0x54A1}, + {0x54A2, 0x54A5, 0x54AE, 0x54B0, 0x54B2, 0x54B5, 0x54B6, 0x54B7, 0x54B9, + 0x54BA, 0x54BC, 0x54BE, 0x54C3, 0x54C5, 0x54CA, 0x54CB, 0x54D6, 0x54D8, + 0x54DB, 0x54E0, 0x54E1, 0x54E2, 0x54E3, 0x54E4, 0x54EB, 0x54EC, 0x54EF, + 0x54F0, 0x54F1, 0x54F4, 0x54F5, 0x54F6, 0x54F7, 0x54F8, 0x54F9, 0x54FB, + 0x54FE, 0x5500, 0x5502, 0x5503, 0x5504, 0x5505, 0x5508, 0x550A, 0x550B, + 0x550C, 0x550D, 0x550E, 0x5512, 0x5513, 0x5515, 0x5516, 0x5517, 0x5518, + 0x5519, 0x551A, 0x551C, 0x551D, 0x551E, 0x551F, 0x5521, 0x5525, 0x5526, + 0x003F, 0x5528, 0x5529, 0x552B, 0x552D, 0x5532, 0x5534, 0x5535, 0x5536, + 0x5538, 0x5539, 0x553A, 0x553B, 0x553D, 0x5540, 0x5542, 0x5545, 0x5547, + 0x5548, 0x554B, 0x554C, 0x554D, 0x554E, 0x554F, 0x5551, 0x5552, 0x5553, + 0x5554, 0x5557, 0x5558, 0x5559, 0x555A, 0x555B, 0x555D, 0x555E, 0x555F, + 0x5560, 0x5562, 0x5563, 0x5568, 0x5569, 0x556B, 0x556F, 0x5570, 0x5571, + 0x5572, 0x5573, 0x5574, 0x5579, 0x557A, 0x557D, 0x557F, 0x5585, 0x5586, + 0x558C, 0x558D, 0x558E, 0x5590, 0x5592, 0x5593, 0x5595, 0x5596, 0x5597, + 0x559A, 0x559B, 0x559E, 0x55A0, 0x55A1, 0x55A2, 0x55A3, 0x55A4, 0x55A5, + 0x55A6, 0x55A8, 0x55A9, 0x55AA, 0x55AB, 0x55AC, 0x55AD, 0x55AE, 0x55AF, + 0x55B0, 0x55B2, 0x55B4, 0x55B6, 0x55B8, 0x55BA, 0x55BC, 0x55BF, 0x55C0, + 0x55C1, 0x55C2, 0x55C3, 0x55C6, 0x55C7, 0x55C8, 0x55CA, 0x55CB, 0x55CE, + 0x55CF, 0x55D0, 0x55D5, 0x55D7, 0x55D8, 0x55D9, 0x55DA, 0x55DB, 0x55DE, + 0x55E0, 0x55E2, 0x55E7, 0x55E9, 0x55ED, 0x55EE, 0x55F0, 0x55F1, 0x55F4, + 0x55F6, 0x55F8, 0x55F9, 0x55FA, 0x55FB, 0x55FC, 0x55FF, 0x5602, 0x5603, + 0x5604, 0x5605}, + {0x5606, 0x5607, 0x560A, 0x560B, 0x560D, 0x5610, 0x5611, 0x5612, 0x5613, + 0x5614, 0x5615, 0x5616, 0x5617, 0x5619, 0x561A, 0x561C, 0x561D, 0x5620, + 0x5621, 0x5622, 0x5625, 0x5626, 0x5628, 0x5629, 0x562A, 0x562B, 0x562E, + 0x562F, 0x5630, 0x5633, 0x5635, 0x5637, 0x5638, 0x563A, 0x563C, 0x563D, + 0x563E, 0x5640, 0x5641, 0x5642, 0x5643, 0x5644, 0x5645, 0x5646, 0x5647, + 0x5648, 0x5649, 0x564A, 0x564B, 0x564F, 0x5650, 0x5651, 0x5652, 0x5653, + 0x5655, 0x5656, 0x565A, 0x565B, 0x565D, 0x565E, 0x565F, 0x5660, 0x5661, + 0x003F, 0x5663, 0x5665, 0x5666, 0x5667, 0x566D, 0x566E, 0x566F, 0x5670, + 0x5672, 0x5673, 0x5674, 0x5675, 0x5677, 0x5678, 0x5679, 0x567A, 0x567D, + 0x567E, 0x567F, 0x5680, 0x5681, 0x5682, 0x5683, 0x5684, 0x5687, 0x5688, + 0x5689, 0x568A, 0x568B, 0x568C, 0x568D, 0x5690, 0x5691, 0x5692, 0x5694, + 0x5695, 0x5696, 0x5697, 0x5698, 0x5699, 0x569A, 0x569B, 0x569C, 0x569D, + 0x569E, 0x569F, 0x56A0, 0x56A1, 0x56A2, 0x56A4, 0x56A5, 0x56A6, 0x56A7, + 0x56A8, 0x56A9, 0x56AA, 0x56AB, 0x56AC, 0x56AD, 0x56AE, 0x56B0, 0x56B1, + 0x56B2, 0x56B3, 0x56B4, 0x56B5, 0x56B6, 0x56B8, 0x56B9, 0x56BA, 0x56BB, + 0x56BD, 0x56BE, 0x56BF, 0x56C0, 0x56C1, 0x56C2, 0x56C3, 0x56C4, 0x56C5, + 0x56C6, 0x56C7, 0x56C8, 0x56C9, 0x56CB, 0x56CC, 0x56CD, 0x56CE, 0x56CF, + 0x56D0, 0x56D1, 0x56D2, 0x56D3, 0x56D5, 0x56D6, 0x56D8, 0x56D9, 0x56DC, + 0x56E3, 0x56E5, 0x56E6, 0x56E7, 0x56E8, 0x56E9, 0x56EA, 0x56EC, 0x56EE, + 0x56EF, 0x56F2, 0x56F3, 0x56F6, 0x56F7, 0x56F8, 0x56FB, 0x56FC, 0x5700, + 0x5701, 0x5702, 0x5705, 0x5707, 0x570B, 0x570C, 0x570D, 0x570E, 0x570F, + 0x5710, 0x5711}, + {0x5712, 0x5713, 0x5714, 0x5715, 0x5716, 0x5717, 0x5718, 0x5719, 0x571A, + 0x571B, 0x571D, 0x571E, 0x5720, 0x5721, 0x5722, 0x5724, 0x5725, 0x5726, + 0x5727, 0x572B, 0x5731, 0x5732, 0x5734, 0x5735, 0x5736, 0x5737, 0x5738, + 0x573C, 0x573D, 0x573F, 0x5741, 0x5743, 0x5744, 0x5745, 0x5746, 0x5748, + 0x5749, 0x574B, 0x5752, 0x5753, 0x5754, 0x5755, 0x5756, 0x5758, 0x5759, + 0x5762, 0x5763, 0x5765, 0x5767, 0x576C, 0x576E, 0x5770, 0x5771, 0x5772, + 0x5774, 0x5775, 0x5778, 0x5779, 0x577A, 0x577D, 0x577E, 0x577F, 0x5780, + 0x003F, 0x5781, 0x5787, 0x5788, 0x5789, 0x578A, 0x578D, 0x578E, 0x578F, + 0x5790, 0x5791, 0x5794, 0x5795, 0x5796, 0x5797, 0x5798, 0x5799, 0x579A, + 0x579C, 0x579D, 0x579E, 0x579F, 0x57A5, 0x57A8, 0x57AA, 0x57AC, 0x57AF, + 0x57B0, 0x57B1, 0x57B3, 0x57B5, 0x57B6, 0x57B7, 0x57B9, 0x57BA, 0x57BB, + 0x57BC, 0x57BD, 0x57BE, 0x57BF, 0x57C0, 0x57C1, 0x57C4, 0x57C5, 0x57C6, + 0x57C7, 0x57C8, 0x57C9, 0x57CA, 0x57CC, 0x57CD, 0x57D0, 0x57D1, 0x57D3, + 0x57D6, 0x57D7, 0x57DB, 0x57DC, 0x57DE, 0x57E1, 0x57E2, 0x57E3, 0x57E5, + 0x57E6, 0x57E7, 0x57E8, 0x57E9, 0x57EA, 0x57EB, 0x57EC, 0x57EE, 0x57F0, + 0x57F1, 0x57F2, 0x57F3, 0x57F5, 0x57F6, 0x57F7, 0x57FB, 0x57FC, 0x57FE, + 0x57FF, 0x5801, 0x5803, 0x5804, 0x5805, 0x5808, 0x5809, 0x580A, 0x580C, + 0x580E, 0x580F, 0x5810, 0x5812, 0x5813, 0x5814, 0x5816, 0x5817, 0x5818, + 0x581A, 0x581B, 0x581C, 0x581D, 0x581F, 0x5822, 0x5823, 0x5825, 0x5826, + 0x5827, 0x5828, 0x5829, 0x582B, 0x582C, 0x582D, 0x582E, 0x582F, 0x5831, + 0x5832, 0x5833, 0x5834, 0x5836, 0x5837, 0x5838, 0x5839, 0x583A, 0x583B, + 0x583C, 0x583D}, + {0x583E, 0x583F, 0x5840, 0x5841, 0x5842, 0x5843, 0x5845, 0x5846, 0x5847, + 0x5848, 0x5849, 0x584A, 0x584B, 0x584E, 0x584F, 0x5850, 0x5852, 0x5853, + 0x5855, 0x5856, 0x5857, 0x5859, 0x585A, 0x585B, 0x585C, 0x585D, 0x585F, + 0x5860, 0x5861, 0x5862, 0x5863, 0x5864, 0x5866, 0x5867, 0x5868, 0x5869, + 0x586A, 0x586D, 0x586E, 0x586F, 0x5870, 0x5871, 0x5872, 0x5873, 0x5874, + 0x5875, 0x5876, 0x5877, 0x5878, 0x5879, 0x587A, 0x587B, 0x587C, 0x587D, + 0x587F, 0x5882, 0x5884, 0x5886, 0x5887, 0x5888, 0x588A, 0x588B, 0x588C, + 0x003F, 0x588D, 0x588E, 0x588F, 0x5890, 0x5891, 0x5894, 0x5895, 0x5896, + 0x5897, 0x5898, 0x589B, 0x589C, 0x589D, 0x58A0, 0x58A1, 0x58A2, 0x58A3, + 0x58A4, 0x58A5, 0x58A6, 0x58A7, 0x58AA, 0x58AB, 0x58AC, 0x58AD, 0x58AE, + 0x58AF, 0x58B0, 0x58B1, 0x58B2, 0x58B3, 0x58B4, 0x58B5, 0x58B6, 0x58B7, + 0x58B8, 0x58B9, 0x58BA, 0x58BB, 0x58BD, 0x58BE, 0x58BF, 0x58C0, 0x58C2, + 0x58C3, 0x58C4, 0x58C6, 0x58C7, 0x58C8, 0x58C9, 0x58CA, 0x58CB, 0x58CC, + 0x58CD, 0x58CE, 0x58CF, 0x58D0, 0x58D2, 0x58D3, 0x58D4, 0x58D6, 0x58D7, + 0x58D8, 0x58D9, 0x58DA, 0x58DB, 0x58DC, 0x58DD, 0x58DE, 0x58DF, 0x58E0, + 0x58E1, 0x58E2, 0x58E3, 0x58E5, 0x58E6, 0x58E7, 0x58E8, 0x58E9, 0x58EA, + 0x58ED, 0x58EF, 0x58F1, 0x58F2, 0x58F4, 0x58F5, 0x58F7, 0x58F8, 0x58FA, + 0x58FB, 0x58FC, 0x58FD, 0x58FE, 0x58FF, 0x5900, 0x5901, 0x5903, 0x5905, + 0x5906, 0x5908, 0x5909, 0x590A, 0x590B, 0x590C, 0x590E, 0x5910, 0x5911, + 0x5912, 0x5913, 0x5917, 0x5918, 0x591B, 0x591D, 0x591E, 0x5920, 0x5921, + 0x5922, 0x5923, 0x5926, 0x5928, 0x592C, 0x5930, 0x5932, 0x5933, 0x5935, + 0x5936, 0x593B}, + {0x593D, 0x593E, 0x593F, 0x5940, 0x5943, 0x5945, 0x5946, 0x594A, 0x594C, + 0x594D, 0x5950, 0x5952, 0x5953, 0x5959, 0x595B, 0x595C, 0x595D, 0x595E, + 0x595F, 0x5961, 0x5963, 0x5964, 0x5966, 0x5967, 0x5968, 0x5969, 0x596A, + 0x596B, 0x596C, 0x596D, 0x596E, 0x596F, 0x5970, 0x5971, 0x5972, 0x5975, + 0x5977, 0x597A, 0x597B, 0x597C, 0x597E, 0x597F, 0x5980, 0x5985, 0x5989, + 0x598B, 0x598C, 0x598E, 0x598F, 0x5990, 0x5991, 0x5994, 0x5995, 0x5998, + 0x599A, 0x599B, 0x599C, 0x599D, 0x599F, 0x59A0, 0x59A1, 0x59A2, 0x59A6, + 0x003F, 0x59A7, 0x59AC, 0x59AD, 0x59B0, 0x59B1, 0x59B3, 0x59B4, 0x59B5, + 0x59B6, 0x59B7, 0x59B8, 0x59BA, 0x59BC, 0x59BD, 0x59BF, 0x59C0, 0x59C1, + 0x59C2, 0x59C3, 0x59C4, 0x59C5, 0x59C7, 0x59C8, 0x59C9, 0x59CC, 0x59CD, + 0x59CE, 0x59CF, 0x59D5, 0x59D6, 0x59D9, 0x59DB, 0x59DE, 0x59DF, 0x59E0, + 0x59E1, 0x59E2, 0x59E4, 0x59E6, 0x59E7, 0x59E9, 0x59EA, 0x59EB, 0x59ED, + 0x59EE, 0x59EF, 0x59F0, 0x59F1, 0x59F2, 0x59F3, 0x59F4, 0x59F5, 0x59F6, + 0x59F7, 0x59F8, 0x59FA, 0x59FC, 0x59FD, 0x59FE, 0x5A00, 0x5A02, 0x5A0A, + 0x5A0B, 0x5A0D, 0x5A0E, 0x5A0F, 0x5A10, 0x5A12, 0x5A14, 0x5A15, 0x5A16, + 0x5A17, 0x5A19, 0x5A1A, 0x5A1B, 0x5A1D, 0x5A1E, 0x5A21, 0x5A22, 0x5A24, + 0x5A26, 0x5A27, 0x5A28, 0x5A2A, 0x5A2B, 0x5A2C, 0x5A2D, 0x5A2E, 0x5A2F, + 0x5A30, 0x5A33, 0x5A35, 0x5A37, 0x5A38, 0x5A39, 0x5A3A, 0x5A3B, 0x5A3D, + 0x5A3E, 0x5A3F, 0x5A41, 0x5A42, 0x5A43, 0x5A44, 0x5A45, 0x5A47, 0x5A48, + 0x5A4B, 0x5A4C, 0x5A4D, 0x5A4E, 0x5A4F, 0x5A50, 0x5A51, 0x5A52, 0x5A53, + 0x5A54, 0x5A56, 0x5A57, 0x5A58, 0x5A59, 0x5A5B, 0x5A5C, 0x5A5D, 0x5A5E, + 0x5A5F, 0x5A60}, + {0x5A61, 0x5A63, 0x5A64, 0x5A65, 0x5A66, 0x5A68, 0x5A69, 0x5A6B, 0x5A6C, + 0x5A6D, 0x5A6E, 0x5A6F, 0x5A70, 0x5A71, 0x5A72, 0x5A73, 0x5A78, 0x5A79, + 0x5A7B, 0x5A7C, 0x5A7D, 0x5A7E, 0x5A80, 0x5A81, 0x5A82, 0x5A83, 0x5A84, + 0x5A85, 0x5A86, 0x5A87, 0x5A88, 0x5A89, 0x5A8A, 0x5A8B, 0x5A8C, 0x5A8D, + 0x5A8E, 0x5A8F, 0x5A90, 0x5A91, 0x5A93, 0x5A94, 0x5A95, 0x5A96, 0x5A97, + 0x5A98, 0x5A99, 0x5A9C, 0x5A9D, 0x5A9E, 0x5A9F, 0x5AA0, 0x5AA1, 0x5AA2, + 0x5AA3, 0x5AA4, 0x5AA5, 0x5AA6, 0x5AA7, 0x5AA8, 0x5AA9, 0x5AAB, 0x5AAC, + 0x003F, 0x5AAD, 0x5AAE, 0x5AAF, 0x5AB0, 0x5AB1, 0x5AB4, 0x5AB6, 0x5AB7, + 0x5AB9, 0x5ABA, 0x5ABB, 0x5ABC, 0x5ABD, 0x5ABF, 0x5AC0, 0x5AC3, 0x5AC4, + 0x5AC5, 0x5AC6, 0x5AC7, 0x5AC8, 0x5ACA, 0x5ACB, 0x5ACD, 0x5ACE, 0x5ACF, + 0x5AD0, 0x5AD1, 0x5AD3, 0x5AD5, 0x5AD7, 0x5AD9, 0x5ADA, 0x5ADB, 0x5ADD, + 0x5ADE, 0x5ADF, 0x5AE2, 0x5AE4, 0x5AE5, 0x5AE7, 0x5AE8, 0x5AEA, 0x5AEC, + 0x5AED, 0x5AEE, 0x5AEF, 0x5AF0, 0x5AF2, 0x5AF3, 0x5AF4, 0x5AF5, 0x5AF6, + 0x5AF7, 0x5AF8, 0x5AF9, 0x5AFA, 0x5AFB, 0x5AFC, 0x5AFD, 0x5AFE, 0x5AFF, + 0x5B00, 0x5B01, 0x5B02, 0x5B03, 0x5B04, 0x5B05, 0x5B06, 0x5B07, 0x5B08, + 0x5B0A, 0x5B0B, 0x5B0C, 0x5B0D, 0x5B0E, 0x5B0F, 0x5B10, 0x5B11, 0x5B12, + 0x5B13, 0x5B14, 0x5B15, 0x5B18, 0x5B19, 0x5B1A, 0x5B1B, 0x5B1C, 0x5B1D, + 0x5B1E, 0x5B1F, 0x5B20, 0x5B21, 0x5B22, 0x5B23, 0x5B24, 0x5B25, 0x5B26, + 0x5B27, 0x5B28, 0x5B29, 0x5B2A, 0x5B2B, 0x5B2C, 0x5B2D, 0x5B2E, 0x5B2F, + 0x5B30, 0x5B31, 0x5B33, 0x5B35, 0x5B36, 0x5B38, 0x5B39, 0x5B3A, 0x5B3B, + 0x5B3C, 0x5B3D, 0x5B3E, 0x5B3F, 0x5B41, 0x5B42, 0x5B43, 0x5B44, 0x5B45, + 0x5B46, 0x5B47}, + {0x5B48, 0x5B49, 0x5B4A, 0x5B4B, 0x5B4C, 0x5B4D, 0x5B4E, 0x5B4F, 0x5B52, + 0x5B56, 0x5B5E, 0x5B60, 0x5B61, 0x5B67, 0x5B68, 0x5B6B, 0x5B6D, 0x5B6E, + 0x5B6F, 0x5B72, 0x5B74, 0x5B76, 0x5B77, 0x5B78, 0x5B79, 0x5B7B, 0x5B7C, + 0x5B7E, 0x5B7F, 0x5B82, 0x5B86, 0x5B8A, 0x5B8D, 0x5B8E, 0x5B90, 0x5B91, + 0x5B92, 0x5B94, 0x5B96, 0x5B9F, 0x5BA7, 0x5BA8, 0x5BA9, 0x5BAC, 0x5BAD, + 0x5BAE, 0x5BAF, 0x5BB1, 0x5BB2, 0x5BB7, 0x5BBA, 0x5BBB, 0x5BBC, 0x5BC0, + 0x5BC1, 0x5BC3, 0x5BC8, 0x5BC9, 0x5BCA, 0x5BCB, 0x5BCD, 0x5BCE, 0x5BCF, + 0x003F, 0x5BD1, 0x5BD4, 0x5BD5, 0x5BD6, 0x5BD7, 0x5BD8, 0x5BD9, 0x5BDA, + 0x5BDB, 0x5BDC, 0x5BE0, 0x5BE2, 0x5BE3, 0x5BE6, 0x5BE7, 0x5BE9, 0x5BEA, + 0x5BEB, 0x5BEC, 0x5BED, 0x5BEF, 0x5BF1, 0x5BF2, 0x5BF3, 0x5BF4, 0x5BF5, + 0x5BF6, 0x5BF7, 0x5BFD, 0x5BFE, 0x5C00, 0x5C02, 0x5C03, 0x5C05, 0x5C07, + 0x5C08, 0x5C0B, 0x5C0C, 0x5C0D, 0x5C0E, 0x5C10, 0x5C12, 0x5C13, 0x5C17, + 0x5C19, 0x5C1B, 0x5C1E, 0x5C1F, 0x5C20, 0x5C21, 0x5C23, 0x5C26, 0x5C28, + 0x5C29, 0x5C2A, 0x5C2B, 0x5C2D, 0x5C2E, 0x5C2F, 0x5C30, 0x5C32, 0x5C33, + 0x5C35, 0x5C36, 0x5C37, 0x5C43, 0x5C44, 0x5C46, 0x5C47, 0x5C4C, 0x5C4D, + 0x5C52, 0x5C53, 0x5C54, 0x5C56, 0x5C57, 0x5C58, 0x5C5A, 0x5C5B, 0x5C5C, + 0x5C5D, 0x5C5F, 0x5C62, 0x5C64, 0x5C67, 0x5C68, 0x5C69, 0x5C6A, 0x5C6B, + 0x5C6C, 0x5C6D, 0x5C70, 0x5C72, 0x5C73, 0x5C74, 0x5C75, 0x5C76, 0x5C77, + 0x5C78, 0x5C7B, 0x5C7C, 0x5C7D, 0x5C7E, 0x5C80, 0x5C83, 0x5C84, 0x5C85, + 0x5C86, 0x5C87, 0x5C89, 0x5C8A, 0x5C8B, 0x5C8E, 0x5C8F, 0x5C92, 0x5C93, + 0x5C95, 0x5C9D, 0x5C9E, 0x5C9F, 0x5CA0, 0x5CA1, 0x5CA4, 0x5CA5, 0x5CA6, + 0x5CA7, 0x5CA8}, + {0x5CAA, 0x5CAE, 0x5CAF, 0x5CB0, 0x5CB2, 0x5CB4, 0x5CB6, 0x5CB9, 0x5CBA, + 0x5CBB, 0x5CBC, 0x5CBE, 0x5CC0, 0x5CC2, 0x5CC3, 0x5CC5, 0x5CC6, 0x5CC7, + 0x5CC8, 0x5CC9, 0x5CCA, 0x5CCC, 0x5CCD, 0x5CCE, 0x5CCF, 0x5CD0, 0x5CD1, + 0x5CD3, 0x5CD4, 0x5CD5, 0x5CD6, 0x5CD7, 0x5CD8, 0x5CDA, 0x5CDB, 0x5CDC, + 0x5CDD, 0x5CDE, 0x5CDF, 0x5CE0, 0x5CE2, 0x5CE3, 0x5CE7, 0x5CE9, 0x5CEB, + 0x5CEC, 0x5CEE, 0x5CEF, 0x5CF1, 0x5CF2, 0x5CF3, 0x5CF4, 0x5CF5, 0x5CF6, + 0x5CF7, 0x5CF8, 0x5CF9, 0x5CFA, 0x5CFC, 0x5CFD, 0x5CFE, 0x5CFF, 0x5D00, + 0x003F, 0x5D01, 0x5D04, 0x5D05, 0x5D08, 0x5D09, 0x5D0A, 0x5D0B, 0x5D0C, + 0x5D0D, 0x5D0F, 0x5D10, 0x5D11, 0x5D12, 0x5D13, 0x5D15, 0x5D17, 0x5D18, + 0x5D19, 0x5D1A, 0x5D1C, 0x5D1D, 0x5D1F, 0x5D20, 0x5D21, 0x5D22, 0x5D23, + 0x5D25, 0x5D28, 0x5D2A, 0x5D2B, 0x5D2C, 0x5D2F, 0x5D30, 0x5D31, 0x5D32, + 0x5D33, 0x5D35, 0x5D36, 0x5D37, 0x5D38, 0x5D39, 0x5D3A, 0x5D3B, 0x5D3C, + 0x5D3F, 0x5D40, 0x5D41, 0x5D42, 0x5D43, 0x5D44, 0x5D45, 0x5D46, 0x5D48, + 0x5D49, 0x5D4D, 0x5D4E, 0x5D4F, 0x5D50, 0x5D51, 0x5D52, 0x5D53, 0x5D54, + 0x5D55, 0x5D56, 0x5D57, 0x5D59, 0x5D5A, 0x5D5C, 0x5D5E, 0x5D5F, 0x5D60, + 0x5D61, 0x5D62, 0x5D63, 0x5D64, 0x5D65, 0x5D66, 0x5D67, 0x5D68, 0x5D6A, + 0x5D6D, 0x5D6E, 0x5D70, 0x5D71, 0x5D72, 0x5D73, 0x5D75, 0x5D76, 0x5D77, + 0x5D78, 0x5D79, 0x5D7A, 0x5D7B, 0x5D7C, 0x5D7D, 0x5D7E, 0x5D7F, 0x5D80, + 0x5D81, 0x5D83, 0x5D84, 0x5D85, 0x5D86, 0x5D87, 0x5D88, 0x5D89, 0x5D8A, + 0x5D8B, 0x5D8C, 0x5D8D, 0x5D8E, 0x5D8F, 0x5D90, 0x5D91, 0x5D92, 0x5D93, + 0x5D94, 0x5D95, 0x5D96, 0x5D97, 0x5D98, 0x5D9A, 0x5D9B, 0x5D9C, 0x5D9E, + 0x5D9F, 0x5DA0}, + {0x5DA1, 0x5DA2, 0x5DA3, 0x5DA4, 0x5DA5, 0x5DA6, 0x5DA7, 0x5DA8, 0x5DA9, + 0x5DAA, 0x5DAB, 0x5DAC, 0x5DAD, 0x5DAE, 0x5DAF, 0x5DB0, 0x5DB1, 0x5DB2, + 0x5DB3, 0x5DB4, 0x5DB5, 0x5DB6, 0x5DB8, 0x5DB9, 0x5DBA, 0x5DBB, 0x5DBC, + 0x5DBD, 0x5DBE, 0x5DBF, 0x5DC0, 0x5DC1, 0x5DC2, 0x5DC3, 0x5DC4, 0x5DC6, + 0x5DC7, 0x5DC8, 0x5DC9, 0x5DCA, 0x5DCB, 0x5DCC, 0x5DCE, 0x5DCF, 0x5DD0, + 0x5DD1, 0x5DD2, 0x5DD3, 0x5DD4, 0x5DD5, 0x5DD6, 0x5DD7, 0x5DD8, 0x5DD9, + 0x5DDA, 0x5DDC, 0x5DDF, 0x5DE0, 0x5DE3, 0x5DE4, 0x5DEA, 0x5DEC, 0x5DED, + 0x003F, 0x5DF0, 0x5DF5, 0x5DF6, 0x5DF8, 0x5DF9, 0x5DFA, 0x5DFB, 0x5DFC, + 0x5DFF, 0x5E00, 0x5E04, 0x5E07, 0x5E09, 0x5E0A, 0x5E0B, 0x5E0D, 0x5E0E, + 0x5E12, 0x5E13, 0x5E17, 0x5E1E, 0x5E1F, 0x5E20, 0x5E21, 0x5E22, 0x5E23, + 0x5E24, 0x5E25, 0x5E28, 0x5E29, 0x5E2A, 0x5E2B, 0x5E2C, 0x5E2F, 0x5E30, + 0x5E32, 0x5E33, 0x5E34, 0x5E35, 0x5E36, 0x5E39, 0x5E3A, 0x5E3E, 0x5E3F, + 0x5E40, 0x5E41, 0x5E43, 0x5E46, 0x5E47, 0x5E48, 0x5E49, 0x5E4A, 0x5E4B, + 0x5E4D, 0x5E4E, 0x5E4F, 0x5E50, 0x5E51, 0x5E52, 0x5E53, 0x5E56, 0x5E57, + 0x5E58, 0x5E59, 0x5E5A, 0x5E5C, 0x5E5D, 0x5E5F, 0x5E60, 0x5E63, 0x5E64, + 0x5E65, 0x5E66, 0x5E67, 0x5E68, 0x5E69, 0x5E6A, 0x5E6B, 0x5E6C, 0x5E6D, + 0x5E6E, 0x5E6F, 0x5E70, 0x5E71, 0x5E75, 0x5E77, 0x5E79, 0x5E7E, 0x5E81, + 0x5E82, 0x5E83, 0x5E85, 0x5E88, 0x5E89, 0x5E8C, 0x5E8D, 0x5E8E, 0x5E92, + 0x5E98, 0x5E9B, 0x5E9D, 0x5EA1, 0x5EA2, 0x5EA3, 0x5EA4, 0x5EA8, 0x5EA9, + 0x5EAA, 0x5EAB, 0x5EAC, 0x5EAE, 0x5EAF, 0x5EB0, 0x5EB1, 0x5EB2, 0x5EB4, + 0x5EBA, 0x5EBB, 0x5EBC, 0x5EBD, 0x5EBF, 0x5EC0, 0x5EC1, 0x5EC2, 0x5EC3, + 0x5EC4, 0x5EC5}, + {0x5EC6, 0x5EC7, 0x5EC8, 0x5ECB, 0x5ECC, 0x5ECD, 0x5ECE, 0x5ECF, 0x5ED0, + 0x5ED4, 0x5ED5, 0x5ED7, 0x5ED8, 0x5ED9, 0x5EDA, 0x5EDC, 0x5EDD, 0x5EDE, + 0x5EDF, 0x5EE0, 0x5EE1, 0x5EE2, 0x5EE3, 0x5EE4, 0x5EE5, 0x5EE6, 0x5EE7, + 0x5EE9, 0x5EEB, 0x5EEC, 0x5EED, 0x5EEE, 0x5EEF, 0x5EF0, 0x5EF1, 0x5EF2, + 0x5EF3, 0x5EF5, 0x5EF8, 0x5EF9, 0x5EFB, 0x5EFC, 0x5EFD, 0x5F05, 0x5F06, + 0x5F07, 0x5F09, 0x5F0C, 0x5F0D, 0x5F0E, 0x5F10, 0x5F12, 0x5F14, 0x5F16, + 0x5F19, 0x5F1A, 0x5F1C, 0x5F1D, 0x5F1E, 0x5F21, 0x5F22, 0x5F23, 0x5F24, + 0x003F, 0x5F28, 0x5F2B, 0x5F2C, 0x5F2E, 0x5F30, 0x5F32, 0x5F33, 0x5F34, + 0x5F35, 0x5F36, 0x5F37, 0x5F38, 0x5F3B, 0x5F3D, 0x5F3E, 0x5F3F, 0x5F41, + 0x5F42, 0x5F43, 0x5F44, 0x5F45, 0x5F46, 0x5F47, 0x5F48, 0x5F49, 0x5F4A, + 0x5F4B, 0x5F4C, 0x5F4D, 0x5F4E, 0x5F4F, 0x5F51, 0x5F54, 0x5F59, 0x5F5A, + 0x5F5B, 0x5F5C, 0x5F5E, 0x5F5F, 0x5F60, 0x5F63, 0x5F65, 0x5F67, 0x5F68, + 0x5F6B, 0x5F6E, 0x5F6F, 0x5F72, 0x5F74, 0x5F75, 0x5F76, 0x5F78, 0x5F7A, + 0x5F7D, 0x5F7E, 0x5F7F, 0x5F83, 0x5F86, 0x5F8D, 0x5F8E, 0x5F8F, 0x5F91, + 0x5F93, 0x5F94, 0x5F96, 0x5F9A, 0x5F9B, 0x5F9D, 0x5F9E, 0x5F9F, 0x5FA0, + 0x5FA2, 0x5FA3, 0x5FA4, 0x5FA5, 0x5FA6, 0x5FA7, 0x5FA9, 0x5FAB, 0x5FAC, + 0x5FAF, 0x5FB0, 0x5FB1, 0x5FB2, 0x5FB3, 0x5FB4, 0x5FB6, 0x5FB8, 0x5FB9, + 0x5FBA, 0x5FBB, 0x5FBE, 0x5FBF, 0x5FC0, 0x5FC1, 0x5FC2, 0x5FC7, 0x5FC8, + 0x5FCA, 0x5FCB, 0x5FCE, 0x5FD3, 0x5FD4, 0x5FD5, 0x5FDA, 0x5FDB, 0x5FDC, + 0x5FDE, 0x5FDF, 0x5FE2, 0x5FE3, 0x5FE5, 0x5FE6, 0x5FE8, 0x5FE9, 0x5FEC, + 0x5FEF, 0x5FF0, 0x5FF2, 0x5FF3, 0x5FF4, 0x5FF6, 0x5FF7, 0x5FF9, 0x5FFA, + 0x5FFC, 0x6007}, + {0x6008, 0x6009, 0x600B, 0x600C, 0x6010, 0x6011, 0x6013, 0x6017, 0x6018, + 0x601A, 0x601E, 0x601F, 0x6022, 0x6023, 0x6024, 0x602C, 0x602D, 0x602E, + 0x6030, 0x6031, 0x6032, 0x6033, 0x6034, 0x6036, 0x6037, 0x6038, 0x6039, + 0x603A, 0x603D, 0x603E, 0x6040, 0x6044, 0x6045, 0x6046, 0x6047, 0x6048, + 0x6049, 0x604A, 0x604C, 0x604E, 0x604F, 0x6051, 0x6053, 0x6054, 0x6056, + 0x6057, 0x6058, 0x605B, 0x605C, 0x605E, 0x605F, 0x6060, 0x6061, 0x6065, + 0x6066, 0x606E, 0x6071, 0x6072, 0x6074, 0x6075, 0x6077, 0x607E, 0x6080, + 0x003F, 0x6081, 0x6082, 0x6085, 0x6086, 0x6087, 0x6088, 0x608A, 0x608B, + 0x608E, 0x608F, 0x6090, 0x6091, 0x6093, 0x6095, 0x6097, 0x6098, 0x6099, + 0x609C, 0x609E, 0x60A1, 0x60A2, 0x60A4, 0x60A5, 0x60A7, 0x60A9, 0x60AA, + 0x60AE, 0x60B0, 0x60B3, 0x60B5, 0x60B6, 0x60B7, 0x60B9, 0x60BA, 0x60BD, + 0x60BE, 0x60BF, 0x60C0, 0x60C1, 0x60C2, 0x60C3, 0x60C4, 0x60C7, 0x60C8, + 0x60C9, 0x60CC, 0x60CD, 0x60CE, 0x60CF, 0x60D0, 0x60D2, 0x60D3, 0x60D4, + 0x60D6, 0x60D7, 0x60D9, 0x60DB, 0x60DE, 0x60E1, 0x60E2, 0x60E3, 0x60E4, + 0x60E5, 0x60EA, 0x60F1, 0x60F2, 0x60F5, 0x60F7, 0x60F8, 0x60FB, 0x60FC, + 0x60FD, 0x60FE, 0x60FF, 0x6102, 0x6103, 0x6104, 0x6105, 0x6107, 0x610A, + 0x610B, 0x610C, 0x6110, 0x6111, 0x6112, 0x6113, 0x6114, 0x6116, 0x6117, + 0x6118, 0x6119, 0x611B, 0x611C, 0x611D, 0x611E, 0x6121, 0x6122, 0x6125, + 0x6128, 0x6129, 0x612A, 0x612C, 0x612D, 0x612E, 0x612F, 0x6130, 0x6131, + 0x6132, 0x6133, 0x6134, 0x6135, 0x6136, 0x6137, 0x6138, 0x6139, 0x613A, + 0x613B, 0x613C, 0x613D, 0x613E, 0x6140, 0x6141, 0x6142, 0x6143, 0x6144, + 0x6145, 0x6146}, + {0x6147, 0x6149, 0x614B, 0x614D, 0x614F, 0x6150, 0x6152, 0x6153, 0x6154, + 0x6156, 0x6157, 0x6158, 0x6159, 0x615A, 0x615B, 0x615C, 0x615E, 0x615F, + 0x6160, 0x6161, 0x6163, 0x6164, 0x6165, 0x6166, 0x6169, 0x616A, 0x616B, + 0x616C, 0x616D, 0x616E, 0x616F, 0x6171, 0x6172, 0x6173, 0x6174, 0x6176, + 0x6178, 0x6179, 0x617A, 0x617B, 0x617C, 0x617D, 0x617E, 0x617F, 0x6180, + 0x6181, 0x6182, 0x6183, 0x6184, 0x6185, 0x6186, 0x6187, 0x6188, 0x6189, + 0x618A, 0x618C, 0x618D, 0x618F, 0x6190, 0x6191, 0x6192, 0x6193, 0x6195, + 0x003F, 0x6196, 0x6197, 0x6198, 0x6199, 0x619A, 0x619B, 0x619C, 0x619E, + 0x619F, 0x61A0, 0x61A1, 0x61A2, 0x61A3, 0x61A4, 0x61A5, 0x61A6, 0x61AA, + 0x61AB, 0x61AD, 0x61AE, 0x61AF, 0x61B0, 0x61B1, 0x61B2, 0x61B3, 0x61B4, + 0x61B5, 0x61B6, 0x61B8, 0x61B9, 0x61BA, 0x61BB, 0x61BC, 0x61BD, 0x61BF, + 0x61C0, 0x61C1, 0x61C3, 0x61C4, 0x61C5, 0x61C6, 0x61C7, 0x61C9, 0x61CC, + 0x61CD, 0x61CE, 0x61CF, 0x61D0, 0x61D3, 0x61D5, 0x61D6, 0x61D7, 0x61D8, + 0x61D9, 0x61DA, 0x61DB, 0x61DC, 0x61DD, 0x61DE, 0x61DF, 0x61E0, 0x61E1, + 0x61E2, 0x61E3, 0x61E4, 0x61E5, 0x61E7, 0x61E8, 0x61E9, 0x61EA, 0x61EB, + 0x61EC, 0x61ED, 0x61EE, 0x61EF, 0x61F0, 0x61F1, 0x61F2, 0x61F3, 0x61F4, + 0x61F6, 0x61F7, 0x61F8, 0x61F9, 0x61FA, 0x61FB, 0x61FC, 0x61FD, 0x61FE, + 0x6200, 0x6201, 0x6202, 0x6203, 0x6204, 0x6205, 0x6207, 0x6209, 0x6213, + 0x6214, 0x6219, 0x621C, 0x621D, 0x621E, 0x6220, 0x6223, 0x6226, 0x6227, + 0x6228, 0x6229, 0x622B, 0x622D, 0x622F, 0x6230, 0x6231, 0x6232, 0x6235, + 0x6236, 0x6238, 0x6239, 0x623A, 0x623B, 0x623C, 0x6242, 0x6244, 0x6245, + 0x6246, 0x624A}, + {0x624F, 0x6250, 0x6255, 0x6256, 0x6257, 0x6259, 0x625A, 0x625C, 0x625D, + 0x625E, 0x625F, 0x6260, 0x6261, 0x6262, 0x6264, 0x6265, 0x6268, 0x6271, + 0x6272, 0x6274, 0x6275, 0x6277, 0x6278, 0x627A, 0x627B, 0x627D, 0x6281, + 0x6282, 0x6283, 0x6285, 0x6286, 0x6287, 0x6288, 0x628B, 0x628C, 0x628D, + 0x628E, 0x628F, 0x6290, 0x6294, 0x6299, 0x629C, 0x629D, 0x629E, 0x62A3, + 0x62A6, 0x62A7, 0x62A9, 0x62AA, 0x62AD, 0x62AE, 0x62AF, 0x62B0, 0x62B2, + 0x62B3, 0x62B4, 0x62B6, 0x62B7, 0x62B8, 0x62BA, 0x62BE, 0x62C0, 0x62C1, + 0x003F, 0x62C3, 0x62CB, 0x62CF, 0x62D1, 0x62D5, 0x62DD, 0x62DE, 0x62E0, + 0x62E1, 0x62E4, 0x62EA, 0x62EB, 0x62F0, 0x62F2, 0x62F5, 0x62F8, 0x62F9, + 0x62FA, 0x62FB, 0x6300, 0x6303, 0x6304, 0x6305, 0x6306, 0x630A, 0x630B, + 0x630C, 0x630D, 0x630F, 0x6310, 0x6312, 0x6313, 0x6314, 0x6315, 0x6317, + 0x6318, 0x6319, 0x631C, 0x6326, 0x6327, 0x6329, 0x632C, 0x632D, 0x632E, + 0x6330, 0x6331, 0x6333, 0x6334, 0x6335, 0x6336, 0x6337, 0x6338, 0x633B, + 0x633C, 0x633E, 0x633F, 0x6340, 0x6341, 0x6344, 0x6347, 0x6348, 0x634A, + 0x6351, 0x6352, 0x6353, 0x6354, 0x6356, 0x6357, 0x6358, 0x6359, 0x635A, + 0x635B, 0x635C, 0x635D, 0x6360, 0x6364, 0x6365, 0x6366, 0x6368, 0x636A, + 0x636B, 0x636C, 0x636F, 0x6370, 0x6372, 0x6373, 0x6374, 0x6375, 0x6378, + 0x6379, 0x637C, 0x637D, 0x637E, 0x637F, 0x6381, 0x6383, 0x6384, 0x6385, + 0x6386, 0x638B, 0x638D, 0x6391, 0x6393, 0x6394, 0x6395, 0x6397, 0x6399, + 0x639A, 0x639B, 0x639C, 0x639D, 0x639E, 0x639F, 0x63A1, 0x63A4, 0x63A6, + 0x63AB, 0x63AF, 0x63B1, 0x63B2, 0x63B5, 0x63B6, 0x63B9, 0x63BB, 0x63BD, + 0x63BF, 0x63C0}, + {0x63C1, 0x63C2, 0x63C3, 0x63C5, 0x63C7, 0x63C8, 0x63CA, 0x63CB, 0x63CC, + 0x63D1, 0x63D3, 0x63D4, 0x63D5, 0x63D7, 0x63D8, 0x63D9, 0x63DA, 0x63DB, + 0x63DC, 0x63DD, 0x63DF, 0x63E2, 0x63E4, 0x63E5, 0x63E6, 0x63E7, 0x63E8, + 0x63EB, 0x63EC, 0x63EE, 0x63EF, 0x63F0, 0x63F1, 0x63F3, 0x63F5, 0x63F7, + 0x63F9, 0x63FA, 0x63FB, 0x63FC, 0x63FE, 0x6403, 0x6404, 0x6406, 0x6407, + 0x6408, 0x6409, 0x640A, 0x640D, 0x640E, 0x6411, 0x6412, 0x6415, 0x6416, + 0x6417, 0x6418, 0x6419, 0x641A, 0x641D, 0x641F, 0x6422, 0x6423, 0x6424, + 0x003F, 0x6425, 0x6427, 0x6428, 0x6429, 0x642B, 0x642E, 0x642F, 0x6430, + 0x6431, 0x6432, 0x6433, 0x6435, 0x6436, 0x6437, 0x6438, 0x6439, 0x643B, + 0x643C, 0x643E, 0x6440, 0x6442, 0x6443, 0x6449, 0x644B, 0x644C, 0x644D, + 0x644E, 0x644F, 0x6450, 0x6451, 0x6453, 0x6455, 0x6456, 0x6457, 0x6459, + 0x645A, 0x645B, 0x645C, 0x645D, 0x645F, 0x6460, 0x6461, 0x6462, 0x6463, + 0x6464, 0x6465, 0x6466, 0x6468, 0x646A, 0x646B, 0x646C, 0x646E, 0x646F, + 0x6470, 0x6471, 0x6472, 0x6473, 0x6474, 0x6475, 0x6476, 0x6477, 0x647B, + 0x647C, 0x647D, 0x647E, 0x647F, 0x6480, 0x6481, 0x6483, 0x6486, 0x6488, + 0x6489, 0x648A, 0x648B, 0x648C, 0x648D, 0x648E, 0x648F, 0x6490, 0x6493, + 0x6494, 0x6497, 0x6498, 0x649A, 0x649B, 0x649C, 0x649D, 0x649F, 0x64A0, + 0x64A1, 0x64A2, 0x64A3, 0x64A5, 0x64A6, 0x64A7, 0x64A8, 0x64AA, 0x64AB, + 0x64AF, 0x64B1, 0x64B2, 0x64B3, 0x64B4, 0x64B6, 0x64B9, 0x64BB, 0x64BD, + 0x64BE, 0x64BF, 0x64C1, 0x64C3, 0x64C4, 0x64C6, 0x64C7, 0x64C8, 0x64C9, + 0x64CA, 0x64CB, 0x64CC, 0x64CF, 0x64D1, 0x64D3, 0x64D4, 0x64D5, 0x64D6, + 0x64D9, 0x64DA}, + {0x64DB, 0x64DC, 0x64DD, 0x64DF, 0x64E0, 0x64E1, 0x64E3, 0x64E5, 0x64E7, + 0x64E8, 0x64E9, 0x64EA, 0x64EB, 0x64EC, 0x64ED, 0x64EE, 0x64EF, 0x64F0, + 0x64F1, 0x64F2, 0x64F3, 0x64F4, 0x64F5, 0x64F6, 0x64F7, 0x64F8, 0x64F9, + 0x64FA, 0x64FB, 0x64FC, 0x64FD, 0x64FE, 0x64FF, 0x6501, 0x6502, 0x6503, + 0x6504, 0x6505, 0x6506, 0x6507, 0x6508, 0x650A, 0x650B, 0x650C, 0x650D, + 0x650E, 0x650F, 0x6510, 0x6511, 0x6513, 0x6514, 0x6515, 0x6516, 0x6517, + 0x6519, 0x651A, 0x651B, 0x651C, 0x651D, 0x651E, 0x651F, 0x6520, 0x6521, + 0x003F, 0x6522, 0x6523, 0x6524, 0x6526, 0x6527, 0x6528, 0x6529, 0x652A, + 0x652C, 0x652D, 0x6530, 0x6531, 0x6532, 0x6533, 0x6537, 0x653A, 0x653C, + 0x653D, 0x6540, 0x6541, 0x6542, 0x6543, 0x6544, 0x6546, 0x6547, 0x654A, + 0x654B, 0x654D, 0x654E, 0x6550, 0x6552, 0x6553, 0x6554, 0x6557, 0x6558, + 0x655A, 0x655C, 0x655F, 0x6560, 0x6561, 0x6564, 0x6565, 0x6567, 0x6568, + 0x6569, 0x656A, 0x656D, 0x656E, 0x656F, 0x6571, 0x6573, 0x6575, 0x6576, + 0x6578, 0x6579, 0x657A, 0x657B, 0x657C, 0x657D, 0x657E, 0x657F, 0x6580, + 0x6581, 0x6582, 0x6583, 0x6584, 0x6585, 0x6586, 0x6588, 0x6589, 0x658A, + 0x658D, 0x658E, 0x658F, 0x6592, 0x6594, 0x6595, 0x6596, 0x6598, 0x659A, + 0x659D, 0x659E, 0x65A0, 0x65A2, 0x65A3, 0x65A6, 0x65A8, 0x65AA, 0x65AC, + 0x65AE, 0x65B1, 0x65B2, 0x65B3, 0x65B4, 0x65B5, 0x65B6, 0x65B7, 0x65B8, + 0x65BA, 0x65BB, 0x65BE, 0x65BF, 0x65C0, 0x65C2, 0x65C7, 0x65C8, 0x65C9, + 0x65CA, 0x65CD, 0x65D0, 0x65D1, 0x65D3, 0x65D4, 0x65D5, 0x65D8, 0x65D9, + 0x65DA, 0x65DB, 0x65DC, 0x65DD, 0x65DE, 0x65DF, 0x65E1, 0x65E3, 0x65E4, + 0x65EA, 0x65EB}, + {0x65F2, 0x65F3, 0x65F4, 0x65F5, 0x65F8, 0x65F9, 0x65FB, 0x65FC, 0x65FD, + 0x65FE, 0x65FF, 0x6601, 0x6604, 0x6605, 0x6607, 0x6608, 0x6609, 0x660B, + 0x660D, 0x6610, 0x6611, 0x6612, 0x6616, 0x6617, 0x6618, 0x661A, 0x661B, + 0x661C, 0x661E, 0x6621, 0x6622, 0x6623, 0x6624, 0x6626, 0x6629, 0x662A, + 0x662B, 0x662C, 0x662E, 0x6630, 0x6632, 0x6633, 0x6637, 0x6638, 0x6639, + 0x663A, 0x663B, 0x663D, 0x663F, 0x6640, 0x6642, 0x6644, 0x6645, 0x6646, + 0x6647, 0x6648, 0x6649, 0x664A, 0x664D, 0x664E, 0x6650, 0x6651, 0x6658, + 0x003F, 0x6659, 0x665B, 0x665C, 0x665D, 0x665E, 0x6660, 0x6662, 0x6663, + 0x6665, 0x6667, 0x6669, 0x666A, 0x666B, 0x666C, 0x666D, 0x6671, 0x6672, + 0x6673, 0x6675, 0x6678, 0x6679, 0x667B, 0x667C, 0x667D, 0x667F, 0x6680, + 0x6681, 0x6683, 0x6685, 0x6686, 0x6688, 0x6689, 0x668A, 0x668B, 0x668D, + 0x668E, 0x668F, 0x6690, 0x6692, 0x6693, 0x6694, 0x6695, 0x6698, 0x6699, + 0x669A, 0x669B, 0x669C, 0x669E, 0x669F, 0x66A0, 0x66A1, 0x66A2, 0x66A3, + 0x66A4, 0x66A5, 0x66A6, 0x66A9, 0x66AA, 0x66AB, 0x66AC, 0x66AD, 0x66AF, + 0x66B0, 0x66B1, 0x66B2, 0x66B3, 0x66B5, 0x66B6, 0x66B7, 0x66B8, 0x66BA, + 0x66BB, 0x66BC, 0x66BD, 0x66BF, 0x66C0, 0x66C1, 0x66C2, 0x66C3, 0x66C4, + 0x66C5, 0x66C6, 0x66C7, 0x66C8, 0x66C9, 0x66CA, 0x66CB, 0x66CC, 0x66CD, + 0x66CE, 0x66CF, 0x66D0, 0x66D1, 0x66D2, 0x66D3, 0x66D4, 0x66D5, 0x66D6, + 0x66D7, 0x66D8, 0x66DA, 0x66DE, 0x66DF, 0x66E0, 0x66E1, 0x66E2, 0x66E3, + 0x66E4, 0x66E5, 0x66E7, 0x66E8, 0x66EA, 0x66EB, 0x66EC, 0x66ED, 0x66EE, + 0x66EF, 0x66F1, 0x66F5, 0x66F6, 0x66F8, 0x66FA, 0x66FB, 0x66FD, 0x6701, + 0x6702, 0x6703}, + {0x6704, 0x6705, 0x6706, 0x6707, 0x670C, 0x670E, 0x670F, 0x6711, 0x6712, + 0x6713, 0x6716, 0x6718, 0x6719, 0x671A, 0x671C, 0x671E, 0x6720, 0x6721, + 0x6722, 0x6723, 0x6724, 0x6725, 0x6727, 0x6729, 0x672E, 0x6730, 0x6732, + 0x6733, 0x6736, 0x6737, 0x6738, 0x6739, 0x673B, 0x673C, 0x673E, 0x673F, + 0x6741, 0x6744, 0x6745, 0x6747, 0x674A, 0x674B, 0x674D, 0x6752, 0x6754, + 0x6755, 0x6757, 0x6758, 0x6759, 0x675A, 0x675B, 0x675D, 0x6762, 0x6763, + 0x6764, 0x6766, 0x6767, 0x676B, 0x676C, 0x676E, 0x6771, 0x6774, 0x6776, + 0x003F, 0x6778, 0x6779, 0x677A, 0x677B, 0x677D, 0x6780, 0x6782, 0x6783, + 0x6785, 0x6786, 0x6788, 0x678A, 0x678C, 0x678D, 0x678E, 0x678F, 0x6791, + 0x6792, 0x6793, 0x6794, 0x6796, 0x6799, 0x679B, 0x679F, 0x67A0, 0x67A1, + 0x67A4, 0x67A6, 0x67A9, 0x67AC, 0x67AE, 0x67B1, 0x67B2, 0x67B4, 0x67B9, + 0x67BA, 0x67BB, 0x67BC, 0x67BD, 0x67BE, 0x67BF, 0x67C0, 0x67C2, 0x67C5, + 0x67C6, 0x67C7, 0x67C8, 0x67C9, 0x67CA, 0x67CB, 0x67CC, 0x67CD, 0x67CE, + 0x67D5, 0x67D6, 0x67D7, 0x67DB, 0x67DF, 0x67E1, 0x67E3, 0x67E4, 0x67E6, + 0x67E7, 0x67E8, 0x67EA, 0x67EB, 0x67ED, 0x67EE, 0x67F2, 0x67F5, 0x67F6, + 0x67F7, 0x67F8, 0x67F9, 0x67FA, 0x67FB, 0x67FC, 0x67FE, 0x6801, 0x6802, + 0x6803, 0x6804, 0x6806, 0x680D, 0x6810, 0x6812, 0x6814, 0x6815, 0x6818, + 0x6819, 0x681A, 0x681B, 0x681C, 0x681E, 0x681F, 0x6820, 0x6822, 0x6823, + 0x6824, 0x6825, 0x6826, 0x6827, 0x6828, 0x682B, 0x682C, 0x682D, 0x682E, + 0x682F, 0x6830, 0x6831, 0x6834, 0x6835, 0x6836, 0x683A, 0x683B, 0x683F, + 0x6847, 0x684B, 0x684D, 0x684F, 0x6852, 0x6856, 0x6857, 0x6858, 0x6859, + 0x685A, 0x685B}, + {0x685C, 0x685D, 0x685E, 0x685F, 0x686A, 0x686C, 0x686D, 0x686E, 0x686F, + 0x6870, 0x6871, 0x6872, 0x6873, 0x6875, 0x6878, 0x6879, 0x687A, 0x687B, + 0x687C, 0x687D, 0x687E, 0x687F, 0x6880, 0x6882, 0x6884, 0x6887, 0x6888, + 0x6889, 0x688A, 0x688B, 0x688C, 0x688D, 0x688E, 0x6890, 0x6891, 0x6892, + 0x6894, 0x6895, 0x6896, 0x6898, 0x6899, 0x689A, 0x689B, 0x689C, 0x689D, + 0x689E, 0x689F, 0x68A0, 0x68A1, 0x68A3, 0x68A4, 0x68A5, 0x68A9, 0x68AA, + 0x68AB, 0x68AC, 0x68AE, 0x68B1, 0x68B2, 0x68B4, 0x68B6, 0x68B7, 0x68B8, + 0x003F, 0x68B9, 0x68BA, 0x68BB, 0x68BC, 0x68BD, 0x68BE, 0x68BF, 0x68C1, + 0x68C3, 0x68C4, 0x68C5, 0x68C6, 0x68C7, 0x68C8, 0x68CA, 0x68CC, 0x68CE, + 0x68CF, 0x68D0, 0x68D1, 0x68D3, 0x68D4, 0x68D6, 0x68D7, 0x68D9, 0x68DB, + 0x68DC, 0x68DD, 0x68DE, 0x68DF, 0x68E1, 0x68E2, 0x68E4, 0x68E5, 0x68E6, + 0x68E7, 0x68E8, 0x68E9, 0x68EA, 0x68EB, 0x68EC, 0x68ED, 0x68EF, 0x68F2, + 0x68F3, 0x68F4, 0x68F6, 0x68F7, 0x68F8, 0x68FB, 0x68FD, 0x68FE, 0x68FF, + 0x6900, 0x6902, 0x6903, 0x6904, 0x6906, 0x6907, 0x6908, 0x6909, 0x690A, + 0x690C, 0x690F, 0x6911, 0x6913, 0x6914, 0x6915, 0x6916, 0x6917, 0x6918, + 0x6919, 0x691A, 0x691B, 0x691C, 0x691D, 0x691E, 0x6921, 0x6922, 0x6923, + 0x6925, 0x6926, 0x6927, 0x6928, 0x6929, 0x692A, 0x692B, 0x692C, 0x692E, + 0x692F, 0x6931, 0x6932, 0x6933, 0x6935, 0x6936, 0x6937, 0x6938, 0x693A, + 0x693B, 0x693C, 0x693E, 0x6940, 0x6941, 0x6943, 0x6944, 0x6945, 0x6946, + 0x6947, 0x6948, 0x6949, 0x694A, 0x694B, 0x694C, 0x694D, 0x694E, 0x694F, + 0x6950, 0x6951, 0x6952, 0x6953, 0x6955, 0x6956, 0x6958, 0x6959, 0x695B, + 0x695C, 0x695F}, + {0x6961, 0x6962, 0x6964, 0x6965, 0x6967, 0x6968, 0x6969, 0x696A, 0x696C, + 0x696D, 0x696F, 0x6970, 0x6972, 0x6973, 0x6974, 0x6975, 0x6976, 0x697A, + 0x697B, 0x697D, 0x697E, 0x697F, 0x6981, 0x6983, 0x6985, 0x698A, 0x698B, + 0x698C, 0x698E, 0x698F, 0x6990, 0x6991, 0x6992, 0x6993, 0x6996, 0x6997, + 0x6999, 0x699A, 0x699D, 0x699E, 0x699F, 0x69A0, 0x69A1, 0x69A2, 0x69A3, + 0x69A4, 0x69A5, 0x69A6, 0x69A9, 0x69AA, 0x69AC, 0x69AE, 0x69AF, 0x69B0, + 0x69B2, 0x69B3, 0x69B5, 0x69B6, 0x69B8, 0x69B9, 0x69BA, 0x69BC, 0x69BD, + 0x003F, 0x69BE, 0x69BF, 0x69C0, 0x69C2, 0x69C3, 0x69C4, 0x69C5, 0x69C6, + 0x69C7, 0x69C8, 0x69C9, 0x69CB, 0x69CD, 0x69CF, 0x69D1, 0x69D2, 0x69D3, + 0x69D5, 0x69D6, 0x69D7, 0x69D8, 0x69D9, 0x69DA, 0x69DC, 0x69DD, 0x69DE, + 0x69E1, 0x69E2, 0x69E3, 0x69E4, 0x69E5, 0x69E6, 0x69E7, 0x69E8, 0x69E9, + 0x69EA, 0x69EB, 0x69EC, 0x69EE, 0x69EF, 0x69F0, 0x69F1, 0x69F3, 0x69F4, + 0x69F5, 0x69F6, 0x69F7, 0x69F8, 0x69F9, 0x69FA, 0x69FB, 0x69FC, 0x69FE, + 0x6A00, 0x6A01, 0x6A02, 0x6A03, 0x6A04, 0x6A05, 0x6A06, 0x6A07, 0x6A08, + 0x6A09, 0x6A0B, 0x6A0C, 0x6A0D, 0x6A0E, 0x6A0F, 0x6A10, 0x6A11, 0x6A12, + 0x6A13, 0x6A14, 0x6A15, 0x6A16, 0x6A19, 0x6A1A, 0x6A1B, 0x6A1C, 0x6A1D, + 0x6A1E, 0x6A20, 0x6A22, 0x6A23, 0x6A24, 0x6A25, 0x6A26, 0x6A27, 0x6A29, + 0x6A2B, 0x6A2C, 0x6A2D, 0x6A2E, 0x6A30, 0x6A32, 0x6A33, 0x6A34, 0x6A36, + 0x6A37, 0x6A38, 0x6A39, 0x6A3A, 0x6A3B, 0x6A3C, 0x6A3F, 0x6A40, 0x6A41, + 0x6A42, 0x6A43, 0x6A45, 0x6A46, 0x6A48, 0x6A49, 0x6A4A, 0x6A4B, 0x6A4C, + 0x6A4D, 0x6A4E, 0x6A4F, 0x6A51, 0x6A52, 0x6A53, 0x6A54, 0x6A55, 0x6A56, + 0x6A57, 0x6A5A}, + {0x6A5C, 0x6A5D, 0x6A5E, 0x6A5F, 0x6A60, 0x6A62, 0x6A63, 0x6A64, 0x6A66, + 0x6A67, 0x6A68, 0x6A69, 0x6A6A, 0x6A6B, 0x6A6C, 0x6A6D, 0x6A6E, 0x6A6F, + 0x6A70, 0x6A72, 0x6A73, 0x6A74, 0x6A75, 0x6A76, 0x6A77, 0x6A78, 0x6A7A, + 0x6A7B, 0x6A7D, 0x6A7E, 0x6A7F, 0x6A81, 0x6A82, 0x6A83, 0x6A85, 0x6A86, + 0x6A87, 0x6A88, 0x6A89, 0x6A8A, 0x6A8B, 0x6A8C, 0x6A8D, 0x6A8F, 0x6A92, + 0x6A93, 0x6A94, 0x6A95, 0x6A96, 0x6A98, 0x6A99, 0x6A9A, 0x6A9B, 0x6A9C, + 0x6A9D, 0x6A9E, 0x6A9F, 0x6AA1, 0x6AA2, 0x6AA3, 0x6AA4, 0x6AA5, 0x6AA6, + 0x003F, 0x6AA7, 0x6AA8, 0x6AAA, 0x6AAD, 0x6AAE, 0x6AAF, 0x6AB0, 0x6AB1, + 0x6AB2, 0x6AB3, 0x6AB4, 0x6AB5, 0x6AB6, 0x6AB7, 0x6AB8, 0x6AB9, 0x6ABA, + 0x6ABB, 0x6ABC, 0x6ABD, 0x6ABE, 0x6ABF, 0x6AC0, 0x6AC1, 0x6AC2, 0x6AC3, + 0x6AC4, 0x6AC5, 0x6AC6, 0x6AC7, 0x6AC8, 0x6AC9, 0x6ACA, 0x6ACB, 0x6ACC, + 0x6ACD, 0x6ACE, 0x6ACF, 0x6AD0, 0x6AD1, 0x6AD2, 0x6AD3, 0x6AD4, 0x6AD5, + 0x6AD6, 0x6AD7, 0x6AD8, 0x6AD9, 0x6ADA, 0x6ADB, 0x6ADC, 0x6ADD, 0x6ADE, + 0x6ADF, 0x6AE0, 0x6AE1, 0x6AE2, 0x6AE3, 0x6AE4, 0x6AE5, 0x6AE6, 0x6AE7, + 0x6AE8, 0x6AE9, 0x6AEA, 0x6AEB, 0x6AEC, 0x6AED, 0x6AEE, 0x6AEF, 0x6AF0, + 0x6AF1, 0x6AF2, 0x6AF3, 0x6AF4, 0x6AF5, 0x6AF6, 0x6AF7, 0x6AF8, 0x6AF9, + 0x6AFA, 0x6AFB, 0x6AFC, 0x6AFD, 0x6AFE, 0x6AFF, 0x6B00, 0x6B01, 0x6B02, + 0x6B03, 0x6B04, 0x6B05, 0x6B06, 0x6B07, 0x6B08, 0x6B09, 0x6B0A, 0x6B0B, + 0x6B0C, 0x6B0D, 0x6B0E, 0x6B0F, 0x6B10, 0x6B11, 0x6B12, 0x6B13, 0x6B14, + 0x6B15, 0x6B16, 0x6B17, 0x6B18, 0x6B19, 0x6B1A, 0x6B1B, 0x6B1C, 0x6B1D, + 0x6B1E, 0x6B1F, 0x6B25, 0x6B26, 0x6B28, 0x6B29, 0x6B2A, 0x6B2B, 0x6B2C, + 0x6B2D, 0x6B2E}, + {0x6B2F, 0x6B30, 0x6B31, 0x6B33, 0x6B34, 0x6B35, 0x6B36, 0x6B38, 0x6B3B, + 0x6B3C, 0x6B3D, 0x6B3F, 0x6B40, 0x6B41, 0x6B42, 0x6B44, 0x6B45, 0x6B48, + 0x6B4A, 0x6B4B, 0x6B4D, 0x6B4E, 0x6B4F, 0x6B50, 0x6B51, 0x6B52, 0x6B53, + 0x6B54, 0x6B55, 0x6B56, 0x6B57, 0x6B58, 0x6B5A, 0x6B5B, 0x6B5C, 0x6B5D, + 0x6B5E, 0x6B5F, 0x6B60, 0x6B61, 0x6B68, 0x6B69, 0x6B6B, 0x6B6C, 0x6B6D, + 0x6B6E, 0x6B6F, 0x6B70, 0x6B71, 0x6B72, 0x6B73, 0x6B74, 0x6B75, 0x6B76, + 0x6B77, 0x6B78, 0x6B7A, 0x6B7D, 0x6B7E, 0x6B7F, 0x6B80, 0x6B85, 0x6B88, + 0x003F, 0x6B8C, 0x6B8E, 0x6B8F, 0x6B90, 0x6B91, 0x6B94, 0x6B95, 0x6B97, + 0x6B98, 0x6B99, 0x6B9C, 0x6B9D, 0x6B9E, 0x6B9F, 0x6BA0, 0x6BA2, 0x6BA3, + 0x6BA4, 0x6BA5, 0x6BA6, 0x6BA7, 0x6BA8, 0x6BA9, 0x6BAB, 0x6BAC, 0x6BAD, + 0x6BAE, 0x6BAF, 0x6BB0, 0x6BB1, 0x6BB2, 0x6BB6, 0x6BB8, 0x6BB9, 0x6BBA, + 0x6BBB, 0x6BBC, 0x6BBD, 0x6BBE, 0x6BC0, 0x6BC3, 0x6BC4, 0x6BC6, 0x6BC7, + 0x6BC8, 0x6BC9, 0x6BCA, 0x6BCC, 0x6BCE, 0x6BD0, 0x6BD1, 0x6BD8, 0x6BDA, + 0x6BDC, 0x6BDD, 0x6BDE, 0x6BDF, 0x6BE0, 0x6BE2, 0x6BE3, 0x6BE4, 0x6BE5, + 0x6BE6, 0x6BE7, 0x6BE8, 0x6BE9, 0x6BEC, 0x6BED, 0x6BEE, 0x6BF0, 0x6BF1, + 0x6BF2, 0x6BF4, 0x6BF6, 0x6BF7, 0x6BF8, 0x6BFA, 0x6BFB, 0x6BFC, 0x6BFE, + 0x6BFF, 0x6C00, 0x6C01, 0x6C02, 0x6C03, 0x6C04, 0x6C08, 0x6C09, 0x6C0A, + 0x6C0B, 0x6C0C, 0x6C0E, 0x6C12, 0x6C17, 0x6C1C, 0x6C1D, 0x6C1E, 0x6C20, + 0x6C23, 0x6C25, 0x6C2B, 0x6C2C, 0x6C2D, 0x6C31, 0x6C33, 0x6C36, 0x6C37, + 0x6C39, 0x6C3A, 0x6C3B, 0x6C3C, 0x6C3E, 0x6C3F, 0x6C43, 0x6C44, 0x6C45, + 0x6C48, 0x6C4B, 0x6C4C, 0x6C4D, 0x6C4E, 0x6C4F, 0x6C51, 0x6C52, 0x6C53, + 0x6C56, 0x6C58}, + {0x6C59, 0x6C5A, 0x6C62, 0x6C63, 0x6C65, 0x6C66, 0x6C67, 0x6C6B, 0x6C6C, + 0x6C6D, 0x6C6E, 0x6C6F, 0x6C71, 0x6C73, 0x6C75, 0x6C77, 0x6C78, 0x6C7A, + 0x6C7B, 0x6C7C, 0x6C7F, 0x6C80, 0x6C84, 0x6C87, 0x6C8A, 0x6C8B, 0x6C8D, + 0x6C8E, 0x6C91, 0x6C92, 0x6C95, 0x6C96, 0x6C97, 0x6C98, 0x6C9A, 0x6C9C, + 0x6C9D, 0x6C9E, 0x6CA0, 0x6CA2, 0x6CA8, 0x6CAC, 0x6CAF, 0x6CB0, 0x6CB4, + 0x6CB5, 0x6CB6, 0x6CB7, 0x6CBA, 0x6CC0, 0x6CC1, 0x6CC2, 0x6CC3, 0x6CC6, + 0x6CC7, 0x6CC8, 0x6CCB, 0x6CCD, 0x6CCE, 0x6CCF, 0x6CD1, 0x6CD2, 0x6CD8, + 0x003F, 0x6CD9, 0x6CDA, 0x6CDC, 0x6CDD, 0x6CDF, 0x6CE4, 0x6CE6, 0x6CE7, + 0x6CE9, 0x6CEC, 0x6CED, 0x6CF2, 0x6CF4, 0x6CF9, 0x6CFF, 0x6D00, 0x6D02, + 0x6D03, 0x6D05, 0x6D06, 0x6D08, 0x6D09, 0x6D0A, 0x6D0D, 0x6D0F, 0x6D10, + 0x6D11, 0x6D13, 0x6D14, 0x6D15, 0x6D16, 0x6D18, 0x6D1C, 0x6D1D, 0x6D1F, + 0x6D20, 0x6D21, 0x6D22, 0x6D23, 0x6D24, 0x6D26, 0x6D28, 0x6D29, 0x6D2C, + 0x6D2D, 0x6D2F, 0x6D30, 0x6D34, 0x6D36, 0x6D37, 0x6D38, 0x6D3A, 0x6D3F, + 0x6D40, 0x6D42, 0x6D44, 0x6D49, 0x6D4C, 0x6D50, 0x6D55, 0x6D56, 0x6D57, + 0x6D58, 0x6D5B, 0x6D5D, 0x6D5F, 0x6D61, 0x6D62, 0x6D64, 0x6D65, 0x6D67, + 0x6D68, 0x6D6B, 0x6D6C, 0x6D6D, 0x6D70, 0x6D71, 0x6D72, 0x6D73, 0x6D75, + 0x6D76, 0x6D79, 0x6D7A, 0x6D7B, 0x6D7D, 0x6D7E, 0x6D7F, 0x6D80, 0x6D81, + 0x6D83, 0x6D84, 0x6D86, 0x6D87, 0x6D8A, 0x6D8B, 0x6D8D, 0x6D8F, 0x6D90, + 0x6D92, 0x6D96, 0x6D97, 0x6D98, 0x6D99, 0x6D9A, 0x6D9C, 0x6DA2, 0x6DA5, + 0x6DAC, 0x6DAD, 0x6DB0, 0x6DB1, 0x6DB3, 0x6DB4, 0x6DB6, 0x6DB7, 0x6DB9, + 0x6DBA, 0x6DBB, 0x6DBC, 0x6DBD, 0x6DBE, 0x6DC1, 0x6DC2, 0x6DC3, 0x6DC8, + 0x6DC9, 0x6DCA}, + {0x6DCD, 0x6DCE, 0x6DCF, 0x6DD0, 0x6DD2, 0x6DD3, 0x6DD4, 0x6DD5, 0x6DD7, + 0x6DDA, 0x6DDB, 0x6DDC, 0x6DDF, 0x6DE2, 0x6DE3, 0x6DE5, 0x6DE7, 0x6DE8, + 0x6DE9, 0x6DEA, 0x6DED, 0x6DEF, 0x6DF0, 0x6DF2, 0x6DF4, 0x6DF5, 0x6DF6, + 0x6DF8, 0x6DFA, 0x6DFD, 0x6DFE, 0x6DFF, 0x6E00, 0x6E01, 0x6E02, 0x6E03, + 0x6E04, 0x6E06, 0x6E07, 0x6E08, 0x6E09, 0x6E0B, 0x6E0F, 0x6E12, 0x6E13, + 0x6E15, 0x6E18, 0x6E19, 0x6E1B, 0x6E1C, 0x6E1E, 0x6E1F, 0x6E22, 0x6E26, + 0x6E27, 0x6E28, 0x6E2A, 0x6E2C, 0x6E2E, 0x6E30, 0x6E31, 0x6E33, 0x6E35, + 0x003F, 0x6E36, 0x6E37, 0x6E39, 0x6E3B, 0x6E3C, 0x6E3D, 0x6E3E, 0x6E3F, + 0x6E40, 0x6E41, 0x6E42, 0x6E45, 0x6E46, 0x6E47, 0x6E48, 0x6E49, 0x6E4A, + 0x6E4B, 0x6E4C, 0x6E4F, 0x6E50, 0x6E51, 0x6E52, 0x6E55, 0x6E57, 0x6E59, + 0x6E5A, 0x6E5C, 0x6E5D, 0x6E5E, 0x6E60, 0x6E61, 0x6E62, 0x6E63, 0x6E64, + 0x6E65, 0x6E66, 0x6E67, 0x6E68, 0x6E69, 0x6E6A, 0x6E6C, 0x6E6D, 0x6E6F, + 0x6E70, 0x6E71, 0x6E72, 0x6E73, 0x6E74, 0x6E75, 0x6E76, 0x6E77, 0x6E78, + 0x6E79, 0x6E7A, 0x6E7B, 0x6E7C, 0x6E7D, 0x6E80, 0x6E81, 0x6E82, 0x6E84, + 0x6E87, 0x6E88, 0x6E8A, 0x6E8B, 0x6E8C, 0x6E8D, 0x6E8E, 0x6E91, 0x6E92, + 0x6E93, 0x6E94, 0x6E95, 0x6E96, 0x6E97, 0x6E99, 0x6E9A, 0x6E9B, 0x6E9D, + 0x6E9E, 0x6EA0, 0x6EA1, 0x6EA3, 0x6EA4, 0x6EA6, 0x6EA8, 0x6EA9, 0x6EAB, + 0x6EAC, 0x6EAD, 0x6EAE, 0x6EB0, 0x6EB3, 0x6EB5, 0x6EB8, 0x6EB9, 0x6EBC, + 0x6EBE, 0x6EBF, 0x6EC0, 0x6EC3, 0x6EC4, 0x6EC5, 0x6EC6, 0x6EC8, 0x6EC9, + 0x6ECA, 0x6ECC, 0x6ECD, 0x6ECE, 0x6ED0, 0x6ED2, 0x6ED6, 0x6ED8, 0x6ED9, + 0x6EDB, 0x6EDC, 0x6EDD, 0x6EE3, 0x6EE7, 0x6EEA, 0x6EEB, 0x6EEC, 0x6EED, + 0x6EEE, 0x6EEF}, + {0x6EF0, 0x6EF1, 0x6EF2, 0x6EF3, 0x6EF5, 0x6EF6, 0x6EF7, 0x6EF8, 0x6EFA, + 0x6EFB, 0x6EFC, 0x6EFD, 0x6EFE, 0x6EFF, 0x6F00, 0x6F01, 0x6F03, 0x6F04, + 0x6F05, 0x6F07, 0x6F08, 0x6F0A, 0x6F0B, 0x6F0C, 0x6F0D, 0x6F0E, 0x6F10, + 0x6F11, 0x6F12, 0x6F16, 0x6F17, 0x6F18, 0x6F19, 0x6F1A, 0x6F1B, 0x6F1C, + 0x6F1D, 0x6F1E, 0x6F1F, 0x6F21, 0x6F22, 0x6F23, 0x6F25, 0x6F26, 0x6F27, + 0x6F28, 0x6F2C, 0x6F2E, 0x6F30, 0x6F32, 0x6F34, 0x6F35, 0x6F37, 0x6F38, + 0x6F39, 0x6F3A, 0x6F3B, 0x6F3C, 0x6F3D, 0x6F3F, 0x6F40, 0x6F41, 0x6F42, + 0x003F, 0x6F43, 0x6F44, 0x6F45, 0x6F48, 0x6F49, 0x6F4A, 0x6F4C, 0x6F4E, + 0x6F4F, 0x6F50, 0x6F51, 0x6F52, 0x6F53, 0x6F54, 0x6F55, 0x6F56, 0x6F57, + 0x6F59, 0x6F5A, 0x6F5B, 0x6F5D, 0x6F5F, 0x6F60, 0x6F61, 0x6F63, 0x6F64, + 0x6F65, 0x6F67, 0x6F68, 0x6F69, 0x6F6A, 0x6F6B, 0x6F6C, 0x6F6F, 0x6F70, + 0x6F71, 0x6F73, 0x6F75, 0x6F76, 0x6F77, 0x6F79, 0x6F7B, 0x6F7D, 0x6F7E, + 0x6F7F, 0x6F80, 0x6F81, 0x6F82, 0x6F83, 0x6F85, 0x6F86, 0x6F87, 0x6F8A, + 0x6F8B, 0x6F8F, 0x6F90, 0x6F91, 0x6F92, 0x6F93, 0x6F94, 0x6F95, 0x6F96, + 0x6F97, 0x6F98, 0x6F99, 0x6F9A, 0x6F9B, 0x6F9D, 0x6F9E, 0x6F9F, 0x6FA0, + 0x6FA2, 0x6FA3, 0x6FA4, 0x6FA5, 0x6FA6, 0x6FA8, 0x6FA9, 0x6FAA, 0x6FAB, + 0x6FAC, 0x6FAD, 0x6FAE, 0x6FAF, 0x6FB0, 0x6FB1, 0x6FB2, 0x6FB4, 0x6FB5, + 0x6FB7, 0x6FB8, 0x6FBA, 0x6FBB, 0x6FBC, 0x6FBD, 0x6FBE, 0x6FBF, 0x6FC1, + 0x6FC3, 0x6FC4, 0x6FC5, 0x6FC6, 0x6FC7, 0x6FC8, 0x6FCA, 0x6FCB, 0x6FCC, + 0x6FCD, 0x6FCE, 0x6FCF, 0x6FD0, 0x6FD3, 0x6FD4, 0x6FD5, 0x6FD6, 0x6FD7, + 0x6FD8, 0x6FD9, 0x6FDA, 0x6FDB, 0x6FDC, 0x6FDD, 0x6FDF, 0x6FE2, 0x6FE3, + 0x6FE4, 0x6FE5}, + {0x6FE6, 0x6FE7, 0x6FE8, 0x6FE9, 0x6FEA, 0x6FEB, 0x6FEC, 0x6FED, 0x6FF0, + 0x6FF1, 0x6FF2, 0x6FF3, 0x6FF4, 0x6FF5, 0x6FF6, 0x6FF7, 0x6FF8, 0x6FF9, + 0x6FFA, 0x6FFB, 0x6FFC, 0x6FFD, 0x6FFE, 0x6FFF, 0x7000, 0x7001, 0x7002, + 0x7003, 0x7004, 0x7005, 0x7006, 0x7007, 0x7008, 0x7009, 0x700A, 0x700B, + 0x700C, 0x700D, 0x700E, 0x700F, 0x7010, 0x7012, 0x7013, 0x7014, 0x7015, + 0x7016, 0x7017, 0x7018, 0x7019, 0x701C, 0x701D, 0x701E, 0x701F, 0x7020, + 0x7021, 0x7022, 0x7024, 0x7025, 0x7026, 0x7027, 0x7028, 0x7029, 0x702A, + 0x003F, 0x702B, 0x702C, 0x702D, 0x702E, 0x702F, 0x7030, 0x7031, 0x7032, + 0x7033, 0x7034, 0x7036, 0x7037, 0x7038, 0x703A, 0x703B, 0x703C, 0x703D, + 0x703E, 0x703F, 0x7040, 0x7041, 0x7042, 0x7043, 0x7044, 0x7045, 0x7046, + 0x7047, 0x7048, 0x7049, 0x704A, 0x704B, 0x704D, 0x704E, 0x7050, 0x7051, + 0x7052, 0x7053, 0x7054, 0x7055, 0x7056, 0x7057, 0x7058, 0x7059, 0x705A, + 0x705B, 0x705C, 0x705D, 0x705F, 0x7060, 0x7061, 0x7062, 0x7063, 0x7064, + 0x7065, 0x7066, 0x7067, 0x7068, 0x7069, 0x706A, 0x706E, 0x7071, 0x7072, + 0x7073, 0x7074, 0x7077, 0x7079, 0x707A, 0x707B, 0x707D, 0x7081, 0x7082, + 0x7083, 0x7084, 0x7086, 0x7087, 0x7088, 0x708B, 0x708C, 0x708D, 0x708F, + 0x7090, 0x7091, 0x7093, 0x7097, 0x7098, 0x709A, 0x709B, 0x709E, 0x709F, + 0x70A0, 0x70A1, 0x70A2, 0x70A3, 0x70A4, 0x70A5, 0x70A6, 0x70A7, 0x70A8, + 0x70A9, 0x70AA, 0x70B0, 0x70B2, 0x70B4, 0x70B5, 0x70B6, 0x70BA, 0x70BE, + 0x70BF, 0x70C4, 0x70C5, 0x70C6, 0x70C7, 0x70C9, 0x70CB, 0x70CC, 0x70CD, + 0x70CE, 0x70CF, 0x70D0, 0x70D1, 0x70D2, 0x70D3, 0x70D4, 0x70D5, 0x70D6, + 0x70D7, 0x70DA}, + {0x70DC, 0x70DD, 0x70DE, 0x70E0, 0x70E1, 0x70E2, 0x70E3, 0x70E5, 0x70EA, + 0x70EE, 0x70F0, 0x70F1, 0x70F2, 0x70F3, 0x70F4, 0x70F5, 0x70F6, 0x70F8, + 0x70FA, 0x70FB, 0x70FC, 0x70FE, 0x70FF, 0x7100, 0x7101, 0x7102, 0x7103, + 0x7104, 0x7105, 0x7106, 0x7107, 0x7108, 0x710B, 0x710C, 0x710D, 0x710E, + 0x710F, 0x7111, 0x7112, 0x7114, 0x7117, 0x711B, 0x711C, 0x711D, 0x711E, + 0x711F, 0x7120, 0x7121, 0x7122, 0x7123, 0x7124, 0x7125, 0x7127, 0x7128, + 0x7129, 0x712A, 0x712B, 0x712C, 0x712D, 0x712E, 0x7132, 0x7133, 0x7134, + 0x003F, 0x7135, 0x7137, 0x7138, 0x7139, 0x713A, 0x713B, 0x713C, 0x713D, + 0x713E, 0x713F, 0x7140, 0x7141, 0x7142, 0x7143, 0x7144, 0x7146, 0x7147, + 0x7148, 0x7149, 0x714B, 0x714D, 0x714F, 0x7150, 0x7151, 0x7152, 0x7153, + 0x7154, 0x7155, 0x7156, 0x7157, 0x7158, 0x7159, 0x715A, 0x715B, 0x715D, + 0x715F, 0x7160, 0x7161, 0x7162, 0x7163, 0x7165, 0x7169, 0x716A, 0x716B, + 0x716C, 0x716D, 0x716F, 0x7170, 0x7171, 0x7174, 0x7175, 0x7176, 0x7177, + 0x7179, 0x717B, 0x717C, 0x717E, 0x717F, 0x7180, 0x7181, 0x7182, 0x7183, + 0x7185, 0x7186, 0x7187, 0x7188, 0x7189, 0x718B, 0x718C, 0x718D, 0x718E, + 0x7190, 0x7191, 0x7192, 0x7193, 0x7195, 0x7196, 0x7197, 0x719A, 0x719B, + 0x719C, 0x719D, 0x719E, 0x71A1, 0x71A2, 0x71A3, 0x71A4, 0x71A5, 0x71A6, + 0x71A7, 0x71A9, 0x71AA, 0x71AB, 0x71AD, 0x71AE, 0x71AF, 0x71B0, 0x71B1, + 0x71B2, 0x71B4, 0x71B6, 0x71B7, 0x71B8, 0x71BA, 0x71BB, 0x71BC, 0x71BD, + 0x71BE, 0x71BF, 0x71C0, 0x71C1, 0x71C2, 0x71C4, 0x71C5, 0x71C6, 0x71C7, + 0x71C8, 0x71C9, 0x71CA, 0x71CB, 0x71CC, 0x71CD, 0x71CF, 0x71D0, 0x71D1, + 0x71D2, 0x71D3}, + {0x71D6, 0x71D7, 0x71D8, 0x71D9, 0x71DA, 0x71DB, 0x71DC, 0x71DD, 0x71DE, + 0x71DF, 0x71E1, 0x71E2, 0x71E3, 0x71E4, 0x71E6, 0x71E8, 0x71E9, 0x71EA, + 0x71EB, 0x71EC, 0x71ED, 0x71EF, 0x71F0, 0x71F1, 0x71F2, 0x71F3, 0x71F4, + 0x71F5, 0x71F6, 0x71F7, 0x71F8, 0x71FA, 0x71FB, 0x71FC, 0x71FD, 0x71FE, + 0x71FF, 0x7200, 0x7201, 0x7202, 0x7203, 0x7204, 0x7205, 0x7207, 0x7208, + 0x7209, 0x720A, 0x720B, 0x720C, 0x720D, 0x720E, 0x720F, 0x7210, 0x7211, + 0x7212, 0x7213, 0x7214, 0x7215, 0x7216, 0x7217, 0x7218, 0x7219, 0x721A, + 0x003F, 0x721B, 0x721C, 0x721E, 0x721F, 0x7220, 0x7221, 0x7222, 0x7223, + 0x7224, 0x7225, 0x7226, 0x7227, 0x7229, 0x722B, 0x722D, 0x722E, 0x722F, + 0x7232, 0x7233, 0x7234, 0x723A, 0x723C, 0x723E, 0x7240, 0x7241, 0x7242, + 0x7243, 0x7244, 0x7245, 0x7246, 0x7249, 0x724A, 0x724B, 0x724E, 0x724F, + 0x7250, 0x7251, 0x7253, 0x7254, 0x7255, 0x7257, 0x7258, 0x725A, 0x725C, + 0x725E, 0x7260, 0x7263, 0x7264, 0x7265, 0x7268, 0x726A, 0x726B, 0x726C, + 0x726D, 0x7270, 0x7271, 0x7273, 0x7274, 0x7276, 0x7277, 0x7278, 0x727B, + 0x727C, 0x727D, 0x7282, 0x7283, 0x7285, 0x7286, 0x7287, 0x7288, 0x7289, + 0x728C, 0x728E, 0x7290, 0x7291, 0x7293, 0x7294, 0x7295, 0x7296, 0x7297, + 0x7298, 0x7299, 0x729A, 0x729B, 0x729C, 0x729D, 0x729E, 0x72A0, 0x72A1, + 0x72A2, 0x72A3, 0x72A4, 0x72A5, 0x72A6, 0x72A7, 0x72A8, 0x72A9, 0x72AA, + 0x72AB, 0x72AE, 0x72B1, 0x72B2, 0x72B3, 0x72B5, 0x72BA, 0x72BB, 0x72BC, + 0x72BD, 0x72BE, 0x72BF, 0x72C0, 0x72C5, 0x72C6, 0x72C7, 0x72C9, 0x72CA, + 0x72CB, 0x72CC, 0x72CF, 0x72D1, 0x72D3, 0x72D4, 0x72D5, 0x72D6, 0x72D8, + 0x72DA, 0x72DB}, + {0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x3000, 0x3001, + 0x3002, 0x00B7, 0x02C9, 0x02C7, 0x00A8, 0x3003, 0x3005, 0x2014, 0xFF5E, + 0x2016, 0x2026, 0x2018, 0x2019, 0x201C, 0x201D, 0x3014, 0x3015, 0x3008, + 0x3009, 0x300A, 0x300B, 0x300C, 0x300D, 0x300E, 0x300F, 0x3016, 0x3017, + 0x3010, 0x3011, 0x00B1, 0x00D7, 0x00F7, 0x2236, 0x2227, 0x2228, 0x2211, + 0x220F, 0x222A, 0x2229, 0x2208, 0x2237, 0x221A, 0x22A5, 0x2225, 0x2220, + 0x2312, 0x2299, 0x222B, 0x222E, 0x2261, 0x224C, 0x2248, 0x223D, 0x221D, + 0x2260, 0x226E, 0x226F, 0x2264, 0x2265, 0x221E, 0x2235, 0x2234, 0x2642, + 0x2640, 0x00B0, 0x2032, 0x2033, 0x2103, 0xFF04, 0x00A4, 0xFFE0, 0xFFE1, + 0x2030, 0x00A7, 0x2116, 0x2606, 0x2605, 0x25CB, 0x25CF, 0x25CE, 0x25C7, + 0x25C6, 0x25A1, 0x25A0, 0x25B3, 0x25B2, 0x203B, 0x2192, 0x2190, 0x2191, + 0x2193, 0x3013}, + {0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x2170, 0x2171, + 0x2172, 0x2173, 0x2174, 0x2175, 0x2176, 0x2177, 0x2178, 0x2179, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x2488, 0x2489, 0x248A, 0x248B, + 0x248C, 0x248D, 0x248E, 0x248F, 0x2490, 0x2491, 0x2492, 0x2493, 0x2494, + 0x2495, 0x2496, 0x2497, 0x2498, 0x2499, 0x249A, 0x249B, 0x2474, 0x2475, + 0x2476, 0x2477, 0x2478, 0x2479, 0x247A, 0x247B, 0x247C, 0x247D, 0x247E, + 0x247F, 0x2480, 0x2481, 0x2482, 0x2483, 0x2484, 0x2485, 0x2486, 0x2487, + 0x2460, 0x2461, 0x2462, 0x2463, 0x2464, 0x2465, 0x2466, 0x2467, 0x2468, + 0x2469, 0x003F, 0x003F, 0x3220, 0x3221, 0x3222, 0x3223, 0x3224, 0x3225, + 0x3226, 0x3227, 0x3228, 0x3229, 0x003F, 0x003F, 0x2160, 0x2161, 0x2162, + 0x2163, 0x2164, 0x2165, 0x2166, 0x2167, 0x2168, 0x2169, 0x216A, 0x216B, + 0x003F, 0x003F}, + {0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0xFF01, 0xFF02, + 0xFF03, 0xFFE5, 0xFF05, 0xFF06, 0xFF07, 0xFF08, 0xFF09, 0xFF0A, 0xFF0B, + 0xFF0C, 0xFF0D, 0xFF0E, 0xFF0F, 0xFF10, 0xFF11, 0xFF12, 0xFF13, 0xFF14, + 0xFF15, 0xFF16, 0xFF17, 0xFF18, 0xFF19, 0xFF1A, 0xFF1B, 0xFF1C, 0xFF1D, + 0xFF1E, 0xFF1F, 0xFF20, 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, 0xFF3B, 0xFF3C, 0xFF3D, 0xFF3E, 0xFF3F, 0xFF40, 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, 0xFF5B, 0xFF5C, + 0xFF5D, 0xFFE3}, + {0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 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, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F}, + {0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 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, 0x30E0, 0x30E1, + 0x30E2, 0x30E3, 0x30E4, 0x30E5, 0x30E6, 0x30E7, 0x30E8, 0x30E9, 0x30EA, + 0x30EB, 0x30EC, 0x30ED, 0x30EE, 0x30EF, 0x30F0, 0x30F1, 0x30F2, 0x30F3, + 0x30F4, 0x30F5, 0x30F6, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F}, + {0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x0391, 0x0392, + 0x0393, 0x0394, 0x0395, 0x0396, 0x0397, 0x0398, 0x0399, 0x039A, 0x039B, + 0x039C, 0x039D, 0x039E, 0x039F, 0x03A0, 0x03A1, 0x03A3, 0x03A4, 0x03A5, + 0x03A6, 0x03A7, 0x03A8, 0x03A9, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x03B1, 0x03B2, 0x03B3, 0x03B4, 0x03B5, 0x03B6, + 0x03B7, 0x03B8, 0x03B9, 0x03BA, 0x03BB, 0x03BC, 0x03BD, 0x03BE, 0x03BF, + 0x03C0, 0x03C1, 0x03C3, 0x03C4, 0x03C5, 0x03C6, 0x03C7, 0x03C8, 0x03C9, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0xFE35, 0xFE36, + 0xFE39, 0xFE3A, 0xFE3F, 0xFE40, 0xFE3D, 0xFE3E, 0xFE41, 0xFE42, 0xFE43, + 0xFE44, 0x003F, 0x003F, 0xFE3B, 0xFE3C, 0xFE37, 0xFE38, 0xFE31, 0x003F, + 0xFE33, 0xFE34, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F}, + {0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 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, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0451, 0x0436, + 0x0437, 0x0438, 0x0439, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E, 0x043F, + 0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447, 0x0448, + 0x0449, 0x044A, 0x044B, 0x044C, 0x044D, 0x044E, 0x044F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F}, + {0x02CA, 0x02CB, 0x02D9, 0x2013, 0x2015, 0x2025, 0x2035, 0x2105, 0x2109, + 0x2196, 0x2197, 0x2198, 0x2199, 0x2215, 0x221F, 0x2223, 0x2252, 0x2266, + 0x2267, 0x22BF, 0x2550, 0x2551, 0x2552, 0x2553, 0x2554, 0x2555, 0x2556, + 0x2557, 0x2558, 0x2559, 0x255A, 0x255B, 0x255C, 0x255D, 0x255E, 0x255F, + 0x2560, 0x2561, 0x2562, 0x2563, 0x2564, 0x2565, 0x2566, 0x2567, 0x2568, + 0x2569, 0x256A, 0x256B, 0x256C, 0x256D, 0x256E, 0x256F, 0x2570, 0x2571, + 0x2572, 0x2573, 0x2581, 0x2582, 0x2583, 0x2584, 0x2585, 0x2586, 0x2587, + 0x003F, 0x2588, 0x2589, 0x258A, 0x258B, 0x258C, 0x258D, 0x258E, 0x258F, + 0x2593, 0x2594, 0x2595, 0x25BC, 0x25BD, 0x25E2, 0x25E3, 0x25E4, 0x25E5, + 0x2609, 0x2295, 0x3012, 0x301D, 0x301E, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x0101, 0x00E1, + 0x01CE, 0x00E0, 0x0113, 0x00E9, 0x011B, 0x00E8, 0x012B, 0x00ED, 0x01D0, + 0x00EC, 0x014D, 0x00F3, 0x01D2, 0x00F2, 0x016B, 0x00FA, 0x01D4, 0x00F9, + 0x01D6, 0x01D8, 0x01DA, 0x01DC, 0x00FC, 0x00EA, 0x0251, 0x003F, 0x0144, + 0x0148, 0x003F, 0x0261, 0x003F, 0x003F, 0x003F, 0x003F, 0x3105, 0x3106, + 0x3107, 0x3108, 0x3109, 0x310A, 0x310B, 0x310C, 0x310D, 0x310E, 0x310F, + 0x3110, 0x3111, 0x3112, 0x3113, 0x3114, 0x3115, 0x3116, 0x3117, 0x3118, + 0x3119, 0x311A, 0x311B, 0x311C, 0x311D, 0x311E, 0x311F, 0x3120, 0x3121, + 0x3122, 0x3123, 0x3124, 0x3125, 0x3126, 0x3127, 0x3128, 0x3129, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F}, + {0x3021, 0x3022, 0x3023, 0x3024, 0x3025, 0x3026, 0x3027, 0x3028, 0x3029, + 0x32A3, 0x338E, 0x338F, 0x339C, 0x339D, 0x339E, 0x33A1, 0x33C4, 0x33CE, + 0x33D1, 0x33D2, 0x33D5, 0xFE30, 0xFFE2, 0xFFE4, 0x003F, 0x2121, 0x3231, + 0x003F, 0x2010, 0x003F, 0x003F, 0x003F, 0x30FC, 0x309B, 0x309C, 0x30FD, + 0x30FE, 0x3006, 0x309D, 0x309E, 0xFE49, 0xFE4A, 0xFE4B, 0xFE4C, 0xFE4D, + 0xFE4E, 0xFE4F, 0xFE50, 0xFE51, 0xFE52, 0xFE54, 0xFE55, 0xFE56, 0xFE57, + 0xFE59, 0xFE5A, 0xFE5B, 0xFE5C, 0xFE5D, 0xFE5E, 0xFE5F, 0xFE60, 0xFE61, + 0x003F, 0xFE62, 0xFE63, 0xFE64, 0xFE65, 0xFE66, 0xFE68, 0xFE69, 0xFE6A, + 0xFE6B, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x3007, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x2500, 0x2501, 0x2502, 0x2503, 0x2504, 0x2505, 0x2506, 0x2507, + 0x2508, 0x2509, 0x250A, 0x250B, 0x250C, 0x250D, 0x250E, 0x250F, 0x2510, + 0x2511, 0x2512, 0x2513, 0x2514, 0x2515, 0x2516, 0x2517, 0x2518, 0x2519, + 0x251A, 0x251B, 0x251C, 0x251D, 0x251E, 0x251F, 0x2520, 0x2521, 0x2522, + 0x2523, 0x2524, 0x2525, 0x2526, 0x2527, 0x2528, 0x2529, 0x252A, 0x252B, + 0x252C, 0x252D, 0x252E, 0x252F, 0x2530, 0x2531, 0x2532, 0x2533, 0x2534, + 0x2535, 0x2536, 0x2537, 0x2538, 0x2539, 0x253A, 0x253B, 0x253C, 0x253D, + 0x253E, 0x253F, 0x2540, 0x2541, 0x2542, 0x2543, 0x2544, 0x2545, 0x2546, + 0x2547, 0x2548, 0x2549, 0x254A, 0x254B, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F}, + {0x72DC, 0x72DD, 0x72DF, 0x72E2, 0x72E3, 0x72E4, 0x72E5, 0x72E6, 0x72E7, + 0x72EA, 0x72EB, 0x72F5, 0x72F6, 0x72F9, 0x72FD, 0x72FE, 0x72FF, 0x7300, + 0x7302, 0x7304, 0x7305, 0x7306, 0x7307, 0x7308, 0x7309, 0x730B, 0x730C, + 0x730D, 0x730F, 0x7310, 0x7311, 0x7312, 0x7314, 0x7318, 0x7319, 0x731A, + 0x731F, 0x7320, 0x7323, 0x7324, 0x7326, 0x7327, 0x7328, 0x732D, 0x732F, + 0x7330, 0x7332, 0x7333, 0x7335, 0x7336, 0x733A, 0x733B, 0x733C, 0x733D, + 0x7340, 0x7341, 0x7342, 0x7343, 0x7344, 0x7345, 0x7346, 0x7347, 0x7348, + 0x003F, 0x7349, 0x734A, 0x734B, 0x734C, 0x734E, 0x734F, 0x7351, 0x7353, + 0x7354, 0x7355, 0x7356, 0x7358, 0x7359, 0x735A, 0x735B, 0x735C, 0x735D, + 0x735E, 0x735F, 0x7361, 0x7362, 0x7363, 0x7364, 0x7365, 0x7366, 0x7367, + 0x7368, 0x7369, 0x736A, 0x736B, 0x736E, 0x7370, 0x7371, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F}, + {0x7372, 0x7373, 0x7374, 0x7375, 0x7376, 0x7377, 0x7378, 0x7379, 0x737A, + 0x737B, 0x737C, 0x737D, 0x737F, 0x7380, 0x7381, 0x7382, 0x7383, 0x7385, + 0x7386, 0x7388, 0x738A, 0x738C, 0x738D, 0x738F, 0x7390, 0x7392, 0x7393, + 0x7394, 0x7395, 0x7397, 0x7398, 0x7399, 0x739A, 0x739C, 0x739D, 0x739E, + 0x73A0, 0x73A1, 0x73A3, 0x73A4, 0x73A5, 0x73A6, 0x73A7, 0x73A8, 0x73AA, + 0x73AC, 0x73AD, 0x73B1, 0x73B4, 0x73B5, 0x73B6, 0x73B8, 0x73B9, 0x73BC, + 0x73BD, 0x73BE, 0x73BF, 0x73C1, 0x73C3, 0x73C4, 0x73C5, 0x73C6, 0x73C7, + 0x003F, 0x73CB, 0x73CC, 0x73CE, 0x73D2, 0x73D3, 0x73D4, 0x73D5, 0x73D6, + 0x73D7, 0x73D8, 0x73DA, 0x73DB, 0x73DC, 0x73DD, 0x73DF, 0x73E1, 0x73E2, + 0x73E3, 0x73E4, 0x73E6, 0x73E8, 0x73EA, 0x73EB, 0x73EC, 0x73EE, 0x73EF, + 0x73F0, 0x73F1, 0x73F3, 0x73F4, 0x73F5, 0x73F6, 0x73F7, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F}, + {0x73F8, 0x73F9, 0x73FA, 0x73FB, 0x73FC, 0x73FD, 0x73FE, 0x73FF, 0x7400, + 0x7401, 0x7402, 0x7404, 0x7407, 0x7408, 0x740B, 0x740C, 0x740D, 0x740E, + 0x7411, 0x7412, 0x7413, 0x7414, 0x7415, 0x7416, 0x7417, 0x7418, 0x7419, + 0x741C, 0x741D, 0x741E, 0x741F, 0x7420, 0x7421, 0x7423, 0x7424, 0x7427, + 0x7429, 0x742B, 0x742D, 0x742F, 0x7431, 0x7432, 0x7437, 0x7438, 0x7439, + 0x743A, 0x743B, 0x743D, 0x743E, 0x743F, 0x7440, 0x7442, 0x7443, 0x7444, + 0x7445, 0x7446, 0x7447, 0x7448, 0x7449, 0x744A, 0x744B, 0x744C, 0x744D, + 0x003F, 0x744E, 0x744F, 0x7450, 0x7451, 0x7452, 0x7453, 0x7454, 0x7456, + 0x7458, 0x745D, 0x7460, 0x7461, 0x7462, 0x7463, 0x7464, 0x7465, 0x7466, + 0x7467, 0x7468, 0x7469, 0x746A, 0x746B, 0x746C, 0x746E, 0x746F, 0x7471, + 0x7472, 0x7473, 0x7474, 0x7475, 0x7478, 0x7479, 0x747A, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F}, + {0x747B, 0x747C, 0x747D, 0x747F, 0x7482, 0x7484, 0x7485, 0x7486, 0x7488, + 0x7489, 0x748A, 0x748C, 0x748D, 0x748F, 0x7491, 0x7492, 0x7493, 0x7494, + 0x7495, 0x7496, 0x7497, 0x7498, 0x7499, 0x749A, 0x749B, 0x749D, 0x749F, + 0x74A0, 0x74A1, 0x74A2, 0x74A3, 0x74A4, 0x74A5, 0x74A6, 0x74AA, 0x74AB, + 0x74AC, 0x74AD, 0x74AE, 0x74AF, 0x74B0, 0x74B1, 0x74B2, 0x74B3, 0x74B4, + 0x74B5, 0x74B6, 0x74B7, 0x74B8, 0x74B9, 0x74BB, 0x74BC, 0x74BD, 0x74BE, + 0x74BF, 0x74C0, 0x74C1, 0x74C2, 0x74C3, 0x74C4, 0x74C5, 0x74C6, 0x74C7, + 0x003F, 0x74C8, 0x74C9, 0x74CA, 0x74CB, 0x74CC, 0x74CD, 0x74CE, 0x74CF, + 0x74D0, 0x74D1, 0x74D3, 0x74D4, 0x74D5, 0x74D6, 0x74D7, 0x74D8, 0x74D9, + 0x74DA, 0x74DB, 0x74DD, 0x74DF, 0x74E1, 0x74E5, 0x74E7, 0x74E8, 0x74E9, + 0x74EA, 0x74EB, 0x74EC, 0x74ED, 0x74F0, 0x74F1, 0x74F2, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F}, + {0x74F3, 0x74F5, 0x74F8, 0x74F9, 0x74FA, 0x74FB, 0x74FC, 0x74FD, 0x74FE, + 0x7500, 0x7501, 0x7502, 0x7503, 0x7505, 0x7506, 0x7507, 0x7508, 0x7509, + 0x750A, 0x750B, 0x750C, 0x750E, 0x7510, 0x7512, 0x7514, 0x7515, 0x7516, + 0x7517, 0x751B, 0x751D, 0x751E, 0x7520, 0x7521, 0x7522, 0x7523, 0x7524, + 0x7526, 0x7527, 0x752A, 0x752E, 0x7534, 0x7536, 0x7539, 0x753C, 0x753D, + 0x753F, 0x7541, 0x7542, 0x7543, 0x7544, 0x7546, 0x7547, 0x7549, 0x754A, + 0x754D, 0x7550, 0x7551, 0x7552, 0x7553, 0x7555, 0x7556, 0x7557, 0x7558, + 0x003F, 0x755D, 0x755E, 0x755F, 0x7560, 0x7561, 0x7562, 0x7563, 0x7564, + 0x7567, 0x7568, 0x7569, 0x756B, 0x756C, 0x756D, 0x756E, 0x756F, 0x7570, + 0x7571, 0x7573, 0x7575, 0x7576, 0x7577, 0x757A, 0x757B, 0x757C, 0x757D, + 0x757E, 0x7580, 0x7581, 0x7582, 0x7584, 0x7585, 0x7587, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F}, + {0x7588, 0x7589, 0x758A, 0x758C, 0x758D, 0x758E, 0x7590, 0x7593, 0x7595, + 0x7598, 0x759B, 0x759C, 0x759E, 0x75A2, 0x75A6, 0x75A7, 0x75A8, 0x75A9, + 0x75AA, 0x75AD, 0x75B6, 0x75B7, 0x75BA, 0x75BB, 0x75BF, 0x75C0, 0x75C1, + 0x75C6, 0x75CB, 0x75CC, 0x75CE, 0x75CF, 0x75D0, 0x75D1, 0x75D3, 0x75D7, + 0x75D9, 0x75DA, 0x75DC, 0x75DD, 0x75DF, 0x75E0, 0x75E1, 0x75E5, 0x75E9, + 0x75EC, 0x75ED, 0x75EE, 0x75EF, 0x75F2, 0x75F3, 0x75F5, 0x75F6, 0x75F7, + 0x75F8, 0x75FA, 0x75FB, 0x75FD, 0x75FE, 0x7602, 0x7604, 0x7606, 0x7607, + 0x003F, 0x7608, 0x7609, 0x760B, 0x760D, 0x760E, 0x760F, 0x7611, 0x7612, + 0x7613, 0x7614, 0x7616, 0x761A, 0x761C, 0x761D, 0x761E, 0x7621, 0x7623, + 0x7627, 0x7628, 0x762C, 0x762E, 0x762F, 0x7631, 0x7632, 0x7636, 0x7637, + 0x7639, 0x763A, 0x763B, 0x763D, 0x7641, 0x7642, 0x7644, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F}, + {0x7645, 0x7646, 0x7647, 0x7648, 0x7649, 0x764A, 0x764B, 0x764E, 0x764F, + 0x7650, 0x7651, 0x7652, 0x7653, 0x7655, 0x7657, 0x7658, 0x7659, 0x765A, + 0x765B, 0x765D, 0x765F, 0x7660, 0x7661, 0x7662, 0x7664, 0x7665, 0x7666, + 0x7667, 0x7668, 0x7669, 0x766A, 0x766C, 0x766D, 0x766E, 0x7670, 0x7671, + 0x7672, 0x7673, 0x7674, 0x7675, 0x7676, 0x7677, 0x7679, 0x767A, 0x767C, + 0x767F, 0x7680, 0x7681, 0x7683, 0x7685, 0x7689, 0x768A, 0x768C, 0x768D, + 0x768F, 0x7690, 0x7692, 0x7694, 0x7695, 0x7697, 0x7698, 0x769A, 0x769B, + 0x003F, 0x769C, 0x769D, 0x769E, 0x769F, 0x76A0, 0x76A1, 0x76A2, 0x76A3, + 0x76A5, 0x76A6, 0x76A7, 0x76A8, 0x76A9, 0x76AA, 0x76AB, 0x76AC, 0x76AD, + 0x76AF, 0x76B0, 0x76B3, 0x76B5, 0x76B6, 0x76B7, 0x76B8, 0x76B9, 0x76BA, + 0x76BB, 0x76BC, 0x76BD, 0x76BE, 0x76C0, 0x76C1, 0x76C3, 0x554A, 0x963F, + 0x57C3, 0x6328, 0x54CE, 0x5509, 0x54C0, 0x7691, 0x764C, 0x853C, 0x77EE, + 0x827E, 0x788D, 0x7231, 0x9698, 0x978D, 0x6C28, 0x5B89, 0x4FFA, 0x6309, + 0x6697, 0x5CB8, 0x80FA, 0x6848, 0x80AE, 0x6602, 0x76CE, 0x51F9, 0x6556, + 0x71AC, 0x7FF1, 0x8884, 0x50B2, 0x5965, 0x61CA, 0x6FB3, 0x82AD, 0x634C, + 0x6252, 0x53ED, 0x5427, 0x7B06, 0x516B, 0x75A4, 0x5DF4, 0x62D4, 0x8DCB, + 0x9776, 0x628A, 0x8019, 0x575D, 0x9738, 0x7F62, 0x7238, 0x767D, 0x67CF, + 0x767E, 0x6446, 0x4F70, 0x8D25, 0x62DC, 0x7A17, 0x6591, 0x73ED, 0x642C, + 0x6273, 0x822C, 0x9881, 0x677F, 0x7248, 0x626E, 0x62CC, 0x4F34, 0x74E3, + 0x534A, 0x529E, 0x7ECA, 0x90A6, 0x5E2E, 0x6886, 0x699C, 0x8180, 0x7ED1, + 0x68D2, 0x78C5, 0x868C, 0x9551, 0x508D, 0x8C24, 0x82DE, 0x80DE, 0x5305, + 0x8912, 0x5265}, + {0x76C4, 0x76C7, 0x76C9, 0x76CB, 0x76CC, 0x76D3, 0x76D5, 0x76D9, 0x76DA, + 0x76DC, 0x76DD, 0x76DE, 0x76E0, 0x76E1, 0x76E2, 0x76E3, 0x76E4, 0x76E6, + 0x76E7, 0x76E8, 0x76E9, 0x76EA, 0x76EB, 0x76EC, 0x76ED, 0x76F0, 0x76F3, + 0x76F5, 0x76F6, 0x76F7, 0x76FA, 0x76FB, 0x76FD, 0x76FF, 0x7700, 0x7702, + 0x7703, 0x7705, 0x7706, 0x770A, 0x770C, 0x770E, 0x770F, 0x7710, 0x7711, + 0x7712, 0x7713, 0x7714, 0x7715, 0x7716, 0x7717, 0x7718, 0x771B, 0x771C, + 0x771D, 0x771E, 0x7721, 0x7723, 0x7724, 0x7725, 0x7727, 0x772A, 0x772B, + 0x003F, 0x772C, 0x772E, 0x7730, 0x7731, 0x7732, 0x7733, 0x7734, 0x7739, + 0x773B, 0x773D, 0x773E, 0x773F, 0x7742, 0x7744, 0x7745, 0x7746, 0x7748, + 0x7749, 0x774A, 0x774B, 0x774C, 0x774D, 0x774E, 0x774F, 0x7752, 0x7753, + 0x7754, 0x7755, 0x7756, 0x7757, 0x7758, 0x7759, 0x775C, 0x8584, 0x96F9, + 0x4FDD, 0x5821, 0x9971, 0x5B9D, 0x62B1, 0x62A5, 0x66B4, 0x8C79, 0x9C8D, + 0x7206, 0x676F, 0x7891, 0x60B2, 0x5351, 0x5317, 0x8F88, 0x80CC, 0x8D1D, + 0x94A1, 0x500D, 0x72C8, 0x5907, 0x60EB, 0x7119, 0x88AB, 0x5954, 0x82EF, + 0x672C, 0x7B28, 0x5D29, 0x7EF7, 0x752D, 0x6CF5, 0x8E66, 0x8FF8, 0x903C, + 0x9F3B, 0x6BD4, 0x9119, 0x7B14, 0x5F7C, 0x78A7, 0x84D6, 0x853D, 0x6BD5, + 0x6BD9, 0x6BD6, 0x5E01, 0x5E87, 0x75F9, 0x95ED, 0x655D, 0x5F0A, 0x5FC5, + 0x8F9F, 0x58C1, 0x81C2, 0x907F, 0x965B, 0x97AD, 0x8FB9, 0x7F16, 0x8D2C, + 0x6241, 0x4FBF, 0x53D8, 0x535E, 0x8FA8, 0x8FA9, 0x8FAB, 0x904D, 0x6807, + 0x5F6A, 0x8198, 0x8868, 0x9CD6, 0x618B, 0x522B, 0x762A, 0x5F6C, 0x658C, + 0x6FD2, 0x6EE8, 0x5BBE, 0x6448, 0x5175, 0x51B0, 0x67C4, 0x4E19, 0x79C9, + 0x997C, 0x70B3}, + {0x775D, 0x775E, 0x775F, 0x7760, 0x7764, 0x7767, 0x7769, 0x776A, 0x776D, + 0x776E, 0x776F, 0x7770, 0x7771, 0x7772, 0x7773, 0x7774, 0x7775, 0x7776, + 0x7777, 0x7778, 0x777A, 0x777B, 0x777C, 0x7781, 0x7782, 0x7783, 0x7786, + 0x7787, 0x7788, 0x7789, 0x778A, 0x778B, 0x778F, 0x7790, 0x7793, 0x7794, + 0x7795, 0x7796, 0x7797, 0x7798, 0x7799, 0x779A, 0x779B, 0x779C, 0x779D, + 0x779E, 0x77A1, 0x77A3, 0x77A4, 0x77A6, 0x77A8, 0x77AB, 0x77AD, 0x77AE, + 0x77AF, 0x77B1, 0x77B2, 0x77B4, 0x77B6, 0x77B7, 0x77B8, 0x77B9, 0x77BA, + 0x003F, 0x77BC, 0x77BE, 0x77C0, 0x77C1, 0x77C2, 0x77C3, 0x77C4, 0x77C5, + 0x77C6, 0x77C7, 0x77C8, 0x77C9, 0x77CA, 0x77CB, 0x77CC, 0x77CE, 0x77CF, + 0x77D0, 0x77D1, 0x77D2, 0x77D3, 0x77D4, 0x77D5, 0x77D6, 0x77D8, 0x77D9, + 0x77DA, 0x77DD, 0x77DE, 0x77DF, 0x77E0, 0x77E1, 0x77E4, 0x75C5, 0x5E76, + 0x73BB, 0x83E0, 0x64AD, 0x62E8, 0x94B5, 0x6CE2, 0x535A, 0x52C3, 0x640F, + 0x94C2, 0x7B94, 0x4F2F, 0x5E1B, 0x8236, 0x8116, 0x818A, 0x6E24, 0x6CCA, + 0x9A73, 0x6355, 0x535C, 0x54FA, 0x8865, 0x57E0, 0x4E0D, 0x5E03, 0x6B65, + 0x7C3F, 0x90E8, 0x6016, 0x64E6, 0x731C, 0x88C1, 0x6750, 0x624D, 0x8D22, + 0x776C, 0x8E29, 0x91C7, 0x5F69, 0x83DC, 0x8521, 0x9910, 0x53C2, 0x8695, + 0x6B8B, 0x60ED, 0x60E8, 0x707F, 0x82CD, 0x8231, 0x4ED3, 0x6CA7, 0x85CF, + 0x64CD, 0x7CD9, 0x69FD, 0x66F9, 0x8349, 0x5395, 0x7B56, 0x4FA7, 0x518C, + 0x6D4B, 0x5C42, 0x8E6D, 0x63D2, 0x53C9, 0x832C, 0x8336, 0x67E5, 0x78B4, + 0x643D, 0x5BDF, 0x5C94, 0x5DEE, 0x8BE7, 0x62C6, 0x67F4, 0x8C7A, 0x6400, + 0x63BA, 0x8749, 0x998B, 0x8C17, 0x7F20, 0x94F2, 0x4EA7, 0x9610, 0x98A4, + 0x660C, 0x7316}, + {0x77E6, 0x77E8, 0x77EA, 0x77EF, 0x77F0, 0x77F1, 0x77F2, 0x77F4, 0x77F5, + 0x77F7, 0x77F9, 0x77FA, 0x77FB, 0x77FC, 0x7803, 0x7804, 0x7805, 0x7806, + 0x7807, 0x7808, 0x780A, 0x780B, 0x780E, 0x780F, 0x7810, 0x7813, 0x7815, + 0x7819, 0x781B, 0x781E, 0x7820, 0x7821, 0x7822, 0x7824, 0x7828, 0x782A, + 0x782B, 0x782E, 0x782F, 0x7831, 0x7832, 0x7833, 0x7835, 0x7836, 0x783D, + 0x783F, 0x7841, 0x7842, 0x7843, 0x7844, 0x7846, 0x7848, 0x7849, 0x784A, + 0x784B, 0x784D, 0x784F, 0x7851, 0x7853, 0x7854, 0x7858, 0x7859, 0x785A, + 0x003F, 0x785B, 0x785C, 0x785E, 0x785F, 0x7860, 0x7861, 0x7862, 0x7863, + 0x7864, 0x7865, 0x7866, 0x7867, 0x7868, 0x7869, 0x786F, 0x7870, 0x7871, + 0x7872, 0x7873, 0x7874, 0x7875, 0x7876, 0x7878, 0x7879, 0x787A, 0x787B, + 0x787D, 0x787E, 0x787F, 0x7880, 0x7881, 0x7882, 0x7883, 0x573A, 0x5C1D, + 0x5E38, 0x957F, 0x507F, 0x80A0, 0x5382, 0x655E, 0x7545, 0x5531, 0x5021, + 0x8D85, 0x6284, 0x949E, 0x671D, 0x5632, 0x6F6E, 0x5DE2, 0x5435, 0x7092, + 0x8F66, 0x626F, 0x64A4, 0x63A3, 0x5F7B, 0x6F88, 0x90F4, 0x81E3, 0x8FB0, + 0x5C18, 0x6668, 0x5FF1, 0x6C89, 0x9648, 0x8D81, 0x886C, 0x6491, 0x79F0, + 0x57CE, 0x6A59, 0x6210, 0x5448, 0x4E58, 0x7A0B, 0x60E9, 0x6F84, 0x8BDA, + 0x627F, 0x901E, 0x9A8B, 0x79E4, 0x5403, 0x75F4, 0x6301, 0x5319, 0x6C60, + 0x8FDF, 0x5F1B, 0x9A70, 0x803B, 0x9F7F, 0x4F88, 0x5C3A, 0x8D64, 0x7FC5, + 0x65A5, 0x70BD, 0x5145, 0x51B2, 0x866B, 0x5D07, 0x5BA0, 0x62BD, 0x916C, + 0x7574, 0x8E0C, 0x7A20, 0x6101, 0x7B79, 0x4EC7, 0x7EF8, 0x7785, 0x4E11, + 0x81ED, 0x521D, 0x51FA, 0x6A71, 0x53A8, 0x8E87, 0x9504, 0x96CF, 0x6EC1, + 0x9664, 0x695A}, + {0x7884, 0x7885, 0x7886, 0x7888, 0x788A, 0x788B, 0x788F, 0x7890, 0x7892, + 0x7894, 0x7895, 0x7896, 0x7899, 0x789D, 0x789E, 0x78A0, 0x78A2, 0x78A4, + 0x78A6, 0x78A8, 0x78A9, 0x78AA, 0x78AB, 0x78AC, 0x78AD, 0x78AE, 0x78AF, + 0x78B5, 0x78B6, 0x78B7, 0x78B8, 0x78BA, 0x78BB, 0x78BC, 0x78BD, 0x78BF, + 0x78C0, 0x78C2, 0x78C3, 0x78C4, 0x78C6, 0x78C7, 0x78C8, 0x78CC, 0x78CD, + 0x78CE, 0x78CF, 0x78D1, 0x78D2, 0x78D3, 0x78D6, 0x78D7, 0x78D8, 0x78DA, + 0x78DB, 0x78DC, 0x78DD, 0x78DE, 0x78DF, 0x78E0, 0x78E1, 0x78E2, 0x78E3, + 0x003F, 0x78E4, 0x78E5, 0x78E6, 0x78E7, 0x78E9, 0x78EA, 0x78EB, 0x78ED, + 0x78EE, 0x78EF, 0x78F0, 0x78F1, 0x78F3, 0x78F5, 0x78F6, 0x78F8, 0x78F9, + 0x78FB, 0x78FC, 0x78FD, 0x78FE, 0x78FF, 0x7900, 0x7902, 0x7903, 0x7904, + 0x7906, 0x7907, 0x7908, 0x7909, 0x790A, 0x790B, 0x790C, 0x7840, 0x50A8, + 0x77D7, 0x6410, 0x89E6, 0x5904, 0x63E3, 0x5DDD, 0x7A7F, 0x693D, 0x4F20, + 0x8239, 0x5598, 0x4E32, 0x75AE, 0x7A97, 0x5E62, 0x5E8A, 0x95EF, 0x521B, + 0x5439, 0x708A, 0x6376, 0x9524, 0x5782, 0x6625, 0x693F, 0x9187, 0x5507, + 0x6DF3, 0x7EAF, 0x8822, 0x6233, 0x7EF0, 0x75B5, 0x8328, 0x78C1, 0x96CC, + 0x8F9E, 0x6148, 0x74F7, 0x8BCD, 0x6B64, 0x523A, 0x8D50, 0x6B21, 0x806A, + 0x8471, 0x56F1, 0x5306, 0x4ECE, 0x4E1B, 0x51D1, 0x7C97, 0x918B, 0x7C07, + 0x4FC3, 0x8E7F, 0x7BE1, 0x7A9C, 0x6467, 0x5D14, 0x50AC, 0x8106, 0x7601, + 0x7CB9, 0x6DEC, 0x7FE0, 0x6751, 0x5B58, 0x5BF8, 0x78CB, 0x64AE, 0x6413, + 0x63AA, 0x632B, 0x9519, 0x642D, 0x8FBE, 0x7B54, 0x7629, 0x6253, 0x5927, + 0x5446, 0x6B79, 0x50A3, 0x6234, 0x5E26, 0x6B86, 0x4EE3, 0x8D37, 0x888B, + 0x5F85, 0x902E}, + {0x790D, 0x790E, 0x790F, 0x7910, 0x7911, 0x7912, 0x7914, 0x7915, 0x7916, + 0x7917, 0x7918, 0x7919, 0x791A, 0x791B, 0x791C, 0x791D, 0x791F, 0x7920, + 0x7921, 0x7922, 0x7923, 0x7925, 0x7926, 0x7927, 0x7928, 0x7929, 0x792A, + 0x792B, 0x792C, 0x792D, 0x792E, 0x792F, 0x7930, 0x7931, 0x7932, 0x7933, + 0x7935, 0x7936, 0x7937, 0x7938, 0x7939, 0x793D, 0x793F, 0x7942, 0x7943, + 0x7944, 0x7945, 0x7947, 0x794A, 0x794B, 0x794C, 0x794D, 0x794E, 0x794F, + 0x7950, 0x7951, 0x7952, 0x7954, 0x7955, 0x7958, 0x7959, 0x7961, 0x7963, + 0x003F, 0x7964, 0x7966, 0x7969, 0x796A, 0x796B, 0x796C, 0x796E, 0x7970, + 0x7971, 0x7972, 0x7973, 0x7974, 0x7975, 0x7976, 0x7979, 0x797B, 0x797C, + 0x797D, 0x797E, 0x797F, 0x7982, 0x7983, 0x7986, 0x7987, 0x7988, 0x7989, + 0x798B, 0x798C, 0x798D, 0x798E, 0x7990, 0x7991, 0x7992, 0x6020, 0x803D, + 0x62C5, 0x4E39, 0x5355, 0x90F8, 0x63B8, 0x80C6, 0x65E6, 0x6C2E, 0x4F46, + 0x60EE, 0x6DE1, 0x8BDE, 0x5F39, 0x86CB, 0x5F53, 0x6321, 0x515A, 0x8361, + 0x6863, 0x5200, 0x6363, 0x8E48, 0x5012, 0x5C9B, 0x7977, 0x5BFC, 0x5230, + 0x7A3B, 0x60BC, 0x9053, 0x76D7, 0x5FB7, 0x5F97, 0x7684, 0x8E6C, 0x706F, + 0x767B, 0x7B49, 0x77AA, 0x51F3, 0x9093, 0x5824, 0x4F4E, 0x6EF4, 0x8FEA, + 0x654C, 0x7B1B, 0x72C4, 0x6DA4, 0x7FDF, 0x5AE1, 0x62B5, 0x5E95, 0x5730, + 0x8482, 0x7B2C, 0x5E1D, 0x5F1F, 0x9012, 0x7F14, 0x98A0, 0x6382, 0x6EC7, + 0x7898, 0x70B9, 0x5178, 0x975B, 0x57AB, 0x7535, 0x4F43, 0x7538, 0x5E97, + 0x60E6, 0x5960, 0x6DC0, 0x6BBF, 0x7889, 0x53FC, 0x96D5, 0x51CB, 0x5201, + 0x6389, 0x540A, 0x9493, 0x8C03, 0x8DCC, 0x7239, 0x789F, 0x8776, 0x8FED, + 0x8C0D, 0x53E0}, + {0x7993, 0x7994, 0x7995, 0x7996, 0x7997, 0x7998, 0x7999, 0x799B, 0x799C, + 0x799D, 0x799E, 0x799F, 0x79A0, 0x79A1, 0x79A2, 0x79A3, 0x79A4, 0x79A5, + 0x79A6, 0x79A8, 0x79A9, 0x79AA, 0x79AB, 0x79AC, 0x79AD, 0x79AE, 0x79AF, + 0x79B0, 0x79B1, 0x79B2, 0x79B4, 0x79B5, 0x79B6, 0x79B7, 0x79B8, 0x79BC, + 0x79BF, 0x79C2, 0x79C4, 0x79C5, 0x79C7, 0x79C8, 0x79CA, 0x79CC, 0x79CE, + 0x79CF, 0x79D0, 0x79D3, 0x79D4, 0x79D6, 0x79D7, 0x79D9, 0x79DA, 0x79DB, + 0x79DC, 0x79DD, 0x79DE, 0x79E0, 0x79E1, 0x79E2, 0x79E5, 0x79E8, 0x79EA, + 0x003F, 0x79EC, 0x79EE, 0x79F1, 0x79F2, 0x79F3, 0x79F4, 0x79F5, 0x79F6, + 0x79F7, 0x79F9, 0x79FA, 0x79FC, 0x79FE, 0x79FF, 0x7A01, 0x7A04, 0x7A05, + 0x7A07, 0x7A08, 0x7A09, 0x7A0A, 0x7A0C, 0x7A0F, 0x7A10, 0x7A11, 0x7A12, + 0x7A13, 0x7A15, 0x7A16, 0x7A18, 0x7A19, 0x7A1B, 0x7A1C, 0x4E01, 0x76EF, + 0x53EE, 0x9489, 0x9876, 0x9F0E, 0x952D, 0x5B9A, 0x8BA2, 0x4E22, 0x4E1C, + 0x51AC, 0x8463, 0x61C2, 0x52A8, 0x680B, 0x4F97, 0x606B, 0x51BB, 0x6D1E, + 0x515C, 0x6296, 0x6597, 0x9661, 0x8C46, 0x9017, 0x75D8, 0x90FD, 0x7763, + 0x6BD2, 0x728A, 0x72EC, 0x8BFB, 0x5835, 0x7779, 0x8D4C, 0x675C, 0x9540, + 0x809A, 0x5EA6, 0x6E21, 0x5992, 0x7AEF, 0x77ED, 0x953B, 0x6BB5, 0x65AD, + 0x7F0E, 0x5806, 0x5151, 0x961F, 0x5BF9, 0x58A9, 0x5428, 0x8E72, 0x6566, + 0x987F, 0x56E4, 0x949D, 0x76FE, 0x9041, 0x6387, 0x54C6, 0x591A, 0x593A, + 0x579B, 0x8EB2, 0x6735, 0x8DFA, 0x8235, 0x5241, 0x60F0, 0x5815, 0x86FE, + 0x5CE8, 0x9E45, 0x4FC4, 0x989D, 0x8BB9, 0x5A25, 0x6076, 0x5384, 0x627C, + 0x904F, 0x9102, 0x997F, 0x6069, 0x800C, 0x513F, 0x8033, 0x5C14, 0x9975, + 0x6D31, 0x4E8C}, + {0x7A1D, 0x7A1F, 0x7A21, 0x7A22, 0x7A24, 0x7A25, 0x7A26, 0x7A27, 0x7A28, + 0x7A29, 0x7A2A, 0x7A2B, 0x7A2C, 0x7A2D, 0x7A2E, 0x7A2F, 0x7A30, 0x7A31, + 0x7A32, 0x7A34, 0x7A35, 0x7A36, 0x7A38, 0x7A3A, 0x7A3E, 0x7A40, 0x7A41, + 0x7A42, 0x7A43, 0x7A44, 0x7A45, 0x7A47, 0x7A48, 0x7A49, 0x7A4A, 0x7A4B, + 0x7A4C, 0x7A4D, 0x7A4E, 0x7A4F, 0x7A50, 0x7A52, 0x7A53, 0x7A54, 0x7A55, + 0x7A56, 0x7A58, 0x7A59, 0x7A5A, 0x7A5B, 0x7A5C, 0x7A5D, 0x7A5E, 0x7A5F, + 0x7A60, 0x7A61, 0x7A62, 0x7A63, 0x7A64, 0x7A65, 0x7A66, 0x7A67, 0x7A68, + 0x003F, 0x7A69, 0x7A6A, 0x7A6B, 0x7A6C, 0x7A6D, 0x7A6E, 0x7A6F, 0x7A71, + 0x7A72, 0x7A73, 0x7A75, 0x7A7B, 0x7A7C, 0x7A7D, 0x7A7E, 0x7A82, 0x7A85, + 0x7A87, 0x7A89, 0x7A8A, 0x7A8B, 0x7A8C, 0x7A8E, 0x7A8F, 0x7A90, 0x7A93, + 0x7A94, 0x7A99, 0x7A9A, 0x7A9B, 0x7A9E, 0x7AA1, 0x7AA2, 0x8D30, 0x53D1, + 0x7F5A, 0x7B4F, 0x4F10, 0x4E4F, 0x9600, 0x6CD5, 0x73D0, 0x85E9, 0x5E06, + 0x756A, 0x7FFB, 0x6A0A, 0x77FE, 0x9492, 0x7E41, 0x51E1, 0x70E6, 0x53CD, + 0x8FD4, 0x8303, 0x8D29, 0x72AF, 0x996D, 0x6CDB, 0x574A, 0x82B3, 0x65B9, + 0x80AA, 0x623F, 0x9632, 0x59A8, 0x4EFF, 0x8BBF, 0x7EBA, 0x653E, 0x83F2, + 0x975E, 0x5561, 0x98DE, 0x80A5, 0x532A, 0x8BFD, 0x5420, 0x80BA, 0x5E9F, + 0x6CB8, 0x8D39, 0x82AC, 0x915A, 0x5429, 0x6C1B, 0x5206, 0x7EB7, 0x575F, + 0x711A, 0x6C7E, 0x7C89, 0x594B, 0x4EFD, 0x5FFF, 0x6124, 0x7CAA, 0x4E30, + 0x5C01, 0x67AB, 0x8702, 0x5CF0, 0x950B, 0x98CE, 0x75AF, 0x70FD, 0x9022, + 0x51AF, 0x7F1D, 0x8BBD, 0x5949, 0x51E4, 0x4F5B, 0x5426, 0x592B, 0x6577, + 0x80A4, 0x5B75, 0x6276, 0x62C2, 0x8F90, 0x5E45, 0x6C1F, 0x7B26, 0x4F0F, + 0x4FD8, 0x670D}, + {0x7AA3, 0x7AA4, 0x7AA7, 0x7AA9, 0x7AAA, 0x7AAB, 0x7AAE, 0x7AAF, 0x7AB0, + 0x7AB1, 0x7AB2, 0x7AB4, 0x7AB5, 0x7AB6, 0x7AB7, 0x7AB8, 0x7AB9, 0x7ABA, + 0x7ABB, 0x7ABC, 0x7ABD, 0x7ABE, 0x7AC0, 0x7AC1, 0x7AC2, 0x7AC3, 0x7AC4, + 0x7AC5, 0x7AC6, 0x7AC7, 0x7AC8, 0x7AC9, 0x7ACA, 0x7ACC, 0x7ACD, 0x7ACE, + 0x7ACF, 0x7AD0, 0x7AD1, 0x7AD2, 0x7AD3, 0x7AD4, 0x7AD5, 0x7AD7, 0x7AD8, + 0x7ADA, 0x7ADB, 0x7ADC, 0x7ADD, 0x7AE1, 0x7AE2, 0x7AE4, 0x7AE7, 0x7AE8, + 0x7AE9, 0x7AEA, 0x7AEB, 0x7AEC, 0x7AEE, 0x7AF0, 0x7AF1, 0x7AF2, 0x7AF3, + 0x003F, 0x7AF4, 0x7AF5, 0x7AF6, 0x7AF7, 0x7AF8, 0x7AFB, 0x7AFC, 0x7AFE, + 0x7B00, 0x7B01, 0x7B02, 0x7B05, 0x7B07, 0x7B09, 0x7B0C, 0x7B0D, 0x7B0E, + 0x7B10, 0x7B12, 0x7B13, 0x7B16, 0x7B17, 0x7B18, 0x7B1A, 0x7B1C, 0x7B1D, + 0x7B1F, 0x7B21, 0x7B22, 0x7B23, 0x7B27, 0x7B29, 0x7B2D, 0x6D6E, 0x6DAA, + 0x798F, 0x88B1, 0x5F17, 0x752B, 0x629A, 0x8F85, 0x4FEF, 0x91DC, 0x65A7, + 0x812F, 0x8151, 0x5E9C, 0x8150, 0x8D74, 0x526F, 0x8986, 0x8D4B, 0x590D, + 0x5085, 0x4ED8, 0x961C, 0x7236, 0x8179, 0x8D1F, 0x5BCC, 0x8BA3, 0x9644, + 0x5987, 0x7F1A, 0x5490, 0x5676, 0x560E, 0x8BE5, 0x6539, 0x6982, 0x9499, + 0x76D6, 0x6E89, 0x5E72, 0x7518, 0x6746, 0x67D1, 0x7AFF, 0x809D, 0x8D76, + 0x611F, 0x79C6, 0x6562, 0x8D63, 0x5188, 0x521A, 0x94A2, 0x7F38, 0x809B, + 0x7EB2, 0x5C97, 0x6E2F, 0x6760, 0x7BD9, 0x768B, 0x9AD8, 0x818F, 0x7F94, + 0x7CD5, 0x641E, 0x9550, 0x7A3F, 0x544A, 0x54E5, 0x6B4C, 0x6401, 0x6208, + 0x9E3D, 0x80F3, 0x7599, 0x5272, 0x9769, 0x845B, 0x683C, 0x86E4, 0x9601, + 0x9694, 0x94EC, 0x4E2A, 0x5404, 0x7ED9, 0x6839, 0x8DDF, 0x8015, 0x66F4, + 0x5E9A, 0x7FB9}, + {0x7B2F, 0x7B30, 0x7B32, 0x7B34, 0x7B35, 0x7B36, 0x7B37, 0x7B39, 0x7B3B, + 0x7B3D, 0x7B3F, 0x7B40, 0x7B41, 0x7B42, 0x7B43, 0x7B44, 0x7B46, 0x7B48, + 0x7B4A, 0x7B4D, 0x7B4E, 0x7B53, 0x7B55, 0x7B57, 0x7B59, 0x7B5C, 0x7B5E, + 0x7B5F, 0x7B61, 0x7B63, 0x7B64, 0x7B65, 0x7B66, 0x7B67, 0x7B68, 0x7B69, + 0x7B6A, 0x7B6B, 0x7B6C, 0x7B6D, 0x7B6F, 0x7B70, 0x7B73, 0x7B74, 0x7B76, + 0x7B78, 0x7B7A, 0x7B7C, 0x7B7D, 0x7B7F, 0x7B81, 0x7B82, 0x7B83, 0x7B84, + 0x7B86, 0x7B87, 0x7B88, 0x7B89, 0x7B8A, 0x7B8B, 0x7B8C, 0x7B8E, 0x7B8F, + 0x003F, 0x7B91, 0x7B92, 0x7B93, 0x7B96, 0x7B98, 0x7B99, 0x7B9A, 0x7B9B, + 0x7B9E, 0x7B9F, 0x7BA0, 0x7BA3, 0x7BA4, 0x7BA5, 0x7BAE, 0x7BAF, 0x7BB0, + 0x7BB2, 0x7BB3, 0x7BB5, 0x7BB6, 0x7BB7, 0x7BB9, 0x7BBA, 0x7BBB, 0x7BBC, + 0x7BBD, 0x7BBE, 0x7BBF, 0x7BC0, 0x7BC2, 0x7BC3, 0x7BC4, 0x57C2, 0x803F, + 0x6897, 0x5DE5, 0x653B, 0x529F, 0x606D, 0x9F9A, 0x4F9B, 0x8EAC, 0x516C, + 0x5BAB, 0x5F13, 0x5DE9, 0x6C5E, 0x62F1, 0x8D21, 0x5171, 0x94A9, 0x52FE, + 0x6C9F, 0x82DF, 0x72D7, 0x57A2, 0x6784, 0x8D2D, 0x591F, 0x8F9C, 0x83C7, + 0x5495, 0x7B8D, 0x4F30, 0x6CBD, 0x5B64, 0x59D1, 0x9F13, 0x53E4, 0x86CA, + 0x9AA8, 0x8C37, 0x80A1, 0x6545, 0x987E, 0x56FA, 0x96C7, 0x522E, 0x74DC, + 0x5250, 0x5BE1, 0x6302, 0x8902, 0x4E56, 0x62D0, 0x602A, 0x68FA, 0x5173, + 0x5B98, 0x51A0, 0x89C2, 0x7BA1, 0x9986, 0x7F50, 0x60EF, 0x704C, 0x8D2F, + 0x5149, 0x5E7F, 0x901B, 0x7470, 0x89C4, 0x572D, 0x7845, 0x5F52, 0x9F9F, + 0x95FA, 0x8F68, 0x9B3C, 0x8BE1, 0x7678, 0x6842, 0x67DC, 0x8DEA, 0x8D35, + 0x523D, 0x8F8A, 0x6EDA, 0x68CD, 0x9505, 0x90ED, 0x56FD, 0x679C, 0x88F9, + 0x8FC7, 0x54C8}, + {0x7BC5, 0x7BC8, 0x7BC9, 0x7BCA, 0x7BCB, 0x7BCD, 0x7BCE, 0x7BCF, 0x7BD0, + 0x7BD2, 0x7BD4, 0x7BD5, 0x7BD6, 0x7BD7, 0x7BD8, 0x7BDB, 0x7BDC, 0x7BDE, + 0x7BDF, 0x7BE0, 0x7BE2, 0x7BE3, 0x7BE4, 0x7BE7, 0x7BE8, 0x7BE9, 0x7BEB, + 0x7BEC, 0x7BED, 0x7BEF, 0x7BF0, 0x7BF2, 0x7BF3, 0x7BF4, 0x7BF5, 0x7BF6, + 0x7BF8, 0x7BF9, 0x7BFA, 0x7BFB, 0x7BFD, 0x7BFF, 0x7C00, 0x7C01, 0x7C02, + 0x7C03, 0x7C04, 0x7C05, 0x7C06, 0x7C08, 0x7C09, 0x7C0A, 0x7C0D, 0x7C0E, + 0x7C10, 0x7C11, 0x7C12, 0x7C13, 0x7C14, 0x7C15, 0x7C17, 0x7C18, 0x7C19, + 0x003F, 0x7C1A, 0x7C1B, 0x7C1C, 0x7C1D, 0x7C1E, 0x7C20, 0x7C21, 0x7C22, + 0x7C23, 0x7C24, 0x7C25, 0x7C28, 0x7C29, 0x7C2B, 0x7C2C, 0x7C2D, 0x7C2E, + 0x7C2F, 0x7C30, 0x7C31, 0x7C32, 0x7C33, 0x7C34, 0x7C35, 0x7C36, 0x7C37, + 0x7C39, 0x7C3A, 0x7C3B, 0x7C3C, 0x7C3D, 0x7C3E, 0x7C42, 0x9AB8, 0x5B69, + 0x6D77, 0x6C26, 0x4EA5, 0x5BB3, 0x9A87, 0x9163, 0x61A8, 0x90AF, 0x97E9, + 0x542B, 0x6DB5, 0x5BD2, 0x51FD, 0x558A, 0x7F55, 0x7FF0, 0x64BC, 0x634D, + 0x65F1, 0x61BE, 0x608D, 0x710A, 0x6C57, 0x6C49, 0x592F, 0x676D, 0x822A, + 0x58D5, 0x568E, 0x8C6A, 0x6BEB, 0x90DD, 0x597D, 0x8017, 0x53F7, 0x6D69, + 0x5475, 0x559D, 0x8377, 0x83CF, 0x6838, 0x79BE, 0x548C, 0x4F55, 0x5408, + 0x76D2, 0x8C89, 0x9602, 0x6CB3, 0x6DB8, 0x8D6B, 0x8910, 0x9E64, 0x8D3A, + 0x563F, 0x9ED1, 0x75D5, 0x5F88, 0x72E0, 0x6068, 0x54FC, 0x4EA8, 0x6A2A, + 0x8861, 0x6052, 0x8F70, 0x54C4, 0x70D8, 0x8679, 0x9E3F, 0x6D2A, 0x5B8F, + 0x5F18, 0x7EA2, 0x5589, 0x4FAF, 0x7334, 0x543C, 0x539A, 0x5019, 0x540E, + 0x547C, 0x4E4E, 0x5FFD, 0x745A, 0x58F6, 0x846B, 0x80E1, 0x8774, 0x72D0, + 0x7CCA, 0x6E56}, + {0x7C43, 0x7C44, 0x7C45, 0x7C46, 0x7C47, 0x7C48, 0x7C49, 0x7C4A, 0x7C4B, + 0x7C4C, 0x7C4E, 0x7C4F, 0x7C50, 0x7C51, 0x7C52, 0x7C53, 0x7C54, 0x7C55, + 0x7C56, 0x7C57, 0x7C58, 0x7C59, 0x7C5A, 0x7C5B, 0x7C5C, 0x7C5D, 0x7C5E, + 0x7C5F, 0x7C60, 0x7C61, 0x7C62, 0x7C63, 0x7C64, 0x7C65, 0x7C66, 0x7C67, + 0x7C68, 0x7C69, 0x7C6A, 0x7C6B, 0x7C6C, 0x7C6D, 0x7C6E, 0x7C6F, 0x7C70, + 0x7C71, 0x7C72, 0x7C75, 0x7C76, 0x7C77, 0x7C78, 0x7C79, 0x7C7A, 0x7C7E, + 0x7C7F, 0x7C80, 0x7C81, 0x7C82, 0x7C83, 0x7C84, 0x7C85, 0x7C86, 0x7C87, + 0x003F, 0x7C88, 0x7C8A, 0x7C8B, 0x7C8C, 0x7C8D, 0x7C8E, 0x7C8F, 0x7C90, + 0x7C93, 0x7C94, 0x7C96, 0x7C99, 0x7C9A, 0x7C9B, 0x7CA0, 0x7CA1, 0x7CA3, + 0x7CA6, 0x7CA7, 0x7CA8, 0x7CA9, 0x7CAB, 0x7CAC, 0x7CAD, 0x7CAF, 0x7CB0, + 0x7CB4, 0x7CB5, 0x7CB6, 0x7CB7, 0x7CB8, 0x7CBA, 0x7CBB, 0x5F27, 0x864E, + 0x552C, 0x62A4, 0x4E92, 0x6CAA, 0x6237, 0x82B1, 0x54D7, 0x534E, 0x733E, + 0x6ED1, 0x753B, 0x5212, 0x5316, 0x8BDD, 0x69D0, 0x5F8A, 0x6000, 0x6DEE, + 0x574F, 0x6B22, 0x73AF, 0x6853, 0x8FD8, 0x7F13, 0x6362, 0x60A3, 0x5524, + 0x75EA, 0x8C62, 0x7115, 0x6DA3, 0x5BA6, 0x5E7B, 0x8352, 0x614C, 0x9EC4, + 0x78FA, 0x8757, 0x7C27, 0x7687, 0x51F0, 0x60F6, 0x714C, 0x6643, 0x5E4C, + 0x604D, 0x8C0E, 0x7070, 0x6325, 0x8F89, 0x5FBD, 0x6062, 0x86D4, 0x56DE, + 0x6BC1, 0x6094, 0x6167, 0x5349, 0x60E0, 0x6666, 0x8D3F, 0x79FD, 0x4F1A, + 0x70E9, 0x6C47, 0x8BB3, 0x8BF2, 0x7ED8, 0x8364, 0x660F, 0x5A5A, 0x9B42, + 0x6D51, 0x6DF7, 0x8C41, 0x6D3B, 0x4F19, 0x706B, 0x83B7, 0x6216, 0x60D1, + 0x970D, 0x8D27, 0x7978, 0x51FB, 0x573E, 0x57FA, 0x673A, 0x7578, 0x7A3D, + 0x79EF, 0x7B95}, + {0x7CBF, 0x7CC0, 0x7CC2, 0x7CC3, 0x7CC4, 0x7CC6, 0x7CC9, 0x7CCB, 0x7CCE, + 0x7CCF, 0x7CD0, 0x7CD1, 0x7CD2, 0x7CD3, 0x7CD4, 0x7CD8, 0x7CDA, 0x7CDB, + 0x7CDD, 0x7CDE, 0x7CE1, 0x7CE2, 0x7CE3, 0x7CE4, 0x7CE5, 0x7CE6, 0x7CE7, + 0x7CE9, 0x7CEA, 0x7CEB, 0x7CEC, 0x7CED, 0x7CEE, 0x7CF0, 0x7CF1, 0x7CF2, + 0x7CF3, 0x7CF4, 0x7CF5, 0x7CF6, 0x7CF7, 0x7CF9, 0x7CFA, 0x7CFC, 0x7CFD, + 0x7CFE, 0x7CFF, 0x7D00, 0x7D01, 0x7D02, 0x7D03, 0x7D04, 0x7D05, 0x7D06, + 0x7D07, 0x7D08, 0x7D09, 0x7D0B, 0x7D0C, 0x7D0D, 0x7D0E, 0x7D0F, 0x7D10, + 0x003F, 0x7D11, 0x7D12, 0x7D13, 0x7D14, 0x7D15, 0x7D16, 0x7D17, 0x7D18, + 0x7D19, 0x7D1A, 0x7D1B, 0x7D1C, 0x7D1D, 0x7D1E, 0x7D1F, 0x7D21, 0x7D23, + 0x7D24, 0x7D25, 0x7D26, 0x7D28, 0x7D29, 0x7D2A, 0x7D2C, 0x7D2D, 0x7D2E, + 0x7D30, 0x7D31, 0x7D32, 0x7D33, 0x7D34, 0x7D35, 0x7D36, 0x808C, 0x9965, + 0x8FF9, 0x6FC0, 0x8BA5, 0x9E21, 0x59EC, 0x7EE9, 0x7F09, 0x5409, 0x6781, + 0x68D8, 0x8F91, 0x7C4D, 0x96C6, 0x53CA, 0x6025, 0x75BE, 0x6C72, 0x5373, + 0x5AC9, 0x7EA7, 0x6324, 0x51E0, 0x810A, 0x5DF1, 0x84DF, 0x6280, 0x5180, + 0x5B63, 0x4F0E, 0x796D, 0x5242, 0x60B8, 0x6D4E, 0x5BC4, 0x5BC2, 0x8BA1, + 0x8BB0, 0x65E2, 0x5FCC, 0x9645, 0x5993, 0x7EE7, 0x7EAA, 0x5609, 0x67B7, + 0x5939, 0x4F73, 0x5BB6, 0x52A0, 0x835A, 0x988A, 0x8D3E, 0x7532, 0x94BE, + 0x5047, 0x7A3C, 0x4EF7, 0x67B6, 0x9A7E, 0x5AC1, 0x6B7C, 0x76D1, 0x575A, + 0x5C16, 0x7B3A, 0x95F4, 0x714E, 0x517C, 0x80A9, 0x8270, 0x5978, 0x7F04, + 0x8327, 0x68C0, 0x67EC, 0x78B1, 0x7877, 0x62E3, 0x6361, 0x7B80, 0x4FED, + 0x526A, 0x51CF, 0x8350, 0x69DB, 0x9274, 0x8DF5, 0x8D31, 0x89C1, 0x952E, + 0x7BAD, 0x4EF6}, + {0x7D37, 0x7D38, 0x7D39, 0x7D3A, 0x7D3B, 0x7D3C, 0x7D3D, 0x7D3E, 0x7D3F, + 0x7D40, 0x7D41, 0x7D42, 0x7D43, 0x7D44, 0x7D45, 0x7D46, 0x7D47, 0x7D48, + 0x7D49, 0x7D4A, 0x7D4B, 0x7D4C, 0x7D4D, 0x7D4E, 0x7D4F, 0x7D50, 0x7D51, + 0x7D52, 0x7D53, 0x7D54, 0x7D55, 0x7D56, 0x7D57, 0x7D58, 0x7D59, 0x7D5A, + 0x7D5B, 0x7D5C, 0x7D5D, 0x7D5E, 0x7D5F, 0x7D60, 0x7D61, 0x7D62, 0x7D63, + 0x7D64, 0x7D65, 0x7D66, 0x7D67, 0x7D68, 0x7D69, 0x7D6A, 0x7D6B, 0x7D6C, + 0x7D6D, 0x7D6F, 0x7D70, 0x7D71, 0x7D72, 0x7D73, 0x7D74, 0x7D75, 0x7D76, + 0x003F, 0x7D78, 0x7D79, 0x7D7A, 0x7D7B, 0x7D7C, 0x7D7D, 0x7D7E, 0x7D7F, + 0x7D80, 0x7D81, 0x7D82, 0x7D83, 0x7D84, 0x7D85, 0x7D86, 0x7D87, 0x7D88, + 0x7D89, 0x7D8A, 0x7D8B, 0x7D8C, 0x7D8D, 0x7D8E, 0x7D8F, 0x7D90, 0x7D91, + 0x7D92, 0x7D93, 0x7D94, 0x7D95, 0x7D96, 0x7D97, 0x7D98, 0x5065, 0x8230, + 0x5251, 0x996F, 0x6E10, 0x6E85, 0x6DA7, 0x5EFA, 0x50F5, 0x59DC, 0x5C06, + 0x6D46, 0x6C5F, 0x7586, 0x848B, 0x6868, 0x5956, 0x8BB2, 0x5320, 0x9171, + 0x964D, 0x8549, 0x6912, 0x7901, 0x7126, 0x80F6, 0x4EA4, 0x90CA, 0x6D47, + 0x9A84, 0x5A07, 0x56BC, 0x6405, 0x94F0, 0x77EB, 0x4FA5, 0x811A, 0x72E1, + 0x89D2, 0x997A, 0x7F34, 0x7EDE, 0x527F, 0x6559, 0x9175, 0x8F7F, 0x8F83, + 0x53EB, 0x7A96, 0x63ED, 0x63A5, 0x7686, 0x79F8, 0x8857, 0x9636, 0x622A, + 0x52AB, 0x8282, 0x6854, 0x6770, 0x6377, 0x776B, 0x7AED, 0x6D01, 0x7ED3, + 0x89E3, 0x59D0, 0x6212, 0x85C9, 0x82A5, 0x754C, 0x501F, 0x4ECB, 0x75A5, + 0x8BEB, 0x5C4A, 0x5DFE, 0x7B4B, 0x65A4, 0x91D1, 0x4ECA, 0x6D25, 0x895F, + 0x7D27, 0x9526, 0x4EC5, 0x8C28, 0x8FDB, 0x9773, 0x664B, 0x7981, 0x8FD1, + 0x70EC, 0x6D78}, + {0x7D99, 0x7D9A, 0x7D9B, 0x7D9C, 0x7D9D, 0x7D9E, 0x7D9F, 0x7DA0, 0x7DA1, + 0x7DA2, 0x7DA3, 0x7DA4, 0x7DA5, 0x7DA7, 0x7DA8, 0x7DA9, 0x7DAA, 0x7DAB, + 0x7DAC, 0x7DAD, 0x7DAF, 0x7DB0, 0x7DB1, 0x7DB2, 0x7DB3, 0x7DB4, 0x7DB5, + 0x7DB6, 0x7DB7, 0x7DB8, 0x7DB9, 0x7DBA, 0x7DBB, 0x7DBC, 0x7DBD, 0x7DBE, + 0x7DBF, 0x7DC0, 0x7DC1, 0x7DC2, 0x7DC3, 0x7DC4, 0x7DC5, 0x7DC6, 0x7DC7, + 0x7DC8, 0x7DC9, 0x7DCA, 0x7DCB, 0x7DCC, 0x7DCD, 0x7DCE, 0x7DCF, 0x7DD0, + 0x7DD1, 0x7DD2, 0x7DD3, 0x7DD4, 0x7DD5, 0x7DD6, 0x7DD7, 0x7DD8, 0x7DD9, + 0x003F, 0x7DDA, 0x7DDB, 0x7DDC, 0x7DDD, 0x7DDE, 0x7DDF, 0x7DE0, 0x7DE1, + 0x7DE2, 0x7DE3, 0x7DE4, 0x7DE5, 0x7DE6, 0x7DE7, 0x7DE8, 0x7DE9, 0x7DEA, + 0x7DEB, 0x7DEC, 0x7DED, 0x7DEE, 0x7DEF, 0x7DF0, 0x7DF1, 0x7DF2, 0x7DF3, + 0x7DF4, 0x7DF5, 0x7DF6, 0x7DF7, 0x7DF8, 0x7DF9, 0x7DFA, 0x5C3D, 0x52B2, + 0x8346, 0x5162, 0x830E, 0x775B, 0x6676, 0x9CB8, 0x4EAC, 0x60CA, 0x7CBE, + 0x7CB3, 0x7ECF, 0x4E95, 0x8B66, 0x666F, 0x9888, 0x9759, 0x5883, 0x656C, + 0x955C, 0x5F84, 0x75C9, 0x9756, 0x7ADF, 0x7ADE, 0x51C0, 0x70AF, 0x7A98, + 0x63EA, 0x7A76, 0x7EA0, 0x7396, 0x97ED, 0x4E45, 0x7078, 0x4E5D, 0x9152, + 0x53A9, 0x6551, 0x65E7, 0x81FC, 0x8205, 0x548E, 0x5C31, 0x759A, 0x97A0, + 0x62D8, 0x72D9, 0x75BD, 0x5C45, 0x9A79, 0x83CA, 0x5C40, 0x5480, 0x77E9, + 0x4E3E, 0x6CAE, 0x805A, 0x62D2, 0x636E, 0x5DE8, 0x5177, 0x8DDD, 0x8E1E, + 0x952F, 0x4FF1, 0x53E5, 0x60E7, 0x70AC, 0x5267, 0x6350, 0x9E43, 0x5A1F, + 0x5026, 0x7737, 0x5377, 0x7EE2, 0x6485, 0x652B, 0x6289, 0x6398, 0x5014, + 0x7235, 0x89C9, 0x51B3, 0x8BC0, 0x7EDD, 0x5747, 0x83CC, 0x94A7, 0x519B, + 0x541B, 0x5CFB}, + {0x7DFB, 0x7DFC, 0x7DFD, 0x7DFE, 0x7DFF, 0x7E00, 0x7E01, 0x7E02, 0x7E03, + 0x7E04, 0x7E05, 0x7E06, 0x7E07, 0x7E08, 0x7E09, 0x7E0A, 0x7E0B, 0x7E0C, + 0x7E0D, 0x7E0E, 0x7E0F, 0x7E10, 0x7E11, 0x7E12, 0x7E13, 0x7E14, 0x7E15, + 0x7E16, 0x7E17, 0x7E18, 0x7E19, 0x7E1A, 0x7E1B, 0x7E1C, 0x7E1D, 0x7E1E, + 0x7E1F, 0x7E20, 0x7E21, 0x7E22, 0x7E23, 0x7E24, 0x7E25, 0x7E26, 0x7E27, + 0x7E28, 0x7E29, 0x7E2A, 0x7E2B, 0x7E2C, 0x7E2D, 0x7E2E, 0x7E2F, 0x7E30, + 0x7E31, 0x7E32, 0x7E33, 0x7E34, 0x7E35, 0x7E36, 0x7E37, 0x7E38, 0x7E39, + 0x003F, 0x7E3A, 0x7E3C, 0x7E3D, 0x7E3E, 0x7E3F, 0x7E40, 0x7E42, 0x7E43, + 0x7E44, 0x7E45, 0x7E46, 0x7E48, 0x7E49, 0x7E4A, 0x7E4B, 0x7E4C, 0x7E4D, + 0x7E4E, 0x7E4F, 0x7E50, 0x7E51, 0x7E52, 0x7E53, 0x7E54, 0x7E55, 0x7E56, + 0x7E57, 0x7E58, 0x7E59, 0x7E5A, 0x7E5B, 0x7E5C, 0x7E5D, 0x4FCA, 0x7AE3, + 0x6D5A, 0x90E1, 0x9A8F, 0x5580, 0x5496, 0x5361, 0x54AF, 0x5F00, 0x63E9, + 0x6977, 0x51EF, 0x6168, 0x520A, 0x582A, 0x52D8, 0x574E, 0x780D, 0x770B, + 0x5EB7, 0x6177, 0x7CE0, 0x625B, 0x6297, 0x4EA2, 0x7095, 0x8003, 0x62F7, + 0x70E4, 0x9760, 0x5777, 0x82DB, 0x67EF, 0x68F5, 0x78D5, 0x9897, 0x79D1, + 0x58F3, 0x54B3, 0x53EF, 0x6E34, 0x514B, 0x523B, 0x5BA2, 0x8BFE, 0x80AF, + 0x5543, 0x57A6, 0x6073, 0x5751, 0x542D, 0x7A7A, 0x6050, 0x5B54, 0x63A7, + 0x62A0, 0x53E3, 0x6263, 0x5BC7, 0x67AF, 0x54ED, 0x7A9F, 0x82E6, 0x9177, + 0x5E93, 0x88E4, 0x5938, 0x57AE, 0x630E, 0x8DE8, 0x80EF, 0x5757, 0x7B77, + 0x4FA9, 0x5FEB, 0x5BBD, 0x6B3E, 0x5321, 0x7B50, 0x72C2, 0x6846, 0x77FF, + 0x7736, 0x65F7, 0x51B5, 0x4E8F, 0x76D4, 0x5CBF, 0x7AA5, 0x8475, 0x594E, + 0x9B41, 0x5080}, + {0x7E5E, 0x7E5F, 0x7E60, 0x7E61, 0x7E62, 0x7E63, 0x7E64, 0x7E65, 0x7E66, + 0x7E67, 0x7E68, 0x7E69, 0x7E6A, 0x7E6B, 0x7E6C, 0x7E6D, 0x7E6E, 0x7E6F, + 0x7E70, 0x7E71, 0x7E72, 0x7E73, 0x7E74, 0x7E75, 0x7E76, 0x7E77, 0x7E78, + 0x7E79, 0x7E7A, 0x7E7B, 0x7E7C, 0x7E7D, 0x7E7E, 0x7E7F, 0x7E80, 0x7E81, + 0x7E83, 0x7E84, 0x7E85, 0x7E86, 0x7E87, 0x7E88, 0x7E89, 0x7E8A, 0x7E8B, + 0x7E8C, 0x7E8D, 0x7E8E, 0x7E8F, 0x7E90, 0x7E91, 0x7E92, 0x7E93, 0x7E94, + 0x7E95, 0x7E96, 0x7E97, 0x7E98, 0x7E99, 0x7E9A, 0x7E9C, 0x7E9D, 0x7E9E, + 0x003F, 0x7EAE, 0x7EB4, 0x7EBB, 0x7EBC, 0x7ED6, 0x7EE4, 0x7EEC, 0x7EF9, + 0x7F0A, 0x7F10, 0x7F1E, 0x7F37, 0x7F39, 0x7F3B, 0x7F3C, 0x7F3D, 0x7F3E, + 0x7F3F, 0x7F40, 0x7F41, 0x7F43, 0x7F46, 0x7F47, 0x7F48, 0x7F49, 0x7F4A, + 0x7F4B, 0x7F4C, 0x7F4D, 0x7F4E, 0x7F4F, 0x7F52, 0x7F53, 0x9988, 0x6127, + 0x6E83, 0x5764, 0x6606, 0x6346, 0x56F0, 0x62EC, 0x6269, 0x5ED3, 0x9614, + 0x5783, 0x62C9, 0x5587, 0x8721, 0x814A, 0x8FA3, 0x5566, 0x83B1, 0x6765, + 0x8D56, 0x84DD, 0x5A6A, 0x680F, 0x62E6, 0x7BEE, 0x9611, 0x5170, 0x6F9C, + 0x8C30, 0x63FD, 0x89C8, 0x61D2, 0x7F06, 0x70C2, 0x6EE5, 0x7405, 0x6994, + 0x72FC, 0x5ECA, 0x90CE, 0x6717, 0x6D6A, 0x635E, 0x52B3, 0x7262, 0x8001, + 0x4F6C, 0x59E5, 0x916A, 0x70D9, 0x6D9D, 0x52D2, 0x4E50, 0x96F7, 0x956D, + 0x857E, 0x78CA, 0x7D2F, 0x5121, 0x5792, 0x64C2, 0x808B, 0x7C7B, 0x6CEA, + 0x68F1, 0x695E, 0x51B7, 0x5398, 0x68A8, 0x7281, 0x9ECE, 0x7BF1, 0x72F8, + 0x79BB, 0x6F13, 0x7406, 0x674E, 0x91CC, 0x9CA4, 0x793C, 0x8389, 0x8354, + 0x540F, 0x6817, 0x4E3D, 0x5389, 0x52B1, 0x783E, 0x5386, 0x5229, 0x5088, + 0x4F8B, 0x4FD0}, + {0x7F56, 0x7F59, 0x7F5B, 0x7F5C, 0x7F5D, 0x7F5E, 0x7F60, 0x7F63, 0x7F64, + 0x7F65, 0x7F66, 0x7F67, 0x7F6B, 0x7F6C, 0x7F6D, 0x7F6F, 0x7F70, 0x7F73, + 0x7F75, 0x7F76, 0x7F77, 0x7F78, 0x7F7A, 0x7F7B, 0x7F7C, 0x7F7D, 0x7F7F, + 0x7F80, 0x7F82, 0x7F83, 0x7F84, 0x7F85, 0x7F86, 0x7F87, 0x7F88, 0x7F89, + 0x7F8B, 0x7F8D, 0x7F8F, 0x7F90, 0x7F91, 0x7F92, 0x7F93, 0x7F95, 0x7F96, + 0x7F97, 0x7F98, 0x7F99, 0x7F9B, 0x7F9C, 0x7FA0, 0x7FA2, 0x7FA3, 0x7FA5, + 0x7FA6, 0x7FA8, 0x7FA9, 0x7FAA, 0x7FAB, 0x7FAC, 0x7FAD, 0x7FAE, 0x7FB1, + 0x003F, 0x7FB3, 0x7FB4, 0x7FB5, 0x7FB6, 0x7FB7, 0x7FBA, 0x7FBB, 0x7FBE, + 0x7FC0, 0x7FC2, 0x7FC3, 0x7FC4, 0x7FC6, 0x7FC7, 0x7FC8, 0x7FC9, 0x7FCB, + 0x7FCD, 0x7FCF, 0x7FD0, 0x7FD1, 0x7FD2, 0x7FD3, 0x7FD6, 0x7FD7, 0x7FD9, + 0x7FDA, 0x7FDB, 0x7FDC, 0x7FDD, 0x7FDE, 0x7FE2, 0x7FE3, 0x75E2, 0x7ACB, + 0x7C92, 0x6CA5, 0x96B6, 0x529B, 0x7483, 0x54E9, 0x4FE9, 0x8054, 0x83B2, + 0x8FDE, 0x9570, 0x5EC9, 0x601C, 0x6D9F, 0x5E18, 0x655B, 0x8138, 0x94FE, + 0x604B, 0x70BC, 0x7EC3, 0x7CAE, 0x51C9, 0x6881, 0x7CB1, 0x826F, 0x4E24, + 0x8F86, 0x91CF, 0x667E, 0x4EAE, 0x8C05, 0x64A9, 0x804A, 0x50DA, 0x7597, + 0x71CE, 0x5BE5, 0x8FBD, 0x6F66, 0x4E86, 0x6482, 0x9563, 0x5ED6, 0x6599, + 0x5217, 0x88C2, 0x70C8, 0x52A3, 0x730E, 0x7433, 0x6797, 0x78F7, 0x9716, + 0x4E34, 0x90BB, 0x9CDE, 0x6DCB, 0x51DB, 0x8D41, 0x541D, 0x62CE, 0x73B2, + 0x83F1, 0x96F6, 0x9F84, 0x94C3, 0x4F36, 0x7F9A, 0x51CC, 0x7075, 0x9675, + 0x5CAD, 0x9886, 0x53E6, 0x4EE4, 0x6E9C, 0x7409, 0x69B4, 0x786B, 0x998F, + 0x7559, 0x5218, 0x7624, 0x6D41, 0x67F3, 0x516D, 0x9F99, 0x804B, 0x5499, + 0x7B3C, 0x7ABF}, + {0x7FE4, 0x7FE7, 0x7FE8, 0x7FEA, 0x7FEB, 0x7FEC, 0x7FED, 0x7FEF, 0x7FF2, + 0x7FF4, 0x7FF5, 0x7FF6, 0x7FF7, 0x7FF8, 0x7FF9, 0x7FFA, 0x7FFD, 0x7FFE, + 0x7FFF, 0x8002, 0x8007, 0x8008, 0x8009, 0x800A, 0x800E, 0x800F, 0x8011, + 0x8013, 0x801A, 0x801B, 0x801D, 0x801E, 0x801F, 0x8021, 0x8023, 0x8024, + 0x802B, 0x802C, 0x802D, 0x802E, 0x802F, 0x8030, 0x8032, 0x8034, 0x8039, + 0x803A, 0x803C, 0x803E, 0x8040, 0x8041, 0x8044, 0x8045, 0x8047, 0x8048, + 0x8049, 0x804E, 0x804F, 0x8050, 0x8051, 0x8053, 0x8055, 0x8056, 0x8057, + 0x003F, 0x8059, 0x805B, 0x805C, 0x805D, 0x805E, 0x805F, 0x8060, 0x8061, + 0x8062, 0x8063, 0x8064, 0x8065, 0x8066, 0x8067, 0x8068, 0x806B, 0x806C, + 0x806D, 0x806E, 0x806F, 0x8070, 0x8072, 0x8073, 0x8074, 0x8075, 0x8076, + 0x8077, 0x8078, 0x8079, 0x807A, 0x807B, 0x807C, 0x807D, 0x9686, 0x5784, + 0x62E2, 0x9647, 0x697C, 0x5A04, 0x6402, 0x7BD3, 0x6F0F, 0x964B, 0x82A6, + 0x5362, 0x9885, 0x5E90, 0x7089, 0x63B3, 0x5364, 0x864F, 0x9C81, 0x9E93, + 0x788C, 0x9732, 0x8DEF, 0x8D42, 0x9E7F, 0x6F5E, 0x7984, 0x5F55, 0x9646, + 0x622E, 0x9A74, 0x5415, 0x94DD, 0x4FA3, 0x65C5, 0x5C65, 0x5C61, 0x7F15, + 0x8651, 0x6C2F, 0x5F8B, 0x7387, 0x6EE4, 0x7EFF, 0x5CE6, 0x631B, 0x5B6A, + 0x6EE6, 0x5375, 0x4E71, 0x63A0, 0x7565, 0x62A1, 0x8F6E, 0x4F26, 0x4ED1, + 0x6CA6, 0x7EB6, 0x8BBA, 0x841D, 0x87BA, 0x7F57, 0x903B, 0x9523, 0x7BA9, + 0x9AA1, 0x88F8, 0x843D, 0x6D1B, 0x9A86, 0x7EDC, 0x5988, 0x9EBB, 0x739B, + 0x7801, 0x8682, 0x9A6C, 0x9A82, 0x561B, 0x5417, 0x57CB, 0x4E70, 0x9EA6, + 0x5356, 0x8FC8, 0x8109, 0x7792, 0x9992, 0x86EE, 0x6EE1, 0x8513, 0x66FC, + 0x6162, 0x6F2B}, + {0x807E, 0x8081, 0x8082, 0x8085, 0x8088, 0x808A, 0x808D, 0x808E, 0x808F, + 0x8090, 0x8091, 0x8092, 0x8094, 0x8095, 0x8097, 0x8099, 0x809E, 0x80A3, + 0x80A6, 0x80A7, 0x80A8, 0x80AC, 0x80B0, 0x80B3, 0x80B5, 0x80B6, 0x80B8, + 0x80B9, 0x80BB, 0x80C5, 0x80C7, 0x80C8, 0x80C9, 0x80CA, 0x80CB, 0x80CF, + 0x80D0, 0x80D1, 0x80D2, 0x80D3, 0x80D4, 0x80D5, 0x80D8, 0x80DF, 0x80E0, + 0x80E2, 0x80E3, 0x80E6, 0x80EE, 0x80F5, 0x80F7, 0x80F9, 0x80FB, 0x80FE, + 0x80FF, 0x8100, 0x8101, 0x8103, 0x8104, 0x8105, 0x8107, 0x8108, 0x810B, + 0x003F, 0x810C, 0x8115, 0x8117, 0x8119, 0x811B, 0x811C, 0x811D, 0x811F, + 0x8120, 0x8121, 0x8122, 0x8123, 0x8124, 0x8125, 0x8126, 0x8127, 0x8128, + 0x8129, 0x812A, 0x812B, 0x812D, 0x812E, 0x8130, 0x8133, 0x8134, 0x8135, + 0x8137, 0x8139, 0x813A, 0x813B, 0x813C, 0x813D, 0x813F, 0x8C29, 0x8292, + 0x832B, 0x76F2, 0x6C13, 0x5FD9, 0x83BD, 0x732B, 0x8305, 0x951A, 0x6BDB, + 0x77DB, 0x94C6, 0x536F, 0x8302, 0x5192, 0x5E3D, 0x8C8C, 0x8D38, 0x4E48, + 0x73AB, 0x679A, 0x6885, 0x9176, 0x9709, 0x7164, 0x6CA1, 0x7709, 0x5A92, + 0x9541, 0x6BCF, 0x7F8E, 0x6627, 0x5BD0, 0x59B9, 0x5A9A, 0x95E8, 0x95F7, + 0x4EEC, 0x840C, 0x8499, 0x6AAC, 0x76DF, 0x9530, 0x731B, 0x68A6, 0x5B5F, + 0x772F, 0x919A, 0x9761, 0x7CDC, 0x8FF7, 0x8C1C, 0x5F25, 0x7C73, 0x79D8, + 0x89C5, 0x6CCC, 0x871C, 0x5BC6, 0x5E42, 0x68C9, 0x7720, 0x7EF5, 0x5195, + 0x514D, 0x52C9, 0x5A29, 0x7F05, 0x9762, 0x82D7, 0x63CF, 0x7784, 0x85D0, + 0x79D2, 0x6E3A, 0x5E99, 0x5999, 0x8511, 0x706D, 0x6C11, 0x62BF, 0x76BF, + 0x654F, 0x60AF, 0x95FD, 0x660E, 0x879F, 0x9E23, 0x94ED, 0x540D, 0x547D, + 0x8C2C, 0x6478}, + {0x8140, 0x8141, 0x8142, 0x8143, 0x8144, 0x8145, 0x8147, 0x8149, 0x814D, + 0x814E, 0x814F, 0x8152, 0x8156, 0x8157, 0x8158, 0x815B, 0x815C, 0x815D, + 0x815E, 0x815F, 0x8161, 0x8162, 0x8163, 0x8164, 0x8166, 0x8168, 0x816A, + 0x816B, 0x816C, 0x816F, 0x8172, 0x8173, 0x8175, 0x8176, 0x8177, 0x8178, + 0x8181, 0x8183, 0x8184, 0x8185, 0x8186, 0x8187, 0x8189, 0x818B, 0x818C, + 0x818D, 0x818E, 0x8190, 0x8192, 0x8193, 0x8194, 0x8195, 0x8196, 0x8197, + 0x8199, 0x819A, 0x819E, 0x819F, 0x81A0, 0x81A1, 0x81A2, 0x81A4, 0x81A5, + 0x003F, 0x81A7, 0x81A9, 0x81AB, 0x81AC, 0x81AD, 0x81AE, 0x81AF, 0x81B0, + 0x81B1, 0x81B2, 0x81B4, 0x81B5, 0x81B6, 0x81B7, 0x81B8, 0x81B9, 0x81BC, + 0x81BD, 0x81BE, 0x81BF, 0x81C4, 0x81C5, 0x81C7, 0x81C8, 0x81C9, 0x81CB, + 0x81CD, 0x81CE, 0x81CF, 0x81D0, 0x81D1, 0x81D2, 0x81D3, 0x6479, 0x8611, + 0x6A21, 0x819C, 0x78E8, 0x6469, 0x9B54, 0x62B9, 0x672B, 0x83AB, 0x58A8, + 0x9ED8, 0x6CAB, 0x6F20, 0x5BDE, 0x964C, 0x8C0B, 0x725F, 0x67D0, 0x62C7, + 0x7261, 0x4EA9, 0x59C6, 0x6BCD, 0x5893, 0x66AE, 0x5E55, 0x52DF, 0x6155, + 0x6728, 0x76EE, 0x7766, 0x7267, 0x7A46, 0x62FF, 0x54EA, 0x5450, 0x94A0, + 0x90A3, 0x5A1C, 0x7EB3, 0x6C16, 0x4E43, 0x5976, 0x8010, 0x5948, 0x5357, + 0x7537, 0x96BE, 0x56CA, 0x6320, 0x8111, 0x607C, 0x95F9, 0x6DD6, 0x5462, + 0x9981, 0x5185, 0x5AE9, 0x80FD, 0x59AE, 0x9713, 0x502A, 0x6CE5, 0x5C3C, + 0x62DF, 0x4F60, 0x533F, 0x817B, 0x9006, 0x6EBA, 0x852B, 0x62C8, 0x5E74, + 0x78BE, 0x64B5, 0x637B, 0x5FF5, 0x5A18, 0x917F, 0x9E1F, 0x5C3F, 0x634F, + 0x8042, 0x5B7D, 0x556E, 0x954A, 0x954D, 0x6D85, 0x60A8, 0x67E0, 0x72DE, + 0x51DD, 0x5B81}, + {0x81D4, 0x81D5, 0x81D6, 0x81D7, 0x81D8, 0x81D9, 0x81DA, 0x81DB, 0x81DC, + 0x81DD, 0x81DE, 0x81DF, 0x81E0, 0x81E1, 0x81E2, 0x81E4, 0x81E5, 0x81E6, + 0x81E8, 0x81E9, 0x81EB, 0x81EE, 0x81EF, 0x81F0, 0x81F1, 0x81F2, 0x81F5, + 0x81F6, 0x81F7, 0x81F8, 0x81F9, 0x81FA, 0x81FD, 0x81FF, 0x8203, 0x8207, + 0x8208, 0x8209, 0x820A, 0x820B, 0x820E, 0x820F, 0x8211, 0x8213, 0x8215, + 0x8216, 0x8217, 0x8218, 0x8219, 0x821A, 0x821D, 0x8220, 0x8224, 0x8225, + 0x8226, 0x8227, 0x8229, 0x822E, 0x8232, 0x823A, 0x823C, 0x823D, 0x823F, + 0x003F, 0x8240, 0x8241, 0x8242, 0x8243, 0x8245, 0x8246, 0x8248, 0x824A, + 0x824C, 0x824D, 0x824E, 0x8250, 0x8251, 0x8252, 0x8253, 0x8254, 0x8255, + 0x8256, 0x8257, 0x8259, 0x825B, 0x825C, 0x825D, 0x825E, 0x8260, 0x8261, + 0x8262, 0x8263, 0x8264, 0x8265, 0x8266, 0x8267, 0x8269, 0x62E7, 0x6CDE, + 0x725B, 0x626D, 0x94AE, 0x7EBD, 0x8113, 0x6D53, 0x519C, 0x5F04, 0x5974, + 0x52AA, 0x6012, 0x5973, 0x6696, 0x8650, 0x759F, 0x632A, 0x61E6, 0x7CEF, + 0x8BFA, 0x54E6, 0x6B27, 0x9E25, 0x6BB4, 0x85D5, 0x5455, 0x5076, 0x6CA4, + 0x556A, 0x8DB4, 0x722C, 0x5E15, 0x6015, 0x7436, 0x62CD, 0x6392, 0x724C, + 0x5F98, 0x6E43, 0x6D3E, 0x6500, 0x6F58, 0x76D8, 0x78D0, 0x76FC, 0x7554, + 0x5224, 0x53DB, 0x4E53, 0x5E9E, 0x65C1, 0x802A, 0x80D6, 0x629B, 0x5486, + 0x5228, 0x70AE, 0x888D, 0x8DD1, 0x6CE1, 0x5478, 0x80DA, 0x57F9, 0x88F4, + 0x8D54, 0x966A, 0x914D, 0x4F69, 0x6C9B, 0x55B7, 0x76C6, 0x7830, 0x62A8, + 0x70F9, 0x6F8E, 0x5F6D, 0x84EC, 0x68DA, 0x787C, 0x7BF7, 0x81A8, 0x670B, + 0x9E4F, 0x6367, 0x78B0, 0x576F, 0x7812, 0x9739, 0x6279, 0x62AB, 0x5288, + 0x7435, 0x6BD7}, + {0x826A, 0x826B, 0x826C, 0x826D, 0x8271, 0x8275, 0x8276, 0x8277, 0x8278, + 0x827B, 0x827C, 0x8280, 0x8281, 0x8283, 0x8285, 0x8286, 0x8287, 0x8289, + 0x828C, 0x8290, 0x8293, 0x8294, 0x8295, 0x8296, 0x829A, 0x829B, 0x829E, + 0x82A0, 0x82A2, 0x82A3, 0x82A7, 0x82B2, 0x82B5, 0x82B6, 0x82BA, 0x82BB, + 0x82BC, 0x82BF, 0x82C0, 0x82C2, 0x82C3, 0x82C5, 0x82C6, 0x82C9, 0x82D0, + 0x82D6, 0x82D9, 0x82DA, 0x82DD, 0x82E2, 0x82E7, 0x82E8, 0x82E9, 0x82EA, + 0x82EC, 0x82ED, 0x82EE, 0x82F0, 0x82F2, 0x82F3, 0x82F5, 0x82F6, 0x82F8, + 0x003F, 0x82FA, 0x82FC, 0x82FD, 0x82FE, 0x82FF, 0x8300, 0x830A, 0x830B, + 0x830D, 0x8310, 0x8312, 0x8313, 0x8316, 0x8318, 0x8319, 0x831D, 0x831E, + 0x831F, 0x8320, 0x8321, 0x8322, 0x8323, 0x8324, 0x8325, 0x8326, 0x8329, + 0x832A, 0x832E, 0x8330, 0x8332, 0x8337, 0x833B, 0x833D, 0x5564, 0x813E, + 0x75B2, 0x76AE, 0x5339, 0x75DE, 0x50FB, 0x5C41, 0x8B6C, 0x7BC7, 0x504F, + 0x7247, 0x9A97, 0x98D8, 0x6F02, 0x74E2, 0x7968, 0x6487, 0x77A5, 0x62FC, + 0x9891, 0x8D2B, 0x54C1, 0x8058, 0x4E52, 0x576A, 0x82F9, 0x840D, 0x5E73, + 0x51ED, 0x74F6, 0x8BC4, 0x5C4F, 0x5761, 0x6CFC, 0x9887, 0x5A46, 0x7834, + 0x9B44, 0x8FEB, 0x7C95, 0x5256, 0x6251, 0x94FA, 0x4EC6, 0x8386, 0x8461, + 0x83E9, 0x84B2, 0x57D4, 0x6734, 0x5703, 0x666E, 0x6D66, 0x8C31, 0x66DD, + 0x7011, 0x671F, 0x6B3A, 0x6816, 0x621A, 0x59BB, 0x4E03, 0x51C4, 0x6F06, + 0x67D2, 0x6C8F, 0x5176, 0x68CB, 0x5947, 0x6B67, 0x7566, 0x5D0E, 0x8110, + 0x9F50, 0x65D7, 0x7948, 0x7941, 0x9A91, 0x8D77, 0x5C82, 0x4E5E, 0x4F01, + 0x542F, 0x5951, 0x780C, 0x5668, 0x6C14, 0x8FC4, 0x5F03, 0x6C7D, 0x6CE3, + 0x8BAB, 0x6390}, + {0x833E, 0x833F, 0x8341, 0x8342, 0x8344, 0x8345, 0x8348, 0x834A, 0x834B, + 0x834C, 0x834D, 0x834E, 0x8353, 0x8355, 0x8356, 0x8357, 0x8358, 0x8359, + 0x835D, 0x8362, 0x8370, 0x8371, 0x8372, 0x8373, 0x8374, 0x8375, 0x8376, + 0x8379, 0x837A, 0x837E, 0x837F, 0x8380, 0x8381, 0x8382, 0x8383, 0x8384, + 0x8387, 0x8388, 0x838A, 0x838B, 0x838C, 0x838D, 0x838F, 0x8390, 0x8391, + 0x8394, 0x8395, 0x8396, 0x8397, 0x8399, 0x839A, 0x839D, 0x839F, 0x83A1, + 0x83A2, 0x83A3, 0x83A4, 0x83A5, 0x83A6, 0x83A7, 0x83AC, 0x83AD, 0x83AE, + 0x003F, 0x83AF, 0x83B5, 0x83BB, 0x83BE, 0x83BF, 0x83C2, 0x83C3, 0x83C4, + 0x83C6, 0x83C8, 0x83C9, 0x83CB, 0x83CD, 0x83CE, 0x83D0, 0x83D1, 0x83D2, + 0x83D3, 0x83D5, 0x83D7, 0x83D9, 0x83DA, 0x83DB, 0x83DE, 0x83E2, 0x83E3, + 0x83E4, 0x83E6, 0x83E7, 0x83E8, 0x83EB, 0x83EC, 0x83ED, 0x6070, 0x6D3D, + 0x7275, 0x6266, 0x948E, 0x94C5, 0x5343, 0x8FC1, 0x7B7E, 0x4EDF, 0x8C26, + 0x4E7E, 0x9ED4, 0x94B1, 0x94B3, 0x524D, 0x6F5C, 0x9063, 0x6D45, 0x8C34, + 0x5811, 0x5D4C, 0x6B20, 0x6B49, 0x67AA, 0x545B, 0x8154, 0x7F8C, 0x5899, + 0x8537, 0x5F3A, 0x62A2, 0x6A47, 0x9539, 0x6572, 0x6084, 0x6865, 0x77A7, + 0x4E54, 0x4FA8, 0x5DE7, 0x9798, 0x64AC, 0x7FD8, 0x5CED, 0x4FCF, 0x7A8D, + 0x5207, 0x8304, 0x4E14, 0x602F, 0x7A83, 0x94A6, 0x4FB5, 0x4EB2, 0x79E6, + 0x7434, 0x52E4, 0x82B9, 0x64D2, 0x79BD, 0x5BDD, 0x6C81, 0x9752, 0x8F7B, + 0x6C22, 0x503E, 0x537F, 0x6E05, 0x64CE, 0x6674, 0x6C30, 0x60C5, 0x9877, + 0x8BF7, 0x5E86, 0x743C, 0x7A77, 0x79CB, 0x4E18, 0x90B1, 0x7403, 0x6C42, + 0x56DA, 0x914B, 0x6CC5, 0x8D8B, 0x533A, 0x86C6, 0x66F2, 0x8EAF, 0x5C48, + 0x9A71, 0x6E20}, + {0x83EE, 0x83EF, 0x83F3, 0x83F4, 0x83F5, 0x83F6, 0x83F7, 0x83FA, 0x83FB, + 0x83FC, 0x83FE, 0x83FF, 0x8400, 0x8402, 0x8405, 0x8407, 0x8408, 0x8409, + 0x840A, 0x8410, 0x8412, 0x8413, 0x8414, 0x8415, 0x8416, 0x8417, 0x8419, + 0x841A, 0x841B, 0x841E, 0x841F, 0x8420, 0x8421, 0x8422, 0x8423, 0x8429, + 0x842A, 0x842B, 0x842C, 0x842D, 0x842E, 0x842F, 0x8430, 0x8432, 0x8433, + 0x8434, 0x8435, 0x8436, 0x8437, 0x8439, 0x843A, 0x843B, 0x843E, 0x843F, + 0x8440, 0x8441, 0x8442, 0x8443, 0x8444, 0x8445, 0x8447, 0x8448, 0x8449, + 0x003F, 0x844A, 0x844B, 0x844C, 0x844D, 0x844E, 0x844F, 0x8450, 0x8452, + 0x8453, 0x8454, 0x8455, 0x8456, 0x8458, 0x845D, 0x845E, 0x845F, 0x8460, + 0x8462, 0x8464, 0x8465, 0x8466, 0x8467, 0x8468, 0x846A, 0x846E, 0x846F, + 0x8470, 0x8472, 0x8474, 0x8477, 0x8479, 0x847B, 0x847C, 0x53D6, 0x5A36, + 0x9F8B, 0x8DA3, 0x53BB, 0x5708, 0x98A7, 0x6743, 0x919B, 0x6CC9, 0x5168, + 0x75CA, 0x62F3, 0x72AC, 0x5238, 0x529D, 0x7F3A, 0x7094, 0x7638, 0x5374, + 0x9E4A, 0x69B7, 0x786E, 0x96C0, 0x88D9, 0x7FA4, 0x7136, 0x71C3, 0x5189, + 0x67D3, 0x74E4, 0x58E4, 0x6518, 0x56B7, 0x8BA9, 0x9976, 0x6270, 0x7ED5, + 0x60F9, 0x70ED, 0x58EC, 0x4EC1, 0x4EBA, 0x5FCD, 0x97E7, 0x4EFB, 0x8BA4, + 0x5203, 0x598A, 0x7EAB, 0x6254, 0x4ECD, 0x65E5, 0x620E, 0x8338, 0x84C9, + 0x8363, 0x878D, 0x7194, 0x6EB6, 0x5BB9, 0x7ED2, 0x5197, 0x63C9, 0x67D4, + 0x8089, 0x8339, 0x8815, 0x5112, 0x5B7A, 0x5982, 0x8FB1, 0x4E73, 0x6C5D, + 0x5165, 0x8925, 0x8F6F, 0x962E, 0x854A, 0x745E, 0x9510, 0x95F0, 0x6DA6, + 0x82E5, 0x5F31, 0x6492, 0x6D12, 0x8428, 0x816E, 0x9CC3, 0x585E, 0x8D5B, + 0x4E09, 0x53C1}, + {0x847D, 0x847E, 0x847F, 0x8480, 0x8481, 0x8483, 0x8484, 0x8485, 0x8486, + 0x848A, 0x848D, 0x848F, 0x8490, 0x8491, 0x8492, 0x8493, 0x8494, 0x8495, + 0x8496, 0x8498, 0x849A, 0x849B, 0x849D, 0x849E, 0x849F, 0x84A0, 0x84A2, + 0x84A3, 0x84A4, 0x84A5, 0x84A6, 0x84A7, 0x84A8, 0x84A9, 0x84AA, 0x84AB, + 0x84AC, 0x84AD, 0x84AE, 0x84B0, 0x84B1, 0x84B3, 0x84B5, 0x84B6, 0x84B7, + 0x84BB, 0x84BC, 0x84BE, 0x84C0, 0x84C2, 0x84C3, 0x84C5, 0x84C6, 0x84C7, + 0x84C8, 0x84CB, 0x84CC, 0x84CE, 0x84CF, 0x84D2, 0x84D4, 0x84D5, 0x84D7, + 0x003F, 0x84D8, 0x84D9, 0x84DA, 0x84DB, 0x84DC, 0x84DE, 0x84E1, 0x84E2, + 0x84E4, 0x84E7, 0x84E8, 0x84E9, 0x84EA, 0x84EB, 0x84ED, 0x84EE, 0x84EF, + 0x84F1, 0x84F2, 0x84F3, 0x84F4, 0x84F5, 0x84F6, 0x84F7, 0x84F8, 0x84F9, + 0x84FA, 0x84FB, 0x84FD, 0x84FE, 0x8500, 0x8501, 0x8502, 0x4F1E, 0x6563, + 0x6851, 0x55D3, 0x4E27, 0x6414, 0x9A9A, 0x626B, 0x5AC2, 0x745F, 0x8272, + 0x6DA9, 0x68EE, 0x50E7, 0x838E, 0x7802, 0x6740, 0x5239, 0x6C99, 0x7EB1, + 0x50BB, 0x5565, 0x715E, 0x7B5B, 0x6652, 0x73CA, 0x82EB, 0x6749, 0x5C71, + 0x5220, 0x717D, 0x886B, 0x95EA, 0x9655, 0x64C5, 0x8D61, 0x81B3, 0x5584, + 0x6C55, 0x6247, 0x7F2E, 0x5892, 0x4F24, 0x5546, 0x8D4F, 0x664C, 0x4E0A, + 0x5C1A, 0x88F3, 0x68A2, 0x634E, 0x7A0D, 0x70E7, 0x828D, 0x52FA, 0x97F6, + 0x5C11, 0x54E8, 0x90B5, 0x7ECD, 0x5962, 0x8D4A, 0x86C7, 0x820C, 0x820D, + 0x8D66, 0x6444, 0x5C04, 0x6151, 0x6D89, 0x793E, 0x8BBE, 0x7837, 0x7533, + 0x547B, 0x4F38, 0x8EAB, 0x6DF1, 0x5A20, 0x7EC5, 0x795E, 0x6C88, 0x5BA1, + 0x5A76, 0x751A, 0x80BE, 0x614E, 0x6E17, 0x58F0, 0x751F, 0x7525, 0x7272, + 0x5347, 0x7EF3}, + {0x8503, 0x8504, 0x8505, 0x8506, 0x8507, 0x8508, 0x8509, 0x850A, 0x850B, + 0x850D, 0x850E, 0x850F, 0x8510, 0x8512, 0x8514, 0x8515, 0x8516, 0x8518, + 0x8519, 0x851B, 0x851C, 0x851D, 0x851E, 0x8520, 0x8522, 0x8523, 0x8524, + 0x8525, 0x8526, 0x8527, 0x8528, 0x8529, 0x852A, 0x852D, 0x852E, 0x852F, + 0x8530, 0x8531, 0x8532, 0x8533, 0x8534, 0x8535, 0x8536, 0x853E, 0x853F, + 0x8540, 0x8541, 0x8542, 0x8544, 0x8545, 0x8546, 0x8547, 0x854B, 0x854C, + 0x854D, 0x854E, 0x854F, 0x8550, 0x8551, 0x8552, 0x8553, 0x8554, 0x8555, + 0x003F, 0x8557, 0x8558, 0x855A, 0x855B, 0x855C, 0x855D, 0x855F, 0x8560, + 0x8561, 0x8562, 0x8563, 0x8565, 0x8566, 0x8567, 0x8569, 0x856A, 0x856B, + 0x856C, 0x856D, 0x856E, 0x856F, 0x8570, 0x8571, 0x8573, 0x8575, 0x8576, + 0x8577, 0x8578, 0x857C, 0x857D, 0x857F, 0x8580, 0x8581, 0x7701, 0x76DB, + 0x5269, 0x80DC, 0x5723, 0x5E08, 0x5931, 0x72EE, 0x65BD, 0x6E7F, 0x8BD7, + 0x5C38, 0x8671, 0x5341, 0x77F3, 0x62FE, 0x65F6, 0x4EC0, 0x98DF, 0x8680, + 0x5B9E, 0x8BC6, 0x53F2, 0x77E2, 0x4F7F, 0x5C4E, 0x9A76, 0x59CB, 0x5F0F, + 0x793A, 0x58EB, 0x4E16, 0x67FF, 0x4E8B, 0x62ED, 0x8A93, 0x901D, 0x52BF, + 0x662F, 0x55DC, 0x566C, 0x9002, 0x4ED5, 0x4F8D, 0x91CA, 0x9970, 0x6C0F, + 0x5E02, 0x6043, 0x5BA4, 0x89C6, 0x8BD5, 0x6536, 0x624B, 0x9996, 0x5B88, + 0x5BFF, 0x6388, 0x552E, 0x53D7, 0x7626, 0x517D, 0x852C, 0x67A2, 0x68B3, + 0x6B8A, 0x6292, 0x8F93, 0x53D4, 0x8212, 0x6DD1, 0x758F, 0x4E66, 0x8D4E, + 0x5B70, 0x719F, 0x85AF, 0x6691, 0x66D9, 0x7F72, 0x8700, 0x9ECD, 0x9F20, + 0x5C5E, 0x672F, 0x8FF0, 0x6811, 0x675F, 0x620D, 0x7AD6, 0x5885, 0x5EB6, + 0x6570, 0x6F31}, + {0x8582, 0x8583, 0x8586, 0x8588, 0x8589, 0x858A, 0x858B, 0x858C, 0x858D, + 0x858E, 0x8590, 0x8591, 0x8592, 0x8593, 0x8594, 0x8595, 0x8596, 0x8597, + 0x8598, 0x8599, 0x859A, 0x859D, 0x859E, 0x859F, 0x85A0, 0x85A1, 0x85A2, + 0x85A3, 0x85A5, 0x85A6, 0x85A7, 0x85A9, 0x85AB, 0x85AC, 0x85AD, 0x85B1, + 0x85B2, 0x85B3, 0x85B4, 0x85B5, 0x85B6, 0x85B8, 0x85BA, 0x85BB, 0x85BC, + 0x85BD, 0x85BE, 0x85BF, 0x85C0, 0x85C2, 0x85C3, 0x85C4, 0x85C5, 0x85C6, + 0x85C7, 0x85C8, 0x85CA, 0x85CB, 0x85CC, 0x85CD, 0x85CE, 0x85D1, 0x85D2, + 0x003F, 0x85D4, 0x85D6, 0x85D7, 0x85D8, 0x85D9, 0x85DA, 0x85DB, 0x85DD, + 0x85DE, 0x85DF, 0x85E0, 0x85E1, 0x85E2, 0x85E3, 0x85E5, 0x85E6, 0x85E7, + 0x85E8, 0x85EA, 0x85EB, 0x85EC, 0x85ED, 0x85EE, 0x85EF, 0x85F0, 0x85F1, + 0x85F2, 0x85F3, 0x85F4, 0x85F5, 0x85F6, 0x85F7, 0x85F8, 0x6055, 0x5237, + 0x800D, 0x6454, 0x8870, 0x7529, 0x5E05, 0x6813, 0x62F4, 0x971C, 0x53CC, + 0x723D, 0x8C01, 0x6C34, 0x7761, 0x7A0E, 0x542E, 0x77AC, 0x987A, 0x821C, + 0x8BF4, 0x7855, 0x6714, 0x70C1, 0x65AF, 0x6495, 0x5636, 0x601D, 0x79C1, + 0x53F8, 0x4E1D, 0x6B7B, 0x8086, 0x5BFA, 0x55E3, 0x56DB, 0x4F3A, 0x4F3C, + 0x9972, 0x5DF3, 0x677E, 0x8038, 0x6002, 0x9882, 0x9001, 0x5B8B, 0x8BBC, + 0x8BF5, 0x641C, 0x8258, 0x64DE, 0x55FD, 0x82CF, 0x9165, 0x4FD7, 0x7D20, + 0x901F, 0x7C9F, 0x50F3, 0x5851, 0x6EAF, 0x5BBF, 0x8BC9, 0x8083, 0x9178, + 0x849C, 0x7B97, 0x867D, 0x968B, 0x968F, 0x7EE5, 0x9AD3, 0x788E, 0x5C81, + 0x7A57, 0x9042, 0x96A7, 0x795F, 0x5B59, 0x635F, 0x7B0B, 0x84D1, 0x68AD, + 0x5506, 0x7F29, 0x7410, 0x7D22, 0x9501, 0x6240, 0x584C, 0x4ED6, 0x5B83, + 0x5979, 0x5854}, + {0x85F9, 0x85FA, 0x85FC, 0x85FD, 0x85FE, 0x8600, 0x8601, 0x8602, 0x8603, + 0x8604, 0x8606, 0x8607, 0x8608, 0x8609, 0x860A, 0x860B, 0x860C, 0x860D, + 0x860E, 0x860F, 0x8610, 0x8612, 0x8613, 0x8614, 0x8615, 0x8617, 0x8618, + 0x8619, 0x861A, 0x861B, 0x861C, 0x861D, 0x861E, 0x861F, 0x8620, 0x8621, + 0x8622, 0x8623, 0x8624, 0x8625, 0x8626, 0x8628, 0x862A, 0x862B, 0x862C, + 0x862D, 0x862E, 0x862F, 0x8630, 0x8631, 0x8632, 0x8633, 0x8634, 0x8635, + 0x8636, 0x8637, 0x8639, 0x863A, 0x863B, 0x863D, 0x863E, 0x863F, 0x8640, + 0x003F, 0x8641, 0x8642, 0x8643, 0x8644, 0x8645, 0x8646, 0x8647, 0x8648, + 0x8649, 0x864A, 0x864B, 0x864C, 0x8652, 0x8653, 0x8655, 0x8656, 0x8657, + 0x8658, 0x8659, 0x865B, 0x865C, 0x865D, 0x865F, 0x8660, 0x8661, 0x8663, + 0x8664, 0x8665, 0x8666, 0x8667, 0x8668, 0x8669, 0x866A, 0x736D, 0x631E, + 0x8E4B, 0x8E0F, 0x80CE, 0x82D4, 0x62AC, 0x53F0, 0x6CF0, 0x915E, 0x592A, + 0x6001, 0x6C70, 0x574D, 0x644A, 0x8D2A, 0x762B, 0x6EE9, 0x575B, 0x6A80, + 0x75F0, 0x6F6D, 0x8C2D, 0x8C08, 0x5766, 0x6BEF, 0x8892, 0x78B3, 0x63A2, + 0x53F9, 0x70AD, 0x6C64, 0x5858, 0x642A, 0x5802, 0x68E0, 0x819B, 0x5510, + 0x7CD6, 0x5018, 0x8EBA, 0x6DCC, 0x8D9F, 0x70EB, 0x638F, 0x6D9B, 0x6ED4, + 0x7EE6, 0x8404, 0x6843, 0x9003, 0x6DD8, 0x9676, 0x8BA8, 0x5957, 0x7279, + 0x85E4, 0x817E, 0x75BC, 0x8A8A, 0x68AF, 0x5254, 0x8E22, 0x9511, 0x63D0, + 0x9898, 0x8E44, 0x557C, 0x4F53, 0x66FF, 0x568F, 0x60D5, 0x6D95, 0x5243, + 0x5C49, 0x5929, 0x6DFB, 0x586B, 0x7530, 0x751C, 0x606C, 0x8214, 0x8146, + 0x6311, 0x6761, 0x8FE2, 0x773A, 0x8DF3, 0x8D34, 0x94C1, 0x5E16, 0x5385, + 0x542C, 0x70C3}, + {0x866D, 0x866F, 0x8670, 0x8672, 0x8673, 0x8674, 0x8675, 0x8676, 0x8677, + 0x8678, 0x8683, 0x8684, 0x8685, 0x8686, 0x8687, 0x8688, 0x8689, 0x868E, + 0x868F, 0x8690, 0x8691, 0x8692, 0x8694, 0x8696, 0x8697, 0x8698, 0x8699, + 0x869A, 0x869B, 0x869E, 0x869F, 0x86A0, 0x86A1, 0x86A2, 0x86A5, 0x86A6, + 0x86AB, 0x86AD, 0x86AE, 0x86B2, 0x86B3, 0x86B7, 0x86B8, 0x86B9, 0x86BB, + 0x86BC, 0x86BD, 0x86BE, 0x86BF, 0x86C1, 0x86C2, 0x86C3, 0x86C5, 0x86C8, + 0x86CC, 0x86CD, 0x86D2, 0x86D3, 0x86D5, 0x86D6, 0x86D7, 0x86DA, 0x86DC, + 0x003F, 0x86DD, 0x86E0, 0x86E1, 0x86E2, 0x86E3, 0x86E5, 0x86E6, 0x86E7, + 0x86E8, 0x86EA, 0x86EB, 0x86EC, 0x86EF, 0x86F5, 0x86F6, 0x86F7, 0x86FA, + 0x86FB, 0x86FC, 0x86FD, 0x86FF, 0x8701, 0x8704, 0x8705, 0x8706, 0x870B, + 0x870C, 0x870E, 0x870F, 0x8710, 0x8711, 0x8714, 0x8716, 0x6C40, 0x5EF7, + 0x505C, 0x4EAD, 0x5EAD, 0x633A, 0x8247, 0x901A, 0x6850, 0x916E, 0x77B3, + 0x540C, 0x94DC, 0x5F64, 0x7AE5, 0x6876, 0x6345, 0x7B52, 0x7EDF, 0x75DB, + 0x5077, 0x6295, 0x5934, 0x900F, 0x51F8, 0x79C3, 0x7A81, 0x56FE, 0x5F92, + 0x9014, 0x6D82, 0x5C60, 0x571F, 0x5410, 0x5154, 0x6E4D, 0x56E2, 0x63A8, + 0x9893, 0x817F, 0x8715, 0x892A, 0x9000, 0x541E, 0x5C6F, 0x81C0, 0x62D6, + 0x6258, 0x8131, 0x9E35, 0x9640, 0x9A6E, 0x9A7C, 0x692D, 0x59A5, 0x62D3, + 0x553E, 0x6316, 0x54C7, 0x86D9, 0x6D3C, 0x5A03, 0x74E6, 0x889C, 0x6B6A, + 0x5916, 0x8C4C, 0x5F2F, 0x6E7E, 0x73A9, 0x987D, 0x4E38, 0x70F7, 0x5B8C, + 0x7897, 0x633D, 0x665A, 0x7696, 0x60CB, 0x5B9B, 0x5A49, 0x4E07, 0x8155, + 0x6C6A, 0x738B, 0x4EA1, 0x6789, 0x7F51, 0x5F80, 0x65FA, 0x671B, 0x5FD8, + 0x5984, 0x5A01}, + {0x8719, 0x871B, 0x871D, 0x871F, 0x8720, 0x8724, 0x8726, 0x8727, 0x8728, + 0x872A, 0x872B, 0x872C, 0x872D, 0x872F, 0x8730, 0x8732, 0x8733, 0x8735, + 0x8736, 0x8738, 0x8739, 0x873A, 0x873C, 0x873D, 0x8740, 0x8741, 0x8742, + 0x8743, 0x8744, 0x8745, 0x8746, 0x874A, 0x874B, 0x874D, 0x874F, 0x8750, + 0x8751, 0x8752, 0x8754, 0x8755, 0x8756, 0x8758, 0x875A, 0x875B, 0x875C, + 0x875D, 0x875E, 0x875F, 0x8761, 0x8762, 0x8766, 0x8767, 0x8768, 0x8769, + 0x876A, 0x876B, 0x876C, 0x876D, 0x876F, 0x8771, 0x8772, 0x8773, 0x8775, + 0x003F, 0x8777, 0x8778, 0x8779, 0x877A, 0x877F, 0x8780, 0x8781, 0x8784, + 0x8786, 0x8787, 0x8789, 0x878A, 0x878C, 0x878E, 0x878F, 0x8790, 0x8791, + 0x8792, 0x8794, 0x8795, 0x8796, 0x8798, 0x8799, 0x879A, 0x879B, 0x879C, + 0x879D, 0x879E, 0x87A0, 0x87A1, 0x87A2, 0x87A3, 0x87A4, 0x5DCD, 0x5FAE, + 0x5371, 0x97E6, 0x8FDD, 0x6845, 0x56F4, 0x552F, 0x60DF, 0x4E3A, 0x6F4D, + 0x7EF4, 0x82C7, 0x840E, 0x59D4, 0x4F1F, 0x4F2A, 0x5C3E, 0x7EAC, 0x672A, + 0x851A, 0x5473, 0x754F, 0x80C3, 0x5582, 0x9B4F, 0x4F4D, 0x6E2D, 0x8C13, + 0x5C09, 0x6170, 0x536B, 0x761F, 0x6E29, 0x868A, 0x6587, 0x95FB, 0x7EB9, + 0x543B, 0x7A33, 0x7D0A, 0x95EE, 0x55E1, 0x7FC1, 0x74EE, 0x631D, 0x8717, + 0x6DA1, 0x7A9D, 0x6211, 0x65A1, 0x5367, 0x63E1, 0x6C83, 0x5DEB, 0x545C, + 0x94A8, 0x4E4C, 0x6C61, 0x8BEC, 0x5C4B, 0x65E0, 0x829C, 0x68A7, 0x543E, + 0x5434, 0x6BCB, 0x6B66, 0x4E94, 0x6342, 0x5348, 0x821E, 0x4F0D, 0x4FAE, + 0x575E, 0x620A, 0x96FE, 0x6664, 0x7269, 0x52FF, 0x52A1, 0x609F, 0x8BEF, + 0x6614, 0x7199, 0x6790, 0x897F, 0x7852, 0x77FD, 0x6670, 0x563B, 0x5438, + 0x9521, 0x727A}, + {0x87A5, 0x87A6, 0x87A7, 0x87A9, 0x87AA, 0x87AE, 0x87B0, 0x87B1, 0x87B2, + 0x87B4, 0x87B6, 0x87B7, 0x87B8, 0x87B9, 0x87BB, 0x87BC, 0x87BE, 0x87BF, + 0x87C1, 0x87C2, 0x87C3, 0x87C4, 0x87C5, 0x87C7, 0x87C8, 0x87C9, 0x87CC, + 0x87CD, 0x87CE, 0x87CF, 0x87D0, 0x87D4, 0x87D5, 0x87D6, 0x87D7, 0x87D8, + 0x87D9, 0x87DA, 0x87DC, 0x87DD, 0x87DE, 0x87DF, 0x87E1, 0x87E2, 0x87E3, + 0x87E4, 0x87E6, 0x87E7, 0x87E8, 0x87E9, 0x87EB, 0x87EC, 0x87ED, 0x87EF, + 0x87F0, 0x87F1, 0x87F2, 0x87F3, 0x87F4, 0x87F5, 0x87F6, 0x87F7, 0x87F8, + 0x003F, 0x87FA, 0x87FB, 0x87FC, 0x87FD, 0x87FF, 0x8800, 0x8801, 0x8802, + 0x8804, 0x8805, 0x8806, 0x8807, 0x8808, 0x8809, 0x880B, 0x880C, 0x880D, + 0x880E, 0x880F, 0x8810, 0x8811, 0x8812, 0x8814, 0x8817, 0x8818, 0x8819, + 0x881A, 0x881C, 0x881D, 0x881E, 0x881F, 0x8820, 0x8823, 0x7A00, 0x606F, + 0x5E0C, 0x6089, 0x819D, 0x5915, 0x60DC, 0x7184, 0x70EF, 0x6EAA, 0x6C50, + 0x7280, 0x6A84, 0x88AD, 0x5E2D, 0x4E60, 0x5AB3, 0x559C, 0x94E3, 0x6D17, + 0x7CFB, 0x9699, 0x620F, 0x7EC6, 0x778E, 0x867E, 0x5323, 0x971E, 0x8F96, + 0x6687, 0x5CE1, 0x4FA0, 0x72ED, 0x4E0B, 0x53A6, 0x590F, 0x5413, 0x6380, + 0x9528, 0x5148, 0x4ED9, 0x9C9C, 0x7EA4, 0x54B8, 0x8D24, 0x8854, 0x8237, + 0x95F2, 0x6D8E, 0x5F26, 0x5ACC, 0x663E, 0x9669, 0x73B0, 0x732E, 0x53BF, + 0x817A, 0x9985, 0x7FA1, 0x5BAA, 0x9677, 0x9650, 0x7EBF, 0x76F8, 0x53A2, + 0x9576, 0x9999, 0x7BB1, 0x8944, 0x6E58, 0x4E61, 0x7FD4, 0x7965, 0x8BE6, + 0x60F3, 0x54CD, 0x4EAB, 0x9879, 0x5DF7, 0x6A61, 0x50CF, 0x5411, 0x8C61, + 0x8427, 0x785D, 0x9704, 0x524A, 0x54EE, 0x56A3, 0x9500, 0x6D88, 0x5BB5, + 0x6DC6, 0x6653}, + {0x8824, 0x8825, 0x8826, 0x8827, 0x8828, 0x8829, 0x882A, 0x882B, 0x882C, + 0x882D, 0x882E, 0x882F, 0x8830, 0x8831, 0x8833, 0x8834, 0x8835, 0x8836, + 0x8837, 0x8838, 0x883A, 0x883B, 0x883D, 0x883E, 0x883F, 0x8841, 0x8842, + 0x8843, 0x8846, 0x8847, 0x8848, 0x8849, 0x884A, 0x884B, 0x884E, 0x884F, + 0x8850, 0x8851, 0x8852, 0x8853, 0x8855, 0x8856, 0x8858, 0x885A, 0x885B, + 0x885C, 0x885D, 0x885E, 0x885F, 0x8860, 0x8866, 0x8867, 0x886A, 0x886D, + 0x886F, 0x8871, 0x8873, 0x8874, 0x8875, 0x8876, 0x8878, 0x8879, 0x887A, + 0x003F, 0x887B, 0x887C, 0x8880, 0x8883, 0x8886, 0x8887, 0x8889, 0x888A, + 0x888C, 0x888E, 0x888F, 0x8890, 0x8891, 0x8893, 0x8894, 0x8895, 0x8897, + 0x8898, 0x8899, 0x889A, 0x889B, 0x889D, 0x889E, 0x889F, 0x88A0, 0x88A1, + 0x88A3, 0x88A5, 0x88A6, 0x88A7, 0x88A8, 0x88A9, 0x88AA, 0x5C0F, 0x5B5D, + 0x6821, 0x8096, 0x5578, 0x7B11, 0x6548, 0x6954, 0x4E9B, 0x6B47, 0x874E, + 0x978B, 0x534F, 0x631F, 0x643A, 0x90AA, 0x659C, 0x80C1, 0x8C10, 0x5199, + 0x68B0, 0x5378, 0x87F9, 0x61C8, 0x6CC4, 0x6CFB, 0x8C22, 0x5C51, 0x85AA, + 0x82AF, 0x950C, 0x6B23, 0x8F9B, 0x65B0, 0x5FFB, 0x5FC3, 0x4FE1, 0x8845, + 0x661F, 0x8165, 0x7329, 0x60FA, 0x5174, 0x5211, 0x578B, 0x5F62, 0x90A2, + 0x884C, 0x9192, 0x5E78, 0x674F, 0x6027, 0x59D3, 0x5144, 0x51F6, 0x80F8, + 0x5308, 0x6C79, 0x96C4, 0x718A, 0x4F11, 0x4FEE, 0x7F9E, 0x673D, 0x55C5, + 0x9508, 0x79C0, 0x8896, 0x7EE3, 0x589F, 0x620C, 0x9700, 0x865A, 0x5618, + 0x987B, 0x5F90, 0x8BB8, 0x84C4, 0x9157, 0x53D9, 0x65ED, 0x5E8F, 0x755C, + 0x6064, 0x7D6E, 0x5A7F, 0x7EEA, 0x7EED, 0x8F69, 0x55A7, 0x5BA3, 0x60AC, + 0x65CB, 0x7384}, + {0x88AC, 0x88AE, 0x88AF, 0x88B0, 0x88B2, 0x88B3, 0x88B4, 0x88B5, 0x88B6, + 0x88B8, 0x88B9, 0x88BA, 0x88BB, 0x88BD, 0x88BE, 0x88BF, 0x88C0, 0x88C3, + 0x88C4, 0x88C7, 0x88C8, 0x88CA, 0x88CB, 0x88CC, 0x88CD, 0x88CF, 0x88D0, + 0x88D1, 0x88D3, 0x88D6, 0x88D7, 0x88DA, 0x88DB, 0x88DC, 0x88DD, 0x88DE, + 0x88E0, 0x88E1, 0x88E6, 0x88E7, 0x88E9, 0x88EA, 0x88EB, 0x88EC, 0x88ED, + 0x88EE, 0x88EF, 0x88F2, 0x88F5, 0x88F6, 0x88F7, 0x88FA, 0x88FB, 0x88FD, + 0x88FF, 0x8900, 0x8901, 0x8903, 0x8904, 0x8905, 0x8906, 0x8907, 0x8908, + 0x003F, 0x8909, 0x890B, 0x890C, 0x890D, 0x890E, 0x890F, 0x8911, 0x8914, + 0x8915, 0x8916, 0x8917, 0x8918, 0x891C, 0x891D, 0x891E, 0x891F, 0x8920, + 0x8922, 0x8923, 0x8924, 0x8926, 0x8927, 0x8928, 0x8929, 0x892C, 0x892D, + 0x892E, 0x892F, 0x8931, 0x8932, 0x8933, 0x8935, 0x8937, 0x9009, 0x7663, + 0x7729, 0x7EDA, 0x9774, 0x859B, 0x5B66, 0x7A74, 0x96EA, 0x8840, 0x52CB, + 0x718F, 0x5FAA, 0x65EC, 0x8BE2, 0x5BFB, 0x9A6F, 0x5DE1, 0x6B89, 0x6C5B, + 0x8BAD, 0x8BAF, 0x900A, 0x8FC5, 0x538B, 0x62BC, 0x9E26, 0x9E2D, 0x5440, + 0x4E2B, 0x82BD, 0x7259, 0x869C, 0x5D16, 0x8859, 0x6DAF, 0x96C5, 0x54D1, + 0x4E9A, 0x8BB6, 0x7109, 0x54BD, 0x9609, 0x70DF, 0x6DF9, 0x76D0, 0x4E25, + 0x7814, 0x8712, 0x5CA9, 0x5EF6, 0x8A00, 0x989C, 0x960E, 0x708E, 0x6CBF, + 0x5944, 0x63A9, 0x773C, 0x884D, 0x6F14, 0x8273, 0x5830, 0x71D5, 0x538C, + 0x781A, 0x96C1, 0x5501, 0x5F66, 0x7130, 0x5BB4, 0x8C1A, 0x9A8C, 0x6B83, + 0x592E, 0x9E2F, 0x79E7, 0x6768, 0x626C, 0x4F6F, 0x75A1, 0x7F8A, 0x6D0B, + 0x9633, 0x6C27, 0x4EF0, 0x75D2, 0x517B, 0x6837, 0x6F3E, 0x9080, 0x8170, + 0x5996, 0x7476}, + {0x8938, 0x8939, 0x893A, 0x893B, 0x893C, 0x893D, 0x893E, 0x893F, 0x8940, + 0x8942, 0x8943, 0x8945, 0x8946, 0x8947, 0x8948, 0x8949, 0x894A, 0x894B, + 0x894C, 0x894D, 0x894E, 0x894F, 0x8950, 0x8951, 0x8952, 0x8953, 0x8954, + 0x8955, 0x8956, 0x8957, 0x8958, 0x8959, 0x895A, 0x895B, 0x895C, 0x895D, + 0x8960, 0x8961, 0x8962, 0x8963, 0x8964, 0x8965, 0x8967, 0x8968, 0x8969, + 0x896A, 0x896B, 0x896C, 0x896D, 0x896E, 0x896F, 0x8970, 0x8971, 0x8972, + 0x8973, 0x8974, 0x8975, 0x8976, 0x8977, 0x8978, 0x8979, 0x897A, 0x897C, + 0x003F, 0x897D, 0x897E, 0x8980, 0x8982, 0x8984, 0x8985, 0x8987, 0x8988, + 0x8989, 0x898A, 0x898B, 0x898C, 0x898D, 0x898E, 0x898F, 0x8990, 0x8991, + 0x8992, 0x8993, 0x8994, 0x8995, 0x8996, 0x8997, 0x8998, 0x8999, 0x899A, + 0x899B, 0x899C, 0x899D, 0x899E, 0x899F, 0x89A0, 0x89A1, 0x6447, 0x5C27, + 0x9065, 0x7A91, 0x8C23, 0x59DA, 0x54AC, 0x8200, 0x836F, 0x8981, 0x8000, + 0x6930, 0x564E, 0x8036, 0x7237, 0x91CE, 0x51B6, 0x4E5F, 0x9875, 0x6396, + 0x4E1A, 0x53F6, 0x66F3, 0x814B, 0x591C, 0x6DB2, 0x4E00, 0x58F9, 0x533B, + 0x63D6, 0x94F1, 0x4F9D, 0x4F0A, 0x8863, 0x9890, 0x5937, 0x9057, 0x79FB, + 0x4EEA, 0x80F0, 0x7591, 0x6C82, 0x5B9C, 0x59E8, 0x5F5D, 0x6905, 0x8681, + 0x501A, 0x5DF2, 0x4E59, 0x77E3, 0x4EE5, 0x827A, 0x6291, 0x6613, 0x9091, + 0x5C79, 0x4EBF, 0x5F79, 0x81C6, 0x9038, 0x8084, 0x75AB, 0x4EA6, 0x88D4, + 0x610F, 0x6BC5, 0x5FC6, 0x4E49, 0x76CA, 0x6EA2, 0x8BE3, 0x8BAE, 0x8C0A, + 0x8BD1, 0x5F02, 0x7FFC, 0x7FCC, 0x7ECE, 0x8335, 0x836B, 0x56E0, 0x6BB7, + 0x97F3, 0x9634, 0x59FB, 0x541F, 0x94F6, 0x6DEB, 0x5BC5, 0x996E, 0x5C39, + 0x5F15, 0x9690}, + {0x89A2, 0x89A3, 0x89A4, 0x89A5, 0x89A6, 0x89A7, 0x89A8, 0x89A9, 0x89AA, + 0x89AB, 0x89AC, 0x89AD, 0x89AE, 0x89AF, 0x89B0, 0x89B1, 0x89B2, 0x89B3, + 0x89B4, 0x89B5, 0x89B6, 0x89B7, 0x89B8, 0x89B9, 0x89BA, 0x89BB, 0x89BC, + 0x89BD, 0x89BE, 0x89BF, 0x89C0, 0x89C3, 0x89CD, 0x89D3, 0x89D4, 0x89D5, + 0x89D7, 0x89D8, 0x89D9, 0x89DB, 0x89DD, 0x89DF, 0x89E0, 0x89E1, 0x89E2, + 0x89E4, 0x89E7, 0x89E8, 0x89E9, 0x89EA, 0x89EC, 0x89ED, 0x89EE, 0x89F0, + 0x89F1, 0x89F2, 0x89F4, 0x89F5, 0x89F6, 0x89F7, 0x89F8, 0x89F9, 0x89FA, + 0x003F, 0x89FB, 0x89FC, 0x89FD, 0x89FE, 0x89FF, 0x8A01, 0x8A02, 0x8A03, + 0x8A04, 0x8A05, 0x8A06, 0x8A08, 0x8A09, 0x8A0A, 0x8A0B, 0x8A0C, 0x8A0D, + 0x8A0E, 0x8A0F, 0x8A10, 0x8A11, 0x8A12, 0x8A13, 0x8A14, 0x8A15, 0x8A16, + 0x8A17, 0x8A18, 0x8A19, 0x8A1A, 0x8A1B, 0x8A1C, 0x8A1D, 0x5370, 0x82F1, + 0x6A31, 0x5A74, 0x9E70, 0x5E94, 0x7F28, 0x83B9, 0x8424, 0x8425, 0x8367, + 0x8747, 0x8FCE, 0x8D62, 0x76C8, 0x5F71, 0x9896, 0x786C, 0x6620, 0x54DF, + 0x62E5, 0x4F63, 0x81C3, 0x75C8, 0x5EB8, 0x96CD, 0x8E0A, 0x86F9, 0x548F, + 0x6CF3, 0x6D8C, 0x6C38, 0x607F, 0x52C7, 0x7528, 0x5E7D, 0x4F18, 0x60A0, + 0x5FE7, 0x5C24, 0x7531, 0x90AE, 0x94C0, 0x72B9, 0x6CB9, 0x6E38, 0x9149, + 0x6709, 0x53CB, 0x53F3, 0x4F51, 0x91C9, 0x8BF1, 0x53C8, 0x5E7C, 0x8FC2, + 0x6DE4, 0x4E8E, 0x76C2, 0x6986, 0x865E, 0x611A, 0x8206, 0x4F59, 0x4FDE, + 0x903E, 0x9C7C, 0x6109, 0x6E1D, 0x6E14, 0x9685, 0x4E88, 0x5A31, 0x96E8, + 0x4E0E, 0x5C7F, 0x79B9, 0x5B87, 0x8BED, 0x7FBD, 0x7389, 0x57DF, 0x828B, + 0x90C1, 0x5401, 0x9047, 0x55BB, 0x5CEA, 0x5FA1, 0x6108, 0x6B32, 0x72F1, + 0x80B2, 0x8A89}, + {0x8A1E, 0x8A1F, 0x8A20, 0x8A21, 0x8A22, 0x8A23, 0x8A24, 0x8A25, 0x8A26, + 0x8A27, 0x8A28, 0x8A29, 0x8A2A, 0x8A2B, 0x8A2C, 0x8A2D, 0x8A2E, 0x8A2F, + 0x8A30, 0x8A31, 0x8A32, 0x8A33, 0x8A34, 0x8A35, 0x8A36, 0x8A37, 0x8A38, + 0x8A39, 0x8A3A, 0x8A3B, 0x8A3C, 0x8A3D, 0x8A3F, 0x8A40, 0x8A41, 0x8A42, + 0x8A43, 0x8A44, 0x8A45, 0x8A46, 0x8A47, 0x8A49, 0x8A4A, 0x8A4B, 0x8A4C, + 0x8A4D, 0x8A4E, 0x8A4F, 0x8A50, 0x8A51, 0x8A52, 0x8A53, 0x8A54, 0x8A55, + 0x8A56, 0x8A57, 0x8A58, 0x8A59, 0x8A5A, 0x8A5B, 0x8A5C, 0x8A5D, 0x8A5E, + 0x003F, 0x8A5F, 0x8A60, 0x8A61, 0x8A62, 0x8A63, 0x8A64, 0x8A65, 0x8A66, + 0x8A67, 0x8A68, 0x8A69, 0x8A6A, 0x8A6B, 0x8A6C, 0x8A6D, 0x8A6E, 0x8A6F, + 0x8A70, 0x8A71, 0x8A72, 0x8A73, 0x8A74, 0x8A75, 0x8A76, 0x8A77, 0x8A78, + 0x8A7A, 0x8A7B, 0x8A7C, 0x8A7D, 0x8A7E, 0x8A7F, 0x8A80, 0x6D74, 0x5BD3, + 0x88D5, 0x9884, 0x8C6B, 0x9A6D, 0x9E33, 0x6E0A, 0x51A4, 0x5143, 0x57A3, + 0x8881, 0x539F, 0x63F4, 0x8F95, 0x56ED, 0x5458, 0x5706, 0x733F, 0x6E90, + 0x7F18, 0x8FDC, 0x82D1, 0x613F, 0x6028, 0x9662, 0x66F0, 0x7EA6, 0x8D8A, + 0x8DC3, 0x94A5, 0x5CB3, 0x7CA4, 0x6708, 0x60A6, 0x9605, 0x8018, 0x4E91, + 0x90E7, 0x5300, 0x9668, 0x5141, 0x8FD0, 0x8574, 0x915D, 0x6655, 0x97F5, + 0x5B55, 0x531D, 0x7838, 0x6742, 0x683D, 0x54C9, 0x707E, 0x5BB0, 0x8F7D, + 0x518D, 0x5728, 0x54B1, 0x6512, 0x6682, 0x8D5E, 0x8D43, 0x810F, 0x846C, + 0x906D, 0x7CDF, 0x51FF, 0x85FB, 0x67A3, 0x65E9, 0x6FA1, 0x86A4, 0x8E81, + 0x566A, 0x9020, 0x7682, 0x7076, 0x71E5, 0x8D23, 0x62E9, 0x5219, 0x6CFD, + 0x8D3C, 0x600E, 0x589E, 0x618E, 0x66FE, 0x8D60, 0x624E, 0x55B3, 0x6E23, + 0x672D, 0x8F67}, + {0x8A81, 0x8A82, 0x8A83, 0x8A84, 0x8A85, 0x8A86, 0x8A87, 0x8A88, 0x8A8B, + 0x8A8C, 0x8A8D, 0x8A8E, 0x8A8F, 0x8A90, 0x8A91, 0x8A92, 0x8A94, 0x8A95, + 0x8A96, 0x8A97, 0x8A98, 0x8A99, 0x8A9A, 0x8A9B, 0x8A9C, 0x8A9D, 0x8A9E, + 0x8A9F, 0x8AA0, 0x8AA1, 0x8AA2, 0x8AA3, 0x8AA4, 0x8AA5, 0x8AA6, 0x8AA7, + 0x8AA8, 0x8AA9, 0x8AAA, 0x8AAB, 0x8AAC, 0x8AAD, 0x8AAE, 0x8AAF, 0x8AB0, + 0x8AB1, 0x8AB2, 0x8AB3, 0x8AB4, 0x8AB5, 0x8AB6, 0x8AB7, 0x8AB8, 0x8AB9, + 0x8ABA, 0x8ABB, 0x8ABC, 0x8ABD, 0x8ABE, 0x8ABF, 0x8AC0, 0x8AC1, 0x8AC2, + 0x003F, 0x8AC3, 0x8AC4, 0x8AC5, 0x8AC6, 0x8AC7, 0x8AC8, 0x8AC9, 0x8ACA, + 0x8ACB, 0x8ACC, 0x8ACD, 0x8ACE, 0x8ACF, 0x8AD0, 0x8AD1, 0x8AD2, 0x8AD3, + 0x8AD4, 0x8AD5, 0x8AD6, 0x8AD7, 0x8AD8, 0x8AD9, 0x8ADA, 0x8ADB, 0x8ADC, + 0x8ADD, 0x8ADE, 0x8ADF, 0x8AE0, 0x8AE1, 0x8AE2, 0x8AE3, 0x94E1, 0x95F8, + 0x7728, 0x6805, 0x69A8, 0x548B, 0x4E4D, 0x70B8, 0x8BC8, 0x6458, 0x658B, + 0x5B85, 0x7A84, 0x503A, 0x5BE8, 0x77BB, 0x6BE1, 0x8A79, 0x7C98, 0x6CBE, + 0x76CF, 0x65A9, 0x8F97, 0x5D2D, 0x5C55, 0x8638, 0x6808, 0x5360, 0x6218, + 0x7AD9, 0x6E5B, 0x7EFD, 0x6A1F, 0x7AE0, 0x5F70, 0x6F33, 0x5F20, 0x638C, + 0x6DA8, 0x6756, 0x4E08, 0x5E10, 0x8D26, 0x4ED7, 0x80C0, 0x7634, 0x969C, + 0x62DB, 0x662D, 0x627E, 0x6CBC, 0x8D75, 0x7167, 0x7F69, 0x5146, 0x8087, + 0x53EC, 0x906E, 0x6298, 0x54F2, 0x86F0, 0x8F99, 0x8005, 0x9517, 0x8517, + 0x8FD9, 0x6D59, 0x73CD, 0x659F, 0x771F, 0x7504, 0x7827, 0x81FB, 0x8D1E, + 0x9488, 0x4FA6, 0x6795, 0x75B9, 0x8BCA, 0x9707, 0x632F, 0x9547, 0x9635, + 0x84B8, 0x6323, 0x7741, 0x5F81, 0x72F0, 0x4E89, 0x6014, 0x6574, 0x62EF, + 0x6B63, 0x653F}, + {0x8AE4, 0x8AE5, 0x8AE6, 0x8AE7, 0x8AE8, 0x8AE9, 0x8AEA, 0x8AEB, 0x8AEC, + 0x8AED, 0x8AEE, 0x8AEF, 0x8AF0, 0x8AF1, 0x8AF2, 0x8AF3, 0x8AF4, 0x8AF5, + 0x8AF6, 0x8AF7, 0x8AF8, 0x8AF9, 0x8AFA, 0x8AFB, 0x8AFC, 0x8AFD, 0x8AFE, + 0x8AFF, 0x8B00, 0x8B01, 0x8B02, 0x8B03, 0x8B04, 0x8B05, 0x8B06, 0x8B08, + 0x8B09, 0x8B0A, 0x8B0B, 0x8B0C, 0x8B0D, 0x8B0E, 0x8B0F, 0x8B10, 0x8B11, + 0x8B12, 0x8B13, 0x8B14, 0x8B15, 0x8B16, 0x8B17, 0x8B18, 0x8B19, 0x8B1A, + 0x8B1B, 0x8B1C, 0x8B1D, 0x8B1E, 0x8B1F, 0x8B20, 0x8B21, 0x8B22, 0x8B23, + 0x003F, 0x8B24, 0x8B25, 0x8B27, 0x8B28, 0x8B29, 0x8B2A, 0x8B2B, 0x8B2C, + 0x8B2D, 0x8B2E, 0x8B2F, 0x8B30, 0x8B31, 0x8B32, 0x8B33, 0x8B34, 0x8B35, + 0x8B36, 0x8B37, 0x8B38, 0x8B39, 0x8B3A, 0x8B3B, 0x8B3C, 0x8B3D, 0x8B3E, + 0x8B3F, 0x8B40, 0x8B41, 0x8B42, 0x8B43, 0x8B44, 0x8B45, 0x5E27, 0x75C7, + 0x90D1, 0x8BC1, 0x829D, 0x679D, 0x652F, 0x5431, 0x8718, 0x77E5, 0x80A2, + 0x8102, 0x6C41, 0x4E4B, 0x7EC7, 0x804C, 0x76F4, 0x690D, 0x6B96, 0x6267, + 0x503C, 0x4F84, 0x5740, 0x6307, 0x6B62, 0x8DBE, 0x53EA, 0x65E8, 0x7EB8, + 0x5FD7, 0x631A, 0x63B7, 0x81F3, 0x81F4, 0x7F6E, 0x5E1C, 0x5CD9, 0x5236, + 0x667A, 0x79E9, 0x7A1A, 0x8D28, 0x7099, 0x75D4, 0x6EDE, 0x6CBB, 0x7A92, + 0x4E2D, 0x76C5, 0x5FE0, 0x949F, 0x8877, 0x7EC8, 0x79CD, 0x80BF, 0x91CD, + 0x4EF2, 0x4F17, 0x821F, 0x5468, 0x5DDE, 0x6D32, 0x8BCC, 0x7CA5, 0x8F74, + 0x8098, 0x5E1A, 0x5492, 0x76B1, 0x5B99, 0x663C, 0x9AA4, 0x73E0, 0x682A, + 0x86DB, 0x6731, 0x732A, 0x8BF8, 0x8BDB, 0x9010, 0x7AF9, 0x70DB, 0x716E, + 0x62C4, 0x77A9, 0x5631, 0x4E3B, 0x8457, 0x67F1, 0x52A9, 0x86C0, 0x8D2E, + 0x94F8, 0x7B51}, + {0x8B46, 0x8B47, 0x8B48, 0x8B49, 0x8B4A, 0x8B4B, 0x8B4C, 0x8B4D, 0x8B4E, + 0x8B4F, 0x8B50, 0x8B51, 0x8B52, 0x8B53, 0x8B54, 0x8B55, 0x8B56, 0x8B57, + 0x8B58, 0x8B59, 0x8B5A, 0x8B5B, 0x8B5C, 0x8B5D, 0x8B5E, 0x8B5F, 0x8B60, + 0x8B61, 0x8B62, 0x8B63, 0x8B64, 0x8B65, 0x8B67, 0x8B68, 0x8B69, 0x8B6A, + 0x8B6B, 0x8B6D, 0x8B6E, 0x8B6F, 0x8B70, 0x8B71, 0x8B72, 0x8B73, 0x8B74, + 0x8B75, 0x8B76, 0x8B77, 0x8B78, 0x8B79, 0x8B7A, 0x8B7B, 0x8B7C, 0x8B7D, + 0x8B7E, 0x8B7F, 0x8B80, 0x8B81, 0x8B82, 0x8B83, 0x8B84, 0x8B85, 0x8B86, + 0x003F, 0x8B87, 0x8B88, 0x8B89, 0x8B8A, 0x8B8B, 0x8B8C, 0x8B8D, 0x8B8E, + 0x8B8F, 0x8B90, 0x8B91, 0x8B92, 0x8B93, 0x8B94, 0x8B95, 0x8B96, 0x8B97, + 0x8B98, 0x8B99, 0x8B9A, 0x8B9B, 0x8B9C, 0x8B9D, 0x8B9E, 0x8B9F, 0x8BAC, + 0x8BB1, 0x8BBB, 0x8BC7, 0x8BD0, 0x8BEA, 0x8C09, 0x8C1E, 0x4F4F, 0x6CE8, + 0x795D, 0x9A7B, 0x6293, 0x722A, 0x62FD, 0x4E13, 0x7816, 0x8F6C, 0x64B0, + 0x8D5A, 0x7BC6, 0x6869, 0x5E84, 0x88C5, 0x5986, 0x649E, 0x58EE, 0x72B6, + 0x690E, 0x9525, 0x8FFD, 0x8D58, 0x5760, 0x7F00, 0x8C06, 0x51C6, 0x6349, + 0x62D9, 0x5353, 0x684C, 0x7422, 0x8301, 0x914C, 0x5544, 0x7740, 0x707C, + 0x6D4A, 0x5179, 0x54A8, 0x8D44, 0x59FF, 0x6ECB, 0x6DC4, 0x5B5C, 0x7D2B, + 0x4ED4, 0x7C7D, 0x6ED3, 0x5B50, 0x81EA, 0x6E0D, 0x5B57, 0x9B03, 0x68D5, + 0x8E2A, 0x5B97, 0x7EFC, 0x603B, 0x7EB5, 0x90B9, 0x8D70, 0x594F, 0x63CD, + 0x79DF, 0x8DB3, 0x5352, 0x65CF, 0x7956, 0x8BC5, 0x963B, 0x7EC4, 0x94BB, + 0x7E82, 0x5634, 0x9189, 0x6700, 0x7F6A, 0x5C0A, 0x9075, 0x6628, 0x5DE6, + 0x4F50, 0x67DE, 0x505A, 0x4F5C, 0x5750, 0x5EA7, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F}, + {0x8C38, 0x8C39, 0x8C3A, 0x8C3B, 0x8C3C, 0x8C3D, 0x8C3E, 0x8C3F, 0x8C40, + 0x8C42, 0x8C43, 0x8C44, 0x8C45, 0x8C48, 0x8C4A, 0x8C4B, 0x8C4D, 0x8C4E, + 0x8C4F, 0x8C50, 0x8C51, 0x8C52, 0x8C53, 0x8C54, 0x8C56, 0x8C57, 0x8C58, + 0x8C59, 0x8C5B, 0x8C5C, 0x8C5D, 0x8C5E, 0x8C5F, 0x8C60, 0x8C63, 0x8C64, + 0x8C65, 0x8C66, 0x8C67, 0x8C68, 0x8C69, 0x8C6C, 0x8C6D, 0x8C6E, 0x8C6F, + 0x8C70, 0x8C71, 0x8C72, 0x8C74, 0x8C75, 0x8C76, 0x8C77, 0x8C7B, 0x8C7C, + 0x8C7D, 0x8C7E, 0x8C7F, 0x8C80, 0x8C81, 0x8C83, 0x8C84, 0x8C86, 0x8C87, + 0x003F, 0x8C88, 0x8C8B, 0x8C8D, 0x8C8E, 0x8C8F, 0x8C90, 0x8C91, 0x8C92, + 0x8C93, 0x8C95, 0x8C96, 0x8C97, 0x8C99, 0x8C9A, 0x8C9B, 0x8C9C, 0x8C9D, + 0x8C9E, 0x8C9F, 0x8CA0, 0x8CA1, 0x8CA2, 0x8CA3, 0x8CA4, 0x8CA5, 0x8CA6, + 0x8CA7, 0x8CA8, 0x8CA9, 0x8CAA, 0x8CAB, 0x8CAC, 0x8CAD, 0x4E8D, 0x4E0C, + 0x5140, 0x4E10, 0x5EFF, 0x5345, 0x4E15, 0x4E98, 0x4E1E, 0x9B32, 0x5B6C, + 0x5669, 0x4E28, 0x79BA, 0x4E3F, 0x5315, 0x4E47, 0x592D, 0x723B, 0x536E, + 0x6C10, 0x56DF, 0x80E4, 0x9997, 0x6BD3, 0x777E, 0x9F17, 0x4E36, 0x4E9F, + 0x9F10, 0x4E5C, 0x4E69, 0x4E93, 0x8288, 0x5B5B, 0x556C, 0x560F, 0x4EC4, + 0x538D, 0x539D, 0x53A3, 0x53A5, 0x53AE, 0x9765, 0x8D5D, 0x531A, 0x53F5, + 0x5326, 0x532E, 0x533E, 0x8D5C, 0x5366, 0x5363, 0x5202, 0x5208, 0x520E, + 0x522D, 0x5233, 0x523F, 0x5240, 0x524C, 0x525E, 0x5261, 0x525C, 0x84AF, + 0x527D, 0x5282, 0x5281, 0x5290, 0x5293, 0x5182, 0x7F54, 0x4EBB, 0x4EC3, + 0x4EC9, 0x4EC2, 0x4EE8, 0x4EE1, 0x4EEB, 0x4EDE, 0x4F1B, 0x4EF3, 0x4F22, + 0x4F64, 0x4EF5, 0x4F25, 0x4F27, 0x4F09, 0x4F2B, 0x4F5E, 0x4F67, 0x6538, + 0x4F5A, 0x4F5D}, + {0x8CAE, 0x8CAF, 0x8CB0, 0x8CB1, 0x8CB2, 0x8CB3, 0x8CB4, 0x8CB5, 0x8CB6, + 0x8CB7, 0x8CB8, 0x8CB9, 0x8CBA, 0x8CBB, 0x8CBC, 0x8CBD, 0x8CBE, 0x8CBF, + 0x8CC0, 0x8CC1, 0x8CC2, 0x8CC3, 0x8CC4, 0x8CC5, 0x8CC6, 0x8CC7, 0x8CC8, + 0x8CC9, 0x8CCA, 0x8CCB, 0x8CCC, 0x8CCD, 0x8CCE, 0x8CCF, 0x8CD0, 0x8CD1, + 0x8CD2, 0x8CD3, 0x8CD4, 0x8CD5, 0x8CD6, 0x8CD7, 0x8CD8, 0x8CD9, 0x8CDA, + 0x8CDB, 0x8CDC, 0x8CDD, 0x8CDE, 0x8CDF, 0x8CE0, 0x8CE1, 0x8CE2, 0x8CE3, + 0x8CE4, 0x8CE5, 0x8CE6, 0x8CE7, 0x8CE8, 0x8CE9, 0x8CEA, 0x8CEB, 0x8CEC, + 0x003F, 0x8CED, 0x8CEE, 0x8CEF, 0x8CF0, 0x8CF1, 0x8CF2, 0x8CF3, 0x8CF4, + 0x8CF5, 0x8CF6, 0x8CF7, 0x8CF8, 0x8CF9, 0x8CFA, 0x8CFB, 0x8CFC, 0x8CFD, + 0x8CFE, 0x8CFF, 0x8D00, 0x8D01, 0x8D02, 0x8D03, 0x8D04, 0x8D05, 0x8D06, + 0x8D07, 0x8D08, 0x8D09, 0x8D0A, 0x8D0B, 0x8D0C, 0x8D0D, 0x4F5F, 0x4F57, + 0x4F32, 0x4F3D, 0x4F76, 0x4F74, 0x4F91, 0x4F89, 0x4F83, 0x4F8F, 0x4F7E, + 0x4F7B, 0x4FAA, 0x4F7C, 0x4FAC, 0x4F94, 0x4FE6, 0x4FE8, 0x4FEA, 0x4FC5, + 0x4FDA, 0x4FE3, 0x4FDC, 0x4FD1, 0x4FDF, 0x4FF8, 0x5029, 0x504C, 0x4FF3, + 0x502C, 0x500F, 0x502E, 0x502D, 0x4FFE, 0x501C, 0x500C, 0x5025, 0x5028, + 0x507E, 0x5043, 0x5055, 0x5048, 0x504E, 0x506C, 0x507B, 0x50A5, 0x50A7, + 0x50A9, 0x50BA, 0x50D6, 0x5106, 0x50ED, 0x50EC, 0x50E6, 0x50EE, 0x5107, + 0x510B, 0x4EDD, 0x6C3D, 0x4F58, 0x4F65, 0x4FCE, 0x9FA0, 0x6C46, 0x7C74, + 0x516E, 0x5DFD, 0x9EC9, 0x9998, 0x5181, 0x5914, 0x52F9, 0x530D, 0x8A07, + 0x5310, 0x51EB, 0x5919, 0x5155, 0x4EA0, 0x5156, 0x4EB3, 0x886E, 0x88A4, + 0x4EB5, 0x8114, 0x88D2, 0x7980, 0x5B34, 0x8803, 0x7FB8, 0x51AB, 0x51B1, + 0x51BD, 0x51BC}, + {0x8D0E, 0x8D0F, 0x8D10, 0x8D11, 0x8D12, 0x8D13, 0x8D14, 0x8D15, 0x8D16, + 0x8D17, 0x8D18, 0x8D19, 0x8D1A, 0x8D1B, 0x8D1C, 0x8D20, 0x8D51, 0x8D52, + 0x8D57, 0x8D5F, 0x8D65, 0x8D68, 0x8D69, 0x8D6A, 0x8D6C, 0x8D6E, 0x8D6F, + 0x8D71, 0x8D72, 0x8D78, 0x8D79, 0x8D7A, 0x8D7B, 0x8D7C, 0x8D7D, 0x8D7E, + 0x8D7F, 0x8D80, 0x8D82, 0x8D83, 0x8D86, 0x8D87, 0x8D88, 0x8D89, 0x8D8C, + 0x8D8D, 0x8D8E, 0x8D8F, 0x8D90, 0x8D92, 0x8D93, 0x8D95, 0x8D96, 0x8D97, + 0x8D98, 0x8D99, 0x8D9A, 0x8D9B, 0x8D9C, 0x8D9D, 0x8D9E, 0x8DA0, 0x8DA1, + 0x003F, 0x8DA2, 0x8DA4, 0x8DA5, 0x8DA6, 0x8DA7, 0x8DA8, 0x8DA9, 0x8DAA, + 0x8DAB, 0x8DAC, 0x8DAD, 0x8DAE, 0x8DAF, 0x8DB0, 0x8DB2, 0x8DB6, 0x8DB7, + 0x8DB9, 0x8DBB, 0x8DBD, 0x8DC0, 0x8DC1, 0x8DC2, 0x8DC5, 0x8DC7, 0x8DC8, + 0x8DC9, 0x8DCA, 0x8DCD, 0x8DD0, 0x8DD2, 0x8DD3, 0x8DD4, 0x51C7, 0x5196, + 0x51A2, 0x51A5, 0x8BA0, 0x8BA6, 0x8BA7, 0x8BAA, 0x8BB4, 0x8BB5, 0x8BB7, + 0x8BC2, 0x8BC3, 0x8BCB, 0x8BCF, 0x8BCE, 0x8BD2, 0x8BD3, 0x8BD4, 0x8BD6, + 0x8BD8, 0x8BD9, 0x8BDC, 0x8BDF, 0x8BE0, 0x8BE4, 0x8BE8, 0x8BE9, 0x8BEE, + 0x8BF0, 0x8BF3, 0x8BF6, 0x8BF9, 0x8BFC, 0x8BFF, 0x8C00, 0x8C02, 0x8C04, + 0x8C07, 0x8C0C, 0x8C0F, 0x8C11, 0x8C12, 0x8C14, 0x8C15, 0x8C16, 0x8C19, + 0x8C1B, 0x8C18, 0x8C1D, 0x8C1F, 0x8C20, 0x8C21, 0x8C25, 0x8C27, 0x8C2A, + 0x8C2B, 0x8C2E, 0x8C2F, 0x8C32, 0x8C33, 0x8C35, 0x8C36, 0x5369, 0x537A, + 0x961D, 0x9622, 0x9621, 0x9631, 0x962A, 0x963D, 0x963C, 0x9642, 0x9649, + 0x9654, 0x965F, 0x9667, 0x966C, 0x9672, 0x9674, 0x9688, 0x968D, 0x9697, + 0x96B0, 0x9097, 0x909B, 0x909D, 0x9099, 0x90AC, 0x90A1, 0x90B4, 0x90B3, + 0x90B6, 0x90BA}, + {0x8DD5, 0x8DD8, 0x8DD9, 0x8DDC, 0x8DE0, 0x8DE1, 0x8DE2, 0x8DE5, 0x8DE6, + 0x8DE7, 0x8DE9, 0x8DED, 0x8DEE, 0x8DF0, 0x8DF1, 0x8DF2, 0x8DF4, 0x8DF6, + 0x8DFC, 0x8DFE, 0x8DFF, 0x8E00, 0x8E01, 0x8E02, 0x8E03, 0x8E04, 0x8E06, + 0x8E07, 0x8E08, 0x8E0B, 0x8E0D, 0x8E0E, 0x8E10, 0x8E11, 0x8E12, 0x8E13, + 0x8E15, 0x8E16, 0x8E17, 0x8E18, 0x8E19, 0x8E1A, 0x8E1B, 0x8E1C, 0x8E20, + 0x8E21, 0x8E24, 0x8E25, 0x8E26, 0x8E27, 0x8E28, 0x8E2B, 0x8E2D, 0x8E30, + 0x8E32, 0x8E33, 0x8E34, 0x8E36, 0x8E37, 0x8E38, 0x8E3B, 0x8E3C, 0x8E3E, + 0x003F, 0x8E3F, 0x8E43, 0x8E45, 0x8E46, 0x8E4C, 0x8E4D, 0x8E4E, 0x8E4F, + 0x8E50, 0x8E53, 0x8E54, 0x8E55, 0x8E56, 0x8E57, 0x8E58, 0x8E5A, 0x8E5B, + 0x8E5C, 0x8E5D, 0x8E5E, 0x8E5F, 0x8E60, 0x8E61, 0x8E62, 0x8E63, 0x8E64, + 0x8E65, 0x8E67, 0x8E68, 0x8E6A, 0x8E6B, 0x8E6E, 0x8E71, 0x90B8, 0x90B0, + 0x90CF, 0x90C5, 0x90BE, 0x90D0, 0x90C4, 0x90C7, 0x90D3, 0x90E6, 0x90E2, + 0x90DC, 0x90D7, 0x90DB, 0x90EB, 0x90EF, 0x90FE, 0x9104, 0x9122, 0x911E, + 0x9123, 0x9131, 0x912F, 0x9139, 0x9143, 0x9146, 0x520D, 0x5942, 0x52A2, + 0x52AC, 0x52AD, 0x52BE, 0x54FF, 0x52D0, 0x52D6, 0x52F0, 0x53DF, 0x71EE, + 0x77CD, 0x5EF4, 0x51F5, 0x51FC, 0x9B2F, 0x53B6, 0x5F01, 0x755A, 0x5DEF, + 0x574C, 0x57A9, 0x57A1, 0x587E, 0x58BC, 0x58C5, 0x58D1, 0x5729, 0x572C, + 0x572A, 0x5733, 0x5739, 0x572E, 0x572F, 0x575C, 0x573B, 0x5742, 0x5769, + 0x5785, 0x576B, 0x5786, 0x577C, 0x577B, 0x5768, 0x576D, 0x5776, 0x5773, + 0x57AD, 0x57A4, 0x578C, 0x57B2, 0x57CF, 0x57A7, 0x57B4, 0x5793, 0x57A0, + 0x57D5, 0x57D8, 0x57DA, 0x57D9, 0x57D2, 0x57B8, 0x57F4, 0x57EF, 0x57F8, + 0x57E4, 0x57DD}, + {0x8E73, 0x8E75, 0x8E77, 0x8E78, 0x8E79, 0x8E7A, 0x8E7B, 0x8E7D, 0x8E7E, + 0x8E80, 0x8E82, 0x8E83, 0x8E84, 0x8E86, 0x8E88, 0x8E89, 0x8E8A, 0x8E8B, + 0x8E8C, 0x8E8D, 0x8E8E, 0x8E91, 0x8E92, 0x8E93, 0x8E95, 0x8E96, 0x8E97, + 0x8E98, 0x8E99, 0x8E9A, 0x8E9B, 0x8E9D, 0x8E9F, 0x8EA0, 0x8EA1, 0x8EA2, + 0x8EA3, 0x8EA4, 0x8EA5, 0x8EA6, 0x8EA7, 0x8EA8, 0x8EA9, 0x8EAA, 0x8EAD, + 0x8EAE, 0x8EB0, 0x8EB1, 0x8EB3, 0x8EB4, 0x8EB5, 0x8EB6, 0x8EB7, 0x8EB8, + 0x8EB9, 0x8EBB, 0x8EBC, 0x8EBD, 0x8EBE, 0x8EBF, 0x8EC0, 0x8EC1, 0x8EC2, + 0x003F, 0x8EC3, 0x8EC4, 0x8EC5, 0x8EC6, 0x8EC7, 0x8EC8, 0x8EC9, 0x8ECA, + 0x8ECB, 0x8ECC, 0x8ECD, 0x8ECF, 0x8ED0, 0x8ED1, 0x8ED2, 0x8ED3, 0x8ED4, + 0x8ED5, 0x8ED6, 0x8ED7, 0x8ED8, 0x8ED9, 0x8EDA, 0x8EDB, 0x8EDC, 0x8EDD, + 0x8EDE, 0x8EDF, 0x8EE0, 0x8EE1, 0x8EE2, 0x8EE3, 0x8EE4, 0x580B, 0x580D, + 0x57FD, 0x57ED, 0x5800, 0x581E, 0x5819, 0x5844, 0x5820, 0x5865, 0x586C, + 0x5881, 0x5889, 0x589A, 0x5880, 0x99A8, 0x9F19, 0x61FF, 0x8279, 0x827D, + 0x827F, 0x828F, 0x828A, 0x82A8, 0x8284, 0x828E, 0x8291, 0x8297, 0x8299, + 0x82AB, 0x82B8, 0x82BE, 0x82B0, 0x82C8, 0x82CA, 0x82E3, 0x8298, 0x82B7, + 0x82AE, 0x82CB, 0x82CC, 0x82C1, 0x82A9, 0x82B4, 0x82A1, 0x82AA, 0x829F, + 0x82C4, 0x82CE, 0x82A4, 0x82E1, 0x8309, 0x82F7, 0x82E4, 0x830F, 0x8307, + 0x82DC, 0x82F4, 0x82D2, 0x82D8, 0x830C, 0x82FB, 0x82D3, 0x8311, 0x831A, + 0x8306, 0x8314, 0x8315, 0x82E0, 0x82D5, 0x831C, 0x8351, 0x835B, 0x835C, + 0x8308, 0x8392, 0x833C, 0x8334, 0x8331, 0x839B, 0x835E, 0x832F, 0x834F, + 0x8347, 0x8343, 0x835F, 0x8340, 0x8317, 0x8360, 0x832D, 0x833A, 0x8333, + 0x8366, 0x8365}, + {0x8EE5, 0x8EE6, 0x8EE7, 0x8EE8, 0x8EE9, 0x8EEA, 0x8EEB, 0x8EEC, 0x8EED, + 0x8EEE, 0x8EEF, 0x8EF0, 0x8EF1, 0x8EF2, 0x8EF3, 0x8EF4, 0x8EF5, 0x8EF6, + 0x8EF7, 0x8EF8, 0x8EF9, 0x8EFA, 0x8EFB, 0x8EFC, 0x8EFD, 0x8EFE, 0x8EFF, + 0x8F00, 0x8F01, 0x8F02, 0x8F03, 0x8F04, 0x8F05, 0x8F06, 0x8F07, 0x8F08, + 0x8F09, 0x8F0A, 0x8F0B, 0x8F0C, 0x8F0D, 0x8F0E, 0x8F0F, 0x8F10, 0x8F11, + 0x8F12, 0x8F13, 0x8F14, 0x8F15, 0x8F16, 0x8F17, 0x8F18, 0x8F19, 0x8F1A, + 0x8F1B, 0x8F1C, 0x8F1D, 0x8F1E, 0x8F1F, 0x8F20, 0x8F21, 0x8F22, 0x8F23, + 0x003F, 0x8F24, 0x8F25, 0x8F26, 0x8F27, 0x8F28, 0x8F29, 0x8F2A, 0x8F2B, + 0x8F2C, 0x8F2D, 0x8F2E, 0x8F2F, 0x8F30, 0x8F31, 0x8F32, 0x8F33, 0x8F34, + 0x8F35, 0x8F36, 0x8F37, 0x8F38, 0x8F39, 0x8F3A, 0x8F3B, 0x8F3C, 0x8F3D, + 0x8F3E, 0x8F3F, 0x8F40, 0x8F41, 0x8F42, 0x8F43, 0x8F44, 0x8368, 0x831B, + 0x8369, 0x836C, 0x836A, 0x836D, 0x836E, 0x83B0, 0x8378, 0x83B3, 0x83B4, + 0x83A0, 0x83AA, 0x8393, 0x839C, 0x8385, 0x837C, 0x83B6, 0x83A9, 0x837D, + 0x83B8, 0x837B, 0x8398, 0x839E, 0x83A8, 0x83BA, 0x83BC, 0x83C1, 0x8401, + 0x83E5, 0x83D8, 0x5807, 0x8418, 0x840B, 0x83DD, 0x83FD, 0x83D6, 0x841C, + 0x8438, 0x8411, 0x8406, 0x83D4, 0x83DF, 0x840F, 0x8403, 0x83F8, 0x83F9, + 0x83EA, 0x83C5, 0x83C0, 0x8426, 0x83F0, 0x83E1, 0x845C, 0x8451, 0x845A, + 0x8459, 0x8473, 0x8487, 0x8488, 0x847A, 0x8489, 0x8478, 0x843C, 0x8446, + 0x8469, 0x8476, 0x848C, 0x848E, 0x8431, 0x846D, 0x84C1, 0x84CD, 0x84D0, + 0x84E6, 0x84BD, 0x84D3, 0x84CA, 0x84BF, 0x84BA, 0x84E0, 0x84A1, 0x84B9, + 0x84B4, 0x8497, 0x84E5, 0x84E3, 0x850C, 0x750D, 0x8538, 0x84F0, 0x8539, + 0x851F, 0x853A}, + {0x8F45, 0x8F46, 0x8F47, 0x8F48, 0x8F49, 0x8F4A, 0x8F4B, 0x8F4C, 0x8F4D, + 0x8F4E, 0x8F4F, 0x8F50, 0x8F51, 0x8F52, 0x8F53, 0x8F54, 0x8F55, 0x8F56, + 0x8F57, 0x8F58, 0x8F59, 0x8F5A, 0x8F5B, 0x8F5C, 0x8F5D, 0x8F5E, 0x8F5F, + 0x8F60, 0x8F61, 0x8F62, 0x8F63, 0x8F64, 0x8F65, 0x8F6A, 0x8F80, 0x8F8C, + 0x8F92, 0x8F9D, 0x8FA0, 0x8FA1, 0x8FA2, 0x8FA4, 0x8FA5, 0x8FA6, 0x8FA7, + 0x8FAA, 0x8FAC, 0x8FAD, 0x8FAE, 0x8FAF, 0x8FB2, 0x8FB3, 0x8FB4, 0x8FB5, + 0x8FB7, 0x8FB8, 0x8FBA, 0x8FBB, 0x8FBC, 0x8FBF, 0x8FC0, 0x8FC3, 0x8FC6, + 0x003F, 0x8FC9, 0x8FCA, 0x8FCB, 0x8FCC, 0x8FCD, 0x8FCF, 0x8FD2, 0x8FD6, + 0x8FD7, 0x8FDA, 0x8FE0, 0x8FE1, 0x8FE3, 0x8FE7, 0x8FEC, 0x8FEF, 0x8FF1, + 0x8FF2, 0x8FF4, 0x8FF5, 0x8FF6, 0x8FFA, 0x8FFB, 0x8FFC, 0x8FFE, 0x8FFF, + 0x9007, 0x9008, 0x900C, 0x900E, 0x9013, 0x9015, 0x9018, 0x8556, 0x853B, + 0x84FF, 0x84FC, 0x8559, 0x8548, 0x8568, 0x8564, 0x855E, 0x857A, 0x77A2, + 0x8543, 0x8572, 0x857B, 0x85A4, 0x85A8, 0x8587, 0x858F, 0x8579, 0x85AE, + 0x859C, 0x8585, 0x85B9, 0x85B7, 0x85B0, 0x85D3, 0x85C1, 0x85DC, 0x85FF, + 0x8627, 0x8605, 0x8629, 0x8616, 0x863C, 0x5EFE, 0x5F08, 0x593C, 0x5941, + 0x8037, 0x5955, 0x595A, 0x5958, 0x530F, 0x5C22, 0x5C25, 0x5C2C, 0x5C34, + 0x624C, 0x626A, 0x629F, 0x62BB, 0x62CA, 0x62DA, 0x62D7, 0x62EE, 0x6322, + 0x62F6, 0x6339, 0x634B, 0x6343, 0x63AD, 0x63F6, 0x6371, 0x637A, 0x638E, + 0x63B4, 0x636D, 0x63AC, 0x638A, 0x6369, 0x63AE, 0x63BC, 0x63F2, 0x63F8, + 0x63E0, 0x63FF, 0x63C4, 0x63DE, 0x63CE, 0x6452, 0x63C6, 0x63BE, 0x6445, + 0x6441, 0x640B, 0x641B, 0x6420, 0x640C, 0x6426, 0x6421, 0x645E, 0x6484, + 0x646D, 0x6496}, + {0x9019, 0x901C, 0x9023, 0x9024, 0x9025, 0x9027, 0x9028, 0x9029, 0x902A, + 0x902B, 0x902C, 0x9030, 0x9031, 0x9032, 0x9033, 0x9034, 0x9037, 0x9039, + 0x903A, 0x903D, 0x903F, 0x9040, 0x9043, 0x9045, 0x9046, 0x9048, 0x9049, + 0x904A, 0x904B, 0x904C, 0x904E, 0x9054, 0x9055, 0x9056, 0x9059, 0x905A, + 0x905C, 0x905D, 0x905E, 0x905F, 0x9060, 0x9061, 0x9064, 0x9066, 0x9067, + 0x9069, 0x906A, 0x906B, 0x906C, 0x906F, 0x9070, 0x9071, 0x9072, 0x9073, + 0x9076, 0x9077, 0x9078, 0x9079, 0x907A, 0x907B, 0x907C, 0x907E, 0x9081, + 0x003F, 0x9084, 0x9085, 0x9086, 0x9087, 0x9089, 0x908A, 0x908C, 0x908D, + 0x908E, 0x908F, 0x9090, 0x9092, 0x9094, 0x9096, 0x9098, 0x909A, 0x909C, + 0x909E, 0x909F, 0x90A0, 0x90A4, 0x90A5, 0x90A7, 0x90A8, 0x90A9, 0x90AB, + 0x90AD, 0x90B2, 0x90B7, 0x90BC, 0x90BD, 0x90BF, 0x90C0, 0x647A, 0x64B7, + 0x64B8, 0x6499, 0x64BA, 0x64C0, 0x64D0, 0x64D7, 0x64E4, 0x64E2, 0x6509, + 0x6525, 0x652E, 0x5F0B, 0x5FD2, 0x7519, 0x5F11, 0x535F, 0x53F1, 0x53FD, + 0x53E9, 0x53E8, 0x53FB, 0x5412, 0x5416, 0x5406, 0x544B, 0x5452, 0x5453, + 0x5454, 0x5456, 0x5443, 0x5421, 0x5457, 0x5459, 0x5423, 0x5432, 0x5482, + 0x5494, 0x5477, 0x5471, 0x5464, 0x549A, 0x549B, 0x5484, 0x5476, 0x5466, + 0x549D, 0x54D0, 0x54AD, 0x54C2, 0x54B4, 0x54D2, 0x54A7, 0x54A6, 0x54D3, + 0x54D4, 0x5472, 0x54A3, 0x54D5, 0x54BB, 0x54BF, 0x54CC, 0x54D9, 0x54DA, + 0x54DC, 0x54A9, 0x54AA, 0x54A4, 0x54DD, 0x54CF, 0x54DE, 0x551B, 0x54E7, + 0x5520, 0x54FD, 0x5514, 0x54F3, 0x5522, 0x5523, 0x550F, 0x5511, 0x5527, + 0x552A, 0x5567, 0x558F, 0x55B5, 0x5549, 0x556D, 0x5541, 0x5555, 0x553F, + 0x5550, 0x553C}, + {0x90C2, 0x90C3, 0x90C6, 0x90C8, 0x90C9, 0x90CB, 0x90CC, 0x90CD, 0x90D2, + 0x90D4, 0x90D5, 0x90D6, 0x90D8, 0x90D9, 0x90DA, 0x90DE, 0x90DF, 0x90E0, + 0x90E3, 0x90E4, 0x90E5, 0x90E9, 0x90EA, 0x90EC, 0x90EE, 0x90F0, 0x90F1, + 0x90F2, 0x90F3, 0x90F5, 0x90F6, 0x90F7, 0x90F9, 0x90FA, 0x90FB, 0x90FC, + 0x90FF, 0x9100, 0x9101, 0x9103, 0x9105, 0x9106, 0x9107, 0x9108, 0x9109, + 0x910A, 0x910B, 0x910C, 0x910D, 0x910E, 0x910F, 0x9110, 0x9111, 0x9112, + 0x9113, 0x9114, 0x9115, 0x9116, 0x9117, 0x9118, 0x911A, 0x911B, 0x911C, + 0x003F, 0x911D, 0x911F, 0x9120, 0x9121, 0x9124, 0x9125, 0x9126, 0x9127, + 0x9128, 0x9129, 0x912A, 0x912B, 0x912C, 0x912D, 0x912E, 0x9130, 0x9132, + 0x9133, 0x9134, 0x9135, 0x9136, 0x9137, 0x9138, 0x913A, 0x913B, 0x913C, + 0x913D, 0x913E, 0x913F, 0x9140, 0x9141, 0x9142, 0x9144, 0x5537, 0x5556, + 0x5575, 0x5576, 0x5577, 0x5533, 0x5530, 0x555C, 0x558B, 0x55D2, 0x5583, + 0x55B1, 0x55B9, 0x5588, 0x5581, 0x559F, 0x557E, 0x55D6, 0x5591, 0x557B, + 0x55DF, 0x55BD, 0x55BE, 0x5594, 0x5599, 0x55EA, 0x55F7, 0x55C9, 0x561F, + 0x55D1, 0x55EB, 0x55EC, 0x55D4, 0x55E6, 0x55DD, 0x55C4, 0x55EF, 0x55E5, + 0x55F2, 0x55F3, 0x55CC, 0x55CD, 0x55E8, 0x55F5, 0x55E4, 0x8F94, 0x561E, + 0x5608, 0x560C, 0x5601, 0x5624, 0x5623, 0x55FE, 0x5600, 0x5627, 0x562D, + 0x5658, 0x5639, 0x5657, 0x562C, 0x564D, 0x5662, 0x5659, 0x565C, 0x564C, + 0x5654, 0x5686, 0x5664, 0x5671, 0x566B, 0x567B, 0x567C, 0x5685, 0x5693, + 0x56AF, 0x56D4, 0x56D7, 0x56DD, 0x56E1, 0x56F5, 0x56EB, 0x56F9, 0x56FF, + 0x5704, 0x570A, 0x5709, 0x571C, 0x5E0F, 0x5E19, 0x5E14, 0x5E11, 0x5E31, + 0x5E3B, 0x5E3C}, + {0x9145, 0x9147, 0x9148, 0x9151, 0x9153, 0x9154, 0x9155, 0x9156, 0x9158, + 0x9159, 0x915B, 0x915C, 0x915F, 0x9160, 0x9166, 0x9167, 0x9168, 0x916B, + 0x916D, 0x9173, 0x917A, 0x917B, 0x917C, 0x9180, 0x9181, 0x9182, 0x9183, + 0x9184, 0x9186, 0x9188, 0x918A, 0x918E, 0x918F, 0x9193, 0x9194, 0x9195, + 0x9196, 0x9197, 0x9198, 0x9199, 0x919C, 0x919D, 0x919E, 0x919F, 0x91A0, + 0x91A1, 0x91A4, 0x91A5, 0x91A6, 0x91A7, 0x91A8, 0x91A9, 0x91AB, 0x91AC, + 0x91B0, 0x91B1, 0x91B2, 0x91B3, 0x91B6, 0x91B7, 0x91B8, 0x91B9, 0x91BB, + 0x003F, 0x91BC, 0x91BD, 0x91BE, 0x91BF, 0x91C0, 0x91C1, 0x91C2, 0x91C3, + 0x91C4, 0x91C5, 0x91C6, 0x91C8, 0x91CB, 0x91D0, 0x91D2, 0x91D3, 0x91D4, + 0x91D5, 0x91D6, 0x91D7, 0x91D8, 0x91D9, 0x91DA, 0x91DB, 0x91DD, 0x91DE, + 0x91DF, 0x91E0, 0x91E1, 0x91E2, 0x91E3, 0x91E4, 0x91E5, 0x5E37, 0x5E44, + 0x5E54, 0x5E5B, 0x5E5E, 0x5E61, 0x5C8C, 0x5C7A, 0x5C8D, 0x5C90, 0x5C96, + 0x5C88, 0x5C98, 0x5C99, 0x5C91, 0x5C9A, 0x5C9C, 0x5CB5, 0x5CA2, 0x5CBD, + 0x5CAC, 0x5CAB, 0x5CB1, 0x5CA3, 0x5CC1, 0x5CB7, 0x5CC4, 0x5CD2, 0x5CE4, + 0x5CCB, 0x5CE5, 0x5D02, 0x5D03, 0x5D27, 0x5D26, 0x5D2E, 0x5D24, 0x5D1E, + 0x5D06, 0x5D1B, 0x5D58, 0x5D3E, 0x5D34, 0x5D3D, 0x5D6C, 0x5D5B, 0x5D6F, + 0x5D5D, 0x5D6B, 0x5D4B, 0x5D4A, 0x5D69, 0x5D74, 0x5D82, 0x5D99, 0x5D9D, + 0x8C73, 0x5DB7, 0x5DC5, 0x5F73, 0x5F77, 0x5F82, 0x5F87, 0x5F89, 0x5F8C, + 0x5F95, 0x5F99, 0x5F9C, 0x5FA8, 0x5FAD, 0x5FB5, 0x5FBC, 0x8862, 0x5F61, + 0x72AD, 0x72B0, 0x72B4, 0x72B7, 0x72B8, 0x72C3, 0x72C1, 0x72CE, 0x72CD, + 0x72D2, 0x72E8, 0x72EF, 0x72E9, 0x72F2, 0x72F4, 0x72F7, 0x7301, 0x72F3, + 0x7303, 0x72FA}, + {0x91E6, 0x91E7, 0x91E8, 0x91E9, 0x91EA, 0x91EB, 0x91EC, 0x91ED, 0x91EE, + 0x91EF, 0x91F0, 0x91F1, 0x91F2, 0x91F3, 0x91F4, 0x91F5, 0x91F6, 0x91F7, + 0x91F8, 0x91F9, 0x91FA, 0x91FB, 0x91FC, 0x91FD, 0x91FE, 0x91FF, 0x9200, + 0x9201, 0x9202, 0x9203, 0x9204, 0x9205, 0x9206, 0x9207, 0x9208, 0x9209, + 0x920A, 0x920B, 0x920C, 0x920D, 0x920E, 0x920F, 0x9210, 0x9211, 0x9212, + 0x9213, 0x9214, 0x9215, 0x9216, 0x9217, 0x9218, 0x9219, 0x921A, 0x921B, + 0x921C, 0x921D, 0x921E, 0x921F, 0x9220, 0x9221, 0x9222, 0x9223, 0x9224, + 0x003F, 0x9225, 0x9226, 0x9227, 0x9228, 0x9229, 0x922A, 0x922B, 0x922C, + 0x922D, 0x922E, 0x922F, 0x9230, 0x9231, 0x9232, 0x9233, 0x9234, 0x9235, + 0x9236, 0x9237, 0x9238, 0x9239, 0x923A, 0x923B, 0x923C, 0x923D, 0x923E, + 0x923F, 0x9240, 0x9241, 0x9242, 0x9243, 0x9244, 0x9245, 0x72FB, 0x7317, + 0x7313, 0x7321, 0x730A, 0x731E, 0x731D, 0x7315, 0x7322, 0x7339, 0x7325, + 0x732C, 0x7338, 0x7331, 0x7350, 0x734D, 0x7357, 0x7360, 0x736C, 0x736F, + 0x737E, 0x821B, 0x5925, 0x98E7, 0x5924, 0x5902, 0x9963, 0x9967, 0x9968, + 0x9969, 0x996A, 0x996B, 0x996C, 0x9974, 0x9977, 0x997D, 0x9980, 0x9984, + 0x9987, 0x998A, 0x998D, 0x9990, 0x9991, 0x9993, 0x9994, 0x9995, 0x5E80, + 0x5E91, 0x5E8B, 0x5E96, 0x5EA5, 0x5EA0, 0x5EB9, 0x5EB5, 0x5EBE, 0x5EB3, + 0x8D53, 0x5ED2, 0x5ED1, 0x5EDB, 0x5EE8, 0x5EEA, 0x81BA, 0x5FC4, 0x5FC9, + 0x5FD6, 0x5FCF, 0x6003, 0x5FEE, 0x6004, 0x5FE1, 0x5FE4, 0x5FFE, 0x6005, + 0x6006, 0x5FEA, 0x5FED, 0x5FF8, 0x6019, 0x6035, 0x6026, 0x601B, 0x600F, + 0x600D, 0x6029, 0x602B, 0x600A, 0x603F, 0x6021, 0x6078, 0x6079, 0x607B, + 0x607A, 0x6042}, + {0x9246, 0x9247, 0x9248, 0x9249, 0x924A, 0x924B, 0x924C, 0x924D, 0x924E, + 0x924F, 0x9250, 0x9251, 0x9252, 0x9253, 0x9254, 0x9255, 0x9256, 0x9257, + 0x9258, 0x9259, 0x925A, 0x925B, 0x925C, 0x925D, 0x925E, 0x925F, 0x9260, + 0x9261, 0x9262, 0x9263, 0x9264, 0x9265, 0x9266, 0x9267, 0x9268, 0x9269, + 0x926A, 0x926B, 0x926C, 0x926D, 0x926E, 0x926F, 0x9270, 0x9271, 0x9272, + 0x9273, 0x9275, 0x9276, 0x9277, 0x9278, 0x9279, 0x927A, 0x927B, 0x927C, + 0x927D, 0x927E, 0x927F, 0x9280, 0x9281, 0x9282, 0x9283, 0x9284, 0x9285, + 0x003F, 0x9286, 0x9287, 0x9288, 0x9289, 0x928A, 0x928B, 0x928C, 0x928D, + 0x928F, 0x9290, 0x9291, 0x9292, 0x9293, 0x9294, 0x9295, 0x9296, 0x9297, + 0x9298, 0x9299, 0x929A, 0x929B, 0x929C, 0x929D, 0x929E, 0x929F, 0x92A0, + 0x92A1, 0x92A2, 0x92A3, 0x92A4, 0x92A5, 0x92A6, 0x92A7, 0x606A, 0x607D, + 0x6096, 0x609A, 0x60AD, 0x609D, 0x6083, 0x6092, 0x608C, 0x609B, 0x60EC, + 0x60BB, 0x60B1, 0x60DD, 0x60D8, 0x60C6, 0x60DA, 0x60B4, 0x6120, 0x6126, + 0x6115, 0x6123, 0x60F4, 0x6100, 0x610E, 0x612B, 0x614A, 0x6175, 0x61AC, + 0x6194, 0x61A7, 0x61B7, 0x61D4, 0x61F5, 0x5FDD, 0x96B3, 0x95E9, 0x95EB, + 0x95F1, 0x95F3, 0x95F5, 0x95F6, 0x95FC, 0x95FE, 0x9603, 0x9604, 0x9606, + 0x9608, 0x960A, 0x960B, 0x960C, 0x960D, 0x960F, 0x9612, 0x9615, 0x9616, + 0x9617, 0x9619, 0x961A, 0x4E2C, 0x723F, 0x6215, 0x6C35, 0x6C54, 0x6C5C, + 0x6C4A, 0x6CA3, 0x6C85, 0x6C90, 0x6C94, 0x6C8C, 0x6C68, 0x6C69, 0x6C74, + 0x6C76, 0x6C86, 0x6CA9, 0x6CD0, 0x6CD4, 0x6CAD, 0x6CF7, 0x6CF8, 0x6CF1, + 0x6CD7, 0x6CB2, 0x6CE0, 0x6CD6, 0x6CFA, 0x6CEB, 0x6CEE, 0x6CB1, 0x6CD3, + 0x6CEF, 0x6CFE}, + {0x92A8, 0x92A9, 0x92AA, 0x92AB, 0x92AC, 0x92AD, 0x92AF, 0x92B0, 0x92B1, + 0x92B2, 0x92B3, 0x92B4, 0x92B5, 0x92B6, 0x92B7, 0x92B8, 0x92B9, 0x92BA, + 0x92BB, 0x92BC, 0x92BD, 0x92BE, 0x92BF, 0x92C0, 0x92C1, 0x92C2, 0x92C3, + 0x92C4, 0x92C5, 0x92C6, 0x92C7, 0x92C9, 0x92CA, 0x92CB, 0x92CC, 0x92CD, + 0x92CE, 0x92CF, 0x92D0, 0x92D1, 0x92D2, 0x92D3, 0x92D4, 0x92D5, 0x92D6, + 0x92D7, 0x92D8, 0x92D9, 0x92DA, 0x92DB, 0x92DC, 0x92DD, 0x92DE, 0x92DF, + 0x92E0, 0x92E1, 0x92E2, 0x92E3, 0x92E4, 0x92E5, 0x92E6, 0x92E7, 0x92E8, + 0x003F, 0x92E9, 0x92EA, 0x92EB, 0x92EC, 0x92ED, 0x92EE, 0x92EF, 0x92F0, + 0x92F1, 0x92F2, 0x92F3, 0x92F4, 0x92F5, 0x92F6, 0x92F7, 0x92F8, 0x92F9, + 0x92FA, 0x92FB, 0x92FC, 0x92FD, 0x92FE, 0x92FF, 0x9300, 0x9301, 0x9302, + 0x9303, 0x9304, 0x9305, 0x9306, 0x9307, 0x9308, 0x9309, 0x6D39, 0x6D27, + 0x6D0C, 0x6D43, 0x6D48, 0x6D07, 0x6D04, 0x6D19, 0x6D0E, 0x6D2B, 0x6D4D, + 0x6D2E, 0x6D35, 0x6D1A, 0x6D4F, 0x6D52, 0x6D54, 0x6D33, 0x6D91, 0x6D6F, + 0x6D9E, 0x6DA0, 0x6D5E, 0x6D93, 0x6D94, 0x6D5C, 0x6D60, 0x6D7C, 0x6D63, + 0x6E1A, 0x6DC7, 0x6DC5, 0x6DDE, 0x6E0E, 0x6DBF, 0x6DE0, 0x6E11, 0x6DE6, + 0x6DDD, 0x6DD9, 0x6E16, 0x6DAB, 0x6E0C, 0x6DAE, 0x6E2B, 0x6E6E, 0x6E4E, + 0x6E6B, 0x6EB2, 0x6E5F, 0x6E86, 0x6E53, 0x6E54, 0x6E32, 0x6E25, 0x6E44, + 0x6EDF, 0x6EB1, 0x6E98, 0x6EE0, 0x6F2D, 0x6EE2, 0x6EA5, 0x6EA7, 0x6EBD, + 0x6EBB, 0x6EB7, 0x6ED7, 0x6EB4, 0x6ECF, 0x6E8F, 0x6EC2, 0x6E9F, 0x6F62, + 0x6F46, 0x6F47, 0x6F24, 0x6F15, 0x6EF9, 0x6F2F, 0x6F36, 0x6F4B, 0x6F74, + 0x6F2A, 0x6F09, 0x6F29, 0x6F89, 0x6F8D, 0x6F8C, 0x6F78, 0x6F72, 0x6F7C, + 0x6F7A, 0x6FD1}, + {0x930A, 0x930B, 0x930C, 0x930D, 0x930E, 0x930F, 0x9310, 0x9311, 0x9312, + 0x9313, 0x9314, 0x9315, 0x9316, 0x9317, 0x9318, 0x9319, 0x931A, 0x931B, + 0x931C, 0x931D, 0x931E, 0x931F, 0x9320, 0x9321, 0x9322, 0x9323, 0x9324, + 0x9325, 0x9326, 0x9327, 0x9328, 0x9329, 0x932A, 0x932B, 0x932C, 0x932D, + 0x932E, 0x932F, 0x9330, 0x9331, 0x9332, 0x9333, 0x9334, 0x9335, 0x9336, + 0x9337, 0x9338, 0x9339, 0x933A, 0x933B, 0x933C, 0x933D, 0x933F, 0x9340, + 0x9341, 0x9342, 0x9343, 0x9344, 0x9345, 0x9346, 0x9347, 0x9348, 0x9349, + 0x003F, 0x934A, 0x934B, 0x934C, 0x934D, 0x934E, 0x934F, 0x9350, 0x9351, + 0x9352, 0x9353, 0x9354, 0x9355, 0x9356, 0x9357, 0x9358, 0x9359, 0x935A, + 0x935B, 0x935C, 0x935D, 0x935E, 0x935F, 0x9360, 0x9361, 0x9362, 0x9363, + 0x9364, 0x9365, 0x9366, 0x9367, 0x9368, 0x9369, 0x936B, 0x6FC9, 0x6FA7, + 0x6FB9, 0x6FB6, 0x6FC2, 0x6FE1, 0x6FEE, 0x6FDE, 0x6FE0, 0x6FEF, 0x701A, + 0x7023, 0x701B, 0x7039, 0x7035, 0x704F, 0x705E, 0x5B80, 0x5B84, 0x5B95, + 0x5B93, 0x5BA5, 0x5BB8, 0x752F, 0x9A9E, 0x6434, 0x5BE4, 0x5BEE, 0x8930, + 0x5BF0, 0x8E47, 0x8B07, 0x8FB6, 0x8FD3, 0x8FD5, 0x8FE5, 0x8FEE, 0x8FE4, + 0x8FE9, 0x8FE6, 0x8FF3, 0x8FE8, 0x9005, 0x9004, 0x900B, 0x9026, 0x9011, + 0x900D, 0x9016, 0x9021, 0x9035, 0x9036, 0x902D, 0x902F, 0x9044, 0x9051, + 0x9052, 0x9050, 0x9068, 0x9058, 0x9062, 0x905B, 0x66B9, 0x9074, 0x907D, + 0x9082, 0x9088, 0x9083, 0x908B, 0x5F50, 0x5F57, 0x5F56, 0x5F58, 0x5C3B, + 0x54AB, 0x5C50, 0x5C59, 0x5B71, 0x5C63, 0x5C66, 0x7FBC, 0x5F2A, 0x5F29, + 0x5F2D, 0x8274, 0x5F3C, 0x9B3B, 0x5C6E, 0x5981, 0x5983, 0x598D, 0x59A9, + 0x59AA, 0x59A3}, + {0x936C, 0x936D, 0x936E, 0x936F, 0x9370, 0x9371, 0x9372, 0x9373, 0x9374, + 0x9375, 0x9376, 0x9377, 0x9378, 0x9379, 0x937A, 0x937B, 0x937C, 0x937D, + 0x937E, 0x937F, 0x9380, 0x9381, 0x9382, 0x9383, 0x9384, 0x9385, 0x9386, + 0x9387, 0x9388, 0x9389, 0x938A, 0x938B, 0x938C, 0x938D, 0x938E, 0x9390, + 0x9391, 0x9392, 0x9393, 0x9394, 0x9395, 0x9396, 0x9397, 0x9398, 0x9399, + 0x939A, 0x939B, 0x939C, 0x939D, 0x939E, 0x939F, 0x93A0, 0x93A1, 0x93A2, + 0x93A3, 0x93A4, 0x93A5, 0x93A6, 0x93A7, 0x93A8, 0x93A9, 0x93AA, 0x93AB, + 0x003F, 0x93AC, 0x93AD, 0x93AE, 0x93AF, 0x93B0, 0x93B1, 0x93B2, 0x93B3, + 0x93B4, 0x93B5, 0x93B6, 0x93B7, 0x93B8, 0x93B9, 0x93BA, 0x93BB, 0x93BC, + 0x93BD, 0x93BE, 0x93BF, 0x93C0, 0x93C1, 0x93C2, 0x93C3, 0x93C4, 0x93C5, + 0x93C6, 0x93C7, 0x93C8, 0x93C9, 0x93CB, 0x93CC, 0x93CD, 0x5997, 0x59CA, + 0x59AB, 0x599E, 0x59A4, 0x59D2, 0x59B2, 0x59AF, 0x59D7, 0x59BE, 0x5A05, + 0x5A06, 0x59DD, 0x5A08, 0x59E3, 0x59D8, 0x59F9, 0x5A0C, 0x5A09, 0x5A32, + 0x5A34, 0x5A11, 0x5A23, 0x5A13, 0x5A40, 0x5A67, 0x5A4A, 0x5A55, 0x5A3C, + 0x5A62, 0x5A75, 0x80EC, 0x5AAA, 0x5A9B, 0x5A77, 0x5A7A, 0x5ABE, 0x5AEB, + 0x5AB2, 0x5AD2, 0x5AD4, 0x5AB8, 0x5AE0, 0x5AE3, 0x5AF1, 0x5AD6, 0x5AE6, + 0x5AD8, 0x5ADC, 0x5B09, 0x5B17, 0x5B16, 0x5B32, 0x5B37, 0x5B40, 0x5C15, + 0x5C1C, 0x5B5A, 0x5B65, 0x5B73, 0x5B51, 0x5B53, 0x5B62, 0x9A75, 0x9A77, + 0x9A78, 0x9A7A, 0x9A7F, 0x9A7D, 0x9A80, 0x9A81, 0x9A85, 0x9A88, 0x9A8A, + 0x9A90, 0x9A92, 0x9A93, 0x9A96, 0x9A98, 0x9A9B, 0x9A9C, 0x9A9D, 0x9A9F, + 0x9AA0, 0x9AA2, 0x9AA3, 0x9AA5, 0x9AA7, 0x7E9F, 0x7EA1, 0x7EA3, 0x7EA5, + 0x7EA8, 0x7EA9}, + {0x93CE, 0x93CF, 0x93D0, 0x93D1, 0x93D2, 0x93D3, 0x93D4, 0x93D5, 0x93D7, + 0x93D8, 0x93D9, 0x93DA, 0x93DB, 0x93DC, 0x93DD, 0x93DE, 0x93DF, 0x93E0, + 0x93E1, 0x93E2, 0x93E3, 0x93E4, 0x93E5, 0x93E6, 0x93E7, 0x93E8, 0x93E9, + 0x93EA, 0x93EB, 0x93EC, 0x93ED, 0x93EE, 0x93EF, 0x93F0, 0x93F1, 0x93F2, + 0x93F3, 0x93F4, 0x93F5, 0x93F6, 0x93F7, 0x93F8, 0x93F9, 0x93FA, 0x93FB, + 0x93FC, 0x93FD, 0x93FE, 0x93FF, 0x9400, 0x9401, 0x9402, 0x9403, 0x9404, + 0x9405, 0x9406, 0x9407, 0x9408, 0x9409, 0x940A, 0x940B, 0x940C, 0x940D, + 0x003F, 0x940E, 0x940F, 0x9410, 0x9411, 0x9412, 0x9413, 0x9414, 0x9415, + 0x9416, 0x9417, 0x9418, 0x9419, 0x941A, 0x941B, 0x941C, 0x941D, 0x941E, + 0x941F, 0x9420, 0x9421, 0x9422, 0x9423, 0x9424, 0x9425, 0x9426, 0x9427, + 0x9428, 0x9429, 0x942A, 0x942B, 0x942C, 0x942D, 0x942E, 0x7EAD, 0x7EB0, + 0x7EBE, 0x7EC0, 0x7EC1, 0x7EC2, 0x7EC9, 0x7ECB, 0x7ECC, 0x7ED0, 0x7ED4, + 0x7ED7, 0x7EDB, 0x7EE0, 0x7EE1, 0x7EE8, 0x7EEB, 0x7EEE, 0x7EEF, 0x7EF1, + 0x7EF2, 0x7F0D, 0x7EF6, 0x7EFA, 0x7EFB, 0x7EFE, 0x7F01, 0x7F02, 0x7F03, + 0x7F07, 0x7F08, 0x7F0B, 0x7F0C, 0x7F0F, 0x7F11, 0x7F12, 0x7F17, 0x7F19, + 0x7F1C, 0x7F1B, 0x7F1F, 0x7F21, 0x7F22, 0x7F23, 0x7F24, 0x7F25, 0x7F26, + 0x7F27, 0x7F2A, 0x7F2B, 0x7F2C, 0x7F2D, 0x7F2F, 0x7F30, 0x7F31, 0x7F32, + 0x7F33, 0x7F35, 0x5E7A, 0x757F, 0x5DDB, 0x753E, 0x9095, 0x738E, 0x7391, + 0x73AE, 0x73A2, 0x739F, 0x73CF, 0x73C2, 0x73D1, 0x73B7, 0x73B3, 0x73C0, + 0x73C9, 0x73C8, 0x73E5, 0x73D9, 0x987C, 0x740A, 0x73E9, 0x73E7, 0x73DE, + 0x73BA, 0x73F2, 0x740F, 0x742A, 0x745B, 0x7426, 0x7425, 0x7428, 0x7430, + 0x742E, 0x742C}, + {0x942F, 0x9430, 0x9431, 0x9432, 0x9433, 0x9434, 0x9435, 0x9436, 0x9437, + 0x9438, 0x9439, 0x943A, 0x943B, 0x943C, 0x943D, 0x943F, 0x9440, 0x9441, + 0x9442, 0x9443, 0x9444, 0x9445, 0x9446, 0x9447, 0x9448, 0x9449, 0x944A, + 0x944B, 0x944C, 0x944D, 0x944E, 0x944F, 0x9450, 0x9451, 0x9452, 0x9453, + 0x9454, 0x9455, 0x9456, 0x9457, 0x9458, 0x9459, 0x945A, 0x945B, 0x945C, + 0x945D, 0x945E, 0x945F, 0x9460, 0x9461, 0x9462, 0x9463, 0x9464, 0x9465, + 0x9466, 0x9467, 0x9468, 0x9469, 0x946A, 0x946C, 0x946D, 0x946E, 0x946F, + 0x003F, 0x9470, 0x9471, 0x9472, 0x9473, 0x9474, 0x9475, 0x9476, 0x9477, + 0x9478, 0x9479, 0x947A, 0x947B, 0x947C, 0x947D, 0x947E, 0x947F, 0x9480, + 0x9481, 0x9482, 0x9483, 0x9484, 0x9491, 0x9496, 0x9498, 0x94C7, 0x94CF, + 0x94D3, 0x94D4, 0x94DA, 0x94E6, 0x94FB, 0x951C, 0x9520, 0x741B, 0x741A, + 0x7441, 0x745C, 0x7457, 0x7455, 0x7459, 0x7477, 0x746D, 0x747E, 0x749C, + 0x748E, 0x7480, 0x7481, 0x7487, 0x748B, 0x749E, 0x74A8, 0x74A9, 0x7490, + 0x74A7, 0x74D2, 0x74BA, 0x97EA, 0x97EB, 0x97EC, 0x674C, 0x6753, 0x675E, + 0x6748, 0x6769, 0x67A5, 0x6787, 0x676A, 0x6773, 0x6798, 0x67A7, 0x6775, + 0x67A8, 0x679E, 0x67AD, 0x678B, 0x6777, 0x677C, 0x67F0, 0x6809, 0x67D8, + 0x680A, 0x67E9, 0x67B0, 0x680C, 0x67D9, 0x67B5, 0x67DA, 0x67B3, 0x67DD, + 0x6800, 0x67C3, 0x67B8, 0x67E2, 0x680E, 0x67C1, 0x67FD, 0x6832, 0x6833, + 0x6860, 0x6861, 0x684E, 0x6862, 0x6844, 0x6864, 0x6883, 0x681D, 0x6855, + 0x6866, 0x6841, 0x6867, 0x6840, 0x683E, 0x684A, 0x6849, 0x6829, 0x68B5, + 0x688F, 0x6874, 0x6877, 0x6893, 0x686B, 0x68C2, 0x696E, 0x68FC, 0x691F, + 0x6920, 0x68F9}, + {0x9527, 0x9533, 0x953D, 0x9543, 0x9548, 0x954B, 0x9555, 0x955A, 0x9560, + 0x956E, 0x9574, 0x9575, 0x9577, 0x9578, 0x9579, 0x957A, 0x957B, 0x957C, + 0x957D, 0x957E, 0x9580, 0x9581, 0x9582, 0x9583, 0x9584, 0x9585, 0x9586, + 0x9587, 0x9588, 0x9589, 0x958A, 0x958B, 0x958C, 0x958D, 0x958E, 0x958F, + 0x9590, 0x9591, 0x9592, 0x9593, 0x9594, 0x9595, 0x9596, 0x9597, 0x9598, + 0x9599, 0x959A, 0x959B, 0x959C, 0x959D, 0x959E, 0x959F, 0x95A0, 0x95A1, + 0x95A2, 0x95A3, 0x95A4, 0x95A5, 0x95A6, 0x95A7, 0x95A8, 0x95A9, 0x95AA, + 0x003F, 0x95AB, 0x95AC, 0x95AD, 0x95AE, 0x95AF, 0x95B0, 0x95B1, 0x95B2, + 0x95B3, 0x95B4, 0x95B5, 0x95B6, 0x95B7, 0x95B8, 0x95B9, 0x95BA, 0x95BB, + 0x95BC, 0x95BD, 0x95BE, 0x95BF, 0x95C0, 0x95C1, 0x95C2, 0x95C3, 0x95C4, + 0x95C5, 0x95C6, 0x95C7, 0x95C8, 0x95C9, 0x95CA, 0x95CB, 0x6924, 0x68F0, + 0x690B, 0x6901, 0x6957, 0x68E3, 0x6910, 0x6971, 0x6939, 0x6960, 0x6942, + 0x695D, 0x6984, 0x696B, 0x6980, 0x6998, 0x6978, 0x6934, 0x69CC, 0x6987, + 0x6988, 0x69CE, 0x6989, 0x6966, 0x6963, 0x6979, 0x699B, 0x69A7, 0x69BB, + 0x69AB, 0x69AD, 0x69D4, 0x69B1, 0x69C1, 0x69CA, 0x69DF, 0x6995, 0x69E0, + 0x698D, 0x69FF, 0x6A2F, 0x69ED, 0x6A17, 0x6A18, 0x6A65, 0x69F2, 0x6A44, + 0x6A3E, 0x6AA0, 0x6A50, 0x6A5B, 0x6A35, 0x6A8E, 0x6A79, 0x6A3D, 0x6A28, + 0x6A58, 0x6A7C, 0x6A91, 0x6A90, 0x6AA9, 0x6A97, 0x6AAB, 0x7337, 0x7352, + 0x6B81, 0x6B82, 0x6B87, 0x6B84, 0x6B92, 0x6B93, 0x6B8D, 0x6B9A, 0x6B9B, + 0x6BA1, 0x6BAA, 0x8F6B, 0x8F6D, 0x8F71, 0x8F72, 0x8F73, 0x8F75, 0x8F76, + 0x8F78, 0x8F77, 0x8F79, 0x8F7A, 0x8F7C, 0x8F7E, 0x8F81, 0x8F82, 0x8F84, + 0x8F87, 0x8F8B}, + {0x95CC, 0x95CD, 0x95CE, 0x95CF, 0x95D0, 0x95D1, 0x95D2, 0x95D3, 0x95D4, + 0x95D5, 0x95D6, 0x95D7, 0x95D8, 0x95D9, 0x95DA, 0x95DB, 0x95DC, 0x95DD, + 0x95DE, 0x95DF, 0x95E0, 0x95E1, 0x95E2, 0x95E3, 0x95E4, 0x95E5, 0x95E6, + 0x95E7, 0x95EC, 0x95FF, 0x9607, 0x9613, 0x9618, 0x961B, 0x961E, 0x9620, + 0x9623, 0x9624, 0x9625, 0x9626, 0x9627, 0x9628, 0x9629, 0x962B, 0x962C, + 0x962D, 0x962F, 0x9630, 0x9637, 0x9638, 0x9639, 0x963A, 0x963E, 0x9641, + 0x9643, 0x964A, 0x964E, 0x964F, 0x9651, 0x9652, 0x9653, 0x9656, 0x9657, + 0x003F, 0x9658, 0x9659, 0x965A, 0x965C, 0x965D, 0x965E, 0x9660, 0x9663, + 0x9665, 0x9666, 0x966B, 0x966D, 0x966E, 0x966F, 0x9670, 0x9671, 0x9673, + 0x9678, 0x9679, 0x967A, 0x967B, 0x967C, 0x967D, 0x967E, 0x967F, 0x9680, + 0x9681, 0x9682, 0x9683, 0x9684, 0x9687, 0x9689, 0x968A, 0x8F8D, 0x8F8E, + 0x8F8F, 0x8F98, 0x8F9A, 0x8ECE, 0x620B, 0x6217, 0x621B, 0x621F, 0x6222, + 0x6221, 0x6225, 0x6224, 0x622C, 0x81E7, 0x74EF, 0x74F4, 0x74FF, 0x750F, + 0x7511, 0x7513, 0x6534, 0x65EE, 0x65EF, 0x65F0, 0x660A, 0x6619, 0x6772, + 0x6603, 0x6615, 0x6600, 0x7085, 0x66F7, 0x661D, 0x6634, 0x6631, 0x6636, + 0x6635, 0x8006, 0x665F, 0x6654, 0x6641, 0x664F, 0x6656, 0x6661, 0x6657, + 0x6677, 0x6684, 0x668C, 0x66A7, 0x669D, 0x66BE, 0x66DB, 0x66DC, 0x66E6, + 0x66E9, 0x8D32, 0x8D33, 0x8D36, 0x8D3B, 0x8D3D, 0x8D40, 0x8D45, 0x8D46, + 0x8D48, 0x8D49, 0x8D47, 0x8D4D, 0x8D55, 0x8D59, 0x89C7, 0x89CA, 0x89CB, + 0x89CC, 0x89CE, 0x89CF, 0x89D0, 0x89D1, 0x726E, 0x729F, 0x725D, 0x7266, + 0x726F, 0x727E, 0x727F, 0x7284, 0x728B, 0x728D, 0x728F, 0x7292, 0x6308, + 0x6332, 0x63B0}, + {0x968C, 0x968E, 0x9691, 0x9692, 0x9693, 0x9695, 0x9696, 0x969A, 0x969B, + 0x969D, 0x969E, 0x969F, 0x96A0, 0x96A1, 0x96A2, 0x96A3, 0x96A4, 0x96A5, + 0x96A6, 0x96A8, 0x96A9, 0x96AA, 0x96AB, 0x96AC, 0x96AD, 0x96AE, 0x96AF, + 0x96B1, 0x96B2, 0x96B4, 0x96B5, 0x96B7, 0x96B8, 0x96BA, 0x96BB, 0x96BF, + 0x96C2, 0x96C3, 0x96C8, 0x96CA, 0x96CB, 0x96D0, 0x96D1, 0x96D3, 0x96D4, + 0x96D6, 0x96D7, 0x96D8, 0x96D9, 0x96DA, 0x96DB, 0x96DC, 0x96DD, 0x96DE, + 0x96DF, 0x96E1, 0x96E2, 0x96E3, 0x96E4, 0x96E5, 0x96E6, 0x96E7, 0x96EB, + 0x003F, 0x96EC, 0x96ED, 0x96EE, 0x96F0, 0x96F1, 0x96F2, 0x96F4, 0x96F5, + 0x96F8, 0x96FA, 0x96FB, 0x96FC, 0x96FD, 0x96FF, 0x9702, 0x9703, 0x9705, + 0x970A, 0x970B, 0x970C, 0x9710, 0x9711, 0x9712, 0x9714, 0x9715, 0x9717, + 0x9718, 0x9719, 0x971A, 0x971B, 0x971D, 0x971F, 0x9720, 0x643F, 0x64D8, + 0x8004, 0x6BEA, 0x6BF3, 0x6BFD, 0x6BF5, 0x6BF9, 0x6C05, 0x6C07, 0x6C06, + 0x6C0D, 0x6C15, 0x6C18, 0x6C19, 0x6C1A, 0x6C21, 0x6C29, 0x6C24, 0x6C2A, + 0x6C32, 0x6535, 0x6555, 0x656B, 0x724D, 0x7252, 0x7256, 0x7230, 0x8662, + 0x5216, 0x809F, 0x809C, 0x8093, 0x80BC, 0x670A, 0x80BD, 0x80B1, 0x80AB, + 0x80AD, 0x80B4, 0x80B7, 0x80E7, 0x80E8, 0x80E9, 0x80EA, 0x80DB, 0x80C2, + 0x80C4, 0x80D9, 0x80CD, 0x80D7, 0x6710, 0x80DD, 0x80EB, 0x80F1, 0x80F4, + 0x80ED, 0x810D, 0x810E, 0x80F2, 0x80FC, 0x6715, 0x8112, 0x8C5A, 0x8136, + 0x811E, 0x812C, 0x8118, 0x8132, 0x8148, 0x814C, 0x8153, 0x8174, 0x8159, + 0x815A, 0x8171, 0x8160, 0x8169, 0x817C, 0x817D, 0x816D, 0x8167, 0x584D, + 0x5AB5, 0x8188, 0x8182, 0x8191, 0x6ED5, 0x81A3, 0x81AA, 0x81CC, 0x6726, + 0x81CA, 0x81BB}, + {0x9721, 0x9722, 0x9723, 0x9724, 0x9725, 0x9726, 0x9727, 0x9728, 0x9729, + 0x972B, 0x972C, 0x972E, 0x972F, 0x9731, 0x9733, 0x9734, 0x9735, 0x9736, + 0x9737, 0x973A, 0x973B, 0x973C, 0x973D, 0x973F, 0x9740, 0x9741, 0x9742, + 0x9743, 0x9744, 0x9745, 0x9746, 0x9747, 0x9748, 0x9749, 0x974A, 0x974B, + 0x974C, 0x974D, 0x974E, 0x974F, 0x9750, 0x9751, 0x9754, 0x9755, 0x9757, + 0x9758, 0x975A, 0x975C, 0x975D, 0x975F, 0x9763, 0x9764, 0x9766, 0x9767, + 0x9768, 0x976A, 0x976B, 0x976C, 0x976D, 0x976E, 0x976F, 0x9770, 0x9771, + 0x003F, 0x9772, 0x9775, 0x9777, 0x9778, 0x9779, 0x977A, 0x977B, 0x977D, + 0x977E, 0x977F, 0x9780, 0x9781, 0x9782, 0x9783, 0x9784, 0x9786, 0x9787, + 0x9788, 0x9789, 0x978A, 0x978C, 0x978E, 0x978F, 0x9790, 0x9793, 0x9795, + 0x9796, 0x9797, 0x9799, 0x979A, 0x979B, 0x979C, 0x979D, 0x81C1, 0x81A6, + 0x6B24, 0x6B37, 0x6B39, 0x6B43, 0x6B46, 0x6B59, 0x98D1, 0x98D2, 0x98D3, + 0x98D5, 0x98D9, 0x98DA, 0x6BB3, 0x5F40, 0x6BC2, 0x89F3, 0x6590, 0x9F51, + 0x6593, 0x65BC, 0x65C6, 0x65C4, 0x65C3, 0x65CC, 0x65CE, 0x65D2, 0x65D6, + 0x7080, 0x709C, 0x7096, 0x709D, 0x70BB, 0x70C0, 0x70B7, 0x70AB, 0x70B1, + 0x70E8, 0x70CA, 0x7110, 0x7113, 0x7116, 0x712F, 0x7131, 0x7173, 0x715C, + 0x7168, 0x7145, 0x7172, 0x714A, 0x7178, 0x717A, 0x7198, 0x71B3, 0x71B5, + 0x71A8, 0x71A0, 0x71E0, 0x71D4, 0x71E7, 0x71F9, 0x721D, 0x7228, 0x706C, + 0x7118, 0x7166, 0x71B9, 0x623E, 0x623D, 0x6243, 0x6248, 0x6249, 0x793B, + 0x7940, 0x7946, 0x7949, 0x795B, 0x795C, 0x7953, 0x795A, 0x7962, 0x7957, + 0x7960, 0x796F, 0x7967, 0x797A, 0x7985, 0x798A, 0x799A, 0x79A7, 0x79B3, + 0x5FD1, 0x5FD0}, + {0x979E, 0x979F, 0x97A1, 0x97A2, 0x97A4, 0x97A5, 0x97A6, 0x97A7, 0x97A8, + 0x97A9, 0x97AA, 0x97AC, 0x97AE, 0x97B0, 0x97B1, 0x97B3, 0x97B5, 0x97B6, + 0x97B7, 0x97B8, 0x97B9, 0x97BA, 0x97BB, 0x97BC, 0x97BD, 0x97BE, 0x97BF, + 0x97C0, 0x97C1, 0x97C2, 0x97C3, 0x97C4, 0x97C5, 0x97C6, 0x97C7, 0x97C8, + 0x97C9, 0x97CA, 0x97CB, 0x97CC, 0x97CD, 0x97CE, 0x97CF, 0x97D0, 0x97D1, + 0x97D2, 0x97D3, 0x97D4, 0x97D5, 0x97D6, 0x97D7, 0x97D8, 0x97D9, 0x97DA, + 0x97DB, 0x97DC, 0x97DD, 0x97DE, 0x97DF, 0x97E0, 0x97E1, 0x97E2, 0x97E3, + 0x003F, 0x97E4, 0x97E5, 0x97E8, 0x97EE, 0x97EF, 0x97F0, 0x97F1, 0x97F2, + 0x97F4, 0x97F7, 0x97F8, 0x97F9, 0x97FA, 0x97FB, 0x97FC, 0x97FD, 0x97FE, + 0x97FF, 0x9800, 0x9801, 0x9802, 0x9803, 0x9804, 0x9805, 0x9806, 0x9807, + 0x9808, 0x9809, 0x980A, 0x980B, 0x980C, 0x980D, 0x980E, 0x603C, 0x605D, + 0x605A, 0x6067, 0x6041, 0x6059, 0x6063, 0x60AB, 0x6106, 0x610D, 0x615D, + 0x61A9, 0x619D, 0x61CB, 0x61D1, 0x6206, 0x8080, 0x807F, 0x6C93, 0x6CF6, + 0x6DFC, 0x77F6, 0x77F8, 0x7800, 0x7809, 0x7817, 0x7818, 0x7811, 0x65AB, + 0x782D, 0x781C, 0x781D, 0x7839, 0x783A, 0x783B, 0x781F, 0x783C, 0x7825, + 0x782C, 0x7823, 0x7829, 0x784E, 0x786D, 0x7856, 0x7857, 0x7826, 0x7850, + 0x7847, 0x784C, 0x786A, 0x789B, 0x7893, 0x789A, 0x7887, 0x789C, 0x78A1, + 0x78A3, 0x78B2, 0x78B9, 0x78A5, 0x78D4, 0x78D9, 0x78C9, 0x78EC, 0x78F2, + 0x7905, 0x78F4, 0x7913, 0x7924, 0x791E, 0x7934, 0x9F9B, 0x9EF9, 0x9EFB, + 0x9EFC, 0x76F1, 0x7704, 0x770D, 0x76F9, 0x7707, 0x7708, 0x771A, 0x7722, + 0x7719, 0x772D, 0x7726, 0x7735, 0x7738, 0x7750, 0x7751, 0x7747, 0x7743, + 0x775A, 0x7768}, + {0x980F, 0x9810, 0x9811, 0x9812, 0x9813, 0x9814, 0x9815, 0x9816, 0x9817, + 0x9818, 0x9819, 0x981A, 0x981B, 0x981C, 0x981D, 0x981E, 0x981F, 0x9820, + 0x9821, 0x9822, 0x9823, 0x9824, 0x9825, 0x9826, 0x9827, 0x9828, 0x9829, + 0x982A, 0x982B, 0x982C, 0x982D, 0x982E, 0x982F, 0x9830, 0x9831, 0x9832, + 0x9833, 0x9834, 0x9835, 0x9836, 0x9837, 0x9838, 0x9839, 0x983A, 0x983B, + 0x983C, 0x983D, 0x983E, 0x983F, 0x9840, 0x9841, 0x9842, 0x9843, 0x9844, + 0x9845, 0x9846, 0x9847, 0x9848, 0x9849, 0x984A, 0x984B, 0x984C, 0x984D, + 0x003F, 0x984E, 0x984F, 0x9850, 0x9851, 0x9852, 0x9853, 0x9854, 0x9855, + 0x9856, 0x9857, 0x9858, 0x9859, 0x985A, 0x985B, 0x985C, 0x985D, 0x985E, + 0x985F, 0x9860, 0x9861, 0x9862, 0x9863, 0x9864, 0x9865, 0x9866, 0x9867, + 0x9868, 0x9869, 0x986A, 0x986B, 0x986C, 0x986D, 0x986E, 0x7762, 0x7765, + 0x777F, 0x778D, 0x777D, 0x7780, 0x778C, 0x7791, 0x779F, 0x77A0, 0x77B0, + 0x77B5, 0x77BD, 0x753A, 0x7540, 0x754E, 0x754B, 0x7548, 0x755B, 0x7572, + 0x7579, 0x7583, 0x7F58, 0x7F61, 0x7F5F, 0x8A48, 0x7F68, 0x7F74, 0x7F71, + 0x7F79, 0x7F81, 0x7F7E, 0x76CD, 0x76E5, 0x8832, 0x9485, 0x9486, 0x9487, + 0x948B, 0x948A, 0x948C, 0x948D, 0x948F, 0x9490, 0x9494, 0x9497, 0x9495, + 0x949A, 0x949B, 0x949C, 0x94A3, 0x94A4, 0x94AB, 0x94AA, 0x94AD, 0x94AC, + 0x94AF, 0x94B0, 0x94B2, 0x94B4, 0x94B6, 0x94B7, 0x94B8, 0x94B9, 0x94BA, + 0x94BC, 0x94BD, 0x94BF, 0x94C4, 0x94C8, 0x94C9, 0x94CA, 0x94CB, 0x94CC, + 0x94CD, 0x94CE, 0x94D0, 0x94D1, 0x94D2, 0x94D5, 0x94D6, 0x94D7, 0x94D9, + 0x94D8, 0x94DB, 0x94DE, 0x94DF, 0x94E0, 0x94E2, 0x94E4, 0x94E5, 0x94E7, + 0x94E8, 0x94EA}, + {0x986F, 0x9870, 0x9871, 0x9872, 0x9873, 0x9874, 0x988B, 0x988E, 0x9892, + 0x9895, 0x9899, 0x98A3, 0x98A8, 0x98A9, 0x98AA, 0x98AB, 0x98AC, 0x98AD, + 0x98AE, 0x98AF, 0x98B0, 0x98B1, 0x98B2, 0x98B3, 0x98B4, 0x98B5, 0x98B6, + 0x98B7, 0x98B8, 0x98B9, 0x98BA, 0x98BB, 0x98BC, 0x98BD, 0x98BE, 0x98BF, + 0x98C0, 0x98C1, 0x98C2, 0x98C3, 0x98C4, 0x98C5, 0x98C6, 0x98C7, 0x98C8, + 0x98C9, 0x98CA, 0x98CB, 0x98CC, 0x98CD, 0x98CF, 0x98D0, 0x98D4, 0x98D6, + 0x98D7, 0x98DB, 0x98DC, 0x98DD, 0x98E0, 0x98E1, 0x98E2, 0x98E3, 0x98E4, + 0x003F, 0x98E5, 0x98E6, 0x98E9, 0x98EA, 0x98EB, 0x98EC, 0x98ED, 0x98EE, + 0x98EF, 0x98F0, 0x98F1, 0x98F2, 0x98F3, 0x98F4, 0x98F5, 0x98F6, 0x98F7, + 0x98F8, 0x98F9, 0x98FA, 0x98FB, 0x98FC, 0x98FD, 0x98FE, 0x98FF, 0x9900, + 0x9901, 0x9902, 0x9903, 0x9904, 0x9905, 0x9906, 0x9907, 0x94E9, 0x94EB, + 0x94EE, 0x94EF, 0x94F3, 0x94F4, 0x94F5, 0x94F7, 0x94F9, 0x94FC, 0x94FD, + 0x94FF, 0x9503, 0x9502, 0x9506, 0x9507, 0x9509, 0x950A, 0x950D, 0x950E, + 0x950F, 0x9512, 0x9513, 0x9514, 0x9515, 0x9516, 0x9518, 0x951B, 0x951D, + 0x951E, 0x951F, 0x9522, 0x952A, 0x952B, 0x9529, 0x952C, 0x9531, 0x9532, + 0x9534, 0x9536, 0x9537, 0x9538, 0x953C, 0x953E, 0x953F, 0x9542, 0x9535, + 0x9544, 0x9545, 0x9546, 0x9549, 0x954C, 0x954E, 0x954F, 0x9552, 0x9553, + 0x9554, 0x9556, 0x9557, 0x9558, 0x9559, 0x955B, 0x955E, 0x955F, 0x955D, + 0x9561, 0x9562, 0x9564, 0x9565, 0x9566, 0x9567, 0x9568, 0x9569, 0x956A, + 0x956B, 0x956C, 0x956F, 0x9571, 0x9572, 0x9573, 0x953A, 0x77E7, 0x77EC, + 0x96C9, 0x79D5, 0x79ED, 0x79E3, 0x79EB, 0x7A06, 0x5D47, 0x7A03, 0x7A02, + 0x7A1E, 0x7A14}, + {0x9908, 0x9909, 0x990A, 0x990B, 0x990C, 0x990E, 0x990F, 0x9911, 0x9912, + 0x9913, 0x9914, 0x9915, 0x9916, 0x9917, 0x9918, 0x9919, 0x991A, 0x991B, + 0x991C, 0x991D, 0x991E, 0x991F, 0x9920, 0x9921, 0x9922, 0x9923, 0x9924, + 0x9925, 0x9926, 0x9927, 0x9928, 0x9929, 0x992A, 0x992B, 0x992C, 0x992D, + 0x992F, 0x9930, 0x9931, 0x9932, 0x9933, 0x9934, 0x9935, 0x9936, 0x9937, + 0x9938, 0x9939, 0x993A, 0x993B, 0x993C, 0x993D, 0x993E, 0x993F, 0x9940, + 0x9941, 0x9942, 0x9943, 0x9944, 0x9945, 0x9946, 0x9947, 0x9948, 0x9949, + 0x003F, 0x994A, 0x994B, 0x994C, 0x994D, 0x994E, 0x994F, 0x9950, 0x9951, + 0x9952, 0x9953, 0x9956, 0x9957, 0x9958, 0x9959, 0x995A, 0x995B, 0x995C, + 0x995D, 0x995E, 0x995F, 0x9960, 0x9961, 0x9962, 0x9964, 0x9966, 0x9973, + 0x9978, 0x9979, 0x997B, 0x997E, 0x9982, 0x9983, 0x9989, 0x7A39, 0x7A37, + 0x7A51, 0x9ECF, 0x99A5, 0x7A70, 0x7688, 0x768E, 0x7693, 0x7699, 0x76A4, + 0x74DE, 0x74E0, 0x752C, 0x9E20, 0x9E22, 0x9E28, 0x9E29, 0x9E2A, 0x9E2B, + 0x9E2C, 0x9E32, 0x9E31, 0x9E36, 0x9E38, 0x9E37, 0x9E39, 0x9E3A, 0x9E3E, + 0x9E41, 0x9E42, 0x9E44, 0x9E46, 0x9E47, 0x9E48, 0x9E49, 0x9E4B, 0x9E4C, + 0x9E4E, 0x9E51, 0x9E55, 0x9E57, 0x9E5A, 0x9E5B, 0x9E5C, 0x9E5E, 0x9E63, + 0x9E66, 0x9E67, 0x9E68, 0x9E69, 0x9E6A, 0x9E6B, 0x9E6C, 0x9E71, 0x9E6D, + 0x9E73, 0x7592, 0x7594, 0x7596, 0x75A0, 0x759D, 0x75AC, 0x75A3, 0x75B3, + 0x75B4, 0x75B8, 0x75C4, 0x75B1, 0x75B0, 0x75C3, 0x75C2, 0x75D6, 0x75CD, + 0x75E3, 0x75E8, 0x75E6, 0x75E4, 0x75EB, 0x75E7, 0x7603, 0x75F1, 0x75FC, + 0x75FF, 0x7610, 0x7600, 0x7605, 0x760C, 0x7617, 0x760A, 0x7625, 0x7618, + 0x7615, 0x7619}, + {0x998C, 0x998E, 0x999A, 0x999B, 0x999C, 0x999D, 0x999E, 0x999F, 0x99A0, + 0x99A1, 0x99A2, 0x99A3, 0x99A4, 0x99A6, 0x99A7, 0x99A9, 0x99AA, 0x99AB, + 0x99AC, 0x99AD, 0x99AE, 0x99AF, 0x99B0, 0x99B1, 0x99B2, 0x99B3, 0x99B4, + 0x99B5, 0x99B6, 0x99B7, 0x99B8, 0x99B9, 0x99BA, 0x99BB, 0x99BC, 0x99BD, + 0x99BE, 0x99BF, 0x99C0, 0x99C1, 0x99C2, 0x99C3, 0x99C4, 0x99C5, 0x99C6, + 0x99C7, 0x99C8, 0x99C9, 0x99CA, 0x99CB, 0x99CC, 0x99CD, 0x99CE, 0x99CF, + 0x99D0, 0x99D1, 0x99D2, 0x99D3, 0x99D4, 0x99D5, 0x99D6, 0x99D7, 0x99D8, + 0x003F, 0x99D9, 0x99DA, 0x99DB, 0x99DC, 0x99DD, 0x99DE, 0x99DF, 0x99E0, + 0x99E1, 0x99E2, 0x99E3, 0x99E4, 0x99E5, 0x99E6, 0x99E7, 0x99E8, 0x99E9, + 0x99EA, 0x99EB, 0x99EC, 0x99ED, 0x99EE, 0x99EF, 0x99F0, 0x99F1, 0x99F2, + 0x99F3, 0x99F4, 0x99F5, 0x99F6, 0x99F7, 0x99F8, 0x99F9, 0x761B, 0x763C, + 0x7622, 0x7620, 0x7640, 0x762D, 0x7630, 0x763F, 0x7635, 0x7643, 0x763E, + 0x7633, 0x764D, 0x765E, 0x7654, 0x765C, 0x7656, 0x766B, 0x766F, 0x7FCA, + 0x7AE6, 0x7A78, 0x7A79, 0x7A80, 0x7A86, 0x7A88, 0x7A95, 0x7AA6, 0x7AA0, + 0x7AAC, 0x7AA8, 0x7AAD, 0x7AB3, 0x8864, 0x8869, 0x8872, 0x887D, 0x887F, + 0x8882, 0x88A2, 0x88C6, 0x88B7, 0x88BC, 0x88C9, 0x88E2, 0x88CE, 0x88E3, + 0x88E5, 0x88F1, 0x891A, 0x88FC, 0x88E8, 0x88FE, 0x88F0, 0x8921, 0x8919, + 0x8913, 0x891B, 0x890A, 0x8934, 0x892B, 0x8936, 0x8941, 0x8966, 0x897B, + 0x758B, 0x80E5, 0x76B2, 0x76B4, 0x77DC, 0x8012, 0x8014, 0x8016, 0x801C, + 0x8020, 0x8022, 0x8025, 0x8026, 0x8027, 0x8029, 0x8028, 0x8031, 0x800B, + 0x8035, 0x8043, 0x8046, 0x804D, 0x8052, 0x8069, 0x8071, 0x8983, 0x9878, + 0x9880, 0x9883}, + {0x99FA, 0x99FB, 0x99FC, 0x99FD, 0x99FE, 0x99FF, 0x9A00, 0x9A01, 0x9A02, + 0x9A03, 0x9A04, 0x9A05, 0x9A06, 0x9A07, 0x9A08, 0x9A09, 0x9A0A, 0x9A0B, + 0x9A0C, 0x9A0D, 0x9A0E, 0x9A0F, 0x9A10, 0x9A11, 0x9A12, 0x9A13, 0x9A14, + 0x9A15, 0x9A16, 0x9A17, 0x9A18, 0x9A19, 0x9A1A, 0x9A1B, 0x9A1C, 0x9A1D, + 0x9A1E, 0x9A1F, 0x9A20, 0x9A21, 0x9A22, 0x9A23, 0x9A24, 0x9A25, 0x9A26, + 0x9A27, 0x9A28, 0x9A29, 0x9A2A, 0x9A2B, 0x9A2C, 0x9A2D, 0x9A2E, 0x9A2F, + 0x9A30, 0x9A31, 0x9A32, 0x9A33, 0x9A34, 0x9A35, 0x9A36, 0x9A37, 0x9A38, + 0x003F, 0x9A39, 0x9A3A, 0x9A3B, 0x9A3C, 0x9A3D, 0x9A3E, 0x9A3F, 0x9A40, + 0x9A41, 0x9A42, 0x9A43, 0x9A44, 0x9A45, 0x9A46, 0x9A47, 0x9A48, 0x9A49, + 0x9A4A, 0x9A4B, 0x9A4C, 0x9A4D, 0x9A4E, 0x9A4F, 0x9A50, 0x9A51, 0x9A52, + 0x9A53, 0x9A54, 0x9A55, 0x9A56, 0x9A57, 0x9A58, 0x9A59, 0x9889, 0x988C, + 0x988D, 0x988F, 0x9894, 0x989A, 0x989B, 0x989E, 0x989F, 0x98A1, 0x98A2, + 0x98A5, 0x98A6, 0x864D, 0x8654, 0x866C, 0x866E, 0x867F, 0x867A, 0x867C, + 0x867B, 0x86A8, 0x868D, 0x868B, 0x86AC, 0x869D, 0x86A7, 0x86A3, 0x86AA, + 0x8693, 0x86A9, 0x86B6, 0x86C4, 0x86B5, 0x86CE, 0x86B0, 0x86BA, 0x86B1, + 0x86AF, 0x86C9, 0x86CF, 0x86B4, 0x86E9, 0x86F1, 0x86F2, 0x86ED, 0x86F3, + 0x86D0, 0x8713, 0x86DE, 0x86F4, 0x86DF, 0x86D8, 0x86D1, 0x8703, 0x8707, + 0x86F8, 0x8708, 0x870A, 0x870D, 0x8709, 0x8723, 0x873B, 0x871E, 0x8725, + 0x872E, 0x871A, 0x873E, 0x8748, 0x8734, 0x8731, 0x8729, 0x8737, 0x873F, + 0x8782, 0x8722, 0x877D, 0x877E, 0x877B, 0x8760, 0x8770, 0x874C, 0x876E, + 0x878B, 0x8753, 0x8763, 0x877C, 0x8764, 0x8759, 0x8765, 0x8793, 0x87AF, + 0x87A8, 0x87D2}, + {0x9A5A, 0x9A5B, 0x9A5C, 0x9A5D, 0x9A5E, 0x9A5F, 0x9A60, 0x9A61, 0x9A62, + 0x9A63, 0x9A64, 0x9A65, 0x9A66, 0x9A67, 0x9A68, 0x9A69, 0x9A6A, 0x9A6B, + 0x9A72, 0x9A83, 0x9A89, 0x9A8D, 0x9A8E, 0x9A94, 0x9A95, 0x9A99, 0x9AA6, + 0x9AA9, 0x9AAA, 0x9AAB, 0x9AAC, 0x9AAD, 0x9AAE, 0x9AAF, 0x9AB2, 0x9AB3, + 0x9AB4, 0x9AB5, 0x9AB9, 0x9ABB, 0x9ABD, 0x9ABE, 0x9ABF, 0x9AC3, 0x9AC4, + 0x9AC6, 0x9AC7, 0x9AC8, 0x9AC9, 0x9ACA, 0x9ACD, 0x9ACE, 0x9ACF, 0x9AD0, + 0x9AD2, 0x9AD4, 0x9AD5, 0x9AD6, 0x9AD7, 0x9AD9, 0x9ADA, 0x9ADB, 0x9ADC, + 0x003F, 0x9ADD, 0x9ADE, 0x9AE0, 0x9AE2, 0x9AE3, 0x9AE4, 0x9AE5, 0x9AE7, + 0x9AE8, 0x9AE9, 0x9AEA, 0x9AEC, 0x9AEE, 0x9AF0, 0x9AF1, 0x9AF2, 0x9AF3, + 0x9AF4, 0x9AF5, 0x9AF6, 0x9AF7, 0x9AF8, 0x9AFA, 0x9AFC, 0x9AFD, 0x9AFE, + 0x9AFF, 0x9B00, 0x9B01, 0x9B02, 0x9B04, 0x9B05, 0x9B06, 0x87C6, 0x8788, + 0x8785, 0x87AD, 0x8797, 0x8783, 0x87AB, 0x87E5, 0x87AC, 0x87B5, 0x87B3, + 0x87CB, 0x87D3, 0x87BD, 0x87D1, 0x87C0, 0x87CA, 0x87DB, 0x87EA, 0x87E0, + 0x87EE, 0x8816, 0x8813, 0x87FE, 0x880A, 0x881B, 0x8821, 0x8839, 0x883C, + 0x7F36, 0x7F42, 0x7F44, 0x7F45, 0x8210, 0x7AFA, 0x7AFD, 0x7B08, 0x7B03, + 0x7B04, 0x7B15, 0x7B0A, 0x7B2B, 0x7B0F, 0x7B47, 0x7B38, 0x7B2A, 0x7B19, + 0x7B2E, 0x7B31, 0x7B20, 0x7B25, 0x7B24, 0x7B33, 0x7B3E, 0x7B1E, 0x7B58, + 0x7B5A, 0x7B45, 0x7B75, 0x7B4C, 0x7B5D, 0x7B60, 0x7B6E, 0x7B7B, 0x7B62, + 0x7B72, 0x7B71, 0x7B90, 0x7BA6, 0x7BA7, 0x7BB8, 0x7BAC, 0x7B9D, 0x7BA8, + 0x7B85, 0x7BAA, 0x7B9C, 0x7BA2, 0x7BAB, 0x7BB4, 0x7BD1, 0x7BC1, 0x7BCC, + 0x7BDD, 0x7BDA, 0x7BE5, 0x7BE6, 0x7BEA, 0x7C0C, 0x7BFE, 0x7BFC, 0x7C0F, + 0x7C16, 0x7C0B}, + {0x9B07, 0x9B09, 0x9B0A, 0x9B0B, 0x9B0C, 0x9B0D, 0x9B0E, 0x9B10, 0x9B11, + 0x9B12, 0x9B14, 0x9B15, 0x9B16, 0x9B17, 0x9B18, 0x9B19, 0x9B1A, 0x9B1B, + 0x9B1C, 0x9B1D, 0x9B1E, 0x9B20, 0x9B21, 0x9B22, 0x9B24, 0x9B25, 0x9B26, + 0x9B27, 0x9B28, 0x9B29, 0x9B2A, 0x9B2B, 0x9B2C, 0x9B2D, 0x9B2E, 0x9B30, + 0x9B31, 0x9B33, 0x9B34, 0x9B35, 0x9B36, 0x9B37, 0x9B38, 0x9B39, 0x9B3A, + 0x9B3D, 0x9B3E, 0x9B3F, 0x9B40, 0x9B46, 0x9B4A, 0x9B4B, 0x9B4C, 0x9B4E, + 0x9B50, 0x9B52, 0x9B53, 0x9B55, 0x9B56, 0x9B57, 0x9B58, 0x9B59, 0x9B5A, + 0x003F, 0x9B5B, 0x9B5C, 0x9B5D, 0x9B5E, 0x9B5F, 0x9B60, 0x9B61, 0x9B62, + 0x9B63, 0x9B64, 0x9B65, 0x9B66, 0x9B67, 0x9B68, 0x9B69, 0x9B6A, 0x9B6B, + 0x9B6C, 0x9B6D, 0x9B6E, 0x9B6F, 0x9B70, 0x9B71, 0x9B72, 0x9B73, 0x9B74, + 0x9B75, 0x9B76, 0x9B77, 0x9B78, 0x9B79, 0x9B7A, 0x9B7B, 0x7C1F, 0x7C2A, + 0x7C26, 0x7C38, 0x7C41, 0x7C40, 0x81FE, 0x8201, 0x8202, 0x8204, 0x81EC, + 0x8844, 0x8221, 0x8222, 0x8223, 0x822D, 0x822F, 0x8228, 0x822B, 0x8238, + 0x823B, 0x8233, 0x8234, 0x823E, 0x8244, 0x8249, 0x824B, 0x824F, 0x825A, + 0x825F, 0x8268, 0x887E, 0x8885, 0x8888, 0x88D8, 0x88DF, 0x895E, 0x7F9D, + 0x7F9F, 0x7FA7, 0x7FAF, 0x7FB0, 0x7FB2, 0x7C7C, 0x6549, 0x7C91, 0x7C9D, + 0x7C9C, 0x7C9E, 0x7CA2, 0x7CB2, 0x7CBC, 0x7CBD, 0x7CC1, 0x7CC7, 0x7CCC, + 0x7CCD, 0x7CC8, 0x7CC5, 0x7CD7, 0x7CE8, 0x826E, 0x66A8, 0x7FBF, 0x7FCE, + 0x7FD5, 0x7FE5, 0x7FE1, 0x7FE6, 0x7FE9, 0x7FEE, 0x7FF3, 0x7CF8, 0x7D77, + 0x7DA6, 0x7DAE, 0x7E47, 0x7E9B, 0x9EB8, 0x9EB4, 0x8D73, 0x8D84, 0x8D94, + 0x8D91, 0x8DB1, 0x8D67, 0x8D6D, 0x8C47, 0x8C49, 0x914A, 0x9150, 0x914E, + 0x914F, 0x9164}, + {0x9B7C, 0x9B7D, 0x9B7E, 0x9B7F, 0x9B80, 0x9B81, 0x9B82, 0x9B83, 0x9B84, + 0x9B85, 0x9B86, 0x9B87, 0x9B88, 0x9B89, 0x9B8A, 0x9B8B, 0x9B8C, 0x9B8D, + 0x9B8E, 0x9B8F, 0x9B90, 0x9B91, 0x9B92, 0x9B93, 0x9B94, 0x9B95, 0x9B96, + 0x9B97, 0x9B98, 0x9B99, 0x9B9A, 0x9B9B, 0x9B9C, 0x9B9D, 0x9B9E, 0x9B9F, + 0x9BA0, 0x9BA1, 0x9BA2, 0x9BA3, 0x9BA4, 0x9BA5, 0x9BA6, 0x9BA7, 0x9BA8, + 0x9BA9, 0x9BAA, 0x9BAB, 0x9BAC, 0x9BAD, 0x9BAE, 0x9BAF, 0x9BB0, 0x9BB1, + 0x9BB2, 0x9BB3, 0x9BB4, 0x9BB5, 0x9BB6, 0x9BB7, 0x9BB8, 0x9BB9, 0x9BBA, + 0x003F, 0x9BBB, 0x9BBC, 0x9BBD, 0x9BBE, 0x9BBF, 0x9BC0, 0x9BC1, 0x9BC2, + 0x9BC3, 0x9BC4, 0x9BC5, 0x9BC6, 0x9BC7, 0x9BC8, 0x9BC9, 0x9BCA, 0x9BCB, + 0x9BCC, 0x9BCD, 0x9BCE, 0x9BCF, 0x9BD0, 0x9BD1, 0x9BD2, 0x9BD3, 0x9BD4, + 0x9BD5, 0x9BD6, 0x9BD7, 0x9BD8, 0x9BD9, 0x9BDA, 0x9BDB, 0x9162, 0x9161, + 0x9170, 0x9169, 0x916F, 0x917D, 0x917E, 0x9172, 0x9174, 0x9179, 0x918C, + 0x9185, 0x9190, 0x918D, 0x9191, 0x91A2, 0x91A3, 0x91AA, 0x91AD, 0x91AE, + 0x91AF, 0x91B5, 0x91B4, 0x91BA, 0x8C55, 0x9E7E, 0x8DB8, 0x8DEB, 0x8E05, + 0x8E59, 0x8E69, 0x8DB5, 0x8DBF, 0x8DBC, 0x8DBA, 0x8DC4, 0x8DD6, 0x8DD7, + 0x8DDA, 0x8DDE, 0x8DCE, 0x8DCF, 0x8DDB, 0x8DC6, 0x8DEC, 0x8DF7, 0x8DF8, + 0x8DE3, 0x8DF9, 0x8DFB, 0x8DE4, 0x8E09, 0x8DFD, 0x8E14, 0x8E1D, 0x8E1F, + 0x8E2C, 0x8E2E, 0x8E23, 0x8E2F, 0x8E3A, 0x8E40, 0x8E39, 0x8E35, 0x8E3D, + 0x8E31, 0x8E49, 0x8E41, 0x8E42, 0x8E51, 0x8E52, 0x8E4A, 0x8E70, 0x8E76, + 0x8E7C, 0x8E6F, 0x8E74, 0x8E85, 0x8E8F, 0x8E94, 0x8E90, 0x8E9C, 0x8E9E, + 0x8C78, 0x8C82, 0x8C8A, 0x8C85, 0x8C98, 0x8C94, 0x659B, 0x89D6, 0x89DE, + 0x89DA, 0x89DC}, + {0x9BDC, 0x9BDD, 0x9BDE, 0x9BDF, 0x9BE0, 0x9BE1, 0x9BE2, 0x9BE3, 0x9BE4, + 0x9BE5, 0x9BE6, 0x9BE7, 0x9BE8, 0x9BE9, 0x9BEA, 0x9BEB, 0x9BEC, 0x9BED, + 0x9BEE, 0x9BEF, 0x9BF0, 0x9BF1, 0x9BF2, 0x9BF3, 0x9BF4, 0x9BF5, 0x9BF6, + 0x9BF7, 0x9BF8, 0x9BF9, 0x9BFA, 0x9BFB, 0x9BFC, 0x9BFD, 0x9BFE, 0x9BFF, + 0x9C00, 0x9C01, 0x9C02, 0x9C03, 0x9C04, 0x9C05, 0x9C06, 0x9C07, 0x9C08, + 0x9C09, 0x9C0A, 0x9C0B, 0x9C0C, 0x9C0D, 0x9C0E, 0x9C0F, 0x9C10, 0x9C11, + 0x9C12, 0x9C13, 0x9C14, 0x9C15, 0x9C16, 0x9C17, 0x9C18, 0x9C19, 0x9C1A, + 0x003F, 0x9C1B, 0x9C1C, 0x9C1D, 0x9C1E, 0x9C1F, 0x9C20, 0x9C21, 0x9C22, + 0x9C23, 0x9C24, 0x9C25, 0x9C26, 0x9C27, 0x9C28, 0x9C29, 0x9C2A, 0x9C2B, + 0x9C2C, 0x9C2D, 0x9C2E, 0x9C2F, 0x9C30, 0x9C31, 0x9C32, 0x9C33, 0x9C34, + 0x9C35, 0x9C36, 0x9C37, 0x9C38, 0x9C39, 0x9C3A, 0x9C3B, 0x89E5, 0x89EB, + 0x89EF, 0x8A3E, 0x8B26, 0x9753, 0x96E9, 0x96F3, 0x96EF, 0x9706, 0x9701, + 0x9708, 0x970F, 0x970E, 0x972A, 0x972D, 0x9730, 0x973E, 0x9F80, 0x9F83, + 0x9F85, 0x9F86, 0x9F87, 0x9F88, 0x9F89, 0x9F8A, 0x9F8C, 0x9EFE, 0x9F0B, + 0x9F0D, 0x96B9, 0x96BC, 0x96BD, 0x96CE, 0x96D2, 0x77BF, 0x96E0, 0x928E, + 0x92AE, 0x92C8, 0x933E, 0x936A, 0x93CA, 0x938F, 0x943E, 0x946B, 0x9C7F, + 0x9C82, 0x9C85, 0x9C86, 0x9C87, 0x9C88, 0x7A23, 0x9C8B, 0x9C8E, 0x9C90, + 0x9C91, 0x9C92, 0x9C94, 0x9C95, 0x9C9A, 0x9C9B, 0x9C9E, 0x9C9F, 0x9CA0, + 0x9CA1, 0x9CA2, 0x9CA3, 0x9CA5, 0x9CA6, 0x9CA7, 0x9CA8, 0x9CA9, 0x9CAB, + 0x9CAD, 0x9CAE, 0x9CB0, 0x9CB1, 0x9CB2, 0x9CB3, 0x9CB4, 0x9CB5, 0x9CB6, + 0x9CB7, 0x9CBA, 0x9CBB, 0x9CBC, 0x9CBD, 0x9CC4, 0x9CC5, 0x9CC6, 0x9CC7, + 0x9CCA, 0x9CCB}, + {0x9C3C, 0x9C3D, 0x9C3E, 0x9C3F, 0x9C40, 0x9C41, 0x9C42, 0x9C43, 0x9C44, + 0x9C45, 0x9C46, 0x9C47, 0x9C48, 0x9C49, 0x9C4A, 0x9C4B, 0x9C4C, 0x9C4D, + 0x9C4E, 0x9C4F, 0x9C50, 0x9C51, 0x9C52, 0x9C53, 0x9C54, 0x9C55, 0x9C56, + 0x9C57, 0x9C58, 0x9C59, 0x9C5A, 0x9C5B, 0x9C5C, 0x9C5D, 0x9C5E, 0x9C5F, + 0x9C60, 0x9C61, 0x9C62, 0x9C63, 0x9C64, 0x9C65, 0x9C66, 0x9C67, 0x9C68, + 0x9C69, 0x9C6A, 0x9C6B, 0x9C6C, 0x9C6D, 0x9C6E, 0x9C6F, 0x9C70, 0x9C71, + 0x9C72, 0x9C73, 0x9C74, 0x9C75, 0x9C76, 0x9C77, 0x9C78, 0x9C79, 0x9C7A, + 0x003F, 0x9C7B, 0x9C7D, 0x9C7E, 0x9C80, 0x9C83, 0x9C84, 0x9C89, 0x9C8A, + 0x9C8C, 0x9C8F, 0x9C93, 0x9C96, 0x9C97, 0x9C98, 0x9C99, 0x9C9D, 0x9CAA, + 0x9CAC, 0x9CAF, 0x9CB9, 0x9CBE, 0x9CBF, 0x9CC0, 0x9CC1, 0x9CC2, 0x9CC8, + 0x9CC9, 0x9CD1, 0x9CD2, 0x9CDA, 0x9CDB, 0x9CE0, 0x9CE1, 0x9CCC, 0x9CCD, + 0x9CCE, 0x9CCF, 0x9CD0, 0x9CD3, 0x9CD4, 0x9CD5, 0x9CD7, 0x9CD8, 0x9CD9, + 0x9CDC, 0x9CDD, 0x9CDF, 0x9CE2, 0x977C, 0x9785, 0x9791, 0x9792, 0x9794, + 0x97AF, 0x97AB, 0x97A3, 0x97B2, 0x97B4, 0x9AB1, 0x9AB0, 0x9AB7, 0x9E58, + 0x9AB6, 0x9ABA, 0x9ABC, 0x9AC1, 0x9AC0, 0x9AC5, 0x9AC2, 0x9ACB, 0x9ACC, + 0x9AD1, 0x9B45, 0x9B43, 0x9B47, 0x9B49, 0x9B48, 0x9B4D, 0x9B51, 0x98E8, + 0x990D, 0x992E, 0x9955, 0x9954, 0x9ADF, 0x9AE1, 0x9AE6, 0x9AEF, 0x9AEB, + 0x9AFB, 0x9AED, 0x9AF9, 0x9B08, 0x9B0F, 0x9B13, 0x9B1F, 0x9B23, 0x9EBD, + 0x9EBE, 0x7E3B, 0x9E82, 0x9E87, 0x9E88, 0x9E8B, 0x9E92, 0x93D6, 0x9E9D, + 0x9E9F, 0x9EDB, 0x9EDC, 0x9EDD, 0x9EE0, 0x9EDF, 0x9EE2, 0x9EE9, 0x9EE7, + 0x9EE5, 0x9EEA, 0x9EEF, 0x9F22, 0x9F2C, 0x9F2F, 0x9F39, 0x9F37, 0x9F3D, + 0x9F3E, 0x9F44}, + {0x9CE3, 0x9CE4, 0x9CE5, 0x9CE6, 0x9CE7, 0x9CE8, 0x9CE9, 0x9CEA, 0x9CEB, + 0x9CEC, 0x9CED, 0x9CEE, 0x9CEF, 0x9CF0, 0x9CF1, 0x9CF2, 0x9CF3, 0x9CF4, + 0x9CF5, 0x9CF6, 0x9CF7, 0x9CF8, 0x9CF9, 0x9CFA, 0x9CFB, 0x9CFC, 0x9CFD, + 0x9CFE, 0x9CFF, 0x9D00, 0x9D01, 0x9D02, 0x9D03, 0x9D04, 0x9D05, 0x9D06, + 0x9D07, 0x9D08, 0x9D09, 0x9D0A, 0x9D0B, 0x9D0C, 0x9D0D, 0x9D0E, 0x9D0F, + 0x9D10, 0x9D11, 0x9D12, 0x9D13, 0x9D14, 0x9D15, 0x9D16, 0x9D17, 0x9D18, + 0x9D19, 0x9D1A, 0x9D1B, 0x9D1C, 0x9D1D, 0x9D1E, 0x9D1F, 0x9D20, 0x9D21, + 0x003F, 0x9D22, 0x9D23, 0x9D24, 0x9D25, 0x9D26, 0x9D27, 0x9D28, 0x9D29, + 0x9D2A, 0x9D2B, 0x9D2C, 0x9D2D, 0x9D2E, 0x9D2F, 0x9D30, 0x9D31, 0x9D32, + 0x9D33, 0x9D34, 0x9D35, 0x9D36, 0x9D37, 0x9D38, 0x9D39, 0x9D3A, 0x9D3B, + 0x9D3C, 0x9D3D, 0x9D3E, 0x9D3F, 0x9D40, 0x9D41, 0x9D42, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F}, + {0x9D43, 0x9D44, 0x9D45, 0x9D46, 0x9D47, 0x9D48, 0x9D49, 0x9D4A, 0x9D4B, + 0x9D4C, 0x9D4D, 0x9D4E, 0x9D4F, 0x9D50, 0x9D51, 0x9D52, 0x9D53, 0x9D54, + 0x9D55, 0x9D56, 0x9D57, 0x9D58, 0x9D59, 0x9D5A, 0x9D5B, 0x9D5C, 0x9D5D, + 0x9D5E, 0x9D5F, 0x9D60, 0x9D61, 0x9D62, 0x9D63, 0x9D64, 0x9D65, 0x9D66, + 0x9D67, 0x9D68, 0x9D69, 0x9D6A, 0x9D6B, 0x9D6C, 0x9D6D, 0x9D6E, 0x9D6F, + 0x9D70, 0x9D71, 0x9D72, 0x9D73, 0x9D74, 0x9D75, 0x9D76, 0x9D77, 0x9D78, + 0x9D79, 0x9D7A, 0x9D7B, 0x9D7C, 0x9D7D, 0x9D7E, 0x9D7F, 0x9D80, 0x9D81, + 0x003F, 0x9D82, 0x9D83, 0x9D84, 0x9D85, 0x9D86, 0x9D87, 0x9D88, 0x9D89, + 0x9D8A, 0x9D8B, 0x9D8C, 0x9D8D, 0x9D8E, 0x9D8F, 0x9D90, 0x9D91, 0x9D92, + 0x9D93, 0x9D94, 0x9D95, 0x9D96, 0x9D97, 0x9D98, 0x9D99, 0x9D9A, 0x9D9B, + 0x9D9C, 0x9D9D, 0x9D9E, 0x9D9F, 0x9DA0, 0x9DA1, 0x9DA2, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F}, + {0x9DA3, 0x9DA4, 0x9DA5, 0x9DA6, 0x9DA7, 0x9DA8, 0x9DA9, 0x9DAA, 0x9DAB, + 0x9DAC, 0x9DAD, 0x9DAE, 0x9DAF, 0x9DB0, 0x9DB1, 0x9DB2, 0x9DB3, 0x9DB4, + 0x9DB5, 0x9DB6, 0x9DB7, 0x9DB8, 0x9DB9, 0x9DBA, 0x9DBB, 0x9DBC, 0x9DBD, + 0x9DBE, 0x9DBF, 0x9DC0, 0x9DC1, 0x9DC2, 0x9DC3, 0x9DC4, 0x9DC5, 0x9DC6, + 0x9DC7, 0x9DC8, 0x9DC9, 0x9DCA, 0x9DCB, 0x9DCC, 0x9DCD, 0x9DCE, 0x9DCF, + 0x9DD0, 0x9DD1, 0x9DD2, 0x9DD3, 0x9DD4, 0x9DD5, 0x9DD6, 0x9DD7, 0x9DD8, + 0x9DD9, 0x9DDA, 0x9DDB, 0x9DDC, 0x9DDD, 0x9DDE, 0x9DDF, 0x9DE0, 0x9DE1, + 0x003F, 0x9DE2, 0x9DE3, 0x9DE4, 0x9DE5, 0x9DE6, 0x9DE7, 0x9DE8, 0x9DE9, + 0x9DEA, 0x9DEB, 0x9DEC, 0x9DED, 0x9DEE, 0x9DEF, 0x9DF0, 0x9DF1, 0x9DF2, + 0x9DF3, 0x9DF4, 0x9DF5, 0x9DF6, 0x9DF7, 0x9DF8, 0x9DF9, 0x9DFA, 0x9DFB, + 0x9DFC, 0x9DFD, 0x9DFE, 0x9DFF, 0x9E00, 0x9E01, 0x9E02, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F}, + {0x9E03, 0x9E04, 0x9E05, 0x9E06, 0x9E07, 0x9E08, 0x9E09, 0x9E0A, 0x9E0B, + 0x9E0C, 0x9E0D, 0x9E0E, 0x9E0F, 0x9E10, 0x9E11, 0x9E12, 0x9E13, 0x9E14, + 0x9E15, 0x9E16, 0x9E17, 0x9E18, 0x9E19, 0x9E1A, 0x9E1B, 0x9E1C, 0x9E1D, + 0x9E1E, 0x9E24, 0x9E27, 0x9E2E, 0x9E30, 0x9E34, 0x9E3B, 0x9E3C, 0x9E40, + 0x9E4D, 0x9E50, 0x9E52, 0x9E53, 0x9E54, 0x9E56, 0x9E59, 0x9E5D, 0x9E5F, + 0x9E60, 0x9E61, 0x9E62, 0x9E65, 0x9E6E, 0x9E6F, 0x9E72, 0x9E74, 0x9E75, + 0x9E76, 0x9E77, 0x9E78, 0x9E79, 0x9E7A, 0x9E7B, 0x9E7C, 0x9E7D, 0x9E80, + 0x003F, 0x9E81, 0x9E83, 0x9E84, 0x9E85, 0x9E86, 0x9E89, 0x9E8A, 0x9E8C, + 0x9E8D, 0x9E8E, 0x9E8F, 0x9E90, 0x9E91, 0x9E94, 0x9E95, 0x9E96, 0x9E97, + 0x9E98, 0x9E99, 0x9E9A, 0x9E9B, 0x9E9C, 0x9E9E, 0x9EA0, 0x9EA1, 0x9EA2, + 0x9EA3, 0x9EA4, 0x9EA5, 0x9EA7, 0x9EA8, 0x9EA9, 0x9EAA, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F}, + {0x9EAB, 0x9EAC, 0x9EAD, 0x9EAE, 0x9EAF, 0x9EB0, 0x9EB1, 0x9EB2, 0x9EB3, + 0x9EB5, 0x9EB6, 0x9EB7, 0x9EB9, 0x9EBA, 0x9EBC, 0x9EBF, 0x9EC0, 0x9EC1, + 0x9EC2, 0x9EC3, 0x9EC5, 0x9EC6, 0x9EC7, 0x9EC8, 0x9ECA, 0x9ECB, 0x9ECC, + 0x9ED0, 0x9ED2, 0x9ED3, 0x9ED5, 0x9ED6, 0x9ED7, 0x9ED9, 0x9EDA, 0x9EDE, + 0x9EE1, 0x9EE3, 0x9EE4, 0x9EE6, 0x9EE8, 0x9EEB, 0x9EEC, 0x9EED, 0x9EEE, + 0x9EF0, 0x9EF1, 0x9EF2, 0x9EF3, 0x9EF4, 0x9EF5, 0x9EF6, 0x9EF7, 0x9EF8, + 0x9EFA, 0x9EFD, 0x9EFF, 0x9F00, 0x9F01, 0x9F02, 0x9F03, 0x9F04, 0x9F05, + 0x003F, 0x9F06, 0x9F07, 0x9F08, 0x9F09, 0x9F0A, 0x9F0C, 0x9F0F, 0x9F11, + 0x9F12, 0x9F14, 0x9F15, 0x9F16, 0x9F18, 0x9F1A, 0x9F1B, 0x9F1C, 0x9F1D, + 0x9F1E, 0x9F1F, 0x9F21, 0x9F23, 0x9F24, 0x9F25, 0x9F26, 0x9F27, 0x9F28, + 0x9F29, 0x9F2A, 0x9F2B, 0x9F2D, 0x9F2E, 0x9F30, 0x9F31, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F}, + {0x9F32, 0x9F33, 0x9F34, 0x9F35, 0x9F36, 0x9F38, 0x9F3A, 0x9F3C, 0x9F3F, + 0x9F40, 0x9F41, 0x9F42, 0x9F43, 0x9F45, 0x9F46, 0x9F47, 0x9F48, 0x9F49, + 0x9F4A, 0x9F4B, 0x9F4C, 0x9F4D, 0x9F4E, 0x9F4F, 0x9F52, 0x9F53, 0x9F54, + 0x9F55, 0x9F56, 0x9F57, 0x9F58, 0x9F59, 0x9F5A, 0x9F5B, 0x9F5C, 0x9F5D, + 0x9F5E, 0x9F5F, 0x9F60, 0x9F61, 0x9F62, 0x9F63, 0x9F64, 0x9F65, 0x9F66, + 0x9F67, 0x9F68, 0x9F69, 0x9F6A, 0x9F6B, 0x9F6C, 0x9F6D, 0x9F6E, 0x9F6F, + 0x9F70, 0x9F71, 0x9F72, 0x9F73, 0x9F74, 0x9F75, 0x9F76, 0x9F77, 0x9F78, + 0x003F, 0x9F79, 0x9F7A, 0x9F7B, 0x9F7C, 0x9F7D, 0x9F7E, 0x9F81, 0x9F82, + 0x9F8D, 0x9F8E, 0x9F8F, 0x9F90, 0x9F91, 0x9F92, 0x9F93, 0x9F94, 0x9F95, + 0x9F96, 0x9F97, 0x9F98, 0x9F9C, 0x9F9D, 0x9F9E, 0x9FA1, 0x9FA2, 0x9FA3, + 0x9FA4, 0x9FA5, 0xF92C, 0xF979, 0xF995, 0xF9E7, 0xF9F1, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F}, + {0xFA0C, 0xFA0D, 0xFA0E, 0xFA0F, 0xFA11, 0xFA13, 0xFA14, 0xFA18, 0xFA1F, + 0xFA20, 0xFA21, 0xFA23, 0xFA24, 0xFA27, 0xFA28, 0xFA29, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, 0x003F, + 0x003F, 0x003F}}}; #endif diff --git a/CorsixTH/Src/iso_fs.cpp b/CorsixTH/Src/iso_fs.cpp index afd1cf05..bcf52cd3 100644 --- a/CorsixTH/Src/iso_fs.cpp +++ b/CorsixTH/Src/iso_fs.cpp @@ -21,33 +21,31 @@ SOFTWARE. */ #include "iso_fs.h" -#include "th.h" -#include -#include -#include -#include -#include #include #include +#include +#include +#include +#include #include #include +#include +#include "th.h" namespace { -enum iso_volume_descriptor_type : uint8_t -{ - vdt_primary_volume = 0x01, - // Other type numbers are either reserved for future use, or are not - // interesting to us. - vdt_terminator = 0xFF, +enum iso_volume_descriptor_type : uint8_t { + vdt_primary_volume = 0x01, + // Other type numbers are either reserved for future use, or are not + // interesting to us. + vdt_terminator = 0xFF, }; /// Flag values for directory table entries. The flag itself is a bitmask. -enum iso_dir_ent_flag : uint8_t -{ - def_hidden = 0x01, - def_directory = 0x02, - def_multi_extent = 0x80, +enum iso_dir_ent_flag : uint8_t { + def_hidden = 0x01, + def_directory = 0x02, + def_multi_extent = 0x80, }; /// Offset to the 32bit sector of the file data @@ -79,7 +77,7 @@ constexpr int max_directory_depth = 16; /// Reasonably unique name of a file from Theme Hospital that can be used to /// indicate that we've loaded the right directory. -constexpr const char* vblk_0_filename = "VBLK-0.TAB"; +constexpr const char* vblk_0_filename = "VBLK-0.TAB"; /// Sector sizes can vary, but they must be powers of two, and the minimum /// size is 2048. @@ -100,97 +98,94 @@ constexpr uint32_t first_filesystem_sector = 16; /// Finds the length of the file name within a file identifier. /// The file identifier is `filename;file id`. -void trim_file_id(const uint8_t* sIdent, uint8_t& iLength) -{ - for (uint8_t i = 0; i < iLength; ++i) - { - if (sIdent[i] == ';') - { - iLength = i; - return; - } +void trim_file_id(const uint8_t* sIdent, uint8_t& iLength) { + for (uint8_t i = 0; i < iLength; ++i) { + if (sIdent[i] == ';') { + iLength = i; + return; } + } } /// Convert character to filename normalized format conforming to ISO filename /// limitations. All letters are converted to upper case, and `_` to `-`. char normalise(char c) { - if (c == '_') { - return '-'; - } else if ('a' <= c && c <= 'z') { - return static_cast(c - 'a' + 'A'); - } else { - return c; - } + if (c == '_') { + return '-'; + } else if ('a' <= c && c <= 'z') { + return static_cast(c - 'a' + 'A'); + } else { + return c; + } } /// Convert length bytes from the start pointer to a normalized filename /// string. All ASCII letters are converted to upper case, and `_` to `-`. std::string normalise(const uint8_t* start, size_t length) { - std::string ret; - const uint8_t* p = start; - for (size_t i = 0; i < length; i++) { - ret.push_back(normalise(static_cast(*p))); - ++p; - } - return ret; + std::string ret; + const uint8_t* p = start; + for (size_t i = 0; i < length; i++) { + ret.push_back(normalise(static_cast(*p))); + ++p; + } + return ret; } /// Convert c string to normalized filename string. All ASCII letters are /// converted to upper case, and `_` to `-`. std::string normalise(const char* str) { - std::string ret; - const char* p = str; - while (*p != '\0') { - ret.push_back(normalise(*p)); - ++p; - } - return ret; + std::string ret; + const char* p = str; + while (*p != '\0') { + ret.push_back(normalise(*p)); + ++p; + } + return ret; } /// A file entry from the directory table class iso_file_entry { -public: - /// Construct dummy entry. - iso_file_entry()=default; + public: + /// Construct dummy entry. + iso_file_entry() = default; - /// Construct entry from the given memory location. - /// The first byte is the size of the entry. Other useful headers are read - /// from their offsets from that location. - /// - /// \param Pointer to first byte of file entry in directory table. - iso_file_entry(const uint8_t* b) { - uint8_t size = *b; - if (size < minimum_file_entry_size) { - throw std::runtime_error("size specified for file entry is too small."); - } - - uint8_t filename_length = b[filename_length_offset]; - if (filename_length + filename_offset > size) { - throw std::runtime_error("size specified for file entry is too small."); - } - trim_file_id(b + filename_offset, filename_length); - filename = normalise(b + filename_offset, filename_length); - - data_sector = bytes_to_uint32_le(b + file_sector_offset); - data_length = bytes_to_uint32_le(b + file_data_length_offset); - flags = b[file_flags_offset]; + /// Construct entry from the given memory location. + /// The first byte is the size of the entry. Other useful headers are read + /// from their offsets from that location. + /// + /// \param Pointer to first byte of file entry in directory table. + iso_file_entry(const uint8_t* b) { + uint8_t size = *b; + if (size < minimum_file_entry_size) { + throw std::runtime_error("size specified for file entry is too small."); } - /// Logical location of the data for this file in the ISO image. - uint32_t data_sector; + uint8_t filename_length = b[filename_length_offset]; + if (filename_length + filename_offset > size) { + throw std::runtime_error("size specified for file entry is too small."); + } + trim_file_id(b + filename_offset, filename_length); + filename = normalise(b + filename_offset, filename_length); - /// The length of the data for this file. - uint32_t data_length; + data_sector = bytes_to_uint32_le(b + file_sector_offset); + data_length = bytes_to_uint32_le(b + file_data_length_offset); + flags = b[file_flags_offset]; + } - /// Flags that indicate whether this entry is a file or directory, along - /// with other properties. - /// - /// \see iso_dir_ent_flag - uint8_t flags; + /// Logical location of the data for this file in the ISO image. + uint32_t data_sector; - /// The filename of this entry. - std::string filename; + /// The length of the data for this file. + uint32_t data_length; + + /// Flags that indicate whether this entry is a file or directory, along + /// with other properties. + /// + /// \see iso_dir_ent_flag + uint8_t flags; + + /// The filename of this entry. + std::string filename; }; /** @@ -198,446 +193,432 @@ public: * stored in a byte buffer. */ class iso_directory_iterator final { - using iterator_category = std::input_iterator_tag; - using value_type = const iso_file_entry; - using difference_type = ptrdiff_t; - using pointer = const iso_file_entry*; - using reference = const iso_file_entry&; + using iterator_category = std::input_iterator_tag; + using value_type = const iso_file_entry; + using difference_type = ptrdiff_t; + using pointer = const iso_file_entry*; + using reference = const iso_file_entry&; -public: - iso_directory_iterator()=delete; + public: + iso_directory_iterator() = delete; - /** - * Initialize an iterator for the directory table in the memory region - * defined by by begin and end. This iterator is aware of its container - * and will throw an exception if an attempt is made to access it out of - * range. - * - * \param begin pointer to the first byte of the directory table. - * \param end pointer to the first byte following the directory table. - */ - iso_directory_iterator(const uint8_t* begin, const uint8_t* end) { - directory_ptr = begin; - end_ptr = end; - if (directory_ptr >= end_ptr) { - // dummy value, not accessible. - entry = iso_file_entry(); - } else { - entry = iso_file_entry(begin); - } + /** + * Initialize an iterator for the directory table in the memory region + * defined by by begin and end. This iterator is aware of its container + * and will throw an exception if an attempt is made to access it out of + * range. + * + * \param begin pointer to the first byte of the directory table. + * \param end pointer to the first byte following the directory table. + */ + iso_directory_iterator(const uint8_t* begin, const uint8_t* end) { + directory_ptr = begin; + end_ptr = end; + if (directory_ptr >= end_ptr) { + // dummy value, not accessible. + entry = iso_file_entry(); + } else { + entry = iso_file_entry(begin); + } + } + + /** + * Copy the given iso_directory_iterator + */ + iso_directory_iterator(iso_directory_iterator& it) { + directory_ptr = it.directory_ptr; + end_ptr = it.end_ptr; + entry = it.entry; + } + + /** + * Move the given iso_directory_iterator + */ + iso_directory_iterator(iso_directory_iterator&& it) { + directory_ptr = it.directory_ptr; + end_ptr = it.end_ptr; + entry = std::move(it.entry); + it.directory_ptr = nullptr; + it.end_ptr = nullptr; + it.entry = iso_file_entry(); + } + + ~iso_directory_iterator() = default; + + /** + * Determine whether two iso_directory_iterators point to the same table + * entry. + */ + bool operator==(const iso_directory_iterator& rhs) const { + return (this->directory_ptr == rhs.directory_ptr); + } + + /** + * Determine whether to iso_directory_iterators do not point to the same + * table entry. + */ + bool operator!=(const iso_directory_iterator& rhs) const { + return !((*this) == rhs); + } + + /** + * Get the file entry pointed to by the iterator. + */ + reference operator*() const { + if (directory_ptr >= end_ptr) { + throw std::out_of_range("iso directory iterator is past end of input"); + } + return entry; + } + + /** + * Assign this iterator the value of another iterator by copy + */ + iso_directory_iterator& operator=(iso_directory_iterator& rhs) { + directory_ptr = rhs.directory_ptr; + end_ptr = rhs.end_ptr; + entry = rhs.entry; + return *this; + } + + /** + * Assign this iterator the value of another iterator by move + */ + iso_directory_iterator& operator=(iso_directory_iterator&& rhs) { + directory_ptr = rhs.directory_ptr; + end_ptr = rhs.end_ptr; + entry = std::move(rhs.entry); + rhs.directory_ptr = nullptr; + rhs.end_ptr = nullptr; + rhs.entry = {}; + return *this; + } + + /** + * Advance this iterator to the next file entry in the directory table, + * returning the result. + * In cases where advancing the iterator would read past the end of the + * directory table, an exception is thrown and the iterator is not + * advanced. + */ + iso_directory_iterator& operator++() { + if (directory_ptr >= end_ptr) { + throw std::out_of_range( + "Cannot advance iso directory iterator past end of input"); } - /** - * Copy the given iso_directory_iterator - */ - iso_directory_iterator(iso_directory_iterator& it) { - directory_ptr = it.directory_ptr; - end_ptr = it.end_ptr; - entry = it.entry; + const uint8_t* new_dir_ptr = directory_ptr + *directory_ptr; + while (new_dir_ptr < end_ptr && *new_dir_ptr == 0) { + ++new_dir_ptr; } - /** - * Move the given iso_directory_iterator - */ - iso_directory_iterator(iso_directory_iterator&& it) { - directory_ptr = it.directory_ptr; - end_ptr = it.end_ptr; - entry = std::move(it.entry); - it.directory_ptr = nullptr; - it.end_ptr = nullptr; - it.entry = iso_file_entry(); + // Catch a malformed directory entry where the size would extend past + // the end of the table. + if (new_dir_ptr < end_ptr && new_dir_ptr + *new_dir_ptr > end_ptr) { + throw std::runtime_error( + "The last directory entry was larger than the defined " + "table region."); } - ~iso_directory_iterator()=default; - - /** - * Determine whether two iso_directory_iterators point to the same table - * entry. - */ - bool operator==(const iso_directory_iterator& rhs) const { - return (this->directory_ptr == rhs.directory_ptr); + if (new_dir_ptr < end_ptr) { + entry = iso_file_entry(new_dir_ptr); + } else { + entry = iso_file_entry(); } + directory_ptr = new_dir_ptr; + return *this; + } - /** - * Determine whether to iso_directory_iterators do not point to the same - * table entry. - */ - bool operator!=(const iso_directory_iterator& rhs) const { - return !((*this)==rhs); - } + /** + * Advance this iterator to the next file entry in the directory table, + * returning a copy of the old iterator. + */ + iso_directory_iterator operator++(int) { + iso_directory_iterator old(*this); + ++(*this); + return old; + } - /** - * Get the file entry pointed to by the iterator. - */ - reference operator*() const { - if (directory_ptr >= end_ptr) { - throw std::out_of_range("iso directory iterator is past end of input"); - } - return entry; - } + private: + /// Pointer to the current entry. + const uint8_t* directory_ptr; - /** - * Assign this iterator the value of another iterator by copy - */ - iso_directory_iterator& operator=(iso_directory_iterator& rhs) { - directory_ptr = rhs.directory_ptr; - end_ptr = rhs.end_ptr; - entry = rhs.entry; - return *this; - } + /// Pointer to the end of the directory table. + const uint8_t* end_ptr; - /** - * Assign this iterator the value of another iterator by move - */ - iso_directory_iterator& operator=(iso_directory_iterator&& rhs) { - directory_ptr = rhs.directory_ptr; - end_ptr = rhs.end_ptr; - entry = std::move(rhs.entry); - rhs.directory_ptr = nullptr; - rhs.end_ptr = nullptr; - rhs.entry = {}; - return *this; - } - - /** - * Advance this iterator to the next file entry in the directory table, - * returning the result. - * In cases where advancing the iterator would read past the end of the - * directory table, an exception is thrown and the iterator is not - * advanced. - */ - iso_directory_iterator& operator++() { - if (directory_ptr >= end_ptr) { - throw std::out_of_range("Cannot advance iso directory iterator past end of input"); - } - - const uint8_t* new_dir_ptr = directory_ptr + *directory_ptr; - while (new_dir_ptr < end_ptr && *new_dir_ptr == 0) { - ++new_dir_ptr; - } - - // Catch a malformed directory entry where the size would extend past - // the end of the table. - if (new_dir_ptr < end_ptr && new_dir_ptr + *new_dir_ptr > end_ptr) { - throw std::runtime_error("The last directory entry was larger than the defined table region."); - } - - if (new_dir_ptr < end_ptr) { - entry = iso_file_entry(new_dir_ptr); - } else { - entry = iso_file_entry(); - } - directory_ptr = new_dir_ptr; - return *this; - } - - /** - * Advance this iterator to the next file entry in the directory table, - * returning a copy of the old iterator. - */ - iso_directory_iterator operator++(int) { - iso_directory_iterator old(*this); - ++(*this); - return old; - } - -private: - /// Pointer to the current entry. - const uint8_t* directory_ptr; - - /// Pointer to the end of the directory table. - const uint8_t* end_ptr; - - /// Current entry. - iso_file_entry entry; + /// Current entry. + iso_file_entry entry; }; -} // namespace +} // namespace -iso_filesystem::iso_filesystem() : - raw_file(nullptr), - error(nullptr), - files(), - path_seperator('\\') -{ } +iso_filesystem::iso_filesystem() + : raw_file(nullptr), error(nullptr), files(), path_seperator('\\') {} -iso_filesystem::~iso_filesystem() -{ - clear(); +iso_filesystem::~iso_filesystem() { clear(); } + +void iso_filesystem::clear() { + delete[] error; + error = nullptr; + files.clear(); } -void iso_filesystem::clear() -{ - delete[] error; - error = nullptr; - files.clear(); +void iso_filesystem::set_path_separator(char cSeparator) { + path_seperator = cSeparator; } -void iso_filesystem::set_path_separator(char cSeparator) -{ - path_seperator = cSeparator; -} +bool iso_filesystem::initialise(std::FILE* fRawFile) { + raw_file = fRawFile; + clear(); -bool iso_filesystem::initialise(std::FILE* fRawFile) -{ - raw_file = fRawFile; - clear(); + // Until we know better, assume that sectors are 2048 bytes. + sector_size = min_sector_size; - // Until we know better, assume that sectors are 2048 bytes. - sector_size = min_sector_size; - - // The first 16 sectors are reserved for bootable media. - // Volume descriptor records follow this, with one record per sector. - for(uint32_t iSector = first_filesystem_sector; seek_to_sector(iSector); ++iSector) { - uint8_t aBuffer[root_directory_offset + root_directory_entry_size]; - if(!read_data(sizeof(aBuffer), aBuffer)) { - break; - } - // CD001 is a standard identifier, \x01 is a version number - if(std::memcmp(aBuffer + 1, "CD001\x01", 6) == 0) { - if(aBuffer[0] == vdt_primary_volume) { - sector_size = bytes_to_uint16_le(aBuffer + sector_size_offset); - try { - find_hosp_directory(aBuffer + root_directory_offset, root_directory_entry_size, 0); - if(files.empty()) { - set_error("Could not find Theme Hospital data directory."); - return false; - } else { - return true; - } - } catch (const std::exception& ex) { - set_error(ex.what()); - return false; - } - } else if(aBuffer[0] == vdt_terminator) { - break; - } - } + // The first 16 sectors are reserved for bootable media. + // Volume descriptor records follow this, with one record per sector. + for (uint32_t iSector = first_filesystem_sector; seek_to_sector(iSector); + ++iSector) { + uint8_t aBuffer[root_directory_offset + root_directory_entry_size]; + if (!read_data(sizeof(aBuffer), aBuffer)) { + break; } - set_error("Could not find primary volume descriptor."); + // CD001 is a standard identifier, \x01 is a version number + if (std::memcmp(aBuffer + 1, "CD001\x01", 6) == 0) { + if (aBuffer[0] == vdt_primary_volume) { + sector_size = bytes_to_uint16_le(aBuffer + sector_size_offset); + try { + find_hosp_directory(aBuffer + root_directory_offset, + root_directory_entry_size, 0); + if (files.empty()) { + set_error( + "Could not find Theme Hospital data " + "directory."); + return false; + } else { + return true; + } + } catch (const std::exception& ex) { + set_error(ex.what()); + return false; + } + } else if (aBuffer[0] == vdt_terminator) { + break; + } + } + } + set_error("Could not find primary volume descriptor."); + return false; +} + +bool iso_filesystem::file_metadata_less(const file_metadata& lhs, + const file_metadata& rhs) { + return lhs.path < rhs.path; +} + +int iso_filesystem::find_hosp_directory(const uint8_t* pDirEnt, + int iDirEntsSize, int iLevel) { + // Sanity check + // Apart from at the root level, directory record arrays must take up whole + // sectors, whose sizes are powers of two and at least 2048. + // The formal limit on directory depth is 8, so hitting 16 is insane. + if ((iLevel != 0 && (iDirEntsSize & (min_sector_size - 1)) != 0) || + iLevel > max_directory_depth) + return 0; + + uint8_t* pBuffer = nullptr; + uint32_t iBufferSize = 0; + iso_directory_iterator dir_iter(pDirEnt, pDirEnt + iDirEntsSize); + iso_directory_iterator end_iter(pDirEnt + iDirEntsSize, + pDirEnt + iDirEntsSize); + for (; dir_iter != end_iter; ++dir_iter) { + const iso_file_entry& ent = *dir_iter; + if (ent.flags & def_directory) { + // The names "\x00" and "\x01" are used for the current directory + // the parent directory respectively. We only want to visit these + // when at the root level. + if (iLevel == 0 || !(ent.filename == "\x00" || ent.filename == "\x01")) { + if (ent.data_length > iBufferSize) { + delete[] pBuffer; + iBufferSize = ent.data_length; + pBuffer = new uint8_t[iBufferSize]; + } + if (seek_to_sector(ent.data_sector) && + read_data(ent.data_length, pBuffer)) { + int iFoundLevel = + find_hosp_directory(pBuffer, ent.data_length, iLevel + 1); + if (iFoundLevel != 0) { + if (iFoundLevel == 2) { + build_file_lookup_table(ent.data_sector, ent.data_length, + std::string("")); + } + delete[] pBuffer; + return iFoundLevel + 1; + } + } + } + } else { + // Look for VBLK-0.TAB to serve as indication that we've found the + // Theme Hospital data. + if (ent.filename == vblk_0_filename) { + return 1; + } + } + } + delete[] pBuffer; + + return 0; +} + +void iso_filesystem::build_file_lookup_table(uint32_t iSector, int iDirEntsSize, + const std::string& prefix) { + // Sanity check + // Apart from at the root level, directory record arrays must take up whole + // sectors, whose sizes are powers of two and at least 2048. + // Path lengths shouldn't exceed 256 either (or at least not for the files + // which we're interested in). + if ((prefix.size() != 0 && (iDirEntsSize & 0x7FF)) || (prefix.size() > 256)) + return; + + uint8_t* pBuffer = new uint8_t[iDirEntsSize]; + if (!seek_to_sector(iSector) || !read_data(iDirEntsSize, pBuffer)) { + delete[] pBuffer; + return; + } + + uint8_t* pDirEnt = pBuffer; + iso_directory_iterator dir_iter(pDirEnt, pDirEnt + iDirEntsSize); + iso_directory_iterator end_iter(pDirEnt + iDirEntsSize, + pDirEnt + iDirEntsSize); + for (; dir_iter != end_iter; ++dir_iter) { + const iso_file_entry& ent = *dir_iter; + std::string path; + if (prefix.empty()) { + path = ent.filename; + } else { + path = prefix + path_seperator + ent.filename; + } + + if (ent.flags & def_directory) { + // None of the directories which we're interested in have length 1. + // This also avoids the dummy "current" and "parent" directories. + if (ent.filename.size() > 1) { + build_file_lookup_table(ent.data_sector, ent.data_length, path); + } + } else { + file_metadata file{}; + file.path = std::move(path); + file.sector = ent.data_sector; + file.size = ent.data_length; + files.push_back(file); + } + } + delete[] pBuffer; + + if (prefix.size() == 0) { + // The lookup table will be ordered by the underlying ordering of the + // disk. we want it sorted by the path for ease of lookup. + std::sort(files.begin(), files.end(), file_metadata_less); + } +} + +void iso_filesystem::visit_directory_files( + const char* sPath, void (*fnCallback)(void*, const char*, const char*), + void* pCallbackData) const { + std::string normalised_path = normalise(sPath); + + // Inefficient (better would be to binary search for first and last files + // which begin with sPath), but who cares - this isn't called often + for (const file_metadata& file : files) { + if (normalised_path.size() < file.path.size() && + std::equal(normalised_path.begin(), normalised_path.end(), + file.path.begin())) { + size_t filename_pos = normalised_path.size(); + if (file.path.at(normalised_path.size()) == path_seperator) { + ++filename_pos; + } + std::string filename(file.path.substr(filename_pos)); + + if (filename.find(path_seperator) == filename.npos) { + fnCallback(pCallbackData, filename.c_str(), file.path.c_str()); + } + } + } +} + +iso_filesystem::file_handle iso_filesystem::find_file(const char* sPath) const { + std::string normalised_path = normalise(sPath); + + // Standard binary search over sorted list of files + int iLower = 0; + int iUpper = static_cast(files.size()); + while (iLower != iUpper) { + int iMid = (iLower + iUpper) / 2; + int iComp = normalised_path.compare(files[iMid].path); + if (iComp == 0) { + return iMid + 1; + } else if (iComp < 0) { + iUpper = iMid; + } else { + iLower = iMid + 1; + } + } + return 0; +} + +uint32_t iso_filesystem::get_file_size(file_handle iFile) const { + if (iFile <= 0 || static_cast(iFile) > files.size()) + return 0; + else + return files[iFile - 1].size; +} + +bool iso_filesystem::get_file_data(file_handle iFile, uint8_t* pBuffer) { + if (iFile <= 0 || static_cast(iFile) > files.size()) { + set_error("Invalid file handle."); return false; + } else { + return seek_to_sector(files[iFile - 1].sector) && + read_data(files[iFile - 1].size, pBuffer); + } } -bool iso_filesystem::file_metadata_less(const file_metadata& lhs, const file_metadata& rhs) -{ - return lhs.path < rhs.path; +const char* iso_filesystem::get_error() const { return error; } + +bool iso_filesystem::seek_to_sector(uint32_t iSector) { + if (!raw_file) { + set_error("No raw file."); + return false; + } + int res = + std::fseek(raw_file, sector_size * static_cast(iSector), SEEK_SET); + if (res == 0) { + return true; + } else { + set_error("Unable to seek to sector %i.", static_cast(iSector)); + return false; + } } -int iso_filesystem::find_hosp_directory(const uint8_t *pDirEnt, int iDirEntsSize, int iLevel) -{ - // Sanity check - // Apart from at the root level, directory record arrays must take up whole - // sectors, whose sizes are powers of two and at least 2048. - // The formal limit on directory depth is 8, so hitting 16 is insane. - if((iLevel != 0 && (iDirEntsSize & (min_sector_size - 1)) != 0) || iLevel > max_directory_depth) - return 0; - - uint8_t *pBuffer = nullptr; - uint32_t iBufferSize = 0; - iso_directory_iterator dir_iter(pDirEnt, pDirEnt + iDirEntsSize); - iso_directory_iterator end_iter(pDirEnt + iDirEntsSize, pDirEnt + iDirEntsSize); - for (; dir_iter != end_iter; ++dir_iter) { - const iso_file_entry& ent = *dir_iter; - if (ent.flags & def_directory) { - // The names "\x00" and "\x01" are used for the current directory - // the parent directory respectively. We only want to visit these - // when at the root level. - if (iLevel == 0 || !(ent.filename == "\x00" || ent.filename == "\x01")) { - if (ent.data_length > iBufferSize) { - delete[] pBuffer; - iBufferSize = ent.data_length; - pBuffer = new uint8_t[iBufferSize]; - } - if (seek_to_sector(ent.data_sector) && read_data(ent.data_length, pBuffer)) { - int iFoundLevel = find_hosp_directory(pBuffer, ent.data_length, iLevel + 1); - if (iFoundLevel != 0) { - if (iFoundLevel == 2) { - build_file_lookup_table(ent.data_sector, ent.data_length, std::string("")); - } - delete[] pBuffer; - return iFoundLevel + 1; - } - } - } - } else { - // Look for VBLK-0.TAB to serve as indication that we've found the - // Theme Hospital data. - if (ent.filename == vblk_0_filename) { - return 1; - } - } - } - delete[] pBuffer; - - return 0; +bool iso_filesystem::read_data(uint32_t iByteCount, uint8_t* pBuffer) { + if (!raw_file) { + set_error("No raw file."); + return false; + } + if (std::fread(pBuffer, 1, iByteCount, raw_file) == iByteCount) + return true; + else { + set_error("Unable to read %i bytes.", static_cast(iByteCount)); + return false; + } } -void iso_filesystem::build_file_lookup_table(uint32_t iSector, int iDirEntsSize, const std::string& prefix) -{ - // Sanity check - // Apart from at the root level, directory record arrays must take up whole - // sectors, whose sizes are powers of two and at least 2048. - // Path lengths shouldn't exceed 256 either (or at least not for the files - // which we're interested in). - if((prefix.size() != 0 && (iDirEntsSize & 0x7FF)) || (prefix.size() > 256)) - return; - - uint8_t *pBuffer = new uint8_t[iDirEntsSize]; - if(!seek_to_sector(iSector) || !read_data(iDirEntsSize, pBuffer)) - { - delete[] pBuffer; - return; - } - - uint8_t *pDirEnt = pBuffer; - iso_directory_iterator dir_iter(pDirEnt, pDirEnt + iDirEntsSize); - iso_directory_iterator end_iter(pDirEnt + iDirEntsSize, pDirEnt + iDirEntsSize); - for (; dir_iter != end_iter; ++dir_iter) { - const iso_file_entry& ent = *dir_iter; - std::string path; - if (prefix.empty()) { - path = ent.filename; - } else { - path = prefix + path_seperator + ent.filename; - } - - if (ent.flags & def_directory) { - // None of the directories which we're interested in have length 1. - // This also avoids the dummy "current" and "parent" directories. - if (ent.filename.size() > 1) { - build_file_lookup_table(ent.data_sector, ent.data_length, path); - } - } else { - file_metadata file {}; - file.path = std::move(path); - file.sector = ent.data_sector; - file.size = ent.data_length; - files.push_back(file); - } - } - delete[] pBuffer; - - if(prefix.size() == 0) - { - // The lookup table will be ordered by the underlying ordering of the - // disk. we want it sorted by the path for ease of lookup. - std::sort(files.begin(), files.end(), file_metadata_less); - } -} - -void iso_filesystem::visit_directory_files(const char* sPath, - void (*fnCallback)(void*, const char*, const char*), - void* pCallbackData) const -{ - std::string normalised_path = normalise(sPath); - - // Inefficient (better would be to binary search for first and last files - // which begin with sPath), but who cares - this isn't called often - for (const file_metadata& file : files) { - if (normalised_path.size() < file.path.size() && std::equal(normalised_path.begin(), normalised_path.end(), file.path.begin())) { - size_t filename_pos = normalised_path.size(); - if (file.path.at(normalised_path.size()) == path_seperator) { - ++filename_pos; - } - std::string filename(file.path.substr(filename_pos)); - - if (filename.find(path_seperator) == filename.npos) { - fnCallback(pCallbackData, filename.c_str(), file.path.c_str()); - } - } - } -} - -iso_filesystem::file_handle iso_filesystem::find_file(const char* sPath) const -{ - std::string normalised_path = normalise(sPath); - - // Standard binary search over sorted list of files - int iLower = 0; - int iUpper = static_cast(files.size()); - while(iLower != iUpper) - { - int iMid = (iLower + iUpper) / 2; - int iComp = normalised_path.compare(files[iMid].path); - if (iComp == 0) { - return iMid + 1; - } else if (iComp < 0) { - iUpper = iMid; - } else { - iLower = iMid + 1; - } - } - return 0; -} - -uint32_t iso_filesystem::get_file_size(file_handle iFile) const -{ - if(iFile <= 0 || static_cast(iFile) > files.size()) - return 0; - else - return files[iFile - 1].size; -} - -bool iso_filesystem::get_file_data(file_handle iFile, uint8_t *pBuffer) -{ - if(iFile <= 0 || static_cast(iFile) > files.size()) - { - set_error("Invalid file handle."); - return false; - } - else - { - return seek_to_sector(files[iFile - 1].sector) && - read_data(files[iFile - 1].size, pBuffer); - } -} - -const char* iso_filesystem::get_error() const -{ - return error; -} - -bool iso_filesystem::seek_to_sector(uint32_t iSector) -{ - if(!raw_file) - { - set_error("No raw file."); - return false; - } - if(std::fseek(raw_file, sector_size * static_cast(iSector), SEEK_SET) == 0) - return true; - else - { - set_error("Unable to seek to sector %i.", static_cast(iSector)); - return false; - } -} - -bool iso_filesystem::read_data(uint32_t iByteCount, uint8_t *pBuffer) -{ - if(!raw_file) - { - set_error("No raw file."); - return false; - } - if(std::fread(pBuffer, 1, iByteCount, raw_file) == iByteCount) - return true; - else - { - set_error("Unable to read %i bytes.", static_cast(iByteCount)); - return false; - } -} - -void iso_filesystem::set_error(const char* sFormat, ...) -{ - if(error == nullptr) - { - // None of the errors which we generate will be longer than 1024. - error = new char[1024]; - } - va_list a; - va_start(a, sFormat); - std::vsnprintf(error, 1024, sFormat, a); - va_end(a); +void iso_filesystem::set_error(const char* sFormat, ...) { + if (error == nullptr) { + // None of the errors which we generate will be longer than 1024. + error = new char[1024]; + } + va_list a; + va_start(a, sFormat); + std::vsnprintf(error, 1024, sFormat, a); + va_end(a); } diff --git a/CorsixTH/Src/iso_fs.h b/CorsixTH/Src/iso_fs.h index 8f7cb841..8ded3c5b 100644 --- a/CorsixTH/Src/iso_fs.h +++ b/CorsixTH/Src/iso_fs.h @@ -21,9 +21,9 @@ SOFTWARE. */ #include "config.h" -#include "th_lua.h" #include #include +#include "th_lua.h" //! Layer for reading Theme Hospital files out of an .iso disk image /*! @@ -34,115 +34,116 @@ SOFTWARE. searches for the Theme Hospital data files, and can then be used to read these data files. */ -class iso_filesystem -{ -public: - iso_filesystem(); - ~iso_filesystem(); +class iso_filesystem { + public: + iso_filesystem(); + ~iso_filesystem(); - //! Set the character to be used between components in file paths - void set_path_separator(char cSeparator); + //! Set the character to be used between components in file paths + void set_path_separator(char cSeparator); - //! Load an .iso disk image and search for Theme Hospital data files - /*! - \param fRawFile A file handle of an .iso disk image. This handle must - remain valid for as long as the IsoFilesystem instance exists, and - is not automatically closed by the IsoFilesystem instance. - \return true on success, false on failure - call getError() for reason - */ - bool initialise(std::FILE* fRawFile); + //! Load an .iso disk image and search for Theme Hospital data files + /*! + \param fRawFile A file handle of an .iso disk image. This handle must + remain valid for as long as the IsoFilesystem instance exists, and + is not automatically closed by the IsoFilesystem instance. + \return true on success, false on failure - call getError() for reason + */ + bool initialise(std::FILE* fRawFile); - //! Get the reason for the most recent failure - /*! - Can be called after initialise() or getFileData() return false. - */ - const char* get_error() const; + //! Get the reason for the most recent failure + /*! + Can be called after initialise() or getFileData() return false. + */ + const char* get_error() const; - using file_handle = int; + using file_handle = int; - //! Find a file in the loaded .iso disk image - /*! - If (and only if) the given file could not be found, then isHandleGood() - will return false on the returned handle. - */ - file_handle find_file(const char* sPath) const; + //! Find a file in the loaded .iso disk image + /*! + If (and only if) the given file could not be found, then isHandleGood() + will return false on the returned handle. + */ + file_handle find_file(const char* sPath) const; - //! Iterate all files of the .iso disk image within a given directory - /*! - \param sPath The directory to iterate - \param fnCallback The function to be called for each file. The first - parameter to this function is pCallbackData. The second is the name - of a file which is in sPath. - \param pCallbackData Opaque value to be called to fnCallback. - */ - void visit_directory_files(const char* sPath, - void (*fnCallback)(void*, const char*, const char*), + //! Iterate all files of the .iso disk image within a given directory + /*! + \param sPath The directory to iterate + \param fnCallback The function to be called for each file. The first + parameter to this function is pCallbackData. The second is the name + of a file which is in sPath. + \param pCallbackData Opaque value to be called to fnCallback. + */ + void visit_directory_files(const char* sPath, + void (*fnCallback)(void*, const char*, + const char*), void* pCallbackData) const; - //! Test if a file handle from findFile() is good or is invalid - static inline bool isHandleGood(file_handle x) {return x != 0;} + //! Test if a file handle from findFile() is good or is invalid + static inline bool isHandleGood(file_handle x) { return x != 0; } - //! Get the size (in bytes) of a file in the loaded .iso disk image - /*! - \param iFile A file handle returned by findFile() - */ - uint32_t get_file_size(file_handle iFile) const; + //! Get the size (in bytes) of a file in the loaded .iso disk image + /*! + \param iFile A file handle returned by findFile() + */ + uint32_t get_file_size(file_handle iFile) const; - //! Get the contents of a file in the loaded .iso disk image - /*! - \param iFile A file handle returned by findFile() - \param pBuffer The buffer to place the resulting data in - \return true on success, false on failure - call getError() for reason - */ - bool get_file_data(file_handle iFile, uint8_t *pBuffer); + //! Get the contents of a file in the loaded .iso disk image + /*! + \param iFile A file handle returned by findFile() + \param pBuffer The buffer to place the resulting data in + \return true on success, false on failure - call getError() for reason + */ + bool get_file_data(file_handle iFile, uint8_t* pBuffer); -private: - struct file_metadata - { - std::string path; - uint32_t sector; - uint32_t size; - }; + private: + struct file_metadata { + std::string path; + uint32_t sector; + uint32_t size; + }; - std::FILE* raw_file; - char* error; - std::vector files; - long sector_size; - char path_seperator; + std::FILE* raw_file; + char* error; + std::vector files; + long sector_size; + char path_seperator; - //! Free any memory in use - void clear(); + //! Free any memory in use + void clear(); - //! Set the last error, printf-style - void set_error(const char* sFormat, ...); + //! Set the last error, printf-style + void set_error(const char* sFormat, ...); - //! Seek to a logical sector of the disk image - bool seek_to_sector(uint32_t iSector); + //! Seek to a logical sector of the disk image + bool seek_to_sector(uint32_t iSector); - //! Read data from the disk image - bool read_data(uint32_t iByteCount, uint8_t *pBuffer); + //! Read data from the disk image + bool read_data(uint32_t iByteCount, uint8_t* pBuffer); - //! Scan the given array of directory entries for a Theme Hospital file - /*! - \param pDirEnt Pointer to a padded array of ISO 9660 directory entries. - \param iDirEntsSize The number of bytes in the directory entry array. - \param iLevel The recursion level (used to prevent infinite loops upon - maliciously-formed .iso disk images). - \return 0 if no Theme Hospital files were found. 1 if the given array - contains a Theme Hospital data file. 2 if the given array is the - top-level Theme Hospital data directory. Other values otherwise. - */ - int find_hosp_directory(const uint8_t *pDirEnt, int iDirEntsSize, int iLevel); + //! Scan the given array of directory entries for a Theme Hospital file + /*! + \param pDirEnt Pointer to a padded array of ISO 9660 directory entries. + \param iDirEntsSize The number of bytes in the directory entry array. + \param iLevel The recursion level (used to prevent infinite loops upon + maliciously-formed .iso disk images). + \return 0 if no Theme Hospital files were found. 1 if the given array + contains a Theme Hospital data file. 2 if the given array is the + top-level Theme Hospital data directory. Other values otherwise. + */ + int find_hosp_directory(const uint8_t* pDirEnt, int iDirEntsSize, int iLevel); - //! Build the list of Theme Hospital data files - /*! - \param iSector The ordinal of a logical sector containing a padded - arrary of ISO 9660 directory entries. - \param iDirEntsSize The number of bytes in the directory entry array. - \param prefix The path name to prepend to filenames in the directory. - */ - void build_file_lookup_table(uint32_t iSector, int iDirEntsSize, const std::string& prefix); + //! Build the list of Theme Hospital data files + /*! + \param iSector The ordinal of a logical sector containing a padded + arrary of ISO 9660 directory entries. + \param iDirEntsSize The number of bytes in the directory entry array. + \param prefix The path name to prepend to filenames in the directory. + */ + void build_file_lookup_table(uint32_t iSector, int iDirEntsSize, + const std::string& prefix); - //! std:less like implementation for file_metadata. Based on the path. - static bool file_metadata_less(const file_metadata& lhs, const file_metadata& rhs); + //! std:less like implementation for file_metadata. Based on the path. + static bool file_metadata_less(const file_metadata& lhs, + const file_metadata& rhs); }; diff --git a/CorsixTH/Src/lua.hpp b/CorsixTH/Src/lua.hpp index b6cb1fcf..0fb0e731 100644 --- a/CorsixTH/Src/lua.hpp +++ b/CorsixTH/Src/lua.hpp @@ -24,8 +24,8 @@ SOFTWARE. #define CORSIX_TH_LUA_HPP_ extern "C" { -#include #include +#include #include } @@ -35,22 +35,17 @@ extern "C" { // with LUA_COMPAT_ALL defined, but we have no control over this. #ifndef lua_objlen -inline size_t lua_objlen(lua_State *L, int idx) -{ - return lua_rawlen(L, idx); -} +inline size_t lua_objlen(lua_State* L, int idx) { return lua_rawlen(L, idx); } #endif #ifndef lua_equal -inline int lua_equal(lua_State *L, int idx1, int idx2) -{ +inline int lua_equal(lua_State* L, int idx1, int idx2) { return lua_compare(L, idx1, idx2, LUA_OPEQ); } #endif #ifndef lua_lessthan -inline int lua_lessthan(lua_State *L, int idx1, int idx2) -{ +inline int lua_lessthan(lua_State* L, int idx1, int idx2) { return lua_compare(L, idx1, idx2, LUA_OPLT); } #endif @@ -58,20 +53,14 @@ inline int lua_lessthan(lua_State *L, int idx1, int idx2) // Use our own replacements for lua_[sg]etfenv #ifndef lua_setfenv int luaT_setfenv52(lua_State*, int); -inline int lua_setfenv(lua_State *L, int n) -{ - return luaT_setfenv52(L, n); -} +inline int lua_setfenv(lua_State* L, int n) { return luaT_setfenv52(L, n); } #endif #ifndef lua_getfenv void luaT_getfenv52(lua_State*, int); -inline void lua_getfenv(lua_State *L, int n) -{ - luaT_getfenv52(L, n); -} +inline void lua_getfenv(lua_State* L, int n) { luaT_getfenv52(L, n); } #endif -#endif // LUA_VERSION_NUM >= 502 +#endif // LUA_VERSION_NUM >= 502 -#endif // CORSIX_TH_LUA_HPP_ +#endif // CORSIX_TH_LUA_HPP_ diff --git a/CorsixTH/Src/lua_rnc.cpp b/CorsixTH/Src/lua_rnc.cpp index a1e529a7..144f801c 100644 --- a/CorsixTH/Src/lua_rnc.cpp +++ b/CorsixTH/Src/lua_rnc.cpp @@ -1,7 +1,7 @@ #include "lua_rnc.h" +#include #include "../../common/rnc.h" #include "th_lua.h" -#include //! Provides lua function to decompress RNC data /*! @@ -12,70 +12,65 @@ namespace { -int l_decompress(lua_State *L) -{ - size_t inlen; - const uint8_t* in = reinterpret_cast(luaL_checklstring(L, 1, &inlen)); - - // Verify that the data contains an RNC signature, and that the input - // size matches the size specified in the data header. - if(inlen < rnc_header_size || inlen != rnc_input_size(in)) - { - lua_pushnil(L); - lua_pushliteral(L, "Input is not RNC compressed data"); - return 2; - } - uint32_t outlen = rnc_output_size(in); - - // Allocate scratch area as Lua userdata so that if something horrible - // happens, it'll be cleaned up by Lua's GC. Remember that most Lua API - // calls can throw errors, so they all have to be wrapped with code to - // detect errors and free the buffer if said buffer was not managed by Lua. - void* outbuf = lua_newuserdata(L, outlen); +int l_decompress(lua_State* L) { + size_t inlen; + const uint8_t* in = + reinterpret_cast(luaL_checklstring(L, 1, &inlen)); + // Verify that the data contains an RNC signature, and that the input + // size matches the size specified in the data header. + if (inlen < rnc_header_size || inlen != rnc_input_size(in)) { lua_pushnil(L); - switch(rnc_unpack(in, (uint8_t*)outbuf)) - { + lua_pushliteral(L, "Input is not RNC compressed data"); + return 2; + } + uint32_t outlen = rnc_output_size(in); + + // Allocate scratch area as Lua userdata so that if something horrible + // happens, it'll be cleaned up by Lua's GC. Remember that most Lua API + // calls can throw errors, so they all have to be wrapped with code to + // detect errors and free the buffer if said buffer was not managed by Lua. + void* outbuf = lua_newuserdata(L, outlen); + + lua_pushnil(L); + switch (rnc_unpack(in, (uint8_t*)outbuf)) { case rnc_status::ok: - lua_pushlstring(L, (const char*)outbuf, outlen); - return 1; + lua_pushlstring(L, (const char*)outbuf, outlen); + return 1; case rnc_status::file_is_not_rnc: - lua_pushliteral(L, "Input is not RNC compressed data"); - break; + lua_pushliteral(L, "Input is not RNC compressed data"); + break; case rnc_status::huf_decode_error: - lua_pushliteral(L, "Invalid Huffman coding"); - break; + lua_pushliteral(L, "Invalid Huffman coding"); + break; case rnc_status::file_size_mismatch: - lua_pushliteral(L, "Size mismatch"); - break; + lua_pushliteral(L, "Size mismatch"); + break; case rnc_status::packed_crc_error: - lua_pushliteral(L, "Incorrect packed CRC"); - break; + lua_pushliteral(L, "Incorrect packed CRC"); + break; case rnc_status::unpacked_crc_error: - lua_pushliteral(L, "Incorrect unpacked CRC"); - break; + lua_pushliteral(L, "Incorrect unpacked CRC"); + break; default: - lua_pushliteral(L, "Unknown error decompressing RNC data"); - break; - } - return 2; + lua_pushliteral(L, "Unknown error decompressing RNC data"); + break; + } + return 2; } -constexpr std::array rnclib {{ - {"decompress", l_decompress}, - {nullptr, nullptr} -}}; +constexpr std::array rnclib{ + {{"decompress", l_decompress}, {nullptr, nullptr}}}; -} // namespace +} // namespace -int luaopen_rnc(lua_State *L) -{ - luaT_register(L, "rnc", rnclib); - return 1; +int luaopen_rnc(lua_State* L) { + luaT_register(L, "rnc", rnclib); + return 1; } diff --git a/CorsixTH/Src/lua_rnc.h b/CorsixTH/Src/lua_rnc.h index cb126dd5..d30c9a2c 100644 --- a/CorsixTH/Src/lua_rnc.h +++ b/CorsixTH/Src/lua_rnc.h @@ -3,6 +3,6 @@ #include "th_lua.h" -int luaopen_rnc(lua_State *L); +int luaopen_rnc(lua_State* L); #endif diff --git a/CorsixTH/Src/lua_sdl.h b/CorsixTH/Src/lua_sdl.h index 5157b01f..870c9492 100644 --- a/CorsixTH/Src/lua_sdl.h +++ b/CorsixTH/Src/lua_sdl.h @@ -23,8 +23,8 @@ SOFTWARE. #ifndef CORSIX_TH_LUA_SDL_H_ #define CORSIX_TH_LUA_SDL_H_ -#include "lua.hpp" #include +#include "lua.hpp" // SDL event codes used for delivering custom events to l_mainloop in // sdl_core.cpp @@ -39,8 +39,8 @@ SOFTWARE. // SDL_USEREVENT_SOUND_OVER - informs script of a played sound finishing. #define SDL_USEREVENT_SOUND_OVER (SDL_USEREVENT + 4) -int luaopen_sdl(lua_State *L); +int luaopen_sdl(lua_State* L); -int l_load_music_async_callback(lua_State *L); +int l_load_music_async_callback(lua_State* L); -#endif // CORSIX_TH_LUA_SDL_H_ +#endif // CORSIX_TH_LUA_SDL_H_ diff --git a/CorsixTH/Src/main.cpp b/CorsixTH/Src/main.cpp index 174faed8..1845b6d5 100644 --- a/CorsixTH/Src/main.cpp +++ b/CorsixTH/Src/main.cpp @@ -21,18 +21,15 @@ SOFTWARE. */ #include "config.h" +#include +#include +#include +#include "iso_fs.h" #include "lua.hpp" -extern "C" { -int luaopen_random(lua_State *L); -} -#include "th_lua.h" #include "lua_rnc.h" #include "lua_sdl.h" #include "persist_lua.h" -#include "iso_fs.h" -#include -#include -#include +#include "th_lua.h" // Config file checking #ifndef CORSIX_TH_USE_PACK_PRAGMAS @@ -40,119 +37,120 @@ int luaopen_random(lua_State *L); #endif // End of config file checking +extern "C" { +int luaopen_random(lua_State* L); +} + namespace { -inline void preload_lua_package(lua_State *L, const char* name, lua_CFunction fn) -{ - luaT_execute(L, std::string("package.preload.").append(name).append(" = ...").c_str(), fn); +inline void preload_lua_package(lua_State* L, const char* name, + lua_CFunction fn) { + luaT_execute( + L, std::string("package.preload.").append(name).append(" = ...").c_str(), + fn); } -} // namespace +} // namespace -int lua_main_no_eval(lua_State *L) -{ - // assert(_VERSION == LUA_VERSION) - size_t iLength; - lua_getglobal(L, "_VERSION"); - const char* sVersion = lua_tolstring(L, -1, &iLength); - if(iLength != std::strlen(LUA_VERSION) || std::strcmp(sVersion, LUA_VERSION) != 0) - { - lua_pushliteral(L, "Linked against a version of Lua different to the " - "one used when compiling.\nPlease recompile CorsixTH against the " - "same Lua version it is linked against."); - return lua_error(L); - } - lua_pop(L, 1); +int lua_main_no_eval(lua_State* L) { + // assert(_VERSION == LUA_VERSION) + size_t iLength; + lua_getglobal(L, "_VERSION"); + const char* sVersion = lua_tolstring(L, -1, &iLength); + if (iLength != std::strlen(LUA_VERSION) || + std::strcmp(sVersion, LUA_VERSION) != 0) { + lua_pushliteral( + L, + "Linked against a version of Lua different to the one used " + "when compiling.\nPlease recompile CorsixTH against the same " + "Lua version it is linked against."); + return lua_error(L); + } + lua_pop(L, 1); - // math.random* = Mersenne twister variant - luaT_cpcall(L, luaopen_random, nullptr); + // math.random* = Mersenne twister variant + luaT_cpcall(L, luaopen_random, nullptr); - // Fill in package.preload table so that calls to require("X") from Lua - // will call the appropriate luaopen_X function in C. - preload_lua_package(L, "rnc", luaopen_rnc); - preload_lua_package(L, "TH", luaopen_th); - preload_lua_package(L, "persist", luaopen_persist); - preload_lua_package(L, "sdl", luaopen_sdl); + // Fill in package.preload table so that calls to require("X") from Lua + // will call the appropriate luaopen_X function in C. + preload_lua_package(L, "rnc", luaopen_rnc); + preload_lua_package(L, "TH", luaopen_th); + preload_lua_package(L, "persist", luaopen_persist); + preload_lua_package(L, "sdl", luaopen_sdl); - // require "debug" (Harmless in Lua 5.1, useful in 5.2 for compatbility) - luaT_execute(L, "require \"debug\""); + // require "debug" (Harmless in Lua 5.1, useful in 5.2 for compatbility) + luaT_execute(L, "require \"debug\""); - // Check for --interpreter and run that instead of CorsixTH.lua - bool bGotScriptFile = false; - int iNArgs = lua_gettop(L); - for(int i = 1; i <= iNArgs; ++i) - { - if(lua_type(L, i) == LUA_TSTRING) - { - size_t iLen; - const char* sCmd = lua_tolstring(L, i, &iLen); - if(iLen > 14 && std::memcmp(sCmd, "--interpreter=", 14) == 0) - { - lua_getglobal(L, "assert"); - lua_getglobal(L, "loadfile"); - lua_pushlstring(L, sCmd + 14, iLen - 14); - bGotScriptFile = true; - break; - } - } - } - - if(!bGotScriptFile) - { + // Check for --interpreter and run that instead of CorsixTH.lua + bool bGotScriptFile = false; + int iNArgs = lua_gettop(L); + for (int i = 1; i <= iNArgs; ++i) { + if (lua_type(L, i) == LUA_TSTRING) { + size_t iLen; + const char* sCmd = lua_tolstring(L, i, &iLen); + if (iLen > 14 && std::memcmp(sCmd, "--interpreter=", 14) == 0) { lua_getglobal(L, "assert"); lua_getglobal(L, "loadfile"); - lua_pushstring(L, CORSIX_TH_INTERPRETER_PATH); + lua_pushlstring(L, sCmd + 14, iLen - 14); bGotScriptFile = true; + break; + } } + } - lua_call(L, 1, 2); - lua_call(L, 2, 1); - lua_insert(L, 1); - return lua_gettop(L); + if (!bGotScriptFile) { + lua_getglobal(L, "assert"); + lua_getglobal(L, "loadfile"); + lua_pushstring(L, CORSIX_TH_INTERPRETER_PATH); + bGotScriptFile = true; + } + + lua_call(L, 1, 2); + lua_call(L, 2, 1); + lua_insert(L, 1); + return lua_gettop(L); } -int lua_main(lua_State *L) -{ - lua_call(L, lua_main_no_eval(L) - 1, LUA_MULTRET); - return lua_gettop(L); +int lua_main(lua_State* L) { + lua_call(L, lua_main_no_eval(L) - 1, LUA_MULTRET); + return lua_gettop(L); } -int lua_stacktrace(lua_State *L) -{ - // err = tostring(err) - lua_settop(L, 1); - lua_getglobal(L, "tostring"); - lua_insert(L, 1); - lua_call(L, 1, 1); +int lua_stacktrace(lua_State* L) { + // err = tostring(err) + lua_settop(L, 1); + lua_getglobal(L, "tostring"); + lua_insert(L, 1); + lua_call(L, 1, 1); - // err = .. err - lua_pushliteral(L, "An error has occurred in CorsixTH:\n"); - lua_insert(L, 1); - lua_concat(L, 2); + // err = .. err + lua_pushliteral(L, "An error has occurred in CorsixTH:\n"); + lua_insert(L, 1); + lua_concat(L, 2); - // return debug.traceback(err, 2) - lua_getglobal(L, "debug"); - lua_getfield(L, -1, "traceback"); - lua_pushvalue(L, 1); - lua_pushinteger(L, 2); - lua_call(L, 2, 1); + // return debug.traceback(err, 2) + lua_getglobal(L, "debug"); + lua_getfield(L, -1, "traceback"); + lua_pushvalue(L, 1); + lua_pushinteger(L, 2); + lua_call(L, 2, 1); - return 1; + return 1; } -int lua_panic(lua_State *L) -{ - std::fprintf(stderr, "A Lua error has occurred in CorsixTH outside of protected " - "mode!!\n"); - std::fflush(stderr); +int lua_panic(lua_State* L) { + std::fprintf(stderr, + "A Lua error has occurred in CorsixTH outside of protected " + "mode!\n"); + std::fflush(stderr); - if(lua_type(L, -1) == LUA_TSTRING) - std::fprintf(stderr, "%s\n", lua_tostring(L, -1)); - else - std::fprintf(stderr, "%p\n", lua_topointer(L, -1)); - std::fflush(stderr); + if (lua_type(L, -1) == LUA_TSTRING) + std::fprintf(stderr, "%s\n", lua_tostring(L, -1)); + else + std::fprintf(stderr, "%p\n", lua_topointer(L, -1)); + std::fflush(stderr); - // A stack trace would be nice, but they cannot be done in a panic. + // A stack trace would be nice, but they cannot be done in a panic. - return 0; + return 0; } diff --git a/CorsixTH/Src/main.h b/CorsixTH/Src/main.h index c67e7164..79b53611 100644 --- a/CorsixTH/Src/main.h +++ b/CorsixTH/Src/main.h @@ -33,14 +33,14 @@ SOFTWARE. transfers control to CorsixTH.lua as soon as possible (so that as little as possible behaviour is hardcoded into C rather than Lua). */ -int lua_main(lua_State *L); +int lua_main(lua_State* L); //! Alternative lua mode entry point /*! Behaves like CorsixTH_lua_main, except that it doesn't transfer control over to Lua scripts - it just prepares everything for them and loads them. */ -int lua_main_no_eval(lua_State *L); +int lua_main_no_eval(lua_State* L); //! Process a caught error before returning it to the caller /*! @@ -49,7 +49,7 @@ int lua_main_no_eval(lua_State *L); processing the error, the caller receives LUA_ERRERR rather than panicking while processing it itself. */ -int lua_stacktrace(lua_State *L); +int lua_stacktrace(lua_State* L); //! Process an uncaught Lua error before aborting /*! @@ -57,6 +57,6 @@ int lua_stacktrace(lua_State *L); which can be done when they do, but at least the user should be informed, and the error message printed. */ -int lua_panic(lua_State *L); +int lua_panic(lua_State* L); -#endif // CORSIX_TH_MAIN_H_ +#endif // CORSIX_TH_MAIN_H_ diff --git a/CorsixTH/Src/persist_lua.cpp b/CorsixTH/Src/persist_lua.cpp index 56192b62..1e638e31 100644 --- a/CorsixTH/Src/persist_lua.cpp +++ b/CorsixTH/Src/persist_lua.cpp @@ -21,59 +21,59 @@ SOFTWARE. */ #include "persist_lua.h" -#include #include +#include #include #include -#include #include +#include #include -#include +#include #ifdef _MSC_VER -#pragma warning(disable: 4996) // Disable "std::strcpy unsafe" warnings under MSVC +#pragma warning( \ + disable : 4996) // Disable "std::strcpy unsafe" warnings under MSVC #endif namespace { -enum persist_type -{ - // LUA_TNIL = 0, - // LUA_TBOOLEAN, // Used for false - // LUA_TLIGHTUSERDATA, // Not currently persisted - // LUA_TNUMBER, - // LUA_TSTRING, - // LUA_TTABLE, // Used for tables without metatables - // LUA_TFUNCTION, // Used for Lua closures - // LUA_TUSERDATA, - // LUA_TTHREAD, // Not currently persisted - PERSIST_TPERMANENT = LUA_TTHREAD + 1, - PERSIST_TTRUE, - PERSIST_TTABLE_WITH_META, - PERSIST_TINTEGER, - PERSIST_TPROTOTYPE, - PERSIST_TRESERVED1, // Not currently used - PERSIST_TRESERVED2, // Not currently used - PERSIST_TCOUNT, // must equal 16 (for compatibility) +enum persist_type { + // LUA_TNIL = 0, + // LUA_TBOOLEAN, // Used for false + // LUA_TLIGHTUSERDATA, // Not currently persisted + // LUA_TNUMBER, + // LUA_TSTRING, + // LUA_TTABLE, // Used for tables without metatables + // LUA_TFUNCTION, // Used for Lua closures + // LUA_TUSERDATA, + // LUA_TTHREAD, // Not currently persisted + PERSIST_TPERMANENT = LUA_TTHREAD + 1, + PERSIST_TTRUE, + PERSIST_TTABLE_WITH_META, + PERSIST_TINTEGER, + PERSIST_TPROTOTYPE, + PERSIST_TRESERVED1, // Not currently used + PERSIST_TRESERVED2, // Not currently used + PERSIST_TCOUNT, // must equal 16 (for compatibility) }; -int l_writer_mt_index(lua_State *L); +int l_writer_mt_index(lua_State* L); template -int l_crude_gc(lua_State *L) -{ - // This __gc metamethod does not verify that the given value is the correct - // type of userdata, or that the value is userdata at all. - reinterpret_cast(lua_touserdata(L, 1))->~T(); - return 0; +int l_crude_gc(lua_State* L) { + // This __gc metamethod does not verify that the given value is the correct + // type of userdata, or that the value is userdata at all. + reinterpret_cast(lua_touserdata(L, 1))->~T(); + return 0; } -} // namespace +} // namespace -//! Structure for loading multiple strings as a Lua chunk, avoiding concatenation +//! Structure for loading multiple strings as a Lua chunk, avoiding +//! concatenation /*! - luaL_loadbuffer() is a good way to load a string as a Lua chunk. If there are - several strings which need to be concatenated before being loaded, then it - can be more efficient to use this structure, which can load them without + luaL_loadbuffer() is a good way to load a string as a Lua chunk. If there + are several strings which need to be concatenated before being loaded, then + it can be more efficient to use this structure, which can load them without concatenating them. Sample usage is: ``` @@ -83,36 +83,31 @@ int l_crude_gc(lua_State *L) lua_load(L, LoadMultiBuffer_t::load_fn, &ls, "chunk name"); ``` */ -class load_multi_buffer -{ -public: - const char *s[3]; - size_t i[3]; - int n; +class load_multi_buffer { + public: + const char* s[3]; + size_t i[3]; + int n; - load_multi_buffer() - { - s[0] = s[1] = s[2] = nullptr; - i[0] = i[1] = i[2] = 0; - n = 0; + load_multi_buffer() { + s[0] = s[1] = s[2] = nullptr; + i[0] = i[1] = i[2] = 0; + n = 0; + } + + static const char* load_fn(lua_State* L, void* ud, size_t* size) { + load_multi_buffer* pThis = reinterpret_cast(ud); + + for (; pThis->n < 3; ++pThis->n) { + if (pThis->i[pThis->n] != 0) { + *size = pThis->i[pThis->n]; + return pThis->s[pThis->n++]; + } } - static const char* load_fn(lua_State *L, void *ud, size_t *size) - { - load_multi_buffer *pThis = reinterpret_cast(ud); - - for( ; pThis->n < 3; ++pThis->n) - { - if(pThis->i[pThis->n] != 0) - { - *size = pThis->i[pThis->n]; - return pThis->s[pThis->n++]; - } - } - - *size = 0; - return nullptr; - } + *size = 0; + return nullptr; + } }; //! Basic implementation of persistance interface @@ -132,579 +127,528 @@ public: upvalue 1 - permanents table upvalue 2 - self */ -class lua_persist_basic_writer : public lua_persist_writer -{ -public: - lua_persist_basic_writer(lua_State *L) - : L(L), - data() - { } +class lua_persist_basic_writer : public lua_persist_writer { + public: + lua_persist_basic_writer(lua_State* L) : L(L), data() {} - ~lua_persist_basic_writer() - { } + ~lua_persist_basic_writer() {} - lua_State* get_stack() override - { - return L; + lua_State* get_stack() override { return L; } + + void init() { + lua_createtable(L, 1, 8); // Environment + lua_pushvalue(L, 2); // Permanent objects + lua_rawseti(L, -2, 1); + lua_createtable(L, 1, 0); // Environment metatable + lua_pushvalue(L, 2); // Permanent objects + lua_pushvalue(L, 1); // self + luaT_pushcclosure(L, l_writer_mt_index, 2); + lua_setfield(L, -2, "__index"); + lua_setmetatable(L, -2); + lua_setfenv(L, 1); + lua_createtable(L, 1, 4); // Metatable + luaT_pushcclosure(L, l_crude_gc, 0); + lua_setfield(L, -2, "__gc"); + lua_pushvalue(L, luaT_upvalueindex(1)); // Prototype persistance names + lua_rawseti(L, -2, 1); + lua_setmetatable(L, 1); + + next_index = 1; + data_size = 0; + had_error = false; + } + + int finish() { + if (get_error() != nullptr) { + lua_pushnil(L); + lua_pushstring(L, get_error()); + lua_getmetatable(L, 1); + lua_getfield(L, -1, "err"); + lua_replace(L, -2); + return 3; + } else { + lua_pushlstring(L, data.c_str(), data.length()); + return 1; + } + } + + void fast_write_stack_object(int iIndex) override { + if (lua_type(L, iIndex) != LUA_TUSERDATA) { + write_stack_object(iIndex); + return; } - void init() - { - lua_createtable(L, 1, 8); // Environment - lua_pushvalue(L, 2); // Permanent objects - lua_rawseti(L, -2, 1); - lua_createtable(L, 1, 0); // Environment metatable - lua_pushvalue(L, 2); // Permanent objects - lua_pushvalue(L, 1); // self - luaT_pushcclosure(L, l_writer_mt_index, 2); - lua_setfield(L, -2, "__index"); - lua_setmetatable(L, -2); - lua_setfenv(L, 1); - lua_createtable(L, 1, 4); // Metatable - luaT_pushcclosure(L, l_crude_gc, 0); - lua_setfield(L, -2, "__gc"); - lua_pushvalue(L, luaT_upvalueindex(1)); // Prototype persistance names - lua_rawseti(L, -2, 1); - lua_setmetatable(L, 1); + // Convert index from relative to absolute + if (iIndex < 0 && iIndex > LUA_REGISTRYINDEX) + iIndex = lua_gettop(L) + 1 + iIndex; - next_index = 1; - data_size = 0; - had_error = false; + // Check for no cycle + lua_getfenv(L, 1); + lua_pushvalue(L, iIndex); + lua_rawget(L, -2); + lua_rawgeti(L, -2, 1); + lua_pushvalue(L, iIndex); + lua_gettable(L, -2); + lua_replace(L, -2); + if (!lua_isnil(L, -1) || !lua_isnil(L, -2)) { + lua_pop(L, 3); + write_stack_object(iIndex); + return; } + lua_pop(L, 2); - int finish() - { - if(get_error() != nullptr) - { - lua_pushnil(L); - lua_pushstring(L, get_error()); - lua_getmetatable(L, 1); - lua_getfield(L, -1, "err"); - lua_replace(L, -2); - return 3; - } - else - { - lua_pushlstring(L, data.c_str(), data.length()); - return 1; - } - } + // Save the index to the cache + lua_pushvalue(L, iIndex); + lua_pushnumber(L, (lua_Number)(next_index++)); + lua_settable(L, -3); - void fast_write_stack_object(int iIndex) override - { - if(lua_type(L, iIndex) != LUA_TUSERDATA) - { - write_stack_object(iIndex); - return; - } + if (!check_that_userdata_can_be_depersisted(iIndex)) return; - // Convert index from relative to absolute - if(iIndex < 0 && iIndex > LUA_REGISTRYINDEX) - iIndex = lua_gettop(L) + 1 + iIndex; + // Write type, metatable, and then environment + uint8_t iType = LUA_TUSERDATA; + write_byte_stream(&iType, 1); + write_stack_object(-1); + lua_getfenv(L, iIndex); + write_stack_object(-1); + lua_pop(L, 1); - // Check for no cycle - lua_getfenv(L, 1); + // Write the raw data + if (lua_type(L, -1) == LUA_TTABLE) { + lua_getfield(L, -1, "__persist"); + if (lua_isnil(L, -1)) + lua_pop(L, 1); + else { lua_pushvalue(L, iIndex); - lua_rawget(L, -2); - lua_rawgeti(L, -2, 1); - lua_pushvalue(L, iIndex); - lua_gettable(L, -2); - lua_replace(L, -2); - if(!lua_isnil(L, -1) || !lua_isnil(L, -2)) - { - lua_pop(L, 3); - write_stack_object(iIndex); - return; - } + lua_checkstack(L, 20); + lua_CFunction fn = lua_tocfunction(L, -2); + fn(L); lua_pop(L, 2); + } + } + write_uint((uint64_t)0x42); // sync marker + lua_pop(L, 1); + } - // Save the index to the cache - lua_pushvalue(L, iIndex); - lua_pushnumber(L, (lua_Number)(next_index++)); - lua_settable(L, -3); + void write_stack_object(int iIndex) override { + // Convert index from relative to absolute + if (iIndex < 0 && iIndex > LUA_REGISTRYINDEX) + iIndex = lua_gettop(L) + 1 + iIndex; - if(!check_that_userdata_can_be_depersisted(iIndex)) - return; + // Basic types always have their value written + int iType = lua_type(L, iIndex); + if (iType == LUA_TNIL || iType == LUA_TNONE) { + uint8_t iByte = LUA_TNIL; + write_byte_stream(&iByte, 1); + } else if (iType == LUA_TBOOLEAN) { + uint8_t iByte; + if (lua_toboolean(L, iIndex)) + iByte = PERSIST_TTRUE; + else + iByte = LUA_TBOOLEAN; + write_byte_stream(&iByte, 1); + } else if (iType == LUA_TNUMBER) { + double fValue = lua_tonumber(L, iIndex); + if (floor(fValue) == fValue && 0.0 <= fValue && fValue <= 16383.0) { + // Small integers are written as just a few bytes + // NB: 16383 = 2^14-1, which is the maximum value which + // can fit into two bytes of VUInt. + uint8_t iByte = PERSIST_TINTEGER; + write_byte_stream(&iByte, 1); + uint16_t iValue = (uint16_t)fValue; + write_uint(iValue); + } else { + // Other numbers are written as an 8 byte double + uint8_t iByte = LUA_TNUMBER; + write_byte_stream(&iByte, 1); + write_byte_stream(reinterpret_cast(&fValue), sizeof(double)); + } + } else { + // Complex values are cached, and are only written once (if this + // weren't done, then cycles in the object graph would break + // things). + lua_getfenv(L, 1); + lua_pushvalue(L, iIndex); + lua_gettable(L, -2); // Might (indirectly) call writeObjectRaw + uint64_t iValue = (uint64_t)lua_tonumber(L, -1); + lua_pop(L, 2); + if (iValue != 0) { + // If the value has not previously been written, then + // writeObjectRaw would have been called, and the appropriate + // data written, and 0 would be returned. Otherwise, the index + // would be returned, which we offset by the number of types, + // and then write. + write_uint(iValue + PERSIST_TCOUNT - 1); + } + } + } - // Write type, metatable, and then environment - uint8_t iType = LUA_TUSERDATA; - write_byte_stream(&iType, 1); - write_stack_object(-1); - lua_getfenv(L, iIndex); - write_stack_object(-1); - lua_pop(L, 1); + int write_object_raw() { + uint8_t iType; - // Write the raw data - if(lua_type(L, -1) == LUA_TTABLE) - { - lua_getfield(L, -1, "__persist"); - if(lua_isnil(L, -1)) - lua_pop(L, 1); - else - { - lua_pushvalue(L, iIndex); - lua_checkstack(L, 20); - lua_CFunction fn = lua_tocfunction(L, -2); - fn(L); + // Save the index to the cache + lua_pushvalue(L, 2); + lua_pushnumber(L, (lua_Number)(next_index++)); + lua_settable(L, 1); + + // Lookup the object in the permanents table + lua_pushvalue(L, 2); + lua_gettable(L, luaT_upvalueindex(1)); + if (lua_type(L, -1) != LUA_TNIL) { + // Object is in the permanents table. + + uint8_t iType = PERSIST_TPERMANENT; + write_byte_stream(&iType, 1); + + // Replace self's environment with self (for call to + // writeStackObject) + lua_pushvalue(L, luaT_upvalueindex(2)); + lua_replace(L, 1); + + // Write the key corresponding to the permanent object + write_stack_object(-1); + } else { + // Object is not in the permanents table. + lua_pop(L, 1); + + switch (lua_type(L, 2)) { + // LUA_TNIL handled in writeStackObject + // LUA_TBOOLEAN handled in writeStackObject + // LUA_TNUMBER handled in writeStackObject + + case LUA_TSTRING: { + iType = LUA_TSTRING; + write_byte_stream(&iType, 1); + // Strings are simple: length and then bytes (not null + // terminated) + size_t iLength; + const char* sString = lua_tolstring(L, 2, &iLength); + write_uint(iLength); + write_byte_stream(reinterpret_cast(sString), iLength); + break; + } + + case LUA_TTABLE: { + // Replace self's environment with self (for calls to + // writeStackObject) + lua_pushvalue(L, luaT_upvalueindex(2)); + lua_replace(L, 1); + + // Save env and insert prior to table + lua_getfenv(L, 1); + lua_insert(L, 2); + + int iTable = 3; + table_reentry: + + // Handle the metatable + if (lua_getmetatable(L, iTable)) { + iType = PERSIST_TTABLE_WITH_META; + write_byte_stream(&iType, 1); + write_stack_object(-1); + lua_pop(L, 1); + } else { + iType = LUA_TTABLE; + write_byte_stream(&iType, 1); + } + + // Write the children as key, value pairs + lua_pushnil(L); + while (lua_next(L, iTable)) { + write_stack_object(-2); + // The naive thing to do now would be + // writeStackObject(-1) but this can easily lead to + // Lua's C call stack limit being hit. To reduce the + // likelihood of this happening, we check to see if + // about to write another table. + if (lua_type(L, -1) == LUA_TTABLE) { + lua_pushvalue(L, -1); + lua_rawget(L, 2); + lua_pushvalue(L, -2); + lua_gettable(L, luaT_upvalueindex(1)); + if (lua_isnil(L, -1) && lua_isnil(L, -2)) { lua_pop(L, 2); - } + lua_checkstack(L, 10); + iTable += 2; + lua_pushvalue(L, iTable); + lua_pushnumber(L, (lua_Number)(next_index++)); + lua_settable(L, 2); + goto table_reentry; + table_resume: + iTable -= 2; + } else { + lua_pop(L, 2); + write_stack_object(-1); + } + } else + write_stack_object(-1); + lua_pop(L, 1); + } + + // Write a nil to mark the end of the children (as nil is + // the only value which cannot be used as a key in a table). + iType = LUA_TNIL; + write_byte_stream(&iType, 1); + if (iTable != 3) goto table_resume; + break; } - write_uint((uint64_t)0x42); // sync marker - lua_pop(L, 1); - } - void write_stack_object(int iIndex) override - { - // Convert index from relative to absolute - if(iIndex < 0 && iIndex > LUA_REGISTRYINDEX) - iIndex = lua_gettop(L) + 1 + iIndex; - - // Basic types always have their value written - int iType = lua_type(L, iIndex); - if(iType == LUA_TNIL || iType == LUA_TNONE) - { - uint8_t iByte = LUA_TNIL; - write_byte_stream(&iByte, 1); - } - else if(iType == LUA_TBOOLEAN) - { - uint8_t iByte; - if(lua_toboolean(L, iIndex)) - iByte = PERSIST_TTRUE; - else - iByte = LUA_TBOOLEAN; - write_byte_stream(&iByte, 1); - } - else if(iType == LUA_TNUMBER) - { - double fValue = lua_tonumber(L, iIndex); - if(floor(fValue) == fValue && 0.0 <= fValue && fValue <= 16383.0) - { - // Small integers are written as just a few bytes - // NB: 16383 = 2^14-1, which is the maximum value which - // can fit into two bytes of VUInt. - uint8_t iByte = PERSIST_TINTEGER; - write_byte_stream(&iByte, 1); - uint16_t iValue = (uint16_t)fValue; - write_uint(iValue); - } - else - { - // Other numbers are written as an 8 byte double - uint8_t iByte = LUA_TNUMBER; - write_byte_stream(&iByte, 1); - write_byte_stream(reinterpret_cast(&fValue), sizeof(double)); - } - } - else - { - // Complex values are cached, and are only written once (if this weren't - // done, then cycles in the object graph would break things). - lua_getfenv(L, 1); - lua_pushvalue(L, iIndex); - lua_gettable(L, -2); // Might (indirectly) call writeObjectRaw - uint64_t iValue = (uint64_t)lua_tonumber(L, -1); - lua_pop(L, 2); - if(iValue != 0) - { - // If the value has not previously been written, then writeObjectRaw - // would have been called, and the appropriate data written, and 0 - // would be returned. Otherwise, the index would be returned, which - // we offset by the number of types, and then write. - write_uint(iValue + PERSIST_TCOUNT - 1); - } - } - } - - int write_object_raw() - { - uint8_t iType; - - // Save the index to the cache - lua_pushvalue(L, 2); - lua_pushnumber(L, (lua_Number)(next_index++)); - lua_settable(L, 1); - - // Lookup the object in the permanents table - lua_pushvalue(L, 2); - lua_gettable(L, luaT_upvalueindex(1)); - if(lua_type(L, -1) != LUA_TNIL) - { - // Object is in the permanents table. - - uint8_t iType = PERSIST_TPERMANENT; + case LUA_TFUNCTION: + if (lua_iscfunction(L, 2)) { + set_error_object(2); + set_error("Cannot persist C functions"); + } else { + iType = LUA_TFUNCTION; write_byte_stream(&iType, 1); - // Replace self's environment with self (for call to writeStackObject) + // Replace self's environment with self (for calls to + // writeStackObject) lua_pushvalue(L, luaT_upvalueindex(2)); lua_replace(L, 1); - // Write the key corresponding to the permanent object - write_stack_object(-1); - } - else - { - // Object is not in the permanents table. - lua_pop(L, 1); + // Write the prototype (the part of a function which is + // common across multiple closures - see LClosure / + // Proto in Lua's lobject.h). + lua_Debug proto_info; + lua_pushvalue(L, 2); + lua_getinfo(L, ">Su", &proto_info); + write_prototype(&proto_info, 2); - switch(lua_type(L, 2)) - { - // LUA_TNIL handled in writeStackObject - // LUA_TBOOLEAN handled in writeStackObject - // LUA_TNUMBER handled in writeStackObject - - case LUA_TSTRING: { - iType = LUA_TSTRING; - write_byte_stream(&iType, 1); - // Strings are simple: length and then bytes (not null terminated) - size_t iLength; - const char *sString = lua_tolstring(L, 2, &iLength); - write_uint(iLength); - write_byte_stream(reinterpret_cast(sString), iLength); - break; } - - case LUA_TTABLE: { - // Replace self's environment with self (for calls to writeStackObject) - lua_pushvalue(L, luaT_upvalueindex(2)); - lua_replace(L, 1); - - // Save env and insert prior to table - lua_getfenv(L, 1); - lua_insert(L, 2); - - int iTable = 3; table_reentry: - - // Handle the metatable - if(lua_getmetatable(L, iTable)) - { - iType = PERSIST_TTABLE_WITH_META; - write_byte_stream(&iType, 1); - write_stack_object(-1); - lua_pop(L, 1); - } - else - { - iType = LUA_TTABLE; - write_byte_stream(&iType, 1); - } - - // Write the children as key, value pairs - lua_pushnil(L); - while(lua_next(L, iTable)) - { - write_stack_object(-2); - // The naive thing to do now would be writeStackObject(-1) - // but this can easily lead to Lua's C call stack limit - // being hit. To reduce the likelihood of this happening, - // we check to see if about to write another table. - if(lua_type(L, -1) == LUA_TTABLE) - { - lua_pushvalue(L, -1); - lua_rawget(L, 2); - lua_pushvalue(L, -2); - lua_gettable(L, luaT_upvalueindex(1)); - if(lua_isnil(L, -1) && lua_isnil(L, -2)) - { - lua_pop(L, 2); - lua_checkstack(L, 10); - iTable += 2; - lua_pushvalue(L, iTable); - lua_pushnumber(L, (lua_Number)(next_index++)); - lua_settable(L, 2); - goto table_reentry; table_resume: - iTable -= 2; - } - else - { - lua_pop(L, 2); - write_stack_object(-1); - } - } - else - write_stack_object(-1); - lua_pop(L, 1); - } - - // Write a nil to mark the end of the children (as nil is the - // only value which cannot be used as a key in a table). - iType = LUA_TNIL; - write_byte_stream(&iType, 1); - if(iTable != 3) - goto table_resume; - break; } - - case LUA_TFUNCTION: - if(lua_iscfunction(L, 2)) - { - set_error_object(2); - set_error("Cannot persist C functions"); - } - else - { - iType = LUA_TFUNCTION; - write_byte_stream(&iType, 1); - - // Replace self's environment with self (for calls to writeStackObject) - lua_pushvalue(L, luaT_upvalueindex(2)); - lua_replace(L, 1); - - // Write the prototype (the part of a function which is common across - // multiple closures - see LClosure / Proto in Lua's lobject.h). - lua_Debug proto_info; - lua_pushvalue(L, 2); - lua_getinfo(L, ">Su", &proto_info); - write_prototype(&proto_info, 2); - - // Write the values of the upvalues - // If available, also write the upvalue IDs (so that in - // the future, we could hypothetically rejoin shared - // upvalues). An ID is just an opaque sequence of bytes. - write_uint(proto_info.nups); + // Write the values of the upvalues + // If available, also write the upvalue IDs (so that in + // the future, we could hypothetically rejoin shared + // upvalues). An ID is just an opaque sequence of bytes. + write_uint(proto_info.nups); #if LUA_VERSION_NUM >= 502 - write_uint(sizeof(void*)); + write_uint(sizeof(void*)); #else - write_uint(0); + write_uint(0); #endif - for(int i = 1; i <= proto_info.nups; ++i) - { - lua_getupvalue(L, 2, i); - write_stack_object(-1); + for (int i = 1; i <= proto_info.nups; ++i) { + lua_getupvalue(L, 2, i); + write_stack_object(-1); #if LUA_VERSION_NUM >= 502 - void *pUpvalueID = lua_upvalueid(L, 2, i); - write_byte_stream((uint8_t*)&pUpvalueID, sizeof(void*)); + void* pUpvalueID = lua_upvalueid(L, 2, i); + write_byte_stream((uint8_t*)&pUpvalueID, sizeof(void*)); #endif - } - - // Write the environment table - lua_getfenv(L, 2); - write_stack_object(-1); - lua_pop(L, 1); - } - break; - - case LUA_TUSERDATA: - if(!check_that_userdata_can_be_depersisted(2)) - break; - - // Replace self's environment with self (for calls to writeStackObject) - lua_pushvalue(L, luaT_upvalueindex(2)); - lua_replace(L, 1); - - // Write type, metatable, and then environment - iType = LUA_TUSERDATA; - write_byte_stream(&iType, 1); - write_stack_object(-1); - lua_getfenv(L, 2); - write_stack_object(-1); - lua_pop(L, 1); - - // Write the raw data - if(lua_type(L, -1) == LUA_TTABLE) - { - lua_getfield(L, -1, "__persist"); - if(lua_isnil(L, -1)) - lua_pop(L, 1); - else - { - lua_pushvalue(L, 2); - lua_pushvalue(L, luaT_upvalueindex(2)); - lua_call(L, 2, 0); - } - } - write_uint((uint64_t)0x42); // sync marker - break; - - default: - set_error(lua_pushfstring(L, "Cannot persist %s values", luaL_typename(L, 2))); - break; } - } - lua_pushnumber(L, 0); - return 1; - } - bool check_that_userdata_can_be_depersisted(int iIndex) - { - if(lua_getmetatable(L, iIndex)) - { - lua_getfield(L, -1, "__depersist_size"); - if(lua_isnumber(L, -1)) - { - if(static_cast(lua_tointeger(L, -1)) - != static_cast(lua_objlen(L, iIndex))) - { - set_error(lua_pushfstring(L, "__depersist_size is " - "incorrect (%d vs. %d)", (int)lua_objlen(L, iIndex), - (int)lua_tointeger(L, -1))); - return false; - } - if(lua_objlen(L, iIndex) != 0) - { - lua_getfield(L, -2, "__persist"); - lua_getfield(L, -3, "__depersist"); - if(lua_isnil(L, -1) || lua_isnil(L, -2)) - { - set_error("Can only persist non-empty userdata" - " if they have __persist and __depersist " - "metamethods"); - return false; - } - lua_pop(L, 2); - } - } - else - { - if(lua_objlen(L, iIndex) != 0) - { - set_error("Can only persist non-empty userdata if " - "they have a __depersist_size metafield"); - return false; - } - } - lua_pop(L, 1); - } - else - { - if(lua_objlen(L, iIndex) != 0) - { - set_error("Can only persist userdata without a metatable" - " if their size is zero"); - return false; - } - lua_pushnil(L); - } - return true; - } - - void write_prototype(lua_Debug *pProtoInfo, int iInstanceIndex) - { - // Sanity checks - if(pProtoInfo->source[0] != '@') - { - // @ denotes that the source was a file - // (http://www.lua.org/manual/5.1/manual.html#lua_Debug) - set_error("Can only persist Lua functions defined in source files"); - return; - } - if(std::strcmp(pProtoInfo->what, "Lua") != 0) - { - // what == "C" should have been caught by writeObjectRaw(). - // what == "tail" should be impossible. - // Hence "Lua" and "main" should be the only values seen. - // NB: Chunks are not functions defined *in* source files, because - // chunks *are* source files. - set_error(lua_pushfstring(L, "Cannot persist entire Lua chunks (%s)", pProtoInfo->source + 1)); - lua_pop(L, 1); - return; - } - - // Attempt cached lookup (prototypes are not publicly visible Lua objects, - // and hence cannot be cached in the normal way of self's environment). - lua_getmetatable(L, 1); - lua_pushfstring(L, "%s:%d", pProtoInfo->source + 1, pProtoInfo->linedefined); - lua_pushvalue(L, -1); - lua_rawget(L, -3); - if(!lua_isnil(L, -1)) - { - uint64_t iValue = (uint64_t)lua_tonumber(L, -1); - lua_pop(L, 3); - write_uint(iValue + PERSIST_TCOUNT - 1); - return; - } - lua_pop(L, 1); - lua_pushvalue(L, -1); - lua_pushnumber(L, (lua_Number)next_index++); - lua_rawset(L, -4); - - uint8_t iType = PERSIST_TPROTOTYPE; - write_byte_stream(&iType, 1); - - // Write upvalue names - write_uint(pProtoInfo->nups); - for(int i = 1; i <= pProtoInfo->nups; ++i) - { - lua_pushstring(L, lua_getupvalue(L, iInstanceIndex, i)); + // Write the environment table + lua_getfenv(L, 2); write_stack_object(-1); - lua_pop(L, 2); + lua_pop(L, 1); + } + break; + + case LUA_TUSERDATA: + if (!check_that_userdata_can_be_depersisted(2)) break; + + // Replace self's environment with self (for calls to + // writeStackObject) + lua_pushvalue(L, luaT_upvalueindex(2)); + lua_replace(L, 1); + + // Write type, metatable, and then environment + iType = LUA_TUSERDATA; + write_byte_stream(&iType, 1); + write_stack_object(-1); + lua_getfenv(L, 2); + write_stack_object(-1); + lua_pop(L, 1); + + // Write the raw data + if (lua_type(L, -1) == LUA_TTABLE) { + lua_getfield(L, -1, "__persist"); + if (lua_isnil(L, -1)) + lua_pop(L, 1); + else { + lua_pushvalue(L, 2); + lua_pushvalue(L, luaT_upvalueindex(2)); + lua_call(L, 2, 0); + } + } + write_uint((uint64_t)0x42); // sync marker + break; + + default: + set_error(lua_pushfstring(L, "Cannot persist %s values", + luaL_typename(L, 2))); + break; + } + } + lua_pushnumber(L, 0); + return 1; + } + + bool check_that_userdata_can_be_depersisted(int iIndex) { + if (lua_getmetatable(L, iIndex)) { + lua_getfield(L, -1, "__depersist_size"); + if (lua_isnumber(L, -1)) { + if (static_cast(lua_tointeger(L, -1)) != + static_cast(lua_objlen(L, iIndex))) { + set_error(lua_pushfstring(L, + "__depersist_size is " + "incorrect (%d vs. %d)", + (int)lua_objlen(L, iIndex), + (int)lua_tointeger(L, -1))); + return false; } - - // Write the function's persist name - lua_rawgeti(L, -2, 1); - lua_replace(L, -3); - lua_rawget(L, -2); - if(lua_isnil(L, -1)) - { - set_error(lua_pushfstring(L, "Lua functions must be given a unique " - "persistable name in order to be persisted (attempt to persist" - " %s:%d)", pProtoInfo->source + 1, pProtoInfo->linedefined)); - lua_pop(L, 2); - return; + if (lua_objlen(L, iIndex) != 0) { + lua_getfield(L, -2, "__persist"); + lua_getfield(L, -3, "__depersist"); + if (lua_isnil(L, -1) || lua_isnil(L, -2)) { + set_error( + "Can only persist non-empty userdata" + " if they have __persist and __depersist " + "metamethods"); + return false; + } + lua_pop(L, 2); } - write_stack_object(-1); - lua_pop(L, 2); - } - - void write_byte_stream(const uint8_t *pBytes, size_t iCount) override - { - if(had_error) - { - // If an error occurred, then silently fail to write any - // data. - return; + } else { + if (lua_objlen(L, iIndex) != 0) { + set_error( + "Can only persist non-empty userdata if " + "they have a __depersist_size metafield"); + return false; } + } + lua_pop(L, 1); + } else { + if (lua_objlen(L, iIndex) != 0) { + set_error( + "Can only persist userdata without a metatable" + " if their size is zero"); + return false; + } + lua_pushnil(L); + } + return true; + } - data.append(reinterpret_cast(pBytes), iCount); + void write_prototype(lua_Debug* pProtoInfo, int iInstanceIndex) { + // Sanity checks + if (pProtoInfo->source[0] != '@') { + // @ denotes that the source was a file + // (http://www.lua.org/manual/5.1/manual.html#lua_Debug) + set_error("Can only persist Lua functions defined in source files"); + return; + } + if (std::strcmp(pProtoInfo->what, "Lua") != 0) { + // what == "C" should have been caught by writeObjectRaw(). + // what == "tail" should be impossible. + // Hence "Lua" and "main" should be the only values seen. + // NB: Chunks are not functions defined *in* source files, because + // chunks *are* source files. + set_error(lua_pushfstring(L, "Cannot persist entire Lua chunks (%s)", + pProtoInfo->source + 1)); + lua_pop(L, 1); + return; } - void set_error(const char *sError) override - { - // If multiple errors occur, only record the first. - if (had_error) { - return; - } - had_error = true; + // Attempt cached lookup (prototypes are not publicly visible Lua + // objects, and hence cannot be cached in the normal way of self's + // environment). + lua_getmetatable(L, 1); + lua_pushfstring(L, "%s:%d", pProtoInfo->source + 1, + pProtoInfo->linedefined); + lua_pushvalue(L, -1); + lua_rawget(L, -3); + if (!lua_isnil(L, -1)) { + uint64_t iValue = (uint64_t)lua_tonumber(L, -1); + lua_pop(L, 3); + write_uint(iValue + PERSIST_TCOUNT - 1); + return; + } + lua_pop(L, 1); + lua_pushvalue(L, -1); + lua_pushnumber(L, (lua_Number)next_index++); + lua_rawset(L, -4); - // Use the written data buffer to store the error message - data.assign(sError); + uint8_t iType = PERSIST_TPROTOTYPE; + write_byte_stream(&iType, 1); + + // Write upvalue names + write_uint(pProtoInfo->nups); + for (int i = 1; i <= pProtoInfo->nups; ++i) { + lua_pushstring(L, lua_getupvalue(L, iInstanceIndex, i)); + write_stack_object(-1); + lua_pop(L, 2); } - void set_error_object(int iStackObject) - { - if(had_error) - return; + // Write the function's persist name + lua_rawgeti(L, -2, 1); + lua_replace(L, -3); + lua_rawget(L, -2); + if (lua_isnil(L, -1)) { + set_error(lua_pushfstring( + L, + "Lua functions must be given a unique " + "persistable name in order to be persisted (attempt to " + "persist" + " %s:%d)", + pProtoInfo->source + 1, pProtoInfo->linedefined)); + lua_pop(L, 2); + return; + } + write_stack_object(-1); + lua_pop(L, 2); + } - lua_pushvalue(L, iStackObject); - lua_getmetatable(L, luaT_upvalueindex(2)); - lua_insert(L, -2); - lua_setfield(L, -2, "err"); - lua_pop(L, 1); + void write_byte_stream(const uint8_t* pBytes, size_t iCount) override { + if (had_error) { + // If an error occurred, then silently fail to write any + // data. + return; } - const char* get_error() - { - if(had_error) - return data.c_str(); - else - return nullptr; - } + data.append(reinterpret_cast(pBytes), iCount); + } -private: - lua_State *L; - uint64_t next_index; - std::string data; - size_t data_size; - bool had_error; + void set_error(const char* sError) override { + // If multiple errors occur, only record the first. + if (had_error) { + return; + } + had_error = true; + + // Use the written data buffer to store the error message + data.assign(sError); + } + + void set_error_object(int iStackObject) { + if (had_error) return; + + lua_pushvalue(L, iStackObject); + lua_getmetatable(L, luaT_upvalueindex(2)); + lua_insert(L, -2); + lua_setfield(L, -2, "err"); + lua_pop(L, 1); + } + + const char* get_error() { + if (had_error) + return data.c_str(); + else + return nullptr; + } + + private: + lua_State* L; + uint64_t next_index; + std::string data; + size_t data_size; + bool had_error; }; namespace { -int l_writer_mt_index(lua_State *L) -{ - return reinterpret_cast( - lua_touserdata(L, luaT_upvalueindex(2)))->write_object_raw(); +int l_writer_mt_index(lua_State* L) { + return reinterpret_cast( + lua_touserdata(L, luaT_upvalueindex(2))) + ->write_object_raw(); } -} // namespace +} // namespace //! Basic implementation of depersistance interface /*! @@ -721,759 +665,681 @@ int l_writer_mt_index(lua_State *L) `__gc` - ~lua_persist_basic_reader (via l_crude_gc) `` - userdata to have second `__depersist` call */ -class lua_persist_basic_reader : public lua_persist_reader -{ -public: - lua_persist_basic_reader(lua_State *L) - : L(L), - string_buffer() - { } +class lua_persist_basic_reader : public lua_persist_reader { + public: + lua_persist_basic_reader(lua_State* L) : L(L), string_buffer() {} - ~lua_persist_basic_reader() - { } + ~lua_persist_basic_reader() {} - lua_State* get_stack() override - { - return L; + lua_State* get_stack() override { return L; } + + void set_error(const char* sError) override { + had_error = true; + string_buffer.assign(sError); + } + + void init(const uint8_t* pData, size_t iLength) { + data = pData; + data_buffer_size = iLength; + next_index = 1; + had_error = false; + lua_createtable(L, 32, 0); // Environment + lua_pushvalue(L, 2); + lua_rawseti(L, -2, 0); + lua_pushvalue(L, luaT_upvalueindex(1)); + lua_rawseti(L, -2, -1); + lua_pushvalue(L, luaT_upvalueindex(2)); + lua_rawseti(L, -2, -2); + lua_pushvalue(L, 1); + lua_rawseti(L, -2, -3); + lua_setfenv(L, 1); + lua_createtable(L, 0, 1); // Metatable + luaT_pushcclosure(L, l_crude_gc, 0); + lua_setfield(L, -2, "__gc"); + lua_setmetatable(L, 1); + } + + bool read_stack_object() override { + uint64_t iIndex; + if (!read_uint(iIndex)) { + set_error("Expected stack object"); + return false; } - - void set_error(const char *sError) override - { - had_error = true; - string_buffer.assign(sError); + if (lua_type(L, 1) != LUA_TTABLE) { + // Ensure that index #1 is self environment + lua_getfenv(L, 1); + lua_replace(L, 1); } - - void init(const uint8_t *pData, size_t iLength) - { - data = pData; - data_buffer_size = iLength; - next_index = 1; - had_error = false; - lua_createtable(L, 32, 0); // Environment - lua_pushvalue(L, 2); - lua_rawseti(L, -2, 0); - lua_pushvalue(L, luaT_upvalueindex(1)); - lua_rawseti(L, -2, -1); - lua_pushvalue(L, luaT_upvalueindex(2)); - lua_rawseti(L, -2, -2); - lua_pushvalue(L, 1); - lua_rawseti(L, -2, -3); - lua_setfenv(L, 1); - lua_createtable(L, 0, 1); // Metatable - luaT_pushcclosure(L, l_crude_gc, 0); - lua_setfield(L, -2, "__gc"); - lua_setmetatable(L, 1); - } - - bool read_stack_object() override - { - uint64_t iIndex; - if(!read_uint(iIndex)) - { - set_error("Expected stack object"); + if (iIndex >= PERSIST_TCOUNT) { + iIndex += 1 - PERSIST_TCOUNT; + if (iIndex < (uint64_t)INT_MAX) + lua_rawgeti(L, 1, (int)iIndex); + else { + lua_pushnumber(L, (lua_Number)iIndex); + lua_rawget(L, 1); + } + if (lua_isnil(L, -1)) { + set_error( + "Cycle while depersisting permanent object key or " + "userdata metatable"); + return false; + } + } else { + uint8_t iType = (uint8_t)iIndex; + switch (iType) { + case LUA_TNIL: + lua_pushnil(L); + break; + case PERSIST_TPERMANENT: { + uint64_t iOldIndex = next_index; + ++next_index; // Temporary marker + lua_rawgeti(L, 1, 0); // Permanents table + if (!read_stack_object()) return false; + lua_gettable(L, -2); + lua_replace(L, -2); + // Replace marker with actual object + uint64_t iNewIndex = next_index; + next_index = iOldIndex; + save_stack_object(); + next_index = iNewIndex; + break; + } + case LUA_TBOOLEAN: + lua_pushboolean(L, 0); + break; + case PERSIST_TTRUE: + lua_pushboolean(L, 1); + break; + case LUA_TSTRING: { + size_t iLength; + if (!read_uint(iLength)) return false; + if (!read_byte_stream(string_buffer, iLength)) return false; + lua_pushlstring(L, string_buffer.c_str(), string_buffer.length()); + save_stack_object(); + break; + } + case LUA_TTABLE: + lua_newtable(L); + save_stack_object(); + if (!lua_checkstack(L, 8)) return false; + if (!read_table_contents()) return false; + break; + case PERSIST_TTABLE_WITH_META: + lua_newtable(L); + save_stack_object(); + if (!lua_checkstack(L, 8)) return false; + if (!read_stack_object()) return false; + lua_setmetatable(L, -2); + if (!read_table_contents()) return false; + break; + case LUA_TNUMBER: { + double fValue; + if (!read_byte_stream(reinterpret_cast(&fValue), + sizeof(double))) return false; + lua_pushnumber(L, fValue); + break; } - if(lua_type(L, 1) != LUA_TTABLE) - { - // Ensure that index #1 is self environment - lua_getfenv(L, 1); - lua_replace(L, 1); + case LUA_TFUNCTION: { + if (!lua_checkstack(L, 8)) return false; + uint64_t iOldIndex = next_index; + ++next_index; // Temporary marker + if (!read_stack_object()) return false; + lua_call(L, 0, 2); + // Replace marker with closure + uint64_t iNewIndex = next_index; + next_index = iOldIndex; + save_stack_object(); + next_index = iNewIndex; + // Set upvalues + lua_insert(L, -2); + int iNups, i; + if (!read_uint(iNups)) return false; + size_t iIDSize; + if (!read_uint(iIDSize)) return false; + for (i = 0; i < iNups; ++i) { + if (!read_stack_object()) return false; + // For now, just skip over the upvalue IDs. In the + // future, the ID may be used to rejoin shared upvalues. + if (!read_byte_stream(nullptr, iIDSize)) return false; + } + lua_call(L, iNups, 0); + // Read environment + if (!read_stack_object()) return false; + lua_setfenv(L, -2); + break; } - if(iIndex >= PERSIST_TCOUNT) - { - iIndex += 1 - PERSIST_TCOUNT; - if(iIndex < (uint64_t)INT_MAX) - lua_rawgeti(L, 1, (int)iIndex); - else - { - lua_pushnumber(L, (lua_Number)iIndex); - lua_rawget(L, 1); - } - if(lua_isnil(L, -1)) - { - set_error("Cycle while depersisting permanent object key or userdata metatable"); + case PERSIST_TPROTOTYPE: { + if (!lua_checkstack(L, 8)) return false; + + uint64_t iOldIndex = next_index; + ++next_index; // Temporary marker + int iNups; + if (!read_uint(iNups)) return false; + if (iNups == 0) + lua_pushliteral(L, "return function() end,"); + else { + lua_pushliteral(L, "local "); + lua_checkstack(L, (iNups + 1) * 2); + for (int i = 0; i < iNups; ++i) { + if (i != 0) lua_pushliteral(L, ","); + if (!read_stack_object()) return false; + if (lua_type(L, -1) != LUA_TSTRING) { + set_error("Upvalue name not a string"); return false; + } } - } - else - { - uint8_t iType = (uint8_t)iIndex; - switch(iType) - { - case LUA_TNIL: - lua_pushnil(L); - break; - case PERSIST_TPERMANENT: { - uint64_t iOldIndex = next_index; - ++next_index; // Temporary marker - lua_rawgeti(L, 1, 0); // Permanents table - if(!read_stack_object()) - return false; - lua_gettable(L, -2); - lua_replace(L, -2); - // Replace marker with actual object - uint64_t iNewIndex = next_index; - next_index = iOldIndex; - save_stack_object(); - next_index = iNewIndex; - break; } - case LUA_TBOOLEAN: - lua_pushboolean(L, 0); - break; - case PERSIST_TTRUE: - lua_pushboolean(L, 1); - break; - case LUA_TSTRING: { - size_t iLength; - if(!read_uint(iLength)) - return false; - if(!read_byte_stream(string_buffer, iLength)) - return false; - lua_pushlstring(L, string_buffer.c_str(), string_buffer.length()); - save_stack_object(); - break; } - case LUA_TTABLE: - lua_newtable(L); - save_stack_object(); - if(!lua_checkstack(L, 8)) - return false; - if(!read_table_contents()) - return false; - break; - case PERSIST_TTABLE_WITH_META: - lua_newtable(L); - save_stack_object(); - if(!lua_checkstack(L, 8)) - return false; - if(!read_stack_object()) - return false; - lua_setmetatable(L, -2); - if(!read_table_contents()) - return false; - break; - case LUA_TNUMBER: { - double fValue; - if(!read_byte_stream(reinterpret_cast(&fValue), sizeof(double))) - return false; - lua_pushnumber(L, fValue); - break; } - case LUA_TFUNCTION: { - if(!lua_checkstack(L, 8)) - return false; - uint64_t iOldIndex = next_index; - ++next_index; // Temporary marker - if(!read_stack_object()) - return false; - lua_call(L, 0, 2); - // Replace marker with closure - uint64_t iNewIndex = next_index; - next_index = iOldIndex; - save_stack_object(); - next_index = iNewIndex; - // Set upvalues - lua_insert(L, -2); - int iNups, i; - if(!read_uint(iNups)) - return false; - size_t iIDSize; - if(!read_uint(iIDSize)) - return false; - for(i = 0; i < iNups; ++i) - { - if(!read_stack_object()) - return false; - // For now, just skip over the upvalue IDs. In the future, - // the ID may be used to rejoin shared upvalues. - if(!read_byte_stream(nullptr, iIDSize)) - return false; - } - lua_call(L, iNups, 0); - // Read environment - if(!read_stack_object()) - return false; - lua_setfenv(L, -2); - break; } - case PERSIST_TPROTOTYPE: { - if(!lua_checkstack(L, 8)) - return false; - - uint64_t iOldIndex = next_index; - ++next_index; // Temporary marker - int iNups; - if(!read_uint(iNups)) - return false; - if(iNups == 0) - lua_pushliteral(L, "return function() end,"); - else - { - lua_pushliteral(L, "local "); - lua_checkstack(L, (iNups + 1) * 2); - for(int i = 0; i < iNups; ++i) - { - if(i != 0) - lua_pushliteral(L, ","); - if(!read_stack_object()) - return false; - if(lua_type(L, -1) != LUA_TSTRING) - { - set_error("Upvalue name not a string"); - return false; - } - } - lua_concat(L, iNups * 2 - 1); - lua_pushliteral(L, ";return function(...)"); - lua_pushvalue(L, -2); - lua_pushliteral(L, "=...end,"); - lua_concat(L, 5); - } - // Fetch name and then lookup filename and code - if(!read_stack_object()) - return false; - lua_pushliteral(L, "@"); - - lua_rawgeti(L, 1, -1); - lua_pushvalue(L, -3); - lua_gettable(L, -2); - lua_replace(L, -2); - - if(lua_isnil(L, -1)) - { - set_error(lua_pushfstring(L, "Unable to depersist prototype" - " \'%s\'", lua_tostring(L, -3))); - return false; - } - lua_concat(L, 2); // Prepend the @ to the filename - lua_rawgeti(L, 1, -2); - lua_pushvalue(L, -3); - - lua_gettable(L, -2); - lua_replace(L, -2); - lua_remove(L, -3); - // Construct the closure factory - load_multi_buffer ls; - ls.s[0] = lua_tolstring(L, -3, &ls.i[0]); - ls.s[1] = lua_tolstring(L, -1, &ls.i[1]); - if(luaT_load(L, load_multi_buffer::load_fn, &ls, lua_tostring(L, -2), "bt") != 0) - { - // Should never happen - lua_error(L); - return false; - } - lua_replace(L, -4); - lua_pop(L, 2); - // Replace marker with closure factory - uint64_t iNewIndex = next_index; - next_index = iOldIndex; - save_stack_object(); - next_index = iNewIndex; - break; } - case LUA_TUSERDATA: { - bool bHasSetMetatable = false; - uint64_t iOldIndex = next_index; - ++next_index; // Temporary marker - // Read metatable - if(!read_stack_object()) - return false; - lua_getfield(L, -1, "__depersist_size"); - if(!lua_isnumber(L, -1)) - { - set_error("Userdata missing __depersist_size metafield"); - return false; - } - lua_newuserdata(L, (size_t)lua_tonumber(L, -1)); - lua_replace(L, -2); - // Replace marker with userdata - uint64_t iNewIndex = next_index; - next_index = iOldIndex; - save_stack_object(); - next_index = iNewIndex; - // Perform immediate initialisation - lua_getfield(L, -2, "__pre_depersist"); - if(lua_isnil(L, -1)) - lua_pop(L, 1); - else - { - // Set metatable now, as pre-depersister may expect it - // NB: Setting metatable if there isn't a pre-depersister - // is not a good idea, as if there is an error while the - // environment table is being de-persisted, then the __gc - // handler of the userdata will eventually be called with - // the userdata's contents still being uninitialised. - lua_pushvalue(L, -3); - lua_setmetatable(L, -3); - bHasSetMetatable = true; - lua_pushvalue(L, -2); - lua_call(L, 1, 0); - } - // Read environment - if(!read_stack_object()) - return false; - lua_setfenv(L, -2); - // Set metatable and read the raw data - if(!bHasSetMetatable) - { - lua_pushvalue(L, -2); - lua_setmetatable(L, -2); - } - lua_getfield(L, -2, "__depersist"); - if(lua_isnil(L, -1)) - lua_pop(L, 1); - else - { - lua_pushvalue(L, -2); - lua_rawgeti(L, 1, -3); - lua_call(L, 2, 1); - if(lua_toboolean(L, -1) != 0) - { - lua_pop(L, 1); - lua_rawgeti(L, 1, -3); - lua_getmetatable(L, -1); - lua_replace(L, -2); - lua_pushvalue(L, -2); - lua_rawseti(L, -2, (int)lua_objlen(L, -2) + 1); - } - lua_pop(L, 1); - } - lua_replace(L, -2); - uint64_t iSyncMarker; - if(!read_uint(iSyncMarker)) - return false; - if(iSyncMarker != 0x42) - { - set_error("sync fail"); - return false; - } - break; } - case PERSIST_TINTEGER: { - uint16_t iValue; - if(!read_uint(iValue)) - return false; - lua_pushinteger(L, iValue); - break; } - default: - lua_pushliteral(L, "Unable to depersist values of type \'"); - if(iType <= LUA_TTHREAD) - lua_pushstring(L, lua_typename(L, iType)); - else - { - switch(iType) - { - case PERSIST_TPERMANENT: - lua_pushliteral(L, "permanent"); break; - case PERSIST_TTRUE: - lua_pushliteral(L, "boolean-true"); break; - case PERSIST_TTABLE_WITH_META: - lua_pushliteral(L, "table-with-metatable"); break; - case PERSIST_TINTEGER: - lua_pushliteral(L, "integer"); break; - case PERSIST_TPROTOTYPE: - lua_pushliteral(L, "prototype"); break; - case PERSIST_TRESERVED1: - lua_pushliteral(L, "reserved1"); break; - case PERSIST_TRESERVED2: - lua_pushliteral(L, "reserved2"); break; - } - } - lua_pushliteral(L, "\'"); - lua_concat(L, 3); - set_error(lua_tostring(L, -1)); - lua_pop(L, 1); - return false; - } - } - return true; - } - - void save_stack_object() - { - if(next_index < (uint64_t)INT_MAX) - { - lua_pushvalue(L, -1); - lua_rawseti(L, 1, (int)next_index); - } - else - { - lua_pushnumber(L, (lua_Number)next_index); + lua_concat(L, iNups * 2 - 1); + lua_pushliteral(L, ";return function(...)"); lua_pushvalue(L, -2); - lua_rawset(L, 1); - } - ++next_index; - } + lua_pushliteral(L, "=...end,"); + lua_concat(L, 5); + } + // Fetch name and then lookup filename and code + if (!read_stack_object()) return false; + lua_pushliteral(L, "@"); - bool read_table_contents() - { - while(true) - { - if(!read_stack_object()) - return false; - if(lua_type(L, -1) == LUA_TNIL) - { - lua_pop(L, 1); - return true; - } - if(!read_stack_object()) - return false; - // NB: lua_rawset used rather than lua_settable to avoid invoking - // any metamethods, as they may not have been designed to be called - // during depersistance. - lua_rawset(L, -3); - } - } + lua_rawgeti(L, 1, -1); + lua_pushvalue(L, -3); + lua_gettable(L, -2); + lua_replace(L, -2); - bool finish() - { - - // Ensure that all data has been read - if(data_buffer_size != 0) - { - set_error(lua_pushfstring(L, "%d bytes of data remain unpersisted", (int)data_buffer_size)); + if (lua_isnil(L, -1)) { + set_error(lua_pushfstring(L, + "Unable to depersist prototype" + " \'%s\'", + lua_tostring(L, -3))); return false; - } + } + lua_concat(L, 2); // Prepend the @ to the filename + lua_rawgeti(L, 1, -2); + lua_pushvalue(L, -3); - // Ensure that index #1 is self environment - if(lua_type(L, 1) != LUA_TTABLE) - { - lua_getfenv(L, 1); - lua_replace(L, 1); + lua_gettable(L, -2); + lua_replace(L, -2); + lua_remove(L, -3); + // Construct the closure factory + load_multi_buffer ls; + ls.s[0] = lua_tolstring(L, -3, &ls.i[0]); + ls.s[1] = lua_tolstring(L, -1, &ls.i[1]); + if (luaT_load(L, load_multi_buffer::load_fn, &ls, lua_tostring(L, -2), + "bt") != 0) { + // Should never happen + lua_error(L); + return false; + } + lua_replace(L, -4); + lua_pop(L, 2); + // Replace marker with closure factory + uint64_t iNewIndex = next_index; + next_index = iOldIndex; + save_stack_object(); + next_index = iNewIndex; + break; } - // Ensure that index #1 is self metatable - lua_rawgeti(L, 1, -3); - lua_getmetatable(L, -1); - lua_replace(L, 1); - lua_pop(L, 1); - - // Call all the __depersist functions which need a 2nd call - int iNumCalls = (int)lua_objlen(L, 1); - for(int i = 1; i <= iNumCalls; ++i) - { - lua_rawgeti(L, 1, i); - luaL_getmetafield(L, -1, "__depersist"); - lua_insert(L, -2); + case LUA_TUSERDATA: { + bool bHasSetMetatable = false; + uint64_t iOldIndex = next_index; + ++next_index; // Temporary marker + // Read metatable + if (!read_stack_object()) return false; + lua_getfield(L, -1, "__depersist_size"); + if (!lua_isnumber(L, -1)) { + set_error("Userdata missing __depersist_size metafield"); + return false; + } + lua_newuserdata(L, (size_t)lua_tonumber(L, -1)); + lua_replace(L, -2); + // Replace marker with userdata + uint64_t iNewIndex = next_index; + next_index = iOldIndex; + save_stack_object(); + next_index = iNewIndex; + // Perform immediate initialisation + lua_getfield(L, -2, "__pre_depersist"); + if (lua_isnil(L, -1)) + lua_pop(L, 1); + else { + // Set metatable now, as pre-depersister may expect it + // NB: Setting metatable if there isn't a + // pre-depersister is not a good idea, as if there is an + // error while the environment table is being + // de-persisted, then the __gc handler of the userdata + // will eventually be called with the userdata's + // contents still being uninitialised. + lua_pushvalue(L, -3); + lua_setmetatable(L, -3); + bHasSetMetatable = true; + lua_pushvalue(L, -2); lua_call(L, 1, 0); - } - return true; - } - - bool read_byte_stream(uint8_t *pBytes, size_t iCount) override - { - if(iCount > data_buffer_size) - { - set_error(lua_pushfstring(L, - "End of input reached while attempting to read %d byte%s", - (int)iCount, iCount == 1 ? "" : "s")); + } + // Read environment + if (!read_stack_object()) return false; + lua_setfenv(L, -2); + // Set metatable and read the raw data + if (!bHasSetMetatable) { + lua_pushvalue(L, -2); + lua_setmetatable(L, -2); + } + lua_getfield(L, -2, "__depersist"); + if (lua_isnil(L, -1)) lua_pop(L, 1); - return false; - } - - if(pBytes != nullptr) - std::memcpy(pBytes, data, iCount); - - data += iCount; - data_buffer_size -= iCount; - return true; - } - - bool read_byte_stream(std::string& bytes, size_t iCount) - { - if(iCount > data_buffer_size) { - set_error(lua_pushfstring(L, - "End of input reached while attempting to read %d byte%s", - (int)iCount, iCount == 1 ? "" : "s")); + else { + lua_pushvalue(L, -2); + lua_rawgeti(L, 1, -3); + lua_call(L, 2, 1); + if (lua_toboolean(L, -1) != 0) { + lua_pop(L, 1); + lua_rawgeti(L, 1, -3); + lua_getmetatable(L, -1); + lua_replace(L, -2); + lua_pushvalue(L, -2); + lua_rawseti(L, -2, (int)lua_objlen(L, -2) + 1); + } lua_pop(L, 1); + } + lua_replace(L, -2); + uint64_t iSyncMarker; + if (!read_uint(iSyncMarker)) return false; + if (iSyncMarker != 0x42) { + set_error("sync fail"); return false; + } + break; } + case PERSIST_TINTEGER: { + uint16_t iValue; + if (!read_uint(iValue)) return false; + lua_pushinteger(L, iValue); + break; + } + default: + lua_pushliteral(L, "Unable to depersist values of type \'"); + if (iType <= LUA_TTHREAD) + lua_pushstring(L, lua_typename(L, iType)); + else { + switch (iType) { + case PERSIST_TPERMANENT: + lua_pushliteral(L, "permanent"); + break; + case PERSIST_TTRUE: + lua_pushliteral(L, "boolean-true"); + break; + case PERSIST_TTABLE_WITH_META: + lua_pushliteral(L, "table-with-metatable"); + break; + case PERSIST_TINTEGER: + lua_pushliteral(L, "integer"); + break; + case PERSIST_TPROTOTYPE: + lua_pushliteral(L, "prototype"); + break; + case PERSIST_TRESERVED1: + lua_pushliteral(L, "reserved1"); + break; + case PERSIST_TRESERVED2: + lua_pushliteral(L, "reserved2"); + break; + } + } + lua_pushliteral(L, "\'"); + lua_concat(L, 3); + set_error(lua_tostring(L, -1)); + lua_pop(L, 1); + return false; + } + } + return true; + } - bytes.assign(reinterpret_cast(data), iCount); + void save_stack_object() { + if (next_index < (uint64_t)INT_MAX) { + lua_pushvalue(L, -1); + lua_rawseti(L, 1, (int)next_index); + } else { + lua_pushnumber(L, (lua_Number)next_index); + lua_pushvalue(L, -2); + lua_rawset(L, 1); + } + ++next_index; + } - data += iCount; - data_buffer_size -= iCount; + bool read_table_contents() { + while (true) { + if (!read_stack_object()) return false; + if (lua_type(L, -1) == LUA_TNIL) { + lua_pop(L, 1); return true; + } + if (!read_stack_object()) return false; + // NB: lua_rawset used rather than lua_settable to avoid invoking + // any metamethods, as they may not have been designed to be called + // during depersistance. + lua_rawset(L, -3); + } + } + + bool finish() { + // Ensure that all data has been read + if (data_buffer_size != 0) { + set_error(lua_pushfstring(L, "%d bytes of data remain unpersisted", + (int)data_buffer_size)); + return false; } - const uint8_t* get_pointer() {return data;} - uint64_t get_object_count() {return next_index;} + // Ensure that index #1 is self environment + if (lua_type(L, 1) != LUA_TTABLE) { + lua_getfenv(L, 1); + lua_replace(L, 1); + } + // Ensure that index #1 is self metatable + lua_rawgeti(L, 1, -3); + lua_getmetatable(L, -1); + lua_replace(L, 1); + lua_pop(L, 1); - const char* get_error() - { - if(had_error) - return string_buffer.c_str(); - else - return nullptr; + // Call all the __depersist functions which need a 2nd call + int iNumCalls = (int)lua_objlen(L, 1); + for (int i = 1; i <= iNumCalls; ++i) { + lua_rawgeti(L, 1, i); + luaL_getmetafield(L, -1, "__depersist"); + lua_insert(L, -2); + lua_call(L, 1, 0); + } + return true; + } + + bool read_byte_stream(uint8_t* pBytes, size_t iCount) override { + if (iCount > data_buffer_size) { + set_error(lua_pushfstring( + L, "End of input reached while attempting to read %d byte%s", + (int)iCount, iCount == 1 ? "" : "s")); + lua_pop(L, 1); + return false; } -private: - lua_State *L; - uint64_t next_index; - const uint8_t* data; - size_t data_buffer_size; - std::string string_buffer; - bool had_error; + if (pBytes != nullptr) std::memcpy(pBytes, data, iCount); + + data += iCount; + data_buffer_size -= iCount; + return true; + } + + bool read_byte_stream(std::string& bytes, size_t iCount) { + if (iCount > data_buffer_size) { + set_error(lua_pushfstring( + L, "End of input reached while attempting to read %d byte%s", + (int)iCount, iCount == 1 ? "" : "s")); + lua_pop(L, 1); + return false; + } + + bytes.assign(reinterpret_cast(data), iCount); + + data += iCount; + data_buffer_size -= iCount; + return true; + } + + const uint8_t* get_pointer() { return data; } + uint64_t get_object_count() { return next_index; } + + const char* get_error() { + if (had_error) + return string_buffer.c_str(); + else + return nullptr; + } + + private: + lua_State* L; + uint64_t next_index; + const uint8_t* data; + size_t data_buffer_size; + std::string string_buffer; + bool had_error; }; namespace { -int l_dump_toplevel(lua_State *L) -{ - luaL_checktype(L, 2, LUA_TTABLE); - lua_settop(L, 2); - lua_pushvalue(L, 1); - lua_persist_basic_writer *pWriter = new (lua_newuserdata(L, sizeof(lua_persist_basic_writer))) lua_persist_basic_writer(L); - lua_replace(L, 1); - pWriter->init(); - pWriter->write_stack_object(3); - return pWriter->finish(); +int l_dump_toplevel(lua_State* L) { + luaL_checktype(L, 2, LUA_TTABLE); + lua_settop(L, 2); + lua_pushvalue(L, 1); + lua_persist_basic_writer* pWriter = + new (lua_newuserdata(L, sizeof(lua_persist_basic_writer))) + lua_persist_basic_writer(L); + lua_replace(L, 1); + pWriter->init(); + pWriter->write_stack_object(3); + return pWriter->finish(); } -int l_load_toplevel(lua_State *L) -{ - size_t iDataLength; - const uint8_t *pData = luaT_checkfile(L, 1, &iDataLength); - luaL_checktype(L, 2, LUA_TTABLE); - lua_settop(L, 2); - lua_pushvalue(L, 1); - lua_persist_basic_reader *pReader = new (lua_newuserdata(L, sizeof(lua_persist_basic_reader))) lua_persist_basic_reader(L); - lua_replace(L, 1); - pReader->init(pData, iDataLength); - if(!pReader->read_stack_object() || !pReader->finish()) - { - int iNumObjects = (int)pReader->get_object_count(); - int iNumBytes = (int)(pReader->get_pointer() - pData); - lua_pushnil(L); - lua_pushfstring(L, "%s after %d objects (%d bytes)", - pReader->get_error() ? pReader->get_error() : "Error while depersisting", - iNumObjects, iNumBytes); - return 2; - } - else - { - return 1; - } -} - -int calculate_line_number(const char *sStart, const char *sPosition) -{ - int iLine = 1; - for(; sStart != sPosition; ++sStart) - { - switch(sStart[0]) - { - case '\0': - return -1; // error return value - case '\n': - ++iLine; - if(sStart[1] == '\r') - ++sStart; - break; - case '\r': - ++iLine; - if(sStart[1] == '\n') - ++sStart; - break; - } - } - return iLine; -} - -const char* find_function_end(lua_State *L, const char* sStart) -{ - const char* sEnd = sStart; - while(sEnd) - { - sEnd = std::strstr(sEnd, "end"); - if(sEnd) - { - sEnd += 3; - load_multi_buffer ls; - ls.s[0] = "return function"; - ls.i[0] = sizeof("return function") - 1; - ls.s[1] = sStart; - ls.i[1] = sEnd - sStart; - if(luaT_load(L, load_multi_buffer::load_fn, &ls, "", "bt") == 0) - { - lua_pop(L, 1); - return sEnd; - } - lua_pop(L, 1); - } - } - return nullptr; -} - -int l_persist_dofile(lua_State *L) -{ - const char *sFilename = luaL_checkstring(L, 1); - lua_settop(L, 1); - - // Read entire file into memory - std::FILE *fFile = std::fopen(sFilename, "r"); - if(fFile == nullptr) - { - const char *sError =std::strerror(errno); - return luaL_error(L, "cannot open %s: %s", sFilename, sError); - } - size_t iBufferSize = lua_objlen(L, luaT_upvalueindex(1)); - size_t iBufferUsed = 0; - while(!std::feof(fFile)) - { - iBufferUsed += std::fread(reinterpret_cast(lua_touserdata(L, - luaT_upvalueindex(1))) + iBufferUsed, 1, iBufferSize - iBufferUsed, fFile); - if(iBufferUsed == iBufferSize) - { - iBufferSize *= 2; - std::memcpy(lua_newuserdata(L, iBufferSize), lua_touserdata(L, luaT_upvalueindex(1)), iBufferUsed); - lua_replace(L, luaT_upvalueindex(1)); - } - else - break; - } - int iStatus = std::ferror(fFile); - std::fclose(fFile); - if(iStatus) - { - const char *sError =std::strerror(errno); - return luaL_error(L, "cannot read %s: %s", sFilename, sError); - } - - // Check file - char *sFile = reinterpret_cast(lua_touserdata(L, luaT_upvalueindex(1))); - sFile[iBufferUsed] = 0; - if(sFile[0] == '#') - { - do - { - ++sFile; - --iBufferUsed; - } while(sFile[0] != 0 && sFile[0] != '\r' && sFile[0] != '\n'); - } - if(sFile[0] == LUA_SIGNATURE[0]) - { - return luaL_error(L, "cannot load %s: compiled files not permitted", sFilename); - } - - // Load and do file - lua_pushliteral(L, "@"); - lua_pushvalue(L, 1); - lua_concat(L, 2); - if(luaL_loadbuffer(L, sFile, iBufferUsed, lua_tostring(L, -1)) != 0) - return lua_error(L); - lua_remove(L, -2); - int iBufferCopyIndex = lua_gettop(L); - std::memcpy(lua_newuserdata(L, iBufferUsed + 1), sFile, iBufferUsed + 1); - lua_insert(L, -2); - lua_call(L, 0, LUA_MULTRET); - sFile = reinterpret_cast(lua_touserdata(L, luaT_upvalueindex(1))); - std::memcpy(sFile, lua_touserdata(L, iBufferCopyIndex), iBufferUsed + 1); - lua_remove(L, iBufferCopyIndex); - - // Extract persistable functions - const char *sPosition = sFile; - while(true) - { - sPosition = std::strstr(sPosition, "--[[persistable:"); - if(!sPosition) - break; - sPosition += 16; - const char *sNameEnd = std::strstr(sPosition, "]]"); - if(sNameEnd) - { - int iLineNumber = calculate_line_number(sFile, sNameEnd); - const char *sFunctionArgs = std::strchr(sNameEnd + 2, '('); - const char *sFunctionEnd = find_function_end(L, sFunctionArgs); - if((sNameEnd - sPosition) == 1 && *sPosition == ':') - { - // --[[persistable::]] means take the existing name of the function - sPosition = std::strstr(sNameEnd, "function") + 8; - sPosition += std::strspn(sPosition, " \t"); - sNameEnd = sFunctionArgs; - while(sNameEnd[-1] == ' ') - --sNameEnd; - } - if(iLineNumber != -1 && sFunctionArgs && sFunctionEnd) - { - // Save : => - lua_pushfstring(L, "%s:%d", sFilename, iLineNumber); - lua_pushvalue(L, -1); - lua_gettable(L, luaT_upvalueindex(2)); - if(lua_isnil(L, -1)) - { - lua_pop(L, 1); - lua_pushlstring(L, sPosition, sNameEnd - sPosition); - lua_settable(L, luaT_upvalueindex(2)); - } - else - { - return luaL_error(L, "Multiple persistable functions defin" - "ed on the same line (%s:%d)", sFilename, iLineNumber); - } - - // Save => - lua_pushlstring(L, sPosition, sNameEnd - sPosition); - lua_pushvalue(L, -1); - lua_gettable(L, luaT_upvalueindex(3)); - if(lua_isnil(L, -1)) - { - lua_pop(L, 1); - lua_pushvalue(L, 1); - lua_settable(L, luaT_upvalueindex(3)); - } - else - { - return luaL_error(L, "Persistable function name \'%s\' is" - " not unique (defined in both %s and %s)", - lua_tostring(L, -2), lua_tostring(L, -1), sFilename); - } - - // Save => - lua_pushlstring(L, sPosition, sNameEnd - sPosition); - lua_pushliteral(L, "\n"); - lua_getfield(L, -1, "rep"); - lua_insert(L, -2); - lua_pushinteger(L, iLineNumber - 1); - lua_call(L, 2, 1); - lua_pushliteral(L, "function"); - lua_pushlstring(L, sFunctionArgs, sFunctionEnd - sFunctionArgs); - lua_concat(L, 3); - lua_settable(L, luaT_upvalueindex(4)); - } - } - } - - // Finish - return lua_gettop(L) - 1; -} - -int l_errcatch(lua_State *L) -{ - // Dummy function for debugging - place a breakpoint on the following - // return statement to inspect the full C call stack when a Lua error - // occurs (assuming that l_errcatch is used as the error catch handler). +int l_load_toplevel(lua_State* L) { + size_t iDataLength; + const uint8_t* pData = luaT_checkfile(L, 1, &iDataLength); + luaL_checktype(L, 2, LUA_TTABLE); + lua_settop(L, 2); + lua_pushvalue(L, 1); + lua_persist_basic_reader* pReader = + new (lua_newuserdata(L, sizeof(lua_persist_basic_reader))) + lua_persist_basic_reader(L); + lua_replace(L, 1); + pReader->init(pData, iDataLength); + if (!pReader->read_stack_object() || !pReader->finish()) { + int iNumObjects = (int)pReader->get_object_count(); + int iNumBytes = (int)(pReader->get_pointer() - pData); + lua_pushnil(L); + lua_pushfstring(L, "%s after %d objects (%d bytes)", + pReader->get_error() ? pReader->get_error() + : "Error while depersisting", + iNumObjects, iNumBytes); + return 2; + } else { return 1; + } } -constexpr std::array persist_lib {{ - // Due to the various required upvalues, functions are registered - // manually, but we still need a dummy to pass to luaL_register. - {"errcatch", l_errcatch}, - {nullptr, nullptr} -}}; - -} // namespace - -int luaopen_persist(lua_State *L) -{ - luaT_register(L, "persist", persist_lib); - lua_newuserdata(L, 512); // buffer for dofile - lua_newtable(L); - lua_newtable(L); - lua_newtable(L); - lua_pushvalue(L, -3); - luaT_pushcclosure(L, l_dump_toplevel, 1); - lua_setfield(L, -6, "dump"); - lua_pushvalue(L, -2); - lua_pushvalue(L, -2); - luaT_pushcclosure(L, l_load_toplevel, 2); - lua_setfield(L, -6, "load"); - luaT_pushcclosure(L, l_persist_dofile, 4); - lua_setfield(L, -2, "dofile"); - return 1; +int calculate_line_number(const char* sStart, const char* sPosition) { + int iLine = 1; + for (; sStart != sPosition; ++sStart) { + switch (sStart[0]) { + case '\0': + return -1; // error return value + case '\n': + ++iLine; + if (sStart[1] == '\r') ++sStart; + break; + case '\r': + ++iLine; + if (sStart[1] == '\n') ++sStart; + break; + } + } + return iLine; +} + +const char* find_function_end(lua_State* L, const char* sStart) { + const char* sEnd = sStart; + while (sEnd) { + sEnd = std::strstr(sEnd, "end"); + if (sEnd) { + sEnd += 3; + load_multi_buffer ls; + ls.s[0] = "return function"; + ls.i[0] = sizeof("return function") - 1; + ls.s[1] = sStart; + ls.i[1] = sEnd - sStart; + if (luaT_load(L, load_multi_buffer::load_fn, &ls, "", "bt") == 0) { + lua_pop(L, 1); + return sEnd; + } + lua_pop(L, 1); + } + } + return nullptr; +} + +int l_persist_dofile(lua_State* L) { + const char* sFilename = luaL_checkstring(L, 1); + lua_settop(L, 1); + + // Read entire file into memory + std::FILE* fFile = std::fopen(sFilename, "r"); + if (fFile == nullptr) { + const char* sError = std::strerror(errno); + return luaL_error(L, "cannot open %s: %s", sFilename, sError); + } + size_t iBufferSize = lua_objlen(L, luaT_upvalueindex(1)); + size_t iBufferUsed = 0; + while (!std::feof(fFile)) { + iBufferUsed += std::fread( + reinterpret_cast(lua_touserdata(L, luaT_upvalueindex(1))) + + iBufferUsed, + 1, iBufferSize - iBufferUsed, fFile); + if (iBufferUsed == iBufferSize) { + iBufferSize *= 2; + std::memcpy(lua_newuserdata(L, iBufferSize), + lua_touserdata(L, luaT_upvalueindex(1)), iBufferUsed); + lua_replace(L, luaT_upvalueindex(1)); + } else + break; + } + int iStatus = std::ferror(fFile); + std::fclose(fFile); + if (iStatus) { + const char* sError = std::strerror(errno); + return luaL_error(L, "cannot read %s: %s", sFilename, sError); + } + + // Check file + char* sFile = + reinterpret_cast(lua_touserdata(L, luaT_upvalueindex(1))); + sFile[iBufferUsed] = 0; + if (sFile[0] == '#') { + do { + ++sFile; + --iBufferUsed; + } while (sFile[0] != 0 && sFile[0] != '\r' && sFile[0] != '\n'); + } + if (sFile[0] == LUA_SIGNATURE[0]) { + return luaL_error(L, "cannot load %s: compiled files not permitted", + sFilename); + } + + // Load and do file + lua_pushliteral(L, "@"); + lua_pushvalue(L, 1); + lua_concat(L, 2); + if (luaL_loadbuffer(L, sFile, iBufferUsed, lua_tostring(L, -1)) != 0) + return lua_error(L); + lua_remove(L, -2); + int iBufferCopyIndex = lua_gettop(L); + std::memcpy(lua_newuserdata(L, iBufferUsed + 1), sFile, iBufferUsed + 1); + lua_insert(L, -2); + lua_call(L, 0, LUA_MULTRET); + sFile = reinterpret_cast(lua_touserdata(L, luaT_upvalueindex(1))); + std::memcpy(sFile, lua_touserdata(L, iBufferCopyIndex), iBufferUsed + 1); + lua_remove(L, iBufferCopyIndex); + + // Extract persistable functions + const char* sPosition = sFile; + while (true) { + sPosition = std::strstr(sPosition, "--[[persistable:"); + if (!sPosition) break; + sPosition += 16; + const char* sNameEnd = std::strstr(sPosition, "]]"); + if (sNameEnd) { + int iLineNumber = calculate_line_number(sFile, sNameEnd); + const char* sFunctionArgs = std::strchr(sNameEnd + 2, '('); + const char* sFunctionEnd = find_function_end(L, sFunctionArgs); + if ((sNameEnd - sPosition) == 1 && *sPosition == ':') { + // --[[persistable::]] means take the existing name of the + // function + sPosition = std::strstr(sNameEnd, "function") + 8; + sPosition += std::strspn(sPosition, " \t"); + sNameEnd = sFunctionArgs; + while (sNameEnd[-1] == ' ') --sNameEnd; + } + if (iLineNumber != -1 && sFunctionArgs && sFunctionEnd) { + // Save : => + lua_pushfstring(L, "%s:%d", sFilename, iLineNumber); + lua_pushvalue(L, -1); + lua_gettable(L, luaT_upvalueindex(2)); + if (lua_isnil(L, -1)) { + lua_pop(L, 1); + lua_pushlstring(L, sPosition, sNameEnd - sPosition); + lua_settable(L, luaT_upvalueindex(2)); + } else { + return luaL_error(L, + "Multiple persistable functions defin" + "ed on the same line (%s:%d)", + sFilename, iLineNumber); + } + + // Save => + lua_pushlstring(L, sPosition, sNameEnd - sPosition); + lua_pushvalue(L, -1); + lua_gettable(L, luaT_upvalueindex(3)); + if (lua_isnil(L, -1)) { + lua_pop(L, 1); + lua_pushvalue(L, 1); + lua_settable(L, luaT_upvalueindex(3)); + } else { + return luaL_error(L, + "Persistable function name \'%s\' is" + " not unique (defined in both %s and %s)", + lua_tostring(L, -2), lua_tostring(L, -1), + sFilename); + } + + // Save => + lua_pushlstring(L, sPosition, sNameEnd - sPosition); + lua_pushliteral(L, "\n"); + lua_getfield(L, -1, "rep"); + lua_insert(L, -2); + lua_pushinteger(L, iLineNumber - 1); + lua_call(L, 2, 1); + lua_pushliteral(L, "function"); + lua_pushlstring(L, sFunctionArgs, sFunctionEnd - sFunctionArgs); + lua_concat(L, 3); + lua_settable(L, luaT_upvalueindex(4)); + } + } + } + + // Finish + return lua_gettop(L) - 1; +} + +int l_errcatch(lua_State* L) { + // Dummy function for debugging - place a breakpoint on the following + // return statement to inspect the full C call stack when a Lua error + // occurs (assuming that l_errcatch is used as the error catch handler). + return 1; +} + +// Due to the various required upvalues, functions are registered manually, but +// we still need a dummy to pass to luaL_register. +constexpr std::array persist_lib{ + {{"errcatch", l_errcatch}, {nullptr, nullptr}}}; + +} // namespace + +int luaopen_persist(lua_State* L) { + luaT_register(L, "persist", persist_lib); + lua_newuserdata(L, 512); // buffer for dofile + lua_newtable(L); + lua_newtable(L); + lua_newtable(L); + lua_pushvalue(L, -3); + luaT_pushcclosure(L, l_dump_toplevel, 1); + lua_setfield(L, -6, "dump"); + lua_pushvalue(L, -2); + lua_pushvalue(L, -2); + luaT_pushcclosure(L, l_load_toplevel, 2); + lua_setfield(L, -6, "load"); + luaT_pushcclosure(L, l_persist_dofile, 4); + lua_setfield(L, -2, "dofile"); + return 1; } diff --git a/CorsixTH/Src/persist_lua.h b/CorsixTH/Src/persist_lua.h index fe58cdb9..728a2a04 100644 --- a/CorsixTH/Src/persist_lua.h +++ b/CorsixTH/Src/persist_lua.h @@ -23,84 +23,76 @@ SOFTWARE. #ifndef CORSIX_TH_PERSIST_LUA_H_ #define CORSIX_TH_PERSIST_LUA_H_ #include "config.h" -#include "th_lua.h" -#include #include +#include +#include "th_lua.h" -template struct lua_persist_int {}; -template <> struct lua_persist_int {typedef unsigned int T;}; +template +struct lua_persist_int {}; +template <> +struct lua_persist_int { + typedef unsigned int T; +}; //! Interface used for persisting Lua objects /*! When userdata are persisted, they get an instance of this interface for writing binary data and other Lua objects. */ -class lua_persist_writer -{ -public: - virtual ~lua_persist_writer() = default; +class lua_persist_writer { + public: + virtual ~lua_persist_writer() = default; - virtual lua_State* get_stack() = 0; - virtual void write_stack_object(int iIndex) = 0; - virtual void write_byte_stream(const uint8_t *pBytes, size_t iCount) = 0; - virtual void set_error(const char *sError) = 0; + virtual lua_State* get_stack() = 0; + virtual void write_stack_object(int iIndex) = 0; + virtual void write_byte_stream(const uint8_t* pBytes, size_t iCount) = 0; + virtual void set_error(const char* sError) = 0; - // write_stack_object for userdata without growing the Lua call stack - // The given index should be a userdata whose __persist metamethod supports - // fast persistance (being called with extra arguments and the wrong - // environment / upvalues). - virtual void fast_write_stack_object(int iIndex) = 0; + // write_stack_object for userdata without growing the Lua call stack + // The given index should be a userdata whose __persist metamethod supports + // fast persistance (being called with extra arguments and the wrong + // environment / upvalues). + virtual void fast_write_stack_object(int iIndex) = 0; - // Writes an unsigned integer as a variable number of bytes - // Endian independant and underlying type size independant - template - void write_uint(T tValue) - { - T tTemp(tValue); - int iNumBytes; - for(iNumBytes = 1; tTemp >= (T)0x80; tTemp /= (T)0x80) - ++iNumBytes; - if(iNumBytes == 1) - { - uint8_t iByte = (uint8_t)tValue; - write_byte_stream(&iByte, 1); - } - else - { - std::vector bytes(iNumBytes); - bytes[iNumBytes - 1] = 0x7F & (uint8_t)(tValue); - for(int i = 1; i < iNumBytes; ++i) - { - tValue /= (T)0x80; - bytes[iNumBytes - 1 - i] = 0x80 | (0x7F & (uint8_t)tValue); - } - write_byte_stream(bytes.data(), iNumBytes); - } + // Writes an unsigned integer as a variable number of bytes + // Endian independant and underlying type size independant + template + void write_uint(T tValue) { + T tTemp(tValue); + int iNumBytes; + for (iNumBytes = 1; tTemp >= (T)0x80; tTemp /= (T)0x80) ++iNumBytes; + if (iNumBytes == 1) { + uint8_t iByte = (uint8_t)tValue; + write_byte_stream(&iByte, 1); + } else { + std::vector bytes(iNumBytes); + bytes[iNumBytes - 1] = 0x7F & (uint8_t)(tValue); + for (int i = 1; i < iNumBytes; ++i) { + tValue /= (T)0x80; + bytes[iNumBytes - 1 - i] = 0x80 | (0x7F & (uint8_t)tValue); + } + write_byte_stream(bytes.data(), iNumBytes); } + } - template - void write_int(T tValue) - { - typename lua_persist_int::T tValueToWrite; - if(tValue >= 0) - { - tValueToWrite = (typename lua_persist_int::T)tValue; - tValueToWrite <<= 1; - } - else - { - tValueToWrite = (typename lua_persist_int::T)(-(tValue + 1)); - tValueToWrite <<= 1; - tValueToWrite |= 1; - } - write_uint(tValueToWrite); + template + void write_int(T tValue) { + typename lua_persist_int::T tValueToWrite; + if (tValue >= 0) { + tValueToWrite = (typename lua_persist_int::T)tValue; + tValueToWrite <<= 1; + } else { + tValueToWrite = (typename lua_persist_int::T)(-(tValue + 1)); + tValueToWrite <<= 1; + tValueToWrite |= 1; } + write_uint(tValueToWrite); + } - template - void write_float(T fValue) - { - write_byte_stream(reinterpret_cast(&fValue), sizeof(T)); - } + template + void write_float(T fValue) { + write_byte_stream(reinterpret_cast(&fValue), sizeof(T)); + } }; //! Interface used for depersisting Lua objects @@ -108,65 +100,55 @@ public: When userdata are depersisted, they get an instance of this interface for reading binary data and other Lua objects. */ -class lua_persist_reader -{ -public: - virtual ~lua_persist_reader() = default; +class lua_persist_reader { + public: + virtual ~lua_persist_reader() = default; - virtual lua_State* get_stack() = 0; - virtual bool read_stack_object() = 0; - virtual bool read_byte_stream(uint8_t *pBytes, size_t iCount) = 0; - virtual void set_error(const char *sError) = 0; + virtual lua_State* get_stack() = 0; + virtual bool read_stack_object() = 0; + virtual bool read_byte_stream(uint8_t* pBytes, size_t iCount) = 0; + virtual void set_error(const char* sError) = 0; - // Reads an integer previously written by lua_persist_writer::write_uint() - template - bool read_uint(T& tValue) - { - T tTemp(0); - uint8_t iByte; + // Reads an integer previously written by lua_persist_writer::write_uint() + template + bool read_uint(T& tValue) { + T tTemp(0); + uint8_t iByte; - while(true) - { - if(!read_byte_stream(&iByte, 1)) - return false; - if(iByte & 0x80) - { - tTemp = static_cast(tTemp | (iByte & 0x7F)); - tTemp = static_cast(tTemp << 7); - } - else - { - tTemp = static_cast(tTemp | iByte); - break; - } - } - - tValue = tTemp; - return true; + while (true) { + if (!read_byte_stream(&iByte, 1)) return false; + if (iByte & 0x80) { + tTemp = static_cast(tTemp | (iByte & 0x7F)); + tTemp = static_cast(tTemp << 7); + } else { + tTemp = static_cast(tTemp | iByte); + break; + } } - template - bool read_int(T& tValue) - { - typename lua_persist_int::T tWrittenValue; - if(!read_uint(tWrittenValue)) - return false; - if(tWrittenValue & 1) - tValue = (-(T)(tWrittenValue >> 1)) - 1; - else - tValue = static_cast(tWrittenValue >> 1); - return true; - } + tValue = tTemp; + return true; + } - template - bool read_float(T& fValue) - { - if(!read_byte_stream(reinterpret_cast(&fValue), sizeof(T))) - return false; - return true; - } + template + bool read_int(T& tValue) { + typename lua_persist_int::T tWrittenValue; + if (!read_uint(tWrittenValue)) return false; + if (tWrittenValue & 1) + tValue = (-(T)(tWrittenValue >> 1)) - 1; + else + tValue = static_cast(tWrittenValue >> 1); + return true; + } + + template + bool read_float(T& fValue) { + if (!read_byte_stream(reinterpret_cast(&fValue), sizeof(T))) + return false; + return true; + } }; -int luaopen_persist(lua_State *L); +int luaopen_persist(lua_State* L); -#endif // CORSIX_TH_PERSIST_LUA_H_ +#endif // CORSIX_TH_PERSIST_LUA_H_ diff --git a/CorsixTH/Src/random.c b/CorsixTH/Src/random.c index 681a659b..e28d2567 100644 --- a/CorsixTH/Src/random.c +++ b/CorsixTH/Src/random.c @@ -41,8 +41,8 @@ email: m-mat @ math.sci.hiroshima-u.ac.jp (remove space) */ -#include #include +#include #ifdef _MSC_VER typedef unsigned __int16 uint16_t; typedef unsigned __int32 uint32_t; @@ -57,16 +57,14 @@ typedef unsigned __int32 uint32_t; #define UPPER_MASK 0x80000000UL /* most significant w-r bits */ #define LOWER_MASK 0x7fffffffUL /* least significant r bits */ -uint32_t mt[N]; /* the array for the state vector */ -uint16_t mti=N+1; /* mti==N+1 means mt[N] is not initialized */ +uint32_t mt[N]; /* the array for the state vector */ +uint16_t mti = N + 1; /* mti==N+1 means mt[N] is not initialized */ /* initializes mt[N] with a seed */ -static void init_genrand(uint32_t s) -{ - mt[0]= s; - for (mti=1; mti> 30)) + mti); +static void init_genrand(uint32_t s) { + mt[0] = s; + for (mti = 1; mti < N; mti++) { + mt[mti] = (1812433253UL * (mt[mti - 1] ^ (mt[mti - 1] >> 30)) + mti); /* See Knuth TAOCP Vol2. 3rd Ed. P.106 for multiplier. */ /* In the previous versions, MSBs of the seed affect */ /* only MSBs of the array mt[]. */ @@ -74,28 +72,27 @@ static void init_genrand(uint32_t s) } /* generates a random number on [0,0xffffffff]-interval */ -static uint32_t genrand_int32(void) -{ +static uint32_t genrand_int32(void) { uint32_t y; - static uint32_t mag01[2]={0x0UL, MATRIX_A}; + static uint32_t mag01[2] = {0x0UL, MATRIX_A}; /* mag01[x] = x * MATRIX_A for x=0,1 */ if (mti >= N) { /* generate N words at one time */ int kk; - if (mti == N+1) /* if init_genrand() has not been called, */ + if (mti == N + 1) /* if init_genrand() has not been called, */ init_genrand(5489UL); /* a default initial seed is used */ - for (kk=0;kk> 1) ^ mag01[y & 0x1UL]; + for (kk = 0; kk < N - M; kk++) { + y = (mt[kk] & UPPER_MASK) | (mt[kk + 1] & LOWER_MASK); + mt[kk] = mt[kk + M] ^ (y >> 1) ^ mag01[y & 0x1UL]; } - for (;kk> 1) ^ mag01[y & 0x1UL]; + for (; kk < N - 1; kk++) { + y = (mt[kk] & UPPER_MASK) | (mt[kk + 1] & LOWER_MASK); + mt[kk] = mt[kk + (M - N)] ^ (y >> 1) ^ mag01[y & 0x1UL]; } - y = (mt[N-1]&UPPER_MASK)|(mt[0]&LOWER_MASK); - mt[N-1] = mt[M-1] ^ (y >> 1) ^ mag01[y & 0x1UL]; + y = (mt[N - 1] & UPPER_MASK) | (mt[0] & LOWER_MASK); + mt[N - 1] = mt[M - 1] ^ (y >> 1) ^ mag01[y & 0x1UL]; mti = 0; } @@ -112,10 +109,9 @@ static uint32_t genrand_int32(void) } /* generates a random number on [0,1) with 53-bit resolution*/ -static double genrand_res53(void) -{ - uint32_t a=genrand_int32()>>5, b=genrand_int32()>>6; - return(a*67108864.0+b)*(1.0/9007199254740992.0); +static double genrand_res53(void) { + uint32_t a = genrand_int32() >> 5, b = genrand_int32() >> 6; + return (a * 67108864.0 + b) * (1.0 / 9007199254740992.0); } /* These real versions are due to Isaku Wada, 2002/01/09 added */ @@ -129,27 +125,25 @@ static double genrand_res53(void) [1, range_end]. If called with two or more arguments, returns a random integer in the range [range_start, range_end]. */ -static int l_random(lua_State *L) -{ +static int l_random(lua_State* L) { lua_Integer min; uint32_t max; - switch(lua_gettop(L)) - { - default: - case 2: - min = luaL_checkinteger(L, 1); - max = (uint32_t)(luaL_checkinteger(L, 2) - min + 1); - luaL_argcheck(L, max > 0, 2, "interval is empty"); - lua_pushinteger(L, min + (lua_Integer)(genrand_int32() % max)); - break; - case 1: - max = (uint32_t)luaL_checkinteger(L, 1); - luaL_argcheck(L, 1 <= max, 1, "interval is empty"); - lua_pushinteger(L, 1 + (lua_Integer)(genrand_int32() % max)); - break; - case 0: - lua_pushnumber(L, (lua_Number)genrand_res53()); - break; + switch (lua_gettop(L)) { + default: + case 2: + min = luaL_checkinteger(L, 1); + max = (uint32_t)(luaL_checkinteger(L, 2) - min + 1); + luaL_argcheck(L, max > 0, 2, "interval is empty"); + lua_pushinteger(L, min + (lua_Integer)(genrand_int32() % max)); + break; + case 1: + max = (uint32_t)luaL_checkinteger(L, 1); + luaL_argcheck(L, 1 <= max, 1, "interval is empty"); + lua_pushinteger(L, 1 + (lua_Integer)(genrand_int32() % max)); + break; + case 0: + lua_pushnumber(L, (lua_Number)genrand_res53()); + break; } return 1; } @@ -162,8 +156,7 @@ static int l_random(lua_State *L) Returns a string which can later be passed to math.randomseed() to restore the random number generator to its current state. */ -static int l_randomdump(lua_State *L) -{ +static int l_randomdump(lua_State* L) { lua_pushlstring(L, (const char*)mt, N * 4); lua_pushlstring(L, (const char*)&mti, 2); lua_concat(L, 2); @@ -180,28 +173,21 @@ static int l_randomdump(lua_State *L) the random number generator state from a string previously generated from math.randomdump(). */ -static int l_randomseed(lua_State *L) -{ - if(lua_type(L, 1) == LUA_TSTRING) - { +static int l_randomseed(lua_State* L) { + if (lua_type(L, 1) == LUA_TSTRING) { int i; size_t len; - const char *data = lua_tolstring(L, 1, &len); - if(len != N * 4 + 2) - luaL_argerror(L, 1, "Seed string wrong length"); - for(i = 0; i < N; ++i) - mt[i] = ((uint32_t*)data)[i]; + const char* data = lua_tolstring(L, 1, &len); + if (len != N * 4 + 2) luaL_argerror(L, 1, "Seed string wrong length"); + for (i = 0; i < N; ++i) mt[i] = ((uint32_t*)data)[i]; mti = *(uint16_t*)(data + len - 2); - } - else - { + } else { init_genrand((uint32_t)luaL_checkinteger(L, 1)); } return 0; } -int luaopen_random(lua_State *L) -{ +int luaopen_random(lua_State* L) { lua_getglobal(L, "math"); lua_pushliteral(L, "random"); lua_pushcfunction(L, l_random); diff --git a/CorsixTH/Src/run_length_encoder.cpp b/CorsixTH/Src/run_length_encoder.cpp index edc2497d..f920cc46 100644 --- a/CorsixTH/Src/run_length_encoder.cpp +++ b/CorsixTH/Src/run_length_encoder.cpp @@ -21,307 +21,258 @@ SOFTWARE. */ #include "run_length_encoder.h" -#include "persist_lua.h" -#include #include +#include +#include "persist_lua.h" -integer_run_length_encoder::integer_run_length_encoder() -{ - buffer = nullptr; - output = nullptr; - clean(); +integer_run_length_encoder::integer_run_length_encoder() { + buffer = nullptr; + output = nullptr; + clean(); } -integer_run_length_encoder::~integer_run_length_encoder() -{ - clean(); +integer_run_length_encoder::~integer_run_length_encoder() { clean(); } + +void integer_run_length_encoder::clean() { + delete[] buffer; + delete[] output; + buffer = nullptr; + output = nullptr; + record_size = 0; + buffer_capacity = 0; + buffer_size = 0; + buffer_offset = 0; + output_capacity = 0; + output_size = 0; + object_size = 0; + object_copies = 0; } -void integer_run_length_encoder::clean() -{ - delete[] buffer; +bool integer_run_length_encoder::initialise(size_t iRecordSize) { + clean(); + record_size = iRecordSize; + + // Buffer must hold at least 7 + 2 * 8 records, as the maximum object size + // is 8 records, 2 of which are needed to detect a repeat, and 7 for the + // offset at which the objects are found. + buffer_capacity = iRecordSize * 8 * 4; + buffer_size = 0; + buffer_offset = 0; + buffer = new (std::nothrow) uint32_t[buffer_capacity]; + if (!buffer) return false; + + output_capacity = iRecordSize * 32; + output_size = 0; + output = new (std::nothrow) uint32_t[output_capacity]; + if (!output) return false; + + object_size = 0; + object_copies = 0; + + return true; +} + +void integer_run_length_encoder::write(uint32_t iValue) { + buffer[(buffer_offset + buffer_size) % buffer_capacity] = iValue; + if (++buffer_size == buffer_capacity) flush(false); +} + +void integer_run_length_encoder::finish() { + if (buffer_size != 0) flush(true); +} + +void integer_run_length_encoder::flush(bool bAll) { + do { + if (object_size == 0) { + // Decide on the size of the next object + // Want the object size which gives most object repeats, then for + // two sizes with the same repeat count, the smaller size. + size_t iBestRepeats = 0; + size_t iBestSize = 0; + size_t iBestOffset = 0; + for (size_t iNumRecords = 1; iNumRecords <= 8; ++iNumRecords) { + for (size_t iOffset = 0; iOffset < iNumRecords; ++iOffset) { + size_t iNumRepeats = 0; + size_t iObjSize = iNumRecords * record_size; + while (iObjSize * (iOffset + iNumRepeats + 1) <= buffer_size && + are_ranges_equal(0, iNumRepeats, iOffset, iObjSize)) { + ++iNumRepeats; + } + if (iNumRepeats > iBestRepeats || + (iNumRepeats == iBestRepeats && iObjSize < iBestSize)) { + iBestRepeats = iNumRepeats; + iBestSize = iObjSize; + iBestOffset = iOffset; + } + } + } + if (iBestRepeats == 1) { + // No repeats were found, so the best we can do is output + // a large non-repeating blob. + move_object_to_output(std::min(buffer_size, 8 * record_size), 1); + } else { + if (iBestOffset != 0) + move_object_to_output(iBestOffset * record_size, 1); + // Mark the object as the current one, and remove all but the + // last instance of it from the buffer. On the next flush, the + // new data might continue the same object, hence why the + // object isn't output just yet. + object_size = iBestSize; + object_copies = iBestRepeats - 1; + buffer_offset = + (buffer_offset + object_size * object_copies) % buffer_capacity; + buffer_size -= object_size * object_copies; + } + } else { + // Try to match more of the current object + while (object_size * 2 <= buffer_size && + are_ranges_equal(0, 1, 0, object_size)) { + ++object_copies; + buffer_offset = (buffer_offset + object_size) % buffer_capacity; + buffer_size -= object_size; + } + // Write data + if (object_size * 2 <= buffer_size || bAll) { + move_object_to_output(object_size, object_copies + 1); + object_size = 0; + object_copies = 0; + } + } + } while (bAll && buffer_size != 0); +} + +bool integer_run_length_encoder::are_ranges_equal(size_t iObjIdx1, + size_t iObjIdx2, + size_t iOffset, + size_t iObjSize) const { + iObjIdx1 = buffer_offset + iOffset * record_size + iObjIdx1 * iObjSize; + iObjIdx2 = buffer_offset + iOffset * record_size + iObjIdx2 * iObjSize; + for (size_t i = 0; i < iObjSize; ++i) { + if (buffer[(iObjIdx1 + i) % buffer_capacity] != + buffer[(iObjIdx2 + i) % buffer_capacity]) { + return false; + } + } + return true; +} + +bool integer_run_length_encoder::move_object_to_output(size_t iObjSize, + size_t iObjCount) { + // Grow the output array if needed + if (output_capacity - output_size <= iObjSize) { + size_t iNewSize = (output_capacity + iObjSize) * 2; + uint32_t* pNewOutput = new (std::nothrow) uint32_t[iNewSize]; + if (!pNewOutput) return false; +#ifdef _MSC_VER +#pragma warning(disable : 4996) +#endif + std::copy(output, output + output_size, pNewOutput); +#ifdef _MSC_VER +#pragma warning(default : 4996) +#endif delete[] output; - buffer = nullptr; - output = nullptr; - record_size = 0; - buffer_capacity = 0; - buffer_size = 0; - buffer_offset = 0; - output_capacity = 0; - output_size = 0; - object_size = 0; - object_copies = 0; + output = pNewOutput; + output_capacity = iNewSize; + } + size_t iHeader = (iObjSize / record_size - 1) + 8 * (iObjCount - 1); + output[output_size++] = static_cast(iHeader); + // Move the object from the buffer to the output + for (size_t i = 0; i < iObjSize; ++i) { + output[output_size++] = buffer[buffer_offset]; + buffer_offset = (buffer_offset + 1) % buffer_capacity; + } + buffer_size -= iObjSize; + return true; } -bool integer_run_length_encoder::initialise(size_t iRecordSize) -{ - clean(); - record_size = iRecordSize; - - // Buffer must hold at least 7 + 2 * 8 records, as the maximum object size - // is 8 records, 2 of which are needed to detect a repeat, and 7 for the - // offset at which the objects are found. - buffer_capacity = iRecordSize * 8 * 4; - buffer_size = 0; - buffer_offset = 0; - buffer = new (std::nothrow) uint32_t[buffer_capacity]; - if(!buffer) - return false; - - output_capacity = iRecordSize * 32; - output_size = 0; - output = new (std::nothrow) uint32_t[output_capacity]; - if(!output) - return false; - - object_size = 0; - object_copies = 0; - - return true; +uint32_t* integer_run_length_encoder::get_output(size_t* pCount) const { + if (pCount) *pCount = output_size; + return output; } -void integer_run_length_encoder::write(uint32_t iValue) -{ - buffer[(buffer_offset + buffer_size) % buffer_capacity] = iValue; - if(++buffer_size == buffer_capacity) - flush(false); +void integer_run_length_encoder::pump_output( + lua_persist_writer* pWriter) const { + pWriter->write_uint(output_size); + for (size_t i = 0; i < output_size; ++i) { + pWriter->write_uint(output[i]); + } } -void integer_run_length_encoder::finish() -{ - if(buffer_size != 0) - flush(true); +integer_run_length_decoder::integer_run_length_decoder() { + buffer = nullptr; + clean(); } -void integer_run_length_encoder::flush(bool bAll) -{ - do - { - if(object_size == 0) - { - // Decide on the size of the next object - // Want the object size which gives most object repeats, then for - // two sizes with the same repeat count, the smaller size. - size_t iBestRepeats = 0; - size_t iBestSize = 0; - size_t iBestOffset = 0; - for(size_t iNumRecords = 1; iNumRecords <= 8; ++iNumRecords) - { - for(size_t iOffset = 0; iOffset < iNumRecords; ++iOffset) - { - size_t iNumRepeats = 0; - size_t iObjSize = iNumRecords * record_size; - while(iObjSize * (iOffset + iNumRepeats + 1) <= buffer_size - && are_ranges_equal(0, iNumRepeats, iOffset, iObjSize)) - { - ++iNumRepeats; - } - if(iNumRepeats > iBestRepeats - ||(iNumRepeats == iBestRepeats && iObjSize < iBestSize)) - { - iBestRepeats = iNumRepeats; - iBestSize = iObjSize; - iBestOffset = iOffset; - } - } - } - if(iBestRepeats == 1) - { - // No repeats were found, so the best we can do is output - // a large non-repeating blob. - move_object_to_output(std::min(buffer_size, 8 * record_size), 1); - } - else - { - if(iBestOffset != 0) - move_object_to_output(iBestOffset * record_size, 1); - // Mark the object as the current one, and remove all but the - // last instance of it from the buffer. On the next flush, the - // new data might continue the same object, hence why the - // object isn't output just yet. - object_size = iBestSize; - object_copies = iBestRepeats - 1; - buffer_offset = (buffer_offset + object_size * object_copies) % buffer_capacity; - buffer_size -= object_size * object_copies; - } - } - else - { - // Try to match more of the current object - while(object_size * 2 <= buffer_size && - are_ranges_equal(0, 1, 0, object_size)) - { - ++object_copies; - buffer_offset = (buffer_offset + object_size) % buffer_capacity; - buffer_size -= object_size; - } - // Write data - if(object_size * 2 <= buffer_size || bAll) - { - move_object_to_output(object_size, object_copies + 1); - object_size = 0; - object_copies = 0; - } - } - } while(bAll && buffer_size != 0); +integer_run_length_decoder::~integer_run_length_decoder() { clean(); } + +void integer_run_length_decoder::clean() { + delete[] buffer; + buffer = nullptr; + reader = nullptr; + input = nullptr; + input_end = nullptr; + reads_remaining = 0; + object_copies = 0; + record_size = 0; + object_index = 0; + object_size = 0; } -bool integer_run_length_encoder::are_ranges_equal(size_t iObjIdx1, size_t iObjIdx2, - size_t iOffset, size_t iObjSize) const -{ - iObjIdx1 = buffer_offset + iOffset * record_size + iObjIdx1 * iObjSize; - iObjIdx2 = buffer_offset + iOffset * record_size + iObjIdx2 * iObjSize; - for(size_t i = 0; i < iObjSize; ++i) - { - if(buffer[(iObjIdx1 + i) % buffer_capacity] - != buffer[(iObjIdx2 + i) % buffer_capacity]) - { - return false; - } +bool integer_run_length_decoder::initialise(size_t iRecordSize, + lua_persist_reader* pReader) { + clean(); + + buffer = new (std::nothrow) uint32_t[9 * iRecordSize]; + if (!buffer) return false; + reader = pReader; + record_size = iRecordSize; + return pReader->read_uint(reads_remaining); +} + +bool integer_run_length_decoder::initialise(size_t iRecordSize, + const uint32_t* pInput, + size_t iCount) { + clean(); + + buffer = new (std::nothrow) uint32_t[9 * iRecordSize]; + if (!buffer) return false; + input = pInput; + input_end = pInput + iCount; + record_size = iRecordSize; + return true; +} + +uint32_t integer_run_length_decoder::read() { + if (object_copies == 0) { + uint32_t iHeader = 0; + if (reader) { + reader->read_uint(iHeader); + --reads_remaining; + } else + iHeader = *(input++); + object_size = record_size * (1 + (iHeader & 7)); + object_copies = (iHeader / 8) + 1; + if (reader) { + for (size_t i = 0; i < object_size; ++i) { + reader->read_uint(buffer[i]); + --reads_remaining; + } + } else { + for (size_t i = 0; i < object_size; ++i) buffer[i] = *(input++); } - return true; -} + } -bool integer_run_length_encoder::move_object_to_output(size_t iObjSize, size_t iObjCount) -{ - // Grow the output array if needed - if(output_capacity - output_size <= iObjSize) - { - size_t iNewSize = (output_capacity + iObjSize) * 2; - uint32_t *pNewOutput = new (std::nothrow) uint32_t[iNewSize]; - if(!pNewOutput) - return false; -#ifdef _MSC_VER -#pragma warning(disable: 4996) -#endif - std::copy(output, output + output_size, pNewOutput); -#ifdef _MSC_VER -#pragma warning(default: 4996) -#endif - delete[] output; - output = pNewOutput; - output_capacity = iNewSize; - } - size_t iHeader = (iObjSize / record_size - 1) + 8 * (iObjCount - 1); - output[output_size++] = static_cast(iHeader); - // Move the object from the buffer to the output - for(size_t i = 0; i < iObjSize; ++i) - { - output[output_size++] = buffer[buffer_offset]; - buffer_offset = (buffer_offset + 1) % buffer_capacity; - } - buffer_size -= iObjSize; - return true; -} - -uint32_t* integer_run_length_encoder::get_output(size_t *pCount) const -{ - if(pCount) - *pCount = output_size; - return output; -} - -void integer_run_length_encoder::pump_output(lua_persist_writer *pWriter) const -{ - pWriter->write_uint(output_size); - for(size_t i = 0; i < output_size; ++i) - { - pWriter->write_uint(output[i]); - } -} - -integer_run_length_decoder::integer_run_length_decoder() -{ - buffer = nullptr; - clean(); -} - -integer_run_length_decoder::~integer_run_length_decoder() -{ - clean(); -} - -void integer_run_length_decoder::clean() -{ - delete[] buffer; - buffer = nullptr; - reader = nullptr; - input = nullptr; - input_end = nullptr; - reads_remaining = 0; - object_copies = 0; - record_size = 0; + uint32_t iValue = buffer[object_index]; + if (++object_index == object_size) { object_index = 0; - object_size = 0; + --object_copies; + } + return iValue; } -bool integer_run_length_decoder::initialise(size_t iRecordSize, lua_persist_reader *pReader) -{ - clean(); - - buffer = new (std::nothrow) uint32_t[9 * iRecordSize]; - if(!buffer) - return false; - reader = pReader; - record_size = iRecordSize; - return pReader->read_uint(reads_remaining); -} - -bool integer_run_length_decoder::initialise(size_t iRecordSize, const uint32_t *pInput, size_t iCount) -{ - clean(); - - buffer = new (std::nothrow) uint32_t[9 * iRecordSize]; - if(!buffer) - return false; - input = pInput; - input_end = pInput + iCount; - record_size = iRecordSize; - return true; -} - -uint32_t integer_run_length_decoder::read() -{ - if(object_copies == 0) - { - uint32_t iHeader = 0; - if(reader) - { - reader->read_uint(iHeader); - --reads_remaining; - } - else - iHeader = *(input++); - object_size = record_size * (1 + (iHeader & 7)); - object_copies = (iHeader / 8) + 1; - if(reader) - { - for(size_t i = 0; i < object_size; ++i) - { - reader->read_uint(buffer[i]); - --reads_remaining; - } - } - else - { - for(size_t i = 0; i < object_size; ++i) - buffer[i] = *(input++); - } - } - - uint32_t iValue = buffer[object_index]; - if(++object_index == object_size) - { - object_index = 0; - --object_copies; - } - return iValue; -} - -bool integer_run_length_decoder::is_finished() const -{ - if(reader) - return reads_remaining == 0 && object_copies == 0; - else - return input == input_end && object_copies == 0; +bool integer_run_length_decoder::is_finished() const { + if (reader) + return reads_remaining == 0 && object_copies == 0; + else + return input == input_end && object_copies == 0; } diff --git a/CorsixTH/Src/run_length_encoder.h b/CorsixTH/Src/run_length_encoder.h index 678beebc..8fdafbe9 100644 --- a/CorsixTH/Src/run_length_encoder.h +++ b/CorsixTH/Src/run_length_encoder.h @@ -39,96 +39,94 @@ class lua_persist_writer; * Object - One or more records. Each object has an associated repeat count. */ -class integer_run_length_encoder -{ -public: - integer_run_length_encoder(); - ~integer_run_length_encoder(); +class integer_run_length_encoder { + public: + integer_run_length_encoder(); + ~integer_run_length_encoder(); - //! (Re-)initialise the encoder - /*! - Prepares the encoder for accepting a sequence of records. + //! (Re-)initialise the encoder + /*! + Prepares the encoder for accepting a sequence of records. - \param iRecordSize The number of integers in a record. - */ - bool initialise(size_t iRecordSize); + \param iRecordSize The number of integers in a record. + */ + bool initialise(size_t iRecordSize); - //! Supply the next integer in the input sequence to the encoder - void write(uint32_t iValue); + //! Supply the next integer in the input sequence to the encoder + void write(uint32_t iValue); - //! Inform the encoder that the input sequence has finished - /*! - finish() must be called prior to getOutput() or pumpOutput(). - write() must not be called after finish() has been called. - */ - void finish(); + //! Inform the encoder that the input sequence has finished + /*! + finish() must be called prior to getOutput() or pumpOutput(). + write() must not be called after finish() has been called. + */ + void finish(); - uint32_t* get_output(size_t *pCount) const; - void pump_output(lua_persist_writer *pWriter) const; + uint32_t* get_output(size_t* pCount) const; + void pump_output(lua_persist_writer* pWriter) const; -private: - void clean(); + private: + void clean(); - //! Reduce the amount of data in the buffer - /*! - \param bAll If true, will reduce buffer_size to zero. - If false, will reduce buffer_size by some amount. - */ - void flush(bool bAll); + //! Reduce the amount of data in the buffer + /*! + \param bAll If true, will reduce buffer_size to zero. + If false, will reduce buffer_size by some amount. + */ + void flush(bool bAll); - bool are_ranges_equal(size_t iObjIdx1, size_t iObjIdx2, size_t iOffset, size_t iObjSize) const; - bool move_object_to_output(size_t iObjSize, size_t iObjCount); + bool are_ranges_equal(size_t iObjIdx1, size_t iObjIdx2, size_t iOffset, + size_t iObjSize) const; + bool move_object_to_output(size_t iObjSize, size_t iObjCount); - //! A circular fixed-size buffer holding the most recent input - uint32_t* buffer; - //! A variable-length array holding the output sequence - uint32_t* output; - //! The number of integers in a record - size_t record_size; - //! The maximum number of integers stored in the buffer - size_t buffer_capacity; - //! The current number of integers stored in the buffer - size_t buffer_size; - //! The index into buffer of the 1st integer - size_t buffer_offset; - //! The maximum number of integers storable in the output (before the - //! output array has to be resized). - size_t output_capacity; - //! The current number of integers stored in the output - size_t output_size; - //! The number of integers in the current object (multiple of record size) - size_t object_size; - //! The number of copies of the current object already seen and removed - //! from the buffer. - size_t object_copies; + //! A circular fixed-size buffer holding the most recent input + uint32_t* buffer; + //! A variable-length array holding the output sequence + uint32_t* output; + //! The number of integers in a record + size_t record_size; + //! The maximum number of integers stored in the buffer + size_t buffer_capacity; + //! The current number of integers stored in the buffer + size_t buffer_size; + //! The index into buffer of the 1st integer + size_t buffer_offset; + //! The maximum number of integers storable in the output (before the + //! output array has to be resized). + size_t output_capacity; + //! The current number of integers stored in the output + size_t output_size; + //! The number of integers in the current object (multiple of record size) + size_t object_size; + //! The number of copies of the current object already seen and removed + //! from the buffer. + size_t object_copies; }; -class integer_run_length_decoder -{ -public: - integer_run_length_decoder(); - ~integer_run_length_decoder(); +class integer_run_length_decoder { + public: + integer_run_length_decoder(); + ~integer_run_length_decoder(); - bool initialise(size_t iRecordSize, lua_persist_reader *pReader); - bool initialise(size_t iRecordSize, const uint32_t *pInput, size_t iCount); - uint32_t read(); - bool is_finished() const; + bool initialise(size_t iRecordSize, lua_persist_reader* pReader); + bool initialise(size_t iRecordSize, const uint32_t* pInput, size_t iCount); + uint32_t read(); + bool is_finished() const; -private: - void clean(); + private: + void clean(); - uint32_t* buffer; - lua_persist_reader* reader; - const uint32_t* input; - union - { - const uint32_t* input_end; - size_t reads_remaining; - }; - size_t object_copies; - size_t record_size; - size_t object_index; - size_t object_size; + uint32_t* buffer; + lua_persist_reader* reader; + const uint32_t* input; + union { + const uint32_t* input_end; + size_t reads_remaining; + }; + size_t object_copies; + size_t record_size; + size_t object_index; + size_t object_size; }; -#endif // CORSIX_TH_RLE_H_ +#endif // CORSIX_TH_RLE_H_ diff --git a/CorsixTH/Src/sdl_audio.cpp b/CorsixTH/Src/sdl_audio.cpp index d565ba01..e7e03f7f 100644 --- a/CorsixTH/Src/sdl_audio.cpp +++ b/CorsixTH/Src/sdl_audio.cpp @@ -21,325 +21,295 @@ SOFTWARE. */ #include "config.h" -#include "lua_sdl.h" #ifdef CORSIX_TH_USE_SDL_MIXER +#include +#include +#include +#include "lua_sdl.h" #include "th_lua.h" #include "xmi2mid.h" -#include #ifdef _MSC_VER #pragma comment(lib, "SDL2_mixer") #endif -#include -#include -class music -{ -public: - Mix_Music* pMusic; +class music { + public: + Mix_Music* pMusic; - music() - { - pMusic = nullptr; - } - - ~music() - { - if(pMusic) - { - Mix_FreeMusic(pMusic); - pMusic = nullptr; - } + music() { pMusic = nullptr; } + + ~music() { + if (pMusic) { + Mix_FreeMusic(pMusic); + pMusic = nullptr; } + } }; namespace { -void audio_music_over_callback() -{ - SDL_Event e; - e.type = SDL_USEREVENT_MUSIC_OVER; - SDL_PushEvent(&e); +void audio_music_over_callback() { + SDL_Event e; + e.type = SDL_USEREVENT_MUSIC_OVER; + SDL_PushEvent(&e); } -int l_init(lua_State *L) -{ - if(Mix_OpenAudio(static_cast(luaL_optinteger(L, 1, MIX_DEFAULT_FREQUENCY)), - MIX_DEFAULT_FORMAT, - static_cast(luaL_optinteger(L, 2, MIX_DEFAULT_CHANNELS)), - static_cast(luaL_optinteger(L, 3, 2048)) /* chunk size */) != 0) - { - lua_pushboolean(L, 0); - lua_pushstring(L, Mix_GetError()); - return 2; - } - else - { - lua_pushboolean(L, 1); - Mix_HookMusicFinished(audio_music_over_callback); - return 1; - } +int l_init(lua_State* L) { + constexpr int chunk_size = 2048; + + int err = Mix_OpenAudio( + static_cast(luaL_optinteger(L, 1, MIX_DEFAULT_FREQUENCY)), + MIX_DEFAULT_FORMAT, + static_cast(luaL_optinteger(L, 2, MIX_DEFAULT_CHANNELS)), + static_cast(luaL_optinteger(L, 3, chunk_size))); + if (err != 0) { + lua_pushboolean(L, 0); + lua_pushstring(L, Mix_GetError()); + return 2; + } else { + lua_pushboolean(L, 1); + Mix_HookMusicFinished(audio_music_over_callback); + return 1; + } } -struct load_music_async_data -{ - lua_State* L; - Mix_Music* music; - SDL_RWops* rwop; - char* err; - SDL_Thread* thread; +struct load_music_async_data { + lua_State* L; + Mix_Music* music; + SDL_RWops* rwop; + char* err; + SDL_Thread* thread; }; -int load_music_async_thread(void* arg) -{ - load_music_async_data *async = (load_music_async_data*)arg; +int load_music_async_thread(void* arg) { + load_music_async_data* async = (load_music_async_data*)arg; - async->music = Mix_LoadMUS_RW(async->rwop, 1); - async->rwop = nullptr; - if(async->music == nullptr) - { - size_t iLen = std::strlen(Mix_GetError()) + 1; - async->err = (char*)malloc(iLen); - std::memcpy(async->err, Mix_GetError(), iLen); - } - SDL_Event e; - e.type = SDL_USEREVENT_MUSIC_LOADED; - e.user.data1 = arg; - SDL_PushEvent(&e); - return 0; + async->music = Mix_LoadMUS_RW(async->rwop, 1); + async->rwop = nullptr; + if (async->music == nullptr) { + size_t iLen = std::strlen(Mix_GetError()) + 1; + async->err = (char*)malloc(iLen); + std::memcpy(async->err, Mix_GetError(), iLen); + } + SDL_Event e; + e.type = SDL_USEREVENT_MUSIC_LOADED; + e.user.data1 = arg; + SDL_PushEvent(&e); + return 0; } -int l_load_music_async(lua_State *L) -{ - size_t iLength; - const uint8_t *pData = luaT_checkfile(L, 1, &iLength); - luaL_checktype(L, 2, LUA_TFUNCTION); - SDL_RWops* rwop = SDL_RWFromConstMem(pData, (int)iLength); - lua_settop(L, 2); +int l_load_music_async(lua_State* L) { + size_t iLength; + const uint8_t* pData = luaT_checkfile(L, 1, &iLength); + luaL_checktype(L, 2, LUA_TFUNCTION); + SDL_RWops* rwop = SDL_RWFromConstMem(pData, (int)iLength); + lua_settop(L, 2); - load_music_async_data *async = luaT_new(L); - lua_pushlightuserdata(L, async); - lua_pushvalue(L, -2); - lua_settable(L, LUA_REGISTRYINDEX); - async->L = L; - async->music = nullptr; - async->rwop = rwop; - async->err = nullptr; - lua_createtable(L, 2, 0); - lua_pushvalue(L, 2); - lua_rawseti(L, -2, 1); - luaT_stdnew(L, luaT_environindex, true); - lua_pushvalue(L, 1); - luaT_setenvfield(L, -2, "data"); - lua_rawseti(L, -2, 2); - lua_settable(L, LUA_REGISTRYINDEX); + load_music_async_data* async = luaT_new(L); + lua_pushlightuserdata(L, async); + lua_pushvalue(L, -2); + lua_settable(L, LUA_REGISTRYINDEX); + async->L = L; + async->music = nullptr; + async->rwop = rwop; + async->err = nullptr; + lua_createtable(L, 2, 0); + lua_pushvalue(L, 2); + lua_rawseti(L, -2, 1); + luaT_stdnew(L, luaT_environindex, true); + lua_pushvalue(L, 1); + luaT_setenvfield(L, -2, "data"); + lua_rawseti(L, -2, 2); + lua_settable(L, LUA_REGISTRYINDEX); - /* - In registry: - [light userdata async] -> [full userdata async] - [full userdata async] -> { - [1] = callback_function, - [2] = empty music_t userdata, - } - - New thread will load music, and inform the main loop, which will then - call the callback and remove the new entries from the registry. - */ - - async->thread = SDL_CreateThread(load_music_async_thread, "music_thread", async); - - return 0; -} - -int l_load_music(lua_State *L) -{ - size_t iLength; - const uint8_t *pData = luaT_checkfile(L, 1, &iLength); - SDL_RWops* rwop = SDL_RWFromConstMem(pData, (int)iLength); - Mix_Music* pMusic = Mix_LoadMUS_RW(rwop, 1); - if(pMusic == nullptr) - { - lua_pushnil(L); - lua_pushstring(L, Mix_GetError()); - return 2; - } - music* pLMusic = luaT_stdnew(L, luaT_environindex, true); - pLMusic->pMusic = pMusic; - lua_pushvalue(L, 1); - luaT_setenvfield(L, -2, "data"); - return 1; -} - -int l_music_volume(lua_State *L) -{ - lua_Number fValue = luaL_checknumber(L, 1); - fValue = fValue * (lua_Number)MIX_MAX_VOLUME; - int iVolume = (int)(fValue + 0.5); - if(iVolume < 0) - iVolume = 0; - else if(iVolume > MIX_MAX_VOLUME) - iVolume = MIX_MAX_VOLUME; - Mix_VolumeMusic(iVolume); - return 0; -} - -int l_play_music(lua_State *L) -{ - music* pLMusic = luaT_testuserdata(L, -1); - if(Mix_PlayMusic(pLMusic->pMusic, static_cast(luaL_optinteger(L, 2, 1))) != 0) - { - lua_pushnil(L); - lua_pushstring(L, Mix_GetError()); - return 2; - } - lua_pushboolean(L, 1); - return 1; -} - -int l_pause_music(lua_State *L) -{ - Mix_PauseMusic(); - lua_pushboolean(L, Mix_PausedMusic() != 0 ? 1 : 0); - return 1; -} - -int l_resume_music(lua_State *L) -{ - Mix_ResumeMusic(); - lua_pushboolean(L, Mix_PausedMusic() == 0 ? 1 : 0); - return 1; -} - -int l_stop_music(lua_State *L) -{ - Mix_HaltMusic(); - return 0; -} - -int l_transcode_xmi(lua_State *L) -{ - size_t iLength, iMidLength; - const uint8_t *pData = luaT_checkfile(L, 1, &iLength); - - uint8_t *pMidData = transcode_xmi_to_midi(pData, iLength, &iMidLength); - if(pMidData == nullptr) - { - lua_pushnil(L); - lua_pushliteral(L, "Unable to transcode XMI to MIDI"); - return 2; - } - lua_pushlstring(L, (const char*)pMidData, iMidLength); - delete[] pMidData; - - return 1; -} - -constexpr std::array sdl_audiolib {{ - {"init", l_init}, - {"transcodeXmiToMid", l_transcode_xmi}, - {nullptr, nullptr} -}}; - -constexpr std::array sdl_musiclib {{ - {"loadMusic", l_load_music}, - {"loadMusicAsync", l_load_music_async}, - {"playMusic", l_play_music}, - {"stopMusic", l_stop_music}, - {"pauseMusic", l_pause_music}, - {"resumeMusic", l_resume_music}, - {"setMusicVolume", l_music_volume}, - {nullptr, nullptr} -}}; - -} // namespace - -int l_load_music_async_callback(lua_State *L) -{ - load_music_async_data *async = (load_music_async_data*)lua_touserdata(L, 1); - - // Frees resources allocated to the thread - SDL_WaitThread(async->thread, nullptr); - - // Replace light UD with full UD - lua_pushvalue(L, 1); - lua_gettable(L, LUA_REGISTRYINDEX); - lua_insert(L, 1); - lua_pushnil(L); - lua_settable(L, LUA_REGISTRYINDEX); - - // Get CB function - lua_pushvalue(L, 1); - lua_gettable(L, LUA_REGISTRYINDEX); - lua_rawgeti(L, -1, 1); - - // Push CB arguments - int nargs = 1; - if(async->music == nullptr) - { - lua_pushnil(L); - if(async->err) - { - if(*async->err) - { - lua_pushstring(L, async->err); - nargs = 2; - } - free(async->err); + /* + In registry: + [light userdata async] -> [full userdata async] + [full userdata async] -> { + [1] = callback_function, + [2] = empty music_t userdata, } - } - else - { - lua_rawgeti(L, 2, 2); - music* pLMusic = (music*)lua_touserdata(L, -1); - pLMusic->pMusic = async->music; - async->music = nullptr; - } - // Finish cleanup - lua_pushvalue(L, 1); + New thread will load music, and inform the main loop, which will then + call the callback and remove the new entries from the registry. + */ + + async->thread = + SDL_CreateThread(load_music_async_thread, "music_thread", async); + + return 0; +} + +int l_load_music(lua_State* L) { + size_t iLength; + const uint8_t* pData = luaT_checkfile(L, 1, &iLength); + SDL_RWops* rwop = SDL_RWFromConstMem(pData, (int)iLength); + Mix_Music* pMusic = Mix_LoadMUS_RW(rwop, 1); + if (pMusic == nullptr) { lua_pushnil(L); - lua_settable(L, LUA_REGISTRYINDEX); - - // Callback - lua_call(L, nargs, 0); - - return 0; + lua_pushstring(L, Mix_GetError()); + return 2; + } + music* pLMusic = luaT_stdnew(L, luaT_environindex, true); + pLMusic->pMusic = pMusic; + lua_pushvalue(L, 1); + luaT_setenvfield(L, -2, "data"); + return 1; } -int luaopen_sdl_audio(lua_State *L) -{ - lua_newtable(L); - luaT_setfuncs(L, sdl_audiolib.data()); - lua_pushboolean(L, 1); - lua_setfield(L, -2, "loaded"); - - lua_createtable(L, 0, 2); - lua_pushvalue(L, -1); - lua_replace(L, luaT_environindex); - lua_pushvalue(L, luaT_environindex); - luaT_pushcclosure(L, luaT_stdgc, 1); - lua_setfield(L, -2, "__gc"); - lua_pushvalue(L, 1); - lua_setfield(L, -2, "__index"); - lua_pop(L, 1); - luaT_setfuncs(L, sdl_musiclib.data()); - - return 1; +int l_music_volume(lua_State* L) { + lua_Number fValue = luaL_checknumber(L, 1); + fValue = fValue * (lua_Number)MIX_MAX_VOLUME; + int iVolume = (int)(fValue + 0.5); + if (iVolume < 0) { + iVolume = 0; + } else if (iVolume > MIX_MAX_VOLUME) { + iVolume = MIX_MAX_VOLUME; + } + Mix_VolumeMusic(iVolume); + return 0; } -#else // CORSIX_TH_USE_SDL_MIXER - -int luaopen_sdl_audio(lua_State *L) -{ - lua_newtable(L); - lua_pushboolean(L, 0); - lua_setfield(L, -2, "loaded"); - - return 1; +int l_play_music(lua_State* L) { + music* pLMusic = luaT_testuserdata(L, -1); + int err = Mix_PlayMusic(pLMusic->pMusic, + static_cast(luaL_optinteger(L, 2, 1))); + if (err != 0) { + lua_pushnil(L); + lua_pushstring(L, Mix_GetError()); + return 2; + } + lua_pushboolean(L, 1); + return 1; } -int l_load_music_async_callback(lua_State *L) -{ - return 0; +int l_pause_music(lua_State* L) { + Mix_PauseMusic(); + lua_pushboolean(L, Mix_PausedMusic() != 0 ? 1 : 0); + return 1; } -#endif // CORSIX_TH_USE_SDL_MIXER +int l_resume_music(lua_State* L) { + Mix_ResumeMusic(); + lua_pushboolean(L, Mix_PausedMusic() == 0 ? 1 : 0); + return 1; +} + +int l_stop_music(lua_State* L) { + Mix_HaltMusic(); + return 0; +} + +int l_transcode_xmi(lua_State* L) { + size_t iLength, iMidLength; + const uint8_t* pData = luaT_checkfile(L, 1, &iLength); + + uint8_t* pMidData = transcode_xmi_to_midi(pData, iLength, &iMidLength); + if (pMidData == nullptr) { + lua_pushnil(L); + lua_pushliteral(L, "Unable to transcode XMI to MIDI"); + return 2; + } + lua_pushlstring(L, (const char*)pMidData, iMidLength); + delete[] pMidData; + + return 1; +} + +constexpr std::array sdl_audiolib{ + {{"init", l_init}, + {"transcodeXmiToMid", l_transcode_xmi}, + {nullptr, nullptr}}}; + +constexpr std::array sdl_musiclib{ + {{"loadMusic", l_load_music}, + {"loadMusicAsync", l_load_music_async}, + {"playMusic", l_play_music}, + {"stopMusic", l_stop_music}, + {"pauseMusic", l_pause_music}, + {"resumeMusic", l_resume_music}, + {"setMusicVolume", l_music_volume}, + {nullptr, nullptr}}}; + +} // namespace + +int l_load_music_async_callback(lua_State* L) { + load_music_async_data* async = (load_music_async_data*)lua_touserdata(L, 1); + + // Frees resources allocated to the thread + SDL_WaitThread(async->thread, nullptr); + + // Replace light UD with full UD + lua_pushvalue(L, 1); + lua_gettable(L, LUA_REGISTRYINDEX); + lua_insert(L, 1); + lua_pushnil(L); + lua_settable(L, LUA_REGISTRYINDEX); + + // Get CB function + lua_pushvalue(L, 1); + lua_gettable(L, LUA_REGISTRYINDEX); + lua_rawgeti(L, -1, 1); + + // Push CB arguments + int nargs = 1; + if (async->music == nullptr) { + lua_pushnil(L); + if (async->err) { + if (*async->err) { + lua_pushstring(L, async->err); + nargs = 2; + } + free(async->err); + } + } else { + lua_rawgeti(L, 2, 2); + music* pLMusic = (music*)lua_touserdata(L, -1); + pLMusic->pMusic = async->music; + async->music = nullptr; + } + + // Finish cleanup + lua_pushvalue(L, 1); + lua_pushnil(L); + lua_settable(L, LUA_REGISTRYINDEX); + + // Callback + lua_call(L, nargs, 0); + + return 0; +} + +int luaopen_sdl_audio(lua_State* L) { + lua_newtable(L); + luaT_setfuncs(L, sdl_audiolib.data()); + lua_pushboolean(L, 1); + lua_setfield(L, -2, "loaded"); + + lua_createtable(L, 0, 2); + lua_pushvalue(L, -1); + lua_replace(L, luaT_environindex); + lua_pushvalue(L, luaT_environindex); + luaT_pushcclosure(L, luaT_stdgc, 1); + lua_setfield(L, -2, "__gc"); + lua_pushvalue(L, 1); + lua_setfield(L, -2, "__index"); + lua_pop(L, 1); + luaT_setfuncs(L, sdl_musiclib.data()); + + return 1; +} + +#else // CORSIX_TH_USE_SDL_MIXER + +int luaopen_sdl_audio(lua_State* L) { + lua_newtable(L); + lua_pushboolean(L, 0); + lua_setfield(L, -2, "loaded"); + + return 1; +} + +int l_load_music_async_callback(lua_State* L) { return 0; } + +#endif // CORSIX_TH_USE_SDL_MIXER diff --git a/CorsixTH/Src/sdl_core.cpp b/CorsixTH/Src/sdl_core.cpp index 69e0e389..8011465e 100644 --- a/CorsixTH/Src/sdl_core.cpp +++ b/CorsixTH/Src/sdl_core.cpp @@ -21,386 +21,350 @@ SOFTWARE. */ #include "config.h" +#include +#include +#include #include "lua_sdl.h" #include "th_lua.h" -#include -#include -#include namespace { -int l_init(lua_State *L) -{ - Uint32 flags = 0; - int i; - int argc = lua_gettop(L); - for(i = 1; i <= argc; ++i) - { - const char* s = luaL_checkstring(L, i); - if(std::strcmp(s, "video") == 0) - flags |= SDL_INIT_VIDEO; - else if(std::strcmp(s, "audio") == 0) - flags |= SDL_INIT_AUDIO; - else if(std::strcmp(s, "timer") == 0) - flags |= SDL_INIT_TIMER; - else if(std::strcmp(s, "*") == 0) - flags |= SDL_INIT_EVERYTHING; - else - luaL_argerror(L, i, "Expected SDL part name"); - } - if(SDL_Init(flags) != 0) - { - std::fprintf(stderr, "SDL_Init failed: %s\n", SDL_GetError()); - lua_pushboolean(L, 0); - return 1; - } - - lua_pushboolean(L, 1); +int l_init(lua_State* L) { + Uint32 flags = 0; + int i; + int argc = lua_gettop(L); + for (i = 1; i <= argc; ++i) { + const char* s = luaL_checkstring(L, i); + if (std::strcmp(s, "video") == 0) + flags |= SDL_INIT_VIDEO; + else if (std::strcmp(s, "audio") == 0) + flags |= SDL_INIT_AUDIO; + else if (std::strcmp(s, "timer") == 0) + flags |= SDL_INIT_TIMER; + else if (std::strcmp(s, "*") == 0) + flags |= SDL_INIT_EVERYTHING; + else + luaL_argerror(L, i, "Expected SDL part name"); + } + if (SDL_Init(flags) != 0) { + std::fprintf(stderr, "SDL_Init failed: %s\n", SDL_GetError()); + lua_pushboolean(L, 0); return 1; + } + + lua_pushboolean(L, 1); + return 1; } -Uint32 timer_frame_callback(Uint32 interval, void *param) -{ - SDL_Event e; - e.type = SDL_USEREVENT_TICK; - SDL_PushEvent(&e); - return interval; +Uint32 timer_frame_callback(Uint32 interval, void* param) { + SDL_Event e; + e.type = SDL_USEREVENT_TICK; + SDL_PushEvent(&e); + return interval; } -class fps_ctrl -{ -public: - bool limit_fps; - bool track_fps; +class fps_ctrl { + public: + bool limit_fps; + bool track_fps; - int q_front; - int q_back; - int frame_count; - Uint32 frame_time[4096]; + int q_front; + int q_back; + int frame_count; + std::array frame_time; - void init() - { - limit_fps = true; - track_fps = true; - q_front = 0; - q_back = 0; - frame_count = 0; + void init() { + limit_fps = true; + track_fps = true; + q_front = 0; + q_back = 0; + frame_count = 0; + } + + void count_frame() { + Uint32 now = SDL_GetTicks(); + frame_time[q_front] = now; + q_front = (q_front + 1) % frame_time.size(); + if (q_front == q_back) { + q_back = (q_back + 1) % frame_time.size(); + } else { + ++frame_count; } - void count_frame() - { - Uint32 now = SDL_GetTicks(); - frame_time[q_front] = now; - q_front = (q_front + 1) % static_cast((sizeof(frame_time) / sizeof(*frame_time))); - if(q_front == q_back) - q_back = (q_back + 1) % static_cast((sizeof(frame_time) / sizeof(*frame_time))); - else - ++frame_count; - if(now < 1000) - now = 0; - else - now -= 1000; - while(frame_time[q_back] < now) - { - --frame_count; - q_back = (q_back + 1) % static_cast((sizeof(frame_time) / sizeof(*frame_time))); - } + if (now < 1000) { + now = 0; + } else { + now -= 1000; } + + while (frame_time[q_back] < now) { + --frame_count; + q_back = (q_back + 1) % frame_time.size(); + } + } }; -void l_push_modifiers_table(lua_State *L, Uint16 mod) -{ - lua_newtable(L); - if ((mod & KMOD_SHIFT) != 0) - { - luaT_pushtablebool(L, "shift", true); - } - if ((mod & KMOD_ALT) != 0) - { - luaT_pushtablebool(L, "alt", true); - } - if ((mod & KMOD_CTRL) != 0) - { - luaT_pushtablebool(L, "ctrl", true); - } - if ((mod & KMOD_GUI) != 0) - { - luaT_pushtablebool(L, "gui", true); - } - if ((mod & KMOD_NUM) != 0) - { - luaT_pushtablebool(L, "numlockactive", true); - } +void l_push_modifiers_table(lua_State* L, Uint16 mod) { + lua_newtable(L); + if ((mod & KMOD_SHIFT) != 0) { + luaT_pushtablebool(L, "shift", true); + } + if ((mod & KMOD_ALT) != 0) { + luaT_pushtablebool(L, "alt", true); + } + if ((mod & KMOD_CTRL) != 0) { + luaT_pushtablebool(L, "ctrl", true); + } + if ((mod & KMOD_GUI) != 0) { + luaT_pushtablebool(L, "gui", true); + } + if ((mod & KMOD_NUM) != 0) { + luaT_pushtablebool(L, "numlockactive", true); + } } -int l_get_key_modifiers(lua_State *L) -{ - l_push_modifiers_table(L, SDL_GetModState()); - return 1; +int l_get_key_modifiers(lua_State* L) { + l_push_modifiers_table(L, SDL_GetModState()); + return 1; } -int l_mainloop(lua_State *L) -{ - luaL_checktype(L, 1, LUA_TTHREAD); - lua_State *dispatcher = lua_tothread(L, 1); +int l_mainloop(lua_State* L) { + luaL_checktype(L, 1, LUA_TTHREAD); + lua_State* dispatcher = lua_tothread(L, 1); - fps_ctrl *fps_control = (fps_ctrl*)lua_touserdata(L, luaT_upvalueindex(1)); - SDL_TimerID timer = SDL_AddTimer(30, timer_frame_callback, nullptr); - SDL_Event e; + fps_ctrl* fps_control = (fps_ctrl*)lua_touserdata(L, luaT_upvalueindex(1)); + SDL_TimerID timer = SDL_AddTimer(30, timer_frame_callback, nullptr); + SDL_Event e; - while(SDL_WaitEvent(&e) != 0) - { - bool do_frame = false; - bool do_timer = false; - do - { - int nargs; - switch(e.type) - { - case SDL_QUIT: - goto leave_loop; - case SDL_KEYDOWN: - lua_pushliteral(dispatcher, "keydown"); - lua_pushstring(dispatcher, SDL_GetKeyName(e.key.keysym.sym)); - l_push_modifiers_table(dispatcher, e.key.keysym.mod); - lua_pushboolean(dispatcher, e.key.repeat != 0); - nargs = 4; - break; - case SDL_KEYUP: - lua_pushliteral(dispatcher, "keyup"); - lua_pushstring(dispatcher, SDL_GetKeyName(e.key.keysym.sym)); - nargs = 2; - break; - case SDL_TEXTINPUT: - lua_pushliteral(dispatcher, "textinput"); - lua_pushstring(dispatcher, e.text.text); - nargs = 2; - break; - case SDL_TEXTEDITING: - lua_pushliteral(dispatcher, "textediting"); - lua_pushstring(dispatcher, e.edit.text); - lua_pushinteger(dispatcher, e.edit.start); - lua_pushinteger(dispatcher, e.edit.length); - nargs = 4; - break; - case SDL_MOUSEBUTTONDOWN: - lua_pushliteral(dispatcher, "buttondown"); - lua_pushinteger(dispatcher, e.button.button); - lua_pushinteger(dispatcher, e.button.x); - lua_pushinteger(dispatcher, e.button.y); - nargs = 4; - break; - case SDL_MOUSEBUTTONUP: - lua_pushliteral(dispatcher, "buttonup"); - lua_pushinteger(dispatcher, e.button.button); - lua_pushinteger(dispatcher, e.button.x); - lua_pushinteger(dispatcher, e.button.y); - nargs = 4; - break; - case SDL_MOUSEWHEEL: - lua_pushliteral(dispatcher, "mousewheel"); - lua_pushinteger(dispatcher, e.wheel.x); - lua_pushinteger(dispatcher, e.wheel.y); - nargs = 3; - break; - case SDL_MOUSEMOTION: - lua_pushliteral(dispatcher, "motion"); - lua_pushinteger(dispatcher, e.motion.x); - lua_pushinteger(dispatcher, e.motion.y); - lua_pushinteger(dispatcher, e.motion.xrel); - lua_pushinteger(dispatcher, e.motion.yrel); - nargs = 5; - break; - case SDL_MULTIGESTURE: - lua_pushliteral(dispatcher, "multigesture"); - lua_pushinteger(dispatcher, e.mgesture.numFingers); - lua_pushnumber(dispatcher, e.mgesture.dTheta); - lua_pushnumber(dispatcher, e.mgesture.dDist); - lua_pushnumber(dispatcher, e.mgesture.x); - lua_pushnumber(dispatcher, e.mgesture.y); - nargs = 6; - break; - case SDL_WINDOWEVENT: - switch (e.window.event) { - case SDL_WINDOWEVENT_FOCUS_GAINED: - lua_pushliteral(dispatcher, "active"); - lua_pushinteger(dispatcher, 1); - nargs = 2; - break; - case SDL_WINDOWEVENT_FOCUS_LOST: - lua_pushliteral(dispatcher, "active"); - lua_pushinteger(dispatcher, 0); - nargs = 2; - break; - case SDL_WINDOWEVENT_SIZE_CHANGED: - lua_pushliteral(dispatcher, "window_resize"); - lua_pushinteger(dispatcher, e.window.data1); - lua_pushinteger(dispatcher, e.window.data2); - nargs = 3; - break; - default: - nargs = 0; - break; - } - break; - case SDL_USEREVENT_MUSIC_OVER: - lua_pushliteral(dispatcher, "music_over"); - nargs = 1; - break; - case SDL_USEREVENT_MUSIC_LOADED: - if(luaT_cpcall(L, (lua_CFunction)l_load_music_async_callback, e.user.data1)) - { - SDL_RemoveTimer(timer); - lua_pushliteral(L, "callback"); - return 2; - } - nargs = 0; - break; - case SDL_USEREVENT_TICK: - do_timer = true; - nargs = 0; - break; - case SDL_USEREVENT_MOVIE_OVER: - lua_pushliteral(dispatcher, "movie_over"); - nargs = 1; - break; - case SDL_USEREVENT_SOUND_OVER: - lua_pushliteral(dispatcher, "sound_over"); - lua_pushinteger(dispatcher, *(static_cast(e.user.data1))); - nargs = 2; - break; + while (SDL_WaitEvent(&e) != 0) { + bool do_frame = false; + bool do_timer = false; + do { + int nargs; + switch (e.type) { + case SDL_QUIT: + goto leave_loop; + case SDL_KEYDOWN: + lua_pushliteral(dispatcher, "keydown"); + lua_pushstring(dispatcher, SDL_GetKeyName(e.key.keysym.sym)); + l_push_modifiers_table(dispatcher, e.key.keysym.mod); + lua_pushboolean(dispatcher, e.key.repeat != 0); + nargs = 4; + break; + case SDL_KEYUP: + lua_pushliteral(dispatcher, "keyup"); + lua_pushstring(dispatcher, SDL_GetKeyName(e.key.keysym.sym)); + nargs = 2; + break; + case SDL_TEXTINPUT: + lua_pushliteral(dispatcher, "textinput"); + lua_pushstring(dispatcher, e.text.text); + nargs = 2; + break; + case SDL_TEXTEDITING: + lua_pushliteral(dispatcher, "textediting"); + lua_pushstring(dispatcher, e.edit.text); + lua_pushinteger(dispatcher, e.edit.start); + lua_pushinteger(dispatcher, e.edit.length); + nargs = 4; + break; + case SDL_MOUSEBUTTONDOWN: + lua_pushliteral(dispatcher, "buttondown"); + lua_pushinteger(dispatcher, e.button.button); + lua_pushinteger(dispatcher, e.button.x); + lua_pushinteger(dispatcher, e.button.y); + nargs = 4; + break; + case SDL_MOUSEBUTTONUP: + lua_pushliteral(dispatcher, "buttonup"); + lua_pushinteger(dispatcher, e.button.button); + lua_pushinteger(dispatcher, e.button.x); + lua_pushinteger(dispatcher, e.button.y); + nargs = 4; + break; + case SDL_MOUSEWHEEL: + lua_pushliteral(dispatcher, "mousewheel"); + lua_pushinteger(dispatcher, e.wheel.x); + lua_pushinteger(dispatcher, e.wheel.y); + nargs = 3; + break; + case SDL_MOUSEMOTION: + lua_pushliteral(dispatcher, "motion"); + lua_pushinteger(dispatcher, e.motion.x); + lua_pushinteger(dispatcher, e.motion.y); + lua_pushinteger(dispatcher, e.motion.xrel); + lua_pushinteger(dispatcher, e.motion.yrel); + nargs = 5; + break; + case SDL_MULTIGESTURE: + lua_pushliteral(dispatcher, "multigesture"); + lua_pushinteger(dispatcher, e.mgesture.numFingers); + lua_pushnumber(dispatcher, e.mgesture.dTheta); + lua_pushnumber(dispatcher, e.mgesture.dDist); + lua_pushnumber(dispatcher, e.mgesture.x); + lua_pushnumber(dispatcher, e.mgesture.y); + nargs = 6; + break; + case SDL_WINDOWEVENT: + switch (e.window.event) { + case SDL_WINDOWEVENT_FOCUS_GAINED: + lua_pushliteral(dispatcher, "active"); + lua_pushinteger(dispatcher, 1); + nargs = 2; + break; + case SDL_WINDOWEVENT_FOCUS_LOST: + lua_pushliteral(dispatcher, "active"); + lua_pushinteger(dispatcher, 0); + nargs = 2; + break; + case SDL_WINDOWEVENT_SIZE_CHANGED: + lua_pushliteral(dispatcher, "window_resize"); + lua_pushinteger(dispatcher, e.window.data1); + lua_pushinteger(dispatcher, e.window.data2); + nargs = 3; + break; default: - nargs = 0; - break; - } - if(nargs != 0) - { - if(luaT_resume(dispatcher, dispatcher, nargs) != LUA_YIELD) - { - goto leave_loop; - } - do_frame = do_frame || (lua_toboolean(dispatcher, 1) != 0); - lua_settop(dispatcher, 0); - } - } while(SDL_PollEvent(&e) != 0); - if(do_timer) - { - lua_pushliteral(dispatcher, "timer"); - if(luaT_resume(dispatcher, dispatcher, 1) != LUA_YIELD) - { - break; - } - do_frame = do_frame || (lua_toboolean(dispatcher, 1) != 0); - lua_settop(dispatcher, 0); + nargs = 0; + break; + } + break; + case SDL_USEREVENT_MUSIC_OVER: + lua_pushliteral(dispatcher, "music_over"); + nargs = 1; + break; + case SDL_USEREVENT_MUSIC_LOADED: + if (luaT_cpcall(L, (lua_CFunction)l_load_music_async_callback, + e.user.data1)) { + SDL_RemoveTimer(timer); + lua_pushliteral(L, "callback"); + return 2; + } + nargs = 0; + break; + case SDL_USEREVENT_TICK: + do_timer = true; + nargs = 0; + break; + case SDL_USEREVENT_MOVIE_OVER: + lua_pushliteral(dispatcher, "movie_over"); + nargs = 1; + break; + case SDL_USEREVENT_SOUND_OVER: + lua_pushliteral(dispatcher, "sound_over"); + lua_pushinteger(dispatcher, *(static_cast(e.user.data1))); + nargs = 2; + break; + default: + nargs = 0; + break; + } + if (nargs != 0) { + if (luaT_resume(dispatcher, dispatcher, nargs) != LUA_YIELD) { + goto leave_loop; } - if(do_frame || !fps_control->limit_fps) - { - do - { - if(fps_control->track_fps) - { - fps_control->count_frame(); - } - lua_pushliteral(dispatcher, "frame"); - if(luaT_resume(dispatcher, dispatcher, 1) != LUA_YIELD) - { - goto leave_loop; - } - lua_settop(dispatcher, 0); - } while(fps_control->limit_fps == false && SDL_PollEvent(nullptr) == 0); - } - - // No events pending - a good time to do a bit of garbage collection - lua_gc(L, LUA_GCSTEP, 2); + do_frame = do_frame || (lua_toboolean(dispatcher, 1) != 0); + lua_settop(dispatcher, 0); + } + } while (SDL_PollEvent(&e) != 0); + if (do_timer) { + lua_pushliteral(dispatcher, "timer"); + if (luaT_resume(dispatcher, dispatcher, 1) != LUA_YIELD) { + break; + } + do_frame = do_frame || (lua_toboolean(dispatcher, 1) != 0); + lua_settop(dispatcher, 0); } + if (do_frame || !fps_control->limit_fps) { + do { + if (fps_control->track_fps) { + fps_control->count_frame(); + } + lua_pushliteral(dispatcher, "frame"); + if (luaT_resume(dispatcher, dispatcher, 1) != LUA_YIELD) { + goto leave_loop; + } + lua_settop(dispatcher, 0); + } while (fps_control->limit_fps == false && SDL_PollEvent(nullptr) == 0); + } + + // No events pending - a good time to do a bit of garbage collection + lua_gc(L, LUA_GCSTEP, 2); + } leave_loop: - SDL_RemoveTimer(timer); - int n = lua_gettop(dispatcher); - if(lua_status(dispatcher) >= LUA_ERRRUN) - { - n = 1; - } - lua_checkstack(L, n); - lua_xmove(dispatcher, L, n); - return n; + SDL_RemoveTimer(timer); + int n = lua_gettop(dispatcher); + if (lua_status(dispatcher) >= LUA_ERRRUN) { + n = 1; + } + lua_checkstack(L, n); + lua_xmove(dispatcher, L, n); + return n; } -int l_track_fps(lua_State *L) -{ - fps_ctrl *ctrl = (fps_ctrl*)lua_touserdata(L, luaT_upvalueindex(1)); - ctrl->track_fps = lua_isnone(L, 1) ? true : (lua_toboolean(L, 1) != 0); - return 0; +int l_track_fps(lua_State* L) { + fps_ctrl* ctrl = (fps_ctrl*)lua_touserdata(L, luaT_upvalueindex(1)); + ctrl->track_fps = lua_isnone(L, 1) ? true : (lua_toboolean(L, 1) != 0); + return 0; } -int l_limit_fps(lua_State *L) -{ - fps_ctrl *ctrl = (fps_ctrl*)lua_touserdata(L, luaT_upvalueindex(1)); - ctrl->limit_fps = lua_isnone(L, 1) ? true : (lua_toboolean(L, 1) != 0); - return 0; +int l_limit_fps(lua_State* L) { + fps_ctrl* ctrl = (fps_ctrl*)lua_touserdata(L, luaT_upvalueindex(1)); + ctrl->limit_fps = lua_isnone(L, 1) ? true : (lua_toboolean(L, 1) != 0); + return 0; } -int l_get_fps(lua_State *L) -{ - fps_ctrl *ctrl = (fps_ctrl*)lua_touserdata(L, luaT_upvalueindex(1)); - if(ctrl->track_fps) - { - lua_pushinteger(L, ctrl->frame_count); - } - else - { - lua_pushnil(L); - } - return 1; +int l_get_fps(lua_State* L) { + fps_ctrl* ctrl = (fps_ctrl*)lua_touserdata(L, luaT_upvalueindex(1)); + if (ctrl->track_fps) { + lua_pushinteger(L, ctrl->frame_count); + } else { + lua_pushnil(L); + } + return 1; } -int l_get_ticks(lua_State *L) -{ - lua_pushinteger(L, (lua_Integer)SDL_GetTicks()); - return 1; +int l_get_ticks(lua_State* L) { + lua_pushinteger(L, (lua_Integer)SDL_GetTicks()); + return 1; } -constexpr std::array sdllib {{ - {"init", l_init}, - {"getTicks", l_get_ticks}, - {"getKeyModifiers", l_get_key_modifiers}, - {nullptr, nullptr} -}}; +constexpr std::array sdllib{ + {{"init", l_init}, + {"getTicks", l_get_ticks}, + {"getKeyModifiers", l_get_key_modifiers}, + {nullptr, nullptr}}}; -constexpr std::array sdllib_with_upvalue {{ - {"mainloop", l_mainloop}, - {"getFPS", l_get_fps}, - {"trackFPS", l_track_fps}, - {"limitFPS", l_limit_fps}, - {nullptr, nullptr} -}}; +constexpr std::array sdllib_with_upvalue{ + {{"mainloop", l_mainloop}, + {"getFPS", l_get_fps}, + {"trackFPS", l_track_fps}, + {"limitFPS", l_limit_fps}, + {nullptr, nullptr}}}; -inline void load_extra(lua_State *L, const char *name, lua_CFunction fn) -{ - luaT_pushcfunction(L, fn); - lua_call(L, 0, 1); - lua_setfield(L, -2, name); +inline void load_extra(lua_State* L, const char* name, lua_CFunction fn) { + luaT_pushcfunction(L, fn); + lua_call(L, 0, 1); + lua_setfield(L, -2, name); } -} // namespace +} // namespace -int luaopen_sdl_audio(lua_State *L); -int luaopen_sdl_wm(lua_State *L); +int luaopen_sdl_audio(lua_State* L); +int luaopen_sdl_wm(lua_State* L); -int luaopen_sdl(lua_State *L) -{ - fps_ctrl* ctrl = (fps_ctrl*)lua_newuserdata(L, sizeof(fps_ctrl)); - ctrl->init(); - luaT_register(L, "sdl", sdllib); - for (auto reg = sdllib_with_upvalue.begin(); reg->name; ++reg) - { - lua_pushvalue(L, -2); - luaT_pushcclosure(L, reg->func, 1); - lua_setfield(L, -2, reg->name); - } +int luaopen_sdl(lua_State* L) { + fps_ctrl* ctrl = (fps_ctrl*)lua_newuserdata(L, sizeof(fps_ctrl)); + ctrl->init(); + luaT_register(L, "sdl", sdllib); + for (auto reg = sdllib_with_upvalue.begin(); reg->name; ++reg) { + lua_pushvalue(L, -2); + luaT_pushcclosure(L, reg->func, 1); + lua_setfield(L, -2, reg->name); + } - load_extra(L, "audio", luaopen_sdl_audio); - load_extra(L, "wm", luaopen_sdl_wm); + load_extra(L, "audio", luaopen_sdl_audio); + load_extra(L, "wm", luaopen_sdl_wm); - return 1; + return 1; } diff --git a/CorsixTH/Src/sdl_wm.cpp b/CorsixTH/Src/sdl_wm.cpp index 2e314f62..1fb6fb0d 100644 --- a/CorsixTH/Src/sdl_wm.cpp +++ b/CorsixTH/Src/sdl_wm.cpp @@ -21,23 +21,22 @@ SOFTWARE. */ #include "config.h" -#include "th_lua.h" #include "lua_sdl.h" +#include "th_lua.h" #ifdef CORSIX_TH_USE_WIN32_SDK -#include #include +#include #include "../resource.h" #endif #include namespace { -int l_set_icon_win32(lua_State *L) -{ - // Hack to set the window icon from the EXE resource under Windows. - // Does nothing (and returns false) on other platforms. +int l_set_icon_win32(lua_State* L) { + // Hack to set the window icon from the EXE resource under Windows. + // Does nothing (and returns false) on other platforms. - lua_pushboolean(L, 0); + lua_pushboolean(L, 0); #if 0 // XXX: Doesn't work any more, since window is inside renderer. Move to renderer. SDL_SysWMinfo oWindowInfo; @@ -53,27 +52,24 @@ int l_set_icon_win32(lua_State *L) lua_pushboolean(L, 1); } #endif - return 1; + return 1; } -int l_show_cursor(lua_State *L) -{ - SDL_ShowCursor(lua_toboolean(L, 1)); - return 0; +int l_show_cursor(lua_State* L) { + SDL_ShowCursor(lua_toboolean(L, 1)); + return 0; } -constexpr std::array sdl_wmlib {{ - {"setIconWin32", l_set_icon_win32}, - {"showCursor", l_show_cursor}, - {nullptr, nullptr} -}}; +constexpr std::array sdl_wmlib{ + {{"setIconWin32", l_set_icon_win32}, + {"showCursor", l_show_cursor}, + {nullptr, nullptr}}}; -} // namespace +} // namespace -int luaopen_sdl_wm(lua_State *L) -{ - lua_newtable(L); - luaT_setfuncs(L, sdl_wmlib.data()); +int luaopen_sdl_wm(lua_State* L) { + lua_newtable(L); + luaT_setfuncs(L, sdl_wmlib.data()); - return 1; + return 1; } diff --git a/CorsixTH/Src/th.cpp b/CorsixTH/Src/th.cpp index 24dd777b..21816e50 100644 --- a/CorsixTH/Src/th.cpp +++ b/CorsixTH/Src/th.cpp @@ -20,35 +20,28 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -#include "config.h" #include "th.h" +#include "config.h" #include #include -link_list::link_list() -{ - drawing_layer = 0; - prev = nullptr; +link_list::link_list() { + drawing_layer = 0; + prev = nullptr; + next = nullptr; +} + +link_list::~link_list() { remove_from_list(); } + +void link_list::remove_from_list() { + if (prev != nullptr) { + prev->next = next; + } + if (next != nullptr) { + next->prev = prev; next = nullptr; -} - -link_list::~link_list() -{ - remove_from_list(); -} - -void link_list::remove_from_list() -{ - if(prev != nullptr) - { - prev->next = next; - } - if(next != nullptr) - { - next->prev = prev; - next = nullptr; - } - prev = nullptr; + } + prev = nullptr; } #include "cp437_table.h" @@ -56,177 +49,141 @@ void link_list::remove_from_list() namespace { -void utf8encode(uint8_t*& sOut, uint32_t iCodepoint) -{ - if(iCodepoint <= 0x7F) - { - *sOut = static_cast(iCodepoint); +void utf8encode(uint8_t*& sOut, uint32_t iCodepoint) { + if (iCodepoint <= 0x7F) { + *sOut = static_cast(iCodepoint); + ++sOut; + } else if (iCodepoint <= 0x7FF) { + uint8_t cSextet = iCodepoint & 0x3F; + iCodepoint >>= 6; + sOut[0] = static_cast(0xC0 + iCodepoint); + sOut[1] = static_cast(0x80 + cSextet); + sOut += 2; + } else if (iCodepoint <= 0xFFFF) { + uint8_t cSextet2 = iCodepoint & 0x3F; + iCodepoint >>= 6; + uint8_t cSextet1 = iCodepoint & 0x3F; + iCodepoint >>= 6; + sOut[0] = static_cast(0xE0 + iCodepoint); + sOut[1] = static_cast(0x80 + cSextet1); + sOut[2] = static_cast(0x80 + cSextet2); + sOut += 3; + } else { + uint8_t cSextet3 = iCodepoint & 0x3F; + iCodepoint >>= 6; + uint8_t cSextet2 = iCodepoint & 0x3F; + iCodepoint >>= 6; + uint8_t cSextet1 = iCodepoint & 0x3F; + iCodepoint >>= 6; + sOut[0] = static_cast(0xF0 + iCodepoint); + sOut[1] = static_cast(0x80 + cSextet1); + sOut[2] = static_cast(0x80 + cSextet2); + sOut[3] = static_cast(0x80 + cSextet3); + sOut += 4; + } +} + +void CopyStringCP437(const uint8_t*& sIn, uint8_t*& sOut) { + uint8_t cChar; + do { + cChar = *sIn; + ++sIn; + if (cChar < 0x80) { + *sOut = cChar; + ++sOut; + } else { + utf8encode(sOut, cp437_to_unicode_table[cChar - 0x80]); + } + } while (cChar != 0); +} + +void CopyStringCP936(const uint8_t*& sIn, uint8_t*& sOut) { + uint8_t cChar1, cChar2; + do { + cChar1 = *sIn; + ++sIn; + if (cChar1 < 0x81 || cChar1 == 0xFF) { + *sOut = cChar1; + ++sOut; + } else { + cChar2 = *sIn; + ++sIn; + if (0x40 <= cChar2 && cChar2 <= 0xFE) { + utf8encode(sOut, cp936_to_unicode_table[cChar1 - 0x81][cChar2 - 0x40]); + // The Theme Hospital string tables seem to like following a + // multibyte character with a superfluous space. + cChar2 = *sIn; + if (cChar2 == ' ') ++sIn; + } else { + *sOut = cChar1; ++sOut; + *sOut = cChar2; + ++sOut; + } } - else if(iCodepoint <= 0x7FF) - { - uint8_t cSextet = iCodepoint & 0x3F; - iCodepoint >>= 6; - sOut[0] = static_cast(0xC0 + iCodepoint); - sOut[1] = static_cast(0x80 + cSextet); - sOut += 2; - } - else if(iCodepoint <= 0xFFFF) - { - uint8_t cSextet2 = iCodepoint & 0x3F; - iCodepoint >>= 6; - uint8_t cSextet1 = iCodepoint & 0x3F; - iCodepoint >>= 6; - sOut[0] = static_cast(0xE0 + iCodepoint); - sOut[1] = static_cast(0x80 + cSextet1); - sOut[2] = static_cast(0x80 + cSextet2); - sOut += 3; - } - else - { - uint8_t cSextet3 = iCodepoint & 0x3F; - iCodepoint >>= 6; - uint8_t cSextet2 = iCodepoint & 0x3F; - iCodepoint >>= 6; - uint8_t cSextet1 = iCodepoint & 0x3F; - iCodepoint >>= 6; - sOut[0] = static_cast(0xF0 + iCodepoint); - sOut[1] = static_cast(0x80 + cSextet1); - sOut[2] = static_cast(0x80 + cSextet2); - sOut[3] = static_cast(0x80 + cSextet3); - sOut += 4; - } + } while (cChar1 != 0); } -void CopyStringCP437(const uint8_t*& sIn, uint8_t*& sOut) -{ - uint8_t cChar; - do - { - cChar = *sIn; - ++sIn; - if(cChar < 0x80) - { - *sOut = cChar; - ++sOut; - } - else - { - utf8encode(sOut, cp437_to_unicode_table[cChar - 0x80]); - } - } while(cChar != 0); -} +} // namespace -void CopyStringCP936(const uint8_t*& sIn, uint8_t*& sOut) -{ - uint8_t cChar1, cChar2; - do - { - cChar1 = *sIn; - ++sIn; - if(cChar1 < 0x81 || cChar1 == 0xFF) - { - *sOut = cChar1; - ++sOut; - } - else - { - cChar2 = *sIn; - ++sIn; - if(0x40 <= cChar2 && cChar2 <= 0xFE) - { - utf8encode(sOut, cp936_to_unicode_table[cChar1-0x81][cChar2-0x40]); - // The Theme Hospital string tables seem to like following a - // multibyte character with a superfluous space. - cChar2 = *sIn; - if(cChar2 == ' ') - ++sIn; - } - else - { - *sOut = cChar1; - ++sOut; - *sOut = cChar2; - ++sOut; - } - } - } while(cChar1 != 0); -} +th_string_list::th_string_list(const uint8_t* data, size_t length) { + if (length < 2) throw std::invalid_argument("length must be 2 or larger"); -} // namespace + size_t iSectionCount = *reinterpret_cast(data); + size_t iHeaderLength = (iSectionCount + 1) * 2; -th_string_list::th_string_list(const uint8_t* data, size_t length) -{ - if(length < 2) - throw std::invalid_argument("length must be 2 or larger"); + if (length < iHeaderLength) + throw std::invalid_argument("iDataLength must be larger than the header"); - size_t iSectionCount = *reinterpret_cast(data); - size_t iHeaderLength = (iSectionCount + 1) * 2; + size_t iStringDataLength = length - iHeaderLength; + const uint8_t* sStringData = data + iHeaderLength; + const uint8_t* sDataEnd = sStringData + iStringDataLength; - if(length < iHeaderLength) - throw std::invalid_argument("iDataLength must be larger than the header"); + // Determine whether the encoding is CP437 or GB2312 (CP936). + // The range of bytes 0xB0 through 0xDF are box drawing characters in CP437 + // which shouldn't occur much (if ever) in TH strings, whereas they are + // commonly used in GB2312 encoding. We use 10% as a threshold. + size_t iBCDCount = 0; + for (size_t i = 0; i < iStringDataLength; ++i) { + if (0xB0 <= sStringData[i] && sStringData[i] <= 0xDF) ++iBCDCount; + } + void (*fnCopyString)(const uint8_t*&, uint8_t*&); + if (iBCDCount * 10 >= iStringDataLength) + fnCopyString = CopyStringCP936; + else + fnCopyString = CopyStringCP437; - size_t iStringDataLength = length - iHeaderLength; - const uint8_t *sStringData = data + iHeaderLength; - const uint8_t *sDataEnd = sStringData + iStringDataLength; + // String buffer sized to accept the largest possible reencoding of the + // characters interpreted as CP936 or CP437 (2 bytes per character). + string_buffer.resize(iStringDataLength * 2 + 2); - // Determine whether the encoding is CP437 or GB2312 (CP936). - // The range of bytes 0xB0 through 0xDF are box drawing characters in CP437 - // which shouldn't occur much (if ever) in TH strings, whereas they are - // commonly used in GB2312 encoding. We use 10% as a threshold. - size_t iBCDCount = 0; - for(size_t i = 0; i < iStringDataLength; ++i) - { - if(0xB0 <= sStringData[i] && sStringData[i] <= 0xDF) - ++iBCDCount; + uint8_t* sDataOut = string_buffer.data(); + sections.resize(iSectionCount); + for (size_t i = 0; i < iSectionCount; ++i) { + size_t section_size = reinterpret_cast(data)[i + 1]; + sections[i].reserve(section_size); + for (size_t j = 0; j < section_size; ++j) { + sections[i].push_back(reinterpret_cast(sDataOut)); + if (sStringData != sDataEnd) { + fnCopyString(sStringData, sDataOut); + } } - void (*fnCopyString)(const uint8_t*&, uint8_t*&); - if(iBCDCount * 10 >= iStringDataLength) - fnCopyString = CopyStringCP936; - else - fnCopyString = CopyStringCP437; - - // String buffer sized to accept the largest possible reencoding of the - // characters interpreted as CP936 or CP437 (2 bytes per character). - string_buffer.resize(iStringDataLength * 2 + 2); - - uint8_t *sDataOut = string_buffer.data(); - sections.resize(iSectionCount); - for(size_t i = 0; i < iSectionCount; ++i) - { - size_t section_size = reinterpret_cast(data)[i + 1]; - sections[i].reserve(section_size); - for(size_t j = 0; j < section_size; ++j) - { - sections[i].push_back(reinterpret_cast(sDataOut)); - if(sStringData != sDataEnd) - { - fnCopyString(sStringData, sDataOut); - } - } - } - // Terminate final string with nil character - *sDataOut = 0; + } + // Terminate final string with nil character + *sDataOut = 0; } -th_string_list::~th_string_list() -{} +th_string_list::~th_string_list() {} -size_t th_string_list::get_section_count() -{ - return sections.size(); +size_t th_string_list::get_section_count() { return sections.size(); } + +size_t th_string_list::get_section_size(size_t section) { + return section < sections.size() ? sections[section].size() : 0; } -size_t th_string_list::get_section_size(size_t section) -{ - return section < sections.size() ? sections[section].size() : 0; -} - -const char* th_string_list::get_string(size_t section, size_t index) -{ - if(index < get_section_size(section)) - { - return sections[section][index]; - } - return nullptr; +const char* th_string_list::get_string(size_t section, size_t index) { + if (index < get_section_size(section)) { + return sections[section][index]; + } + return nullptr; } diff --git a/CorsixTH/Src/th.h b/CorsixTH/Src/th.h index 4f6d4066..f3e9370d 100644 --- a/CorsixTH/Src/th.h +++ b/CorsixTH/Src/th.h @@ -26,70 +26,69 @@ SOFTWARE. #include //! Generic linked list class (for inheriting from) -class link_list -{ -public: - link_list() ; - ~link_list(); +class link_list { + public: + link_list(); + ~link_list(); - link_list* prev; - link_list* next; + link_list* prev; + link_list* next; - void remove_from_list(); + void remove_from_list(); - // TODO: drawing layer doesn't belong in a generic link list. - int get_drawing_layer() {return drawing_layer;} - void set_drawing_layer(int layer) {drawing_layer = layer;} + // TODO: drawing layer doesn't belong in a generic link list. + int get_drawing_layer() { return drawing_layer; } + void set_drawing_layer(int layer) { drawing_layer = layer; } -private: - int drawing_layer; + private: + int drawing_layer; }; //! \brief Theme Hospital localised string list //! //! Presents Theme Hospital strings by section and index. -class th_string_list -{ -public: - //! Construct an instance of string_list from the given data - //! from a Theme Hosptial string file. The format of the data is - //! described at: - //! https://github.com/alexandergitter/theme-hospital-spec/blob/master/format-specification.md#strings - //! - //! \param data A pointer to the raw data - //! \param length The size of the data - th_string_list(const uint8_t* data, size_t length); +class th_string_list { + public: + //! Construct an instance of string_list from the given data + //! from a Theme Hosptial string file. The format of the data is + //! described at: + //! https://github.com/alexandergitter/theme-hospital-spec/blob/master/format-specification.md#strings + //! + //! \param data A pointer to the raw data + //! \param length The size of the data + th_string_list(const uint8_t* data, size_t length); - // Delete default constructors and assignment operators. They - // can be implemented properly later if they are needed but - // for now they are unneeded so it is safer to remove them. - th_string_list() = delete; - th_string_list(const th_string_list &) = delete; - th_string_list(th_string_list &&) = delete; - th_string_list& operator= (const th_string_list &) = delete; - th_string_list&& operator= (th_string_list &&) = delete; - ~th_string_list(); + // Delete default constructors and assignment operators. They + // can be implemented properly later if they are needed but + // for now they are unneeded so it is safer to remove them. + th_string_list() = delete; + th_string_list(const th_string_list&) = delete; + th_string_list(th_string_list&&) = delete; + th_string_list& operator=(const th_string_list&) = delete; + th_string_list&& operator=(th_string_list&&) = delete; + ~th_string_list(); - //! Get the number of sections in the string list - size_t get_section_count(); + //! Get the number of sections in the string list + size_t get_section_count(); - //! Get the number of strings in a section of the string list - size_t get_section_size(size_t section); + //! Get the number of strings in a section of the string list + size_t get_section_size(size_t section); - //! Get a string from the string list - /*! - @param section Section index in range [0, getSectionCount() - 1] - @param index String index in range [0, getSectionSize(iSection) - 1] - @return nullptr if the index is invalid, otherwise a UTF-8 encoded string. - */ - const char* get_string(size_t section, size_t index); + //! Get a string from the string list + /*! + @param section Section index in range [0, getSectionCount() - 1] + @param index String index in range [0, getSectionSize(iSection) - 1] + @return nullptr if the index is invalid, otherwise a UTF-8 encoded + string. + */ + const char* get_string(size_t section, size_t index); -private: - //! Section information - std::vector> sections; + private: + //! Section information + std::vector> sections; - //! Memory block containing all the actual strings utf-8 encoded - std::vector string_buffer; + //! Memory block containing all the actual strings utf-8 encoded + std::vector string_buffer; }; /** @@ -99,17 +98,16 @@ private: * @param bytes A pointer to the first of 4 sequential bytes in memory making * up the uint32. */ -inline uint32_t bytes_to_uint32_le(const uint8_t* bytes) -{ - uint32_t res = bytes[3]; - res <<= 8; - res |= bytes[2]; - res <<= 8; - res |= bytes[1]; - res <<= 8; - res |= bytes[0]; +inline uint32_t bytes_to_uint32_le(const uint8_t* bytes) { + uint32_t res = bytes[3]; + res <<= 8; + res |= bytes[2]; + res <<= 8; + res |= bytes[1]; + res <<= 8; + res |= bytes[0]; - return res; + return res; } /** @@ -119,13 +117,12 @@ inline uint32_t bytes_to_uint32_le(const uint8_t* bytes) * @param bytes A pointer to the first of 2 sequential bytes in memory making * up the uint16. */ -inline uint16_t bytes_to_uint16_le(const uint8_t* bytes) -{ - uint16_t res = bytes[1]; - res <<= 8; - res |= bytes[0]; +inline uint16_t bytes_to_uint16_le(const uint8_t* bytes) { + uint16_t res = bytes[1]; + res <<= 8; + res |= bytes[0]; - return res; + return res; } -#endif // CORSIX_TH_TH_H_ +#endif // CORSIX_TH_TH_H_ diff --git a/CorsixTH/Src/th_gfx.cpp b/CorsixTH/Src/th_gfx.cpp index fc973dff..184d6606 100644 --- a/CorsixTH/Src/th_gfx.cpp +++ b/CorsixTH/Src/th_gfx.cpp @@ -20,276 +20,273 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -#include "config.h" #include "th_gfx.h" +#include "config.h" +#include +#include +#include +#include +#include #include "persist_lua.h" #include "th_map.h" #include "th_sound.h" -#include -#include -#include -#include -#include -/** Data retrieval class, simulating sequential access to the data, keeping track of available length. */ -class memory_reader -{ -public: - memory_reader(const uint8_t *pData, size_t iLength) - { - data = pData; - remaining_bytes = iLength; +/** Data retrieval class, simulating sequential access to the data, keeping + * track of available length. */ +class memory_reader { + public: + memory_reader(const uint8_t* pData, size_t iLength) { + data = pData; + remaining_bytes = iLength; + } + + const uint8_t* data; ///< Pointer to the remaining data. + size_t remaining_bytes; ///< Remaining number of bytes. + + //! Can \a iSize bytes be read from the file? + /*! + @param iSize Number of bytes that are queried. + @return Whether the requested number of bytes is still available. + */ + bool are_bytes_available(size_t iSize) { return iSize <= remaining_bytes; } + + //! Is EOF reached? + /*! + @return Whether EOF has been reached. + */ + bool is_at_end_of_file() { return remaining_bytes == 0; } + + //! Get an 8 bit value from the file. + /*! + @return Read 8 bit value. + @pre There should be at least a byte available for reading. + */ + uint8_t read_uint8() { + assert(remaining_bytes > 0); + + uint8_t iVal = *data; + data++; + remaining_bytes--; + return iVal; + } + + //! Get a 16 bit value from the file. + /*! + @return Read 16 bit value. + @pre There should be at least 2 bytes available for reading. + */ + uint16_t read_uint16() { + uint16_t iVal = read_uint8(); + uint16_t iVal2 = read_uint8(); + return static_cast(iVal | (iVal2 << 8)); + } + + //! Get a signed 16 bit value from the file. + /*! + @return The read signed 16 bit value. + @pre There should be at least 2 bytes available for reading. + */ + int read_int16() { + int val = read_uint16(); + if (val < 0x7FFF) return val; + + int ret = -1; + return (ret & ~0xFFFF) | val; + } + + //! Get a 32 bit value from the file. + /*! + @return Read 32 bit value. + @pre There should be at least 4 bytes available for reading. + */ + uint32_t read_uint32() { + uint32_t iVal = read_uint16(); + uint32_t iVal2 = read_uint16(); + return iVal | (iVal2 << 16); + } + + //! Load string from the memory_reader. + /*! + @param [out] pStr String to load. + @return Whether the string could be loaded. + */ + bool read_string(std::string* pStr) { + char buff[256]; + + if (is_at_end_of_file()) { + return false; } - const uint8_t *data; ///< Pointer to the remaining data. - size_t remaining_bytes; ///< Remaining number of bytes. - - //! Can \a iSize bytes be read from the file? - /*! - @param iSize Number of bytes that are queried. - @return Whether the requested number of bytes is still available. - */ - bool are_bytes_available(size_t iSize) - { - return iSize <= remaining_bytes; + size_t iLength = read_uint8(); + if (!are_bytes_available(iLength)) { + return false; } - //! Is EOF reached? - /*! - @return Whether EOF has been reached. - */ - bool is_at_end_of_file() - { - return remaining_bytes == 0; + size_t idx; + for (idx = 0; idx < iLength; idx++) { + buff[idx] = read_uint8(); } + buff[idx] = '\0'; + *pStr = std::string(buff); - //! Get an 8 bit value from the file. - /*! - @return Read 8 bit value. - @pre There should be at least a byte available for reading. - */ - uint8_t read_uint8() - { - assert(remaining_bytes > 0); - - uint8_t iVal = *data; - data++; - remaining_bytes--; - return iVal; - } - - //! Get a 16 bit value from the file. - /*! - @return Read 16 bit value. - @pre There should be at least 2 bytes available for reading. - */ - uint16_t read_uint16() - { - uint16_t iVal = read_uint8(); - uint16_t iVal2 = read_uint8(); - return static_cast(iVal | (iVal2 << 8)); - } - - //! Get a signed 16 bit value from the file. - /*! - @return The read signed 16 bit value. - @pre There should be at least 2 bytes available for reading. - */ - int read_int16() - { - int val = read_uint16(); - if (val < 0x7FFF) - return val; - - int ret = -1; - return (ret & ~0xFFFF) | val; - } - - //! Get a 32 bit value from the file. - /*! - @return Read 32 bit value. - @pre There should be at least 4 bytes available for reading. - */ - uint32_t read_uint32() - { - uint32_t iVal = read_uint16(); - uint32_t iVal2 = read_uint16(); - return iVal | (iVal2 << 16); - } - - //! Load string from the memory_reader. - /*! - @param [out] pStr String to load. - @return Whether the string could be loaded. - */ - bool read_string(std::string *pStr) - { - char buff[256]; - - if (is_at_end_of_file()) - return false; - - size_t iLength = read_uint8(); - if (!are_bytes_available(iLength)) - return false; - - size_t idx; - for (idx = 0; idx < iLength; idx++) - buff[idx] = read_uint8(); - buff[idx] = '\0'; - *pStr = std::string(buff); - return true; - } + return true; + } }; -animation_manager::animation_manager() -{ - first_frames.clear(); - frames.clear(); - element_list.clear(); - elements.clear(); - custom_sheets.clear(); +animation_manager::animation_manager() { + first_frames.clear(); + frames.clear(); + element_list.clear(); + elements.clear(); + custom_sheets.clear(); - sheet = nullptr; + sheet = nullptr; - animation_count = 0; - frame_count = 0; - element_list_count = 0; - element_count = 0; + animation_count = 0; + frame_count = 0; + element_list_count = 0; + element_count = 0; } -animation_manager::~animation_manager() -{ - for (size_t i = 0; i < custom_sheets.size(); i++) - delete custom_sheets[i]; +animation_manager::~animation_manager() { + for (size_t i = 0; i < custom_sheets.size(); i++) { + delete custom_sheets[i]; + } } -void animation_manager::set_sprite_sheet(sprite_sheet* pSpriteSheet) -{ - sheet = pSpriteSheet; +void animation_manager::set_sprite_sheet(sprite_sheet* pSpriteSheet) { + sheet = pSpriteSheet; } bool animation_manager::load_from_th_file( - const uint8_t* pStartData, size_t iStartDataLength, - const uint8_t* pFrameData, size_t iFrameDataLength, - const uint8_t* pListData, size_t iListDataLength, - const uint8_t* pElementData, size_t iElementDataLength) -{ - size_t iAnimationCount = iStartDataLength / sizeof(th_animation_properties); - size_t iFrameCount = iFrameDataLength / sizeof(th_frame_properties); - size_t iListCount = iListDataLength / 2; - size_t iElementCount = iElementDataLength / sizeof(th_element_properties); + const uint8_t* pStartData, size_t iStartDataLength, + const uint8_t* pFrameData, size_t iFrameDataLength, + const uint8_t* pListData, size_t iListDataLength, + const uint8_t* pElementData, size_t iElementDataLength) { + size_t iAnimationCount = iStartDataLength / sizeof(th_animation_properties); + size_t iFrameCount = iFrameDataLength / sizeof(th_frame_properties); + size_t iListCount = iListDataLength / 2; + size_t iElementCount = iElementDataLength / sizeof(th_element_properties); - if(iAnimationCount == 0 || iFrameCount == 0 || iListCount == 0 || iElementCount == 0) - return false; + if (iAnimationCount == 0 || iFrameCount == 0 || iListCount == 0 || + iElementCount == 0) { + return false; + } - // Start offset of the file data into the vectors. - size_t iAnimationStart = animation_count; - size_t iFrameStart = frame_count; - size_t iListStart = element_list_count; - size_t iElementStart = element_count; + // Start offset of the file data into the vectors. + size_t iAnimationStart = animation_count; + size_t iFrameStart = frame_count; + size_t iListStart = element_list_count; + size_t iElementStart = element_count; - // Original data file must start at offset 0 due to the hard-coded animation numbers in the Lua code. - if (iAnimationStart > 0 || iFrameStart > 0 || iListStart > 0 || iElementStart > 0) - return false; + // Original data file must start at offset 0 due to the hard-coded animation + // numbers in the Lua code. + if (iAnimationStart > 0 || iFrameStart > 0 || iListStart > 0 || + iElementStart > 0) { + return false; + } - if (iElementStart + iElementCount >= 0xFFFF) // Overflow of list elements. - return false; + // Overflow of list elements. + if (iElementStart + iElementCount >= 0xFFFF) { + return false; + } - // Create new space for the data. - first_frames.reserve(iAnimationStart + iAnimationCount); - frames.reserve(iFrameStart + iFrameCount); - element_list.reserve(iListStart + iListCount + 1); - elements.reserve(iElementStart + iElementCount); + // Create new space for the data. + first_frames.reserve(iAnimationStart + iAnimationCount); + frames.reserve(iFrameStart + iFrameCount); + element_list.reserve(iListStart + iListCount + 1); + elements.reserve(iElementStart + iElementCount); - // Read animations. - for(size_t i = 0; i < iAnimationCount; ++i) - { - size_t iFirstFrame = reinterpret_cast(pStartData)[i].first_frame; - if(iFirstFrame > iFrameCount) - iFirstFrame = 0; - - iFirstFrame += iFrameStart; - first_frames.push_back(iFirstFrame); + // Read animations. + for (size_t i = 0; i < iAnimationCount; ++i) { + size_t iFirstFrame = + reinterpret_cast(pStartData)[i] + .first_frame; + if (iFirstFrame > iFrameCount) { + iFirstFrame = 0; } - // Read frames. - for(size_t i = 0; i < iFrameCount; ++i) - { - const th_frame_properties* pFrame = reinterpret_cast(pFrameData) + i; + iFirstFrame += iFrameStart; + first_frames.push_back(iFirstFrame); + } - frame oFrame; - oFrame.list_index = iListStart + (pFrame->list_index < iListCount ? pFrame->list_index : 0); - oFrame.next_frame = iFrameStart + (pFrame->next < iFrameCount ? pFrame->next : 0); - oFrame.sound = pFrame->sound; - oFrame.flags = pFrame->flags; - // Bounding box fields initialised later - oFrame.marker_x = 0; - oFrame.marker_y = 0; - oFrame.secondary_marker_x = 0; - oFrame.secondary_marker_y = 0; + // Read frames. + for (size_t i = 0; i < iFrameCount; ++i) { + const th_frame_properties* pFrame = + reinterpret_cast(pFrameData) + i; - frames.push_back(oFrame); + frame oFrame; + oFrame.list_index = + iListStart + (pFrame->list_index < iListCount ? pFrame->list_index : 0); + oFrame.next_frame = + iFrameStart + (pFrame->next < iFrameCount ? pFrame->next : 0); + oFrame.sound = pFrame->sound; + oFrame.flags = pFrame->flags; + // Bounding box fields initialised later + oFrame.marker_x = 0; + oFrame.marker_y = 0; + oFrame.secondary_marker_x = 0; + oFrame.secondary_marker_y = 0; + + frames.push_back(oFrame); + } + + // Read element list. + for (size_t i = 0; i < iListCount; ++i) { + uint16_t iElmNumber = *(reinterpret_cast(pListData) + i); + if (iElmNumber >= iElementCount) { + iElmNumber = 0xFFFF; + } else { + iElmNumber = static_cast(iElmNumber + iElementStart); } - // Read element list. - for(size_t i = 0; i < iListCount; ++i) - { - uint16_t iElmNumber = *(reinterpret_cast(pListData) + i); - if (iElmNumber >= iElementCount) - { - iElmNumber = 0xFFFF; - } - else - { - iElmNumber = static_cast(iElmNumber + iElementStart); - } + element_list.push_back(iElmNumber); + } + element_list.push_back(0xFFFF); - element_list.push_back(iElmNumber); + // Read elements. + size_t iSpriteCount = sheet->get_sprite_count(); + for (size_t i = 0; i < iElementCount; ++i) { + const th_element_properties* pTHElement = + reinterpret_cast(pElementData) + i; + + element oElement; + oElement.sprite = pTHElement->table_position / 6; + oElement.flags = pTHElement->flags & 0xF; + oElement.x = static_cast(pTHElement->offx) - 141; + oElement.y = static_cast(pTHElement->offy) - 186; + oElement.layer = static_cast( + pTHElement->flags >> 4); // High nibble, layer of the element. + if (oElement.layer > 12) { + // Nothing lives on layer 6 + oElement.layer = 6; } - element_list.push_back(0xFFFF); - - // Read elements. - size_t iSpriteCount = sheet->get_sprite_count(); - for(size_t i = 0; i < iElementCount; ++i) - { - const th_element_properties* pTHElement = reinterpret_cast(pElementData) + i; - - element oElement; - oElement.sprite = pTHElement->table_position / 6; - oElement.flags = pTHElement->flags & 0xF; - oElement.x = static_cast(pTHElement->offx) - 141; - oElement.y = static_cast(pTHElement->offy) - 186; - oElement.layer = static_cast(pTHElement->flags >> 4); // High nibble, layer of the element. - if(oElement.layer > 12) - oElement.layer = 6; // Nothing lives on layer 6 - oElement.layer_id = pTHElement->layerid; - if (oElement.sprite < iSpriteCount) { - oElement.element_sprite_sheet = sheet; - } else { - oElement.element_sprite_sheet = nullptr; - } - - elements.push_back(oElement); + oElement.layer_id = pTHElement->layerid; + if (oElement.sprite < iSpriteCount) { + oElement.element_sprite_sheet = sheet; + } else { + oElement.element_sprite_sheet = nullptr; } - // Compute bounding box of the animations using the sprite sheet. - for(size_t i = 0; i < iFrameCount; ++i) - { - set_bounding_box(frames[iFrameStart + i]); - } + elements.push_back(oElement); + } - animation_count += iAnimationCount; - frame_count += iFrameCount; - element_list_count += iListCount + 1; - element_count += iElementCount; + // Compute bounding box of the animations using the sprite sheet. + for (size_t i = 0; i < iFrameCount; ++i) { + set_bounding_box(frames[iFrameStart + i]); + } - assert(first_frames.size() == animation_count); - assert(frames.size() == frame_count); - assert(element_list.size() == element_list_count); - assert(elements.size() == element_count); + animation_count += iAnimationCount; + frame_count += iFrameCount; + element_list_count += iListCount + 1; + element_count += iElementCount; - return true; + assert(first_frames.size() == animation_count); + assert(frames.size() == frame_count); + assert(element_list.size() == element_list_count); + assert(elements.size() == element_count); + + return true; } namespace { @@ -299,10 +296,8 @@ namespace { @param [inout] iLeft Left value to check and update. @param iRight Second value to check. */ -void set_left_to_min(int& iLeft, int iRight) -{ - if(iRight < iLeft) - iLeft = iRight; +void set_left_to_min(int& iLeft, int iRight) { + if (iRight < iLeft) iLeft = iRight; } //! Update \a iLeft with the biggest of both values. @@ -310,44 +305,41 @@ void set_left_to_min(int& iLeft, int iRight) @param [inout] iLeft Left value to check and update. @param iRight Second value to check. */ -void set_left_to_max(int& iLeft, int iRight) -{ - if(iRight > iLeft) - iLeft = iRight; +void set_left_to_max(int& iLeft, int iRight) { + if (iRight > iLeft) iLeft = iRight; } -} // namespace +} // namespace -void animation_manager::set_bounding_box(frame &oFrame) -{ - oFrame.bounding_left = INT_MAX; - oFrame.bounding_right = INT_MIN; - oFrame.bounding_top = INT_MAX; - oFrame.bounding_bottom = INT_MIN; - size_t iListIndex = oFrame.list_index; - for(; ; ++iListIndex) - { - uint16_t iElement = element_list[iListIndex]; - if(iElement >= elements.size()) - break; +void animation_manager::set_bounding_box(frame& oFrame) { + oFrame.bounding_left = INT_MAX; + oFrame.bounding_right = INT_MIN; + oFrame.bounding_top = INT_MAX; + oFrame.bounding_bottom = INT_MIN; - element& oElement = elements[iElement]; - if(oElement.element_sprite_sheet == nullptr) - continue; - - unsigned int iWidth, iHeight; - oElement.element_sprite_sheet->get_sprite_size_unchecked(oElement.sprite, &iWidth, &iHeight); - set_left_to_min(oFrame.bounding_left , oElement.x); - set_left_to_min(oFrame.bounding_top , oElement.y); - set_left_to_max(oFrame.bounding_right , oElement.x - 1 + (int)iWidth); - set_left_to_max(oFrame.bounding_bottom, oElement.y - 1 + (int)iHeight); + size_t iListIndex = oFrame.list_index; + for (;; ++iListIndex) { + uint16_t iElement = element_list[iListIndex]; + if (iElement >= elements.size()) { + break; } + + element& oElement = elements[iElement]; + if (oElement.element_sprite_sheet == nullptr) { + continue; + } + + unsigned int iWidth, iHeight; + oElement.element_sprite_sheet->get_sprite_size_unchecked(oElement.sprite, + &iWidth, &iHeight); + set_left_to_min(oFrame.bounding_left, oElement.x); + set_left_to_min(oFrame.bounding_top, oElement.y); + set_left_to_max(oFrame.bounding_right, oElement.x - 1 + (int)iWidth); + set_left_to_max(oFrame.bounding_bottom, oElement.y - 1 + (int)iHeight); + } } -void animation_manager::set_canvas(render_target *pCanvas) -{ - canvas = pCanvas; -} +void animation_manager::set_canvas(render_target* pCanvas) { canvas = pCanvas; } namespace { @@ -356,86 +348,91 @@ namespace { @param [inout] input Data to read. @return Number of consumed bytes, a negative number indicates an error. */ -int load_header(memory_reader &input) -{ - static const uint8_t aHdr[] = {'C', 'T', 'H', 'G', 1, 2}; +int load_header(memory_reader& input) { + static const uint8_t aHdr[] = {'C', 'T', 'H', 'G', 1, 2}; - if (!input.are_bytes_available(6)) - return false; - for (int i = 0; i < 6; i++) - { - if (input.read_uint8() != aHdr[i]) - return false; + if (!input.are_bytes_available(6)) { + return false; + } + + for (int i = 0; i < 6; i++) { + if (input.read_uint8() != aHdr[i]) { + return false; } - return true; + } + return true; } -} // namespace +} // namespace -size_t animation_manager::load_elements(memory_reader &input, sprite_sheet *pSpriteSheet, - size_t iNumElements, size_t &iLoadedElements, - size_t iElementStart, size_t iElementCount) -{ - size_t iFirst = iLoadedElements + iElementStart; +size_t animation_manager::load_elements( + memory_reader& input, sprite_sheet* pSpriteSheet, size_t iNumElements, + size_t& iLoadedElements, size_t iElementStart, size_t iElementCount) { + size_t iFirst = iLoadedElements + iElementStart; - size_t iSpriteCount = pSpriteSheet->get_sprite_count(); - while (iNumElements > 0) - { - if (iLoadedElements >= iElementCount || !input.are_bytes_available(12)) - return SIZE_MAX; - - size_t iSprite = input.read_uint32(); - int iX = input.read_int16(); - int iY = input.read_int16(); - uint8_t iLayerClass = input.read_uint8(); - uint8_t iLayerId = input.read_uint8(); - uint32_t iFlags = input.read_uint16(); - - if (iLayerClass > 12) - iLayerClass = 6; // Nothing lives on layer 6 - - element oElement; - oElement.sprite = iSprite; - oElement.flags = iFlags; - oElement.x = iX; - oElement.y = iY; - oElement.layer = iLayerClass; - oElement.layer_id = iLayerId; - if (oElement.sprite >= iSpriteCount) - oElement.element_sprite_sheet = nullptr; - else - oElement.element_sprite_sheet = pSpriteSheet; - - elements.push_back(oElement); - iLoadedElements++; - iNumElements--; + size_t iSpriteCount = pSpriteSheet->get_sprite_count(); + while (iNumElements > 0) { + if (iLoadedElements >= iElementCount || !input.are_bytes_available(12)) { + return SIZE_MAX; } - return iFirst; + + size_t iSprite = input.read_uint32(); + int iX = input.read_int16(); + int iY = input.read_int16(); + uint8_t iLayerClass = input.read_uint8(); + uint8_t iLayerId = input.read_uint8(); + uint32_t iFlags = input.read_uint16(); + + if (iLayerClass > 12) { + // Nothing lives on layer 6 + iLayerClass = 6; + } + + element oElement; + oElement.sprite = iSprite; + oElement.flags = iFlags; + oElement.x = iX; + oElement.y = iY; + oElement.layer = iLayerClass; + oElement.layer_id = iLayerId; + if (oElement.sprite >= iSpriteCount) + oElement.element_sprite_sheet = nullptr; + else + oElement.element_sprite_sheet = pSpriteSheet; + + elements.push_back(oElement); + iLoadedElements++; + iNumElements--; + } + return iFirst; } -size_t animation_manager::make_list_elements(size_t iFirstElement, size_t iNumElements, - size_t &iLoadedListElements, - size_t iListStart, size_t iListCount) -{ - size_t iFirst = iLoadedListElements + iListStart; +size_t animation_manager::make_list_elements(size_t iFirstElement, + size_t iNumElements, + size_t& iLoadedListElements, + size_t iListStart, + size_t iListCount) { + size_t iFirst = iLoadedListElements + iListStart; - // Verify there is enough room for all list elements + 0xFFFF - if (iLoadedListElements + iNumElements + 1 > iListCount) - return SIZE_MAX; - assert(iFirstElement + iNumElements < 0xFFFF); // Overflow for list elements. + // Verify there is enough room for all list elements + 0xFFFF + if (iLoadedListElements + iNumElements + 1 > iListCount) { + return SIZE_MAX; + } - while (iNumElements > 0) - { - element_list.push_back(static_cast(iFirstElement)); - iLoadedListElements++; - iFirstElement++; - iNumElements--; - } - // Add 0xFFFF. - element_list.push_back(0xFFFF); + // Overflow for list elements. + assert(iFirstElement + iNumElements < 0xFFFF); + + while (iNumElements > 0) { + element_list.push_back(static_cast(iFirstElement)); iLoadedListElements++; + iFirstElement++; + iNumElements--; + } + // Add 0xFFFF. + element_list.push_back(0xFFFF); + iLoadedListElements++; - return iFirst; + return iFirst; } namespace { @@ -448,1498 +445,1416 @@ namespace { @param iLoaded Number of loaded frames. @return The shifted first frame, or 0xFFFFFFFFu. */ -uint32_t shift_first(uint32_t iFirst, size_t iLength, - size_t iStart, size_t iLoaded) -{ - if (iFirst == 0xFFFFFFFFu || iFirst + iLength > iLoaded) - return 0xFFFFFFFFu; - return iFirst + static_cast(iStart); +uint32_t shift_first(uint32_t iFirst, size_t iLength, size_t iStart, + size_t iLoaded) { + if (iFirst == 0xFFFFFFFFu || iFirst + iLength > iLoaded) { + return 0xFFFFFFFFu; + } + return iFirst + static_cast(iStart); } -} // namespace +} // namespace -void animation_manager::fix_next_frame(uint32_t iFirst, size_t iLength) -{ - if (iFirst == 0xFFFFFFFFu) - return; +void animation_manager::fix_next_frame(uint32_t iFirst, size_t iLength) { + if (iFirst == 0xFFFFFFFFu) { + return; + } - frame &oFirst = frames[iFirst]; - oFirst.flags |= 0x1; // Start of animation flag. + frame& oFirst = frames[iFirst]; + oFirst.flags |= 0x1; // Start of animation flag. - frame &oLast = frames[iFirst + iLength - 1]; - oLast.next_frame = iFirst; // Loop last frame back to the first. + frame& oLast = frames[iFirst + iLength - 1]; + oLast.next_frame = iFirst; // Loop last frame back to the first. } -bool animation_manager::load_custom_animations(const uint8_t* pData, size_t iDataLength) -{ - memory_reader input(pData, iDataLength); - - if (!load_header(input)) - return false; - - if (!input.are_bytes_available(5*4)) - return false; - - size_t iAnimationCount = input.read_uint32(); - size_t iFrameCount = input.read_uint32(); - size_t iElementCount = input.read_uint32(); - size_t iSpriteCount = input.read_uint32(); - input.read_uint32(); // Total number of bytes sprite data is not used. - - // Every element is referenced once, and one 0xFFFF for every frame. - size_t iListCount = iElementCount + iFrameCount; - - size_t iFrameStart = frame_count; - size_t iListStart = element_list_count; - size_t iElementStart = element_count; - - if (iAnimationCount == 0 || iFrameCount == 0 || iElementCount == 0 || iSpriteCount == 0) - return false; - - if (iElementStart + iElementCount >= 0xFFFF) // Overflow of list elements. - return false; - - // Create new space for the elements. - first_frames.reserve(first_frames.size() + iAnimationCount * 4); // Be optimistic in reservation. - frames.reserve(iFrameStart + iFrameCount); - element_list.reserve(iListStart + iListCount); - elements.reserve(iElementStart + iElementCount); - - // Construct a sprite sheet for the sprites to be loaded. - sprite_sheet *pSheet = new sprite_sheet; - pSheet->set_sprite_count(iSpriteCount, canvas); - custom_sheets.push_back(pSheet); - - size_t iLoadedFrames = 0; - size_t iLoadedListElements = 0; - size_t iLoadedElements = 0; - size_t iLoadedSprites = 0; - - // Read the blocks of the file, until hitting EOF. - for (;;) - { - if (input.is_at_end_of_file()) - break; - - // Read identification bytes at the start of each block, and dispatch loading. - if (!input.are_bytes_available(2)) - return false; - int first = input.read_uint8(); - int second = input.read_uint8(); - - // Recognized a grouped animation block, load it. - if (first == 'C' && second == 'A') - { - animation_key oKey; - - if (!input.are_bytes_available(2+4)) - return false; - oKey.tile_size = input.read_uint16(); - size_t iNumFrames = input.read_uint32(); - if (iNumFrames == 0) - return false; - - if (!input.read_string(&oKey.name)) - return false; - - if (!input.are_bytes_available(4*4)) - return false; - uint32_t iNorthFirst = input.read_uint32(); - uint32_t iEastFirst = input.read_uint32(); - uint32_t iSouthFirst = input.read_uint32(); - uint32_t iWestFirst = input.read_uint32(); - - iNorthFirst = shift_first(iNorthFirst, iNumFrames, iFrameStart, iLoadedFrames); - iEastFirst = shift_first(iEastFirst, iNumFrames, iFrameStart, iLoadedFrames); - iSouthFirst = shift_first(iSouthFirst, iNumFrames, iFrameStart, iLoadedFrames); - iWestFirst = shift_first(iWestFirst, iNumFrames, iFrameStart, iLoadedFrames); - - animation_start_frames oFrames; - oFrames.north = -1; - oFrames.east = -1; - oFrames.south = -1; - oFrames.west = -1; - - if (iNorthFirst != 0xFFFFFFFFu) - { - fix_next_frame(iNorthFirst, iNumFrames); - oFrames.north = static_cast(first_frames.size()); - first_frames.push_back(iNorthFirst); - } - if (iEastFirst != 0xFFFFFFFFu) - { - fix_next_frame(iEastFirst, iNumFrames); - oFrames.east = static_cast(first_frames.size()); - first_frames.push_back(iEastFirst); - } - if (iSouthFirst != 0xFFFFFFFFu) - { - fix_next_frame(iSouthFirst, iNumFrames); - oFrames.south = static_cast(first_frames.size()); - first_frames.push_back(iSouthFirst); - } - if (iWestFirst != 0xFFFFFFFFu) - { - fix_next_frame(iWestFirst, iNumFrames); - oFrames.west = static_cast(first_frames.size()); - first_frames.push_back(iWestFirst); - } - - named_animation_pair p(oKey, oFrames); - named_animations.insert(p); - continue; - } - - // Recognized a frame block, load it. - else if (first == 'F' && second == 'R') - { - if (iLoadedFrames >= iFrameCount) - return false; - - if (!input.are_bytes_available(2+2)) - return false; - int iSound = input.read_uint16(); - size_t iNumElements = input.read_uint16(); - - size_t iElm = load_elements(input, pSheet, iNumElements, - iLoadedElements, iElementStart, iElementCount); - if (iElm == SIZE_MAX) - return false; - - size_t iListElm = make_list_elements(iElm, iNumElements, - iLoadedListElements, iListStart, iListCount); - if (iListElm == SIZE_MAX) - return false; - - frame oFrame; - oFrame.list_index = iListElm; - oFrame.next_frame = iFrameStart + iLoadedFrames + 1; // Point to next frame (changed later). - oFrame.sound = iSound; - oFrame.flags = 0; // Set later. - oFrame.marker_x = 0; - oFrame.marker_y = 0; - oFrame.secondary_marker_x = 0; - oFrame.secondary_marker_y = 0; - - set_bounding_box(oFrame); - - frames.push_back(oFrame); - iLoadedFrames++; - continue; - } - - // Recognized a Sprite block, load it. - else if (first == 'S' && second == 'P') - { - if (iLoadedSprites >= iSpriteCount) - return false; - - if (!input.are_bytes_available(2+2+4)) - return false; - int iWidth = input.read_uint16(); - int iHeight = input.read_uint16(); - uint32_t iSize = input.read_uint32(); - if (iSize > INT_MAX) // Check it is safe to use as 'int' - return false; - - // Load data. - uint8_t *pData = new (std::nothrow) uint8_t[iSize]; - if (pData == nullptr) { - return false; - } - if (!input.are_bytes_available(iSize)) { - delete[] pData; - return false; - } - for (uint32_t i = 0; i < iSize; i++) - pData[i] = input.read_uint8(); - - if (!pSheet->set_sprite_data(iLoadedSprites, pData, true, iSize, - iWidth, iHeight)) - return false; - - iLoadedSprites++; - continue; - } - - // Unrecognized block, fail. - else - { - return false; - } - } - - assert(iLoadedFrames == iFrameCount); - assert(iLoadedListElements == iListCount); - assert(iLoadedElements == iElementCount); - assert(iLoadedSprites == iSpriteCount); - - // Fix the next pointer of the last frame in case it points to non-existing frames. - frame &oFrame = frames[iFrameStart + iFrameCount - 1]; - if (iFrameCount > 0 && oFrame.next_frame >= iFrameStart + iFrameCount) - oFrame.next_frame = iFrameStart; // Useless, but maybe less crashy. - - animation_count = first_frames.size(); - frame_count += iFrameCount; - element_list_count += iListCount; - element_count += iElementCount; - assert(frames.size() == frame_count); - assert(element_list.size() == element_list_count); - assert(elements.size() == element_count); - - return true; -} - -const animation_start_frames &animation_manager::get_named_animations(const std::string &sName, int iTilesize) const -{ - static const animation_start_frames oNoneAnimations = {-1, -1, -1, -1}; - - animation_key oKey; - oKey.name = sName; - oKey.tile_size = iTilesize; - - named_animations_map::const_iterator iter = named_animations.find(oKey); - if (iter == named_animations.end()) - return oNoneAnimations; - return (*iter).second; -} - -size_t animation_manager::get_animation_count() const -{ - return animation_count; -} - -size_t animation_manager::get_frame_count() const -{ - return frame_count; -} - -size_t animation_manager::get_first_frame(size_t iAnimation) const -{ - if(iAnimation < animation_count) - return first_frames[iAnimation]; - else - return 0; -} - -size_t animation_manager::get_next_frame(size_t iFrame) const -{ - if(iFrame < frame_count) - return frames[iFrame].next_frame; - else - return iFrame; -} - -void animation_manager::set_animation_alt_palette_map(size_t iAnimation, const uint8_t* pMap, uint32_t iAlt32) -{ - if(iAnimation >= animation_count) - return; - - size_t iFrame = first_frames[iAnimation]; - size_t iFirstFrame = iFrame; - do - { - size_t iListIndex = frames[iFrame].list_index; - for(; ; ++iListIndex) - { - uint16_t iElement = element_list[iListIndex]; - if(iElement >= element_count) - break; - - element& oElement = elements[iElement]; - if (oElement.element_sprite_sheet != nullptr) - oElement.element_sprite_sheet->set_sprite_alt_palette_map(oElement.sprite, pMap, iAlt32); - } - iFrame = frames[iFrame].next_frame; - } while(iFrame != iFirstFrame); -} - -bool animation_manager::set_frame_marker(size_t iFrame, int iX, int iY) -{ - if(iFrame >= frame_count) - return false; - frames[iFrame].marker_x = iX; - frames[iFrame].marker_y = iY; - return true; -} - -bool animation_manager::set_frame_secondary_marker(size_t iFrame, int iX, int iY) -{ - if(iFrame >= frame_count) - return false; - frames[iFrame].secondary_marker_x = iX; - frames[iFrame].secondary_marker_y = iY; - return true; -} - -bool animation_manager::get_frame_marker(size_t iFrame, int* pX, int* pY) -{ - if(iFrame >= frame_count) - return false; - *pX = frames[iFrame].marker_x; - *pY = frames[iFrame].marker_y; - return true; -} - -bool animation_manager::get_frame_secondary_marker(size_t iFrame, int* pX, int* pY) -{ - if(iFrame >= frame_count) - return false; - *pX = frames[iFrame].secondary_marker_x; - *pY = frames[iFrame].secondary_marker_y; - return true; -} - -bool animation_manager::hit_test(size_t iFrame, const ::layers& oLayers, - int iX, int iY, uint32_t iFlags, - int iTestX, int iTestY) const -{ - if(iFrame >= frame_count) - return false; - - const frame& oFrame = frames[iFrame]; - iTestX -= iX; - iTestY -= iY; - - if(iFlags & thdf_flip_horizontal) - iTestX = -iTestX; - if(iTestX < oFrame.bounding_left || iTestX > oFrame.bounding_right) - return false; - - if(iFlags & thdf_flip_vertical) - { - if(-iTestY < oFrame.bounding_top || -iTestY > oFrame.bounding_bottom) - return false; - } - else - { - if(iTestY < oFrame.bounding_top || iTestY > oFrame.bounding_bottom) - return false; - } - - if(iFlags & thdf_bound_box_hit_test) - return true; - - size_t iListIndex = oFrame.list_index; - for(; ; ++iListIndex) - { - uint16_t iElement = element_list[iListIndex]; - if(iElement >= element_count) - break; - - const element &oElement = elements[iElement]; - if((oElement.layer_id != 0 && oLayers.layer_contents[oElement.layer] != oElement.layer_id) - || oElement.element_sprite_sheet == nullptr) - { - continue; - } - - if(iFlags & thdf_flip_horizontal) - { - unsigned int iWidth, iHeight; - oElement.element_sprite_sheet->get_sprite_size_unchecked(oElement.sprite, &iWidth, &iHeight); - if(oElement.element_sprite_sheet->hit_test_sprite(oElement.sprite, oElement.x + iWidth - iTestX, - iTestY - oElement.y, oElement.flags ^ thdf_flip_horizontal)) - { - return true; - } - } - else - { - if(oElement.element_sprite_sheet->hit_test_sprite(oElement.sprite, iTestX - oElement.x, - iTestY - oElement.y, oElement.flags)) - { - return true; - } - } - } +bool animation_manager::load_custom_animations(const uint8_t* pData, + size_t iDataLength) { + memory_reader input(pData, iDataLength); + if (!load_header(input)) { return false; + } + + if (!input.are_bytes_available(5 * 4)) { + return false; + } + + size_t iAnimationCount = input.read_uint32(); + size_t iFrameCount = input.read_uint32(); + size_t iElementCount = input.read_uint32(); + size_t iSpriteCount = input.read_uint32(); + input.read_uint32(); // Total number of bytes sprite data is not used. + + // Every element is referenced once, and one 0xFFFF for every frame. + size_t iListCount = iElementCount + iFrameCount; + + size_t iFrameStart = frame_count; + size_t iListStart = element_list_count; + size_t iElementStart = element_count; + + if (iAnimationCount == 0 || iFrameCount == 0 || iElementCount == 0 || + iSpriteCount == 0) { + return false; + } + + // Overflow of list elements. + if (iElementStart + iElementCount >= 0xFFFF) { + return false; + } + + // Create new space for the elements. + // Be optimistic in reservation. + first_frames.reserve(first_frames.size() + iAnimationCount * 4); + frames.reserve(iFrameStart + iFrameCount); + element_list.reserve(iListStart + iListCount); + elements.reserve(iElementStart + iElementCount); + + // Construct a sprite sheet for the sprites to be loaded. + sprite_sheet* pSheet = new sprite_sheet; + pSheet->set_sprite_count(iSpriteCount, canvas); + custom_sheets.push_back(pSheet); + + size_t iLoadedFrames = 0; + size_t iLoadedListElements = 0; + size_t iLoadedElements = 0; + size_t iLoadedSprites = 0; + + // Read the blocks of the file, until hitting EOF. + for (;;) { + if (input.is_at_end_of_file()) break; + + // Read identification bytes at the start of each block, and dispatch + // loading. + if (!input.are_bytes_available(2)) return false; + int first = input.read_uint8(); + int second = input.read_uint8(); + + // Recognized a grouped animation block, load it. + if (first == 'C' && second == 'A') { + animation_key oKey; + + if (!input.are_bytes_available(2 + 4)) { + return false; + } + + oKey.tile_size = input.read_uint16(); + size_t iNumFrames = input.read_uint32(); + if (iNumFrames == 0) { + return false; + } + + if (!input.read_string(&oKey.name)) { + return false; + } + + if (!input.are_bytes_available(4 * 4)) { + return false; + } + + uint32_t iNorthFirst = input.read_uint32(); + uint32_t iEastFirst = input.read_uint32(); + uint32_t iSouthFirst = input.read_uint32(); + uint32_t iWestFirst = input.read_uint32(); + + iNorthFirst = + shift_first(iNorthFirst, iNumFrames, iFrameStart, iLoadedFrames); + iEastFirst = + shift_first(iEastFirst, iNumFrames, iFrameStart, iLoadedFrames); + iSouthFirst = + shift_first(iSouthFirst, iNumFrames, iFrameStart, iLoadedFrames); + iWestFirst = + shift_first(iWestFirst, iNumFrames, iFrameStart, iLoadedFrames); + + animation_start_frames oFrames; + oFrames.north = -1; + oFrames.east = -1; + oFrames.south = -1; + oFrames.west = -1; + + if (iNorthFirst != 0xFFFFFFFFu) { + fix_next_frame(iNorthFirst, iNumFrames); + oFrames.north = static_cast(first_frames.size()); + first_frames.push_back(iNorthFirst); + } + if (iEastFirst != 0xFFFFFFFFu) { + fix_next_frame(iEastFirst, iNumFrames); + oFrames.east = static_cast(first_frames.size()); + first_frames.push_back(iEastFirst); + } + if (iSouthFirst != 0xFFFFFFFFu) { + fix_next_frame(iSouthFirst, iNumFrames); + oFrames.south = static_cast(first_frames.size()); + first_frames.push_back(iSouthFirst); + } + if (iWestFirst != 0xFFFFFFFFu) { + fix_next_frame(iWestFirst, iNumFrames); + oFrames.west = static_cast(first_frames.size()); + first_frames.push_back(iWestFirst); + } + + named_animation_pair p(oKey, oFrames); + named_animations.insert(p); + continue; + } else if (first == 'F' && second == 'R') { + // Recognized a frame block, load it. + + if (iLoadedFrames >= iFrameCount) { + return false; + } + + if (!input.are_bytes_available(2 + 2)) { + return false; + } + + int iSound = input.read_uint16(); + size_t iNumElements = input.read_uint16(); + + size_t iElm = load_elements(input, pSheet, iNumElements, iLoadedElements, + iElementStart, iElementCount); + if (iElm == SIZE_MAX) { + return false; + } + + size_t iListElm = make_list_elements( + iElm, iNumElements, iLoadedListElements, iListStart, iListCount); + if (iListElm == SIZE_MAX) { + return false; + } + + frame oFrame; + oFrame.list_index = iListElm; + + // Point to next frame. + // Last frame of each animation corrected later. + oFrame.next_frame = iFrameStart + iLoadedFrames + 1; + + oFrame.sound = iSound; + + // Set later + oFrame.flags = 0; + oFrame.marker_x = 0; + oFrame.marker_y = 0; + oFrame.secondary_marker_x = 0; + oFrame.secondary_marker_y = 0; + + set_bounding_box(oFrame); + + frames.push_back(oFrame); + iLoadedFrames++; + continue; + } else if (first == 'S' && second == 'P') { + // Recognized a Sprite block, load it. + + if (iLoadedSprites >= iSpriteCount) { + return false; + } + + if (!input.are_bytes_available(2 + 2 + 4)) { + return false; + } + + int iWidth = input.read_uint16(); + int iHeight = input.read_uint16(); + uint32_t iSize = input.read_uint32(); + + // Check it is safe to use as 'int' + if (iSize > INT_MAX) { + return false; + } + + // Load data. + uint8_t* pData = new (std::nothrow) uint8_t[iSize]; + if (pData == nullptr) { + return false; + } + + if (!input.are_bytes_available(iSize)) { + delete[] pData; + return false; + } + + for (uint32_t i = 0; i < iSize; i++) { + pData[i] = input.read_uint8(); + } + + if (!pSheet->set_sprite_data(iLoadedSprites, pData, true, iSize, iWidth, + iHeight)) { + return false; + } + + iLoadedSprites++; + continue; + } else { + // Unrecognized block, fail. + return false; + } + } + + assert(iLoadedFrames == iFrameCount); + assert(iLoadedListElements == iListCount); + assert(iLoadedElements == iElementCount); + assert(iLoadedSprites == iSpriteCount); + + // Fix the next pointer of the last frame in case it points to non-existing + // frames. + frame& oFrame = frames[iFrameStart + iFrameCount - 1]; + if (iFrameCount > 0 && oFrame.next_frame >= iFrameStart + iFrameCount) { + // Useless, but maybe less crashy. + oFrame.next_frame = iFrameStart; + } + + animation_count = first_frames.size(); + frame_count += iFrameCount; + element_list_count += iListCount; + element_count += iElementCount; + assert(frames.size() == frame_count); + assert(element_list.size() == element_list_count); + assert(elements.size() == element_count); + + return true; +} + +const animation_start_frames& animation_manager::get_named_animations( + const std::string& sName, int iTilesize) const { + static const animation_start_frames oNoneAnimations = {-1, -1, -1, -1}; + + animation_key oKey; + oKey.name = sName; + oKey.tile_size = iTilesize; + + named_animations_map::const_iterator iter = named_animations.find(oKey); + if (iter == named_animations.end()) { + return oNoneAnimations; + } + return (*iter).second; +} + +size_t animation_manager::get_animation_count() const { + return animation_count; +} + +size_t animation_manager::get_frame_count() const { return frame_count; } + +size_t animation_manager::get_first_frame(size_t iAnimation) const { + if (iAnimation < animation_count) { + return first_frames[iAnimation]; + } else { + return 0; + } +} + +size_t animation_manager::get_next_frame(size_t iFrame) const { + if (iFrame < frame_count) { + return frames[iFrame].next_frame; + } else { + return iFrame; + } +} + +void animation_manager::set_animation_alt_palette_map(size_t iAnimation, + const uint8_t* pMap, + uint32_t iAlt32) { + if (iAnimation >= animation_count) { + return; + } + + size_t iFrame = first_frames[iAnimation]; + size_t iFirstFrame = iFrame; + do { + size_t iListIndex = frames[iFrame].list_index; + for (;; ++iListIndex) { + uint16_t iElement = element_list[iListIndex]; + if (iElement >= element_count) { + break; + } + + element& oElement = elements[iElement]; + if (oElement.element_sprite_sheet != nullptr) { + oElement.element_sprite_sheet->set_sprite_alt_palette_map( + oElement.sprite, pMap, iAlt32); + } + } + iFrame = frames[iFrame].next_frame; + } while (iFrame != iFirstFrame); +} + +bool animation_manager::set_frame_marker(size_t iFrame, int iX, int iY) { + if (iFrame >= frame_count) { + return false; + } + + frames[iFrame].marker_x = iX; + frames[iFrame].marker_y = iY; + return true; +} + +bool animation_manager::set_frame_secondary_marker(size_t iFrame, int iX, + int iY) { + if (iFrame >= frame_count) { + return false; + } + + frames[iFrame].secondary_marker_x = iX; + frames[iFrame].secondary_marker_y = iY; + return true; +} + +bool animation_manager::get_frame_marker(size_t iFrame, int* pX, int* pY) { + if (iFrame >= frame_count) { + return false; + } + + *pX = frames[iFrame].marker_x; + *pY = frames[iFrame].marker_y; + return true; +} + +bool animation_manager::get_frame_secondary_marker(size_t iFrame, int* pX, + int* pY) { + if (iFrame >= frame_count) { + return false; + } + + *pX = frames[iFrame].secondary_marker_x; + *pY = frames[iFrame].secondary_marker_y; + return true; +} + +bool animation_manager::hit_test(size_t iFrame, const ::layers& oLayers, int iX, + int iY, uint32_t iFlags, int iTestX, + int iTestY) const { + if (iFrame >= frame_count) { + return false; + } + + const frame& oFrame = frames[iFrame]; + iTestX -= iX; + iTestY -= iY; + + if (iFlags & thdf_flip_horizontal) { + iTestX = -iTestX; + } + + if (iTestX < oFrame.bounding_left || iTestX > oFrame.bounding_right) { + return false; + } + + if (iFlags & thdf_flip_vertical) { + if (-iTestY < oFrame.bounding_top || -iTestY > oFrame.bounding_bottom) { + return false; + } + } else { + if (iTestY < oFrame.bounding_top || iTestY > oFrame.bounding_bottom) { + return false; + } + } + + if (iFlags & thdf_bound_box_hit_test) { + return true; + } + + size_t iListIndex = oFrame.list_index; + for (;; ++iListIndex) { + uint16_t iElement = element_list[iListIndex]; + if (iElement >= element_count) { + break; + } + + const element& oElement = elements[iElement]; + if ((oElement.layer_id != 0 && + oLayers.layer_contents[oElement.layer] != oElement.layer_id) || + oElement.element_sprite_sheet == nullptr) { + continue; + } + + if (iFlags & thdf_flip_horizontal) { + unsigned int iWidth, iHeight; + oElement.element_sprite_sheet->get_sprite_size_unchecked( + oElement.sprite, &iWidth, &iHeight); + if (oElement.element_sprite_sheet->hit_test_sprite( + oElement.sprite, oElement.x + iWidth - iTestX, + iTestY - oElement.y, oElement.flags ^ thdf_flip_horizontal)) { + return true; + } + } else { + if (oElement.element_sprite_sheet->hit_test_sprite( + oElement.sprite, iTestX - oElement.x, iTestY - oElement.y, + oElement.flags)) { + return true; + } + } + } + + return false; } void animation_manager::draw_frame(render_target* pCanvas, size_t iFrame, - const ::layers& oLayers, - int iX, int iY, uint32_t iFlags) const -{ - if(iFrame >= frame_count) - return; + const ::layers& oLayers, int iX, int iY, + uint32_t iFlags) const { + if (iFrame >= frame_count) { + return; + } - uint32_t iPassOnFlags = iFlags & thdf_alt_palette; + uint32_t iPassOnFlags = iFlags & thdf_alt_palette; - size_t iListIndex = frames[iFrame].list_index; - for(; ; ++iListIndex) - { - uint16_t iElement = element_list[iListIndex]; - if(iElement >= element_count) - break; - - const element &oElement = elements[iElement]; - if (oElement.element_sprite_sheet == nullptr) - continue; - - if(oElement.layer_id != 0 && oLayers.layer_contents[oElement.layer] != oElement.layer_id) - { - // Some animations involving doctors (i.e. #72, #74, maybe others) - // only provide versions for heads W1 and B1, not W2 and B2. The - // quickest way to fix this is this dirty hack here, which draws - // the W1 layer as well as W2 if W2 is being used, and similarly - // for B1 / B2. A better fix would be to go into each animation - // which needs it, and duplicate the W1 / B1 layers to W2 / B2. - if(oElement.layer == 5 && oLayers.layer_contents[5] - 4 == oElement.layer_id) - /* don't skip */; - else - continue; - } - - if(iFlags & thdf_flip_horizontal) - { - unsigned int iWidth, iHeight; - oElement.element_sprite_sheet->get_sprite_size_unchecked(oElement.sprite, &iWidth, &iHeight); - - oElement.element_sprite_sheet->draw_sprite(pCanvas, oElement.sprite, iX - oElement.x - iWidth, - iY + oElement.y, iPassOnFlags | (oElement.flags ^ thdf_flip_horizontal)); - } - else - { - oElement.element_sprite_sheet->draw_sprite(pCanvas, oElement.sprite, - iX + oElement.x, iY + oElement.y, iPassOnFlags | oElement.flags); - } + size_t iListIndex = frames[iFrame].list_index; + for (;; ++iListIndex) { + uint16_t iElement = element_list[iListIndex]; + if (iElement >= element_count) { + break; } + + const element& oElement = elements[iElement]; + if (oElement.element_sprite_sheet == nullptr) { + continue; + } + + if (oElement.layer_id != 0 && + oLayers.layer_contents[oElement.layer] != oElement.layer_id) { + // Some animations involving doctors (i.e. #72, #74, maybe others) + // only provide versions for heads W1 and B1, not W2 and B2. The + // quickest way to fix this is this dirty hack here, which draws + // the W1 layer as well as W2 if W2 is being used, and similarly + // for B1 / B2. A better fix would be to go into each animation + // which needs it, and duplicate the W1 / B1 layers to W2 / B2. + if (oElement.layer == 5 && + oLayers.layer_contents[5] - 4 == oElement.layer_id) { + /* don't skip */; + } else { + continue; + } + } + + if (iFlags & thdf_flip_horizontal) { + unsigned int iWidth, iHeight; + oElement.element_sprite_sheet->get_sprite_size_unchecked( + oElement.sprite, &iWidth, &iHeight); + + oElement.element_sprite_sheet->draw_sprite( + pCanvas, oElement.sprite, iX - oElement.x - iWidth, iY + oElement.y, + iPassOnFlags | (oElement.flags ^ thdf_flip_horizontal)); + } else { + oElement.element_sprite_sheet->draw_sprite( + pCanvas, oElement.sprite, iX + oElement.x, iY + oElement.y, + iPassOnFlags | oElement.flags); + } + } } -size_t animation_manager::get_frame_sound(size_t iFrame) -{ - if(iFrame < frame_count) - return frames[iFrame].sound; - else - return 0; +size_t animation_manager::get_frame_sound(size_t iFrame) { + if (iFrame < frame_count) { + return frames[iFrame].sound; + } else { + return 0; + } } void animation_manager::get_frame_extent(size_t iFrame, const ::layers& oLayers, - int* pMinX, int* pMaxX, - int* pMinY, int* pMaxY, - uint32_t iFlags) const -{ - int iMinX = INT_MAX; - int iMaxX = INT_MIN; - int iMinY = INT_MAX; - int iMaxY = INT_MIN; - if(iFrame < frame_count) - { - size_t iListIndex = frames[iFrame].list_index; + int* pMinX, int* pMaxX, int* pMinY, + int* pMaxY, uint32_t iFlags) const { + int iMinX = INT_MAX; + int iMaxX = INT_MIN; + int iMinY = INT_MAX; + int iMaxY = INT_MIN; + if (iFrame < frame_count) { + size_t iListIndex = frames[iFrame].list_index; - for(; ; ++iListIndex) - { - uint16_t iElement = element_list[iListIndex]; - if(iElement >= element_count) - break; + for (;; ++iListIndex) { + uint16_t iElement = element_list[iListIndex]; + if (iElement >= element_count) { + break; + } - const element &oElement = elements[iElement]; - if((oElement.layer_id != 0 && oLayers.layer_contents[oElement.layer] != oElement.layer_id) - || oElement.element_sprite_sheet == nullptr) - { - continue; - } + const element& oElement = elements[iElement]; + if ((oElement.layer_id != 0 && + oLayers.layer_contents[oElement.layer] != oElement.layer_id) || + oElement.element_sprite_sheet == nullptr) { + continue; + } - int iX = oElement.x; - int iY = oElement.y; - unsigned int iWidth_, iHeight_; - oElement.element_sprite_sheet->get_sprite_size_unchecked(oElement.sprite, &iWidth_, &iHeight_); - int iWidth = static_cast(iWidth_); - int iHeight = static_cast(iHeight_); - if(iFlags & thdf_flip_horizontal) - iX = -(iX + iWidth); - if(iX < iMinX) - iMinX = iX; - if(iY < iMinY) - iMinY = iY; - if(iX + iWidth + 1 > iMaxX) - iMaxX = iX + iWidth + 1; - if(iY + iHeight + 1 > iMaxY) - iMaxY = iY + iHeight + 1; - } + int iX = oElement.x; + int iY = oElement.y; + unsigned int iWidth_, iHeight_; + oElement.element_sprite_sheet->get_sprite_size_unchecked( + oElement.sprite, &iWidth_, &iHeight_); + int iWidth = static_cast(iWidth_); + int iHeight = static_cast(iHeight_); + if (iFlags & thdf_flip_horizontal) iX = -(iX + iWidth); + if (iX < iMinX) iMinX = iX; + if (iY < iMinY) iMinY = iY; + if (iX + iWidth + 1 > iMaxX) iMaxX = iX + iWidth + 1; + if (iY + iHeight + 1 > iMaxY) iMaxY = iY + iHeight + 1; } - if(pMinX) - *pMinX = iMinX; - if(pMaxX) - *pMaxX = iMaxX; - if(pMinY) - *pMinY = iMinY; - if(pMaxY) - *pMaxY = iMaxY; + } + if (pMinX) *pMinX = iMinX; + if (pMaxX) *pMaxX = iMaxX; + if (pMinY) *pMinY = iMinY; + if (pMaxY) *pMaxY = iMaxY; } -chunk_renderer::chunk_renderer(int width, int height, uint8_t *buffer) -{ - data = buffer ? buffer : new uint8_t[width * height]; - ptr = data; - end = data + width * height; - x = 0; - y = 0; - this->width = width; - this->height = height; - skip_eol = false; +chunk_renderer::chunk_renderer(int width, int height, uint8_t* buffer) { + data = buffer ? buffer : new uint8_t[width * height]; + ptr = data; + end = data + width * height; + x = 0; + y = 0; + this->width = width; + this->height = height; + skip_eol = false; } -chunk_renderer::~chunk_renderer() -{ - delete[] data; +chunk_renderer::~chunk_renderer() { delete[] data; } + +uint8_t* chunk_renderer::take_data() { + uint8_t* buffer = data; + data = 0; + return buffer; } -uint8_t* chunk_renderer::take_data() -{ - uint8_t *buffer = data; - data = 0; - return buffer; +void chunk_renderer::chunk_fill_to_end_of_line(uint8_t value) { + if (x != 0 || !skip_eol) { + chunk_fill(width - x, value); + } + skip_eol = false; } -void chunk_renderer::chunk_fill_to_end_of_line(uint8_t value) -{ - if(x != 0 || !skip_eol) - { - chunk_fill(width - x, value); - } - skip_eol = false; +void chunk_renderer::chunk_finish(uint8_t value) { + chunk_fill(static_cast(end - ptr), value); } -void chunk_renderer::chunk_finish(uint8_t value) -{ - chunk_fill(static_cast(end - ptr), value); +void chunk_renderer::chunk_fill(int npixels, uint8_t value) { + fix_n_pixels(npixels); + if (npixels > 0) { + std::memset(ptr, value, npixels); + increment_position(npixels); + } } -void chunk_renderer::chunk_fill(int npixels, uint8_t value) -{ - fix_n_pixels(npixels); - if(npixels > 0) - { - std::memset(ptr, value, npixels); - increment_position(npixels); - } +void chunk_renderer::chunk_copy(int npixels, const uint8_t* in_data) { + fix_n_pixels(npixels); + if (npixels > 0) { + std::memcpy(ptr, in_data, npixels); + increment_position(npixels); + } } -void chunk_renderer::chunk_copy(int npixels, const uint8_t* in_data) -{ - fix_n_pixels(npixels); - if(npixels > 0) - { - std::memcpy(ptr, in_data, npixels); - increment_position(npixels); - } +void chunk_renderer::fix_n_pixels(int& npixels) const { + if (ptr + npixels > end) { + npixels = static_cast(end - ptr); + } } - -void chunk_renderer::fix_n_pixels(int& npixels) const -{ - if(ptr + npixels > end) - { - npixels = static_cast(end - ptr); - } +void chunk_renderer::increment_position(int npixels) { + ptr += npixels; + x += npixels; + y += x / width; + x = x % width; + skip_eol = true; } -void chunk_renderer::increment_position(int npixels) -{ - ptr += npixels; - x += npixels; - y += x / width; - x = x % width; - skip_eol = true; -} - -void chunk_renderer::decode_chunks(const uint8_t* data, int datalen, bool complex) -{ - if(complex) - { - while(!is_done() && datalen > 0) - { - uint8_t b = *data; - --datalen; +void chunk_renderer::decode_chunks(const uint8_t* data, int datalen, + bool complex) { + if (complex) { + while (!is_done() && datalen > 0) { + uint8_t b = *data; + --datalen; + ++data; + if (b == 0) { + chunk_fill_to_end_of_line(0xFF); + } else if (b < 0x40) { + int amt = b; + if (datalen < amt) amt = datalen; + chunk_copy(amt, data); + data += amt; + datalen -= amt; + } else if ((b & 0xC0) == 0x80) { + chunk_fill(b - 0x80, 0xFF); + } else { + int amt; + uint8_t colour = 0; + if (b == 0xFF) { + if (datalen < 2) { + break; + } + amt = (int)data[0]; + colour = data[1]; + data += 2; + datalen -= 2; + } else { + amt = b - 60 - (b & 0x80) / 2; + if (datalen > 0) { + colour = *data; ++data; - if(b == 0) - { - chunk_fill_to_end_of_line(0xFF); - } - else if(b < 0x40) - { - int amt = b; - if(datalen < amt) - amt = datalen; - chunk_copy(amt, data); - data += amt; - datalen -= amt; - } - else if((b & 0xC0) == 0x80) - { - chunk_fill(b - 0x80, 0xFF); - } - else - { - int amt; - uint8_t colour = 0; - if(b == 0xFF) - { - if(datalen < 2) - { - break; - } - amt = (int)data[0]; - colour = data[1]; - data += 2; - datalen -= 2; - } - else - { - amt = b - 60 - (b & 0x80) / 2; - if(datalen > 0) - { - colour = *data; - ++data; - --datalen; - } - } - chunk_fill(amt, colour); - } - } - } - else - { - while(!is_done() && datalen > 0) - { - uint8_t b = *data; --datalen; - ++data; - if(b == 0) - { - chunk_fill_to_end_of_line(0xFF); - } - else if(b < 0x80) - { - int amt = b; - if(datalen < amt) - amt = datalen; - chunk_copy(amt, data); - data += amt; - datalen -= amt; - } - else - { - chunk_fill(0x100 - b, 0xFF); - } + } } + chunk_fill(amt, colour); + } } - chunk_finish(0xFF); + } else { + while (!is_done() && datalen > 0) { + uint8_t b = *data; + --datalen; + ++data; + if (b == 0) { + chunk_fill_to_end_of_line(0xFF); + } else if (b < 0x80) { + int amt = b; + if (datalen < amt) amt = datalen; + chunk_copy(amt, data); + data += amt; + datalen -= amt; + } else { + chunk_fill(0x100 - b, 0xFF); + } + } + } + chunk_finish(0xFF); } namespace { -bool are_flags_set(uint32_t val, uint32_t flags) -{ - return (val & flags) == flags; +bool are_flags_set(uint32_t val, uint32_t flags) { + return (val & flags) == flags; } -} // namespace +} // namespace -void animation::draw(render_target* pCanvas, int iDestX, int iDestY) -{ - if(are_flags_set(flags, thdf_alpha_50 | thdf_alpha_75)) - return; +void animation::draw(render_target* pCanvas, int iDestX, int iDestY) { + if (are_flags_set(flags, thdf_alpha_50 | thdf_alpha_75)) return; - iDestX += x_relative_to_tile; - iDestY += y_relative_to_tile; - if(sound_to_play) - { - sound_player *pSounds = sound_player::get_singleton(); - if(pSounds) - pSounds->play_at(sound_to_play, iDestX, iDestY); - sound_to_play = 0; - } - if(manager) - { - if(flags & thdf_crop) - { - clip_rect rcOld, rcNew; - pCanvas->get_clip_rect(&rcOld); - rcNew.y = rcOld.y; - rcNew.h = rcOld.h; - rcNew.x = iDestX + (crop_column - 1) * 32; - rcNew.w = 64; - clip_rect_intersection(rcNew, rcOld); - pCanvas->set_clip_rect(&rcNew); - manager->draw_frame(pCanvas, frame_index, layers, iDestX, iDestY, - flags); - pCanvas->set_clip_rect(&rcOld); - } - else - manager->draw_frame(pCanvas, frame_index, layers, iDestX, iDestY, - flags); - } + iDestX += x_relative_to_tile; + iDestY += y_relative_to_tile; + if (sound_to_play) { + sound_player* pSounds = sound_player::get_singleton(); + if (pSounds) pSounds->play_at(sound_to_play, iDestX, iDestY); + sound_to_play = 0; + } + if (manager) { + if (flags & thdf_crop) { + clip_rect rcOld, rcNew; + pCanvas->get_clip_rect(&rcOld); + rcNew.y = rcOld.y; + rcNew.h = rcOld.h; + rcNew.x = iDestX + (crop_column - 1) * 32; + rcNew.w = 64; + clip_rect_intersection(rcNew, rcOld); + pCanvas->set_clip_rect(&rcNew); + manager->draw_frame(pCanvas, frame_index, layers, iDestX, iDestY, flags); + pCanvas->set_clip_rect(&rcOld); + } else + manager->draw_frame(pCanvas, frame_index, layers, iDestX, iDestY, flags); + } } -void animation::draw_child(render_target* pCanvas, int iDestX, int iDestY) -{ - if (are_flags_set(flags, thdf_alpha_50 | thdf_alpha_75)) - return; - if (are_flags_set(parent->flags, thdf_alpha_50 | thdf_alpha_75)) - return; - int iX = 0, iY = 0; - parent->get_marker(&iX, &iY); - iX += x_relative_to_tile + iDestX; - iY += y_relative_to_tile + iDestY; - if(sound_to_play) - { - sound_player *pSounds = sound_player::get_singleton(); - if(pSounds) - pSounds->play_at(sound_to_play, iX, iY); - sound_to_play = 0; - } - if(manager) - manager->draw_frame(pCanvas, frame_index, layers, iX, iY, flags); +void animation::draw_child(render_target* pCanvas, int iDestX, int iDestY) { + if (are_flags_set(flags, thdf_alpha_50 | thdf_alpha_75)) return; + if (are_flags_set(parent->flags, thdf_alpha_50 | thdf_alpha_75)) return; + int iX = 0, iY = 0; + parent->get_marker(&iX, &iY); + iX += x_relative_to_tile + iDestX; + iY += y_relative_to_tile + iDestY; + if (sound_to_play) { + sound_player* pSounds = sound_player::get_singleton(); + if (pSounds) pSounds->play_at(sound_to_play, iX, iY); + sound_to_play = 0; + } + if (manager) manager->draw_frame(pCanvas, frame_index, layers, iX, iY, flags); } -bool animation::hit_test_child(int iDestX, int iDestY, int iTestX, int iTestY) -{ - // TODO +bool animation::hit_test_child(int iDestX, int iDestY, int iTestX, int iTestY) { + // TODO + return false; +} + +namespace { + +void CalculateMorphRect(const clip_rect& rcOriginal, clip_rect& rcMorph, + int iYLow, int iYHigh) { + rcMorph = rcOriginal; + if (rcMorph.y < iYLow) { + rcMorph.h += rcMorph.y - iYLow; + rcMorph.y = iYLow; + } + if (rcMorph.y + rcMorph.h >= iYHigh) { + rcMorph.h = iYHigh - rcMorph.y - 1; + } +} + +} // namespace + +void animation::draw_morph(render_target* pCanvas, int iDestX, int iDestY) { + if (are_flags_set(flags, thdf_alpha_50 | thdf_alpha_75)) return; + + if (!manager) return; + + iDestX += x_relative_to_tile; + iDestY += y_relative_to_tile; + if (sound_to_play) { + sound_player* pSounds = sound_player::get_singleton(); + if (pSounds) pSounds->play_at(sound_to_play, iDestX, iDestY); + sound_to_play = 0; + } + + clip_rect oClipRect; + pCanvas->get_clip_rect(&oClipRect); + clip_rect oMorphRect; + CalculateMorphRect(oClipRect, oMorphRect, + iDestY + morph_target->x_relative_to_tile, + iDestY + morph_target->y_relative_to_tile + 1); + pCanvas->set_clip_rect(&oMorphRect); + manager->draw_frame(pCanvas, frame_index, layers, iDestX, iDestY, flags); + CalculateMorphRect(oClipRect, oMorphRect, + iDestY + morph_target->y_relative_to_tile, + iDestY + morph_target->speed.dx); + pCanvas->set_clip_rect(&oMorphRect); + manager->draw_frame(pCanvas, morph_target->frame_index, morph_target->layers, + iDestX, iDestY, morph_target->flags); + pCanvas->set_clip_rect(&oClipRect); +} + +bool animation::hit_test(int iDestX, int iDestY, int iTestX, int iTestY) { + if (are_flags_set(flags, thdf_alpha_50 | thdf_alpha_75)) { return false; + } + + if (manager == nullptr) { + return false; + } + + return manager->hit_test(frame_index, layers, x_relative_to_tile + iDestX, + y_relative_to_tile + iDestY, flags, iTestX, iTestY); +} + +bool animation::hit_test_morph(int iDestX, int iDestY, int iTestX, int iTestY) { + if (are_flags_set(flags, thdf_alpha_50 | thdf_alpha_75)) { + return false; + } + + if (manager == nullptr) { + return false; + } + + return manager->hit_test(frame_index, layers, x_relative_to_tile + iDestX, + y_relative_to_tile + iDestY, flags, iTestX, + iTestY) || + morph_target->hit_test(iDestX, iDestY, iTestX, iTestY); } namespace { - -void CalculateMorphRect(const clip_rect& rcOriginal, clip_rect& rcMorph, int iYLow, int iYHigh) -{ - rcMorph = rcOriginal; - if(rcMorph.y < iYLow) - { - rcMorph.h += rcMorph.y - iYLow; - rcMorph.y = iYLow; - } - if(rcMorph.y + rcMorph.h >= iYHigh) - { - rcMorph.h = iYHigh - rcMorph.y - 1; - } +bool THAnimation_hit_test_child(drawable* pSelf, int iDestX, int iDestY, + int iTestX, int iTestY) { + return reinterpret_cast(pSelf)->hit_test_child(iDestX, iDestY, + iTestX, iTestY); } -} // namespace - -void animation::draw_morph(render_target* pCanvas, int iDestX, int iDestY) -{ - if (are_flags_set(flags, thdf_alpha_50 | thdf_alpha_75)) - return; - - if(!manager) - return; - - iDestX += x_relative_to_tile; - iDestY += y_relative_to_tile; - if(sound_to_play) - { - sound_player *pSounds = sound_player::get_singleton(); - if(pSounds) - pSounds->play_at(sound_to_play, iDestX, iDestY); - sound_to_play = 0; - } - - clip_rect oClipRect; - pCanvas->get_clip_rect(&oClipRect); - clip_rect oMorphRect; - CalculateMorphRect(oClipRect, oMorphRect, iDestY + morph_target->x_relative_to_tile, - iDestY + morph_target->y_relative_to_tile + 1); - pCanvas->set_clip_rect(&oMorphRect); - manager->draw_frame(pCanvas, frame_index, layers, iDestX, iDestY, - flags); - CalculateMorphRect(oClipRect, oMorphRect, iDestY + morph_target->y_relative_to_tile, - iDestY + morph_target->speed.dx); - pCanvas->set_clip_rect(&oMorphRect); - manager->draw_frame(pCanvas, morph_target->frame_index, - morph_target->layers, iDestX, - iDestY, morph_target->flags); - pCanvas->set_clip_rect(&oClipRect); +void THAnimation_draw_child(drawable* pSelf, render_target* pCanvas, int iDestX, + int iDestY) { + reinterpret_cast(pSelf)->draw_child(pCanvas, iDestX, iDestY); } - -bool animation::hit_test(int iDestX, int iDestY, int iTestX, int iTestY) -{ - if (are_flags_set(flags, thdf_alpha_50 | thdf_alpha_75)) - return false; - if(manager == nullptr) - return false; - return manager->hit_test(frame_index, layers, x_relative_to_tile + iDestX, - y_relative_to_tile + iDestY, flags, iTestX, iTestY); +bool THAnimation_hit_test_morph(drawable* pSelf, int iDestX, int iDestY, + int iTestX, int iTestY) { + return reinterpret_cast(pSelf)->hit_test_morph(iDestX, iDestY, + iTestX, iTestY); } -bool animation::hit_test_morph(int iDestX, int iDestY, int iTestX, int iTestY) -{ - if (are_flags_set(flags, thdf_alpha_50 | thdf_alpha_75)) - return false; - if(manager == nullptr) - return false; - return manager->hit_test(frame_index, layers, x_relative_to_tile + iDestX, - y_relative_to_tile + iDestY, flags, iTestX, iTestY) || morph_target->hit_test( - iDestX, iDestY, iTestX, iTestY); +void THAnimation_draw_morph(drawable* pSelf, render_target* pCanvas, int iDestX, + int iDestY) { + reinterpret_cast(pSelf)->draw_morph(pCanvas, iDestX, iDestY); } -namespace { -bool THAnimation_hit_test_child(drawable* pSelf, int iDestX, int iDestY, int iTestX, int iTestY) -{ - return reinterpret_cast(pSelf)->hit_test_child(iDestX, iDestY, iTestX, iTestY); +bool THAnimation_hit_test(drawable* pSelf, int iDestX, int iDestY, int iTestX, + int iTestY) { + return reinterpret_cast(pSelf)->hit_test(iDestX, iDestY, iTestX, + iTestY); } -void THAnimation_draw_child(drawable* pSelf, render_target* pCanvas, int iDestX, int iDestY) -{ - reinterpret_cast(pSelf)->draw_child(pCanvas, iDestX, iDestY); +void THAnimation_draw(drawable* pSelf, render_target* pCanvas, int iDestX, + int iDestY) { + reinterpret_cast(pSelf)->draw(pCanvas, iDestX, iDestY); } -bool THAnimation_hit_test_morph(drawable* pSelf, int iDestX, int iDestY, int iTestX, int iTestY) -{ - return reinterpret_cast(pSelf)->hit_test_morph(iDestX, iDestY, iTestX, iTestY); +bool THAnimation_is_multiple_frame_animation(drawable* pSelf) { + animation* pAnimation = reinterpret_cast(pSelf); + if (pAnimation) { + size_t firstFrame = pAnimation->get_animation_manager()->get_first_frame( + pAnimation->get_animation()); + size_t nextFrame = + pAnimation->get_animation_manager()->get_next_frame(firstFrame); + return nextFrame != firstFrame; + } else { + return false; + } } -void THAnimation_draw_morph(drawable* pSelf, render_target* pCanvas, int iDestX, int iDestY) -{ - reinterpret_cast(pSelf)->draw_morph(pCanvas, iDestX, iDestY); +} // namespace + +animation_base::animation_base() { + x_relative_to_tile = 0; + y_relative_to_tile = 0; + for (int i = 0; i < 13; ++i) { + layers.layer_contents[i] = 0; + } + flags = 0; } -bool THAnimation_hit_test(drawable* pSelf, int iDestX, int iDestY, int iTestX, int iTestY) -{ - return reinterpret_cast(pSelf)->hit_test(iDestX, iDestY, iTestX, iTestY); +animation::animation() + : manager(nullptr), + morph_target(nullptr), + animation_index(0), + frame_index(0), + speed({0, 0}), + sound_to_play(0), + crop_column(0) { + draw_fn = THAnimation_draw; + hit_test_fn = THAnimation_hit_test; + is_multiple_frame_animation_fn = THAnimation_is_multiple_frame_animation; } -void THAnimation_draw(drawable* pSelf, render_target* pCanvas, int iDestX, int iDestY) -{ - reinterpret_cast(pSelf)->draw(pCanvas, iDestX, iDestY); -} +void animation::persist(lua_persist_writer* pWriter) const { + lua_State* L = pWriter->get_stack(); -bool THAnimation_is_multiple_frame_animation(drawable* pSelf) -{ - animation *pAnimation = reinterpret_cast(pSelf); - if(pAnimation) - { - size_t firstFrame = pAnimation->get_animation_manager()->get_first_frame(pAnimation->get_animation()); - size_t nextFrame = pAnimation->get_animation_manager()->get_next_frame(firstFrame); - return nextFrame != firstFrame; - } - else - return false; + // Write the next chained thing + lua_rawgeti(L, luaT_environindex, 2); + lua_pushlightuserdata(L, next); + lua_rawget(L, -2); + pWriter->fast_write_stack_object(-1); + lua_pop(L, 2); -} + // Write the drawable fields + pWriter->write_uint(flags); -} // namespace - -animation_base::animation_base() -{ - x_relative_to_tile = 0; - y_relative_to_tile = 0; - for(int i = 0; i < 13; ++i) - layers.layer_contents[i] = 0; - flags = 0; -} - -animation::animation(): - manager(nullptr), - morph_target(nullptr), - animation_index(0), - frame_index(0), - speed({0,0}), - sound_to_play(0), - crop_column(0) -{ - draw_fn = THAnimation_draw; - hit_test_fn = THAnimation_hit_test; - is_multiple_frame_animation_fn = THAnimation_is_multiple_frame_animation; -} - -void animation::persist(lua_persist_writer *pWriter) const -{ - lua_State *L = pWriter->get_stack(); - - // Write the next chained thing + if (draw_fn == THAnimation_draw && hit_test_fn == THAnimation_hit_test) { + pWriter->write_uint(1); + } else if (draw_fn == THAnimation_draw_child && + hit_test_fn == THAnimation_hit_test_child) { + pWriter->write_uint(2); + } else if (draw_fn == THAnimation_draw_morph && + hit_test_fn == THAnimation_hit_test_morph) { + // NB: Prior version of code used the number 3 here, and forgot + // to persist the morph target. + pWriter->write_uint(4); lua_rawgeti(L, luaT_environindex, 2); - lua_pushlightuserdata(L, next); + lua_pushlightuserdata(L, morph_target); lua_rawget(L, -2); - pWriter->fast_write_stack_object(-1); + pWriter->write_stack_object(-1); lua_pop(L, 2); + } else { + pWriter->write_uint(0); + } - // Write the drawable fields - pWriter->write_uint(flags); + // Write the simple fields + pWriter->write_uint(animation_index); + pWriter->write_uint(frame_index); + pWriter->write_int(x_relative_to_tile); + pWriter->write_int(y_relative_to_tile); - if (draw_fn == THAnimation_draw && hit_test_fn == THAnimation_hit_test) - pWriter->write_uint(1); - else if (draw_fn == THAnimation_draw_child && hit_test_fn == THAnimation_hit_test_child) - pWriter->write_uint(2); - else if(draw_fn == THAnimation_draw_morph && hit_test_fn == THAnimation_hit_test_morph) - { - // NB: Prior version of code used the number 3 here, and forgot - // to persist the morph target. - pWriter->write_uint(4); - lua_rawgeti(L, luaT_environindex, 2); - lua_pushlightuserdata(L, morph_target); - lua_rawget(L, -2); - pWriter->write_stack_object(-1); - lua_pop(L, 2); - } - else - pWriter->write_uint(0); + // Not a uint, for compatibility + pWriter->write_int((int)sound_to_play); - // Write the simple fields - pWriter->write_uint(animation_index); - pWriter->write_uint(frame_index); - pWriter->write_int(x_relative_to_tile); - pWriter->write_int(y_relative_to_tile); - pWriter->write_int((int)sound_to_play); // Not a uint, for compatibility - pWriter->write_int(0); // For compatibility - if(flags & thdf_crop) - pWriter->write_int(crop_column); + // For compatibility + pWriter->write_int(0); - // Write the unioned fields - if(draw_fn != THAnimation_draw_child) - { - pWriter->write_int(speed.dx); - pWriter->write_int(speed.dy); - } - else - { - lua_rawgeti(L, luaT_environindex, 2); - lua_pushlightuserdata(L, parent); - lua_rawget(L, -2); - pWriter->write_stack_object(-1); - lua_pop(L, 2); - } + if (flags & thdf_crop) { + pWriter->write_int(crop_column); + } - // Write the layers - int iNumLayers = 13; - for( ; iNumLayers >= 1; --iNumLayers) - { - if(layers.layer_contents[iNumLayers - 1] != 0) - break; - } - pWriter->write_uint(iNumLayers); - pWriter->write_byte_stream(layers.layer_contents, iNumLayers); + // Write the unioned fields + if (draw_fn != THAnimation_draw_child) { + pWriter->write_int(speed.dx); + pWriter->write_int(speed.dy); + } else { + lua_rawgeti(L, luaT_environindex, 2); + lua_pushlightuserdata(L, parent); + lua_rawget(L, -2); + pWriter->write_stack_object(-1); + lua_pop(L, 2); + } + + // Write the layers + int iNumLayers = 13; + for (; iNumLayers >= 1; --iNumLayers) { + if (layers.layer_contents[iNumLayers - 1] != 0) break; + } + pWriter->write_uint(iNumLayers); + pWriter->write_byte_stream(layers.layer_contents, iNumLayers); } -void animation::depersist(lua_persist_reader *pReader) -{ - lua_State *L = pReader->get_stack(); +void animation::depersist(lua_persist_reader* pReader) { + lua_State* L = pReader->get_stack(); - do - { - // Read the chain - if(!pReader->read_stack_object()) - break; - next = reinterpret_cast(lua_touserdata(L, -1)); - if(next) - next->prev = this; - lua_pop(L, 1); + do { + // Read the chain + if (!pReader->read_stack_object()) break; + next = reinterpret_cast(lua_touserdata(L, -1)); + if (next) next->prev = this; + lua_pop(L, 1); - // Read drawable fields - if(!pReader->read_uint(flags)) - break; - int iFunctionSet; - if(!pReader->read_uint(iFunctionSet)) - break; - switch(iFunctionSet) - { - case 3: - // 3 should be the morph set, but the actual morph target is - // missing, so settle for a graphical bug rather than a segfault - // by reverting to the normal function set. - case 1: - draw_fn = THAnimation_draw; - hit_test_fn = THAnimation_hit_test; - break; - case 2: - draw_fn = THAnimation_draw_child; - hit_test_fn = THAnimation_hit_test_child; - break; - case 4: - draw_fn = THAnimation_draw_morph; - hit_test_fn = THAnimation_hit_test_morph; - pReader->read_stack_object(); - morph_target = reinterpret_cast(lua_touserdata(L, -1)); - lua_pop(L, 1); - break; - default: - pReader->set_error(lua_pushfstring(L, "Unknown animation function set #%i", iFunctionSet)); - return; - } - - // Read the simple fields - if(!pReader->read_uint(animation_index)) - break; - if(!pReader->read_uint(frame_index)) - break; - if(!pReader->read_int(x_relative_to_tile)) - break; - if(!pReader->read_int(y_relative_to_tile)) - break; - int iDummy; - if(!pReader->read_int(iDummy)) - break; - if(iDummy >= 0) - sound_to_play = (unsigned int)iDummy; - if(!pReader->read_int(iDummy)) - break; - if(flags & thdf_crop) - { - if(!pReader->read_int(crop_column)) - break; - } - else - crop_column = 0; - - // Read the unioned fields - if(draw_fn != THAnimation_draw_child) - { - if(!pReader->read_int(speed.dx)) - break; - if(!pReader->read_int(speed.dy)) - break; - } - else - { - if(!pReader->read_stack_object()) - break; - parent = (animation*)lua_touserdata(L, -1); - lua_pop(L, 1); - } - - // Read the layers - std::memset(layers.layer_contents, 0, sizeof(layers.layer_contents)); - int iNumLayers; - if(!pReader->read_uint(iNumLayers)) - break; - if(iNumLayers > 13) - { - if(!pReader->read_byte_stream(layers.layer_contents, 13)) - break; - if(!pReader->read_byte_stream(nullptr, iNumLayers - 13)) - break; - } - else - { - if(!pReader->read_byte_stream(layers.layer_contents, iNumLayers)) - break; - } - - // Fix the m_pAnimator field - luaT_getenvfield(L, 2, "animator"); - manager = (animation_manager*)lua_touserdata(L, -1); - lua_pop(L, 1); - - return; - } while(false); - - pReader->set_error("Cannot depersist animation instance"); -} - -void animation::tick() -{ - frame_index = manager->get_next_frame(frame_index); - if(draw_fn != THAnimation_draw_child) - { - x_relative_to_tile += speed.dx; - y_relative_to_tile += speed.dy; - } - if(morph_target) - { - morph_target->y_relative_to_tile += morph_target->speed.dy; - if(morph_target->y_relative_to_tile < morph_target->x_relative_to_tile) - morph_target->y_relative_to_tile = morph_target->x_relative_to_tile; - } - - //Female flying to heaven sound fix: - if(frame_index == 6987) - sound_to_play = 123; - else - sound_to_play = manager->get_frame_sound(frame_index); -} - -void animation_base::remove_from_tile() -{ - link_list::remove_from_list(); -} - -void animation_base::attach_to_tile(map_tile *pMapNode, int layer) -{ - remove_from_tile(); - link_list *pList; - if(flags & thdf_early_list) - pList = &pMapNode->oEarlyEntities; - else - pList = pMapNode; - - this->set_drawing_layer(layer); - - while(pList->next && pList->next->get_drawing_layer() < layer) - { - pList = pList->next; - } - - prev = pList; - if(pList->next != nullptr) - { - pList->next->prev = this; - this->next = pList->next; - } - else - { - next = nullptr; - } - pList->next = this; -} - -void animation::set_parent(animation *pParent) -{ - remove_from_tile(); - if(pParent == nullptr) - { + // Read drawable fields + if (!pReader->read_uint(flags)) break; + int iFunctionSet; + if (!pReader->read_uint(iFunctionSet)) break; + switch (iFunctionSet) { + case 3: + // 3 should be the morph set, but the actual morph target is + // missing, so settle for a graphical bug rather than a segfault + // by reverting to the normal function set. + case 1: draw_fn = THAnimation_draw; hit_test_fn = THAnimation_hit_test; - speed = { 0, 0 }; - } - else - { + break; + case 2: draw_fn = THAnimation_draw_child; hit_test_fn = THAnimation_hit_test_child; - parent = pParent; - next = parent->next; - if(next) - next->prev = this; - prev = parent; - parent->next = this; - } -} - -void animation::set_animation(animation_manager* pManager, size_t iAnimation) -{ - manager = pManager; - animation_index = iAnimation; - frame_index = pManager->get_first_frame(iAnimation); - if(morph_target) - { - morph_target = nullptr; - draw_fn = THAnimation_draw; - hit_test_fn = THAnimation_hit_test; - } -} - -bool animation::get_marker(int* pX, int* pY) -{ - if(!manager || !manager->get_frame_marker(frame_index, pX, pY)) - return false; - if(flags & thdf_flip_horizontal) - *pX = -*pX; - *pX += x_relative_to_tile; - *pY += y_relative_to_tile + 16; - return true; -} - -bool animation::get_secondary_marker(int* pX, int* pY) -{ - if(!manager || !manager->get_frame_secondary_marker(frame_index, pX, pY)) - return false; - if(flags & thdf_flip_horizontal) - *pX = -*pX; - *pX += x_relative_to_tile; - *pY += y_relative_to_tile + 16; - return true; -} - -namespace { - -int GetAnimationDurationAndExtent(animation_manager *pManager, - size_t iFrame, - const ::layers& oLayers, - int* pMinY, int* pMaxY, - uint32_t iFlags) -{ - int iMinY = INT_MAX; - int iMaxY = INT_MIN; - int iDuration = 0; - size_t iCurFrame = iFrame; - do - { - int iFrameMinY; - int iFrameMaxY; - pManager->get_frame_extent(iCurFrame, oLayers, nullptr, nullptr, &iFrameMinY, &iFrameMaxY, iFlags); - if(iFrameMinY < iMinY) - iMinY = iFrameMinY; - if(iFrameMaxY > iMaxY) - iMaxY = iFrameMaxY; - iCurFrame = pManager->get_next_frame(iCurFrame); - ++iDuration; - } while(iCurFrame != iFrame); - if(pMinY) - *pMinY = iMinY; - if(pMaxY) - *pMaxY = iMaxY; - return iDuration; -} - -} // namespace - -void animation::set_morph_target(animation *pMorphTarget, unsigned int iDurationFactor) -{ - morph_target = pMorphTarget; - draw_fn = THAnimation_draw_morph; - hit_test_fn = THAnimation_hit_test_morph; - - /* Morphing is the process by which two animations are combined to give a - single animation of one animation turning into another. At the moment, - morphing is done by having a y value, above which the original animation is - rendered, and below which the new animation is rendered, and having the y - value move upward a bit each frame. - One example of where this is used is when transparent or invisible patients - are cured at the pharmacy cabinet. - The process of morphing requires four state variables, which are stored in - the morph target animation: - * The y value top limit - morph_target->x - * The y value threshold - morph_target->y - * The y value bottom limit - morph_target->speed.dx - * The y value increment per frame - morph_target->speed.dy - This obviously means that the morph target should not be ticked or rendered - as it's position and speed contain other values. - */ - - int iOrigMinY, iOrigMaxY; - int iMorphMinY, iMorphMaxY; - - int iOriginalDuration = GetAnimationDurationAndExtent(manager, frame_index, layers, &iOrigMinY, - &iOrigMaxY, flags); - int iMorphDuration = GetAnimationDurationAndExtent(morph_target->manager, - morph_target->frame_index, - morph_target->layers, &iMorphMinY, - &iMorphMaxY, morph_target->flags); - if(iMorphDuration > iOriginalDuration) - iMorphDuration = iOriginalDuration; - - iMorphDuration *= iDurationFactor; - if(iOrigMinY < iMorphMinY) - morph_target->x_relative_to_tile = iOrigMinY; - else - morph_target->x_relative_to_tile = iMorphMinY; - - if(iOrigMaxY > iMorphMaxY) - morph_target->speed.dx = iOrigMaxY; - else - morph_target->speed.dx = iMorphMaxY; - - int iDist = morph_target->x_relative_to_tile - morph_target->speed.dx; - morph_target->speed.dy = (iDist - iMorphDuration + 1) / iMorphDuration; - morph_target->y_relative_to_tile = morph_target->speed.dx; -} - -void animation::set_frame(size_t iFrame) -{ - frame_index = iFrame; -} - -void animation_base::set_layer(int iLayer, int iId) -{ - if(0 <= iLayer && iLayer <= 12) - { - layers.layer_contents[iLayer] = static_cast(iId); - } -} - -namespace { - -bool THSpriteRenderList_hit_test(drawable* pSelf, int iDestX, - int iDestY, int iTestX, int iTestY) -{ - return reinterpret_cast(pSelf)-> - hit_test(iDestX, iDestY, iTestX, iTestY); -} - -void THSpriteRenderList_draw(drawable* pSelf, render_target* pCanvas, - int iDestX, int iDestY) -{ - reinterpret_cast(pSelf)-> - draw(pCanvas, iDestX, iDestY); -} - -bool THSpriteRenderList_is_multiple_frame_animation(drawable* pSelf) -{ - return false; -} - -} // namespace - -sprite_render_list::sprite_render_list() -{ - draw_fn = THSpriteRenderList_draw; - hit_test_fn = THSpriteRenderList_hit_test; - is_multiple_frame_animation_fn = THSpriteRenderList_is_multiple_frame_animation; - buffer_size = 0; - sprite_count = 0; - sheet = nullptr; - sprites = nullptr; - dx_per_tick = 0; - dy_per_tick = 0; - lifetime = -1; -} - -sprite_render_list::~sprite_render_list() -{ - delete[] sprites; -} - -void sprite_render_list::tick() -{ - x_relative_to_tile += dx_per_tick; - y_relative_to_tile += dy_per_tick; - if(lifetime > 0) - --lifetime; -} - -void sprite_render_list::draw(render_target* pCanvas, int iDestX, int iDestY) -{ - if(!sheet) + break; + case 4: + draw_fn = THAnimation_draw_morph; + hit_test_fn = THAnimation_hit_test_morph; + pReader->read_stack_object(); + morph_target = reinterpret_cast(lua_touserdata(L, -1)); + lua_pop(L, 1); + break; + default: + pReader->set_error(lua_pushfstring( + L, "Unknown animation function set #%i", iFunctionSet)); return; - - iDestX += x_relative_to_tile; - iDestY += y_relative_to_tile; - for(sprite *pSprite = sprites, *pLast = sprites + sprite_count; - pSprite != pLast; ++pSprite) - { - sheet->draw_sprite(pCanvas, pSprite->index, - iDestX + pSprite->x, iDestY + pSprite->y, flags); - } -} - -bool sprite_render_list::hit_test(int iDestX, int iDestY, int iTestX, int iTestY) -{ - // TODO - return false; -} - -void sprite_render_list::set_lifetime(int iLifetime) -{ - if(iLifetime < 0) - iLifetime = -1; - lifetime = iLifetime; -} - -void sprite_render_list::append_sprite(size_t iSprite, int iX, int iY) -{ - if(buffer_size == sprite_count) - { - int iNewSize = buffer_size * 2; - if(iNewSize == 0) - iNewSize = 4; - sprite* pNewSprites = new sprite[iNewSize]; -#ifdef _MSC_VER -#pragma warning(disable: 4996) -#endif - std::copy(sprites, sprites + sprite_count, pNewSprites); -#ifdef _MSC_VER -#pragma warning(default: 4996) -#endif - delete[] sprites; - sprites = pNewSprites; - buffer_size = iNewSize; - } - sprites[sprite_count].index = iSprite; - sprites[sprite_count].x = iX; - sprites[sprite_count].y = iY; - ++sprite_count; -} - -void sprite_render_list::persist(lua_persist_writer *pWriter) const -{ - lua_State *L = pWriter->get_stack(); - - pWriter->write_uint(sprite_count); - pWriter->write_uint(flags); - pWriter->write_int(x_relative_to_tile); - pWriter->write_int(y_relative_to_tile); - pWriter->write_int(dx_per_tick); - pWriter->write_int(dy_per_tick); - pWriter->write_int(lifetime); - for(sprite *pSprite = sprites, *pLast = sprites + sprite_count; - pSprite != pLast; ++pSprite) - { - pWriter->write_uint(pSprite->index); - pWriter->write_int(pSprite->x); - pWriter->write_int(pSprite->y); } - // Write the layers - int iNumLayers = 13; - for( ; iNumLayers >= 1; --iNumLayers) - { - if(layers.layer_contents[iNumLayers - 1] != 0) - break; + // Read the simple fields + if (!pReader->read_uint(animation_index)) break; + if (!pReader->read_uint(frame_index)) break; + if (!pReader->read_int(x_relative_to_tile)) break; + if (!pReader->read_int(y_relative_to_tile)) break; + int iDummy; + if (!pReader->read_int(iDummy)) break; + if (iDummy >= 0) sound_to_play = (unsigned int)iDummy; + if (!pReader->read_int(iDummy)) break; + if (flags & thdf_crop) { + if (!pReader->read_int(crop_column)) { + break; + } + } else { + crop_column = 0; } - pWriter->write_uint(iNumLayers); - pWriter->write_byte_stream(layers.layer_contents, iNumLayers); - // Write the next chained thing - lua_rawgeti(L, luaT_environindex, 2); - lua_pushlightuserdata(L, next); - lua_rawget(L, -2); - pWriter->fast_write_stack_object(-1); - lua_pop(L, 2); -} - -void sprite_render_list::depersist(lua_persist_reader *pReader) -{ - lua_State *L = pReader->get_stack(); - - if(!pReader->read_uint(sprite_count)) - return; - buffer_size = sprite_count; - delete[] sprites; - sprites = new sprite[buffer_size]; - - if(!pReader->read_uint(flags)) - return; - if(!pReader->read_int(x_relative_to_tile)) - return; - if(!pReader->read_int(y_relative_to_tile)) - return; - if(!pReader->read_int(dx_per_tick)) - return; - if(!pReader->read_int(dy_per_tick)) - return; - if(!pReader->read_int(lifetime)) - return; - for(sprite *pSprite = sprites, *pLast = sprites + sprite_count; - pSprite != pLast; ++pSprite) - { - if(!pReader->read_uint(pSprite->index)) - return; - if(!pReader->read_int(pSprite->x)) - return; - if(!pReader->read_int(pSprite->y)) - return; + // Read the unioned fields + if (draw_fn != THAnimation_draw_child) { + if (!pReader->read_int(speed.dx)) break; + if (!pReader->read_int(speed.dy)) break; + } else { + if (!pReader->read_stack_object()) break; + parent = (animation*)lua_touserdata(L, -1); + lua_pop(L, 1); } // Read the layers std::memset(layers.layer_contents, 0, sizeof(layers.layer_contents)); int iNumLayers; - if(!pReader->read_uint(iNumLayers)) - return; - if(iNumLayers > 13) - { - if(!pReader->read_byte_stream(layers.layer_contents, 13)) - return; - if(!pReader->read_byte_stream(nullptr, iNumLayers - 13)) - return; - } - else - { - if(!pReader->read_byte_stream(layers.layer_contents, iNumLayers)) - return; + if (!pReader->read_uint(iNumLayers)) { + break; } - // Read the chain - if(!pReader->read_stack_object()) - return; - next = reinterpret_cast(lua_touserdata(L, -1)); - if(next) - next->prev = this; + if (iNumLayers > 13) { + if (!pReader->read_byte_stream(layers.layer_contents, 13)) break; + if (!pReader->read_byte_stream(nullptr, iNumLayers - 13)) break; + } else { + if (!pReader->read_byte_stream(layers.layer_contents, iNumLayers)) break; + } + + // Fix the m_pAnimator field + luaT_getenvfield(L, 2, "animator"); + manager = (animation_manager*)lua_touserdata(L, -1); lua_pop(L, 1); - // Fix the sheet field - luaT_getenvfield(L, 2, "sheet"); - sheet = (sprite_sheet*)lua_touserdata(L, -1); - lua_pop(L, 1); + return; + } while (false); + + pReader->set_error("Cannot depersist animation instance"); +} + +void animation::tick() { + frame_index = manager->get_next_frame(frame_index); + if (draw_fn != THAnimation_draw_child) { + x_relative_to_tile += speed.dx; + y_relative_to_tile += speed.dy; + } + + if (morph_target) { + morph_target->y_relative_to_tile += morph_target->speed.dy; + if (morph_target->y_relative_to_tile < morph_target->x_relative_to_tile) { + morph_target->y_relative_to_tile = morph_target->x_relative_to_tile; + } + } + + // Female flying to heaven sound fix: + if (frame_index == 6987) { + sound_to_play = 123; + } else { + sound_to_play = manager->get_frame_sound(frame_index); + } +} + +void animation_base::remove_from_tile() { link_list::remove_from_list(); } + +void animation_base::attach_to_tile(map_tile* pMapNode, int layer) { + remove_from_tile(); + link_list* pList; + if (flags & thdf_early_list) { + pList = &pMapNode->oEarlyEntities; + } else { + pList = pMapNode; + } + + this->set_drawing_layer(layer); + + while (pList->next && pList->next->get_drawing_layer() < layer) { + pList = pList->next; + } + + prev = pList; + if (pList->next != nullptr) { + pList->next->prev = this; + this->next = pList->next; + } else { + next = nullptr; + } + pList->next = this; +} + +void animation::set_parent(animation* pParent) { + remove_from_tile(); + if (pParent == nullptr) { + draw_fn = THAnimation_draw; + hit_test_fn = THAnimation_hit_test; + speed = {0, 0}; + } else { + draw_fn = THAnimation_draw_child; + hit_test_fn = THAnimation_hit_test_child; + parent = pParent; + next = parent->next; + if (next) next->prev = this; + prev = parent; + parent->next = this; + } +} + +void animation::set_animation(animation_manager* pManager, size_t iAnimation) { + manager = pManager; + animation_index = iAnimation; + frame_index = pManager->get_first_frame(iAnimation); + if (morph_target) { + morph_target = nullptr; + draw_fn = THAnimation_draw; + hit_test_fn = THAnimation_hit_test; + } +} + +bool animation::get_marker(int* pX, int* pY) { + if (!manager || !manager->get_frame_marker(frame_index, pX, pY)) { + return false; + } + + if (flags & thdf_flip_horizontal) { + *pX = -*pX; + } + + *pX += x_relative_to_tile; + *pY += y_relative_to_tile + 16; + return true; +} + +bool animation::get_secondary_marker(int* pX, int* pY) { + if (!manager || !manager->get_frame_secondary_marker(frame_index, pX, pY)) { + return false; + } + + if (flags & thdf_flip_horizontal) { + *pX = -*pX; + } + + *pX += x_relative_to_tile; + *pY += y_relative_to_tile + 16; + return true; +} + +namespace { + +int GetAnimationDurationAndExtent(animation_manager* pManager, size_t iFrame, + const ::layers& oLayers, int* pMinY, + int* pMaxY, uint32_t iFlags) { + int iMinY = INT_MAX; + int iMaxY = INT_MIN; + int iDuration = 0; + size_t iCurFrame = iFrame; + do { + int iFrameMinY; + int iFrameMaxY; + pManager->get_frame_extent(iCurFrame, oLayers, nullptr, nullptr, + &iFrameMinY, &iFrameMaxY, iFlags); + if (iFrameMinY < iMinY) iMinY = iFrameMinY; + if (iFrameMaxY > iMaxY) iMaxY = iFrameMaxY; + iCurFrame = pManager->get_next_frame(iCurFrame); + ++iDuration; + } while (iCurFrame != iFrame); + if (pMinY) { + *pMinY = iMinY; + } + if (pMaxY) { + *pMaxY = iMaxY; + } + return iDuration; +} + +} // namespace + +void animation::set_morph_target(animation* pMorphTarget, + unsigned int iDurationFactor) { + morph_target = pMorphTarget; + draw_fn = THAnimation_draw_morph; + hit_test_fn = THAnimation_hit_test_morph; + + /* Morphing is the process by which two animations are combined to give a + single animation of one animation turning into another. At the moment, + morphing is done by having a y value, above which the original animation is + rendered, and below which the new animation is rendered, and having the y + value move upward a bit each frame. + One example of where this is used is when transparent or invisible patients + are cured at the pharmacy cabinet. + The process of morphing requires four state variables, which are stored in + the morph target animation: + * The y value top limit - morph_target->x + * The y value threshold - morph_target->y + * The y value bottom limit - morph_target->speed.dx + * The y value increment per frame - morph_target->speed.dy + This obviously means that the morph target should not be ticked or rendered + as it's position and speed contain other values. + */ + + int iOrigMinY, iOrigMaxY; + int iMorphMinY, iMorphMaxY; + + int iOriginalDuration = GetAnimationDurationAndExtent( + manager, frame_index, layers, &iOrigMinY, &iOrigMaxY, flags); + int iMorphDuration = GetAnimationDurationAndExtent( + morph_target->manager, morph_target->frame_index, morph_target->layers, + &iMorphMinY, &iMorphMaxY, morph_target->flags); + if (iMorphDuration > iOriginalDuration) { + iMorphDuration = iOriginalDuration; + } + + iMorphDuration *= iDurationFactor; + if (iOrigMinY < iMorphMinY) { + morph_target->x_relative_to_tile = iOrigMinY; + } else { + morph_target->x_relative_to_tile = iMorphMinY; + } + + if (iOrigMaxY > iMorphMaxY) { + morph_target->speed.dx = iOrigMaxY; + } else { + morph_target->speed.dx = iMorphMaxY; + } + + int iDist = morph_target->x_relative_to_tile - morph_target->speed.dx; + morph_target->speed.dy = (iDist - iMorphDuration + 1) / iMorphDuration; + morph_target->y_relative_to_tile = morph_target->speed.dx; +} + +void animation::set_frame(size_t iFrame) { frame_index = iFrame; } + +void animation_base::set_layer(int iLayer, int iId) { + if (0 <= iLayer && iLayer <= 12) { + layers.layer_contents[iLayer] = static_cast(iId); + } +} + +namespace { + +bool THSpriteRenderList_hit_test(drawable* pSelf, int iDestX, int iDestY, + int iTestX, int iTestY) { + return reinterpret_cast(pSelf)->hit_test(iDestX, iDestY, + iTestX, iTestY); +} + +void THSpriteRenderList_draw(drawable* pSelf, render_target* pCanvas, + int iDestX, int iDestY) { + reinterpret_cast(pSelf)->draw(pCanvas, iDestX, iDestY); +} + +bool THSpriteRenderList_is_multiple_frame_animation(drawable* pSelf) { + return false; +} + +} // namespace + +sprite_render_list::sprite_render_list() { + draw_fn = THSpriteRenderList_draw; + hit_test_fn = THSpriteRenderList_hit_test; + is_multiple_frame_animation_fn = + THSpriteRenderList_is_multiple_frame_animation; + buffer_size = 0; + sprite_count = 0; + sheet = nullptr; + sprites = nullptr; + dx_per_tick = 0; + dy_per_tick = 0; + lifetime = -1; +} + +sprite_render_list::~sprite_render_list() { delete[] sprites; } + +void sprite_render_list::tick() { + x_relative_to_tile += dx_per_tick; + y_relative_to_tile += dy_per_tick; + if (lifetime > 0) { + --lifetime; + } +} + +void sprite_render_list::draw(render_target* pCanvas, int iDestX, int iDestY) { + if (!sheet) { + return; + } + + iDestX += x_relative_to_tile; + iDestY += y_relative_to_tile; + + sprite* pLast = sprites + sprite_count; + for (sprite* pSprite = sprites; pSprite != pLast; ++pSprite) { + sheet->draw_sprite(pCanvas, pSprite->index, iDestX + pSprite->x, + iDestY + pSprite->y, flags); + } +} + +bool sprite_render_list::hit_test(int iDestX, int iDestY, int iTestX, + int iTestY) { + // TODO + return false; +} + +void sprite_render_list::set_lifetime(int iLifetime) { + if (iLifetime < 0) { + iLifetime = -1; + } + lifetime = iLifetime; +} + +void sprite_render_list::append_sprite(size_t iSprite, int iX, int iY) { + if (buffer_size == sprite_count) { + int iNewSize = buffer_size * 2; + if (iNewSize == 0) { + iNewSize = 4; + } + sprite* pNewSprites = new sprite[iNewSize]; +#ifdef _MSC_VER +#pragma warning(disable : 4996) +#endif + std::copy(sprites, sprites + sprite_count, pNewSprites); +#ifdef _MSC_VER +#pragma warning(default : 4996) +#endif + delete[] sprites; + sprites = pNewSprites; + buffer_size = iNewSize; + } + sprites[sprite_count].index = iSprite; + sprites[sprite_count].x = iX; + sprites[sprite_count].y = iY; + ++sprite_count; +} + +void sprite_render_list::persist(lua_persist_writer* pWriter) const { + lua_State* L = pWriter->get_stack(); + + pWriter->write_uint(sprite_count); + pWriter->write_uint(flags); + pWriter->write_int(x_relative_to_tile); + pWriter->write_int(y_relative_to_tile); + pWriter->write_int(dx_per_tick); + pWriter->write_int(dy_per_tick); + pWriter->write_int(lifetime); + + sprite* pLast = sprites + sprite_count; + for (sprite* pSprite = sprites; pSprite != pLast; ++pSprite) { + pWriter->write_uint(pSprite->index); + pWriter->write_int(pSprite->x); + pWriter->write_int(pSprite->y); + } + + // Write the layers + int iNumLayers = 13; + for (; iNumLayers >= 1; --iNumLayers) { + if (layers.layer_contents[iNumLayers - 1] != 0) { + break; + } + } + pWriter->write_uint(iNumLayers); + pWriter->write_byte_stream(layers.layer_contents, iNumLayers); + + // Write the next chained thing + lua_rawgeti(L, luaT_environindex, 2); + lua_pushlightuserdata(L, next); + lua_rawget(L, -2); + pWriter->fast_write_stack_object(-1); + lua_pop(L, 2); +} + +void sprite_render_list::depersist(lua_persist_reader* pReader) { + lua_State* L = pReader->get_stack(); + + if (!pReader->read_uint(sprite_count)) return; + buffer_size = sprite_count; + delete[] sprites; + sprites = new sprite[buffer_size]; + + if (!pReader->read_uint(flags)) return; + if (!pReader->read_int(x_relative_to_tile)) return; + if (!pReader->read_int(y_relative_to_tile)) return; + if (!pReader->read_int(dx_per_tick)) return; + if (!pReader->read_int(dy_per_tick)) return; + if (!pReader->read_int(lifetime)) return; + for (sprite *pSprite = sprites, *pLast = sprites + sprite_count; + pSprite != pLast; ++pSprite) { + if (!pReader->read_uint(pSprite->index)) return; + if (!pReader->read_int(pSprite->x)) return; + if (!pReader->read_int(pSprite->y)) return; + } + + // Read the layers + std::memset(layers.layer_contents, 0, sizeof(layers.layer_contents)); + int iNumLayers; + if (!pReader->read_uint(iNumLayers)) { + return; + } + + if (iNumLayers > 13) { + if (!pReader->read_byte_stream(layers.layer_contents, 13)) { + return; + } + + if (!pReader->read_byte_stream(nullptr, iNumLayers - 13)) { + return; + } + } else { + if (!pReader->read_byte_stream(layers.layer_contents, iNumLayers)) { + return; + } + } + + // Read the chain + if (!pReader->read_stack_object()) { + return; + } + + next = reinterpret_cast(lua_touserdata(L, -1)); + if (next) { + next->prev = this; + } + lua_pop(L, 1); + + // Fix the sheet field + luaT_getenvfield(L, 2, "sheet"); + sheet = (sprite_sheet*)lua_touserdata(L, -1); + lua_pop(L, 1); } diff --git a/CorsixTH/Src/th_gfx.h b/CorsixTH/Src/th_gfx.h index ea7cb7b8..cd3b9c30 100644 --- a/CorsixTH/Src/th_gfx.h +++ b/CorsixTH/Src/th_gfx.h @@ -22,79 +22,71 @@ SOFTWARE. #ifndef CORSIX_TH_TH_GFX_H_ #define CORSIX_TH_TH_GFX_H_ +#include +#include +#include #include "th.h" +#include "th_gfx_sdl.h" class lua_persist_reader; class lua_persist_writer; -enum class scaled_items { - none, - sprite_sheets, - bitmaps, - all -}; - -#include "th_gfx_sdl.h" -#include "th_gfx_font.h" -#include -#include -#include +enum class scaled_items { none, sprite_sheets, bitmaps, all }; void clip_rect_intersection(clip_rect& rcClip, const clip_rect& rcIntersect); //! Bitflags for drawing operations -enum draw_flags : uint32_t -{ - /** Sprite drawing flags **/ - /* Where possible, designed to be the same values used by TH data files */ +enum draw_flags : uint32_t { + /** Sprite drawing flags **/ + /* Where possible, designed to be the same values used by TH data files */ - //! Draw with the left becoming the right and vice versa - thdf_flip_horizontal = 1 << 0, - //! Draw with the top becoming the bottom and vice versa - thdf_flip_vertical = 1 << 1, - //! Draw with 50% transparency - thdf_alpha_50 = 1 << 2, - //! Draw with 75% transparency - thdf_alpha_75 = 1 << 3, - //! Draw using a remapped palette - thdf_alt_palette = 1 << 4, + //! Draw with the left becoming the right and vice versa + thdf_flip_horizontal = 1 << 0, + //! Draw with the top becoming the bottom and vice versa + thdf_flip_vertical = 1 << 1, + //! Draw with 50% transparency + thdf_alpha_50 = 1 << 2, + //! Draw with 75% transparency + thdf_alpha_75 = 1 << 3, + //! Draw using a remapped palette + thdf_alt_palette = 1 << 4, - /** How to draw alternative palette in 32bpp. */ - /* A 3 bit field (bits 5,6,7), currently 2 bits used. */ + /** How to draw alternative palette in 32bpp. */ + /* A 3 bit field (bits 5,6,7), currently 2 bits used. */ - //! Lowest bit of the field. - thdf_alt32_start = 5, - //! Mask for the 32bpp alternative drawing values. - thdf_alt32_mask = 0x7 << thdf_alt32_start, + //! Lowest bit of the field. + thdf_alt32_start = 5, + //! Mask for the 32bpp alternative drawing values. + thdf_alt32_mask = 0x7 << thdf_alt32_start, - //! Draw the sprite with the normal palette (fallback option). - thdf_alt32_plain = 0 << thdf_alt32_start, - //! Draw the sprite in grey scale. - thdf_alt32_grey_scale = 1 << thdf_alt32_start, - //! Draw the sprite with red and blue colours swapped. - thdf_alt32_blue_red_swap = 2 << thdf_alt32_start, + //! Draw the sprite with the normal palette (fallback option). + thdf_alt32_plain = 0 << thdf_alt32_start, + //! Draw the sprite in grey scale. + thdf_alt32_grey_scale = 1 << thdf_alt32_start, + //! Draw the sprite with red and blue colours swapped. + thdf_alt32_blue_red_swap = 2 << thdf_alt32_start, - /** Object attached to tile flags **/ - /* (should be set prior to attaching to a tile) */ + /** Object attached to tile flags **/ + /* (should be set prior to attaching to a tile) */ - //! Attach to the early sprite list (right-to-left pass) - thdf_early_list = 1 << 10, - //! Keep this sprite at the bottom of the attached list - thdf_list_bottom = 1 << 11, - //! Hit-test using bounding-box precision rather than pixel-perfect - thdf_bound_box_hit_test = 1 << 12, - //! Apply a cropping operation prior to drawing - thdf_crop = 1 << 13, + //! Attach to the early sprite list (right-to-left pass) + thdf_early_list = 1 << 10, + //! Keep this sprite at the bottom of the attached list + thdf_list_bottom = 1 << 11, + //! Hit-test using bounding-box precision rather than pixel-perfect + thdf_bound_box_hit_test = 1 << 12, + //! Apply a cropping operation prior to drawing + thdf_crop = 1 << 13, }; /** Helper structure with parameters to create a #render_target. */ -struct render_target_creation_params -{ - int width; ///< Expected width of the render target. - int height; ///< Expected height of the render target. - int bpp; ///< Expected colour depth of the render target. - bool fullscreen; ///< Run full-screen. - bool present_immediate; ///< Whether to present immediately to the user (else wait for Vsync). +struct render_target_creation_params { + int width; ///< Expected width of the render target. + int height; ///< Expected height of the render target. + int bpp; ///< Expected colour depth of the render target. + bool fullscreen; ///< Run full-screen. + bool present_immediate; ///< Whether to present immediately to the user + ///< (else wait for Vsync). }; /*! @@ -103,109 +95,107 @@ struct render_target_creation_params game objects (though they are the most common thing in drawing lists). */ // TODO: Replace this struct with something cleaner -struct drawable : public link_list -{ - //! Draw the object at a specific point on a render target - /*! - Can also "draw" the object to the speakers, i.e. play sounds. - */ - void (*draw_fn)(drawable* pSelf, render_target* pCanvas, int iDestX, int iDestY); +struct drawable : public link_list { + //! Draw the object at a specific point on a render target + /*! + Can also "draw" the object to the speakers, i.e. play sounds. + */ + void (*draw_fn)(drawable* pSelf, render_target* pCanvas, int iDestX, + int iDestY); - //! Perform a hit test against the object - /*! - Should return true if when the object is drawn at (iDestX, iDestY) on a canvas, - the point (iTestX, iTestY) is within / on the object. - */ - bool (*hit_test_fn)(drawable* pSelf, int iDestX, int iDestY, int iTestX, int iTestY); + //! Perform a hit test against the object + /*! + Should return true if when the object is drawn at (iDestX, iDestY) on a + canvas, the point (iTestX, iTestY) is within / on the object. + */ + bool (*hit_test_fn)(drawable* pSelf, int iDestX, int iDestY, int iTestX, + int iTestY); - //! Drawing flags (zero or more list flags from #draw_flags). - uint32_t flags; + //! Drawing flags (zero or more list flags from #draw_flags). + uint32_t flags; - /** Returns true if instance is a multiple frame animation. - Should be overloaded in derived class. - */ - bool (*is_multiple_frame_animation_fn)(drawable *pSelf); + /** Returns true if instance is a multiple frame animation. + Should be overloaded in derived class. + */ + bool (*is_multiple_frame_animation_fn)(drawable* pSelf); }; /*! Utility class for decoding Theme Hospital "chunked" graphics files. Generally used internally by sprite_sheet. */ -class chunk_renderer -{ -public: - //! Initialise a renderer for a specific size result - /*! - @param width Pixel width of the resulting image - @param height Pixel height of the resulting image - @param buffer If nullptr, then a new buffer is created to render the image - onto. Otherwise, should be an array at least width*height in size. - Ownership of this pointer is assumed by the class - call takeData() - to take ownership back again. - */ - chunk_renderer(int width, int height, uint8_t *buffer = nullptr); +class chunk_renderer { + public: + //! Initialise a renderer for a specific size result + /*! + @param width Pixel width of the resulting image + @param height Pixel height of the resulting image + @param buffer If nullptr, then a new buffer is created to render the + image onto. Otherwise, should be an array at least width*height in size. + Ownership of this pointer is assumed by the class - call takeData() + to take ownership back again. + */ + chunk_renderer(int width, int height, uint8_t* buffer = nullptr); - ~chunk_renderer(); + ~chunk_renderer(); - // TODO: Should be function, not method of chunk_renderer - //! Convert a stream of chunks into a raw bitmap - /*! - @param pData Stream data. - @param iDataLen Length of \a pData. - @param bComplex true if pData is a stream of "complex" chunks, false if - pData is a stream of "simple" chunks. Passing the wrong value will - usually result in a very visible wrong result. + // TODO: Should be function, not method of chunk_renderer + //! Convert a stream of chunks into a raw bitmap + /*! + @param pData Stream data. + @param iDataLen Length of \a pData. + @param bComplex true if pData is a stream of "complex" chunks, false if + pData is a stream of "simple" chunks. Passing the wrong value will + usually result in a very visible wrong result. - Use getData() or takeData() to obtain the resulting bitmap. - */ - void decode_chunks(const uint8_t* pData, int iDataLen, bool bComplex); + Use getData() or takeData() to obtain the resulting bitmap. + */ + void decode_chunks(const uint8_t* pData, int iDataLen, bool bComplex); - //! Get the result buffer, and take ownership of it - /*! - This transfers ownership of the buffer to the caller. After calling, - the class will not have any buffer, and thus cannot be used for - anything. - */ - uint8_t* take_data(); + //! Get the result buffer, and take ownership of it + /*! + This transfers ownership of the buffer to the caller. After calling, + the class will not have any buffer, and thus cannot be used for + anything. + */ + uint8_t* take_data(); - //! Get the result buffer - inline const uint8_t* get_data() const {return data;} + //! Get the result buffer + inline const uint8_t* get_data() const { return data; } - //! Perform a "copy" chunk (normally called by decodeChunks) - void chunk_copy(int npixels, const uint8_t* in_data); + //! Perform a "copy" chunk (normally called by decodeChunks) + void chunk_copy(int npixels, const uint8_t* in_data); - //! Perform a "fill" chunk (normally called by decodeChunks) - void chunk_fill(int npixels, uint8_t value); + //! Perform a "fill" chunk (normally called by decodeChunks) + void chunk_fill(int npixels, uint8_t value); - //! Perform a "fill to end of line" chunk (normally called by decodeChunks) - void chunk_fill_to_end_of_line(uint8_t value); + //! Perform a "fill to end of line" chunk (normally called by decodeChunks) + void chunk_fill_to_end_of_line(uint8_t value); - //! Perform a "fill to end of file" chunk (normally called by decodeChunks) - void chunk_finish(uint8_t value); + //! Perform a "fill to end of file" chunk (normally called by decodeChunks) + void chunk_finish(uint8_t value); -private: - inline bool is_done() {return ptr == end;} - inline void fix_n_pixels(int& npixels) const; - inline void increment_position(int npixels); + private: + inline bool is_done() { return ptr == end; } + inline void fix_n_pixels(int& npixels) const; + inline void increment_position(int npixels); - uint8_t *data, *ptr, *end; - int x, y, width, height; - bool skip_eol; + uint8_t *data, *ptr, *end; + int x, y, width, height; + bool skip_eol; }; //! Layer information (see animation_manager::draw_frame) -struct layers -{ - uint8_t layer_contents[13]; +struct layers { + uint8_t layer_contents[13]; }; class memory_reader; /** Key value for finding an animation. */ -struct animation_key -{ - std::string name; ///< Name of the animations. - int tile_size; ///< Size of a tile. +struct animation_key { + std::string name; ///< Name of the animations. + int tile_size; ///< Size of a tile. }; //! Less-than operator for map-sorting. @@ -214,22 +204,20 @@ struct animation_key @param oL Second key value. @return Whether \a oK should be before \a oL. */ -inline bool operator<(const animation_key &oK, const animation_key &oL) -{ - if (oK.tile_size != oL.tile_size) return oK.tile_size < oL.tile_size; - return oK.name < oL.name; +inline bool operator<(const animation_key& oK, const animation_key& oL) { + if (oK.tile_size != oL.tile_size) return oK.tile_size < oL.tile_size; + return oK.name < oL.name; } /** * Start frames of an animation, in each view direction. * A negative number indicates there is no animation in that direction. */ -struct animation_start_frames -{ - long north; ///< Animation start frame for the 'north' view. - long east; ///< Animation start frame for the 'east' view. - long south; ///< Animation start frame for the 'south' view. - long west; ///< Animation start frame for the 'west' view. +struct animation_start_frames { + long north; ///< Animation start frame for the 'north' view. + long east; ///< Animation start frame for the 'east' view. + long south; ///< Animation start frame for the 'south' view. + long west; ///< Animation start frame for the 'west' view. }; /** Map holding the custom animations. */ @@ -244,374 +232,388 @@ typedef std::pair named_animation_pair; files, and uses them to draw animation frames and provide information about the animations. */ -class animation_manager -{ -public: - animation_manager(); - ~animation_manager(); +class animation_manager { + public: + animation_manager(); + ~animation_manager(); - void set_sprite_sheet(sprite_sheet* pSpriteSheet); + void set_sprite_sheet(sprite_sheet* pSpriteSheet); - //! Load original animations. - /*! - set_sprite_sheet() must be called before calling this. - @param pStartData Animation first frame indices (e.g. VSTART-1.ANI) - @param iStartDataLength Length of \a pStartData. - @param pFrameData Frame details (e.g. VFRA-1.ANI) - @param iFrameDataLength Length of \a pFrameData - @param pListData Element indices list (e.g. VLIST-1.ANI) - @param iListDataLength Length of \a pListData - @param pElementData Element details (e.g. VELE-1.ANI) - @param iElementDataLength Length of \a pElementData - @return Loading was successful. - */ - bool load_from_th_file(const uint8_t* pStartData, size_t iStartDataLength, - const uint8_t* pFrameData, size_t iFrameDataLength, - const uint8_t* pListData, size_t iListDataLength, - const uint8_t* pElementData, size_t iElementDataLength); + //! Load original animations. + /*! + set_sprite_sheet() must be called before calling this. + @param pStartData Animation first frame indices (e.g. VSTART-1.ANI) + @param iStartDataLength Length of \a pStartData. + @param pFrameData Frame details (e.g. VFRA-1.ANI) + @param iFrameDataLength Length of \a pFrameData + @param pListData Element indices list (e.g. VLIST-1.ANI) + @param iListDataLength Length of \a pListData + @param pElementData Element details (e.g. VELE-1.ANI) + @param iElementDataLength Length of \a pElementData + @return Loading was successful. + */ + bool load_from_th_file(const uint8_t* pStartData, size_t iStartDataLength, + const uint8_t* pFrameData, size_t iFrameDataLength, + const uint8_t* pListData, size_t iListDataLength, + const uint8_t* pElementData, + size_t iElementDataLength); - //! Set the video target. - /*! - @param pCanvas Video surface to use. - */ - void set_canvas(render_target *pCanvas); + //! Set the video target. + /*! + @param pCanvas Video surface to use. + */ + void set_canvas(render_target* pCanvas); - //! Load free animations. - /*! - @param pData Start of the loaded data. - @param iDataLength Length of the loaded data. - @return Loading was successful. - */ - bool load_custom_animations(const uint8_t* pData, size_t iDataLength); + //! Load free animations. + /*! + @param pData Start of the loaded data. + @param iDataLength Length of the loaded data. + @return Loading was successful. + */ + bool load_custom_animations(const uint8_t* pData, size_t iDataLength); - //! Get the total numer of animations - size_t get_animation_count() const; + //! Get the total numer of animations + size_t get_animation_count() const; - //! Get the total number of animation frames - size_t get_frame_count() const; + //! Get the total number of animation frames + size_t get_frame_count() const; - //! Get the index of the first frame of an animation - size_t get_first_frame(size_t iAnimation) const; + //! Get the index of the first frame of an animation + size_t get_first_frame(size_t iAnimation) const; - //! Get the index of the frame after a given frame - /*! - To draw an animation frame by frame, call get_first_frame() to get the - index of the first frame, and then keep on calling get_next_frame() using - the most recent return value from get_next_frame() or get_first_frame(). - */ - size_t get_next_frame(size_t iFrame) const; + //! Get the index of the frame after a given frame + /*! + To draw an animation frame by frame, call get_first_frame() to get the + index of the first frame, and then keep on calling get_next_frame() + using the most recent return value from get_next_frame() or + get_first_frame(). + */ + size_t get_next_frame(size_t iFrame) const; - //! Set the palette remap data for an animation - /*! - This sets the palette remap data for every single sprite used by the - given animation. If the animation (or any of its sprites) are drawn - using the thdf_alt_palette flag, then palette indices will be mapped to - new palette indices by the 256 byte array pMap. This is typically used - to draw things in different colours or in greyscale. - */ - void set_animation_alt_palette_map(size_t iAnimation, const uint8_t* pMap, uint32_t iAlt32); + //! Set the palette remap data for an animation + /*! + This sets the palette remap data for every single sprite used by the + given animation. If the animation (or any of its sprites) are drawn + using the thdf_alt_palette flag, then palette indices will be mapped to + new palette indices by the 256 byte array pMap. This is typically used + to draw things in different colours or in greyscale. + */ + void set_animation_alt_palette_map(size_t iAnimation, const uint8_t* pMap, + uint32_t iAlt32); - //! Draw an animation frame - /*! - @param pCanvas The render target to draw onto. - @param iFrame The frame index to draw (should be in range [0, getFrameCount() - 1]) - @param oLayers Information to decide what to draw on each layer. - An animation is comprised of up to thirteen layers, numbered 0 - through 12. Some animations will have different options for what to - render on each layer. For example, patient animations generally - have the different options on layer 1 as different clothes, so if - layer 1 is set to the value 0, they may have their default clothes, - and if set to the value 2 or 4 or 6, they may have other clothes. - Play with the AnimView tool for a better understanding of layers, - though note that while it can draw more than one option on each - layer, this class can only draw a single option for each layer. - @param iX The screen position to use as the animation X origin. - @param iY The screen position to use as the animation Y origin. - @param iFlags Zero or more THDrawFlags flags. - */ - void draw_frame(render_target* pCanvas, size_t iFrame, - const ::layers& oLayers, - int iX, int iY, uint32_t iFlags) const; + //! Draw an animation frame + /*! + @param pCanvas The render target to draw onto. + @param iFrame The frame index to draw (should be in range [0, + getFrameCount() - 1]) + @param oLayers Information to decide what to draw on each layer. + An animation is comprised of up to thirteen layers, numbered 0 + through 12. Some animations will have different options for what to + render on each layer. For example, patient animations generally + have the different options on layer 1 as different clothes, so if + layer 1 is set to the value 0, they may have their default clothes, + and if set to the value 2 or 4 or 6, they may have other clothes. + Play with the AnimView tool for a better understanding of layers, + though note that while it can draw more than one option on each + layer, this class can only draw a single option for each layer. + @param iX The screen position to use as the animation X origin. + @param iY The screen position to use as the animation Y origin. + @param iFlags Zero or more THDrawFlags flags. + */ + void draw_frame(render_target* pCanvas, size_t iFrame, + const ::layers& oLayers, int iX, int iY, + uint32_t iFlags) const; - void get_frame_extent(size_t iFrame, const ::layers& oLayers, - int* pMinX, int* pMaxX, int* pMinY, int* pMaxY, - uint32_t iFlags) const; - size_t get_frame_sound(size_t iFrame); + void get_frame_extent(size_t iFrame, const ::layers& oLayers, int* pMinX, + int* pMaxX, int* pMinY, int* pMaxY, + uint32_t iFlags) const; + size_t get_frame_sound(size_t iFrame); - bool hit_test(size_t iFrame, const ::layers& oLayers, - int iX, int iY, uint32_t iFlags, int iTestX, int iTestY) const; + bool hit_test(size_t iFrame, const ::layers& oLayers, int iX, int iY, + uint32_t iFlags, int iTestX, int iTestY) const; - bool set_frame_marker(size_t iFrame, int iX, int iY); - bool set_frame_secondary_marker(size_t iFrame, int iX, int iY); - bool get_frame_marker(size_t iFrame, int* pX, int* pY); - bool get_frame_secondary_marker(size_t iFrame, int* pX, int* pY); + bool set_frame_marker(size_t iFrame, int iX, int iY); + bool set_frame_secondary_marker(size_t iFrame, int iX, int iY); + bool get_frame_marker(size_t iFrame, int* pX, int* pY); + bool get_frame_secondary_marker(size_t iFrame, int* pX, int* pY); - //! Retrieve a custom animation by name and tile size. - /*! - @param sName Name of the animation. - @param iTilesize Tile size of the animation. - @return A set starting frames for the queried animation. - */ - const animation_start_frames &get_named_animations(const std::string &sName, int iTilesize) const; + //! Retrieve a custom animation by name and tile size. + /*! + @param sName Name of the animation. + @param iTilesize Tile size of the animation. + @return A set starting frames for the queried animation. + */ + const animation_start_frames& get_named_animations(const std::string& sName, + int iTilesize) const; -private: + private: #if CORSIX_TH_USE_PACK_PRAGMAS #pragma pack(push) #pragma pack(1) #endif - // Animation information structure reinterpreted from Theme Hospital data. - struct th_animation_properties - { - uint16_t first_frame; - // It could be that frame is a uint32_t rather than a uint16_t, which - // would resolve the following unknown (which seems to always be zero). - uint16_t unknown; - } CORSIX_TH_PACKED_FLAGS; + // Animation information structure reinterpreted from Theme Hospital data. + struct th_animation_properties { + uint16_t first_frame; + // It could be that frame is a uint32_t rather than a uint16_t, which + // would resolve the following unknown (which seems to always be zero). + uint16_t unknown; + } CORSIX_TH_PACKED_FLAGS; - // Frame information structure reinterpreted from Theme Hospital data. - struct th_frame_properties - { - uint32_t list_index; - // These fields have something to do with width and height, but it's - // not clear quite exactly how. - uint8_t width; - uint8_t height; - // If non-zero, index into sound.dat filetable. - uint8_t sound; - // Combination of zero or more fame_flags values - uint8_t flags; - uint16_t next; - } CORSIX_TH_PACKED_FLAGS; + // Frame information structure reinterpreted from Theme Hospital data. + struct th_frame_properties { + uint32_t list_index; + // These fields have something to do with width and height, but it's + // not clear quite exactly how. + uint8_t width; + uint8_t height; + // If non-zero, index into sound.dat filetable. + uint8_t sound; + // Combination of zero or more fame_flags values + uint8_t flags; + uint16_t next; + } CORSIX_TH_PACKED_FLAGS; - // Structure reinterpreted from Theme Hospital data. - struct th_element_properties - { - uint16_t table_position; - uint8_t offx; - uint8_t offy; - // High nibble: The layer which the element belongs to [0, 12] - // Low nibble: Zero or more draw_flags - uint8_t flags; - // The layer option / layer id - uint8_t layerid; - } CORSIX_TH_PACKED_FLAGS; + // Structure reinterpreted from Theme Hospital data. + struct th_element_properties { + uint16_t table_position; + uint8_t offx; + uint8_t offy; + // High nibble: The layer which the element belongs to [0, 12] + // Low nibble: Zero or more draw_flags + uint8_t flags; + // The layer option / layer id + uint8_t layerid; + } CORSIX_TH_PACKED_FLAGS; #if CORSIX_TH_USE_PACK_PRAGMAS #pragma pack(pop) #endif - struct frame - { - size_t list_index; ///< First entry in #element_list (pointing to an element) for this frame. - size_t next_frame; ///< Number of the next frame. - unsigned int sound; ///< Sound to play, if non-zero. - unsigned int flags; ///< Flags of the frame. Bit 0=start of animation. + struct frame { + size_t list_index; ///< First entry in #element_list (pointing to an + ///< element) for this frame. + size_t next_frame; ///< Number of the next frame. + unsigned int sound; ///< Sound to play, if non-zero. + unsigned int flags; ///< Flags of the frame. Bit 0=start of animation. - // Bounding rectangle is with all layers / options enabled - used as a - // quick test prior to a full pixel perfect test. - int bounding_left; ///< Left edge of the bounding rectangle of this frame. - int bounding_right; ///< Right edge of the bounding rectangle of this frame. - int bounding_top; ///< Top edge of the bounding rectangle of this frame. - int bounding_bottom; ///< Bottom edge of the bounding rectangle of this frame. + // Bounding rectangle is with all layers / options enabled - used as a + // quick test prior to a full pixel perfect test. + int bounding_left; ///< Left edge of the bounding rectangle of this + ///< frame. + int bounding_right; ///< Right edge of the bounding rectangle of this + ///< frame. + int bounding_top; ///< Top edge of the bounding rectangle of this + ///< frame. + int bounding_bottom; ///< Bottom edge of the bounding rectangle of this + ///< frame. - // Markers are used to know where humanoids are on an frame. The - // positions are pixels offsets from the centre of the frame's base - // tile to the centre of the humanoid's feet. - int marker_x; ///< X position of the first center of a humanoids feet. - int marker_y; ///< Y position of the first center of a humanoids feet. - int secondary_marker_x; ///< X position of the second center of a humanoids feet. - int secondary_marker_y; ///< Y position of the second center of a humanoids feet. - }; + // Markers are used to know where humanoids are on an frame. The + // positions are pixels offsets from the centre of the frame's base + // tile to the centre of the humanoid's feet. + int marker_x; ///< X position of the first center of a humanoids feet. + int marker_y; ///< Y position of the first center of a humanoids feet. + int secondary_marker_x; ///< X position of the second center of a + ///< humanoids feet. + int secondary_marker_y; ///< Y position of the second center of a + ///< humanoids feet. + }; - struct element - { - size_t sprite; ///< Sprite number of the sprite sheet to display. - uint32_t flags; ///< Flags of the sprite. - ///< bit 0=flip vertically, bit 1=flip horizontally, - ///< bit 2=draw 50% alpha, bit 3=draw 75% alpha. - int x; ///< X offset of the sprite. - int y; ///< Y offset of the sprite. - uint8_t layer; ///< Layer class (0..12). - uint8_t layer_id; ///< Value of the layer class to match. + struct element { + size_t sprite; ///< Sprite number of the sprite sheet to display. + uint32_t flags; ///< Flags of the sprite. + ///< bit 0=flip vertically, bit 1=flip horizontally, + ///< bit 2=draw 50% alpha, bit 3=draw 75% alpha. + int x; ///< X offset of the sprite. + int y; ///< Y offset of the sprite. + uint8_t layer; ///< Layer class (0..12). + uint8_t layer_id; ///< Value of the layer class to match. - sprite_sheet *element_sprite_sheet; ///< Sprite sheet to use for this element. - }; + sprite_sheet* element_sprite_sheet; ///< Sprite sheet to use for this + ///< element. + }; - std::vector first_frames; ///< First frame number of an animation. - std::vector frames; ///< The loaded frames. - std::vector element_list; ///< List of elements for a frame. - std::vector elements; ///< Sprite Elements. - std::vector custom_sheets; ///< Sprite sheets with custom graphics. - named_animations_map named_animations; ///< Collected named animations. + std::vector first_frames; ///< First frame number of an animation. + std::vector frames; ///< The loaded frames. + std::vector element_list; ///< List of elements for a frame. + std::vector elements; ///< Sprite Elements. + std::vector + custom_sheets; ///< Sprite sheets with custom graphics. + named_animations_map named_animations; ///< Collected named animations. - sprite_sheet* sheet; ///< Sprite sheet to use. - render_target *canvas; ///< Video surface to use. + sprite_sheet* sheet; ///< Sprite sheet to use. + render_target* canvas; ///< Video surface to use. - size_t animation_count; ///< Number of animations. - size_t frame_count; ///< Number of frames. - size_t element_list_count; ///< Number of list elements. - size_t element_count; ///< Number of sprite elements. + size_t animation_count; ///< Number of animations. + size_t frame_count; ///< Number of frames. + size_t element_list_count; ///< Number of list elements. + size_t element_count; ///< Number of sprite elements. - //! Compute the bounding box of the frame. - /*! - @param oFrame Frame to inspect/set. - */ - void set_bounding_box(frame &oFrame); + //! Compute the bounding box of the frame. + /*! + @param oFrame Frame to inspect/set. + */ + void set_bounding_box(frame& oFrame); - //! Load sprite elements from the input. - /*! - @param [inout] input Data to read. - @param pSpriteSheet Sprite sheet to use. - @param iNumElements Number of elements to read. - @param [inout] iLoadedElements Number of loaded elements so far. - @param iElementStart Offset of the first element. - @param iElementCount Number of elements to load. - @return Index of the first loaded element in #elements. Negative value means failure. - */ - size_t load_elements(memory_reader &input, sprite_sheet *pSpriteSheet, - size_t iNumElements, size_t &iLoadedElements, - size_t iElementStart, size_t iElementCount); + //! Load sprite elements from the input. + /*! + @param [inout] input Data to read. + @param pSpriteSheet Sprite sheet to use. + @param iNumElements Number of elements to read. + @param [inout] iLoadedElements Number of loaded elements so far. + @param iElementStart Offset of the first element. + @param iElementCount Number of elements to load. + @return Index of the first loaded element in #elements. Negative value + means failure. + */ + size_t load_elements(memory_reader& input, sprite_sheet* pSpriteSheet, + size_t iNumElements, size_t& iLoadedElements, + size_t iElementStart, size_t iElementCount); - //! Construct a list element for every element, and a 0xFFFF at the end. - /*! - @param iFirstElement Index of the first element in #elements. - @param iNumElements Number of elements to add. - @param [inout] iLoadedListElements Number of created list elements so far. - @param iListStart Offset of the first created list element. - @param iListCount Expected number of list elements to create. - @return Index of the list elements, or a negative value to indicate failure. - */ - size_t make_list_elements(size_t iFirstElement, size_t iNumElements, - size_t &iLoadedListElements, - size_t iListStart, size_t iListCount); + //! Construct a list element for every element, and a 0xFFFF at the end. + /*! + @param iFirstElement Index of the first element in #elements. + @param iNumElements Number of elements to add. + @param [inout] iLoadedListElements Number of created list elements so + far. + @param iListStart Offset of the first created list element. + @param iListCount Expected number of list elements to create. + @return Index of the list elements, or a negative value to indicate + failure. + */ + size_t make_list_elements(size_t iFirstElement, size_t iNumElements, + size_t& iLoadedListElements, size_t iListStart, + size_t iListCount); - //! Fix the flags of the first frame, and set the next frame of the last frame back to the first frame. - /*! - @param iFirst First frame of the animation, or 0xFFFFFFFFu. - @param iLength Number of frames in the animation. - */ - void fix_next_frame(uint32_t iFirst, size_t iLength); + //! Fix the flags of the first frame, and set the next frame of the last + //! frame back to the first frame. + /*! + @param iFirst First frame of the animation, or 0xFFFFFFFFu. + @param iLength Number of frames in the animation. + */ + void fix_next_frame(uint32_t iFirst, size_t iLength); }; struct map_tile; -class animation_base : public drawable -{ -public: - animation_base(); +class animation_base : public drawable { + public: + animation_base(); - void remove_from_tile(); - void attach_to_tile(map_tile *pMapNode, int layer); + void remove_from_tile(); + void attach_to_tile(map_tile* pMapNode, int layer); - uint32_t get_flags() const {return flags;} - int get_x() const {return x_relative_to_tile;} - int get_y() const {return y_relative_to_tile;} + uint32_t get_flags() const { return flags; } + int get_x() const { return x_relative_to_tile; } + int get_y() const { return y_relative_to_tile; } - void set_flags(uint32_t iFlags) {flags = iFlags;} - void set_position(int iX, int iY) {x_relative_to_tile = iX, y_relative_to_tile = iY;} - void set_layer(int iLayer, int iId); - void set_layers_from(const animation_base *pSrc) {layers = pSrc->layers;} + void set_flags(uint32_t iFlags) { flags = iFlags; } + void set_position(int iX, int iY) { + x_relative_to_tile = iX, y_relative_to_tile = iY; + } + void set_layer(int iLayer, int iId); + void set_layers_from(const animation_base* pSrc) { layers = pSrc->layers; } - // bool isMultipleFrameAnimation() { return false;} -protected: - //! X position on tile (not tile x-index) - int x_relative_to_tile; - //! Y position on tile (not tile y-index) - int y_relative_to_tile; + // bool isMultipleFrameAnimation() { return false;} + protected: + //! X position on tile (not tile x-index) + int x_relative_to_tile; + //! Y position on tile (not tile y-index) + int y_relative_to_tile; - ::layers layers; + ::layers layers; }; -class animation : public animation_base -{ -public: - animation(); - - void set_parent(animation *pParent); - - void tick(); - void draw(render_target* pCanvas, int iDestX, int iDestY); - bool hit_test(int iDestX, int iDestY, int iTestX, int iTestY); - void draw_morph(render_target* pCanvas, int iDestX, int iDestY); - bool hit_test_morph(int iDestX, int iDestY, int iTestX, int iTestY); - void draw_child(render_target* pCanvas, int iDestX, int iDestY); - bool hit_test_child(int iDestX, int iDestY, int iTestX, int iTestY); - - link_list* get_previous() {return prev;} - size_t get_animation() const {return animation_index;} - bool get_marker(int* pX, int* pY); - bool get_secondary_marker(int* pX, int* pY); - size_t get_frame() const {return frame_index;} - int get_crop_column() const {return crop_column;} - - void set_animation(animation_manager* pManager, size_t iAnimation); - void set_morph_target(animation *pMorphTarget, unsigned int iDurationFactor = 1); - void set_frame(size_t iFrame); - - void set_speed(int iX, int iY) {speed.dx = iX, speed.dy = iY;} - void set_crop_column(int iColumn) {crop_column = iColumn;} - - void persist(lua_persist_writer *pWriter) const; - void depersist(lua_persist_reader *pReader); - - animation_manager* get_animation_manager(){ return manager;} -private: - animation_manager *manager; - animation* morph_target; - size_t animation_index; ///< Animation number. - size_t frame_index; ///< Frame number. - union { - struct { - //! Amount to change x per tick - int dx; - //! Amount to change y per tick - int dy; - } speed; - //! Some animations are tied to the marker of another animation and - //! hence have a parent rather than a speed. - animation* parent; - }; - - size_t sound_to_play; - int crop_column; +struct xy_diff { + //! Amount to change x per tick + int dx; + //! Amount to change y per tick + int dy; }; -class sprite_render_list : public animation_base -{ -public: - sprite_render_list(); - ~sprite_render_list(); +class animation : public animation_base { + public: + animation(); - void tick(); - void draw(render_target* pCanvas, int iDestX, int iDestY); - bool hit_test(int iDestX, int iDestY, int iTestX, int iTestY); + void set_parent(animation* pParent); - void set_sheet(sprite_sheet* pSheet) {sheet = pSheet;} - void set_speed(int iX, int iY) {dx_per_tick = iX, dy_per_tick = iY;} - void set_lifetime(int iLifetime); - void append_sprite(size_t iSprite, int iX, int iY); - bool is_dead() const {return lifetime == 0;} + void tick(); + void draw(render_target* pCanvas, int iDestX, int iDestY); + bool hit_test(int iDestX, int iDestY, int iTestX, int iTestY); + void draw_morph(render_target* pCanvas, int iDestX, int iDestY); + bool hit_test_morph(int iDestX, int iDestY, int iTestX, int iTestY); + void draw_child(render_target* pCanvas, int iDestX, int iDestY); + bool hit_test_child(int iDestX, int iDestY, int iTestX, int iTestY); - void persist(lua_persist_writer *pWriter) const; - void depersist(lua_persist_reader *pReader); + link_list* get_previous() { return prev; } + size_t get_animation() const { return animation_index; } + bool get_marker(int* pX, int* pY); + bool get_secondary_marker(int* pX, int* pY); + size_t get_frame() const { return frame_index; } + int get_crop_column() const { return crop_column; } -private: - struct sprite - { - size_t index; - int x; - int y; - }; + void set_animation(animation_manager* pManager, size_t iAnimation); + void set_morph_target(animation* pMorphTarget, + unsigned int iDurationFactor = 1); + void set_frame(size_t iFrame); - sprite_sheet* sheet; - sprite* sprites; - int sprite_count; - int buffer_size; + void set_speed(int iX, int iY) { speed.dx = iX, speed.dy = iY; } + void set_crop_column(int iColumn) { crop_column = iColumn; } - //! Amount to change x per tick - int dx_per_tick; - //! Amount to change y per tick - int dy_per_tick; - //! Number of ticks until reports as dead (-1 = never dies) - int lifetime; + void persist(lua_persist_writer* pWriter) const; + void depersist(lua_persist_reader* pReader); + + animation_manager* get_animation_manager() { return manager; } + + private: + animation_manager* manager; + animation* morph_target; + size_t animation_index; ///< Animation number. + size_t frame_index; ///< Frame number. + union { + xy_diff speed; + //! Some animations are tied to the marker of another animation and + //! hence have a parent rather than a speed. + animation* parent; + }; + + size_t sound_to_play; + int crop_column; }; -#endif // CORSIX_TH_TH_GFX_H_ +class sprite_render_list : public animation_base { + public: + sprite_render_list(); + ~sprite_render_list(); + + void tick(); + void draw(render_target* pCanvas, int iDestX, int iDestY); + bool hit_test(int iDestX, int iDestY, int iTestX, int iTestY); + + void set_sheet(sprite_sheet* pSheet) { sheet = pSheet; } + void set_speed(int iX, int iY) { dx_per_tick = iX, dy_per_tick = iY; } + void set_lifetime(int iLifetime); + void append_sprite(size_t iSprite, int iX, int iY); + bool is_dead() const { return lifetime == 0; } + + void persist(lua_persist_writer* pWriter) const; + void depersist(lua_persist_reader* pReader); + + private: + struct sprite { + size_t index; + int x; + int y; + }; + + sprite_sheet* sheet; + sprite* sprites; + int sprite_count; + int buffer_size; + + //! Amount to change x per tick + int dx_per_tick; + //! Amount to change y per tick + int dy_per_tick; + //! Number of ticks until reports as dead (-1 = never dies) + int lifetime; +}; + +#endif // CORSIX_TH_TH_GFX_H_ diff --git a/CorsixTH/Src/th_gfx_font.cpp b/CorsixTH/Src/th_gfx_font.cpp index 0b44f74e..f38eb837 100644 --- a/CorsixTH/Src/th_gfx_font.cpp +++ b/CorsixTH/Src/th_gfx_font.cpp @@ -1,7 +1,8 @@ /* Copyright (c) 2010 Peter "Corsix" Cawley -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 +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 @@ -19,53 +20,53 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -#include "config.h" #include "th_gfx_font.h" +#include "config.h" #ifdef CORSIX_TH_USE_FREETYPE2 #include FT_GLYPH_H -#include #include +#include #endif +#include #include #include -#include namespace { constexpr unsigned int invalid_char_codepoint = 0xFFFD; -size_t discard_leading_set_bits(uint8_t &byte) { - size_t count = 0; - while ((byte & 0x80) != 0) { - count++; - byte = byte << 1; - } - byte = byte >> count; - return count; +size_t discard_leading_set_bits(uint8_t& byte) { + size_t count = 0; + while ((byte & 0x80) != 0) { + count++; + byte = byte << 1; + } + byte = byte >> count; + return count; } -unsigned int next_utf8_codepoint(const char*& sString) -{ - uint8_t cur_byte = *reinterpret_cast(sString++); - size_t leading_bit_count = discard_leading_set_bits(cur_byte); +unsigned int next_utf8_codepoint(const char*& sString) { + uint8_t cur_byte = *reinterpret_cast(sString++); + size_t leading_bit_count = discard_leading_set_bits(cur_byte); - if (leading_bit_count == 1 || leading_bit_count > 4) { - // A single leading bit is a continuation character. A utf-8 character can be at most 4 bytes long. - return invalid_char_codepoint; + if (leading_bit_count == 1 || leading_bit_count > 4) { + // A single leading bit is a continuation character. A utf-8 character + // can be at most 4 bytes long. + return invalid_char_codepoint; + } + + unsigned int codepoint = cur_byte; + for (size_t i = 1; i < leading_bit_count; ++i) { + cur_byte = *reinterpret_cast(sString++); + size_t continue_leading_bits = discard_leading_set_bits(cur_byte); + + if (continue_leading_bits != 1) { + // Not enough continuation characters + return invalid_char_codepoint; } - - unsigned int codepoint = cur_byte; - for (size_t i = 1; i < leading_bit_count; ++i) { - cur_byte = *reinterpret_cast(sString++); - size_t continue_leading_bits = discard_leading_set_bits(cur_byte); - - if (continue_leading_bits != 1) { - // Not enough continuation characters - return invalid_char_codepoint; - } - codepoint = (codepoint << 6) | cur_byte; - } - return codepoint; + codepoint = (codepoint << 6) | cur_byte; + } + return codepoint; } constexpr uint16_t unicode_to_cp437_table[0x60] = { @@ -76,756 +77,706 @@ constexpr uint16_t unicode_to_cp437_table[0x60] = { 0x3F, 0xA5, 0x3F, 0x3F, 0x3F, 0x3F, 0x99, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x9A, 0x3F, 0x3F, 0xE1, 0x85, 0xA0, 0x83, 0x3F, 0x84, 0x86, 0x91, 0x87, 0x8A, 0x82, 0x88, 0x89, 0x8D, 0xA1, 0x8C, 0x8B, 0x3F, 0xA4, 0x95, 0xA2, - 0x93, 0x3F, 0x94, 0xF6, 0x3F, 0x97, 0xA3, 0x96, 0x81, 0x3F, 0x3F, 0x98 -}; + 0x93, 0x3F, 0x94, 0xF6, 0x3F, 0x97, 0xA3, 0x96, 0x81, 0x3F, 0x3F, 0x98}; -unsigned int unicode_to_codepage_437(unsigned int iCodePoint) -{ - if(iCodePoint < 0x80) - return iCodePoint; - if(iCodePoint < 0xA0) - return '?'; - if(iCodePoint < 0x100) - return unicode_to_cp437_table[iCodePoint - 0xA0]; - switch(iCodePoint) - { - case 0x0192: return 0x9F; - case 0x0393: return 0xE2; - case 0x0398: return 0xE9; - case 0x03A3: return 0xE4; - case 0x03A6: return 0xE8; - case 0x03A9: return 0xEA; - case 0x03B1: return 0xE0; - case 0x03B4: return 0xEB; - case 0x03B5: return 0xEE; - case 0x03BC: return 0xE6; - case 0x03C0: return 0xE3; - case 0x03C3: return 0xE5; - case 0x03C4: return 0xE7; - case 0x03C6: return 0xED; - case 0x207F: return 0xFC; - case 0x20A7: return 0x9E; - case 0x2219: return 0xF9; - case 0x221A: return 0xFB; - case 0x221E: return 0xEC; - case 0x2229: return 0xEF; - case 0x2248: return 0xF7; - case 0x2261: return 0xF0; - case 0x2264: return 0xF3; - case 0x2265: return 0xF2; - case 0x2310: return 0xA9; - case 0x2320: return 0xF4; - case 0x2321: return 0xF5; - case 0x25A0: return 0xFE; - } - return 0x3F; +unsigned int unicode_to_codepage_437(unsigned int iCodePoint) { + if (iCodePoint < 0x80) return iCodePoint; + if (iCodePoint < 0xA0) return '?'; + if (iCodePoint < 0x100) return unicode_to_cp437_table[iCodePoint - 0xA0]; + switch (iCodePoint) { + case 0x0192: + return 0x9F; + case 0x0393: + return 0xE2; + case 0x0398: + return 0xE9; + case 0x03A3: + return 0xE4; + case 0x03A6: + return 0xE8; + case 0x03A9: + return 0xEA; + case 0x03B1: + return 0xE0; + case 0x03B4: + return 0xEB; + case 0x03B5: + return 0xEE; + case 0x03BC: + return 0xE6; + case 0x03C0: + return 0xE3; + case 0x03C3: + return 0xE5; + case 0x03C4: + return 0xE7; + case 0x03C6: + return 0xED; + case 0x207F: + return 0xFC; + case 0x20A7: + return 0x9E; + case 0x2219: + return 0xF9; + case 0x221A: + return 0xFB; + case 0x221E: + return 0xEC; + case 0x2229: + return 0xEF; + case 0x2248: + return 0xF7; + case 0x2261: + return 0xF0; + case 0x2264: + return 0xF3; + case 0x2265: + return 0xF2; + case 0x2310: + return 0xA9; + case 0x2320: + return 0xF4; + case 0x2321: + return 0xF5; + case 0x25A0: + return 0xFE; + } + return 0x3F; } #ifdef CORSIX_TH_USE_FREETYPE2 // Since these functions are only used when we use freetype2, this silences // warnings about defined and not used. -unsigned int decode_utf8(const char* sString) -{ - return next_utf8_codepoint(sString); +unsigned int decode_utf8(const char* sString) { + return next_utf8_codepoint(sString); } -const char* previous_utf8_codepoint(const char* sString) -{ - do - { - --sString; - } while(((*sString) & 0xC0) == 0x80); - return sString; +const char* previous_utf8_codepoint(const char* sString) { + do { + --sString; + } while (((*sString) & 0xC0) == 0x80); + return sString; } #endif -} // namespace +} // namespace -bitmap_font::bitmap_font() -{ - sheet = nullptr; - letter_spacing = 0; - line_spacing = 0; +bitmap_font::bitmap_font() { + sheet = nullptr; + letter_spacing = 0; + line_spacing = 0; } -void bitmap_font::set_sprite_sheet(sprite_sheet* pSpriteSheet) -{ - sheet = pSpriteSheet; +void bitmap_font::set_sprite_sheet(sprite_sheet* pSpriteSheet) { + sheet = pSpriteSheet; } -void bitmap_font::set_separation(int iCharSep, int iLineSep) -{ - letter_spacing = iCharSep; - line_spacing = iLineSep; +void bitmap_font::set_separation(int iCharSep, int iLineSep) { + letter_spacing = iCharSep; + line_spacing = iLineSep; } -text_layout bitmap_font::get_text_dimensions(const char* sMessage, size_t iMessageLength, - int iMaxWidth) const -{ - return draw_text_wrapped(nullptr, sMessage, iMessageLength, 0, 0, iMaxWidth, INT_MAX, 0); +text_layout bitmap_font::get_text_dimensions(const char* sMessage, + size_t iMessageLength, + int iMaxWidth) const { + return draw_text_wrapped(nullptr, sMessage, iMessageLength, 0, 0, iMaxWidth, + INT_MAX, 0); } -void bitmap_font::draw_text(render_target* pCanvas, const char* sMessage, size_t iMessageLength, int iX, int iY) const -{ - pCanvas->start_nonoverlapping_draws(); - if(iMessageLength != 0 && sheet != nullptr) - { - const unsigned int iFirstASCII = 31; - unsigned int iLastASCII = static_cast(sheet->get_sprite_count()) + iFirstASCII; - const char* sMessageEnd = sMessage + iMessageLength; +void bitmap_font::draw_text(render_target* pCanvas, const char* sMessage, + size_t iMessageLength, int iX, int iY) const { + pCanvas->start_nonoverlapping_draws(); + if (iMessageLength != 0 && sheet != nullptr) { + const unsigned int iFirstASCII = 31; + unsigned int iLastASCII = + static_cast(sheet->get_sprite_count()) + iFirstASCII; + const char* sMessageEnd = sMessage + iMessageLength; - while(sMessage != sMessageEnd) - { - unsigned int iChar = unicode_to_codepage_437(next_utf8_codepoint(sMessage)); - if(iFirstASCII <= iChar && iChar <= iLastASCII) - { - iChar -= iFirstASCII; - unsigned int iWidth, iHeight; - sheet->draw_sprite(pCanvas, iChar, iX, iY, 0); - sheet->get_sprite_size_unchecked(iChar, &iWidth, &iHeight); - iX += iWidth + letter_spacing; - } - } + while (sMessage != sMessageEnd) { + unsigned int iChar = + unicode_to_codepage_437(next_utf8_codepoint(sMessage)); + if (iFirstASCII <= iChar && iChar <= iLastASCII) { + iChar -= iFirstASCII; + unsigned int iWidth, iHeight; + sheet->draw_sprite(pCanvas, iChar, iX, iY, 0); + sheet->get_sprite_size_unchecked(iChar, &iWidth, &iHeight); + iX += iWidth + letter_spacing; + } } - pCanvas->finish_nonoverlapping_draws(); + } + pCanvas->finish_nonoverlapping_draws(); } -text_layout bitmap_font::draw_text_wrapped(render_target* pCanvas, const char* sMessage, - size_t iMessageLength, int iX, int iY, int iWidth, - int iMaxRows, int iSkipRows, text_alignment eAlign) const -{ - text_layout oDrawArea = {}; - int iSkippedRows = 0; - if(iMessageLength != 0 && sheet != nullptr) - { - const unsigned int iFirstASCII = 31; - unsigned int iLastASCII = static_cast(sheet->get_sprite_count()) + iFirstASCII; - const char* sMessageEnd = sMessage + iMessageLength; +text_layout bitmap_font::draw_text_wrapped(render_target* pCanvas, + const char* sMessage, + size_t iMessageLength, int iX, + int iY, int iWidth, int iMaxRows, + int iSkipRows, + text_alignment eAlign) const { + text_layout oDrawArea = {}; + int iSkippedRows = 0; + if (iMessageLength != 0 && sheet != nullptr) { + const unsigned int iFirstASCII = 31; + unsigned int iLastASCII = + static_cast(sheet->get_sprite_count()) + iFirstASCII; + const char* sMessageEnd = sMessage + iMessageLength; - while(sMessage != sMessageEnd && oDrawArea.row_count < iMaxRows) - { - const char* sBreakPosition = sMessageEnd; - const char* sLastGoodBreakPosition = sBreakPosition; - int iMsgWidth = -letter_spacing; - int iMsgBreakWidth = iMsgWidth; - unsigned int iTallest = 0; - const char* s; - bool foundNewLine = false; - unsigned int iNextChar = 0; + while (sMessage != sMessageEnd && oDrawArea.row_count < iMaxRows) { + const char* sBreakPosition = sMessageEnd; + const char* sLastGoodBreakPosition = sBreakPosition; + int iMsgWidth = -letter_spacing; + int iMsgBreakWidth = iMsgWidth; + unsigned int iTallest = 0; + const char* s; + bool foundNewLine = false; + unsigned int iNextChar = 0; - for(s = sMessage; s != sMessageEnd; ) - { - const char* sOld = s; - unsigned int iChar = unicode_to_codepage_437(next_utf8_codepoint(s)); - iNextChar = unicode_to_codepage_437(static_cast(*s)); - if((iChar == '\n' && iNextChar == '\n') || (iChar == '/' && iNextChar == '/')) - { - foundNewLine = true; - iMsgBreakWidth = iMsgWidth; - sBreakPosition = sOld; - break; - } - unsigned int iCharWidth = 0, iCharHeight = 0; - if(iFirstASCII <= iChar && iChar <= iLastASCII) - { - sheet->get_sprite_size_unchecked(iChar - iFirstASCII, &iCharWidth, &iCharHeight); - } - iMsgWidth += letter_spacing + iCharWidth; - if(iChar == ' ') - { - sLastGoodBreakPosition = sOld; - iMsgBreakWidth = iMsgWidth - iCharWidth; - } - - if(iMsgWidth > iWidth) - { - sBreakPosition = sLastGoodBreakPosition; - break; - } - if(iCharHeight > iTallest) - iTallest = iCharHeight; - } - - if(s == sMessageEnd) - iMsgBreakWidth = iMsgWidth; - if(iMsgBreakWidth > oDrawArea.width) - oDrawArea.width = iMsgBreakWidth; - - if(iSkippedRows >= iSkipRows) - { - if(pCanvas) - { - int iXOffset = 0; - if(iMsgBreakWidth < iWidth) - iXOffset = (iWidth - iMsgBreakWidth) * static_cast(eAlign) / 2; - draw_text(pCanvas, sMessage, sBreakPosition - sMessage, iX + iXOffset, iY); - } - iY += static_cast(iTallest) + line_spacing; - oDrawArea.end_x = iMsgWidth; - oDrawArea.row_count++; - if (foundNewLine) { - iY += static_cast(iTallest) + line_spacing; - oDrawArea.row_count++; - } - } - else - { - iSkippedRows++; - if(foundNewLine) - { - if(iSkippedRows == iSkipRows) - { - iY += static_cast(iTallest) + line_spacing; - oDrawArea.row_count++; - } - iSkippedRows++; - } - } - sMessage = sBreakPosition; - if(sMessage != sMessageEnd) - { - next_utf8_codepoint(sMessage); - if(foundNewLine) - { - next_utf8_codepoint(sMessage); - } - } - foundNewLine = 0; + for (s = sMessage; s != sMessageEnd;) { + const char* sOld = s; + unsigned int iChar = unicode_to_codepage_437(next_utf8_codepoint(s)); + iNextChar = unicode_to_codepage_437(static_cast(*s)); + if ((iChar == '\n' && iNextChar == '\n') || + (iChar == '/' && iNextChar == '/')) { + foundNewLine = true; + iMsgBreakWidth = iMsgWidth; + sBreakPosition = sOld; + break; } + unsigned int iCharWidth = 0, iCharHeight = 0; + if (iFirstASCII <= iChar && iChar <= iLastASCII) { + sheet->get_sprite_size_unchecked(iChar - iFirstASCII, &iCharWidth, + &iCharHeight); + } + iMsgWidth += letter_spacing + iCharWidth; + if (iChar == ' ') { + sLastGoodBreakPosition = sOld; + iMsgBreakWidth = iMsgWidth - iCharWidth; + } + + if (iMsgWidth > iWidth) { + sBreakPosition = sLastGoodBreakPosition; + break; + } + if (iCharHeight > iTallest) iTallest = iCharHeight; + } + + if (s == sMessageEnd) iMsgBreakWidth = iMsgWidth; + if (iMsgBreakWidth > oDrawArea.width) oDrawArea.width = iMsgBreakWidth; + + if (iSkippedRows >= iSkipRows) { + if (pCanvas) { + int iXOffset = 0; + if (iMsgBreakWidth < iWidth) + iXOffset = (iWidth - iMsgBreakWidth) * static_cast(eAlign) / 2; + draw_text(pCanvas, sMessage, sBreakPosition - sMessage, iX + iXOffset, + iY); + } + iY += static_cast(iTallest) + line_spacing; + oDrawArea.end_x = iMsgWidth; + oDrawArea.row_count++; + if (foundNewLine) { + iY += static_cast(iTallest) + line_spacing; + oDrawArea.row_count++; + } + } else { + iSkippedRows++; + if (foundNewLine) { + if (iSkippedRows == iSkipRows) { + iY += static_cast(iTallest) + line_spacing; + oDrawArea.row_count++; + } + iSkippedRows++; + } + } + sMessage = sBreakPosition; + if (sMessage != sMessageEnd) { + next_utf8_codepoint(sMessage); + if (foundNewLine) { + next_utf8_codepoint(sMessage); + } + } + foundNewLine = 0; } - oDrawArea.end_x = iX + oDrawArea.end_x; - oDrawArea.end_y = iY; - return oDrawArea; + } + oDrawArea.end_x = iX + oDrawArea.end_x; + oDrawArea.end_y = iY; + return oDrawArea; } #ifdef CORSIX_TH_USE_FREETYPE2 FT_Library freetype_font::freetype_library = nullptr; int freetype_font::freetype_init_count = 0; -freetype_font::freetype_font() -{ +freetype_font::freetype_font() { + font_face = nullptr; + is_done_freetype_init = false; + for (cached_text* pEntry = cache; pEntry != cache + (1 << cache_size_log2); + ++pEntry) { + pEntry->message = nullptr; + pEntry->message_length = 0; + pEntry->message_buffer_length = 0; + pEntry->alignment = text_alignment::left; + pEntry->width = 0; + pEntry->height = 0; + pEntry->widest_line_width = 0; + pEntry->last_x = 0; + pEntry->data = nullptr; + pEntry->is_valid = false; + pEntry->texture = nullptr; + } +} + +freetype_font::~freetype_font() { + for (cached_text* pEntry = cache; pEntry != cache + (1 << cache_size_log2); + ++pEntry) { + delete[] pEntry->message; + delete[] pEntry->data; + free_texture(pEntry); + } + if (font_face != nullptr) FT_Done_Face(font_face); + if (is_done_freetype_init) { + if (--freetype_init_count == 0) { + FT_Done_FreeType(freetype_library); + freetype_library = nullptr; + } + } +} + +const char* freetype_font::get_copyright_notice() { + return "Portions of this software are copyright \xC2\xA9 2010 " + "The FreeType Project (www.freetype.org). All rights reserved."; +} + +FT_Error freetype_font::initialise() { + if (is_done_freetype_init) return FT_Err_Ok; + if (freetype_init_count == 0) { + int iError = FT_Init_FreeType(&freetype_library); + if (iError != FT_Err_Ok) return iError; + } + ++freetype_init_count; + is_done_freetype_init = true; + return FT_Err_Ok; +} + +void freetype_font::clear_cache() { + for (cached_text* pEntry = cache; pEntry != cache + (1 << cache_size_log2); + ++pEntry) { + pEntry->is_valid = false; + free_texture(pEntry); + } +} + +FT_Error freetype_font::set_face(const uint8_t* pData, size_t iLength) { + int iError; + if (freetype_library == nullptr) { + iError = initialise(); + if (iError != FT_Err_Ok) return iError; + } + if (font_face) { + iError = FT_Done_Face(font_face); + if (iError != FT_Err_Ok) return iError; font_face = nullptr; - is_done_freetype_init = false; - for(cached_text* pEntry = cache; - pEntry != cache + (1 << cache_size_log2); ++pEntry) - { - pEntry->message = nullptr; - pEntry->message_length = 0; - pEntry->message_buffer_length = 0; - pEntry->alignment = text_alignment::left; - pEntry->width = 0; - pEntry->height = 0; - pEntry->widest_line_width = 0; - pEntry->last_x = 0; - pEntry->data = nullptr; - pEntry->is_valid = false; - pEntry->texture = nullptr; - } + } + iError = FT_New_Memory_Face(freetype_library, pData, + static_cast(iLength), 0, &font_face); + return iError; } -freetype_font::~freetype_font() -{ - for( cached_text* pEntry = cache; - pEntry != cache + (1 << cache_size_log2); ++pEntry) { - delete[] pEntry->message; - delete[] pEntry->data; - free_texture(pEntry); - } - if(font_face != nullptr) - FT_Done_Face(font_face); - if(is_done_freetype_init) - { - if(--freetype_init_count == 0) - { - FT_Done_FreeType(freetype_library); - freetype_library = nullptr; - } +FT_Error freetype_font::match_bitmap_font( + sprite_sheet* pBitmapFontSpriteSheet) { + if (pBitmapFontSpriteSheet == nullptr) return FT_Err_Invalid_Argument; + + // Try to take the size and colour of a standard character (em is generally + // the standard font character, but for fonts which only have numbers, zero + // seems like the next best choice). + for (const char* sCharToTry = "M0"; *sCharToTry; ++sCharToTry) { + unsigned int iWidth, iHeight; + unsigned int iSprite = *sCharToTry - 31; + if (pBitmapFontSpriteSheet->get_sprite_size(iSprite, &iWidth, &iHeight) && + pBitmapFontSpriteSheet->get_sprite_average_colour(iSprite, &colour) && + iWidth > 1 && iHeight > 1) { + return set_ideal_character_size(iWidth, iHeight); } + } + + // Take the average size of all characters, and the colour of one of them. + unsigned int iWidthSum = 0, iHeightSum = 0, iAverageNum = 0; + for (unsigned int i = 0; i < pBitmapFontSpriteSheet->get_sprite_count(); + ++i) { + unsigned int iWidth, iHeight; + pBitmapFontSpriteSheet->get_sprite_size_unchecked(i, &iWidth, &iHeight); + if (iWidth <= 1 || iHeight <= 1) continue; + if (!pBitmapFontSpriteSheet->get_sprite_average_colour(i, &colour)) + continue; + iWidthSum += iWidth; + iHeightSum += iHeight; + ++iAverageNum; + } + if (iAverageNum == 0) return FT_Err_Divide_By_Zero; + + return set_ideal_character_size((iWidthSum + iAverageNum / 2) / iAverageNum, + (iHeightSum + iAverageNum / 2) / iAverageNum); } -const char* freetype_font::get_copyright_notice() -{ - return "Portions of this software are copyright \xC2\xA9 2010 " \ - "The FreeType Project (www.freetype.org). All rights reserved."; +FT_Error freetype_font::set_ideal_character_size(int iWidth, int iHeight) { + if (font_face == nullptr) return FT_Err_Invalid_Face_Handle; + + if (is_monochrome() || iHeight <= 14 || iWidth <= 9) { + // Look for a bitmap strike of a similar size + int iBestBitmapScore = 50; + FT_Int iBestBitmapIndex = -1; + for (FT_Int i = 0; i < font_face->num_fixed_sizes; ++i) { + if (font_face->available_sizes[i].height > iHeight) continue; + int iDeltaH = iHeight - font_face->available_sizes[i].height; + int iDeltaW = font_face->available_sizes[i].width - iWidth; + int iScore = iDeltaH * iDeltaH * 3 + iDeltaW * iDeltaW; + if (iScore < iBestBitmapScore) { + iBestBitmapScore = iScore; + iBestBitmapIndex = i; + } + } + + // Select the bitmap strike, if there was one + if (iBestBitmapIndex != -1) + return FT_Select_Size(font_face, iBestBitmapIndex); + } + + // Go with the original size request if there was no bitmap strike, unless + // the size was very small, in which case scale things up, as vector fonts + // look rather poor at small sizes. + if (iHeight < 14) { + iWidth = iWidth * 14 / iHeight; + iHeight = 14; + } + if (iWidth < 9) { + iHeight = iHeight * 9 / iWidth; + iWidth = 9; + } + return FT_Set_Pixel_Sizes(font_face, iWidth, iHeight); } -FT_Error freetype_font::initialise() -{ - if(is_done_freetype_init) - return FT_Err_Ok; - if(freetype_init_count == 0) - { - int iError = FT_Init_FreeType(&freetype_library); - if(iError != FT_Err_Ok) - return iError; - } - ++freetype_init_count; - is_done_freetype_init = true; - return FT_Err_Ok; -} - -void freetype_font::clear_cache() -{ - for(cached_text* pEntry = cache; - pEntry != cache + (1 << cache_size_log2); ++pEntry) - { - pEntry->is_valid = false; - free_texture(pEntry); - } -} - -FT_Error freetype_font::set_face(const uint8_t* pData, size_t iLength) -{ - int iError; - if(freetype_library == nullptr) - { - iError = initialise(); - if(iError != FT_Err_Ok) - return iError; - } - if(font_face) - { - iError = FT_Done_Face(font_face); - if(iError != FT_Err_Ok) - return iError; - font_face = nullptr; - } - iError = FT_New_Memory_Face(freetype_library, pData, static_cast(iLength), 0, &font_face); - return iError; -} - -FT_Error freetype_font::match_bitmap_font(sprite_sheet* pBitmapFontSpriteSheet) -{ - if(pBitmapFontSpriteSheet == nullptr) - return FT_Err_Invalid_Argument; - - // Try to take the size and colour of a standard character (em is generally - // the standard font character, but for fonts which only have numbers, zero - // seems like the next best choice). - for(const char* sCharToTry = "M0"; *sCharToTry; ++sCharToTry) - { - unsigned int iWidth, iHeight; - unsigned int iSprite = *sCharToTry - 31; - if(pBitmapFontSpriteSheet->get_sprite_size(iSprite, &iWidth, &iHeight) - && pBitmapFontSpriteSheet->get_sprite_average_colour(iSprite, &colour) - && iWidth > 1 && iHeight > 1) - { - return set_ideal_character_size(iWidth, iHeight); - } - } - - // Take the average size of all characters, and the colour of one of them. - unsigned int iWidthSum = 0, iHeightSum = 0, iAverageNum = 0; - for(unsigned int i = 0; i < pBitmapFontSpriteSheet->get_sprite_count(); ++i) - { - unsigned int iWidth, iHeight; - pBitmapFontSpriteSheet->get_sprite_size_unchecked(i, &iWidth, &iHeight); - if(iWidth <= 1 || iHeight <= 1) - continue; - if(!pBitmapFontSpriteSheet->get_sprite_average_colour(i, &colour)) - continue; - iWidthSum += iWidth; - iHeightSum += iHeight; - ++iAverageNum; - } - if(iAverageNum == 0) - return FT_Err_Divide_By_Zero; - - return set_ideal_character_size((iWidthSum + iAverageNum / 2) / iAverageNum, - (iHeightSum + iAverageNum / 2) / iAverageNum); -} - -FT_Error freetype_font::set_ideal_character_size(int iWidth, int iHeight) -{ - if(font_face == nullptr) - return FT_Err_Invalid_Face_Handle; - - if(is_monochrome() || iHeight <= 14 || iWidth <= 9) - { - // Look for a bitmap strike of a similar size - int iBestBitmapScore = 50; - FT_Int iBestBitmapIndex = -1; - for(FT_Int i = 0; i < font_face->num_fixed_sizes; ++i) - { - if(font_face->available_sizes[i].height > iHeight) - continue; - int iDeltaH = iHeight - font_face->available_sizes[i].height; - int iDeltaW = font_face->available_sizes[i].width - iWidth; - int iScore = iDeltaH * iDeltaH * 3 + iDeltaW * iDeltaW; - if(iScore < iBestBitmapScore) - { - iBestBitmapScore = iScore; - iBestBitmapIndex = i; - } - } - - // Select the bitmap strike, if there was one - if(iBestBitmapIndex != -1) - return FT_Select_Size(font_face, iBestBitmapIndex); - } - - // Go with the original size request if there was no bitmap strike, unless - // the size was very small, in which case scale things up, as vector fonts - // look rather poor at small sizes. - if(iHeight < 14) - { - iWidth = iWidth * 14 / iHeight; - iHeight = 14; - } - if(iWidth < 9) - { - iHeight = iHeight * 9 / iWidth; - iWidth = 9; - } - return FT_Set_Pixel_Sizes(font_face, iWidth, iHeight); -} - -text_layout freetype_font::get_text_dimensions(const char* sMessage, size_t iMessageLength, int iMaxWidth) const -{ - return draw_text_wrapped(nullptr, sMessage, iMessageLength, 0, 0, iMaxWidth, INT_MAX, 0); +text_layout freetype_font::get_text_dimensions(const char* sMessage, + size_t iMessageLength, + int iMaxWidth) const { + return draw_text_wrapped(nullptr, sMessage, iMessageLength, 0, 0, iMaxWidth, + INT_MAX, 0); } void freetype_font::draw_text(render_target* pCanvas, const char* sMessage, - size_t iMessageLength, int iX, int iY) const -{ - draw_text_wrapped(pCanvas, sMessage, iMessageLength, iX, iY, INT_MAX); + size_t iMessageLength, int iX, int iY) const { + draw_text_wrapped(pCanvas, sMessage, iMessageLength, iX, iY, INT_MAX); } namespace { -struct codepoint_glyph -{ - FT_Glyph_Metrics metrics; - FT_Glyph glyph; - FT_UInt index; +struct codepoint_glyph { + FT_Glyph_Metrics metrics; + FT_Glyph glyph; + FT_UInt index; }; // Determine if the character code is a suitable Chinese/Japanese/Korean // character for a line break. bool isCjkBreakCharacter(int charcode) { - return (charcode == 0x3000 || // Ideographic space - charcode == 0x3002 || // Ideographic full stop - charcode == 0xff0c || // Fullwidth comma - charcode == 0xff0d || // Fullwidth hyphen-minus - charcode == 0xff1b || // Fullwidth semicolon - charcode == 0xff1f); //Fullwidth question mark + return (charcode == 0x3000 || // Ideographic space + charcode == 0x3002 || // Ideographic full stop + charcode == 0xff0c || // Fullwidth comma + charcode == 0xff0d || // Fullwidth hyphen-minus + charcode == 0xff1b || // Fullwidth semicolon + charcode == 0xff1f); // Fullwidth question mark } -} // namespace +FT_Pos pixel_align(FT_Pos position) { return ((position + 63) >> 6) << 6; } -text_layout freetype_font::draw_text_wrapped(render_target* pCanvas, const char* sMessage, - size_t iMessageLength, int iX, int iY, - int iWidth, int iMaxRows, int iSkipRows, text_alignment eAlign) const -{ - text_layout oDrawArea = {}; - int iNumRows = 0; - int iHandledRows = 0; +} // namespace - // Calculate an index into the cache to use for this piece of text. - size_t iHash = iMessageLength - + (static_cast(iMaxRows) << (cache_size_log2 / 8)) - + (static_cast(iSkipRows) << (cache_size_log2 / 4)) - + (static_cast(iWidth) << (cache_size_log2 / 2)) - + (static_cast(eAlign) << cache_size_log2); - for(size_t i = 0; i < iMessageLength; ++i) - iHash ^= (iHash << 5) + (iHash >> 2) + static_cast(sMessage[i]); - iHash &= (1 << cache_size_log2) - 1; +text_layout freetype_font::draw_text_wrapped(render_target* pCanvas, + const char* sMessage, + size_t iMessageLength, int iX, + int iY, int iWidth, int iMaxRows, + int iSkipRows, + text_alignment eAlign) const { + text_layout oDrawArea = {}; + int iNumRows = 0; + int iHandledRows = 0; - cached_text* pEntry = cache + iHash; - if(pEntry->message_length != iMessageLength || pEntry->width > iWidth - || (iWidth != INT_MAX && pEntry->width < iWidth) - || pEntry->alignment != eAlign || !pEntry->is_valid - || std::memcmp(pEntry->message, sMessage, iMessageLength) != 0) - { - // Cache entry does not match the message being drawn, so discard the - // cache entry. - free_texture(pEntry); - delete[] pEntry->data; - pEntry->data = nullptr; - pEntry->is_valid = false; + // Calculate an index into the cache to use for this piece of text. + size_t iHash = iMessageLength + + (static_cast(iMaxRows) << (cache_size_log2 / 8)) + + (static_cast(iSkipRows) << (cache_size_log2 / 4)) + + (static_cast(iWidth) << (cache_size_log2 / 2)) + + (static_cast(eAlign) << cache_size_log2); + for (size_t i = 0; i < iMessageLength; ++i) + iHash ^= (iHash << 5) + (iHash >> 2) + static_cast(sMessage[i]); + iHash &= (1 << cache_size_log2) - 1; - // Set the entry metadata to that of the new message. - if(iMessageLength > pEntry->message_buffer_length) - { - delete[] pEntry->message; - pEntry->message = new char[iMessageLength]; - pEntry->message_buffer_length = iMessageLength; + cached_text* pEntry = cache + iHash; + if (pEntry->message_length != iMessageLength || pEntry->width > iWidth || + (iWidth != INT_MAX && pEntry->width < iWidth) || + pEntry->alignment != eAlign || !pEntry->is_valid || + std::memcmp(pEntry->message, sMessage, iMessageLength) != 0) { + // Cache entry does not match the message being drawn, so discard the + // cache entry. + free_texture(pEntry); + delete[] pEntry->data; + pEntry->data = nullptr; + pEntry->is_valid = false; + + // Set the entry metadata to that of the new message. + if (iMessageLength > pEntry->message_buffer_length) { + delete[] pEntry->message; + pEntry->message = new char[iMessageLength]; + pEntry->message_buffer_length = iMessageLength; + } + std::memcpy(pEntry->message, sMessage, iMessageLength); + pEntry->message_length = iMessageLength; + pEntry->width = iWidth; + pEntry->alignment = eAlign; + + // Split the message into lines, and determine the position within the + // line for each character. + std::vector > vLines; + std::vector vCharPositions(iMessageLength); + std::map mapGlyphs; + vLines.reserve(2); + + FT_Vector ftvPen = {0, 0}; + FT_Bool bUseKerning = FT_HAS_KERNING(font_face); + FT_UInt iPreviousGlyphIndex = 0; + + const char* sMessageStart = sMessage; + const char* sMessageEnd = sMessage + iMessageLength; + const char* sLineStart = sMessageStart; + const char* sLineBreakPosition = sLineStart; + + while (sMessage != sMessageEnd) { + const char* sOldMessage = sMessage; + unsigned int iCode = next_utf8_codepoint(sMessage); + unsigned int iNextCode = + *reinterpret_cast(sMessage); + bool bIsNewLine = (iCode == '\n' && iNextCode == '\n') || + (iCode == '/' && iNextCode == '/'); + // Just replace single line breaks with space. + if (!bIsNewLine && iCode == '\n') { + iCode = ' '; + } + + codepoint_glyph& oGlyph = mapGlyphs[iCode]; + if (oGlyph.glyph == nullptr) { + oGlyph.index = FT_Get_Char_Index(font_face, iCode); + + /* FT_Error iError = */ + FT_Load_Glyph(font_face, oGlyph.index, FT_LOAD_DEFAULT); + // TODO: iError != FT_Err_Ok + + /* iError = */ + FT_Get_Glyph(font_face->glyph, &oGlyph.glyph); + // TODO: iError != FT_Err_Ok + + oGlyph.metrics = font_face->glyph->metrics; + } + + // Apply kerning + if (bUseKerning && iPreviousGlyphIndex && oGlyph.index) { + FT_Vector ftvKerning; + FT_Get_Kerning(font_face, iPreviousGlyphIndex, oGlyph.index, + FT_KERNING_DEFAULT, &ftvKerning); + ftvPen.x += ftvKerning.x; + ftvPen.y += ftvKerning.y; + } + + // Make an automatic line break if one is needed. + int line_width_with_glyph = + (ftvPen.x + oGlyph.metrics.horiBearingX + oGlyph.metrics.width + 63) / + 64; + if (line_width_with_glyph >= iWidth || bIsNewLine) { + if (bIsNewLine) { + sLineBreakPosition = sOldMessage; } - std::memcpy(pEntry->message, sMessage, iMessageLength); - pEntry->message_length = iMessageLength; - pEntry->width = iWidth; - pEntry->alignment = eAlign; - - // Split the message into lines, and determine the position within the - // line for each character. - std::vector > vLines; - std::vector vCharPositions(iMessageLength); - std::map mapGlyphs; - vLines.reserve(2); - - FT_Vector ftvPen = {0, 0}; - FT_Bool bUseKerning = FT_HAS_KERNING(font_face); - FT_UInt iPreviousGlyphIndex = 0; - - const char* sMessageStart = sMessage; - const char* sMessageEnd = sMessage + iMessageLength; - const char* sLineStart = sMessageStart; - const char* sLineBreakPosition = sLineStart; - - while(sMessage != sMessageEnd) - { - const char* sOldMessage = sMessage; - unsigned int iCode = next_utf8_codepoint(sMessage); - unsigned int iNextCode = *reinterpret_cast(sMessage); - bool bIsNewLine = (iCode == '\n' && iNextCode == '\n') || (iCode == '/' && iNextCode == '/'); - // Just replace single line breaks with space. - if(!bIsNewLine && iCode == '\n') - { - iCode = ' '; + ftvPen.x = ftvPen.y = 0; + iPreviousGlyphIndex = 0; + if (sLineStart != sLineBreakPosition) { + // Only really save if we have skipped enough lines + if (iHandledRows >= iSkipRows) { + vLines.push_back(std::make_pair(sLineStart, sLineBreakPosition)); + } + if (bIsNewLine) { + if (iHandledRows + 1 >= iSkipRows) { + vLines.push_back( + std::make_pair(sLineBreakPosition, sLineBreakPosition)); } - - codepoint_glyph& oGlyph = mapGlyphs[iCode]; - if(oGlyph.glyph == nullptr) - { - oGlyph.index = FT_Get_Char_Index(font_face, iCode); - - /* FT_Error iError = */ - FT_Load_Glyph(font_face, oGlyph.index, FT_LOAD_DEFAULT); - // TODO: iError != FT_Err_Ok - - /* iError = */ - FT_Get_Glyph(font_face->glyph, &oGlyph.glyph); - // TODO: iError != FT_Err_Ok - - oGlyph.metrics = font_face->glyph->metrics; - } - - // Apply kerning - if(bUseKerning && iPreviousGlyphIndex && oGlyph.index) - { - FT_Vector ftvKerning; - FT_Get_Kerning(font_face, iPreviousGlyphIndex, - oGlyph.index, FT_KERNING_DEFAULT, &ftvKerning); - ftvPen.x += ftvKerning.x; - ftvPen.y += ftvKerning.y; - } - - // Make an automatic line break if one is needed. - if((ftvPen.x + oGlyph.metrics.horiBearingX + - oGlyph.metrics.width + 63) / 64 >= iWidth || bIsNewLine) - { - if(bIsNewLine) - { - sLineBreakPosition = sOldMessage; - } - ftvPen.x = ftvPen.y = 0; - iPreviousGlyphIndex = 0; - if(sLineStart != sLineBreakPosition) - { - // Only really save if we have skipped enough lines - if(iHandledRows >= iSkipRows) - { - vLines.push_back(std::make_pair(sLineStart, sLineBreakPosition)); - } - if(bIsNewLine) - { - if(iHandledRows + 1 >= iSkipRows) - { - vLines.push_back(std::make_pair(sLineBreakPosition, sLineBreakPosition)); - } - next_utf8_codepoint(sLineBreakPosition); - iHandledRows++; - } - sMessage = sLineBreakPosition; - next_utf8_codepoint(sMessage); - sLineStart = sMessage; - } - else - { - if(iHandledRows >= iSkipRows) { - vLines.push_back(std::make_pair(sLineStart, sOldMessage)); - } - if(bIsNewLine) - { - next_utf8_codepoint(sMessage); - sLineStart = sLineBreakPosition = sMessage; - } - else - { - sMessage = sLineStart = sLineBreakPosition = sOldMessage; - } - } - iHandledRows++; - continue; - } - - // Determine if a line can be broken at the current position. - if (iCode == ' ') { - sLineBreakPosition = sOldMessage; - } else if (isCjkBreakCharacter(iCode)) { - // break after this codepoint (cjk codepoints are 3 bytes in utf-8) - sLineBreakPosition = sOldMessage + 3; - } - - // Save (unless we are skipping lines) and advance the pen. - if(iHandledRows >= iSkipRows) - { - vCharPositions[sOldMessage - sMessageStart] = ftvPen; - } - - iPreviousGlyphIndex = oGlyph.index; - ftvPen.x += oGlyph.metrics.horiAdvance; + next_utf8_codepoint(sLineBreakPosition); + iHandledRows++; + } + sMessage = sLineBreakPosition; + next_utf8_codepoint(sMessage); + sLineStart = sMessage; + } else { + if (iHandledRows >= iSkipRows) { + vLines.push_back(std::make_pair(sLineStart, sOldMessage)); + } + if (bIsNewLine) { + next_utf8_codepoint(sMessage); + sLineStart = sLineBreakPosition = sMessage; + } else { + sMessage = sLineStart = sLineBreakPosition = sOldMessage; + } } - if(sLineStart != sMessageEnd) - vLines.push_back(std::make_pair(sLineStart, sMessageEnd)); - sMessage = sMessageStart; + iHandledRows++; + continue; + } - // Finalise the position of each character (alignment might change X, - // and baseline / lines will change Y), and calculate overall height - // and widest line. - FT_Pos iPriorLinesHeight = 0; - FT_Pos iLineWidth = 0, iAlignDelta = 0, iWidestLine = 0; - const FT_Pos iLineSpacing = 2 << 6; - codepoint_glyph& oGlyph = mapGlyphs['l']; + // Determine if a line can be broken at the current position. + if (iCode == ' ') { + sLineBreakPosition = sOldMessage; + } else if (isCjkBreakCharacter(iCode)) { + // break after this codepoint (cjk codepoints are 3 bytes in + // utf-8) + sLineBreakPosition = sOldMessage + 3; + } + + // Save (unless we are skipping lines) and advance the pen. + if (iHandledRows >= iSkipRows) { + vCharPositions[sOldMessage - sMessageStart] = ftvPen; + } + + iPreviousGlyphIndex = oGlyph.index; + ftvPen.x += oGlyph.metrics.horiAdvance; + } + if (sLineStart != sMessageEnd) + vLines.push_back(std::make_pair(sLineStart, sMessageEnd)); + sMessage = sMessageStart; + + // Finalise the position of each character (alignment might change X, + // and baseline / lines will change Y), and calculate overall height + // and widest line. + FT_Pos iPriorLinesHeight = 0; + FT_Pos iLineWidth = 0, iAlignDelta = 0, iWidestLine = 0; + const FT_Pos iLineSpacing = 2 << 6; + codepoint_glyph& oGlyph = mapGlyphs['l']; + FT_Pos iBearingY = oGlyph.metrics.horiBearingY; + FT_Pos iNormalLineHeight = oGlyph.metrics.height - iBearingY; + + iBearingY = pixel_align(iBearingY); + iNormalLineHeight += iBearingY; + iNormalLineHeight += iLineSpacing; + + iNormalLineHeight = pixel_align(iNormalLineHeight); + + for (auto itr = vLines.begin(); itr != vLines.end() && iNumRows < iMaxRows; + ++itr) { + // Calculate the X change resulting from alignment. + const char* sLastChar = previous_utf8_codepoint(itr->second); + codepoint_glyph& oLastGlyph = mapGlyphs[decode_utf8(sLastChar)]; + iLineWidth = vCharPositions[sLastChar - sMessage].x + + oLastGlyph.metrics.horiBearingX + oLastGlyph.metrics.width; + if ((iLineWidth >> 6) < iWidth) { + iAlignDelta = + ((iWidth * 64 - iLineWidth) * static_cast(eAlign)) / 2; + } + if (iLineWidth > iWidestLine) iWidestLine = iLineWidth; + + // Calculate the line height and baseline position. + FT_Pos iLineHeight = 0; + FT_Pos iBaselinePos = 0; + for (const char* s = itr->first; s < itr->second;) { + codepoint_glyph& oGlyph = mapGlyphs[next_utf8_codepoint(s)]; FT_Pos iBearingY = oGlyph.metrics.horiBearingY; - FT_Pos iNormalLineHeight = oGlyph.metrics.height - iBearingY; - iBearingY = ((iBearingY + 63) >> 6) << 6; // Pixel-align - iNormalLineHeight += iBearingY; - iNormalLineHeight += iLineSpacing; - iNormalLineHeight = ((iNormalLineHeight + 63) >> 6) << 6; // Pixel-align - for(std::vector >::const_iterator - itr = vLines.begin(), itrEnd = vLines.end(); itr != itrEnd && iNumRows < iMaxRows; ++itr) - { - // Calculate the X change resulting from alignment. - const char* sLastChar = previous_utf8_codepoint(itr->second); - codepoint_glyph& oLastGlyph = mapGlyphs[decode_utf8(sLastChar)]; - iLineWidth = vCharPositions[sLastChar - sMessage].x - + oLastGlyph.metrics.horiBearingX - + oLastGlyph.metrics.width; - if((iLineWidth >> 6) < iWidth) - { - iAlignDelta = ((iWidth * 64 - iLineWidth) * - static_cast(eAlign)) / 2; - } - if(iLineWidth > iWidestLine) - iWidestLine = iLineWidth; + FT_Pos iCoBearingY = oGlyph.metrics.height - iBearingY; + if (iBearingY > iBaselinePos) iBaselinePos = iBearingY; + if (iCoBearingY > iLineHeight) iLineHeight = iCoBearingY; + } - // Calculate the line height and baseline position. - FT_Pos iLineHeight = 0; - FT_Pos iBaselinePos = 0; - for(const char* s = itr->first; s < itr->second; ) - { - codepoint_glyph& oGlyph = mapGlyphs[next_utf8_codepoint(s)]; - FT_Pos iBearingY = oGlyph.metrics.horiBearingY; - FT_Pos iCoBearingY = oGlyph.metrics.height - iBearingY; - if(iBearingY > iBaselinePos) - iBaselinePos = iBearingY; - if(iCoBearingY > iLineHeight) - iLineHeight = iCoBearingY; - } - iBaselinePos = ((iBaselinePos + 63) >> 6) << 6; // Pixel-align - iLineHeight += iBaselinePos; - iLineHeight += iLineSpacing; - iLineHeight = ((iLineHeight + 63) >> 6) << 6; // Pixel-align + iBaselinePos = pixel_align(iBaselinePos); + iLineHeight += iBaselinePos; + iLineHeight += iLineSpacing; + iLineHeight = pixel_align(iLineHeight); - iNormalLineHeight = std::max(iNormalLineHeight, iLineHeight); + iNormalLineHeight = std::max(iNormalLineHeight, iLineHeight); - // Apply the character position changes. - for(const char* s = itr->first; s < itr->second; next_utf8_codepoint(s)) - { - FT_Vector& ftvPos = vCharPositions[s - sMessage]; - ftvPos.x += iAlignDelta; - ftvPos.y += iBaselinePos + iPriorLinesHeight; - } - // Empty lines is a special case - if(itr->first == itr->second) - { - iPriorLinesHeight += iNormalLineHeight; - } - else - { - iPriorLinesHeight += iLineHeight; - } - iNumRows++; - } - if(iPriorLinesHeight > 0) - iPriorLinesHeight -= iLineSpacing; - pEntry->height = static_cast(1 + (iPriorLinesHeight >> 6)); - pEntry->widest_line_width = static_cast(1 + (iWidestLine >> 6)); - pEntry->row_count = iNumRows; - if(iWidth == INT_MAX) - pEntry->width = pEntry->widest_line_width; - pEntry->last_x = 1 + (static_cast(iLineWidth + iAlignDelta) >> 6); + // Apply the character position changes. + for (const char* s = itr->first; s < itr->second; + next_utf8_codepoint(s)) { + FT_Vector& ftvPos = vCharPositions[s - sMessage]; + ftvPos.x += iAlignDelta; + ftvPos.y += iBaselinePos + iPriorLinesHeight; + } + // Empty lines is a special case + if (itr->first == itr->second) { + iPriorLinesHeight += iNormalLineHeight; + } else { + iPriorLinesHeight += iLineHeight; + } + iNumRows++; + } + if (iPriorLinesHeight > 0) iPriorLinesHeight -= iLineSpacing; + pEntry->height = static_cast(1 + (iPriorLinesHeight >> 6)); + pEntry->widest_line_width = static_cast(1 + (iWidestLine >> 6)); + pEntry->row_count = iNumRows; + if (iWidth == INT_MAX) pEntry->width = pEntry->widest_line_width; + pEntry->last_x = 1 + (static_cast(iLineWidth + iAlignDelta) >> 6); - // Get a bitmap for each glyph. - bool bIsMonochrome = is_monochrome(); - FT_Render_Mode eRenderMode = bIsMonochrome ? FT_RENDER_MODE_MONO - : FT_RENDER_MODE_NORMAL; - for(std::map::iterator itr = - mapGlyphs.begin(), itrEnd = mapGlyphs.end(); itr != itrEnd; ++itr) - { - FT_Glyph_To_Bitmap(&itr->second.glyph, eRenderMode, nullptr, 1); - } - - // Prepare a canvas for rendering. - pEntry->data = new uint8_t[pEntry->width * pEntry->height]; - std::memset(pEntry->data, 0, pEntry->width * pEntry->height); - - int iDrawnLines = 0; - // Render each character to the canvas. - for(std::vector >::const_iterator - itr = vLines.begin(), itrEnd = vLines.end(); - itr != itrEnd && iDrawnLines < iMaxRows + iSkipRows; ++itr) - { - iDrawnLines++; - for(const char* s = itr->first; s < itr->second; ) - { - FT_Vector& ftvPos = vCharPositions[s - sMessage]; - unsigned int iCode = next_utf8_codepoint(s); - if(iCode == '\n') - { - iCode = ' '; - } - FT_BitmapGlyph pGlyph = reinterpret_cast( - mapGlyphs[iCode].glyph); - FT_Pos x = pGlyph->left + (ftvPos.x >> 6); - FT_Pos y = (ftvPos.y >> 6) - pGlyph->top; - // We may have asked for grayscale but been given monochrome, - // hence use the bitmap's pixel_mode rather than bIsMonochrome. - switch(pGlyph->bitmap.pixel_mode) - { - case FT_PIXEL_MODE_GRAY: - render_gray(pEntry, &pGlyph->bitmap, x, y); - break; - case FT_PIXEL_MODE_MONO: - render_mono(pEntry, &pGlyph->bitmap, x, y); - break; - } - } - } - - // Free all glyphs. - for(std::map::const_iterator itr = - mapGlyphs.begin(), itrEnd = mapGlyphs.end(); itr != itrEnd; ++itr) - { - FT_Done_Glyph(itr->second.glyph); - } - - pEntry->is_valid = true; + // Get a bitmap for each glyph. + bool bIsMonochrome = is_monochrome(); + FT_Render_Mode eRenderMode = + bIsMonochrome ? FT_RENDER_MODE_MONO : FT_RENDER_MODE_NORMAL; + for (auto itr = mapGlyphs.begin(), itrEnd = mapGlyphs.end(); itr != itrEnd; + ++itr) { + FT_Glyph_To_Bitmap(&itr->second.glyph, eRenderMode, nullptr, 1); } - if (pCanvas != nullptr) - { - if (pEntry->texture == nullptr) - make_texture(pCanvas, pEntry); - draw_texture(pCanvas, pEntry, iX, iY); + // Prepare a canvas for rendering. + pEntry->data = new uint8_t[pEntry->width * pEntry->height]; + std::memset(pEntry->data, 0, pEntry->width * pEntry->height); + + int iDrawnLines = 0; + // Render each character to the canvas. + for (auto itr = vLines.begin(); + itr != vLines.end() && iDrawnLines < iMaxRows + iSkipRows; ++itr) { + iDrawnLines++; + for (const char* s = itr->first; s < itr->second;) { + FT_Vector& ftvPos = vCharPositions[s - sMessage]; + unsigned int iCode = next_utf8_codepoint(s); + if (iCode == '\n') { + iCode = ' '; + } + FT_BitmapGlyph pGlyph = + reinterpret_cast(mapGlyphs[iCode].glyph); + FT_Pos x = pGlyph->left + (ftvPos.x >> 6); + FT_Pos y = (ftvPos.y >> 6) - pGlyph->top; + // We may have asked for grayscale but been given monochrome, + // hence use the bitmap's pixel_mode rather than bIsMonochrome. + switch (pGlyph->bitmap.pixel_mode) { + case FT_PIXEL_MODE_GRAY: + render_gray(pEntry, &pGlyph->bitmap, x, y); + break; + case FT_PIXEL_MODE_MONO: + render_mono(pEntry, &pGlyph->bitmap, x, y); + break; + } + } } - oDrawArea.width = pEntry->widest_line_width; - oDrawArea.end_x = iX + pEntry->last_x; - oDrawArea.end_y = iY + pEntry->height; - oDrawArea.row_count = pEntry->row_count; - return oDrawArea; + + // Free all glyphs. + for (auto itr = mapGlyphs.begin(); itr != mapGlyphs.end(); ++itr) { + FT_Done_Glyph(itr->second.glyph); + } + + pEntry->is_valid = true; + } + + if (pCanvas != nullptr) { + if (pEntry->texture == nullptr) { + make_texture(pCanvas, pEntry); + } + draw_texture(pCanvas, pEntry, iX, iY); + } + oDrawArea.width = pEntry->widest_line_width; + oDrawArea.end_x = iX + pEntry->last_x; + oDrawArea.end_y = iY + pEntry->height; + oDrawArea.row_count = pEntry->row_count; + return oDrawArea; } // In theory, the renderers should only be invoked with coordinates which end @@ -833,69 +784,55 @@ text_layout freetype_font::draw_text_wrapped(render_target* pCanvas, const char* // at which point the following line can be removed. // #define TRUST_RENDER_COORDS -void freetype_font::render_mono(cached_text *pCacheEntry, FT_Bitmap* pBitmap, FT_Pos x, FT_Pos y) const -{ - uint8_t* pOutRow = pCacheEntry->data + y * pCacheEntry->width + x; - uint8_t* pInRow = pBitmap->buffer; - for(int iY = 0; iY < pBitmap->rows; ++iY, pOutRow += pCacheEntry->width, - pInRow += pBitmap->pitch) - { +void freetype_font::render_mono(cached_text* pCacheEntry, FT_Bitmap* pBitmap, + FT_Pos x, FT_Pos y) const { + uint8_t* pOutRow = pCacheEntry->data + y * pCacheEntry->width + x; + uint8_t* pInRow = pBitmap->buffer; + for (int iY = 0; iY < pBitmap->rows; + ++iY, pOutRow += pCacheEntry->width, pInRow += pBitmap->pitch) { #ifndef TRUST_RENDER_COORDS - if(y + iY < 0) - continue; - if(y + iY >= pCacheEntry->height) - break; + if (y + iY < 0) continue; + if (y + iY >= pCacheEntry->height) break; #endif - uint8_t *pIn = pInRow, *pOut = pOutRow; - uint8_t iMask = 0x80; - for(int iX = 0; iX < pBitmap->width; ++iX, ++pOut) - { + uint8_t *pIn = pInRow, *pOut = pOutRow; + uint8_t iMask = 0x80; + for (int iX = 0; iX < pBitmap->width; ++iX, ++pOut) { #ifndef TRUST_RENDER_COORDS - if(x + iX < 0) - continue; - if(x + iX >= pCacheEntry->width) - break; + if (x + iX < 0) continue; + if (x + iX >= pCacheEntry->width) break; #endif - if(*pIn & iMask) - *pOut = 0xFF; - iMask = static_cast(iMask / 2); - if(iMask == 0) - { - iMask = 0x80; - ++pIn; - } - } + if (*pIn & iMask) *pOut = 0xFF; + iMask = static_cast(iMask / 2); + if (iMask == 0) { + iMask = 0x80; + ++pIn; + } } + } } -void freetype_font::render_gray(cached_text *pCacheEntry, FT_Bitmap* pBitmap, FT_Pos x, FT_Pos y) const -{ - uint8_t* pOutRow = pCacheEntry->data + y * pCacheEntry->width + x; - uint8_t* pInRow = pBitmap->buffer; - for(int iY = 0; iY < pBitmap->rows; ++iY, pOutRow += pCacheEntry->width, - pInRow += pBitmap->pitch) - { +void freetype_font::render_gray(cached_text* pCacheEntry, FT_Bitmap* pBitmap, + FT_Pos x, FT_Pos y) const { + uint8_t* pOutRow = pCacheEntry->data + y * pCacheEntry->width + x; + uint8_t* pInRow = pBitmap->buffer; + for (int iY = 0; iY < pBitmap->rows; + ++iY, pOutRow += pCacheEntry->width, pInRow += pBitmap->pitch) { #ifndef TRUST_RENDER_COORDS - if(y + iY < 0) - continue; - if(y + iY >= pCacheEntry->height) - break; + if (y + iY < 0) continue; + if (y + iY >= pCacheEntry->height) break; #endif - uint8_t *pIn = pInRow, *pOut = pOutRow; - for(int iX = 0; iX < pBitmap->width; ++iX, ++pIn, ++pOut) - { + uint8_t *pIn = pInRow, *pOut = pOutRow; + for (int iX = 0; iX < pBitmap->width; ++iX, ++pIn, ++pOut) { #ifndef TRUST_RENDER_COORDS - if(x + iX < 0) - continue; - if(x + iX >= pCacheEntry->width) - break; + if (x + iX < 0) continue; + if (x + iX >= pCacheEntry->width) break; #endif - unsigned int iIn = *pIn; - unsigned int iOut = *pOut; - uint8_t cMerged = static_cast(iIn + iOut - (iIn * iOut) / 255); - *pOut = cMerged; - } + unsigned int iIn = *pIn; + unsigned int iOut = *pOut; + uint8_t cMerged = static_cast(iIn + iOut - (iIn * iOut) / 255); + *pOut = cMerged; } + } } -#endif // CORSIX_TH_USE_FREETYPE2 +#endif // CORSIX_TH_USE_FREETYPE2 diff --git a/CorsixTH/Src/th_gfx_font.h b/CorsixTH/Src/th_gfx_font.h index a0602381..54c45f4b 100644 --- a/CorsixTH/Src/th_gfx_font.h +++ b/CorsixTH/Src/th_gfx_font.h @@ -28,129 +28,131 @@ SOFTWARE. #include FT_FREETYPE_H #endif +class render_target; + +class sprite_sheet; + enum class text_alignment { - left = 0, - center = 1, - right = 2, + left = 0, + center = 1, + right = 2, }; /** Structure for the bounds of a text string that is rendered to the screen. */ -struct text_layout -{ - //! Number of rows the rendered text spans - int row_count; +struct text_layout { + //! Number of rows the rendered text spans + int row_count; - //! Left X-coordinate for the start of the text - int start_x; + //! Left X-coordinate for the start of the text + int start_x; - //! Right X-coordinate for the right part of the last letter rendered - int end_x; + //! Right X-coordinate for the right part of the last letter rendered + int end_x; - //! Top Y-coordinate for the start of the text - int start_y; + //! Top Y-coordinate for the start of the text + int start_y; - //! Bottom Y-coordinate for the end of the text - int end_y; + //! Bottom Y-coordinate for the end of the text + int end_y; - //! Width of the widest line in the text - int width; + //! Width of the widest line in the text + int width; }; -class font -{ -public: - virtual ~font() = default; +class font { + public: + virtual ~font() = default; - //! Get the size of drawn text. - /*! - If iMaxWidth is specified the text will wrap, so that the height can - span multiple rows. Otherwise gets the size of a single line of text. - @param sMessage A UTF-8 encoded string containing a single line of text - to measure the width and height of. - @param iMessageLength The length, in bytes (not characters), of the - string at sMessage. - @param iMaxWidth The maximum length, in pixels, that the text may - occupy. Default is INT_MAX. - */ - virtual text_layout get_text_dimensions(const char* sMessage, size_t iMessageLength, - int iMaxWidth = INT_MAX) const = 0; + //! Get the size of drawn text. + /*! + If iMaxWidth is specified the text will wrap, so that the height can + span multiple rows. Otherwise gets the size of a single line of text. + @param sMessage A UTF-8 encoded string containing a single line of text + to measure the width and height of. + @param iMessageLength The length, in bytes (not characters), of the + string at sMessage. + @param iMaxWidth The maximum length, in pixels, that the text may + occupy. Default is INT_MAX. + */ + virtual text_layout get_text_dimensions(const char* sMessage, + size_t iMessageLength, + int iMaxWidth = INT_MAX) const = 0; - //! Draw a single line of text - /*! - @param pCanvas The render target to draw onto. - @param sMessage A UTF-8 encoded string containing a single line of text - to draw. - @param iMessageLength The length, in bytes (not characters), of the - string at sMessage. - @param iX The X coordinate of the top-left corner of the bounding - rectangle for the drawn text. - @param iY The Y coordinate of the top-left corner of the bounding - rectangle for the drawn text. - */ - virtual void draw_text(render_target* pCanvas, const char* sMessage, - size_t iMessageLength, int iX, int iY) const = 0; + //! Draw a single line of text + /*! + @param pCanvas The render target to draw onto. + @param sMessage A UTF-8 encoded string containing a single line of text + to draw. + @param iMessageLength The length, in bytes (not characters), of the + string at sMessage. + @param iX The X coordinate of the top-left corner of the bounding + rectangle for the drawn text. + @param iY The Y coordinate of the top-left corner of the bounding + rectangle for the drawn text. + */ + virtual void draw_text(render_target* pCanvas, const char* sMessage, + size_t iMessageLength, int iX, int iY) const = 0; - //! Draw a single line of text, splitting it at word boundaries - /*! - This function still only draws a single line of text (i.e. any line - breaks like `\r` and `\n` in sMessage are ignored), but inserts line breaks - between words so that no single line is wider than iWidth pixels. - If iMaxRows is specified it will simply cut after that many rows. - @param pCanvas The canvas on which to draw. Can be nullptr, in which case - nothing is drawn, but other calculations are still made. - @param sMessage The line of text to draw, encoded in CP437. - @param iMessageLength The length (in bytes) of sMessage. - @param iX The X position to start drawing on the canvas. - @param iY The Y position to start drawing on the canvas. - @param iWidth The maximum width of each line of text. - @param iMaxRows The maximum number of rows to draw. Default is INT_MAX. - @param iSkipRows Start rendering text after skipping this many rows. - @param eAlign How to align each line of text if the width of the line - of text is smaller than iWidth. - */ - virtual text_layout draw_text_wrapped(render_target* pCanvas, const char* sMessage, - size_t iMessageLength, int iX, int iY, - int iWidth, int iMaxRows = INT_MAX, int iSkipRows = 0, - text_alignment eAlign = text_alignment::left) const = 0; + //! Draw a single line of text, splitting it at word boundaries + /*! + This function still only draws a single line of text (i.e. any line + breaks like `\r` and `\n` in sMessage are ignored), but inserts line + breaks between words so that no single line is wider than iWidth pixels. + If iMaxRows is specified it will simply cut after that many rows. + @param pCanvas The canvas on which to draw. Can be nullptr, in which + case nothing is drawn, but other calculations are still made. + @param sMessage The line of text to draw, encoded in CP437. + @param iMessageLength The length (in bytes) of sMessage. + @param iX The X position to start drawing on the canvas. + @param iY The Y position to start drawing on the canvas. + @param iWidth The maximum width of each line of text. + @param iMaxRows The maximum number of rows to draw. Default is INT_MAX. + @param iSkipRows Start rendering text after skipping this many rows. + @param eAlign How to align each line of text if the width of the line + of text is smaller than iWidth. + */ + virtual text_layout draw_text_wrapped( + render_target* pCanvas, const char* sMessage, size_t iMessageLength, + int iX, int iY, int iWidth, int iMaxRows = INT_MAX, int iSkipRows = 0, + text_alignment eAlign = text_alignment::left) const = 0; }; -class bitmap_font final : public font -{ -public: - bitmap_font(); +class bitmap_font final : public font { + public: + bitmap_font(); - //! Set the character glyph sprite sheet - /*! - The sprite sheet should have the space character (ASCII 0x20) at sprite - index 1, and other ASCII characters following on in simple order (i.e. - '!' (ASCII 0x21) at index 2, 'A' (ASCII 0x41) at index 34, etc.) - */ - void set_sprite_sheet(sprite_sheet* pSpriteSheet); + //! Set the character glyph sprite sheet + /*! + The sprite sheet should have the space character (ASCII 0x20) at sprite + index 1, and other ASCII characters following on in simple order (i.e. + '!' (ASCII 0x21) at index 2, 'A' (ASCII 0x41) at index 34, etc.) + */ + void set_sprite_sheet(sprite_sheet* pSpriteSheet); - sprite_sheet* get_sprite_sheet() {return sheet;} + sprite_sheet* get_sprite_sheet() { return sheet; } - //! Set the separation between characters and between lines - /*! - Generally, the sprite sheet glyphs will already include separation, and - thus no extra separation is required (set iCharSep and iLineSep to 0). - */ - void set_separation(int iCharSep, int iLineSep); + //! Set the separation between characters and between lines + /*! + Generally, the sprite sheet glyphs will already include separation, and + thus no extra separation is required (set iCharSep and iLineSep to 0). + */ + void set_separation(int iCharSep, int iLineSep); - text_layout get_text_dimensions(const char* sMessage, size_t iMessageLength, - int iMaxWidth = INT_MAX) const override; + text_layout get_text_dimensions(const char* sMessage, size_t iMessageLength, + int iMaxWidth = INT_MAX) const override; - void draw_text(render_target* pCanvas, const char* sMessage, - size_t iMessageLength, int iX, int iY) const override; + void draw_text(render_target* pCanvas, const char* sMessage, + size_t iMessageLength, int iX, int iY) const override; - text_layout draw_text_wrapped(render_target* pCanvas, const char* sMessage, - size_t iMessageLength, int iX, int iY, - int iWidth, int iMaxRows = INT_MAX, int iSkipRows = 0, - text_alignment eAlign = text_alignment::left) const override; + text_layout draw_text_wrapped( + render_target* pCanvas, const char* sMessage, size_t iMessageLength, + int iX, int iY, int iWidth, int iMaxRows = INT_MAX, int iSkipRows = 0, + text_alignment eAlign = text_alignment::left) const override; -private: - sprite_sheet* sheet; - int letter_spacing; - int line_spacing; + private: + sprite_sheet* sheet; + int letter_spacing; + int line_spacing; }; #ifdef CORSIX_TH_USE_FREETYPE2 @@ -167,162 +169,163 @@ private: THRawBitmap class, but with an alpha channel, and a single colour rather than a palette). */ -class freetype_font final : public font -{ -public: - freetype_font(); - ~freetype_font(); +class freetype_font final : public font { + public: + freetype_font(); + ~freetype_font(); - //! Get the copyright notice which should be displayed for FreeType2. - /*! - To comply with the FreeType2 license, the string returned by this - function needs to be displayed at some point. - @return A null-terminated UTF-8 encoded string. - */ - static const char* get_copyright_notice(); + //! Get the copyright notice which should be displayed for FreeType2. + /*! + To comply with the FreeType2 license, the string returned by this + function needs to be displayed at some point. + @return A null-terminated UTF-8 encoded string. + */ + static const char* get_copyright_notice(); - //! Initialise the FreeType2 library. - /*! - This will be called automatically by setFace() as required. - */ - FT_Error initialise(); + //! Initialise the FreeType2 library. + /*! + This will be called automatically by setFace() as required. + */ + FT_Error initialise(); - //! Remove all cached strings, as our graphics context has changed - void clear_cache(); + //! Remove all cached strings, as our graphics context has changed + void clear_cache(); - //! Set the font face to be used. - /*! - @param pData Pointer to the start of a font file loaded into memory. - This block of memory must remain valid for at least the lifetime - of the THFreeTypeFont objcect. - @param iLength The size, in bytes, of the font file at pData. - */ - FT_Error set_face(const uint8_t* pData, size_t iLength); + //! Set the font face to be used. + /*! + @param pData Pointer to the start of a font file loaded into memory. + This block of memory must remain valid for at least the lifetime + of the THFreeTypeFont objcect. + @param iLength The size, in bytes, of the font file at pData. + */ + FT_Error set_face(const uint8_t* pData, size_t iLength); - //! Set the font size and colour to match that of a bitmap font. - /*! - Note that the matching is done on a best-effort basis, and will likely - not be perfect. This must be called after setFace(). + //! Set the font size and colour to match that of a bitmap font. + /*! + Note that the matching is done on a best-effort basis, and will likely + not be perfect. This must be called after setFace(). - @param pBitmapFontSpriteSheet The sprite sheet of the bitmap font. - */ - FT_Error match_bitmap_font(sprite_sheet* pBitmapFontSpriteSheet); + @param pBitmapFontSpriteSheet The sprite sheet of the bitmap font. + */ + FT_Error match_bitmap_font(sprite_sheet* pBitmapFontSpriteSheet); - //! Set the ideal character size using pixel values. - /*! - Note that the given size might be changed a small amount if doing so - would result in a much nicer rendered font. This must be called after - setFace(). - */ - FT_Error set_ideal_character_size(int iWidth, int iHeight); + //! Set the ideal character size using pixel values. + /*! + Note that the given size might be changed a small amount if doing so + would result in a much nicer rendered font. This must be called after + setFace(). + */ + FT_Error set_ideal_character_size(int iWidth, int iHeight); - text_layout get_text_dimensions(const char* sMessage, size_t iMessageLength, - int iMaxWidth = INT_MAX) const override; + text_layout get_text_dimensions(const char* sMessage, size_t iMessageLength, + int iMaxWidth = INT_MAX) const override; - void draw_text(render_target* pCanvas, const char* sMessage, - size_t iMessageLength, int iX, int iY) const override; + void draw_text(render_target* pCanvas, const char* sMessage, + size_t iMessageLength, int iX, int iY) const override; - text_layout draw_text_wrapped(render_target* pCanvas, const char* sMessage, - size_t iMessageLength, int iX, int iY, - int iWidth, int iMaxRows = INT_MAX, int iSkipRows = 0, - text_alignment eAlign = text_alignment::left) const override; + text_layout draw_text_wrapped( + render_target* pCanvas, const char* sMessage, size_t iMessageLength, + int iX, int iY, int iWidth, int iMaxRows = INT_MAX, int iSkipRows = 0, + text_alignment eAlign = text_alignment::left) const override; -private: - struct cached_text - { - //! The text being converted to pixels - char* message; + private: + struct cached_text { + //! The text being converted to pixels + char* message; - //! Raw pixel data in row major 8-bit greyscale - uint8_t* data; + //! Raw pixel data in row major 8-bit greyscale + uint8_t* data; - //! Generated texture ready to be rendered - SDL_Texture* texture; + //! Generated texture ready to be rendered + SDL_Texture* texture; - //! The length of sMessage - size_t message_length; + //! The length of sMessage + size_t message_length; - //! The size of the buffer allocated to store sMessage - size_t message_buffer_length; + //! The size of the buffer allocated to store sMessage + size_t message_buffer_length; - //! Width of the image to draw - int width; + //! Width of the image to draw + int width; - //! Height of the image to draw - int height; + //! Height of the image to draw + int height; - //! The width of the longest line of text in in the textbox in pixels - int widest_line_width; + //! The width of the longest line of text in in the textbox in pixels + int widest_line_width; - //! X Coordinate trailing the last character in canvas coordinates - int last_x; + //! X Coordinate trailing the last character in canvas coordinates + int last_x; - //! Number of rows required - int row_count; + //! Number of rows required + int row_count; - //! Alignment of the message in the box - text_alignment alignment; + //! Alignment of the message in the box + text_alignment alignment; - //! True when the data reflects the message given the size constraints - bool is_valid; - }; + //! True when the data reflects the message given the size constraints + bool is_valid; + }; - //! Render a FreeType2 monochrome bitmap to a cache canvas. - void render_mono(cached_text *pCacheEntry, FT_Bitmap* pBitmap, FT_Pos x, FT_Pos y) const; + //! Render a FreeType2 monochrome bitmap to a cache canvas. + void render_mono(cached_text* pCacheEntry, FT_Bitmap* pBitmap, FT_Pos x, + FT_Pos y) const; - //! Render a FreeType2 grayscale bitmap to a cache canvas. - void render_gray(cached_text *pCacheEntry, FT_Bitmap* pBitmap, FT_Pos x, FT_Pos y) const; + //! Render a FreeType2 grayscale bitmap to a cache canvas. + void render_gray(cached_text* pCacheEntry, FT_Bitmap* pBitmap, FT_Pos x, + FT_Pos y) const; - static FT_Library freetype_library; - static int freetype_init_count; - static const int cache_size_log2 = 7; - FT_Face font_face; - argb_colour colour; - bool is_done_freetype_init; - mutable cached_text cache[1 << cache_size_log2]; + static FT_Library freetype_library; + static int freetype_init_count; + static const int cache_size_log2 = 7; + FT_Face font_face; + argb_colour colour; + bool is_done_freetype_init; + mutable cached_text cache[1 << cache_size_log2]; - // The following five methods are implemented by the rendering engine. + // The following five methods are implemented by the rendering engine. - //! Query if 1-bit monochrome or 8-bit grayscale rendering should be used. - /*! - @return true if 1-bit monochrome rendering should be used, false if - 8-bit grayscale rendering should be used (though in the latter - case, 1-bit rendering might still get used). - */ - bool is_monochrome() const; + //! Query if 1-bit monochrome or 8-bit grayscale rendering should be used. + /*! + @return true if 1-bit monochrome rendering should be used, false if + 8-bit grayscale rendering should be used (though in the latter + case, 1-bit rendering might still get used). + */ + bool is_monochrome() const; - //! Convert a cache canvas containing rendered text into a texture. - /*! - @param pEventualCanvas A pointer to the rendertarget we'll be using to - draw this. - @param pCacheEntry A cache entry whose pData field points to a pixmap - of size iWidth by iHeight. This method will convert said pixmap to - an object which can be used by the rendering engine, and store the - result in the pTexture or iTexture field. - */ - void make_texture(render_target *pEventualCanvas, cached_text* pCacheEntry) const; + //! Convert a cache canvas containing rendered text into a texture. + /*! + @param pEventualCanvas A pointer to the rendertarget we'll be using to + draw this. + @param pCacheEntry A cache entry whose pData field points to a pixmap + of size iWidth by iHeight. This method will convert said pixmap to + an object which can be used by the rendering engine, and store the + result in the pTexture or iTexture field. + */ + void make_texture(render_target* pEventualCanvas, + cached_text* pCacheEntry) const; - //! Free a previously-made texture of a cache entry. - /*! - This call should free all the resources previously allocated by a call - to _makeTexture() and set the texture field to indicate no texture. + //! Free a previously-made texture of a cache entry. + /*! + This call should free all the resources previously allocated by a call + to _makeTexture() and set the texture field to indicate no texture. - @param pCacheEntry A cache entry previously passed to _makeTexture(). - */ - void free_texture(cached_text* pCacheEntry) const; + @param pCacheEntry A cache entry previously passed to _makeTexture(). + */ + void free_texture(cached_text* pCacheEntry) const; - //! Render a previously-made texture of a cache entry. - /*! - @param pCanvas The canvas on which to draw. - @param pCacheEntry A cache entry containing the texture to draw, which - will have been stored in the pTexture or iTexture field by a prior - call to _makeTexture(). - @param iX The X position at which to draw the texture on the canvas. - @param iY The Y position at which to draw the texture on the canvas. - */ - void draw_texture(render_target* pCanvas, cached_text* pCacheEntry, - int iX, int iY) const; + //! Render a previously-made texture of a cache entry. + /*! + @param pCanvas The canvas on which to draw. + @param pCacheEntry A cache entry containing the texture to draw, which + will have been stored in the pTexture or iTexture field by a prior + call to _makeTexture(). + @param iX The X position at which to draw the texture on the canvas. + @param iY The Y position at which to draw the texture on the canvas. + */ + void draw_texture(render_target* pCanvas, cached_text* pCacheEntry, int iX, + int iY) const; }; -#endif // CORSIX_TH_USE_FREETYPE2 +#endif // CORSIX_TH_USE_FREETYPE2 -#endif // CORSIX_TH_TH_GFX_FONT_H_ +#endif // CORSIX_TH_TH_GFX_FONT_H_ diff --git a/CorsixTH/Src/th_gfx_sdl.cpp b/CorsixTH/Src/th_gfx_sdl.cpp index 7661db23..155e5bae 100644 --- a/CorsixTH/Src/th_gfx_sdl.cpp +++ b/CorsixTH/Src/th_gfx_sdl.cpp @@ -26,18 +26,18 @@ SOFTWARE. #ifdef CORSIX_TH_USE_FREETYPE2 #include "th_gfx_font.h" #endif -#include "th_map.h" -#include -#include -#include -#include #include +#include +#include +#include +#include #include +#include "th_map.h" -full_colour_renderer::full_colour_renderer(int iWidth, int iHeight) : width(iWidth), height(iHeight) -{ - x = 0; - y = 0; +full_colour_renderer::full_colour_renderer(int iWidth, int iHeight) + : width(iWidth), height(iHeight) { + x = 0; + y = 0; } namespace { @@ -50,16 +50,17 @@ namespace { @param iB Blue colour intensity. @return 32bpp colour pixel in grey scale. */ -inline uint32_t makeGreyScale(uint8_t iOpacity, uint8_t iR, uint8_t iG, uint8_t iB) -{ - // http://en.wikipedia.org/wiki/Grayscale#Converting_color_to_grayscale - // 0.2126*R + 0.7152*G + 0.0722*B - // 0.2126 * 65536 = 13932.9536 -> 1393 - // 0.7152 * 65536 = 46871.3472 - // 0.0722 * 65536 = 4731.6992 -> 4732 - // 13933 + 46871 + 4732 = 65536 = 2**16 - uint8_t iGrey = static_cast((13933 * iR + 46871 * iG + 4732 * iB) >> 16); - return palette::pack_argb(iOpacity, iGrey, iGrey, iGrey); +inline uint32_t makeGreyScale(uint8_t iOpacity, uint8_t iR, uint8_t iG, + uint8_t iB) { + // http://en.wikipedia.org/wiki/Grayscale#Converting_color_to_grayscale + // 0.2126*R + 0.7152*G + 0.0722*B + // 0.2126 * 65536 = 13932.9536 -> 1393 + // 0.7152 * 65536 = 46871.3472 + // 0.0722 * 65536 = 4731.6992 -> 4732 + // 13933 + 46871 + 4732 = 65536 = 2**16 + uint8_t iGrey = + static_cast((13933 * iR + 46871 * iG + 4732 * iB) >> 16); + return palette::pack_argb(iOpacity, iGrey, iGrey, iGrey); } //! Convert a colour by swapping red and blue channel. @@ -70,757 +71,670 @@ inline uint32_t makeGreyScale(uint8_t iOpacity, uint8_t iR, uint8_t iG, uint8_t @param iB Blue colour intensity. @return 32bpp colour pixel with red and blue swapped. */ -inline uint32_t makeSwapRedBlue(uint8_t iOpacity, uint8_t iR, uint8_t iG, uint8_t iB) -{ - // http://en.wikipedia.org/wiki/Grayscale#Converting_color_to_grayscale - // The Y factor for red is 0.2126, and for blue 0.0722. This means red is about 3 times stronger than blue. - // Simple swapping channels will thus distort the balance. This code compensates for that by computing - // red = blue * 0.0722 / 0.2126 = blue * 1083 / 3189 - // blue = red * 0.2126 / 0.0722 = red * 1063 / 361 (clipped at max blue, 255) - uint8_t iNewRed = static_cast(iB * 1083 / 3189); - int iNewBlue = iR * 1063 / 361; - if (iNewBlue > 255) - iNewBlue = 255; - return palette::pack_argb(iOpacity, iNewRed, iG, static_cast(iNewBlue)); +inline uint32_t makeSwapRedBlue(uint8_t iOpacity, uint8_t iR, uint8_t iG, + uint8_t iB) { + // http://en.wikipedia.org/wiki/Grayscale#Converting_color_to_grayscale + // The Y factor for red is 0.2126, and for blue 0.0722. This means red is + // about 3 times stronger than blue. Simple swapping channels will thus + // distort the balance. This code compensates for that by computing red = + // blue * 0.0722 / 0.2126 = blue * 1083 / 3189 blue = red * 0.2126 / 0.0722 + // = red * 1063 / 361 (clipped at max blue, 255) + uint8_t iNewRed = static_cast(iB * 1083 / 3189); + int iNewBlue = iR * 1063 / 361; + if (iNewBlue > 255) iNewBlue = 255; + return palette::pack_argb(iOpacity, iNewRed, iG, + static_cast(iNewBlue)); } -uint8_t convert_6bit_to_8bit_colour_component(uint8_t colour_component) -{ - constexpr uint8_t mask_6bit = 0x3F; - return static_cast(((colour_component & mask_6bit) * static_cast(0xFF) / mask_6bit) + 0.5); +uint8_t convert_6bit_to_8bit_colour_component(uint8_t colour_component) { + constexpr uint8_t mask_6bit = 0x3F; + return static_cast( + ((colour_component & mask_6bit) * static_cast(0xFF) / mask_6bit) + + 0.5); } -} // namespace +} // namespace -palette::palette() -{ - colour_count = 0; -} +palette::palette() { colour_count = 0; } -bool palette::load_from_th_file(const uint8_t* pData, size_t iDataLength) -{ - if(iDataLength != 256 * 3) - return false; +bool palette::load_from_th_file(const uint8_t* pData, size_t iDataLength) { + if (iDataLength != 256 * 3) return false; - colour_count = static_cast(iDataLength / 3); - for(int i = 0; i < colour_count; ++i, pData += 3) - { - uint8_t iR = convert_6bit_to_8bit_colour_component(pData[0]); - uint8_t iG = convert_6bit_to_8bit_colour_component(pData[1]); - uint8_t iB = convert_6bit_to_8bit_colour_component(pData[2]); - uint32_t iColour = pack_argb(0xFF, iR, iG, iB); - // Remap magenta to transparent - if(iColour == pack_argb(0xFF, 0xFF, 0x00, 0xFF)) - iColour = pack_argb(0x00, 0x00, 0x00, 0x00); - colour_index_to_argb_map[i] = iColour; - } - - return true; -} - -bool palette::set_entry(int iEntry, uint8_t iR, uint8_t iG, uint8_t iB) -{ - if(iEntry < 0 || iEntry >= colour_count) - return false; + colour_count = static_cast(iDataLength / 3); + for (int i = 0; i < colour_count; ++i, pData += 3) { + uint8_t iR = convert_6bit_to_8bit_colour_component(pData[0]); + uint8_t iG = convert_6bit_to_8bit_colour_component(pData[1]); + uint8_t iB = convert_6bit_to_8bit_colour_component(pData[2]); uint32_t iColour = pack_argb(0xFF, iR, iG, iB); // Remap magenta to transparent - if(iColour == pack_argb(0xFF, 0xFF, 0x00, 0xFF)) - iColour = pack_argb(0x00, 0x00, 0x00, 0x00); - colour_index_to_argb_map[iEntry] = iColour; - return true; + if (iColour == pack_argb(0xFF, 0xFF, 0x00, 0xFF)) + iColour = pack_argb(0x00, 0x00, 0x00, 0x00); + colour_index_to_argb_map[i] = iColour; + } + + return true; } -int palette::get_colour_count() const -{ - return colour_count; +bool palette::set_entry(int iEntry, uint8_t iR, uint8_t iG, uint8_t iB) { + if (iEntry < 0 || iEntry >= colour_count) return false; + uint32_t iColour = pack_argb(0xFF, iR, iG, iB); + // Remap magenta to transparent + if (iColour == pack_argb(0xFF, 0xFF, 0x00, 0xFF)) + iColour = pack_argb(0x00, 0x00, 0x00, 0x00); + colour_index_to_argb_map[iEntry] = iColour; + return true; } -const uint32_t* palette::get_argb_data() const -{ - return colour_index_to_argb_map; +int palette::get_colour_count() const { return colour_count; } + +const uint32_t* palette::get_argb_data() const { + return colour_index_to_argb_map; } -void full_colour_renderer::decode_image(const uint8_t* pImg, const palette *pPalette, uint32_t iSpriteFlags) -{ - if (width <= 0) { - throw std::logic_error("width cannot be <= 0 when decoding an image"); - } - if (height <= 0) { - throw std::logic_error("height cannot be <= 0 when decoding an image"); - } +void full_colour_renderer::decode_image(const uint8_t* pImg, + const palette* pPalette, + uint32_t iSpriteFlags) { + if (width <= 0) { + throw std::logic_error("width cannot be <= 0 when decoding an image"); + } + if (height <= 0) { + throw std::logic_error("height cannot be <= 0 when decoding an image"); + } - iSpriteFlags &= thdf_alt32_mask; + iSpriteFlags &= thdf_alt32_mask; - const uint32_t* pColours = pPalette->get_argb_data(); - for (;;) { - uint8_t iType = *pImg++; - size_t iLength = iType & 63; - switch (iType >> 6) - { - case 0: // Fixed fully opaque 32bpp pixels - while (iLength > 0) - { - uint32_t iColour; - if (iSpriteFlags == thdf_alt32_blue_red_swap) - iColour = makeSwapRedBlue(0xFF, pImg[0], pImg[1], pImg[2]); - else if (iSpriteFlags == thdf_alt32_grey_scale) - iColour = makeGreyScale(0xFF, pImg[0], pImg[1], pImg[2]); - else - iColour = palette::pack_argb(0xFF, pImg[0], pImg[1], pImg[2]); - push_pixel(iColour); - pImg += 3; - iLength--; - } - break; - - case 1: // Fixed partially transparent 32bpp pixels - { - uint8_t iOpacity = *pImg++; - while (iLength > 0) - { - uint32_t iColour; - if (iSpriteFlags == thdf_alt32_blue_red_swap) - iColour = makeSwapRedBlue(0xFF, pImg[0], pImg[1], pImg[2]); - else if (iSpriteFlags == thdf_alt32_grey_scale) - iColour = makeGreyScale(iOpacity, pImg[0], pImg[1], pImg[2]); - else - iColour = palette::pack_argb(iOpacity, pImg[0], pImg[1], pImg[2]); - push_pixel(iColour); - pImg += 3; - iLength--; - } - break; - } - - case 2: // Fixed fully transparent pixels - { - static const uint32_t iTransparent = palette::pack_argb(0, 0, 0, 0); - while (iLength > 0) - { - push_pixel(iTransparent); - iLength--; - } - break; - } - - case 3: // Recolour layer - { - uint8_t iTable = *pImg++; - pImg++; // Skip reading the opacity for now. - if (iTable == 0xFF) - { - // Legacy sprite data. Use the palette to recolour the layer. - // Note that the iOpacity is ignored here. - while (iLength > 0) - { - push_pixel(pColours[*pImg++]); - iLength--; - } - } - else - { - // TODO: Add proper recolour layers, where RGB comes from - // table 'iTable' at index *pImg (iLength times), and - // opacity comes from the byte after the iTable byte. - // - // For now just draw black pixels, so it won't go unnoticed. - while (iLength > 0) - { - uint32_t iColour = palette::pack_argb(0xFF, 0, 0, 0); - push_pixel(iColour); - iLength--; - } - } - break; - } + const uint32_t* pColours = pPalette->get_argb_data(); + for (;;) { + uint8_t iType = *pImg++; + size_t iLength = iType & 63; + switch (iType >> 6) { + case 0: // Fixed fully opaque 32bpp pixels + while (iLength > 0) { + uint32_t iColour; + if (iSpriteFlags == thdf_alt32_blue_red_swap) + iColour = makeSwapRedBlue(0xFF, pImg[0], pImg[1], pImg[2]); + else if (iSpriteFlags == thdf_alt32_grey_scale) + iColour = makeGreyScale(0xFF, pImg[0], pImg[1], pImg[2]); + else + iColour = palette::pack_argb(0xFF, pImg[0], pImg[1], pImg[2]); + push_pixel(iColour); + pImg += 3; + iLength--; } + break; - if (y >= height) - break; - } - if (y != height || x != 0) { - throw std::logic_error("Image data does not match given dimensions"); + case 1: // Fixed partially transparent 32bpp pixels + { + uint8_t iOpacity = *pImg++; + while (iLength > 0) { + uint32_t iColour; + if (iSpriteFlags == thdf_alt32_blue_red_swap) + iColour = makeSwapRedBlue(0xFF, pImg[0], pImg[1], pImg[2]); + else if (iSpriteFlags == thdf_alt32_grey_scale) + iColour = makeGreyScale(iOpacity, pImg[0], pImg[1], pImg[2]); + else + iColour = palette::pack_argb(iOpacity, pImg[0], pImg[1], pImg[2]); + push_pixel(iColour); + pImg += 3; + iLength--; + } + break; + } + + case 2: // Fixed fully transparent pixels + { + static const uint32_t iTransparent = palette::pack_argb(0, 0, 0, 0); + while (iLength > 0) { + push_pixel(iTransparent); + iLength--; + } + break; + } + + case 3: // Recolour layer + { + uint8_t iTable = *pImg++; + pImg++; // Skip reading the opacity for now. + if (iTable == 0xFF) { + // Legacy sprite data. Use the palette to recolour the + // layer. Note that the iOpacity is ignored here. + while (iLength > 0) { + push_pixel(pColours[*pImg++]); + iLength--; + } + } else { + // TODO: Add proper recolour layers, where RGB comes from + // table 'iTable' at index *pImg (iLength times), and + // opacity comes from the byte after the iTable byte. + // + // For now just draw black pixels, so it won't go unnoticed. + while (iLength > 0) { + uint32_t iColour = palette::pack_argb(0xFF, 0, 0, 0); + push_pixel(iColour); + iLength--; + } + } + break; + } } + + if (y >= height) break; + } + if (y != height || x != 0) { + throw std::logic_error("Image data does not match given dimensions"); + } } -full_colour_storing::full_colour_storing(uint32_t *pDest, int iWidth, int iHeight) : full_colour_renderer(iWidth, iHeight) -{ - destination = pDest; +full_colour_storing::full_colour_storing(uint32_t* pDest, int iWidth, + int iHeight) + : full_colour_renderer(iWidth, iHeight) { + destination = pDest; } -void full_colour_storing::store_argb(uint32_t pixel) -{ - *destination++ = pixel; +void full_colour_storing::store_argb(uint32_t pixel) { *destination++ = pixel; } + +wx_storing::wx_storing(uint8_t* pRGBData, uint8_t* pAData, int iWidth, + int iHeight) + : full_colour_renderer(iWidth, iHeight) { + rgb_data = pRGBData; + alpha_data = pAData; } -wx_storing::wx_storing(uint8_t* pRGBData, uint8_t* pAData, int iWidth, int iHeight) : full_colour_renderer(iWidth, iHeight) -{ - rgb_data = pRGBData; - alpha_data = pAData; +void wx_storing::store_argb(uint32_t pixel) { + rgb_data[0] = palette::get_red(pixel); + rgb_data[1] = palette::get_green(pixel); + rgb_data[2] = palette::get_blue(pixel); + rgb_data += 3; + + *alpha_data++ = palette::get_alpha(pixel); } -void wx_storing::store_argb(uint32_t pixel) -{ - rgb_data[0] = palette::get_red(pixel); - rgb_data[1] = palette::get_green(pixel); - rgb_data[2] = palette::get_blue(pixel); - rgb_data += 3; - - *alpha_data++ = palette::get_alpha(pixel); +render_target::render_target() { + window = nullptr; + renderer = nullptr; + pixel_format = nullptr; + game_cursor = nullptr; + zoom_texture = nullptr; + scale_bitmaps = false; + blue_filter_active = false; + apply_opengl_clip_fix = false; + width = -1; + height = -1; } -render_target::render_target() -{ - window = nullptr; - renderer = nullptr; +render_target::~render_target() { destroy(); } + +bool render_target::create(const render_target_creation_params* pParams) { + if (renderer != nullptr) return false; + + SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear"); + pixel_format = SDL_AllocFormat(SDL_PIXELFORMAT_ABGR8888); + window = + SDL_CreateWindow("CorsixTH", SDL_WINDOWPOS_UNDEFINED, + SDL_WINDOWPOS_UNDEFINED, pParams->width, pParams->height, + SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE); + if (!window) { + return false; + } + + Uint32 iRendererFlags = + (pParams->present_immediate ? 0 : SDL_RENDERER_PRESENTVSYNC); + renderer = SDL_CreateRenderer(window, -1, iRendererFlags); + + SDL_RendererInfo info; + SDL_GetRendererInfo(renderer, &info); + supports_target_textures = (info.flags & SDL_RENDERER_TARGETTEXTURE) != 0; + + SDL_version sdlVersion; + SDL_GetVersion(&sdlVersion); + apply_opengl_clip_fix = std::strncmp(info.name, "opengl", 6) == 0 && + sdlVersion.major == 2 && sdlVersion.minor == 0 && + sdlVersion.patch < 4; + + return update(pParams); +} + +bool render_target::update(const render_target_creation_params* pParams) { + if (window == nullptr) { + return false; + } + + bool bUpdateSize = (width != pParams->width) || (height != pParams->height); + width = pParams->width; + height = pParams->height; + + bool bIsFullscreen = + ((SDL_GetWindowFlags(window) & SDL_WINDOW_FULLSCREEN_DESKTOP) == + SDL_WINDOW_FULLSCREEN_DESKTOP); + if (bIsFullscreen != pParams->fullscreen) { + SDL_SetWindowFullscreen( + window, (pParams->fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0)); + } + + if (bUpdateSize || bIsFullscreen != pParams->fullscreen) { + SDL_SetWindowSize(window, width, height); + } + + if (bUpdateSize) { + SDL_RenderSetLogicalSize(renderer, width, height); + } + + return true; +} + +void render_target::destroy() { + if (pixel_format) { + SDL_FreeFormat(pixel_format); pixel_format = nullptr; - game_cursor = nullptr; + } + + if (zoom_texture) { + SDL_DestroyTexture(zoom_texture); zoom_texture = nullptr; - scale_bitmaps = false; - blue_filter_active = false; - apply_opengl_clip_fix = false; - width = -1; - height = -1; + } + + if (renderer) { + SDL_DestroyRenderer(renderer); + renderer = nullptr; + } + + if (window) { + SDL_DestroyWindow(window); + window = nullptr; + } } -render_target::~render_target() -{ - destroy(); -} +bool render_target::set_scale_factor(double fScale, scaled_items eWhatToScale) { + flush_zoom_buffer(); + scale_bitmaps = false; -bool render_target::create(const render_target_creation_params* pParams) -{ - if (renderer != nullptr) - return false; + if (fScale <= 0.000) { + return false; + } else if (eWhatToScale == scaled_items::all && supports_target_textures) { + // Draw everything from now until the next scale to zoom_texture + // with the appropriate virtual size, which will be copied scaled to + // fit the window. + int virtWidth = static_cast(width / fScale); + int virtHeight = static_cast(height / fScale); - SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear"); - pixel_format = SDL_AllocFormat(SDL_PIXELFORMAT_ABGR8888); - window = SDL_CreateWindow("CorsixTH", - SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, - pParams->width, pParams->height, - SDL_WINDOW_OPENGL|SDL_WINDOW_RESIZABLE); - if (!window) - { - return false; + zoom_texture = + SDL_CreateTexture(renderer, SDL_PIXELFORMAT_ABGR8888, + SDL_TEXTUREACCESS_TARGET, virtWidth, virtHeight); + + SDL_RenderSetLogicalSize(renderer, virtWidth, virtHeight); + if (SDL_SetRenderTarget(renderer, zoom_texture) != 0) { + std::cout << "Warning: Could not render to zoom texture - " + << SDL_GetError() << std::endl; + + SDL_RenderSetLogicalSize(renderer, width, height); + SDL_DestroyTexture(zoom_texture); + zoom_texture = nullptr; + return false; } - Uint32 iRendererFlags = (pParams->present_immediate ? 0 : SDL_RENDERER_PRESENTVSYNC); - renderer = SDL_CreateRenderer(window, -1, iRendererFlags); - - SDL_RendererInfo info; - SDL_GetRendererInfo(renderer, &info); - supports_target_textures = (info.flags & SDL_RENDERER_TARGETTEXTURE) != 0; - - SDL_version sdlVersion; - SDL_GetVersion(&sdlVersion); - apply_opengl_clip_fix = std::strncmp(info.name, "opengl", 6) == 0 && sdlVersion.major == 2 && sdlVersion.minor == 0 && sdlVersion.patch < 4; - - return update(pParams); -} - -bool render_target::update(const render_target_creation_params* pParams) -{ - if (window == nullptr) - { - return false; - } - - bool bUpdateSize = (width != pParams->width) || (height != pParams->height); - width = pParams->width; - height = pParams->height; - - bool bIsFullscreen = ((SDL_GetWindowFlags(window) & SDL_WINDOW_FULLSCREEN_DESKTOP) == SDL_WINDOW_FULLSCREEN_DESKTOP); - if (bIsFullscreen != pParams->fullscreen) - { - SDL_SetWindowFullscreen(window, (pParams->fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0)); - } - - if (bUpdateSize || bIsFullscreen != pParams->fullscreen) - { - SDL_SetWindowSize(window, width, height); - } - - if (bUpdateSize) - { - SDL_RenderSetLogicalSize(renderer, width, height); - } - - return true; -} - -void render_target::destroy() -{ - if (pixel_format) - { - SDL_FreeFormat(pixel_format); - pixel_format = nullptr; - } - - if (zoom_texture) - { - SDL_DestroyTexture(zoom_texture); - zoom_texture = nullptr; - } - - if (renderer) - { - SDL_DestroyRenderer(renderer); - renderer = nullptr; - } - - if (window) - { - SDL_DestroyWindow(window); - window = nullptr; - } -} - -bool render_target::set_scale_factor(double fScale, scaled_items eWhatToScale) -{ - flush_zoom_buffer(); - scale_bitmaps = false; - - if(fScale <= 0.000) - { - return false; - } - else if(eWhatToScale == scaled_items::all && supports_target_textures) - { - //Draw everything from now until the next scale to zoom_texture - //with the appropriate virtual size, which will be copied scaled to - //fit the window. - int virtWidth = static_cast(width / fScale); - int virtHeight = static_cast(height / fScale); - - zoom_texture = SDL_CreateTexture(renderer, - SDL_PIXELFORMAT_ABGR8888, - SDL_TEXTUREACCESS_TARGET, - virtWidth, - virtHeight - ); - - SDL_RenderSetLogicalSize(renderer, virtWidth, virtHeight); - if(SDL_SetRenderTarget(renderer, zoom_texture) != 0) - { - std::cout << "Warning: Could not render to zoom texture - " << SDL_GetError() << std::endl; - - SDL_RenderSetLogicalSize(renderer, width, height); - SDL_DestroyTexture(zoom_texture); - zoom_texture = nullptr; - return false; - } - - // Clear the new texture to transparent/black. - SDL_SetRenderDrawColor(renderer, 0, 0, 0, SDL_ALPHA_TRANSPARENT); - SDL_RenderClear(renderer); - - return true; - } - else if(0.999 <= fScale && fScale <= 1.001) - { - return true; - } - else if(eWhatToScale == scaled_items::bitmaps) - { - scale_bitmaps = true; - bitmap_scale_factor = fScale; - - return true; - } - else - { - return false; - } -} - -void render_target::set_caption(const char* sCaption) -{ - SDL_SetWindowTitle(window, sCaption); -} - -const char *render_target::get_renderer_details() const -{ - SDL_RendererInfo info = {}; - SDL_GetRendererInfo(renderer, &info); - return info.name; -} - -const char* render_target::get_last_error() -{ - return SDL_GetError(); -} - -bool render_target::start_frame() -{ - fill_black(); - return true; -} - -bool render_target::end_frame() -{ - flush_zoom_buffer(); - - // End the frame by adding the cursor and possibly a filter. - if(game_cursor) - { - game_cursor->draw(this, cursor_x, cursor_y); - } - if(blue_filter_active) - { - SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND); - SDL_SetRenderDrawColor(renderer, 51, 51, 255, 128); // r=0.2, g=0.2, b=1, a=0.5 . - SDL_RenderFillRect(renderer, nullptr); - } - - SDL_RenderPresent(renderer); - return true; -} - -bool render_target::fill_black() -{ - SDL_SetRenderDrawColor(renderer, 0, 0, 0, SDL_ALPHA_OPAQUE); + // Clear the new texture to transparent/black. + SDL_SetRenderDrawColor(renderer, 0, 0, 0, SDL_ALPHA_TRANSPARENT); SDL_RenderClear(renderer); return true; + } else if (0.999 <= fScale && fScale <= 1.001) { + return true; + } else if (eWhatToScale == scaled_items::bitmaps) { + scale_bitmaps = true; + bitmap_scale_factor = fScale; + + return true; + } else { + return false; + } } -void render_target::set_blue_filter_active(bool bActivate) -{ - blue_filter_active = bActivate; +void render_target::set_caption(const char* sCaption) { + SDL_SetWindowTitle(window, sCaption); +} + +const char* render_target::get_renderer_details() const { + SDL_RendererInfo info = {}; + SDL_GetRendererInfo(renderer, &info); + return info.name; +} + +const char* render_target::get_last_error() { return SDL_GetError(); } + +bool render_target::start_frame() { + fill_black(); + return true; +} + +bool render_target::end_frame() { + flush_zoom_buffer(); + + // End the frame by adding the cursor and possibly a filter. + if (game_cursor) { + game_cursor->draw(this, cursor_x, cursor_y); + } + if (blue_filter_active) { + SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND); + SDL_SetRenderDrawColor(renderer, 51, 51, 255, + 128); // r=0.2, g=0.2, b=1, a=0.5 . + SDL_RenderFillRect(renderer, nullptr); + } + + SDL_RenderPresent(renderer); + return true; +} + +bool render_target::fill_black() { + SDL_SetRenderDrawColor(renderer, 0, 0, 0, SDL_ALPHA_OPAQUE); + SDL_RenderClear(renderer); + + return true; +} + +void render_target::set_blue_filter_active(bool bActivate) { + blue_filter_active = bActivate; } // Actiate and Deactivate SDL function to capture mouse to window -void render_target::set_window_grab(bool bActivate) -{ - SDL_SetWindowGrab(window, bActivate ? SDL_TRUE : SDL_FALSE); +void render_target::set_window_grab(bool bActivate) { + SDL_SetWindowGrab(window, bActivate ? SDL_TRUE : SDL_FALSE); } -bool render_target::fill_rect(uint32_t iColour, int iX, int iY, int iW, int iH) -{ - SDL_Rect rcDest = { iX, iY, iW, iH }; +bool render_target::fill_rect(uint32_t iColour, int iX, int iY, int iW, + int iH) { + SDL_Rect rcDest = {iX, iY, iW, iH}; - Uint8 r, g, b, a; - SDL_GetRGBA(iColour, pixel_format, &r, &g, &b, &a); + Uint8 r, g, b, a; + SDL_GetRGBA(iColour, pixel_format, &r, &g, &b, &a); - SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND); - SDL_SetRenderDrawColor(renderer, r, g, b, a); - SDL_RenderFillRect(renderer, &rcDest); + SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND); + SDL_SetRenderDrawColor(renderer, r, g, b, a); + SDL_RenderFillRect(renderer, &rcDest); - return true; + return true; } -void render_target::get_clip_rect(clip_rect* pRect) const -{ - SDL_RenderGetClipRect(renderer, reinterpret_cast(pRect)); - // SDL returns empty rect when clipping is disabled -> return full rect for CTH - if (SDL_RectEmpty(pRect)) - { - pRect->x = pRect->y = 0; - pRect->w = width; - pRect->h = height; - } +void render_target::get_clip_rect(clip_rect* pRect) const { + SDL_RenderGetClipRect(renderer, reinterpret_cast(pRect)); + // SDL returns empty rect when clipping is disabled -> return full rect for + // CTH + if (SDL_RectEmpty(pRect)) { + pRect->x = pRect->y = 0; + pRect->w = width; + pRect->h = height; + } - if(apply_opengl_clip_fix) - { - int renderWidth, renderHeight; - SDL_RenderGetLogicalSize(renderer, &renderWidth, &renderHeight); - pRect->y = renderHeight - pRect->y - pRect->h; - } + if (apply_opengl_clip_fix) { + int renderWidth, renderHeight; + SDL_RenderGetLogicalSize(renderer, &renderWidth, &renderHeight); + pRect->y = renderHeight - pRect->y - pRect->h; + } } -void render_target::set_clip_rect(const clip_rect* pRect) -{ - // Full clip rect for CTH means clipping disabled - if (pRect == nullptr || (pRect->w == width && pRect->h == height)) - { - SDL_RenderSetClipRect(renderer, nullptr); - return; - } +void render_target::set_clip_rect(const clip_rect* pRect) { + // Full clip rect for CTH means clipping disabled + if (pRect == nullptr || (pRect->w == width && pRect->h == height)) { + SDL_RenderSetClipRect(renderer, nullptr); + return; + } - SDL_Rect SDLRect = { - pRect->x, - pRect->y, - pRect->w, - pRect->h - }; + SDL_Rect SDLRect = {pRect->x, pRect->y, pRect->w, pRect->h}; - // For some reason, SDL treats an empty rect (h or w <= 0) as if you turned - // off clipping, so we replace it with a rect that's outside our viewport. - const SDL_Rect rcBogus = { -2, -2, 1, 1 }; - if (SDL_RectEmpty(&SDLRect)) - { - SDLRect = rcBogus; - } + // For some reason, SDL treats an empty rect (h or w <= 0) as if you turned + // off clipping, so we replace it with a rect that's outside our viewport. + const SDL_Rect rcBogus = {-2, -2, 1, 1}; + if (SDL_RectEmpty(&SDLRect)) { + SDLRect = rcBogus; + } - if(apply_opengl_clip_fix) - { - int renderWidth, renderHeight; - SDL_RenderGetLogicalSize(renderer, &renderWidth, &renderHeight); - SDLRect.y = renderHeight - SDLRect.y - SDLRect.h; - } + if (apply_opengl_clip_fix) { + int renderWidth, renderHeight; + SDL_RenderGetLogicalSize(renderer, &renderWidth, &renderHeight); + SDLRect.y = renderHeight - SDLRect.y - SDLRect.h; + } - SDL_RenderSetClipRect(renderer, &SDLRect); + SDL_RenderSetClipRect(renderer, &SDLRect); } -int render_target::get_width() const -{ - int w; - SDL_RenderGetLogicalSize(renderer, &w, nullptr); - return w; +int render_target::get_width() const { + int w; + SDL_RenderGetLogicalSize(renderer, &w, nullptr); + return w; } -int render_target::get_height() const -{ - int h; - SDL_RenderGetLogicalSize(renderer, nullptr, &h); - return h; +int render_target::get_height() const { + int h; + SDL_RenderGetLogicalSize(renderer, nullptr, &h); + return h; } -void render_target::start_nonoverlapping_draws() -{ - // SDL has no optimisations for drawing lots of non-overlapping sprites +void render_target::start_nonoverlapping_draws() { + // SDL has no optimisations for drawing lots of non-overlapping sprites } -void render_target::finish_nonoverlapping_draws() -{ - // SDL has no optimisations for drawing lots of non-overlapping sprites +void render_target::finish_nonoverlapping_draws() { + // SDL has no optimisations for drawing lots of non-overlapping sprites } -void render_target::set_cursor(cursor* pCursor) -{ - game_cursor = pCursor; +void render_target::set_cursor(cursor* pCursor) { game_cursor = pCursor; } + +void render_target::set_cursor_position(int iX, int iY) { + cursor_x = iX; + cursor_y = iY; } -void render_target::set_cursor_position(int iX, int iY) -{ - cursor_x = iX; - cursor_y = iY; +bool render_target::take_screenshot(const char* sFile) { + int width = 0, height = 0; + if (SDL_GetRendererOutputSize(renderer, &width, &height) == -1) return false; + + // Create a window-sized surface, RGB format (0 Rmask means RGB.) + SDL_Surface* pRgbSurface = + SDL_CreateRGBSurface(0, width, height, 24, 0, 0, 0, 0); + if (pRgbSurface == nullptr) return false; + + int readStatus = -1; + if (SDL_LockSurface(pRgbSurface) != -1) { + // Ask the renderer to (slowly) fill the surface with renderer + // output data. + readStatus = + SDL_RenderReadPixels(renderer, nullptr, pRgbSurface->format->format, + pRgbSurface->pixels, pRgbSurface->pitch); + SDL_UnlockSurface(pRgbSurface); + + if (readStatus != -1) SDL_SaveBMP(pRgbSurface, sFile); + } + + SDL_FreeSurface(pRgbSurface); + + return (readStatus != -1); } -bool render_target::take_screenshot(const char* sFile) -{ - int width = 0, height = 0; - if (SDL_GetRendererOutputSize(renderer, &width, &height) == -1) - return false; - - // Create a window-sized surface, RGB format (0 Rmask means RGB.) - SDL_Surface* pRgbSurface = SDL_CreateRGBSurface(0, width, height, 24, 0, 0, 0, 0); - if (pRgbSurface == nullptr) - return false; - - int readStatus = -1; - if (SDL_LockSurface(pRgbSurface) != -1) - { - // Ask the renderer to (slowly) fill the surface with renderer - // output data. - readStatus = SDL_RenderReadPixels(renderer, - nullptr, - pRgbSurface->format->format, - pRgbSurface->pixels, - pRgbSurface->pitch); - SDL_UnlockSurface(pRgbSurface); - - if (readStatus != -1) - SDL_SaveBMP(pRgbSurface, sFile); - } - - SDL_FreeSurface(pRgbSurface); - - return (readStatus != -1); +bool render_target::should_scale_bitmaps(double* pFactor) { + if (!scale_bitmaps) return false; + if (pFactor) *pFactor = bitmap_scale_factor; + return true; } +void render_target::flush_zoom_buffer() { + if (zoom_texture == nullptr) { + return; + } -bool render_target::should_scale_bitmaps(double* pFactor) -{ - if(!scale_bitmaps) - return false; - if(pFactor) - *pFactor = bitmap_scale_factor; - return true; -} - -void render_target::flush_zoom_buffer() -{ - if(zoom_texture == nullptr) { return; } - - SDL_SetRenderTarget(renderer, nullptr); - SDL_RenderSetLogicalSize(renderer, width, height); - SDL_SetTextureBlendMode(zoom_texture, SDL_BLENDMODE_BLEND); - SDL_RenderCopy(renderer, zoom_texture, nullptr, nullptr); - SDL_DestroyTexture(zoom_texture); - zoom_texture = nullptr; + SDL_SetRenderTarget(renderer, nullptr); + SDL_RenderSetLogicalSize(renderer, width, height); + SDL_SetTextureBlendMode(zoom_texture, SDL_BLENDMODE_BLEND); + SDL_RenderCopy(renderer, zoom_texture, nullptr, nullptr); + SDL_DestroyTexture(zoom_texture); + zoom_texture = nullptr; } namespace { -//! Convert legacy 8bpp sprite data to recoloured 32bpp data, using special recolour table 0xFF. +//! Convert legacy 8bpp sprite data to recoloured 32bpp data, using special +//! recolour table 0xFF. /*! @param pPixelData Legacy 8bpp pixels. @param iPixelDataLength Number of pixels in the \a pPixelData. - @return Converted 32bpp pixel data, if succeeded else nullptr is returned. Caller should free the returned memory. + @return Converted 32bpp pixel data, if succeeded else nullptr is returned. + Caller should free the returned memory. */ -uint8_t *convertLegacySprite(const uint8_t* pPixelData, size_t iPixelDataLength) -{ - // Recolour blocks are 63 pixels long. - // XXX To reduce the size of the 32bpp data, transparent pixels can be stored more compactly. - size_t iNumFilled = iPixelDataLength / 63; - size_t iRemaining = iPixelDataLength - iNumFilled * 63; - size_t iNewSize = iNumFilled * (3 + 63) + ((iRemaining > 0) ? 3 + iRemaining : 0); - uint8_t *pData = new uint8_t[iNewSize]; +uint8_t* convertLegacySprite(const uint8_t* pPixelData, + size_t iPixelDataLength) { + // Recolour blocks are 63 pixels long. + // XXX To reduce the size of the 32bpp data, transparent pixels can be + // stored more compactly. + size_t iNumFilled = iPixelDataLength / 63; + size_t iRemaining = iPixelDataLength - iNumFilled * 63; + size_t iNewSize = + iNumFilled * (3 + 63) + ((iRemaining > 0) ? 3 + iRemaining : 0); + uint8_t* pData = new uint8_t[iNewSize]; - uint8_t *pDest = pData; - while (iPixelDataLength > 0) - { - size_t iLength = (iPixelDataLength >= 63) ? 63 : iPixelDataLength; - *pDest++ = static_cast(iLength + 0xC0); // Recolour layer type of block. - *pDest++ = 0xFF; // Use special table 0xFF (which uses the palette as table). - *pDest++ = 0xFF; // Non-transparent. - std::memcpy(pDest, pPixelData, iLength); - pDest += iLength; - pPixelData += iLength; - iPixelDataLength -= iLength; - } - return pData; + uint8_t* pDest = pData; + while (iPixelDataLength > 0) { + size_t iLength = (iPixelDataLength >= 63) ? 63 : iPixelDataLength; + *pDest++ = + static_cast(iLength + 0xC0); // Recolour layer type of block. + *pDest++ = 0xFF; // Use special table 0xFF (which uses the palette as + // table). + *pDest++ = 0xFF; // Non-transparent. + std::memcpy(pDest, pPixelData, iLength); + pDest += iLength; + pPixelData += iLength; + iPixelDataLength -= iLength; + } + return pData; } -} // namespace +} // namespace SDL_Texture* render_target::create_palettized_texture( - int iWidth, int iHeight, const uint8_t* pPixels, - const palette* pPalette, uint32_t iSpriteFlags) const -{ - uint32_t *pARGBPixels = new uint32_t[iWidth * iHeight]; + int iWidth, int iHeight, const uint8_t* pPixels, const palette* pPalette, + uint32_t iSpriteFlags) const { + uint32_t* pARGBPixels = new uint32_t[iWidth * iHeight]; - full_colour_storing oRenderer(pARGBPixels, iWidth, iHeight); - oRenderer.decode_image(pPixels, pPalette, iSpriteFlags); + full_colour_storing oRenderer(pARGBPixels, iWidth, iHeight); + oRenderer.decode_image(pPixels, pPalette, iSpriteFlags); - SDL_Texture *pTexture = create_texture(iWidth, iHeight, pARGBPixels); - delete [] pARGBPixels; - return pTexture; + SDL_Texture* pTexture = create_texture(iWidth, iHeight, pARGBPixels); + delete[] pARGBPixels; + return pTexture; } SDL_Texture* render_target::create_texture(int iWidth, int iHeight, - const uint32_t* pPixels) const -{ - SDL_Texture *pTexture = SDL_CreateTexture(renderer, pixel_format->format, SDL_TEXTUREACCESS_STATIC, iWidth, iHeight); + const uint32_t* pPixels) const { + SDL_Texture* pTexture = + SDL_CreateTexture(renderer, pixel_format->format, + SDL_TEXTUREACCESS_STATIC, iWidth, iHeight); - if (pTexture == nullptr) { - throw std::runtime_error(SDL_GetError()); - } + if (pTexture == nullptr) { + throw std::runtime_error(SDL_GetError()); + } - int err = 0; - err = SDL_UpdateTexture(pTexture, nullptr, pPixels, static_cast(sizeof(*pPixels) * iWidth)); - if (err < 0) { - throw std::runtime_error(SDL_GetError()); - } + int err = 0; + err = SDL_UpdateTexture(pTexture, nullptr, pPixels, + static_cast(sizeof(*pPixels) * iWidth)); + if (err < 0) { + throw std::runtime_error(SDL_GetError()); + } - err = SDL_SetTextureBlendMode(pTexture, SDL_BLENDMODE_BLEND); - if (err < 0) { - throw std::runtime_error(SDL_GetError()); - } + err = SDL_SetTextureBlendMode(pTexture, SDL_BLENDMODE_BLEND); + if (err < 0) { + throw std::runtime_error(SDL_GetError()); + } - err = SDL_SetTextureColorMod(pTexture, 0xFF, 0xFF, 0xFF); - if (err < 0) { - throw std::runtime_error(SDL_GetError()); - } + err = SDL_SetTextureColorMod(pTexture, 0xFF, 0xFF, 0xFF); + if (err < 0) { + throw std::runtime_error(SDL_GetError()); + } - err = SDL_SetTextureAlphaMod(pTexture, 0xFF); - if (err < 0) { - throw std::runtime_error(SDL_GetError()); - } + err = SDL_SetTextureAlphaMod(pTexture, 0xFF); + if (err < 0) { + throw std::runtime_error(SDL_GetError()); + } - return pTexture; + return pTexture; } -void render_target::draw(SDL_Texture *pTexture, const SDL_Rect *prcSrcRect, const SDL_Rect *prcDstRect, int iFlags) -{ - SDL_SetTextureAlphaMod(pTexture, 0xFF); - if (iFlags & thdf_alpha_50) - { - SDL_SetTextureAlphaMod(pTexture, 0x80); - } - else if (iFlags & thdf_alpha_75) - { - SDL_SetTextureAlphaMod(pTexture, 0x40); - } +void render_target::draw(SDL_Texture* pTexture, const SDL_Rect* prcSrcRect, + const SDL_Rect* prcDstRect, int iFlags) { + SDL_SetTextureAlphaMod(pTexture, 0xFF); + if (iFlags & thdf_alpha_50) { + SDL_SetTextureAlphaMod(pTexture, 0x80); + } else if (iFlags & thdf_alpha_75) { + SDL_SetTextureAlphaMod(pTexture, 0x40); + } - int iSDLFlip = SDL_FLIP_NONE; - if(iFlags & thdf_flip_horizontal) - iSDLFlip |= SDL_FLIP_HORIZONTAL; - if (iFlags & thdf_flip_vertical) - iSDLFlip |= SDL_FLIP_VERTICAL; + int iSDLFlip = SDL_FLIP_NONE; + if (iFlags & thdf_flip_horizontal) iSDLFlip |= SDL_FLIP_HORIZONTAL; + if (iFlags & thdf_flip_vertical) iSDLFlip |= SDL_FLIP_VERTICAL; - if (iSDLFlip != 0) { - SDL_RenderCopyEx(renderer, pTexture, prcSrcRect, prcDstRect, 0, nullptr, (SDL_RendererFlip)iSDLFlip); - } else { - SDL_RenderCopy(renderer, pTexture, prcSrcRect, prcDstRect); - } + if (iSDLFlip != 0) { + SDL_RenderCopyEx(renderer, pTexture, prcSrcRect, prcDstRect, 0, nullptr, + (SDL_RendererFlip)iSDLFlip); + } else { + SDL_RenderCopy(renderer, pTexture, prcSrcRect, prcDstRect); + } } +void render_target::draw_line(line* pLine, int iX, int iY) { + SDL_SetRenderDrawColor(renderer, pLine->red, pLine->green, pLine->blue, + pLine->alpha); -void render_target::draw_line(line *pLine, int iX, int iY) -{ - SDL_SetRenderDrawColor(renderer, pLine->red, pLine->green, pLine->blue, pLine->alpha); + double lastX, lastY; + lastX = pLine->first_operation->x; + lastY = pLine->first_operation->y; - double lastX, lastY; - lastX = pLine->first_operation->x; - lastY = pLine->first_operation->y; - - line::line_operation* op = (line::line_operation*)(pLine->first_operation->next); - while (op) { - if (op->type == line::line_operation_type::line) { - SDL_RenderDrawLine(renderer, static_cast(lastX + iX), - static_cast(lastY + iY), - static_cast(op->x + iX), - static_cast(op->y + iY)); - } - - lastX = op->x; - lastY = op->y; - - op = (line::line_operation*)(op->next); + line::line_operation* op = + (line::line_operation*)(pLine->first_operation->next); + while (op) { + if (op->type == line::line_operation_type::line) { + SDL_RenderDrawLine( + renderer, static_cast(lastX + iX), static_cast(lastY + iY), + static_cast(op->x + iX), static_cast(op->y + iY)); } + + lastX = op->x; + lastY = op->y; + + op = (line::line_operation*)(op->next); + } } -raw_bitmap::raw_bitmap() -{ - texture = nullptr; - bitmap_palette = nullptr; - target = nullptr; - width = 0; - height = 0; +raw_bitmap::raw_bitmap() { + texture = nullptr; + bitmap_palette = nullptr; + target = nullptr; + width = 0; + height = 0; } -raw_bitmap::~raw_bitmap() -{ - if (texture) - { - SDL_DestroyTexture(texture); - } +raw_bitmap::~raw_bitmap() { + if (texture) { + SDL_DestroyTexture(texture); + } } -void raw_bitmap::set_palette(const palette* pPalette) -{ - bitmap_palette = pPalette; +void raw_bitmap::set_palette(const palette* pPalette) { + bitmap_palette = pPalette; } void raw_bitmap::load_from_th_file(const uint8_t* pPixelData, - size_t iPixelDataLength, int iWidth, - render_target *pEventualCanvas) -{ - if (pEventualCanvas == nullptr) { - throw std::invalid_argument("pEventualCanvas cannot be null"); - } + size_t iPixelDataLength, int iWidth, + render_target* pEventualCanvas) { + if (pEventualCanvas == nullptr) { + throw std::invalid_argument("pEventualCanvas cannot be null"); + } - uint8_t* converted_sprite = convertLegacySprite(pPixelData, iPixelDataLength); + uint8_t* converted_sprite = convertLegacySprite(pPixelData, iPixelDataLength); - int iHeight = static_cast(iPixelDataLength) / iWidth; - texture = pEventualCanvas->create_palettized_texture(iWidth, iHeight, converted_sprite, bitmap_palette, thdf_alt32_plain); - delete[] converted_sprite; + int iHeight = static_cast(iPixelDataLength) / iWidth; + texture = pEventualCanvas->create_palettized_texture( + iWidth, iHeight, converted_sprite, bitmap_palette, thdf_alt32_plain); + delete[] converted_sprite; - width = iWidth; - height = iHeight; - target = pEventualCanvas; + width = iWidth; + height = iHeight; + target = pEventualCanvas; } namespace { @@ -831,400 +745,351 @@ namespace { * @param iDataLength Length of the sprite data. * @param iWidth Width of the sprite. * @param iHeight Height of the sprite. - * @return Whether the sprite loads correctly (at the end of the sprite, all data is used). + * @return Whether the sprite loads correctly (at the end of the sprite, all + * data is used). */ -bool testSprite(const uint8_t* pData, size_t iDataLength, int iWidth, int iHeight) -{ - if (iWidth <= 0 || iHeight <= 0) - return true; +bool testSprite(const uint8_t* pData, size_t iDataLength, int iWidth, + int iHeight) { + if (iWidth <= 0 || iHeight <= 0) return true; - size_t iCount = iWidth * iHeight; - while (iCount > 0) - { - if (iDataLength < 1) - return false; + size_t iCount = iWidth * iHeight; + while (iCount > 0) { + if (iDataLength < 1) return false; + iDataLength--; + uint8_t iType = *pData++; + + size_t iLength = iType & 63; + switch (iType >> 6) { + case 0: // Fixed fully opaque 32bpp pixels + if (iCount < iLength || iDataLength < iLength * 3) return false; + iCount -= iLength; + iDataLength -= iLength * 3; + pData += iLength * 3; + break; + + case 1: // Fixed partially transparent 32bpp pixels + if (iDataLength < 1) return false; iDataLength--; - uint8_t iType = *pData++; + pData++; // Opacity byte. - size_t iLength = iType & 63; - switch (iType >> 6) - { - case 0: // Fixed fully opaque 32bpp pixels - if (iCount < iLength || iDataLength < iLength * 3) - return false; - iCount -= iLength; - iDataLength -= iLength * 3; - pData += iLength * 3; - break; + if (iCount < iLength || iDataLength < iLength * 3) return false; + iCount -= iLength; + iDataLength -= iLength * 3; + pData += iLength * 3; + break; - case 1: // Fixed partially transparent 32bpp pixels - if (iDataLength < 1) - return false; - iDataLength--; - pData++; // Opacity byte. + case 2: // Fixed fully transparent pixels + if (iCount < iLength) return false; + iCount -= iLength; + break; - if (iCount < iLength || iDataLength < iLength * 3) - return false; - iCount -= iLength; - iDataLength -= iLength * 3; - pData += iLength * 3; - break; + case 3: // Recolour layer + if (iDataLength < 2) return false; + iDataLength -= 2; + pData += 2; // Table number, opacity byte. - case 2: // Fixed fully transparent pixels - if (iCount < iLength) - return false; - iCount -= iLength; - break; - - case 3: // Recolour layer - if (iDataLength < 2) - return false; - iDataLength -= 2; - pData += 2; // Table number, opacity byte. - - if (iCount < iLength || iDataLength < iLength) - return false; - iCount -= iLength; - iDataLength -= iLength; - pData += iLength; - break; - } + if (iCount < iLength || iDataLength < iLength) return false; + iCount -= iLength; + iDataLength -= iLength; + pData += iLength; + break; } - return iDataLength == 0; + } + return iDataLength == 0; } -} // namespace +} // namespace -void raw_bitmap::draw(render_target* pCanvas, int iX, int iY) -{ - draw(pCanvas, iX, iY, 0, 0, width, height); +void raw_bitmap::draw(render_target* pCanvas, int iX, int iY) { + draw(pCanvas, iX, iY, 0, 0, width, height); } -void raw_bitmap::draw(render_target* pCanvas, int iX, int iY, - int iSrcX, int iSrcY, int iWidth, int iHeight) -{ - double fScaleFactor; - if (texture == nullptr) - return; +void raw_bitmap::draw(render_target* pCanvas, int iX, int iY, int iSrcX, + int iSrcY, int iWidth, int iHeight) { + double fScaleFactor; + if (texture == nullptr) return; - if(!pCanvas->should_scale_bitmaps(&fScaleFactor)) - { - fScaleFactor = 1; - } + if (!pCanvas->should_scale_bitmaps(&fScaleFactor)) { + fScaleFactor = 1; + } - const SDL_Rect rcSrc = { iSrcX, iSrcY, iWidth, iHeight }; - const SDL_Rect rcDest = { iX, - iY, - static_cast(iWidth * fScaleFactor), - static_cast(iHeight * fScaleFactor) }; + const SDL_Rect rcSrc = {iSrcX, iSrcY, iWidth, iHeight}; + const SDL_Rect rcDest = {iX, iY, static_cast(iWidth * fScaleFactor), + static_cast(iHeight * fScaleFactor)}; - pCanvas->draw(texture, &rcSrc, &rcDest, 0); + pCanvas->draw(texture, &rcSrc, &rcDest, 0); } -sprite_sheet::sprite_sheet() -{ - sprites = nullptr; - palette = nullptr; - target = nullptr; +sprite_sheet::sprite_sheet() { + sprites = nullptr; + palette = nullptr; + target = nullptr; + sprite_count = 0; +} + +sprite_sheet::~sprite_sheet() { _freeSprites(); } + +void sprite_sheet::_freeSingleSprite(size_t iNumber) { + if (iNumber >= sprite_count) return; + + if (sprites[iNumber].texture != nullptr) { + SDL_DestroyTexture(sprites[iNumber].texture); + sprites[iNumber].texture = nullptr; + } + if (sprites[iNumber].alt_texture != nullptr) { + SDL_DestroyTexture(sprites[iNumber].alt_texture); + sprites[iNumber].alt_texture = nullptr; + } + if (sprites[iNumber].data != nullptr) { + delete[] sprites[iNumber].data; + sprites[iNumber].data = nullptr; + } +} + +void sprite_sheet::_freeSprites() { + for (size_t i = 0; i < sprite_count; ++i) _freeSingleSprite(i); + + delete[] sprites; + sprites = nullptr; + sprite_count = 0; +} + +void sprite_sheet::set_palette(const ::palette* pPalette) { + palette = pPalette; +} + +bool sprite_sheet::set_sprite_count(size_t iCount, render_target* pCanvas) { + _freeSprites(); + + if (pCanvas == nullptr) return false; + target = pCanvas; + + sprite_count = iCount; + sprites = new (std::nothrow) sprite[sprite_count]; + if (sprites == nullptr) { sprite_count = 0; + return false; + } + + for (size_t i = 0; i < sprite_count; i++) { + sprite& spr = sprites[i]; + spr.texture = nullptr; + spr.alt_texture = nullptr; + spr.data = nullptr; + spr.alt_palette_map = nullptr; + spr.sprite_flags = thdf_alt32_plain; + spr.width = 0; + spr.height = 0; + } + + return true; } -sprite_sheet::~sprite_sheet() -{ - _freeSprites(); +bool sprite_sheet::load_from_th_file(const uint8_t* pTableData, + size_t iTableDataLength, + const uint8_t* pChunkData, + size_t iChunkDataLength, + bool bComplexChunks, + render_target* pCanvas) { + _freeSprites(); + if (pCanvas == nullptr) return false; + + size_t iCount = iTableDataLength / sizeof(th_sprite_properties); + if (!set_sprite_count(iCount, pCanvas)) return false; + + for (size_t i = 0; i < sprite_count; ++i) { + sprite* pSprite = sprites + i; + const th_sprite_properties* pTHSprite = + reinterpret_cast(pTableData) + i; + + pSprite->texture = nullptr; + pSprite->alt_texture = nullptr; + pSprite->data = nullptr; + pSprite->alt_palette_map = nullptr; + pSprite->width = pTHSprite->width; + pSprite->height = pTHSprite->height; + + if (pSprite->width == 0 || pSprite->height == 0) continue; + + { + uint8_t* pData = new uint8_t[pSprite->width * pSprite->height]; + chunk_renderer oRenderer(pSprite->width, pSprite->height, pData); + int iDataLen = static_cast(iChunkDataLength) - + static_cast(pTHSprite->position); + if (iDataLen < 0) iDataLen = 0; + oRenderer.decode_chunks(pChunkData + pTHSprite->position, iDataLen, + bComplexChunks); + pData = oRenderer.take_data(); + pSprite->data = + convertLegacySprite(pData, pSprite->width * pSprite->height); + delete[] pData; + } + } + return true; } -void sprite_sheet::_freeSingleSprite(size_t iNumber) -{ - if (iNumber >= sprite_count) - return; +bool sprite_sheet::set_sprite_data(size_t iSprite, const uint8_t* pData, + bool bTakeData, size_t iDataLength, + int iWidth, int iHeight) { + if (iSprite >= sprite_count) return false; - if (sprites[iNumber].texture != nullptr) - { - SDL_DestroyTexture(sprites[iNumber].texture); - sprites[iNumber].texture = nullptr; - } - if (sprites[iNumber].alt_texture != nullptr) - { - SDL_DestroyTexture(sprites[iNumber].alt_texture); - sprites[iNumber].alt_texture = nullptr; - } - if(sprites[iNumber].data != nullptr) - { - delete[] sprites[iNumber].data; - sprites[iNumber].data = nullptr; - } + if (!testSprite(pData, iDataLength, iWidth, iHeight)) { + std::printf("Sprite number %zu has a bad encoding, skipping", iSprite); + return false; + } + + _freeSingleSprite(iSprite); + sprite* pSprite = sprites + iSprite; + if (bTakeData) { + pSprite->data = pData; + } else { + uint8_t* pNewData = new (std::nothrow) uint8_t[iDataLength]; + if (pNewData == nullptr) return false; + + std::memcpy(pNewData, pData, iDataLength); + pSprite->data = pNewData; + } + + pSprite->width = iWidth; + pSprite->height = iHeight; + return true; } -void sprite_sheet::_freeSprites() -{ - for(size_t i = 0; i < sprite_count; ++i) - _freeSingleSprite(i); +void sprite_sheet::set_sprite_alt_palette_map(size_t iSprite, + const uint8_t* pMap, + uint32_t iAlt32) { + if (iSprite >= sprite_count) return; - delete[] sprites; - sprites = nullptr; - sprite_count = 0; + sprite* pSprite = sprites + iSprite; + if (pSprite->alt_palette_map != pMap) { + pSprite->alt_palette_map = pMap; + pSprite->sprite_flags = iAlt32; + if (pSprite->alt_texture) { + SDL_DestroyTexture(pSprite->alt_texture); + pSprite->alt_texture = nullptr; + } + } } -void sprite_sheet::set_palette(const ::palette* pPalette) -{ - palette = pPalette; +size_t sprite_sheet::get_sprite_count() const { return sprite_count; } + +bool sprite_sheet::get_sprite_size(size_t iSprite, unsigned int* pWidth, + unsigned int* pHeight) const { + if (iSprite >= sprite_count) return false; + if (pWidth != nullptr) *pWidth = sprites[iSprite].width; + if (pHeight != nullptr) *pHeight = sprites[iSprite].height; + return true; } -bool sprite_sheet::set_sprite_count(size_t iCount, render_target* pCanvas) -{ - _freeSprites(); - - if(pCanvas == nullptr) - return false; - target = pCanvas; - - sprite_count = iCount; - sprites = new (std::nothrow) sprite[sprite_count]; - if(sprites == nullptr) - { - sprite_count = 0; - return false; - } - - for (size_t i = 0; i < sprite_count; i++) - { - sprite &spr = sprites[i]; - spr.texture = nullptr; - spr.alt_texture = nullptr; - spr.data = nullptr; - spr.alt_palette_map = nullptr; - spr.sprite_flags = thdf_alt32_plain; - spr.width = 0; - spr.height = 0; - } - - return true; +void sprite_sheet::get_sprite_size_unchecked(size_t iSprite, + unsigned int* pWidth, + unsigned int* pHeight) const { + *pWidth = sprites[iSprite].width; + *pHeight = sprites[iSprite].height; } -bool sprite_sheet::load_from_th_file(const uint8_t* pTableData, size_t iTableDataLength, - const uint8_t* pChunkData, size_t iChunkDataLength, - bool bComplexChunks, render_target* pCanvas) -{ - _freeSprites(); - if(pCanvas == nullptr) - return false; - - size_t iCount = iTableDataLength / sizeof(th_sprite_properties); - if (!set_sprite_count(iCount, pCanvas)) - return false; - - for(size_t i = 0; i < sprite_count; ++i) - { - sprite *pSprite = sprites + i; - const th_sprite_properties *pTHSprite = reinterpret_cast(pTableData) + i; - - pSprite->texture = nullptr; - pSprite->alt_texture = nullptr; - pSprite->data = nullptr; - pSprite->alt_palette_map = nullptr; - pSprite->width = pTHSprite->width; - pSprite->height = pTHSprite->height; - - if(pSprite->width == 0 || pSprite->height == 0) - continue; - - { - uint8_t *pData = new uint8_t[pSprite->width * pSprite->height]; - chunk_renderer oRenderer(pSprite->width, pSprite->height, pData); - int iDataLen = static_cast(iChunkDataLength) - static_cast(pTHSprite->position); - if(iDataLen < 0) - iDataLen = 0; - oRenderer.decode_chunks(pChunkData + pTHSprite->position, iDataLen, bComplexChunks); - pData = oRenderer.take_data(); - pSprite->data = convertLegacySprite(pData, pSprite->width * pSprite->height); - delete[] pData; - } - } - return true; +bool sprite_sheet::get_sprite_average_colour(size_t iSprite, + argb_colour* pColour) const { + if (iSprite >= sprite_count) return false; + const sprite* pSprite = sprites + iSprite; + int iCountTotal = 0; + int iUsageCounts[256] = {0}; + for (long i = 0; i < pSprite->width * pSprite->height; ++i) { + uint8_t cPalIndex = pSprite->data[i]; + uint32_t iColour = palette->get_argb_data()[cPalIndex]; + if ((iColour >> 24) == 0) continue; + // Grant higher score to pixels with high or low intensity (helps avoid + // grey fonts) + int iR = palette::get_red(iColour); + int iG = palette::get_green(iColour); + int iB = palette::get_blue(iColour); + uint8_t cIntensity = static_cast((iR + iG + iB) / 3); + int iScore = 1 + std::max(0, 3 - ((255 - cIntensity) / 32)) + + std::max(0, 3 - (cIntensity / 32)); + iUsageCounts[cPalIndex] += iScore; + iCountTotal += iScore; + } + if (iCountTotal == 0) return false; + int iHighestCountIndex = 0; + for (int i = 0; i < 256; ++i) { + if (iUsageCounts[i] > iUsageCounts[iHighestCountIndex]) + iHighestCountIndex = i; + } + *pColour = palette->get_argb_data()[iHighestCountIndex]; + return true; } -bool sprite_sheet::set_sprite_data(size_t iSprite, const uint8_t *pData, bool bTakeData, - size_t iDataLength, int iWidth, int iHeight) -{ - if (iSprite >= sprite_count) - return false; +void sprite_sheet::draw_sprite(render_target* pCanvas, size_t iSprite, int iX, + int iY, uint32_t iFlags) { + if (iSprite >= sprite_count || pCanvas == nullptr || pCanvas != target) + return; + sprite& sprite = sprites[iSprite]; - if (!testSprite(pData, iDataLength, iWidth, iHeight)) - { - std::printf("Sprite number %zu has a bad encoding, skipping", iSprite); - return false; + // Find or create the texture + SDL_Texture* pTexture = sprite.texture; + if (!pTexture) { + if (sprite.data == nullptr) return; + + uint32_t iSprFlags = + (sprite.sprite_flags & ~thdf_alt32_mask) | thdf_alt32_plain; + pTexture = target->create_palettized_texture( + sprite.width, sprite.height, sprite.data, palette, iSprFlags); + sprite.texture = pTexture; + } + if (iFlags & thdf_alt_palette) { + pTexture = sprite.alt_texture; + if (!pTexture) { + pTexture = _makeAltBitmap(&sprite); + if (!pTexture) return; } + } - _freeSingleSprite(iSprite); - sprite *pSprite = sprites + iSprite; - if (bTakeData) - { - pSprite->data = pData; - } - else - { - uint8_t *pNewData = new (std::nothrow) uint8_t[iDataLength]; - if (pNewData == nullptr) - return false; + SDL_Rect rcSrc = {0, 0, sprite.width, sprite.height}; + SDL_Rect rcDest = {iX, iY, sprite.width, sprite.height}; - std::memcpy(pNewData, pData, iDataLength); - pSprite->data = pNewData; - } - - pSprite->width = iWidth; - pSprite->height = iHeight; - return true; + pCanvas->draw(pTexture, &rcSrc, &rcDest, iFlags); } -void sprite_sheet::set_sprite_alt_palette_map(size_t iSprite, const uint8_t* pMap, uint32_t iAlt32) -{ - if(iSprite >= sprite_count) - return; +void sprite_sheet::wx_draw_sprite(size_t iSprite, uint8_t* pRGBData, + uint8_t* pAData) { + if (iSprite >= sprite_count || pRGBData == nullptr || pAData == nullptr) + return; + sprite* pSprite = sprites + iSprite; - sprite *pSprite = sprites + iSprite; - if(pSprite->alt_palette_map != pMap) - { - pSprite->alt_palette_map = pMap; - pSprite->sprite_flags = iAlt32; - if(pSprite->alt_texture) - { - SDL_DestroyTexture(pSprite->alt_texture); - pSprite->alt_texture = nullptr; - } - } + wx_storing oRenderer(pRGBData, pAData, pSprite->width, pSprite->height); + oRenderer.decode_image(pSprite->data, palette, pSprite->sprite_flags); } -size_t sprite_sheet::get_sprite_count() const -{ - return sprite_count; -} +SDL_Texture* sprite_sheet::_makeAltBitmap(sprite* pSprite) { + const uint32_t* pPalette = palette->get_argb_data(); -bool sprite_sheet::get_sprite_size(size_t iSprite, unsigned int* pWidth, unsigned int* pHeight) const -{ - if(iSprite >= sprite_count) - return false; - if(pWidth != nullptr) - *pWidth = sprites[iSprite].width; - if(pHeight != nullptr) - *pHeight = sprites[iSprite].height; - return true; -} - -void sprite_sheet::get_sprite_size_unchecked(size_t iSprite, unsigned int* pWidth, unsigned int* pHeight) const -{ - *pWidth = sprites[iSprite].width; - *pHeight = sprites[iSprite].height; -} - -bool sprite_sheet::get_sprite_average_colour(size_t iSprite, argb_colour* pColour) const -{ - if(iSprite >= sprite_count) - return false; - const sprite *pSprite = sprites + iSprite; - int iCountTotal = 0; - int iUsageCounts[256] = {0}; - for(long i = 0; i < pSprite->width * pSprite->height; ++i) - { - uint8_t cPalIndex = pSprite->data[i]; - uint32_t iColour = palette->get_argb_data()[cPalIndex]; - if((iColour >> 24) == 0) - continue; - // Grant higher score to pixels with high or low intensity (helps avoid grey fonts) - int iR = palette::get_red(iColour); - int iG = palette::get_green(iColour); - int iB = palette::get_blue(iColour); - uint8_t cIntensity = static_cast((iR + iG + iB) / 3); - int iScore = 1 + std::max(0, 3 - ((255 - cIntensity) / 32)) + std::max(0, 3 - (cIntensity / 32)); - iUsageCounts[cPalIndex] += iScore; - iCountTotal += iScore; + if (!pSprite->alt_palette_map) // Use normal palette. + { + uint32_t iSprFlags = + (pSprite->sprite_flags & ~thdf_alt32_mask) | thdf_alt32_plain; + pSprite->alt_texture = target->create_palettized_texture( + pSprite->width, pSprite->height, pSprite->data, palette, iSprFlags); + } else if (!pPalette) // Draw alternative palette, but no palette set (ie + // 32bpp image). + { + pSprite->alt_texture = target->create_palettized_texture( + pSprite->width, pSprite->height, pSprite->data, palette, + pSprite->sprite_flags); + } else // Paletted image, build recolour palette. + { + ::palette oPalette; + for (int iColour = 0; iColour < 255; iColour++) { + oPalette.set_argb(iColour, pPalette[pSprite->alt_palette_map[iColour]]); } - if(iCountTotal == 0) - return false; - int iHighestCountIndex = 0; - for(int i = 0; i < 256; ++i) - { - if(iUsageCounts[i] > iUsageCounts[iHighestCountIndex]) - iHighestCountIndex = i; - } - *pColour = palette->get_argb_data()[iHighestCountIndex]; - return true; -} + oPalette.set_argb(255, + pPalette[255]); // Colour 0xFF doesn't get remapped. -void sprite_sheet::draw_sprite(render_target* pCanvas, size_t iSprite, int iX, int iY, uint32_t iFlags) -{ - if(iSprite >= sprite_count || pCanvas == nullptr || pCanvas != target) - return; - sprite &sprite = sprites[iSprite]; + pSprite->alt_texture = target->create_palettized_texture( + pSprite->width, pSprite->height, pSprite->data, &oPalette, + pSprite->sprite_flags); + } - // Find or create the texture - SDL_Texture *pTexture = sprite.texture; - if(!pTexture) - { - if(sprite.data == nullptr) - return; - - uint32_t iSprFlags = (sprite.sprite_flags & ~thdf_alt32_mask) | thdf_alt32_plain; - pTexture = target->create_palettized_texture(sprite.width, sprite.height, - sprite.data, palette, iSprFlags); - sprite.texture = pTexture; - } - if(iFlags & thdf_alt_palette) - { - pTexture = sprite.alt_texture; - if(!pTexture) - { - pTexture = _makeAltBitmap(&sprite); - if(!pTexture) - return; - } - } - - SDL_Rect rcSrc = { 0, 0, sprite.width, sprite.height }; - SDL_Rect rcDest = { iX, iY, sprite.width, sprite.height }; - - pCanvas->draw(pTexture, &rcSrc, &rcDest, iFlags); -} - -void sprite_sheet::wx_draw_sprite(size_t iSprite, uint8_t* pRGBData, uint8_t* pAData) -{ - if(iSprite >= sprite_count || pRGBData == nullptr || pAData == nullptr) - return; - sprite *pSprite = sprites + iSprite; - - wx_storing oRenderer(pRGBData, pAData, pSprite->width, pSprite->height); - oRenderer.decode_image(pSprite->data, palette, pSprite->sprite_flags); -} - -SDL_Texture* sprite_sheet::_makeAltBitmap(sprite *pSprite) -{ - const uint32_t *pPalette = palette->get_argb_data(); - - if (!pSprite->alt_palette_map) // Use normal palette. - { - uint32_t iSprFlags = (pSprite->sprite_flags & ~thdf_alt32_mask) | thdf_alt32_plain; - pSprite->alt_texture = target->create_palettized_texture(pSprite->width, pSprite->height, - pSprite->data, palette, iSprFlags); - } - else if (!pPalette) // Draw alternative palette, but no palette set (ie 32bpp image). - { - pSprite->alt_texture = target->create_palettized_texture(pSprite->width, pSprite->height, - pSprite->data, palette, pSprite->sprite_flags); - } - else // Paletted image, build recolour palette. - { - ::palette oPalette; - for (int iColour = 0; iColour < 255; iColour++) - { - oPalette.set_argb(iColour, pPalette[pSprite->alt_palette_map[iColour]]); - } - oPalette.set_argb(255, pPalette[255]); // Colour 0xFF doesn't get remapped. - - pSprite->alt_texture = target->create_palettized_texture(pSprite->width, pSprite->height, - pSprite->data, &oPalette, pSprite->sprite_flags); - } - - return pSprite->alt_texture; + return pSprite->alt_texture; } namespace { @@ -1238,141 +1103,123 @@ namespace { * @param iPixelNumber Number of the pixel to retrieve. */ uint32_t get32BppPixel(const uint8_t* pImg, int iWidth, int iHeight, - const ::palette *pPalette, size_t iPixelNumber) -{ - if (iWidth <= 0 || iHeight <= 0 || iPixelNumber < 0 || - iPixelNumber >= static_cast(iWidth) * iHeight) - { - return palette::pack_argb(0, 0, 0,0); - } + const ::palette* pPalette, size_t iPixelNumber) { + if (iWidth <= 0 || iHeight <= 0 || iPixelNumber < 0 || + iPixelNumber >= static_cast(iWidth) * iHeight) { + return palette::pack_argb(0, 0, 0, 0); + } - for (;;) { - uint8_t iType = *pImg++; - size_t iLength = iType & 63; - switch (iType >> 6) - { - case 0: // Fixed fully opaque 32bpp pixels - if (iPixelNumber >= iLength) - { - pImg += 3 * iLength; - iPixelNumber -= iLength; - break; - } - - while (iLength > 0) - { - if (iPixelNumber == 0) - return palette::pack_argb(0xFF, pImg[0], pImg[1], pImg[2]); - - iPixelNumber--; - pImg += 3; - iLength--; - } - break; - - case 1: // Fixed partially transparent 32bpp pixels - { - uint8_t iOpacity = *pImg++; - if (iPixelNumber >= iLength) - { - pImg += 3 * iLength; - iPixelNumber -= iLength; - break; - } - - while (iLength > 0) - { - if (iPixelNumber == 0) - return palette::pack_argb(iOpacity, pImg[0], pImg[1], pImg[2]); - - iPixelNumber--; - pImg += 3; - iLength--; - } - break; - } - - case 2: // Fixed fully transparent pixels - { - if (iPixelNumber >= iLength) - { - iPixelNumber -= iLength; - break; - } - - return palette::pack_argb(0, 0, 0, 0); - } - - case 3: // Recolour layer - { - uint8_t iTable = *pImg++; - pImg++; // Skip reading the opacity for now. - if (iPixelNumber >= iLength) - { - pImg += iLength; - iPixelNumber -= iLength; - break; - } - - if (iTable == 0xFF && pPalette != nullptr) - { - // Legacy sprite data. Use the palette to recolour the layer. - // Note that the iOpacity is ignored here. - const uint32_t* pColours = pPalette->get_argb_data(); - return pColours[pImg[iPixelNumber]]; - } - else - { - // TODO: Add proper recolour layers, where RGB comes from - // table 'iTable' at index *pImg (iLength times), and - // opacity comes from the byte after the iTable byte. - // - // For now just draw black pixels, so it won't go unnoticed. - return palette::pack_argb(0xFF, 0, 0, 0); - } - } + for (;;) { + uint8_t iType = *pImg++; + size_t iLength = iType & 63; + switch (iType >> 6) { + case 0: // Fixed fully opaque 32bpp pixels + if (iPixelNumber >= iLength) { + pImg += 3 * iLength; + iPixelNumber -= iLength; + break; } + + while (iLength > 0) { + if (iPixelNumber == 0) + return palette::pack_argb(0xFF, pImg[0], pImg[1], pImg[2]); + + iPixelNumber--; + pImg += 3; + iLength--; + } + break; + + case 1: // Fixed partially transparent 32bpp pixels + { + uint8_t iOpacity = *pImg++; + if (iPixelNumber >= iLength) { + pImg += 3 * iLength; + iPixelNumber -= iLength; + break; + } + + while (iLength > 0) { + if (iPixelNumber == 0) + return palette::pack_argb(iOpacity, pImg[0], pImg[1], pImg[2]); + + iPixelNumber--; + pImg += 3; + iLength--; + } + break; + } + + case 2: // Fixed fully transparent pixels + { + if (iPixelNumber >= iLength) { + iPixelNumber -= iLength; + break; + } + + return palette::pack_argb(0, 0, 0, 0); + } + + case 3: // Recolour layer + { + uint8_t iTable = *pImg++; + pImg++; // Skip reading the opacity for now. + if (iPixelNumber >= iLength) { + pImg += iLength; + iPixelNumber -= iLength; + break; + } + + if (iTable == 0xFF && pPalette != nullptr) { + // Legacy sprite data. Use the palette to recolour the + // layer. Note that the iOpacity is ignored here. + const uint32_t* pColours = pPalette->get_argb_data(); + return pColours[pImg[iPixelNumber]]; + } else { + // TODO: Add proper recolour layers, where RGB comes from + // table 'iTable' at index *pImg (iLength times), and + // opacity comes from the byte after the iTable byte. + // + // For now just draw black pixels, so it won't go unnoticed. + return palette::pack_argb(0xFF, 0, 0, 0); + } + } } + } } -} // namespace +} // namespace -bool sprite_sheet::hit_test_sprite(size_t iSprite, int iX, int iY, uint32_t iFlags) const -{ - if(iX < 0 || iY < 0 || iSprite >= sprite_count) - return false; +bool sprite_sheet::hit_test_sprite(size_t iSprite, int iX, int iY, + uint32_t iFlags) const { + if (iX < 0 || iY < 0 || iSprite >= sprite_count) return false; - sprite &sprite = sprites[iSprite]; - int iWidth = sprite.width; - int iHeight = sprite.height; - if(iX >= iWidth || iY >= iHeight) - return false; - if(iFlags & thdf_flip_horizontal) - iX = iWidth - iX - 1; - if(iFlags & thdf_flip_vertical) - iY = iHeight - iY - 1; + sprite& sprite = sprites[iSprite]; + int iWidth = sprite.width; + int iHeight = sprite.height; + if (iX >= iWidth || iY >= iHeight) return false; + if (iFlags & thdf_flip_horizontal) iX = iWidth - iX - 1; + if (iFlags & thdf_flip_vertical) iY = iHeight - iY - 1; - uint32_t iCol = get32BppPixel(sprite.data, iWidth, iHeight, palette, iY * iWidth + iX); - return palette::get_alpha(iCol) != 0; + uint32_t iCol = + get32BppPixel(sprite.data, iWidth, iHeight, palette, iY * iWidth + iX); + return palette::get_alpha(iCol) != 0; } -cursor::cursor() -{ - bitmap = nullptr; - hotspot_x = 0; - hotspot_y = 0; - hidden_cursor = nullptr; +cursor::cursor() { + bitmap = nullptr; + hotspot_x = 0; + hotspot_y = 0; + hidden_cursor = nullptr; } -cursor::~cursor() -{ - SDL_FreeSurface(bitmap); - SDL_FreeCursor(hidden_cursor); +cursor::~cursor() { + SDL_FreeSurface(bitmap); + SDL_FreeCursor(hidden_cursor); } bool cursor::create_from_sprite(sprite_sheet* pSheet, size_t iSprite, - int iHotspotX, int iHotspotY) -{ + int iHotspotX, int iHotspotY) { #if 0 SDL_FreeSurface(m_pBitmap); m_pBitmap = nullptr; @@ -1386,12 +1233,11 @@ bool cursor::create_from_sprite(sprite_sheet* pSheet, size_t iSprite, m_iHotspotY = iHotspotY; return true; #else - return false; + return false; #endif } -void cursor::use(render_target* pTarget) -{ +void cursor::use(render_target* pTarget) { #if 0 //SDL_ShowCursor(0) is buggy in fullscreen until 1.3 (they say) // use transparent cursor for same effect @@ -1402,18 +1248,16 @@ void cursor::use(render_target* pTarget) #endif } -bool cursor::set_position(render_target* pTarget, int iX, int iY) -{ +bool cursor::set_position(render_target* pTarget, int iX, int iY) { #if 0 pTarget->setCursorPosition(iX, iY); return true; #else - return false; + return false; #endif } -void cursor::draw(render_target* pCanvas, int iX, int iY) -{ +void cursor::draw(render_target* pCanvas, int iX, int iY) { #if 0 SDL_Rect rcDest; rcDest.x = (Sint16)(iX - m_iHotspotX); @@ -1422,162 +1266,142 @@ void cursor::draw(render_target* pCanvas, int iX, int iY) #endif } -line::line() -{ - initialize(); +line::line() { initialize(); } + +line::~line() { + line_operation* op = first_operation; + while (op) { + line_operation* next = (line_operation*)(op->next); + delete (op); + op = next; + } } -line::~line() -{ - line_operation* op = first_operation; - while (op) { - line_operation* next = (line_operation*)(op->next); - delete(op); - op = next; - } -} - -void line::initialize() -{ - width = 1; - red = 0; - green = 0; - blue = 0; - alpha = 255; - - // We start at 0,0 - first_operation = new line_operation(line_operation_type::move, 0, 0); - current_operation = first_operation; -} - -void line::move_to(double fX, double fY) -{ - line_operation* previous = current_operation; - current_operation = new line_operation(line_operation_type::move, fX, fY); - previous->next = current_operation; -} - -void line::line_to(double fX, double fY) -{ - line_operation* previous = current_operation; - current_operation = new line_operation(line_operation_type::line, fX, fY); - previous->next = current_operation; -} - -void line::set_width(double pLineWidth) -{ - width = pLineWidth; -} - -void line::set_colour(uint8_t iR, uint8_t iG, uint8_t iB, uint8_t iA) -{ - red = iR; - green = iG; - blue = iB; - alpha = iA; -} - -void line::draw(render_target* pCanvas, int iX, int iY) -{ - pCanvas->draw_line(this, iX, iY); -} - -void line::persist(lua_persist_writer *pWriter) const -{ - pWriter->write_uint((uint32_t)red); - pWriter->write_uint((uint32_t)green); - pWriter->write_uint((uint32_t)blue); - pWriter->write_uint((uint32_t)alpha); - pWriter->write_float(width); - - line_operation* op = (line_operation*)(first_operation->next); - uint32_t numOps = 0; - for (; op; numOps++) { - op = (line_operation*)(op->next); - } - - pWriter->write_uint(numOps); - - op = (line_operation*)(first_operation->next); - while (op) { - pWriter->write_uint((uint32_t)op->type); - pWriter->write_float(op->x); - pWriter->write_float(op->y); - - op = (line_operation*)(op->next); - } -} - -void line::depersist(lua_persist_reader *pReader) -{ - initialize(); - - pReader->read_uint(red); - pReader->read_uint(green); - pReader->read_uint(blue); - pReader->read_uint(alpha); - pReader->read_float(width); - - uint32_t numOps = 0; - pReader->read_uint(numOps); - for (uint32_t i = 0; i < numOps; i++) { - line_operation_type type; - double fX, fY; - pReader->read_uint((uint32_t&)type); - pReader->read_float(fX); - pReader->read_float(fY); - - if (type == line_operation_type::move) { - move_to(fX, fY); - } else if (type == line_operation_type::line) { - line_to(fX, fY); - } +void line::initialize() { + width = 1; + red = 0; + green = 0; + blue = 0; + alpha = 255; + + // We start at 0,0 + first_operation = new line_operation(line_operation_type::move, 0, 0); + current_operation = first_operation; +} + +void line::move_to(double fX, double fY) { + line_operation* previous = current_operation; + current_operation = new line_operation(line_operation_type::move, fX, fY); + previous->next = current_operation; +} + +void line::line_to(double fX, double fY) { + line_operation* previous = current_operation; + current_operation = new line_operation(line_operation_type::line, fX, fY); + previous->next = current_operation; +} + +void line::set_width(double pLineWidth) { width = pLineWidth; } + +void line::set_colour(uint8_t iR, uint8_t iG, uint8_t iB, uint8_t iA) { + red = iR; + green = iG; + blue = iB; + alpha = iA; +} + +void line::draw(render_target* pCanvas, int iX, int iY) { + pCanvas->draw_line(this, iX, iY); +} + +void line::persist(lua_persist_writer* pWriter) const { + pWriter->write_uint((uint32_t)red); + pWriter->write_uint((uint32_t)green); + pWriter->write_uint((uint32_t)blue); + pWriter->write_uint((uint32_t)alpha); + pWriter->write_float(width); + + line_operation* op = (line_operation*)(first_operation->next); + uint32_t numOps = 0; + for (; op; numOps++) { + op = (line_operation*)(op->next); + } + + pWriter->write_uint(numOps); + + op = (line_operation*)(first_operation->next); + while (op) { + pWriter->write_uint((uint32_t)op->type); + pWriter->write_float(op->x); + pWriter->write_float(op->y); + + op = (line_operation*)(op->next); + } +} + +void line::depersist(lua_persist_reader* pReader) { + initialize(); + + pReader->read_uint(red); + pReader->read_uint(green); + pReader->read_uint(blue); + pReader->read_uint(alpha); + pReader->read_float(width); + + uint32_t numOps = 0; + pReader->read_uint(numOps); + for (uint32_t i = 0; i < numOps; i++) { + line_operation_type type; + double fX, fY; + pReader->read_uint((uint32_t&)type); + pReader->read_float(fX); + pReader->read_float(fY); + + if (type == line_operation_type::move) { + move_to(fX, fY); + } else if (type == line_operation_type::line) { + line_to(fX, fY); } + } } #ifdef CORSIX_TH_USE_FREETYPE2 -bool freetype_font::is_monochrome() const -{ - return true; +bool freetype_font::is_monochrome() const { return true; } + +void freetype_font::free_texture(cached_text* pCacheEntry) const { + if (pCacheEntry->texture != nullptr) { + SDL_DestroyTexture(pCacheEntry->texture); + pCacheEntry->texture = nullptr; + } } -void freetype_font::free_texture(cached_text* pCacheEntry) const -{ - if(pCacheEntry->texture != nullptr) - { - SDL_DestroyTexture(pCacheEntry->texture); - pCacheEntry->texture = nullptr; +void freetype_font::make_texture(render_target* pEventualCanvas, + cached_text* pCacheEntry) const { + uint32_t* pPixels = new uint32_t[pCacheEntry->width * pCacheEntry->height]; + std::memset(pPixels, 0, + pCacheEntry->width * pCacheEntry->height * sizeof(uint32_t)); + uint8_t* pInRow = pCacheEntry->data; + uint32_t* pOutRow = pPixels; + uint32_t iColBase = colour & 0xFFFFFF; + for (int iY = 0; iY < pCacheEntry->height; + ++iY, pOutRow += pCacheEntry->width, pInRow += pCacheEntry->width) { + for (int iX = 0; iX < pCacheEntry->width; ++iX) { + pOutRow[iX] = (static_cast(pInRow[iX]) << 24) | iColBase; } + } + + pCacheEntry->texture = pEventualCanvas->create_texture( + pCacheEntry->width, pCacheEntry->height, pPixels); + delete[] pPixels; } +void freetype_font::draw_texture(render_target* pCanvas, + cached_text* pCacheEntry, int iX, + int iY) const { + if (pCacheEntry->texture == nullptr) return; -void freetype_font::make_texture(render_target *pEventualCanvas, cached_text* pCacheEntry) const -{ - uint32_t* pPixels = new uint32_t[pCacheEntry->width * pCacheEntry->height]; - std::memset(pPixels, 0, pCacheEntry->width * pCacheEntry->height * sizeof(uint32_t)); - uint8_t* pInRow = pCacheEntry->data; - uint32_t* pOutRow = pPixels; - uint32_t iColBase = colour & 0xFFFFFF; - for(int iY = 0; iY < pCacheEntry->height; ++iY, pOutRow += pCacheEntry->width, - pInRow += pCacheEntry->width) - { - for(int iX = 0; iX < pCacheEntry->width; ++iX) - { - pOutRow[iX] = (static_cast(pInRow[iX]) << 24) | iColBase; - } - } - - pCacheEntry->texture = pEventualCanvas->create_texture(pCacheEntry->width, pCacheEntry->height, pPixels); - delete[] pPixels; + SDL_Rect rcDest = {iX, iY, pCacheEntry->width, pCacheEntry->height}; + pCanvas->draw(pCacheEntry->texture, nullptr, &rcDest, 0); } -void freetype_font::draw_texture(render_target* pCanvas, cached_text* pCacheEntry, int iX, int iY) const -{ - if(pCacheEntry->texture == nullptr) - return; - - SDL_Rect rcDest = { iX, iY, pCacheEntry->width, pCacheEntry->height }; - pCanvas->draw(pCacheEntry->texture, nullptr, &rcDest, 0); -} - -#endif // CORSIX_TH_USE_FREETYPE2 +#endif // CORSIX_TH_USE_FREETYPE2 diff --git a/CorsixTH/Src/th_gfx_sdl.h b/CorsixTH/Src/th_gfx_sdl.h index 00adbee4..dafd179b 100644 --- a/CorsixTH/Src/th_gfx_sdl.h +++ b/CorsixTH/Src/th_gfx_sdl.h @@ -25,662 +25,657 @@ SOFTWARE. #include "config.h" #include -#include "persist_lua.h" #include +#include "persist_lua.h" class cursor; + struct clip_rect : public SDL_Rect { - typedef Sint16 x_y_type; - typedef Uint16 w_h_type; + typedef Sint16 x_y_type; + typedef Uint16 w_h_type; }; + struct render_target_creation_params; +enum class scaled_items; + //! 32bpp ARGB colour. See #palette::pack_argb typedef uint32_t argb_colour; //! 8bpp palette class. -class palette -{ -public: // External API - palette(); +class palette { + public: // External API + palette(); - //! Load palette from the supplied data. - /*! - Note that the data uses palette entries of 6 bit colours. - @param pData Data loaded from the file. - @param iDataLength Size of the data. - @return Whether loading of the palette succeeded. - */ - bool load_from_th_file(const uint8_t* pData, size_t iDataLength); + //! Load palette from the supplied data. + /*! + Note that the data uses palette entries of 6 bit colours. + @param pData Data loaded from the file. + @param iDataLength Size of the data. + @return Whether loading of the palette succeeded. + */ + bool load_from_th_file(const uint8_t* pData, size_t iDataLength); - //! Set an entry of the palette. - /*! - The RGB colour (255, 0, 255) is used as the transparent colour. - @param iEntry Entry number to change. - @param iR Amount of red in the new entry. - @param iG Amount of green in the new entry. - @param iB Amount of blue in the new entry. - @return Setting the entry succeeded. - */ - bool set_entry(int iEntry, uint8_t iR, uint8_t iG, uint8_t iB); + //! Set an entry of the palette. + /*! + The RGB colour (255, 0, 255) is used as the transparent colour. + @param iEntry Entry number to change. + @param iR Amount of red in the new entry. + @param iG Amount of green in the new entry. + @param iB Amount of blue in the new entry. + @return Setting the entry succeeded. + */ + bool set_entry(int iEntry, uint8_t iR, uint8_t iG, uint8_t iB); -public: // Internal (this rendering engine only) API + public: // Internal (this rendering engine only) API + //! Convert A, R, G, B values to a 32bpp colour. + /*! + @param iA Amount of opacity (0-255). + @param iR Amount of red (0-255). + @param iG Amount of green (0-255). + @param iB Amount of blue (0-255). + @return 32bpp value representing the provided colour values. + */ + static constexpr argb_colour pack_argb(uint8_t iA, uint8_t iR, uint8_t iG, + uint8_t iB) { + return (static_cast(iR) << 0) | + (static_cast(iG) << 8) | + (static_cast(iB) << 16) | + (static_cast(iA) << 24); + } - //! Convert A, R, G, B values to a 32bpp colour. - /*! - @param iA Amount of opacity (0-255). - @param iR Amount of red (0-255). - @param iG Amount of green (0-255). - @param iB Amount of blue (0-255). - @return 32bpp value representing the provided colour values. - */ - static constexpr argb_colour pack_argb(uint8_t iA, uint8_t iR, uint8_t iG, uint8_t iB) - { - return (static_cast(iR) << 0) | - (static_cast(iG) << 8) | - (static_cast(iB) << 16) | - (static_cast(iA) << 24) ; - } + //! Get the red component of a colour. + /*! + @param iColour Colour to examine. + @return The red component intensity of the colour. + */ + static constexpr uint8_t get_red(argb_colour iColour) { + return static_cast((iColour >> 0) & 0xFF); + } - //! Get the red component of a colour. - /*! - @param iColour Colour to examine. - @return The red component intensity of the colour. - */ - static constexpr uint8_t get_red(argb_colour iColour) - { - return static_cast((iColour >> 0) & 0xFF); - } + //! Get the green component of a colour. + /*! + @param iColour Colour to examine. + @return The green component intensity of the colour. + */ + static constexpr uint8_t get_green(argb_colour iColour) { + return static_cast((iColour >> 8) & 0xFF); + } - //! Get the green component of a colour. - /*! - @param iColour Colour to examine. - @return The green component intensity of the colour. - */ - static constexpr uint8_t get_green(argb_colour iColour) - { - return static_cast((iColour >> 8) & 0xFF); - } + //! Get the blue component of a colour. + /*! + @param iColour Colour to examine. + @return The blue component intensity of the colour. + */ + static constexpr uint8_t get_blue(argb_colour iColour) { + return static_cast((iColour >> 16) & 0xFF); + } - //! Get the blue component of a colour. - /*! - @param iColour Colour to examine. - @return The blue component intensity of the colour. - */ - static constexpr uint8_t get_blue(argb_colour iColour) - { - return static_cast((iColour >> 16) & 0xFF); - } + //! Get the opacity component of a colour. + /*! + @param iColour Colour to examine. + @return The opacity of the colour. + */ + static constexpr uint8_t get_alpha(argb_colour iColour) { + return static_cast((iColour >> 24) & 0xFF); + } - //! Get the opacity component of a colour. - /*! - @param iColour Colour to examine. - @return The opacity of the colour. - */ - static constexpr uint8_t get_alpha(argb_colour iColour) - { - return static_cast((iColour >> 24) & 0xFF); - } + //! Get the number of colours in the palette. + /*! + @return The number of colours in the palette. + */ + int get_colour_count() const; - //! Get the number of colours in the palette. - /*! - @return The number of colours in the palette. - */ - int get_colour_count() const; + //! Get the internal palette data for fast (read-only) access. + /*! + @return Table with all 256 colours of the palette. + */ + const argb_colour* get_argb_data() const; - //! Get the internal palette data for fast (read-only) access. - /*! - @return Table with all 256 colours of the palette. - */ - const argb_colour* get_argb_data() const; + //! Set an entry of the palette. + /*! + @param iEntry Entry to modify. + @param iVal Palette value to set. + */ + inline void set_argb(int iEntry, uint32_t iVal) { + colour_index_to_argb_map[iEntry] = iVal; + } - //! Set an entry of the palette. - /*! - @param iEntry Entry to modify. - @param iVal Palette value to set. - */ - inline void set_argb(int iEntry, uint32_t iVal) - { - colour_index_to_argb_map[iEntry] = iVal; - } + private: + //! 32bpp palette colours associated with the 8bpp colour index. + uint32_t colour_index_to_argb_map[256]; -private: - //! 32bpp palette colours associated with the 8bpp colour index. - uint32_t colour_index_to_argb_map[256]; - - //! Number of colours in the palette. - int colour_count; + //! Number of colours in the palette. + int colour_count; }; /*! Utility class for decoding 32bpp images. */ -class full_colour_renderer -{ -public: - //! Initialize the renderer for a specific render. - /*! - @param iWidth Pixel width of the resulting image - @param iHeight Pixel height of the resulting image - */ - full_colour_renderer(int iWidth, int iHeight); - virtual ~full_colour_renderer() = default; +class full_colour_renderer { + public: + //! Initialize the renderer for a specific render. + /*! + @param iWidth Pixel width of the resulting image + @param iHeight Pixel height of the resulting image + */ + full_colour_renderer(int iWidth, int iHeight); + virtual ~full_colour_renderer() = default; - //! Decode a 32bpp image, and push it to the storage backend. - /*! - @param pImg Encoded 32bpp image. - @param pPalette Palette of a legacy sprite. - @param iSpriteFlags Flags how to render the sprite. - @return Decoding was successful. - */ - void decode_image(const uint8_t* pImg, const ::palette *pPalette, uint32_t iSpriteFlags); + //! Decode a 32bpp image, and push it to the storage backend. + /*! + @param pImg Encoded 32bpp image. + @param pPalette Palette of a legacy sprite. + @param iSpriteFlags Flags how to render the sprite. + @return Decoding was successful. + */ + void decode_image(const uint8_t* pImg, const ::palette* pPalette, + uint32_t iSpriteFlags); -private: - //! Store a decoded pixel. Use x and y if necessary. - /*! - @param pixel Pixel to store. - */ - virtual void store_argb(uint32_t pixel) = 0; + private: + //! Store a decoded pixel. Use x and y if necessary. + /*! + @param pixel Pixel to store. + */ + virtual void store_argb(uint32_t pixel) = 0; - const int width; - const int height; - int x; - int y; + const int width; + const int height; + int x; + int y; - //! Push a pixel to the storage. - /*! - @param iValue Pixel value to store. - */ - inline void push_pixel(uint32_t iValue) - { - if (y < height) - { - store_argb(iValue); - x++; - if (x >= width) - { - x = 0; - y++; - } - } - else - { - throw std::logic_error("Attempt to push_pixel past the end of the image"); - } + //! Push a pixel to the storage. + /*! + @param iValue Pixel value to store. + */ + inline void push_pixel(uint32_t iValue) { + if (y < height) { + store_argb(iValue); + x++; + if (x >= width) { + x = 0; + y++; + } + } else { + throw std::logic_error("Attempt to push_pixel past the end of the image"); } + } }; -class full_colour_storing : public full_colour_renderer -{ -public: - full_colour_storing(uint32_t *pDest, int iWidth, int iHeight); +class full_colour_storing : public full_colour_renderer { + public: + full_colour_storing(uint32_t* pDest, int iWidth, int iHeight); -private: - void store_argb(uint32_t pixel) override; + private: + void store_argb(uint32_t pixel) override; - //! Pointer to the storage (not owned by this class). - uint32_t *destination; + //! Pointer to the storage (not owned by this class). + uint32_t* destination; }; -class wx_storing : public full_colour_renderer -{ -public: - wx_storing(uint8_t* pRGBData, uint8_t* pAData, int iWidth, int iHeight); +class wx_storing : public full_colour_renderer { + public: + wx_storing(uint8_t* pRGBData, uint8_t* pAData, int iWidth, int iHeight); -private: - void store_argb(uint32_t pixel) override; + private: + void store_argb(uint32_t pixel) override; - //! Pointer to the RGB storage (not owned by this class). - uint8_t *rgb_data; + //! Pointer to the RGB storage (not owned by this class). + uint8_t* rgb_data; - //! Pointer to the Alpha channel storage (not owned by this class). - uint8_t *alpha_data; + //! Pointer to the Alpha channel storage (not owned by this class). + uint8_t* alpha_data; }; -class render_target -{ -public: // External API - render_target(); - ~render_target(); +class render_target { + public: // External API + render_target(); + ~render_target(); - //! Encode an RGB triplet for fillRect() - static constexpr uint32_t map_colour(uint8_t iR, uint8_t iG, uint8_t iB) - { - return palette::pack_argb(0xFF, iR, iG, iB); - } + //! Encode an RGB triplet for fillRect() + static constexpr uint32_t map_colour(uint8_t iR, uint8_t iG, uint8_t iB) { + return palette::pack_argb(0xFF, iR, iG, iB); + } - //! Initialise the render target - bool create(const render_target_creation_params* pParams); + //! Initialise the render target + bool create(const render_target_creation_params* pParams); - //! Update the parameters for the render target - bool update(const render_target_creation_params* pParams); + //! Update the parameters for the render target + bool update(const render_target_creation_params* pParams); - //! Shut down the render target - void destroy(); + //! Shut down the render target + void destroy(); - //! Get the reason for the last operation failing - const char* get_last_error(); + //! Get the reason for the last operation failing + const char* get_last_error(); - //! Begin rendering a new frame - bool start_frame(); + //! Begin rendering a new frame + bool start_frame(); - //! Finish rendering the current frame and present it - bool end_frame(); + //! Finish rendering the current frame and present it + bool end_frame(); - //! Paint the entire render target black - bool fill_black(); + //! Paint the entire render target black + bool fill_black(); - //! Sets a blue filter on the current surface. - // Used to add the blue effect when the game is paused. - void set_blue_filter_active(bool bActivate); + //! Sets a blue filter on the current surface. + // Used to add the blue effect when the game is paused. + void set_blue_filter_active(bool bActivate); - //! Fill a rectangle of the render target with a solid colour - bool fill_rect(uint32_t iColour, int iX, int iY, int iW, int iH); + //! Fill a rectangle of the render target with a solid colour + bool fill_rect(uint32_t iColour, int iX, int iY, int iW, int iH); - //! Get the current clip rectangle - void get_clip_rect(clip_rect* pRect) const; + //! Get the current clip rectangle + void get_clip_rect(clip_rect* pRect) const; - //! Get the width of the render target (in pixels) - int get_width() const; + //! Get the width of the render target (in pixels) + int get_width() const; - //! Get the height of the render target (in pixels) - int get_height() const; + //! Get the height of the render target (in pixels) + int get_height() const; - //! Set the new clip rectangle - void set_clip_rect(const clip_rect* pRect); + //! Set the new clip rectangle + void set_clip_rect(const clip_rect* pRect); - //! Enable optimisations for non-overlapping draws - void start_nonoverlapping_draws(); + //! Enable optimisations for non-overlapping draws + void start_nonoverlapping_draws(); - //! Disable optimisations for non-overlapping draws - void finish_nonoverlapping_draws(); + //! Disable optimisations for non-overlapping draws + void finish_nonoverlapping_draws(); - //! Set the cursor to be used - void set_cursor(cursor* pCursor); + //! Set the cursor to be used + void set_cursor(cursor* pCursor); - //! Update the cursor position (if the cursor is being simulated) - void set_cursor_position(int iX, int iY); + //! Update the cursor position (if the cursor is being simulated) + void set_cursor_position(int iX, int iY); - //! Take a screenshot and save it as a bitmap - bool take_screenshot(const char* sFile); + //! Take a screenshot and save it as a bitmap + bool take_screenshot(const char* sFile); - //! Set the amount by which future draw operations are scaled. - /*! - @param fScale New scale to use. - @param eWhatToScale Th kind of items to scale. - @return Whether the scale could be set. - */ - bool set_scale_factor(double fScale, scaled_items eWhatToScale); + //! Set the amount by which future draw operations are scaled. + /*! + @param fScale New scale to use. + @param eWhatToScale Th kind of items to scale. + @return Whether the scale could be set. + */ + bool set_scale_factor(double fScale, scaled_items eWhatToScale); - //! Set the window caption - void set_caption(const char* sCaption); + //! Set the window caption + void set_caption(const char* sCaption); - //! Toggle mouse capture on the window. - void set_window_grab(bool bActivate); + //! Toggle mouse capture on the window. + void set_window_grab(bool bActivate); - //! Get any user-displayable information to describe the renderer path used - const char *get_renderer_details() const; + //! Get any user-displayable information to describe the renderer path used + const char* get_renderer_details() const; - // If you add any extra methods here which are called from outside the - // rendering engine, then be sure to at least add dummy implementations - // to the other rendering engines. + // If you add any extra methods here which are called from outside the + // rendering engine, then be sure to at least add dummy implementations + // to the other rendering engines. -public: // Internal (this rendering engine only) API - SDL_Renderer *get_renderer() const { return renderer; } + public: // Internal (this rendering engine only) API + SDL_Renderer* get_renderer() const { return renderer; } - //! Should bitmaps be scaled? - /*! - @param [out] pFactor If the function returns \c true, the factor to use - for scaling (can be \c nullptr if not interested in the value). - @return Whether bitmaps should be scaled. - */ - bool should_scale_bitmaps(double* pFactor); + //! Should bitmaps be scaled? + /*! + @param [out] pFactor If the function returns \c true, the factor to use + for scaling (can be \c nullptr if not interested in the value). + @return Whether bitmaps should be scaled. + */ + bool should_scale_bitmaps(double* pFactor); - SDL_Texture* create_palettized_texture(int iWidth, int iHeight, const uint8_t* pPixels, - const ::palette* pPalette, uint32_t iSpriteFlags) const; - SDL_Texture* create_texture(int iWidth, int iHeight, const uint32_t* pPixels) const; - void draw(SDL_Texture *pTexture, const SDL_Rect *prcSrcRect, const SDL_Rect *prcDstRect, int iFlags); - void draw_line(line *pLine, int iX, int iY); + SDL_Texture* create_palettized_texture(int iWidth, int iHeight, + const uint8_t* pPixels, + const ::palette* pPalette, + uint32_t iSpriteFlags) const; + SDL_Texture* create_texture(int iWidth, int iHeight, + const uint32_t* pPixels) const; + void draw(SDL_Texture* pTexture, const SDL_Rect* prcSrcRect, + const SDL_Rect* prcDstRect, int iFlags); + void draw_line(line* pLine, int iX, int iY); -private: - SDL_Window *window; - SDL_Renderer *renderer; - SDL_Texture *zoom_texture; - SDL_PixelFormat *pixel_format; - bool blue_filter_active; - cursor* game_cursor; - double bitmap_scale_factor; ///< Bitmap scale factor. - int width; - int height; - int cursor_x; - int cursor_y; - bool scale_bitmaps; ///< Whether bitmaps should be scaled. - bool supports_target_textures; + private: + SDL_Window* window; + SDL_Renderer* renderer; + SDL_Texture* zoom_texture; + SDL_PixelFormat* pixel_format; + bool blue_filter_active; + cursor* game_cursor; + double bitmap_scale_factor; ///< Bitmap scale factor. + int width; + int height; + int cursor_x; + int cursor_y; + bool scale_bitmaps; ///< Whether bitmaps should be scaled. + bool supports_target_textures; - // In SDL2 < 2.0.4 there is an issue with the y coordinates used for - // ClipRects in opengl and opengles. - // see: https://bugzilla.libsdl.org/show_bug.cgi?id=2700 - bool apply_opengl_clip_fix; + // In SDL2 < 2.0.4 there is an issue with the y coordinates used for + // ClipRects in opengl and opengles. + // see: https://bugzilla.libsdl.org/show_bug.cgi?id=2700 + bool apply_opengl_clip_fix; - void flush_zoom_buffer(); + void flush_zoom_buffer(); }; //! Stored image. -class raw_bitmap -{ -public: - raw_bitmap(); - ~raw_bitmap(); +class raw_bitmap { + public: + raw_bitmap(); + ~raw_bitmap(); - //! Set the palette of the image. - /*! - @param pPalette Palette to set for this image. - */ - void set_palette(const ::palette* pPalette); + //! Set the palette of the image. + /*! + @param pPalette Palette to set for this image. + */ + void set_palette(const ::palette* pPalette); - //! Load the image from the supplied pixel data. - /*! - Loader uses the palette supplied before. - @param pPixelData Image data loaded from a TH file. - @param iPixelDataLength Size of the loaded image data. - @param iWidth Width of the image. - @param pEventualCanvas Canvas to render the image to (eventually). - @return Loading was a success. - */ - void load_from_th_file(const uint8_t* pPixelData, size_t iPixelDataLength, - int iWidth, render_target *pEventualCanvas); + //! Load the image from the supplied pixel data. + /*! + Loader uses the palette supplied before. + @param pPixelData Image data loaded from a TH file. + @param iPixelDataLength Size of the loaded image data. + @param iWidth Width of the image. + @param pEventualCanvas Canvas to render the image to (eventually). + @return Loading was a success. + */ + void load_from_th_file(const uint8_t* pPixelData, size_t iPixelDataLength, + int iWidth, render_target* pEventualCanvas); - //! Draw the image at a given position at the given canvas. - /*! - @param pCanvas Canvas to draw at. - @param iX Destination x position. - @param iY Destination y position. - */ - void draw(render_target* pCanvas, int iX, int iY); + //! Draw the image at a given position at the given canvas. + /*! + @param pCanvas Canvas to draw at. + @param iX Destination x position. + @param iY Destination y position. + */ + void draw(render_target* pCanvas, int iX, int iY); - //! Draw part of the image at a given position at the given canvas. - /*! - @param pCanvas Canvas to draw at. - @param iX Destination x position. - @param iY Destination y position. - @param iSrcX X position of the part to display. - @param iSrcY Y position of the part to display. - @param iWidth Width of the part to display. - @param iHeight Height of the part to display. - */ - void draw(render_target* pCanvas, int iX, int iY, int iSrcX, int iSrcY, - int iWidth, int iHeight); + //! Draw part of the image at a given position at the given canvas. + /*! + @param pCanvas Canvas to draw at. + @param iX Destination x position. + @param iY Destination y position. + @param iSrcX X position of the part to display. + @param iSrcY Y position of the part to display. + @param iWidth Width of the part to display. + @param iHeight Height of the part to display. + */ + void draw(render_target* pCanvas, int iX, int iY, int iSrcX, int iSrcY, + int iWidth, int iHeight); -private: - //! Image stored in SDL format for quick rendering. - SDL_Texture *texture; + private: + //! Image stored in SDL format for quick rendering. + SDL_Texture* texture; - //! Palette of the image. - const ::palette* bitmap_palette; + //! Palette of the image. + const ::palette* bitmap_palette; - //! Target canvas. - render_target* target; + //! Target canvas. + render_target* target; - //! Width of the stored image. - int width; + //! Width of the stored image. + int width; - //! Height of the stored image. - int height; + //! Height of the stored image. + int height; }; //! Sheet of sprites. -class sprite_sheet -{ -public: // External API - sprite_sheet(); - ~sprite_sheet(); +class sprite_sheet { + public: // External API + sprite_sheet(); + ~sprite_sheet(); - //! Set the palette to use for the sprites in the sheet. - /*! - @param pPalette Palette to use for the sprites at the sheet. - */ - void set_palette(const ::palette* pPalette); + //! Set the palette to use for the sprites in the sheet. + /*! + @param pPalette Palette to use for the sprites at the sheet. + */ + void set_palette(const ::palette* pPalette); - //! Load the sprites from the supplied data (using the palette supplied earlier). - /*! - @param pTableData Start of table data with TH sprite information (see th_sprite_properties). - @param iTableDataLength Length of the table data. - @param pChunkData Start of image data (chunks). - @param iChunkDataLength Length of the chunk data. - @param bComplexChunks Whether the supplied chunks are 'complex'. - @param pEventualCanvas Canvas to draw at. - @return Loading succeeded. - */ - bool load_from_th_file(const uint8_t* pTableData, size_t iTableDataLength, - const uint8_t* pChunkData, size_t iChunkDataLength, - bool bComplexChunks, render_target* pEventualCanvas); + //! Load the sprites from the supplied data (using the palette supplied + //! earlier). + /*! + @param pTableData Start of table data with TH sprite information (see + th_sprite_properties). + @param iTableDataLength Length of the table data. + @param pChunkData Start of image data (chunks). + @param iChunkDataLength Length of the chunk data. + @param bComplexChunks Whether the supplied chunks are 'complex'. + @param pEventualCanvas Canvas to draw at. + @return Loading succeeded. + */ + bool load_from_th_file(const uint8_t* pTableData, size_t iTableDataLength, + const uint8_t* pChunkData, size_t iChunkDataLength, + bool bComplexChunks, render_target* pEventualCanvas); - //! Set the data of a sprite. - /*! - @param iSprite Number of the sprite to set. - @param pData Data of the sprite. - @param bTakeData Whether the data block may be taken (must be new[] then). - @param iDataLength Length of the data. - @param iWidth Width of the sprite. - @param iHeight Height of the sprite. - @return Setting the sprite succeeded. - */ - bool set_sprite_data(size_t iSprite, const uint8_t *pData, bool bTakeData, - size_t iDataLength, int iWidth, int iHeight); + //! Set the data of a sprite. + /*! + @param iSprite Number of the sprite to set. + @param pData Data of the sprite. + @param bTakeData Whether the data block may be taken (must be new[] + then). + @param iDataLength Length of the data. + @param iWidth Width of the sprite. + @param iHeight Height of the sprite. + @return Setting the sprite succeeded. + */ + bool set_sprite_data(size_t iSprite, const uint8_t* pData, bool bTakeData, + size_t iDataLength, int iWidth, int iHeight); - //! Supply a new mapped palette to a sprite. - /*! - @param iSprite Sprite getting the mapped palette. - @param pMap The palette map to apply. - @param iAlt32 What to do for a 32bpp sprite (#thdf_alt32_mask bits). - */ - void set_sprite_alt_palette_map(size_t iSprite, const uint8_t* pMap, uint32_t iAlt32); + //! Supply a new mapped palette to a sprite. + /*! + @param iSprite Sprite getting the mapped palette. + @param pMap The palette map to apply. + @param iAlt32 What to do for a 32bpp sprite (#thdf_alt32_mask bits). + */ + void set_sprite_alt_palette_map(size_t iSprite, const uint8_t* pMap, + uint32_t iAlt32); - //! Get the number of sprites at the sheet. - /*! - @return The number of sprites available at the sheet. - */ - size_t get_sprite_count() const; + //! Get the number of sprites at the sheet. + /*! + @return The number of sprites available at the sheet. + */ + size_t get_sprite_count() const; - //! Set the number of sprites in the sheet. - /*! - @param iCount The desired number of sprites. - @param pCanvas Canvas to draw at. - @return Whether the number of sprites could be allocated. - */ - bool set_sprite_count(size_t iCount, render_target* pCanvas); + //! Set the number of sprites in the sheet. + /*! + @param iCount The desired number of sprites. + @param pCanvas Canvas to draw at. + @return Whether the number of sprites could be allocated. + */ + bool set_sprite_count(size_t iCount, render_target* pCanvas); - //! Get size of a sprite. - /*! - @param iSprite Sprite to get info from. - @param pWidth [out] If not nullptr, the sprite width is stored in the destination. - @param pHeight [out] If not nullptr, the sprite height is stored in the destination. - @return Size could be provided for the sprite. - */ - bool get_sprite_size(size_t iSprite, unsigned int* pWidth, unsigned int* pHeight) const; + //! Get size of a sprite. + /*! + @param iSprite Sprite to get info from. + @param pWidth [out] If not nullptr, the sprite width is stored in the + destination. + @param pHeight [out] If not nullptr, the sprite height is stored in the + destination. + @return Size could be provided for the sprite. + */ + bool get_sprite_size(size_t iSprite, unsigned int* pWidth, + unsigned int* pHeight) const; - //! Get size of a sprite, assuming all input is correctly supplied. - /*! - @param iSprite Sprite to get info from. - @param pWidth [out] The sprite width is stored in the destination. - @param pHeight [out] The sprite height is stored in the destination. - */ - void get_sprite_size_unchecked(size_t iSprite, unsigned int* pWidth, unsigned int* pHeight) const; + //! Get size of a sprite, assuming all input is correctly supplied. + /*! + @param iSprite Sprite to get info from. + @param pWidth [out] The sprite width is stored in the destination. + @param pHeight [out] The sprite height is stored in the destination. + */ + void get_sprite_size_unchecked(size_t iSprite, unsigned int* pWidth, + unsigned int* pHeight) const; - //! Get the best colour to represent the sprite. - /*! - @param iSprite Sprite number to analyze. - @param pColour [out] Resulting colour. - @return Best colour could be established. - */ - bool get_sprite_average_colour(size_t iSprite, argb_colour* pColour) const; + //! Get the best colour to represent the sprite. + /*! + @param iSprite Sprite number to analyze. + @param pColour [out] Resulting colour. + @return Best colour could be established. + */ + bool get_sprite_average_colour(size_t iSprite, argb_colour* pColour) const; - //! Draw a sprite onto the canvas. - /*! - @param pCanvas Canvas to draw on. - @param iSprite Sprite to draw. - @param iX X position to draw the sprite. - @param iY Y position to draw the sprite. - @param iFlags Flags to apply for drawing. - */ - void draw_sprite(render_target* pCanvas, size_t iSprite, int iX, int iY, uint32_t iFlags); + //! Draw a sprite onto the canvas. + /*! + @param pCanvas Canvas to draw on. + @param iSprite Sprite to draw. + @param iX X position to draw the sprite. + @param iY Y position to draw the sprite. + @param iFlags Flags to apply for drawing. + */ + void draw_sprite(render_target* pCanvas, size_t iSprite, int iX, int iY, + uint32_t iFlags); - //! Test whether a sprite was hit. - /*! - @param iSprite Sprite being tested. - @param iX X position of the point to test relative to the origin of the sprite. - @param iY Y position of the point to test relative to the origin of the sprite. - @param iFlags Draw flags to apply to the sprite before testing. - @return Whether the sprite covers the give point. - */ - bool hit_test_sprite(size_t iSprite, int iX, int iY, uint32_t iFlags) const; + //! Test whether a sprite was hit. + /*! + @param iSprite Sprite being tested. + @param iX X position of the point to test relative to the origin of the + sprite. + @param iY Y position of the point to test relative to the origin of the + sprite. + @param iFlags Draw flags to apply to the sprite before testing. + @return Whether the sprite covers the give point. + */ + bool hit_test_sprite(size_t iSprite, int iX, int iY, uint32_t iFlags) const; -public: // Internal (this rendering engine only) API - //! Draw a sprite into wxImage data arrays (for the Map Editor) - /*! - @param iSprite Sprite number to draw. - @param pRGBData Output RGB data array. - @param pAData Output Alpha channel array. - */ - void wx_draw_sprite(size_t iSprite, uint8_t* pRGBData, uint8_t* pAData); + public: // Internal (this rendering engine only) API + //! Draw a sprite into wxImage data arrays (for the Map Editor) + /*! + @param iSprite Sprite number to draw. + @param pRGBData Output RGB data array. + @param pAData Output Alpha channel array. + */ + void wx_draw_sprite(size_t iSprite, uint8_t* pRGBData, uint8_t* pAData); -private: - friend class cursor; + private: + friend class cursor; #if CORSIX_TH_USE_PACK_PRAGMAS #pragma pack(push) #pragma pack(1) #endif - //! Sprite structure in the table file. - struct th_sprite_properties - { - //! Position of the sprite in the chunk data file. - uint32_t position; + //! Sprite structure in the table file. + struct th_sprite_properties { + //! Position of the sprite in the chunk data file. + uint32_t position; - //! Width of the sprite. - uint8_t width; + //! Width of the sprite. + uint8_t width; - //! Height of the sprite. - uint8_t height; - } CORSIX_TH_PACKED_FLAGS; + //! Height of the sprite. + uint8_t height; + } CORSIX_TH_PACKED_FLAGS; #if CORSIX_TH_USE_PACK_PRAGMAS #pragma pack(pop) #endif - //! Sprites of the sheet. - struct sprite - { - //! SDL structure containing the sprite with original palette. - SDL_Texture *texture; + //! Sprites of the sheet. + struct sprite { + //! SDL structure containing the sprite with original palette. + SDL_Texture* texture; - //! SDL structure containing the sprite with alternative palette. - SDL_Texture *alt_texture; + //! SDL structure containing the sprite with alternative palette. + SDL_Texture* alt_texture; - //! Data of the sprite. - const uint8_t *data; + //! Data of the sprite. + const uint8_t* data; - //! Alternative palette (if available). - const uint8_t *alt_palette_map; + //! Alternative palette (if available). + const uint8_t* alt_palette_map; - //! Flags how to render the sprite, contains #THDF_Alt32_Mask bits. - uint32_t sprite_flags; + //! Flags how to render the sprite, contains #THDF_Alt32_Mask bits. + uint32_t sprite_flags; - //! Width of the sprite. - int width; + //! Width of the sprite. + int width; - //! Height of the sprite. - int height; - } *sprites; + //! Height of the sprite. + int height; + } * sprites; - //! Original palette. - const ::palette* palette; + //! Original palette. + const ::palette* palette; - //! Target to render to. - render_target* target; + //! Target to render to. + render_target* target; - //! Number of sprites in the sprite sheet. - size_t sprite_count; + //! Number of sprites in the sprite sheet. + size_t sprite_count; - //! Free memory of a single sprite. - /*! - @param iNumber Number of the sprite to clear. - */ - void _freeSingleSprite(size_t iNumber); + //! Free memory of a single sprite. + /*! + @param iNumber Number of the sprite to clear. + */ + void _freeSingleSprite(size_t iNumber); - //! Free the memory used by the sprites. Also releases the SDL bitmaps. - void _freeSprites(); + //! Free the memory used by the sprites. Also releases the SDL bitmaps. + void _freeSprites(); - //! Construct an alternative version (with its alternative palette map) of the sprite. - /*! - @param pSprite Sprite to change. - @return SDL texture containing the sprite. - */ - SDL_Texture *_makeAltBitmap(sprite *pSprite); + //! Construct an alternative version (with its alternative palette map) of + //! the sprite. + /*! + @param pSprite Sprite to change. + @return SDL texture containing the sprite. + */ + SDL_Texture* _makeAltBitmap(sprite* pSprite); }; -class cursor -{ -public: - cursor(); - ~cursor(); +class cursor { + public: + cursor(); + ~cursor(); - bool create_from_sprite(sprite_sheet* pSheet, size_t iSprite, - int iHotspotX = 0, int iHotspotY = 0); + bool create_from_sprite(sprite_sheet* pSheet, size_t iSprite, + int iHotspotX = 0, int iHotspotY = 0); - void use(render_target* pTarget); + void use(render_target* pTarget); - static bool set_position(render_target* pTarget, int iX, int iY); + static bool set_position(render_target* pTarget, int iX, int iY); - void draw(render_target* pCanvas, int iX, int iY); -private: - SDL_Surface* bitmap; - SDL_Cursor* hidden_cursor; - int hotspot_x; - int hotspot_y; + void draw(render_target* pCanvas, int iX, int iY); + + private: + SDL_Surface* bitmap; + SDL_Cursor* hidden_cursor; + int hotspot_x; + int hotspot_y; }; +class line { + public: + line(); + ~line(); -class line -{ -public: - line(); - ~line(); + void move_to(double fX, double fY); - void move_to(double fX, double fY); + void line_to(double fX, double fY); - void line_to(double fX, double fY); + void set_width(double lineWidth); - void set_width(double lineWidth); + void draw(render_target* pCanvas, int iX, int iY); - void draw(render_target* pCanvas, int iX, int iY); + void set_colour(uint8_t iR, uint8_t iG, uint8_t iB, uint8_t iA = 255); - void set_colour(uint8_t iR, uint8_t iG, uint8_t iB, uint8_t iA = 255); + void persist(lua_persist_writer* pWriter) const; + void depersist(lua_persist_reader* pReader); - void persist(lua_persist_writer *pWriter) const; - void depersist(lua_persist_reader *pReader); + private: + friend class render_target; + void initialize(); -private: - friend class render_target; - void initialize(); + enum class line_operation_type { move, line }; - enum class line_operation_type { - move, - line - }; + class line_operation : public link_list { + public: + line_operation_type type; + double x, y; + line_operation(line_operation_type type, double x, double y) + : type(type), x(x), y(y) { + next = nullptr; + } + }; - class line_operation : public link_list - { - public: - line_operation_type type; - double x, y; - line_operation(line_operation_type type, double x, double y) : type(type), x(x), y(y) { - next = nullptr; - } - }; - - line_operation* first_operation; - line_operation* current_operation; - double width; - uint8_t red, green, blue, alpha; + line_operation* first_operation; + line_operation* current_operation; + double width; + uint8_t red, green, blue, alpha; }; -#endif // CORSIX_TH_TH_GFX_SDL_H_ +#endif // CORSIX_TH_TH_GFX_SDL_H_ diff --git a/CorsixTH/Src/th_lua.cpp b/CorsixTH/Src/th_lua.cpp index 3eb906ad..e575b184 100644 --- a/CorsixTH/Src/th_lua.cpp +++ b/CorsixTH/Src/th_lua.cpp @@ -20,405 +20,357 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +#include +#include +#include +#include "bootstrap.h" #include "th.h" #include "th_lua_internal.h" -#include "bootstrap.h" -#include -#include -#include -void lua_register_anims(const lua_register_state *pState); -void lua_register_gfx(const lua_register_state *pState); -void lua_register_map(const lua_register_state *pState); -void lua_register_sound(const lua_register_state *pState); -void lua_register_movie(const lua_register_state *pState); -void lua_register_strings(const lua_register_state *pState); -void lua_register_ui(const lua_register_state *pState); -void lua_register_lfs_ext(const lua_register_state *pState); -void lua_register_iso_fs(const lua_register_state *pState); +void lua_register_anims(const lua_register_state* pState); +void lua_register_gfx(const lua_register_state* pState); +void lua_register_map(const lua_register_state* pState); +void lua_register_sound(const lua_register_state* pState); +void lua_register_movie(const lua_register_state* pState); +void lua_register_strings(const lua_register_state* pState); +void lua_register_ui(const lua_register_state* pState); +void lua_register_lfs_ext(const lua_register_state* pState); +void lua_register_iso_fs(const lua_register_state* pState); //! Set a field on the environment table of an object -void luaT_setenvfield(lua_State *L, int index, const char *k) -{ - lua_getfenv(L, index); - lua_pushstring(L, k); - lua_pushvalue(L, -3); - lua_settable(L, -3); - lua_pop(L, 2); +void luaT_setenvfield(lua_State* L, int index, const char* k) { + lua_getfenv(L, index); + lua_pushstring(L, k); + lua_pushvalue(L, -3); + lua_settable(L, -3); + lua_pop(L, 2); } //! Get a field from the environment table of an object -void luaT_getenvfield(lua_State *L, int index, const char *k) -{ - lua_getfenv(L, index); - lua_getfield(L, -1, k); - lua_replace(L, -2); +void luaT_getenvfield(lua_State* L, int index, const char* k) { + lua_getfenv(L, index); + lua_getfield(L, -1, k); + lua_replace(L, -2); } #if LUA_VERSION_NUM >= 502 -void luaT_getfenv52(lua_State *L, int iIndex) -{ - int iType = lua_type(L, iIndex); - switch(iType) - { +void luaT_getfenv52(lua_State* L, int iIndex) { + int iType = lua_type(L, iIndex); + switch (iType) { case LUA_TUSERDATA: - lua_getuservalue(L, iIndex); - break; + lua_getuservalue(L, iIndex); + break; case LUA_TFUNCTION: - if(lua_iscfunction(L, iIndex)) - { - // Our convention: upvalue at #1 is environment - if(lua_getupvalue(L, iIndex, 1) == nullptr) - lua_pushglobaltable(L); + if (lua_iscfunction(L, iIndex)) { + // Our convention: upvalue at #1 is environment + if (lua_getupvalue(L, iIndex, 1) == nullptr) lua_pushglobaltable(L); + } else { + // Language convention: upvalue called _ENV is environment + const char* sUpName = nullptr; + for (int i = 1; (sUpName = lua_getupvalue(L, iIndex, i)); ++i) { + if (std::strcmp(sUpName, "_ENV") == 0) + return; + else + lua_pop(L, 1); } - else - { - // Language convention: upvalue called _ENV is environment - const char* sUpName = nullptr; - for(int i = 1; (sUpName = lua_getupvalue(L, iIndex, i)) ; ++i) - { - if(std::strcmp(sUpName, "_ENV") == 0) - return; - else - lua_pop(L, 1); - } - lua_pushglobaltable(L); - } - break; + lua_pushglobaltable(L); + } + break; default: - luaL_error(L, "Unable to get environment of a %s in 5.2", lua_typename(L, iType)); - break; - } + luaL_error(L, "Unable to get environment of a %s in 5.2", + lua_typename(L, iType)); + break; + } } -int luaT_setfenv52(lua_State *L, int iIndex) -{ - int iType = lua_type(L, iIndex); - switch(iType) - { +int luaT_setfenv52(lua_State* L, int iIndex) { + int iType = lua_type(L, iIndex); + switch (iType) { case LUA_TUSERDATA: - lua_setuservalue(L, iIndex); - return 1; + lua_setuservalue(L, iIndex); + return 1; case LUA_TFUNCTION: - if(lua_iscfunction(L, iIndex)) - { - // Our convention: upvalue at #1 is environment - if(lua_setupvalue(L, iIndex, 1) == nullptr) - { - lua_pop(L, 1); - return 0; - } - return 1; + if (lua_iscfunction(L, iIndex)) { + // Our convention: upvalue at #1 is environment + if (lua_setupvalue(L, iIndex, 1) == nullptr) { + lua_pop(L, 1); + return 0; } - else - { - // Language convention: upvalue called _ENV is environment, which - // might be shared with other functions. - const char* sUpName = nullptr; - for(int i = 1; (sUpName = lua_getupvalue(L, iIndex, i)) ; ++i) - { - lua_pop(L, 1); // lua_getupvalue puts the value on the stack, but we just want to replace it - if(std::strcmp(sUpName, "_ENV") == 0) - { - luaL_loadstring(L, "local upv = ... return function() return upv end"); - lua_insert(L, -2); - lua_call(L, 1, 1); - lua_upvaluejoin(L, iIndex, i, -1, 1); - lua_pop(L, 1); - return 1; - } - } + return 1; + } else { + // Language convention: upvalue called _ENV is environment, + // which might be shared with other functions. + const char* sUpName = nullptr; + for (int i = 1; (sUpName = lua_getupvalue(L, iIndex, i)); ++i) { + lua_pop(L, 1); // lua_getupvalue puts the value on the + // stack, but we just want to replace it + if (std::strcmp(sUpName, "_ENV") == 0) { + luaL_loadstring(L, + "local upv = ... return function() return upv " + "end"); + lua_insert(L, -2); + lua_call(L, 1, 1); + lua_upvaluejoin(L, iIndex, i, -1, 1); lua_pop(L, 1); - return 0; + return 1; + } } - default: + lua_pop(L, 1); return 0; - } + } + default: + return 0; + } } #endif //! Push a C closure as a callable table -void luaT_pushcclosuretable(lua_State *L, lua_CFunction fn, int n) -{ - luaT_pushcclosure(L, fn, n); // .. fn (lua_touserdata(L, idx)); - iLength = lua_objlen(L, idx); - } - else - { - pData = reinterpret_cast(luaL_checklstring(L, idx, &iLength)); - } - if(pDataLen != 0) - *pDataLen = iLength; - return pData; +const uint8_t* luaT_checkfile(lua_State* L, int idx, size_t* pDataLen) { + const uint8_t* pData; + size_t iLength; + if (lua_type(L, idx) == LUA_TUSERDATA) { + pData = reinterpret_cast(lua_touserdata(L, idx)); + iLength = lua_objlen(L, idx); + } else { + pData = + reinterpret_cast(luaL_checklstring(L, idx, &iLength)); + } + if (pDataLen != 0) *pDataLen = iLength; + return pData; } namespace { -int l_load_strings(lua_State *L) -{ - size_t iDataLength; - const uint8_t* pData = luaT_checkfile(L, 1, &iDataLength); +int l_load_strings(lua_State* L) { + size_t iDataLength; + const uint8_t* pData = luaT_checkfile(L, 1, &iDataLength); - try - { - th_string_list oStrings(pData, iDataLength); - lua_settop(L, 0); - lua_createtable(L, static_cast(oStrings.get_section_count()), 0); - for(size_t iSec = 0; iSec < oStrings.get_section_count(); ++iSec) - { - size_t iCount = oStrings.get_section_size(iSec); - lua_createtable(L, static_cast(iCount), 0); - for(size_t iStr = 0; iStr < iCount; ++iStr) - { - lua_pushstring(L, oStrings.get_string(iSec, iStr)); - lua_rawseti(L, 2, static_cast(iStr + 1)); - } - lua_rawseti(L, 1, static_cast(iSec + 1)); - } + try { + th_string_list oStrings(pData, iDataLength); + lua_settop(L, 0); + lua_createtable(L, static_cast(oStrings.get_section_count()), 0); + for (size_t iSec = 0; iSec < oStrings.get_section_count(); ++iSec) { + size_t iCount = oStrings.get_section_size(iSec); + lua_createtable(L, static_cast(iCount), 0); + for (size_t iStr = 0; iStr < iCount; ++iStr) { + lua_pushstring(L, oStrings.get_string(iSec, iStr)); + lua_rawseti(L, 2, static_cast(iStr + 1)); + } + lua_rawseti(L, 1, static_cast(iSec + 1)); } - catch(std::invalid_argument) - { - lua_pushboolean(L, 0); - } - return 1; + } catch (std::invalid_argument) { + lua_pushboolean(L, 0); + } + return 1; } -int get_api_version() -{ +int get_api_version() { #include "../Lua/api_version.lua" } -int l_get_compile_options(lua_State *L) -{ - lua_settop(L, 0); - lua_newtable(L); +int l_get_compile_options(lua_State* L) { + lua_settop(L, 0); + lua_newtable(L); #ifdef CORSIX_TH_64BIT - lua_pushboolean(L, 1); + lua_pushboolean(L, 1); #else - lua_pushboolean(L, 0); + lua_pushboolean(L, 0); #endif - lua_setfield(L, -2, "arch_64"); + lua_setfield(L, -2, "arch_64"); - lua_pushliteral(L, "SDL"); - lua_setfield(L, -2, "renderer"); + lua_pushliteral(L, "SDL"); + lua_setfield(L, -2, "renderer"); #ifdef CORSIX_TH_USE_SDL_MIXER - lua_pushboolean(L, 1); + lua_pushboolean(L, 1); #else - lua_pushboolean(L, 0); + lua_pushboolean(L, 0); #endif - lua_setfield(L, -2, "audio"); + lua_setfield(L, -2, "audio"); - lua_getfield(L, LUA_REGISTRYINDEX, "_LOADED"); - lua_getfield(L, -1, "jit"); - if(lua_type(L, -1) == LUA_TNIL) - { - lua_replace(L, -2); - } - else - { - lua_getfield(L, -1, "version"); - lua_replace(L, -3); - lua_pop(L, 1); - } - lua_setfield(L, -2, "jit"); - - lua_pushinteger(L, get_api_version()); - lua_setfield(L, -2, "api_version"); - - return 1; -} - -} // namespace - -void luaT_setclosure(const lua_register_state *pState, lua_CFunction fn, size_t iUps) { - luaT_pushcclosure(pState->L, fn, iUps); -} - -int luaopen_th(lua_State *L) -{ - lua_settop(L, 0); - lua_checkstack(L, 16 + static_cast(lua_metatable::count)); - - lua_register_state oState; - const lua_register_state *pState = &oState; - oState.L = L; - for(int i = 0; i < static_cast(lua_metatable::count); ++i) - { - lua_createtable(L, 0, 5); - oState.metatables[i] = lua_gettop(L); - } - lua_createtable(L, 0, lua_gettop(L)); - oState.main_table = lua_gettop(L); - oState.top = lua_gettop(L); - - // Misc. functions - lua_settop(L, oState.top); - add_lua_function(pState, l_load_strings, "LoadStrings"); - add_lua_function(pState, l_get_compile_options, "GetCompileOptions"); - add_lua_function(pState, bootstrap_lua_resources, "GetBuiltinFont"); - - // Classes - lua_register_map(pState); - lua_register_gfx(pState); - lua_register_anims(pState); - lua_register_sound(pState); - lua_register_movie(pState); - lua_register_strings(pState); - lua_register_ui(pState); - lua_register_lfs_ext(pState); - lua_register_iso_fs(pState); - - lua_settop(L, oState.main_table); - return 1; -} - -void luaT_execute_loadstring(lua_State *L, const char* sLuaString) -{ - static const int iRegistryCacheIndex = 7; - lua_rawgeti(L, LUA_REGISTRYINDEX, iRegistryCacheIndex); - if(lua_isnil(L, -1)) - { - // Cache not yet created - create it. - lua_pop(L, 1); - lua_getglobal(L, "setmetatable"); - if(lua_isnil(L, -1)) - { - // Base library not yet loaded - fallback to simple - // uncached loadstring - lua_pop(L, 1); - if(luaL_loadstring(L, sLuaString)) - lua_error(L); - } - lua_pop(L, 1); -#if LUA_VERSION_NUM >= 502 - luaL_loadstring(L, "local assert, load = assert, load\n" - "return setmetatable({}, {__mode = [[v]], \n" - "__index = function(t, k)\n" - "local v = assert(load(k))\n" - "t[k] = v\n" - "return v\n" - "end})"); -#else - luaL_loadstring(L, "local assert, loadstring = assert, loadstring\n" - "return setmetatable({}, {__mode = [[v]], \n" - "__index = function(t, k)\n" - "local v = assert(loadstring(k))\n" - "t[k] = v\n" - "return v\n" - "end})"); -#endif - lua_call(L, 0, 1); - lua_pushvalue(L, -1); - lua_rawseti(L, LUA_REGISTRYINDEX, iRegistryCacheIndex); - } - lua_getfield(L, -1, sLuaString); + lua_getfield(L, LUA_REGISTRYINDEX, "_LOADED"); + lua_getfield(L, -1, "jit"); + if (lua_type(L, -1) == LUA_TNIL) { lua_replace(L, -2); + } else { + lua_getfield(L, -1, "version"); + lua_replace(L, -3); + lua_pop(L, 1); + } + lua_setfield(L, -2, "jit"); + + lua_pushinteger(L, get_api_version()); + lua_setfield(L, -2, "api_version"); + + return 1; } -void luaT_execute(lua_State *L, const char* sLuaString) -{ - luaT_execute_loadstring(L, sLuaString); - lua_call(L, 0, LUA_MULTRET); +} // namespace + +void luaT_setclosure(const lua_register_state* pState, lua_CFunction fn, + size_t iUps) { + luaT_pushcclosure(pState->L, fn, iUps); } -void luaT_push(lua_State *L, lua_CFunction f) -{ - luaT_pushcfunction(L, f); +int luaopen_th(lua_State* L) { + lua_settop(L, 0); + lua_checkstack(L, 16 + static_cast(lua_metatable::count)); + + lua_register_state oState; + const lua_register_state* pState = &oState; + oState.L = L; + for (int i = 0; i < static_cast(lua_metatable::count); ++i) { + lua_createtable(L, 0, 5); + oState.metatables[i] = lua_gettop(L); + } + lua_createtable(L, 0, lua_gettop(L)); + oState.main_table = lua_gettop(L); + oState.top = lua_gettop(L); + + // Misc. functions + lua_settop(L, oState.top); + add_lua_function(pState, l_load_strings, "LoadStrings"); + add_lua_function(pState, l_get_compile_options, "GetCompileOptions"); + add_lua_function(pState, bootstrap_lua_resources, "GetBuiltinFont"); + + // Classes + lua_register_map(pState); + lua_register_gfx(pState); + lua_register_anims(pState); + lua_register_sound(pState); + lua_register_movie(pState); + lua_register_strings(pState); + lua_register_ui(pState); + lua_register_lfs_ext(pState); + lua_register_iso_fs(pState); + + lua_settop(L, oState.main_table); + return 1; } -void luaT_push(lua_State *L, int i) -{ - lua_pushinteger(L, (lua_Integer)i); -} - -void luaT_push(lua_State *L, const char* s) -{ - lua_pushstring(L, s); -} - -void luaT_pushtablebool(lua_State *L, const char *k, bool v) -{ - lua_pushstring(L, k); - lua_pushboolean(L, v); - lua_settable(L, -3); -} - -void luaT_printstack(lua_State* L) -{ - int i; - int top = lua_gettop(L); - - std::printf("total items in stack %d\n", top); - - for (i = 1; i <= top; i++) - { /* repeat for each level */ - int t = lua_type(L, i); - switch (t) { - case LUA_TSTRING: /* strings */ - std::printf("string: '%s'\n", lua_tostring(L, i)); - break; - case LUA_TBOOLEAN: /* booleans */ - std::printf("boolean %s\n", lua_toboolean(L, i) ? "true" : "false"); - break; - case LUA_TNUMBER: /* numbers */ - std::printf("number: %g\n", lua_tonumber(L, i)); - break; - default: /* other values */ - std::printf("%s\n", lua_typename(L, t)); - break; - } - std::printf(" "); /* put a separator */ - +void luaT_execute_loadstring(lua_State* L, const char* sLuaString) { + static const int iRegistryCacheIndex = 7; + lua_rawgeti(L, LUA_REGISTRYINDEX, iRegistryCacheIndex); + if (lua_isnil(L, -1)) { + // Cache not yet created - create it. + lua_pop(L, 1); + lua_getglobal(L, "setmetatable"); + if (lua_isnil(L, -1)) { + // Base library not yet loaded - fallback to simple + // uncached loadstring + lua_pop(L, 1); + if (luaL_loadstring(L, sLuaString)) lua_error(L); } - std::printf("\n"); /* end the listing */ + lua_pop(L, 1); +#if LUA_VERSION_NUM >= 502 + luaL_loadstring(L, + "local assert, load = assert, load\n" + "return setmetatable({}, {__mode = [[v]], \n" + "__index = function(t, k)\n" + "local v = assert(load(k))\n" + "t[k] = v\n" + "return v\n" + "end})"); +#else + luaL_loadstring(L, + "local assert, loadstring = assert, loadstring\n" + "return setmetatable({}, {__mode = [[v]], \n" + "__index = function(t, k)\n" + "local v = assert(loadstring(k))\n" + "t[k] = v\n" + "return v\n" + "end})"); +#endif + lua_call(L, 0, 1); + lua_pushvalue(L, -1); + lua_rawseti(L, LUA_REGISTRYINDEX, iRegistryCacheIndex); + } + lua_getfield(L, -1, sLuaString); + lua_replace(L, -2); } -void luaT_printrawtable(lua_State* L, int idx) -{ - int i; - int len = static_cast(lua_objlen(L, idx)); +void luaT_execute(lua_State* L, const char* sLuaString) { + luaT_execute_loadstring(L, sLuaString); + lua_call(L, 0, LUA_MULTRET); +} - std::printf("total items in table %d\n", len); +void luaT_push(lua_State* L, lua_CFunction f) { luaT_pushcfunction(L, f); } - for (i = 1; i <= len; i++) - { - lua_rawgeti(L, idx, i); - int t = lua_type(L, -1); - switch (t) { - case LUA_TSTRING: /* strings */ - std::printf("string: '%s'\n", lua_tostring(L, -1)); - break; - case LUA_TBOOLEAN: /* booleans */ - std::printf("boolean %s\n", lua_toboolean(L, -1) ? "true" : "false"); - break; - case LUA_TNUMBER: /* numbers */ - std::printf("number: %g\n", lua_tonumber(L, -1)); - break; - default: /* other values */ - std::printf("%s\n", lua_typename(L, t)); - break; - } - std::printf(" "); /* put a separator */ - lua_pop(L, 1); +void luaT_push(lua_State* L, int i) { lua_pushinteger(L, (lua_Integer)i); } + +void luaT_push(lua_State* L, const char* s) { lua_pushstring(L, s); } + +void luaT_pushtablebool(lua_State* L, const char* k, bool v) { + lua_pushstring(L, k); + lua_pushboolean(L, v); + lua_settable(L, -3); +} + +void luaT_printstack(lua_State* L) { + int i; + int top = lua_gettop(L); + + std::printf("total items in stack %d\n", top); + + for (i = 1; i <= top; i++) { /* repeat for each level */ + int t = lua_type(L, i); + switch (t) { + case LUA_TSTRING: /* strings */ + std::printf("string: '%s'\n", lua_tostring(L, i)); + break; + case LUA_TBOOLEAN: /* booleans */ + std::printf("boolean %s\n", lua_toboolean(L, i) ? "true" : "false"); + break; + case LUA_TNUMBER: /* numbers */ + std::printf("number: %g\n", lua_tonumber(L, i)); + break; + default: /* other values */ + std::printf("%s\n", lua_typename(L, t)); + break; } - std::printf("\n"); /* end the listing */ + std::printf(" "); /* put a separator */ + } + std::printf("\n"); /* end the listing */ +} + +void luaT_printrawtable(lua_State* L, int idx) { + int i; + int len = static_cast(lua_objlen(L, idx)); + + std::printf("total items in table %d\n", len); + + for (i = 1; i <= len; i++) { + lua_rawgeti(L, idx, i); + int t = lua_type(L, -1); + switch (t) { + case LUA_TSTRING: /* strings */ + std::printf("string: '%s'\n", lua_tostring(L, -1)); + break; + case LUA_TBOOLEAN: /* booleans */ + std::printf("boolean %s\n", lua_toboolean(L, -1) ? "true" : "false"); + break; + case LUA_TNUMBER: /* numbers */ + std::printf("number: %g\n", lua_tonumber(L, -1)); + break; + default: /* other values */ + std::printf("%s\n", lua_typename(L, t)); + break; + } + std::printf(" "); /* put a separator */ + lua_pop(L, 1); + } + std::printf("\n"); /* end the listing */ } diff --git a/CorsixTH/Src/th_lua.h b/CorsixTH/Src/th_lua.h index 0d086773..555e9b22 100644 --- a/CorsixTH/Src/th_lua.h +++ b/CorsixTH/Src/th_lua.h @@ -23,12 +23,12 @@ SOFTWARE. #ifndef CORSIX_TH_TH_LUA_H_ #define CORSIX_TH_TH_LUA_H_ #include "config.h" -#include "lua.hpp" #include #include #include +#include "lua.hpp" -int luaopen_th(lua_State *L); +int luaopen_th(lua_State* L); // Compatibility layer for removal of environments in 5.2 #if LUA_VERSION_NUM >= 502 @@ -37,85 +37,78 @@ const int luaT_environindex = lua_upvalueindex(1); const int luaT_environindex = LUA_ENVIRONINDEX; #endif -inline int luaT_upvalueindex(int i) -{ +inline int luaT_upvalueindex(int i) { #if LUA_VERSION_NUM >= 502 - return lua_upvalueindex(i + 1); + return lua_upvalueindex(i + 1); #else - return lua_upvalueindex(i); + return lua_upvalueindex(i); #endif } -template -inline void luaT_register(lua_State *L, const char *n, Collection &l) -{ +template +inline void luaT_register(lua_State* L, const char* n, Collection& l) { #if LUA_VERSION_NUM >= 502 - lua_createtable(L, 0, static_cast(l.size())); - lua_pushvalue(L, luaT_environindex); - luaL_setfuncs(L, l.data(), 1); - lua_pushvalue(L, -1); - lua_setglobal(L, n); + lua_createtable(L, 0, static_cast(l.size())); + lua_pushvalue(L, luaT_environindex); + luaL_setfuncs(L, l.data(), 1); + lua_pushvalue(L, -1); + lua_setglobal(L, n); #else - luaL_register(L, n, l.data()); + luaL_register(L, n, l.data()); #endif } -inline void luaT_setfuncs(lua_State *L, const luaL_Reg *R) -{ +inline void luaT_setfuncs(lua_State* L, const luaL_Reg* R) { #if LUA_VERSION_NUM >= 502 - lua_pushvalue(L, luaT_environindex); - luaL_setfuncs(L, R, 1); + lua_pushvalue(L, luaT_environindex); + luaL_setfuncs(L, R, 1); #else - luaL_register(L, nullptr, R); + luaL_register(L, nullptr, R); #endif } -inline void luaT_pushcclosure(lua_State* L, lua_CFunction f, int nups) -{ +inline void luaT_pushcclosure(lua_State* L, lua_CFunction f, int nups) { #if LUA_VERSION_NUM >= 502 - ++nups; - lua_pushvalue(L, luaT_environindex); - lua_insert(L, -nups); - lua_pushcclosure(L, f, nups); + ++nups; + lua_pushvalue(L, luaT_environindex); + lua_insert(L, -nups); + lua_pushcclosure(L, f, nups); #else - lua_pushcclosure(L, f, nups); + lua_pushcclosure(L, f, nups); #endif } -inline void luaT_pushcfunction(lua_State *L, lua_CFunction f) -{ - luaT_pushcclosure(L, f, 0); +inline void luaT_pushcfunction(lua_State* L, lua_CFunction f) { + luaT_pushcclosure(L, f, 0); } -inline int luaT_cpcall(lua_State *L, lua_CFunction f, void *u) -{ +inline int luaT_cpcall(lua_State* L, lua_CFunction f, void* u) { #if LUA_VERSION_NUM >= 502 - lua_checkstack(L, 2); - lua_pushcfunction(L, f); - lua_pushlightuserdata(L, u); - return lua_pcall(L, 1, 0, 0); + lua_checkstack(L, 2); + lua_pushcfunction(L, f); + lua_pushlightuserdata(L, u); + return lua_pcall(L, 1, 0, 0); #else - return lua_cpcall(L, f, u); + return lua_cpcall(L, f, u); #endif } // Compatibility for missing mode argument on lua_load in 5.1 -inline int luaT_load(lua_State *L, lua_Reader r, void *d, const char *s, const char *m) -{ +inline int luaT_load(lua_State* L, lua_Reader r, void* d, const char* s, + const char* m) { #if LUA_VERSION_NUM >= 502 - return lua_load(L, r, d, s, m); + return lua_load(L, r, d, s, m); #else - return lua_load(L, r, d, s); + return lua_load(L, r, d, s); #endif } // Compatibility for missing from argument on lua_resume in 5.1 -inline int luaT_resume(lua_State *L, lua_State *f, int n) -{ +inline int luaT_resume(lua_State* L, lua_State* f, int n) { #if LUA_VERSION_NUM >= 502 - return lua_resume(L, f, n); + return lua_resume(L, f, n); #else - return lua_resume(L, n); + return lua_resume(L, n); #endif } @@ -128,10 +121,9 @@ inline int luaT_resume(lua_State *L, lua_State *f, int n) See also luaT_stdnew() which allocates, and also sets up the environment table and metatable for the userdata. */ -template -T* luaT_new(lua_State* L, Ts ...args) -{ - return new (lua_newuserdata(L, sizeof(T))) T(args...); +template +T* luaT_new(lua_State* L, Ts... args) { + return new (lua_newuserdata(L, sizeof(T))) T(args...); } //! Check that a Lua argument is a binary data blob @@ -140,269 +132,280 @@ T* luaT_new(lua_State* L, Ts ...args) pointer to the start of it, and the length of it. Otherwise, throws a Lua error. */ -const uint8_t* luaT_checkfile(lua_State *L, int idx, size_t* pDataLen); +const uint8_t* luaT_checkfile(lua_State* L, int idx, size_t* pDataLen); //! Check that a Lua argument is a string or a proxied string -const char* luaT_checkstring(lua_State *L, int idx, size_t* pLength); +const char* luaT_checkstring(lua_State* L, int idx, size_t* pLength); //! Push a C closure as a callable table -void luaT_pushcclosuretable(lua_State *L, lua_CFunction fn, int n); +void luaT_pushcclosuretable(lua_State* L, lua_CFunction fn, int n); //! Set a field on the environment table of a value /*! Performs: env(stack[index])[k] = top; pop() */ -void luaT_setenvfield(lua_State *L, int index, const char *k); +void luaT_setenvfield(lua_State* L, int index, const char* k); //! Get a field from the environment table of a value /*! Performs: push(env(stack[index])[k]) */ -void luaT_getenvfield(lua_State *L, int index, const char *k); +void luaT_getenvfield(lua_State* L, int index, const char* k); template -inline T* luaT_stdnew(lua_State *L, int mt_idx = luaT_environindex, bool env = false) -{ - T* p = luaT_new(L); - lua_pushvalue(L, mt_idx); - lua_setmetatable(L, -2); - if(env) - { - lua_newtable(L); - lua_setfenv(L, -2); - } - return p; +inline T* luaT_stdnew(lua_State* L, int mt_idx = luaT_environindex, + bool env = false) { + T* p = luaT_new(L); + lua_pushvalue(L, mt_idx); + lua_setmetatable(L, -2); + if (env) { + lua_newtable(L); + lua_setfenv(L, -2); + } + return p; } -template struct luaT_classinfo {}; +template +struct luaT_classinfo {}; class render_target; -template <> struct luaT_classinfo { - static inline const char* name() {return "Surface";} +template <> +struct luaT_classinfo { + static inline const char* name() { return "Surface"; } }; class level_map; -template <> struct luaT_classinfo { - static inline const char* name() {return "Map";} +template <> +struct luaT_classinfo { + static inline const char* name() { return "Map"; } }; class sprite_sheet; -template <> struct luaT_classinfo { - static inline const char* name() {return "SpriteSheet";} +template <> +struct luaT_classinfo { + static inline const char* name() { return "SpriteSheet"; } }; class animation; -template <> struct luaT_classinfo { - static inline const char* name() {return "Animation";} +template <> +struct luaT_classinfo { + static inline const char* name() { return "Animation"; } }; class animation_manager; -template <> struct luaT_classinfo { - static inline const char* name() {return "Animator";} +template <> +struct luaT_classinfo { + static inline const char* name() { return "Animator"; } }; class palette; -template <> struct luaT_classinfo { - static inline const char* name() {return "Palette";} +template <> +struct luaT_classinfo { + static inline const char* name() { return "Palette"; } }; class raw_bitmap; -template <> struct luaT_classinfo { - static inline const char* name() {return "RawBitmap";} +template <> +struct luaT_classinfo { + static inline const char* name() { return "RawBitmap"; } }; class font; -template <> struct luaT_classinfo { - static inline const char* name() {return "Font";} +template <> +struct luaT_classinfo { + static inline const char* name() { return "Font"; } }; class bitmap_font; -template <> struct luaT_classinfo { - static inline const char* name() {return "BitmapFont";} +template <> +struct luaT_classinfo { + static inline const char* name() { return "BitmapFont"; } }; #ifdef CORSIX_TH_USE_FREETYPE2 class freetype_font; -template <> struct luaT_classinfo { - static inline const char* name() {return "FreeTypeFont";} +template <> +struct luaT_classinfo { + static inline const char* name() { return "FreeTypeFont"; } }; #endif struct layers; -template <> struct luaT_classinfo { - static inline const char* name() {return "Layers";} +template <> +struct luaT_classinfo { + static inline const char* name() { return "Layers"; } }; class pathfinder; -template <> struct luaT_classinfo { - static inline const char* name() {return "Pathfinder";} +template <> +struct luaT_classinfo { + static inline const char* name() { return "Pathfinder"; } }; class cursor; -template <> struct luaT_classinfo { - static inline const char* name() {return "Cursor";} +template <> +struct luaT_classinfo { + static inline const char* name() { return "Cursor"; } }; class line; -template <> struct luaT_classinfo { - static inline const char* name() {return "Line";} +template <> +struct luaT_classinfo { + static inline const char* name() { return "Line"; } }; class music; -template <> struct luaT_classinfo { - static inline const char* name() {return "Music";} +template <> +struct luaT_classinfo { + static inline const char* name() { return "Music"; } }; class sound_archive; -template <> struct luaT_classinfo { - static inline const char* name() {return "SoundArchive";} +template <> +struct luaT_classinfo { + static inline const char* name() { return "SoundArchive"; } }; class sound_player; -template <> struct luaT_classinfo { - static inline const char* name() {return "SoundEffects";} +template <> +struct luaT_classinfo { + static inline const char* name() { return "SoundEffects"; } }; class movie_player; -template <> struct luaT_classinfo { - static inline const char* name() {return "Movie";} +template <> +struct luaT_classinfo { + static inline const char* name() { return "Movie"; } }; class abstract_window; -template <> struct luaT_classinfo { - static inline const char* name() {return "WindowBase";} +template <> +struct luaT_classinfo { + static inline const char* name() { return "WindowBase"; } }; class sprite_render_list; -template <> struct luaT_classinfo { - static inline const char* name() {return "SpriteRenderList";} +template <> +struct luaT_classinfo { + static inline const char* name() { return "SpriteRenderList"; } }; class string_proxy; -template <> struct luaT_classinfo { - static inline const char* name() {return "StringProxy";} +template <> +struct luaT_classinfo { + static inline const char* name() { return "StringProxy"; } }; class lfs_ext; -template <> struct luaT_classinfo { - static inline const char* name() {return "LfsExt";} +template <> +struct luaT_classinfo { + static inline const char* name() { return "LfsExt"; } }; class iso_filesystem; -template <> struct luaT_classinfo { - static inline const char* name() {return "ISO Filesystem";} +template <> +struct luaT_classinfo { + static inline const char* name() { return "ISO Filesystem"; } }; -template <> struct luaT_classinfo { - static inline const char* name() {return "file";} +template <> +struct luaT_classinfo { + static inline const char* name() { return "file"; } }; template -T* luaT_testuserdata(lua_State *L, int idx, int mt_idx, bool required = true) -{ - // Turn mt_idx into an absolute index, as the stack size changes. - if(mt_idx > LUA_REGISTRYINDEX && mt_idx < 0) - mt_idx = lua_gettop(L) + mt_idx + 1; +T* luaT_testuserdata(lua_State* L, int idx, int mt_idx, bool required = true) { + // Turn mt_idx into an absolute index, as the stack size changes. + if (mt_idx > LUA_REGISTRYINDEX && mt_idx < 0) + mt_idx = lua_gettop(L) + mt_idx + 1; - void *ud = lua_touserdata(L, idx); - if(ud != nullptr && lua_getmetatable(L, idx) != 0) - { - while(true) - { - if(lua_equal(L, mt_idx, -1) != 0) - { - lua_pop(L, 1); - return (T*)ud; - } - // Go up one inheritance level, if there is one. - if(lua_type(L, -1) != LUA_TTABLE) - break; - lua_rawgeti(L, -1, 1); - lua_replace(L, -2); - } + void* ud = lua_touserdata(L, idx); + if (ud != nullptr && lua_getmetatable(L, idx) != 0) { + while (true) { + if (lua_equal(L, mt_idx, -1) != 0) { lua_pop(L, 1); + return (T*)ud; + } + // Go up one inheritance level, if there is one. + if (lua_type(L, -1) != LUA_TTABLE) break; + lua_rawgeti(L, -1, 1); + lua_replace(L, -2); } + lua_pop(L, 1); + } - if (required) - { - const char *msg = lua_pushfstring(L, "%s expected, got %s", luaT_classinfo::name(), luaL_typename(L, idx)); - luaL_argerror(L, idx, msg); - } - return nullptr; + if (required) { + const char* msg = + lua_pushfstring(L, "%s expected, got %s", luaT_classinfo::name(), + luaL_typename(L, idx)); + luaL_argerror(L, idx, msg); + } + return nullptr; } template -T* luaT_testuserdata(lua_State *L, int idx = 1) -{ - int iMetaIndex = luaT_environindex; - if(idx > 1) - iMetaIndex = luaT_upvalueindex(idx - 1); - return luaT_testuserdata(L, idx, iMetaIndex); +T* luaT_testuserdata(lua_State* L, int idx = 1) { + int iMetaIndex = luaT_environindex; + if (idx > 1) iMetaIndex = luaT_upvalueindex(idx - 1); + return luaT_testuserdata(L, idx, iMetaIndex); } template -int luaT_stdgc(lua_State *L) -{ - T* p = luaT_testuserdata(L, 1, mt, false); - if(p != nullptr) - { - p->~T(); - } - return 0; +int luaT_stdgc(lua_State* L) { + T* p = luaT_testuserdata(L, 1, mt, false); + if (p != nullptr) { + p->~T(); + } + return 0; } -void luaT_execute(lua_State *L, const char* sLuaString); -void luaT_execute_loadstring(lua_State *L, const char* sLuaString); +void luaT_execute(lua_State* L, const char* sLuaString); +void luaT_execute_loadstring(lua_State* L, const char* sLuaString); -void luaT_push(lua_State *L, lua_CFunction f); -void luaT_push(lua_State *L, int i); -void luaT_push(lua_State *L, const char* s); +void luaT_push(lua_State* L, lua_CFunction f); +void luaT_push(lua_State* L, int i); +void luaT_push(lua_State* L, const char* s); template -void luaT_execute(lua_State *L, const char* sLuaString, T arg) -{ - luaT_execute_loadstring(L, sLuaString); - luaT_push(L, arg); - lua_call(L, 1, LUA_MULTRET); +void luaT_execute(lua_State* L, const char* sLuaString, T arg) { + luaT_execute_loadstring(L, sLuaString); + luaT_push(L, arg); + lua_call(L, 1, LUA_MULTRET); } template -void luaT_execute(lua_State *L, const char* sLuaString, - T1 arg1, T2 arg2) -{ - luaT_execute_loadstring(L, sLuaString); - luaT_push(L, arg1); - luaT_push(L, arg2); - lua_call(L, 2, LUA_MULTRET); +void luaT_execute(lua_State* L, const char* sLuaString, T1 arg1, T2 arg2) { + luaT_execute_loadstring(L, sLuaString); + luaT_push(L, arg1); + luaT_push(L, arg2); + lua_call(L, 2, LUA_MULTRET); } template -void luaT_execute(lua_State *L, const char* sLuaString, - T1 arg1, T2 arg2, T3 arg3) -{ - luaT_execute_loadstring(L, sLuaString); - luaT_push(L, arg1); - luaT_push(L, arg2); - luaT_push(L, arg3); - lua_call(L, 3, LUA_MULTRET); +void luaT_execute(lua_State* L, const char* sLuaString, T1 arg1, T2 arg2, + T3 arg3) { + luaT_execute_loadstring(L, sLuaString); + luaT_push(L, arg1); + luaT_push(L, arg2); + luaT_push(L, arg3); + lua_call(L, 3, LUA_MULTRET); } template -void luaT_execute(lua_State *L, const char* sLuaString, - T1 arg1, T2 arg2, T3 arg3, T4 arg4) -{ - luaT_execute_loadstring(L, sLuaString); - luaT_push(L, arg1); - luaT_push(L, arg2); - luaT_push(L, arg3); - luaT_push(L, arg4); - lua_call(L, 4, LUA_MULTRET); +void luaT_execute(lua_State* L, const char* sLuaString, T1 arg1, T2 arg2, + T3 arg3, T4 arg4) { + luaT_execute_loadstring(L, sLuaString); + luaT_push(L, arg1); + luaT_push(L, arg2); + luaT_push(L, arg3); + luaT_push(L, arg4); + lua_call(L, 4, LUA_MULTRET); } -void luaT_pushtablebool(lua_State *L, const char *k, bool v); +void luaT_pushtablebool(lua_State* L, const char* k, bool v); -void luaT_printstack(lua_State *L); +void luaT_printstack(lua_State* L); -void luaT_printrawtable(lua_State *L, int idx); +void luaT_printrawtable(lua_State* L, int idx); -#endif // CORSIX_TH_TH_LUA_H_ +#endif // CORSIX_TH_TH_LUA_H_ diff --git a/CorsixTH/Src/th_lua_anims.cpp b/CorsixTH/Src/th_lua_anims.cpp index 8277cbd8..8af0654b 100644 --- a/CorsixTH/Src/th_lua_anims.cpp +++ b/CorsixTH/Src/th_lua_anims.cpp @@ -20,735 +20,727 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -#include "th_lua_internal.h" #include "th_gfx.h" +#include "th_lua_internal.h" #include "th_map.h" namespace { -/* this variable is used to determine the layer of the animation, it should be rewriten at some - point so that the it is passed as an argument in the function l_anim_set_tile */ +/* this variable is used to determine the layer of the animation, it should be + rewriten at some + point so that the it is passed as an argument in the function l_anim_set_tile +*/ int last_layer = 2; -int l_anims_new(lua_State *L) -{ - luaT_stdnew(L, luaT_environindex, true); - return 1; +int l_anims_new(lua_State* L) { + luaT_stdnew(L, luaT_environindex, true); + return 1; } -int l_anims_set_spritesheet(lua_State *L) -{ - animation_manager* pAnims = luaT_testuserdata(L); - sprite_sheet* pSheet = luaT_testuserdata(L, 2); - lua_settop(L, 2); +int l_anims_set_spritesheet(lua_State* L) { + animation_manager* pAnims = luaT_testuserdata(L); + sprite_sheet* pSheet = luaT_testuserdata(L, 2); + lua_settop(L, 2); - pAnims->set_sprite_sheet(pSheet); - luaT_setenvfield(L, 1, "sprites"); - return 1; + pAnims->set_sprite_sheet(pSheet); + luaT_setenvfield(L, 1, "sprites"); + return 1; } //! Set the video target for the sprites. /*! setCanvas() */ -int l_anims_set_canvas(lua_State *L) -{ - animation_manager* pAnims = luaT_testuserdata(L); - render_target* pCanvas = luaT_testuserdata(L, 2); - lua_settop(L, 2); +int l_anims_set_canvas(lua_State* L) { + animation_manager* pAnims = luaT_testuserdata(L); + render_target* pCanvas = luaT_testuserdata(L, 2); + lua_settop(L, 2); - pAnims->set_canvas(pCanvas); - luaT_setenvfield(L, 1, "target"); - return 1; + pAnims->set_canvas(pCanvas); + luaT_setenvfield(L, 1, "target"); + return 1; } -int l_anims_load(lua_State *L) -{ - animation_manager* pAnims = luaT_testuserdata(L); - size_t iStartDataLength, iFrameDataLength, iListDataLength, iElementDataLength; - const uint8_t* pStartData = luaT_checkfile(L, 2, &iStartDataLength); - const uint8_t* pFrameData = luaT_checkfile(L, 3, &iFrameDataLength); - const uint8_t* pListData = luaT_checkfile(L, 4, &iListDataLength); - const uint8_t* pElementData = luaT_checkfile(L, 5, &iElementDataLength); +int l_anims_load(lua_State* L) { + animation_manager* pAnims = luaT_testuserdata(L); + size_t iStartDataLength, iFrameDataLength, iListDataLength, + iElementDataLength; + const uint8_t* pStartData = luaT_checkfile(L, 2, &iStartDataLength); + const uint8_t* pFrameData = luaT_checkfile(L, 3, &iFrameDataLength); + const uint8_t* pListData = luaT_checkfile(L, 4, &iListDataLength); + const uint8_t* pElementData = luaT_checkfile(L, 5, &iElementDataLength); - if(pAnims->load_from_th_file(pStartData, iStartDataLength, pFrameData, iFrameDataLength, - pListData, iListDataLength, pElementData, iElementDataLength)) - { - lua_pushboolean(L, 1); - } - else - { - lua_pushboolean(L, 0); - } + if (pAnims->load_from_th_file(pStartData, iStartDataLength, pFrameData, + iFrameDataLength, pListData, iListDataLength, + pElementData, iElementDataLength)) { + lua_pushboolean(L, 1); + } else { + lua_pushboolean(L, 0); + } - return 1; + return 1; } //! Load custom animations. /*! loadCustom() -> true/false */ -int l_anims_loadcustom(lua_State *L) -{ - animation_manager* pAnims = luaT_testuserdata(L); - size_t iDataLength; - const uint8_t* pData = luaT_checkfile(L, 2, &iDataLength); +int l_anims_loadcustom(lua_State* L) { + animation_manager* pAnims = luaT_testuserdata(L); + size_t iDataLength; + const uint8_t* pData = luaT_checkfile(L, 2, &iDataLength); - if (pAnims->load_custom_animations(pData, iDataLength)) - { - lua_pushboolean(L, 1); - } - else - { - lua_pushboolean(L, 0); - } + if (pAnims->load_custom_animations(pData, iDataLength)) { + lua_pushboolean(L, 1); + } else { + lua_pushboolean(L, 0); + } - return 1; + return 1; } //! Lua interface for getting a set of animations by name and tile size (one for //! each view direction, 'nil' if no animation is available for a direction). /*! - getAnimations(, ) -> (, , , ) + getAnimations(, ) -> (, , + , ) */ -int l_anims_getanims(lua_State *L) -{ - animation_manager* pAnims = luaT_testuserdata(L); - int iTileSize = static_cast(luaL_checkinteger(L, 2)); - const char *pName = luaL_checkstring(L, 3); +int l_anims_getanims(lua_State* L) { + animation_manager* pAnims = luaT_testuserdata(L); + int iTileSize = static_cast(luaL_checkinteger(L, 2)); + const char* pName = luaL_checkstring(L, 3); - const animation_start_frames &oFrames = pAnims->get_named_animations(pName, iTileSize); - if (oFrames.north < 0) { lua_pushnil(L); } else { lua_pushnumber(L, static_cast(oFrames.north)); } - if (oFrames.east < 0) { lua_pushnil(L); } else { lua_pushnumber(L, static_cast(oFrames.east)); } - if (oFrames.south < 0) { lua_pushnil(L); } else { lua_pushnumber(L, static_cast(oFrames.south)); } - if (oFrames.west < 0) { lua_pushnil(L); } else { lua_pushnumber(L, static_cast(oFrames.west)); } - return 4; -} - - -int l_anims_getfirst(lua_State *L) -{ - animation_manager* pAnims = luaT_testuserdata(L); - int iAnim = static_cast(luaL_checkinteger(L, 2)); - - lua_pushinteger(L, pAnims->get_first_frame(iAnim)); - return 1; -} - -int l_anims_getnext(lua_State *L) -{ - animation_manager* pAnims = luaT_testuserdata(L); - int iFrame = static_cast(luaL_checkinteger(L, 2)); - - lua_pushinteger(L, pAnims->get_next_frame(iFrame)); - return 1; -} - -int l_anims_set_alt_pal(lua_State *L) -{ - animation_manager* pAnims = luaT_testuserdata(L); - size_t iAnimation = luaL_checkinteger(L, 2); - size_t iPalLen; - const uint8_t *pPal = luaT_checkfile(L, 3, &iPalLen); - if(iPalLen != 256) - { - return luaL_argerror(L, 3, "GhostPalette string is not a valid palette"); - } - uint32_t iAlt32 = static_cast(luaL_checkinteger(L, 4)); - - pAnims->set_animation_alt_palette_map(iAnimation, pPal, iAlt32); - - lua_getfenv(L, 1); - lua_insert(L, 2); - lua_settop(L, 4); - lua_settable(L, 2); - lua_settop(L, 1); - return 1; -} - -int l_anims_set_marker(lua_State *L) -{ - animation_manager* pAnims = luaT_testuserdata(L); - lua_pushboolean(L, pAnims->set_frame_marker(luaL_checkinteger(L, 2), - static_cast(luaL_checkinteger(L, 3)), static_cast(luaL_checkinteger(L, 4))) ? 1 : 0); - return 1; -} - -int l_anims_set_secondary_marker(lua_State *L) -{ - animation_manager* pAnims = luaT_testuserdata(L); - lua_pushboolean(L, pAnims->set_frame_secondary_marker(luaL_checkinteger(L, 2), - static_cast(luaL_checkinteger(L, 3)), static_cast(luaL_checkinteger(L, 4))) ? 1 : 0); - return 1; -} - -int l_anims_draw(lua_State *L) -{ - animation_manager* pAnims = luaT_testuserdata(L); - render_target* pCanvas = luaT_testuserdata(L, 2); - size_t iFrame = luaL_checkinteger(L, 3); - layers* pLayers = luaT_testuserdata(L, 4, luaT_upvalueindex(2)); - int iX = static_cast(luaL_checkinteger(L, 5)); - int iY = static_cast(luaL_checkinteger(L, 6)); - int iFlags = static_cast(luaL_optinteger(L, 7, 0)); - - pAnims->draw_frame(pCanvas, iFrame, *pLayers, iX, iY, iFlags); - - lua_settop(L, 1); - return 1; -} - -template -int l_anim_new(lua_State *L) -{ - T* pAnimation = luaT_stdnew(L, luaT_environindex, true); - lua_rawgeti(L, luaT_environindex, 2); - lua_pushlightuserdata(L, pAnimation); - lua_pushvalue(L, -3); - lua_rawset(L, -3); - lua_pop(L, 1); - return 1; -} - -template -int l_anim_persist(lua_State *L) -{ - T* pAnimation; - if(lua_gettop(L) == 2) - { - pAnimation = luaT_testuserdata(L, 1, luaT_environindex, false); - lua_insert(L, 1); - } - else - { - // Fast __persist call - pAnimation = (T*)lua_touserdata(L, -1); - } - lua_persist_writer* pWriter = (lua_persist_writer*)lua_touserdata(L, 1); - - pAnimation->persist(pWriter); - lua_rawgeti(L, luaT_environindex, 1); - lua_pushlightuserdata(L, pAnimation); - lua_gettable(L, -2); - pWriter->write_stack_object(-1); - lua_pop(L, 2); - return 0; -} - -template -int l_anim_pre_depersist(lua_State *L) -{ - // Note that anims and the map have nice reference cycles between them - // and hence we cannot be sure which is depersisted first. To ensure that - // things work nicely, we initialise all the fields of a THAnimation as - // soon as possible, thus preventing issues like an anim -> map -> anim - // reference chain whereby l_anim_depersist is called after l_map_depersist - // (as anim references map in its environment table) causing the prev - // field to be set during map depersistence, then cleared to nullptr by the - // constructor during l_anim_depersist. - T* pAnimation = luaT_testuserdata(L); - new (pAnimation) T; // Call constructor - return 0; -} - -template -int l_anim_depersist(lua_State *L) -{ - T* pAnimation = luaT_testuserdata(L); - lua_settop(L, 2); - lua_insert(L, 1); - lua_persist_reader* pReader = (lua_persist_reader*)lua_touserdata(L, 1); - - lua_rawgeti(L, luaT_environindex, 2); - lua_pushlightuserdata(L, pAnimation); - lua_pushvalue(L, 2); - lua_settable(L, -3); - lua_pop(L, 1); - pAnimation->depersist(pReader); - lua_rawgeti(L, luaT_environindex, 1); - lua_pushlightuserdata(L, pAnimation); - if(!pReader->read_stack_object()) - return 0; - lua_settable(L, -3); - lua_pop(L, 1); - return 0; -} - -int l_anim_set_hitresult(lua_State *L) -{ - luaL_checktype(L, 1, LUA_TUSERDATA); - lua_settop(L, 2); - lua_rawgeti(L, luaT_environindex, 1); - lua_pushlightuserdata(L, lua_touserdata(L, 1)); - lua_pushvalue(L, 2); - lua_settable(L, 3); - lua_settop(L, 1); - return 1; -} - -int l_anim_set_frame(lua_State *L) -{ - animation* pAnimation = luaT_testuserdata(L); - pAnimation->set_frame(luaL_checkinteger(L, 2)); - lua_settop(L, 1); - return 1; -} - -int l_anim_get_frame(lua_State *L) -{ - animation* pAnimation = luaT_testuserdata(L); - lua_pushinteger(L, pAnimation->get_frame()); - return 1; -} - -int l_anim_set_crop(lua_State *L) -{ - animation* pAnimation = luaT_testuserdata(L); - pAnimation->set_crop_column(static_cast(luaL_checkinteger(L, 2))); - lua_settop(L, 1); - return 1; -} - -int l_anim_get_crop(lua_State *L) -{ - animation* pAnimation = luaT_testuserdata(L); - lua_pushinteger(L, pAnimation->get_crop_column()); - return 1; -} - -int l_anim_set_anim(lua_State *L) -{ - animation* pAnimation = luaT_testuserdata(L); - animation_manager* pManager = luaT_testuserdata(L, 2); - size_t iAnim = luaL_checkinteger(L, 3); - if(iAnim < 0 || iAnim >= pManager->get_animation_count()) - luaL_argerror(L, 3, "Animation index out of bounds"); - - if(lua_isnoneornil(L, 4)) - pAnimation->set_flags(0); - else - pAnimation->set_flags(static_cast(luaL_checkinteger(L, 4))); - - pAnimation->set_animation(pManager, iAnim); - lua_settop(L, 2); - luaT_setenvfield(L, 1, "animator"); + const animation_start_frames& oFrames = + pAnims->get_named_animations(pName, iTileSize); + if (oFrames.north < 0) { lua_pushnil(L); - luaT_setenvfield(L, 1, "morph_target"); - - return 1; + } else { + lua_pushnumber(L, static_cast(oFrames.north)); + } + if (oFrames.east < 0) { + lua_pushnil(L); + } else { + lua_pushnumber(L, static_cast(oFrames.east)); + } + if (oFrames.south < 0) { + lua_pushnil(L); + } else { + lua_pushnumber(L, static_cast(oFrames.south)); + } + if (oFrames.west < 0) { + lua_pushnil(L); + } else { + lua_pushnumber(L, static_cast(oFrames.west)); + } + return 4; } -int l_anim_set_morph(lua_State *L) -{ - animation* pAnimation = luaT_testuserdata(L); - animation* pMorphTarget = luaT_testuserdata(L, 2, luaT_environindex); +int l_anims_getfirst(lua_State* L) { + animation_manager* pAnims = luaT_testuserdata(L); + int iAnim = static_cast(luaL_checkinteger(L, 2)); - unsigned int iDurationFactor = 1; - if(!lua_isnoneornil(L, 3) && luaL_checkinteger(L, 3) > 0) - iDurationFactor = static_cast(luaL_checkinteger(L, 3)); - - pAnimation->set_morph_target(pMorphTarget, iDurationFactor); - lua_settop(L, 2); - luaT_setenvfield(L, 1, "morph_target"); - - return 1; + lua_pushinteger(L, pAnims->get_first_frame(iAnim)); + return 1; } -int l_anim_set_drawable_layer(lua_State *L) -{ - last_layer = static_cast(luaL_checkinteger(L, 2)); - return 1; +int l_anims_getnext(lua_State* L) { + animation_manager* pAnims = luaT_testuserdata(L); + int iFrame = static_cast(luaL_checkinteger(L, 2)); + + lua_pushinteger(L, pAnims->get_next_frame(iFrame)); + return 1; } -int l_anim_get_anim(lua_State *L) -{ - animation* pAnimation = luaT_testuserdata(L); - lua_pushinteger(L, pAnimation->get_animation()); +int l_anims_set_alt_pal(lua_State* L) { + animation_manager* pAnims = luaT_testuserdata(L); + size_t iAnimation = luaL_checkinteger(L, 2); + size_t iPalLen; + const uint8_t* pPal = luaT_checkfile(L, 3, &iPalLen); + if (iPalLen != 256) { + return luaL_argerror(L, 3, "GhostPalette string is not a valid palette"); + } + uint32_t iAlt32 = static_cast(luaL_checkinteger(L, 4)); - return 1; + pAnims->set_animation_alt_palette_map(iAnimation, pPal, iAlt32); + + lua_getfenv(L, 1); + lua_insert(L, 2); + lua_settop(L, 4); + lua_settable(L, 2); + lua_settop(L, 1); + return 1; +} + +int l_anims_set_marker(lua_State* L) { + animation_manager* pAnims = luaT_testuserdata(L); + lua_pushboolean( + L, pAnims->set_frame_marker(luaL_checkinteger(L, 2), + static_cast(luaL_checkinteger(L, 3)), + static_cast(luaL_checkinteger(L, 4))) + ? 1 + : 0); + return 1; +} + +int l_anims_set_secondary_marker(lua_State* L) { + animation_manager* pAnims = luaT_testuserdata(L); + lua_pushboolean( + L, pAnims->set_frame_secondary_marker( + luaL_checkinteger(L, 2), static_cast(luaL_checkinteger(L, 3)), + static_cast(luaL_checkinteger(L, 4))) + ? 1 + : 0); + return 1; +} + +int l_anims_draw(lua_State* L) { + animation_manager* pAnims = luaT_testuserdata(L); + render_target* pCanvas = luaT_testuserdata(L, 2); + size_t iFrame = luaL_checkinteger(L, 3); + layers* pLayers = luaT_testuserdata(L, 4, luaT_upvalueindex(2)); + int iX = static_cast(luaL_checkinteger(L, 5)); + int iY = static_cast(luaL_checkinteger(L, 6)); + int iFlags = static_cast(luaL_optinteger(L, 7, 0)); + + pAnims->draw_frame(pCanvas, iFrame, *pLayers, iX, iY, iFlags); + + lua_settop(L, 1); + return 1; } template -int l_anim_set_tile(lua_State *L) -{ +int l_anim_new(lua_State* L) { + T* pAnimation = luaT_stdnew(L, luaT_environindex, true); + lua_rawgeti(L, luaT_environindex, 2); + lua_pushlightuserdata(L, pAnimation); + lua_pushvalue(L, -3); + lua_rawset(L, -3); + lua_pop(L, 1); + return 1; +} - T* pAnimation = luaT_testuserdata(L); - if(lua_isnoneornil(L, 2)) - { - pAnimation->remove_from_tile(); - lua_pushnil(L); - luaT_setenvfield(L, 1, "map"); - lua_settop(L, 1); +template +int l_anim_persist(lua_State* L) { + T* pAnimation; + if (lua_gettop(L) == 2) { + pAnimation = luaT_testuserdata(L, 1, luaT_environindex, false); + lua_insert(L, 1); + } else { + // Fast __persist call + pAnimation = (T*)lua_touserdata(L, -1); + } + lua_persist_writer* pWriter = (lua_persist_writer*)lua_touserdata(L, 1); + + pAnimation->persist(pWriter); + lua_rawgeti(L, luaT_environindex, 1); + lua_pushlightuserdata(L, pAnimation); + lua_gettable(L, -2); + pWriter->write_stack_object(-1); + lua_pop(L, 2); + return 0; +} + +template +int l_anim_pre_depersist(lua_State* L) { + // Note that anims and the map have nice reference cycles between them + // and hence we cannot be sure which is depersisted first. To ensure that + // things work nicely, we initialise all the fields of a THAnimation as + // soon as possible, thus preventing issues like an anim -> map -> anim + // reference chain whereby l_anim_depersist is called after l_map_depersist + // (as anim references map in its environment table) causing the prev + // field to be set during map depersistence, then cleared to nullptr by the + // constructor during l_anim_depersist. + T* pAnimation = luaT_testuserdata(L); + new (pAnimation) T; // Call constructor + return 0; +} + +template +int l_anim_depersist(lua_State* L) { + T* pAnimation = luaT_testuserdata(L); + lua_settop(L, 2); + lua_insert(L, 1); + lua_persist_reader* pReader = (lua_persist_reader*)lua_touserdata(L, 1); + + lua_rawgeti(L, luaT_environindex, 2); + lua_pushlightuserdata(L, pAnimation); + lua_pushvalue(L, 2); + lua_settable(L, -3); + lua_pop(L, 1); + pAnimation->depersist(pReader); + lua_rawgeti(L, luaT_environindex, 1); + lua_pushlightuserdata(L, pAnimation); + if (!pReader->read_stack_object()) return 0; + lua_settable(L, -3); + lua_pop(L, 1); + return 0; +} + +int l_anim_set_hitresult(lua_State* L) { + luaL_checktype(L, 1, LUA_TUSERDATA); + lua_settop(L, 2); + lua_rawgeti(L, luaT_environindex, 1); + lua_pushlightuserdata(L, lua_touserdata(L, 1)); + lua_pushvalue(L, 2); + lua_settable(L, 3); + lua_settop(L, 1); + return 1; +} + +int l_anim_set_frame(lua_State* L) { + animation* pAnimation = luaT_testuserdata(L); + pAnimation->set_frame(luaL_checkinteger(L, 2)); + lua_settop(L, 1); + return 1; +} + +int l_anim_get_frame(lua_State* L) { + animation* pAnimation = luaT_testuserdata(L); + lua_pushinteger(L, pAnimation->get_frame()); + return 1; +} + +int l_anim_set_crop(lua_State* L) { + animation* pAnimation = luaT_testuserdata(L); + pAnimation->set_crop_column(static_cast(luaL_checkinteger(L, 2))); + lua_settop(L, 1); + return 1; +} + +int l_anim_get_crop(lua_State* L) { + animation* pAnimation = luaT_testuserdata(L); + lua_pushinteger(L, pAnimation->get_crop_column()); + return 1; +} + +int l_anim_set_anim(lua_State* L) { + animation* pAnimation = luaT_testuserdata(L); + animation_manager* pManager = luaT_testuserdata(L, 2); + size_t iAnim = luaL_checkinteger(L, 3); + if (iAnim < 0 || iAnim >= pManager->get_animation_count()) + luaL_argerror(L, 3, "Animation index out of bounds"); + + if (lua_isnoneornil(L, 4)) { + pAnimation->set_flags(0); + } else { + pAnimation->set_flags(static_cast(luaL_checkinteger(L, 4))); + } + + pAnimation->set_animation(pManager, iAnim); + lua_settop(L, 2); + luaT_setenvfield(L, 1, "animator"); + lua_pushnil(L); + luaT_setenvfield(L, 1, "morph_target"); + + return 1; +} + +int l_anim_set_morph(lua_State* L) { + animation* pAnimation = luaT_testuserdata(L); + animation* pMorphTarget = + luaT_testuserdata(L, 2, luaT_environindex); + + unsigned int iDurationFactor = 1; + if (!lua_isnoneornil(L, 3) && luaL_checkinteger(L, 3) > 0) { + iDurationFactor = static_cast(luaL_checkinteger(L, 3)); + } + + pAnimation->set_morph_target(pMorphTarget, iDurationFactor); + lua_settop(L, 2); + luaT_setenvfield(L, 1, "morph_target"); + + return 1; +} + +int l_anim_set_drawable_layer(lua_State* L) { + last_layer = static_cast(luaL_checkinteger(L, 2)); + return 1; +} + +int l_anim_get_anim(lua_State* L) { + animation* pAnimation = luaT_testuserdata(L); + lua_pushinteger(L, pAnimation->get_animation()); + + return 1; +} + +template +int l_anim_set_tile(lua_State* L) { + T* pAnimation = luaT_testuserdata(L); + if (lua_isnoneornil(L, 2)) { + pAnimation->remove_from_tile(); + lua_pushnil(L); + luaT_setenvfield(L, 1, "map"); + lua_settop(L, 1); + } else { + level_map* pMap = luaT_testuserdata(L, 2); + map_tile* pNode = + pMap->get_tile(static_cast(luaL_checkinteger(L, 3) - 1), + static_cast(luaL_checkinteger(L, 4) - 1)); + if (pNode) { + pAnimation->attach_to_tile(pNode, last_layer); + } else { + luaL_argerror(L, 3, + lua_pushfstring(L, + "Map index out of bounds (" LUA_NUMBER_FMT + "," LUA_NUMBER_FMT ")", + lua_tonumber(L, 3), lua_tonumber(L, 4))); } - else - { - level_map* pMap = luaT_testuserdata(L, 2); - map_tile* pNode = pMap->get_tile(static_cast(luaL_checkinteger(L, 3) - 1), static_cast(luaL_checkinteger(L, 4) - 1)); - if(pNode) - pAnimation->attach_to_tile(pNode, last_layer); - - else - { - luaL_argerror(L, 3, lua_pushfstring(L, "Map index out of bounds (" - LUA_NUMBER_FMT "," LUA_NUMBER_FMT ")", lua_tonumber(L, 3), - lua_tonumber(L, 4))); - } - - lua_settop(L, 2); - luaT_setenvfield(L, 1, "map"); - } - - return 1; -} - -int l_anim_get_tile(lua_State *L) -{ - animation* pAnimation = luaT_testuserdata(L); - lua_settop(L, 1); - lua_getfenv(L, 1); - lua_getfield(L, 2, "map"); - lua_replace(L, 2); - if(lua_isnil(L, 2)) - { - return 0; - } - level_map* pMap = (level_map*)lua_touserdata(L, 2); - const link_list* pListNode = pAnimation->get_previous(); - while(pListNode->prev) - { - pListNode = pListNode->prev; - } - // Casting pListNode to a map_tile* is slightly dubious, but it should - // work. If on the normal list, then pListNode will be a map_tile*, and - // all is fine. However, if on the early list, pListNode will be pointing - // to a member of a map_tile, so we're relying on pointer arithmetic - // being a subtract and integer divide by sizeof(map_tile) to yield the - // correct map_tile. - const map_tile *pRootNode = pMap->get_tile_unchecked(0, 0); - uintptr_t iDiff = reinterpret_cast(pListNode) - - reinterpret_cast(pRootNode); - int iIndex = (int)(iDiff / sizeof(map_tile)); - int iY = iIndex / pMap->get_width(); - int iX = iIndex - (iY * pMap->get_width()); - lua_pushinteger(L, iX + 1); - lua_pushinteger(L, iY + 1); - return 3; // map, x, y -} - -int l_anim_set_parent(lua_State *L) -{ - animation* pAnimation = luaT_testuserdata(L); - animation* pParent = luaT_testuserdata(L, 2, luaT_environindex, false); - pAnimation->set_parent(pParent); - lua_settop(L, 1); - return 1; -} - -template -int l_anim_set_flag(lua_State *L) -{ - T* pAnimation = luaT_testuserdata(L); - pAnimation->set_flags(static_cast(luaL_checkinteger(L, 2))); - - lua_settop(L, 1); - return 1; -} - -template -int l_anim_set_flag_partial(lua_State *L) -{ - T* pAnimation = luaT_testuserdata(L); - uint32_t iFlags = static_cast(luaL_checkinteger(L, 2)); - if(lua_isnone(L, 3) || lua_toboolean(L, 3)) - { - pAnimation->set_flags(pAnimation->get_flags() | iFlags); - } - else - { - pAnimation->set_flags(pAnimation->get_flags() & ~iFlags); - } - lua_settop(L, 1); - return 1; -} - -template -int l_anim_make_visible(lua_State *L) -{ - T* pAnimation = luaT_testuserdata(L); - pAnimation->set_flags(pAnimation->get_flags() & ~static_cast(thdf_alpha_50 | thdf_alpha_75)); - - lua_settop(L, 1); - return 1; -} - -template -int l_anim_make_invisible(lua_State *L) -{ - T* pAnimation = luaT_testuserdata(L); - pAnimation->set_flags(pAnimation->get_flags() | static_cast(thdf_alpha_50 | thdf_alpha_75)); - - lua_settop(L, 1); - return 1; -} - -template -int l_anim_get_flag(lua_State *L) -{ - T* pAnimation = luaT_testuserdata(L); - lua_pushinteger(L, pAnimation->get_flags()); - - return 1; -} - -template -int l_anim_set_position(lua_State *L) -{ - T* pAnimation = luaT_testuserdata(L); - - pAnimation->set_position(static_cast(luaL_checkinteger(L, 2)), static_cast(luaL_checkinteger(L, 3))); - - lua_settop(L, 1); - return 1; -} - -int l_anim_get_position(lua_State *L) -{ - animation* pAnimation = luaT_testuserdata(L); - - lua_pushinteger(L, pAnimation->get_x()); - lua_pushinteger(L, pAnimation->get_y()); - - return 2; -} - -template -int l_anim_set_speed(lua_State *L) -{ - T* pAnimation = luaT_testuserdata(L); - - pAnimation->set_speed(static_cast(luaL_optinteger(L, 2, 0)), static_cast(luaL_optinteger(L, 3, 0))); - - lua_settop(L, 1); - return 1; -} - -template -int l_anim_set_layer(lua_State *L) -{ - T* pAnimation = luaT_testuserdata(L); - - pAnimation->set_layer(static_cast(luaL_checkinteger(L, 2)), static_cast(luaL_optinteger(L, 3, 0))); - - lua_settop(L, 1); - return 1; -} - -int l_anim_set_layers_from(lua_State *L) -{ - animation* pAnimation = luaT_testuserdata(L); - const animation* pAnimationSrc = luaT_testuserdata(L, 2, luaT_environindex); - - pAnimation->set_layers_from(pAnimationSrc); - - lua_settop(L, 1); - return 1; -} - -int l_anim_set_tag(lua_State *L) -{ - luaT_testuserdata(L); - lua_settop(L, 2); - luaT_setenvfield(L, 1, "tag"); - return 1; -} - -int l_anim_get_tag(lua_State *L) -{ - luaT_testuserdata(L); - lua_settop(L, 1); - lua_getfenv(L, 1); - lua_getfield(L, 2, "tag"); - return 1; -} - -int l_anim_get_marker(lua_State *L) -{ - animation* pAnimation = luaT_testuserdata(L); - int iX = 0; - int iY = 0; - pAnimation->get_marker(&iX, &iY); - lua_pushinteger(L, iX); - lua_pushinteger(L, iY); - return 2; -} - -int l_anim_get_secondary_marker(lua_State *L) -{ - animation* pAnimation = luaT_testuserdata(L); - int iX = 0; - int iY = 0; - pAnimation->get_secondary_marker(&iX, &iY); - lua_pushinteger(L, iX); - lua_pushinteger(L, iY); - return 2; -} - -template -int l_anim_tick(lua_State *L) -{ - T* pAnimation = luaT_testuserdata(L); - pAnimation->tick(); - lua_settop(L, 1); - return 1; -} - -template -int l_anim_draw(lua_State *L) -{ - T* pAnimation = luaT_testuserdata(L); - render_target* pCanvas = luaT_testuserdata(L, 2); - pAnimation->draw(pCanvas, static_cast(luaL_checkinteger(L, 3)), static_cast(luaL_checkinteger(L, 4))); - lua_settop(L, 1); - return 1; -} - -int l_srl_set_sheet(lua_State *L) -{ - sprite_render_list *pSrl = luaT_testuserdata(L); - sprite_sheet *pSheet = luaT_testuserdata(L, 2); - pSrl->set_sheet(pSheet); lua_settop(L, 2); - luaT_setenvfield(L, 1, "sheet"); - return 1; + luaT_setenvfield(L, 1, "map"); + } + + return 1; } -int l_srl_append(lua_State *L) -{ - sprite_render_list *pSrl = luaT_testuserdata(L); - pSrl->append_sprite(luaL_checkinteger(L, 2), - static_cast(luaL_checkinteger(L, 3)), static_cast(luaL_checkinteger(L, 4))); - lua_settop(L, 1); - return 1; +int l_anim_get_tile(lua_State* L) { + animation* pAnimation = luaT_testuserdata(L); + lua_settop(L, 1); + lua_getfenv(L, 1); + lua_getfield(L, 2, "map"); + lua_replace(L, 2); + if (lua_isnil(L, 2)) { + return 0; + } + level_map* pMap = (level_map*)lua_touserdata(L, 2); + const link_list* pListNode = pAnimation->get_previous(); + while (pListNode->prev) { + pListNode = pListNode->prev; + } + // Casting pListNode to a map_tile* is slightly dubious, but it should + // work. If on the normal list, then pListNode will be a map_tile*, and + // all is fine. However, if on the early list, pListNode will be pointing + // to a member of a map_tile, so we're relying on pointer arithmetic + // being a subtract and integer divide by sizeof(map_tile) to yield the + // correct map_tile. + const map_tile* pRootNode = pMap->get_tile_unchecked(0, 0); + uintptr_t iDiff = reinterpret_cast(pListNode) - + reinterpret_cast(pRootNode); + int iIndex = (int)(iDiff / sizeof(map_tile)); + int iY = iIndex / pMap->get_width(); + int iX = iIndex - (iY * pMap->get_width()); + lua_pushinteger(L, iX + 1); + lua_pushinteger(L, iY + 1); + return 3; // map, x, y } -int l_srl_set_lifetime(lua_State *L) -{ - sprite_render_list *pSrl = luaT_testuserdata(L); - pSrl->set_lifetime(static_cast(luaL_checkinteger(L, 2))); - lua_settop(L, 1); - return 1; +int l_anim_set_parent(lua_State* L) { + animation* pAnimation = luaT_testuserdata(L); + animation* pParent = + luaT_testuserdata(L, 2, luaT_environindex, false); + pAnimation->set_parent(pParent); + lua_settop(L, 1); + return 1; } -int l_srl_is_dead(lua_State *L) -{ - sprite_render_list *pSrl = luaT_testuserdata(L); - lua_pushboolean(L, pSrl->is_dead() ? 1 : 0); - return 1; +template +int l_anim_set_flag(lua_State* L) { + T* pAnimation = luaT_testuserdata(L); + pAnimation->set_flags(static_cast(luaL_checkinteger(L, 2))); + + lua_settop(L, 1); + return 1; } -} // namespace - -void lua_register_anims(const lua_register_state *pState) -{ - // Anims - { - lua_class_binding lcb(pState, "anims", l_anims_new, lua_metatable::anims); - lcb.add_function(l_anims_load, "load"); - lcb.add_function(l_anims_loadcustom, "loadCustom"); - lcb.add_function(l_anims_set_spritesheet, "setSheet", lua_metatable::sheet); - lcb.add_function(l_anims_set_canvas, "setCanvas", lua_metatable::surface); - lcb.add_function(l_anims_getanims, "getAnimations"); - lcb.add_function(l_anims_getfirst, "getFirstFrame"); - lcb.add_function(l_anims_getnext, "getNextFrame"); - lcb.add_function(l_anims_set_alt_pal, "setAnimationGhostPalette"); - lcb.add_function(l_anims_set_marker, "setFrameMarker"); - lcb.add_function(l_anims_set_secondary_marker, "setFrameSecondaryMarker"); - lcb.add_function(l_anims_draw, "draw", lua_metatable::surface, lua_metatable::layers); - lcb.add_constant("Alt32_GreyScale", thdf_alt32_grey_scale); - lcb.add_constant("Alt32_BlueRedSwap", thdf_alt32_blue_red_swap); - } - - // Weak table at AnimMetatable[1] for light UD -> object lookup - // For hitTest / setHitTestResult - lua_newtable(pState->L); - lua_createtable(pState->L, 0, 1); - lua_pushliteral(pState->L, "v"); - lua_setfield(pState->L, -2, "__mode"); - lua_setmetatable(pState->L, -2); - lua_rawseti(pState->L, pState->metatables[static_cast(lua_metatable::anim)], 1); - - // Weak table at AnimMetatable[2] for light UD -> full UD lookup - // For persisting Map - lua_newtable(pState->L); - lua_createtable(pState->L, 0, 1); - lua_pushliteral(pState->L, "v"); - lua_setfield(pState->L, -2, "__mode"); - lua_setmetatable(pState->L, -2); - lua_rawseti(pState->L, pState->metatables[static_cast(lua_metatable::anim)], 2); - - // Anim - { - lua_class_binding lcb(pState, "animation", l_anim_new, lua_metatable::anim); - lcb.add_metamethod(l_anim_persist, "persist"); - lcb.add_metamethod(l_anim_pre_depersist, "pre_depersist"); - lcb.add_metamethod(l_anim_depersist, "depersist"); - lcb.add_function(l_anim_set_anim, "setAnimation", lua_metatable::anims); - lcb.add_function(l_anim_set_crop, "setCrop"); - lcb.add_function(l_anim_get_crop, "getCrop"); - lcb.add_function(l_anim_set_morph, "setMorph"); - lcb.add_function(l_anim_set_frame, "setFrame"); - lcb.add_function(l_anim_get_frame, "getFrame"); - lcb.add_function(l_anim_get_anim, "getAnimation"); - lcb.add_function(l_anim_set_tile, "setTile", lua_metatable::map); - lcb.add_function(l_anim_get_tile, "getTile"); - lcb.add_function(l_anim_set_parent, "setParent"); - lcb.add_function(l_anim_set_flag, "setFlag"); - lcb.add_function(l_anim_set_flag_partial, "setPartialFlag"); - lcb.add_function(l_anim_get_flag, "getFlag"); - lcb.add_function(l_anim_make_visible, "makeVisible"); - lcb.add_function(l_anim_make_invisible, "makeInvisible"); - lcb.add_function(l_anim_set_tag, "setTag"); - lcb.add_function(l_anim_get_tag, "getTag"); - lcb.add_function(l_anim_set_position, "setPosition"); - lcb.add_function(l_anim_get_position, "getPosition"); - lcb.add_function(l_anim_set_speed, "setSpeed"); - lcb.add_function(l_anim_set_layer, "setLayer"); - lcb.add_function(l_anim_set_layers_from, "setLayersFrom"); - lcb.add_function(l_anim_set_hitresult, "setHitTestResult"); - lcb.add_function(l_anim_get_marker, "getMarker"); - lcb.add_function(l_anim_get_secondary_marker, "getSecondaryMarker"); - lcb.add_function(l_anim_tick, "tick"); - lcb.add_function(l_anim_draw, "draw", lua_metatable::surface); - lcb.add_function(l_anim_set_drawable_layer, "setDrawingLayer"); - } - - // Duplicate AnimMetatable[1,2] to SpriteListMetatable[1,2] - lua_rawgeti(pState->L, pState->metatables[static_cast(lua_metatable::anim)], 1); - lua_rawseti(pState->L, pState->metatables[static_cast(lua_metatable::sprite_list)], 1); - lua_rawgeti(pState->L, pState->metatables[static_cast(lua_metatable::anim)], 2); - lua_rawseti(pState->L, pState->metatables[static_cast(lua_metatable::sprite_list)], 2); - - // SpriteList - { - lua_class_binding lcb(pState, "spriteList", l_anim_new, lua_metatable::sprite_list); - lcb.add_metamethod(l_anim_persist, "persist"); - lcb.add_metamethod(l_anim_pre_depersist, "pre_depersist"); - lcb.add_metamethod(l_anim_depersist, "depersist"); - lcb.add_function(l_srl_set_sheet, "setSheet", lua_metatable::sheet); - lcb.add_function(l_srl_append, "append"); - lcb.add_function(l_srl_set_lifetime, "setLifetime"); - lcb.add_function(l_srl_is_dead, "isDead"); - lcb.add_function(l_anim_set_tile, "setTile", lua_metatable::map); - lcb.add_function(l_anim_set_flag, "setFlag"); - lcb.add_function(l_anim_set_flag_partial, "setPartialFlag"); - lcb.add_function(l_anim_get_flag, "getFlag"); - lcb.add_function(l_anim_make_visible, "makeVisible"); - lcb.add_function(l_anim_make_invisible, "makeInvisible"); - lcb.add_function(l_anim_set_position, "setPosition"); - lcb.add_function(l_anim_set_speed, "setSpeed"); - lcb.add_function(l_anim_set_layer, "setLayer"); - lcb.add_function(l_anim_tick, "tick"); - lcb.add_function(l_anim_draw, "draw", lua_metatable::surface); - } +template +int l_anim_set_flag_partial(lua_State* L) { + T* pAnimation = luaT_testuserdata(L); + uint32_t iFlags = static_cast(luaL_checkinteger(L, 2)); + if (lua_isnone(L, 3) || lua_toboolean(L, 3)) { + pAnimation->set_flags(pAnimation->get_flags() | iFlags); + } else { + pAnimation->set_flags(pAnimation->get_flags() & ~iFlags); + } + lua_settop(L, 1); + return 1; +} + +template +int l_anim_make_visible(lua_State* L) { + T* pAnimation = luaT_testuserdata(L); + pAnimation->set_flags(pAnimation->get_flags() & + ~static_cast(thdf_alpha_50 | thdf_alpha_75)); + + lua_settop(L, 1); + return 1; +} + +template +int l_anim_make_invisible(lua_State* L) { + T* pAnimation = luaT_testuserdata(L); + pAnimation->set_flags(pAnimation->get_flags() | + static_cast(thdf_alpha_50 | thdf_alpha_75)); + + lua_settop(L, 1); + return 1; +} + +template +int l_anim_get_flag(lua_State* L) { + T* pAnimation = luaT_testuserdata(L); + lua_pushinteger(L, pAnimation->get_flags()); + + return 1; +} + +template +int l_anim_set_position(lua_State* L) { + T* pAnimation = luaT_testuserdata(L); + + pAnimation->set_position(static_cast(luaL_checkinteger(L, 2)), + static_cast(luaL_checkinteger(L, 3))); + + lua_settop(L, 1); + return 1; +} + +int l_anim_get_position(lua_State* L) { + animation* pAnimation = luaT_testuserdata(L); + + lua_pushinteger(L, pAnimation->get_x()); + lua_pushinteger(L, pAnimation->get_y()); + + return 2; +} + +template +int l_anim_set_speed(lua_State* L) { + T* pAnimation = luaT_testuserdata(L); + + pAnimation->set_speed(static_cast(luaL_optinteger(L, 2, 0)), + static_cast(luaL_optinteger(L, 3, 0))); + + lua_settop(L, 1); + return 1; +} + +template +int l_anim_set_layer(lua_State* L) { + T* pAnimation = luaT_testuserdata(L); + + pAnimation->set_layer(static_cast(luaL_checkinteger(L, 2)), + static_cast(luaL_optinteger(L, 3, 0))); + + lua_settop(L, 1); + return 1; +} + +int l_anim_set_layers_from(lua_State* L) { + animation* pAnimation = luaT_testuserdata(L); + const animation* pAnimationSrc = + luaT_testuserdata(L, 2, luaT_environindex); + + pAnimation->set_layers_from(pAnimationSrc); + + lua_settop(L, 1); + return 1; +} + +int l_anim_set_tag(lua_State* L) { + luaT_testuserdata(L); + lua_settop(L, 2); + luaT_setenvfield(L, 1, "tag"); + return 1; +} + +int l_anim_get_tag(lua_State* L) { + luaT_testuserdata(L); + lua_settop(L, 1); + lua_getfenv(L, 1); + lua_getfield(L, 2, "tag"); + return 1; +} + +int l_anim_get_marker(lua_State* L) { + animation* pAnimation = luaT_testuserdata(L); + int iX = 0; + int iY = 0; + pAnimation->get_marker(&iX, &iY); + lua_pushinteger(L, iX); + lua_pushinteger(L, iY); + return 2; +} + +int l_anim_get_secondary_marker(lua_State* L) { + animation* pAnimation = luaT_testuserdata(L); + int iX = 0; + int iY = 0; + pAnimation->get_secondary_marker(&iX, &iY); + lua_pushinteger(L, iX); + lua_pushinteger(L, iY); + return 2; +} + +template +int l_anim_tick(lua_State* L) { + T* pAnimation = luaT_testuserdata(L); + pAnimation->tick(); + lua_settop(L, 1); + return 1; +} + +template +int l_anim_draw(lua_State* L) { + T* pAnimation = luaT_testuserdata(L); + render_target* pCanvas = luaT_testuserdata(L, 2); + pAnimation->draw(pCanvas, static_cast(luaL_checkinteger(L, 3)), + static_cast(luaL_checkinteger(L, 4))); + lua_settop(L, 1); + return 1; +} + +int l_srl_set_sheet(lua_State* L) { + sprite_render_list* pSrl = luaT_testuserdata(L); + sprite_sheet* pSheet = luaT_testuserdata(L, 2); + pSrl->set_sheet(pSheet); + + lua_settop(L, 2); + luaT_setenvfield(L, 1, "sheet"); + return 1; +} + +int l_srl_append(lua_State* L) { + sprite_render_list* pSrl = luaT_testuserdata(L); + pSrl->append_sprite(luaL_checkinteger(L, 2), + static_cast(luaL_checkinteger(L, 3)), + static_cast(luaL_checkinteger(L, 4))); + lua_settop(L, 1); + return 1; +} + +int l_srl_set_lifetime(lua_State* L) { + sprite_render_list* pSrl = luaT_testuserdata(L); + pSrl->set_lifetime(static_cast(luaL_checkinteger(L, 2))); + lua_settop(L, 1); + return 1; +} + +int l_srl_is_dead(lua_State* L) { + sprite_render_list* pSrl = luaT_testuserdata(L); + lua_pushboolean(L, pSrl->is_dead() ? 1 : 0); + return 1; +} + +} // namespace + +void lua_register_anims(const lua_register_state* pState) { + // Anims + { + lua_class_binding lcb(pState, "anims", l_anims_new, + lua_metatable::anims); + lcb.add_function(l_anims_load, "load"); + lcb.add_function(l_anims_loadcustom, "loadCustom"); + lcb.add_function(l_anims_set_spritesheet, "setSheet", lua_metatable::sheet); + lcb.add_function(l_anims_set_canvas, "setCanvas", lua_metatable::surface); + lcb.add_function(l_anims_getanims, "getAnimations"); + lcb.add_function(l_anims_getfirst, "getFirstFrame"); + lcb.add_function(l_anims_getnext, "getNextFrame"); + lcb.add_function(l_anims_set_alt_pal, "setAnimationGhostPalette"); + lcb.add_function(l_anims_set_marker, "setFrameMarker"); + lcb.add_function(l_anims_set_secondary_marker, "setFrameSecondaryMarker"); + lcb.add_function(l_anims_draw, "draw", lua_metatable::surface, + lua_metatable::layers); + lcb.add_constant("Alt32_GreyScale", thdf_alt32_grey_scale); + lcb.add_constant("Alt32_BlueRedSwap", thdf_alt32_blue_red_swap); + } + + // Weak table at AnimMetatable[1] for light UD -> object lookup + // For hitTest / setHitTestResult + lua_newtable(pState->L); + lua_createtable(pState->L, 0, 1); + lua_pushliteral(pState->L, "v"); + lua_setfield(pState->L, -2, "__mode"); + lua_setmetatable(pState->L, -2); + lua_rawseti(pState->L, + pState->metatables[static_cast(lua_metatable::anim)], 1); + + // Weak table at AnimMetatable[2] for light UD -> full UD lookup + // For persisting Map + lua_newtable(pState->L); + lua_createtable(pState->L, 0, 1); + lua_pushliteral(pState->L, "v"); + lua_setfield(pState->L, -2, "__mode"); + lua_setmetatable(pState->L, -2); + lua_rawseti(pState->L, + pState->metatables[static_cast(lua_metatable::anim)], 2); + + // Anim + { + lua_class_binding lcb(pState, "animation", l_anim_new, + lua_metatable::anim); + lcb.add_metamethod(l_anim_persist, "persist"); + lcb.add_metamethod(l_anim_pre_depersist, "pre_depersist"); + lcb.add_metamethod(l_anim_depersist, "depersist"); + lcb.add_function(l_anim_set_anim, "setAnimation", lua_metatable::anims); + lcb.add_function(l_anim_set_crop, "setCrop"); + lcb.add_function(l_anim_get_crop, "getCrop"); + lcb.add_function(l_anim_set_morph, "setMorph"); + lcb.add_function(l_anim_set_frame, "setFrame"); + lcb.add_function(l_anim_get_frame, "getFrame"); + lcb.add_function(l_anim_get_anim, "getAnimation"); + lcb.add_function(l_anim_set_tile, "setTile", lua_metatable::map); + lcb.add_function(l_anim_get_tile, "getTile"); + lcb.add_function(l_anim_set_parent, "setParent"); + lcb.add_function(l_anim_set_flag, "setFlag"); + lcb.add_function(l_anim_set_flag_partial, "setPartialFlag"); + lcb.add_function(l_anim_get_flag, "getFlag"); + lcb.add_function(l_anim_make_visible, "makeVisible"); + lcb.add_function(l_anim_make_invisible, "makeInvisible"); + lcb.add_function(l_anim_set_tag, "setTag"); + lcb.add_function(l_anim_get_tag, "getTag"); + lcb.add_function(l_anim_set_position, "setPosition"); + lcb.add_function(l_anim_get_position, "getPosition"); + lcb.add_function(l_anim_set_speed, "setSpeed"); + lcb.add_function(l_anim_set_layer, "setLayer"); + lcb.add_function(l_anim_set_layers_from, "setLayersFrom"); + lcb.add_function(l_anim_set_hitresult, "setHitTestResult"); + lcb.add_function(l_anim_get_marker, "getMarker"); + lcb.add_function(l_anim_get_secondary_marker, "getSecondaryMarker"); + lcb.add_function(l_anim_tick, "tick"); + lcb.add_function(l_anim_draw, "draw", lua_metatable::surface); + lcb.add_function(l_anim_set_drawable_layer, "setDrawingLayer"); + } + + // Duplicate AnimMetatable[1,2] to SpriteListMetatable[1,2] + lua_rawgeti(pState->L, + pState->metatables[static_cast(lua_metatable::anim)], 1); + lua_rawseti( + pState->L, + pState->metatables[static_cast(lua_metatable::sprite_list)], 1); + lua_rawgeti(pState->L, + pState->metatables[static_cast(lua_metatable::anim)], 2); + lua_rawseti( + pState->L, + pState->metatables[static_cast(lua_metatable::sprite_list)], 2); + + // SpriteList + { + lua_class_binding lcb(pState, "spriteList", + l_anim_new, + lua_metatable::sprite_list); + lcb.add_metamethod(l_anim_persist, "persist"); + lcb.add_metamethod(l_anim_pre_depersist, + "pre_depersist"); + lcb.add_metamethod(l_anim_depersist, "depersist"); + lcb.add_function(l_srl_set_sheet, "setSheet", lua_metatable::sheet); + lcb.add_function(l_srl_append, "append"); + lcb.add_function(l_srl_set_lifetime, "setLifetime"); + lcb.add_function(l_srl_is_dead, "isDead"); + lcb.add_function(l_anim_set_tile, "setTile", + lua_metatable::map); + lcb.add_function(l_anim_set_flag, "setFlag"); + lcb.add_function(l_anim_set_flag_partial, + "setPartialFlag"); + lcb.add_function(l_anim_get_flag, "getFlag"); + lcb.add_function(l_anim_make_visible, "makeVisible"); + lcb.add_function(l_anim_make_invisible, + "makeInvisible"); + lcb.add_function(l_anim_set_position, "setPosition"); + lcb.add_function(l_anim_set_speed, "setSpeed"); + lcb.add_function(l_anim_set_layer, "setLayer"); + lcb.add_function(l_anim_tick, "tick"); + lcb.add_function(l_anim_draw, "draw", + lua_metatable::surface); + } } diff --git a/CorsixTH/Src/th_lua_gfx.cpp b/CorsixTH/Src/th_lua_gfx.cpp index 95a3d895..46595447 100644 --- a/CorsixTH/Src/th_lua_gfx.cpp +++ b/CorsixTH/Src/th_lua_gfx.cpp @@ -20,968 +20,920 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -#include "th_lua_internal.h" -#include "th_gfx.h" #include #include #include +#include "th_gfx.h" +#include "th_gfx_font.h" +#include "th_lua_internal.h" namespace { -int l_palette_new(lua_State *L) -{ - luaT_stdnew(L); - return 1; +int l_palette_new(lua_State* L) { + luaT_stdnew(L); + return 1; } -int l_palette_load(lua_State *L) -{ - palette* pPalette = luaT_testuserdata(L); - size_t iDataLen; - const uint8_t* pData = luaT_checkfile(L, 2, &iDataLen); - - if(pPalette->load_from_th_file(pData, iDataLen)) - lua_pushboolean(L, 1); - else - lua_pushboolean(L, 0); - return 1; -} - -int l_palette_set_entry(lua_State *L) -{ - palette* pPalette = luaT_testuserdata(L); - lua_pushboolean(L, pPalette->set_entry(static_cast(luaL_checkinteger(L, 2)), - static_cast(luaL_checkinteger(L, 3)), - static_cast(luaL_checkinteger(L, 4)), - static_cast(luaL_checkinteger(L, 5))) - ? 1 : 0); - return 1; -} - -int l_rawbitmap_new(lua_State *L) -{ - luaT_stdnew(L, luaT_environindex, true); - return 1; -} - -int l_rawbitmap_set_pal(lua_State *L) -{ - raw_bitmap* pBitmap = luaT_testuserdata(L); - palette* pPalette = luaT_testuserdata(L, 2); - lua_settop(L, 2); - - pBitmap->set_palette(pPalette); - luaT_setenvfield(L, 1, "palette"); - return 1; -} - -int l_rawbitmap_load(lua_State *L) -{ - raw_bitmap* pBitmap = luaT_testuserdata(L); - size_t iDataLen; - const uint8_t* pData = luaT_checkfile(L, 2, &iDataLen); - int iWidth = static_cast(luaL_checkinteger(L, 3)); - render_target* pSurface = luaT_testuserdata(L, 4, luaT_upvalueindex(1), false); - - try { - pBitmap->load_from_th_file(pData, iDataLen, iWidth, pSurface); - } - catch (const std::exception& ex) { - lua_pushstring(L, ex.what()); - lua_error(L); - return 1; - } +int l_palette_load(lua_State* L) { + palette* pPalette = luaT_testuserdata(L); + size_t iDataLen; + const uint8_t* pData = luaT_checkfile(L, 2, &iDataLen); + if (pPalette->load_from_th_file(pData, iDataLen)) lua_pushboolean(L, 1); + else + lua_pushboolean(L, 0); + return 1; +} + +int l_palette_set_entry(lua_State* L) { + palette* pPalette = luaT_testuserdata(L); + lua_pushboolean( + L, pPalette->set_entry(static_cast(luaL_checkinteger(L, 2)), + static_cast(luaL_checkinteger(L, 3)), + static_cast(luaL_checkinteger(L, 4)), + static_cast(luaL_checkinteger(L, 5))) + ? 1 + : 0); + return 1; +} + +int l_rawbitmap_new(lua_State* L) { + luaT_stdnew(L, luaT_environindex, true); + return 1; +} + +int l_rawbitmap_set_pal(lua_State* L) { + raw_bitmap* pBitmap = luaT_testuserdata(L); + palette* pPalette = luaT_testuserdata(L, 2); + lua_settop(L, 2); + + pBitmap->set_palette(pPalette); + luaT_setenvfield(L, 1, "palette"); + return 1; +} + +int l_rawbitmap_load(lua_State* L) { + raw_bitmap* pBitmap = luaT_testuserdata(L); + size_t iDataLen; + const uint8_t* pData = luaT_checkfile(L, 2, &iDataLen); + int iWidth = static_cast(luaL_checkinteger(L, 3)); + render_target* pSurface = + luaT_testuserdata(L, 4, luaT_upvalueindex(1), false); + + try { + pBitmap->load_from_th_file(pData, iDataLen, iWidth, pSurface); + } catch (const std::exception& ex) { + lua_pushstring(L, ex.what()); + lua_error(L); return 1; + } + + lua_pushboolean(L, 1); + return 1; } -int l_rawbitmap_draw(lua_State *L) -{ - raw_bitmap* pBitmap = luaT_testuserdata(L); - render_target* pCanvas = luaT_testuserdata(L, 2); +int l_rawbitmap_draw(lua_State* L) { + raw_bitmap* pBitmap = luaT_testuserdata(L); + render_target* pCanvas = luaT_testuserdata(L, 2); - if(lua_gettop(L) >= 8) - { - pBitmap->draw(pCanvas, static_cast(luaL_checkinteger(L, 3)), static_cast(luaL_checkinteger(L, 4)), - static_cast(luaL_checkinteger(L, 5)), static_cast(luaL_checkinteger(L, 6)), static_cast(luaL_checkinteger(L, 7)), - static_cast(luaL_checkinteger(L, 8))); - } - else - pBitmap->draw(pCanvas, static_cast(luaL_optinteger(L, 3, 0)), static_cast(luaL_optinteger(L, 4, 0))); + if (lua_gettop(L) >= 8) { + pBitmap->draw(pCanvas, static_cast(luaL_checkinteger(L, 3)), + static_cast(luaL_checkinteger(L, 4)), + static_cast(luaL_checkinteger(L, 5)), + static_cast(luaL_checkinteger(L, 6)), + static_cast(luaL_checkinteger(L, 7)), + static_cast(luaL_checkinteger(L, 8))); + } else + pBitmap->draw(pCanvas, static_cast(luaL_optinteger(L, 3, 0)), + static_cast(luaL_optinteger(L, 4, 0))); - lua_settop(L, 1); - return 1; + lua_settop(L, 1); + return 1; } -int l_spritesheet_new(lua_State *L) -{ - luaT_stdnew(L, luaT_environindex, true); - return 1; +int l_spritesheet_new(lua_State* L) { + luaT_stdnew(L, luaT_environindex, true); + return 1; } -int l_spritesheet_set_pal(lua_State *L) -{ - sprite_sheet* pSheet = luaT_testuserdata(L); - palette* pPalette = luaT_testuserdata(L, 2); - lua_settop(L, 2); +int l_spritesheet_set_pal(lua_State* L) { + sprite_sheet* pSheet = luaT_testuserdata(L); + palette* pPalette = luaT_testuserdata(L, 2); + lua_settop(L, 2); - pSheet->set_palette(pPalette); - luaT_setenvfield(L, 1, "palette"); - return 1; + pSheet->set_palette(pPalette); + luaT_setenvfield(L, 1, "palette"); + return 1; } -int l_spritesheet_load(lua_State *L) -{ - sprite_sheet* pSheet = luaT_testuserdata(L); - size_t iDataLenTable, iDataLenChunk; - const uint8_t* pDataTable = luaT_checkfile(L, 2, &iDataLenTable); - const uint8_t* pDataChunk = luaT_checkfile(L, 3, &iDataLenChunk); - bool bComplex = lua_toboolean(L, 4) != 0; - render_target* pSurface = luaT_testuserdata(L, 5, luaT_upvalueindex(1), false); +int l_spritesheet_load(lua_State* L) { + sprite_sheet* pSheet = luaT_testuserdata(L); + size_t iDataLenTable, iDataLenChunk; + const uint8_t* pDataTable = luaT_checkfile(L, 2, &iDataLenTable); + const uint8_t* pDataChunk = luaT_checkfile(L, 3, &iDataLenChunk); + bool bComplex = lua_toboolean(L, 4) != 0; + render_target* pSurface = + luaT_testuserdata(L, 5, luaT_upvalueindex(1), false); - if(pSheet->load_from_th_file(pDataTable, iDataLenTable, pDataChunk, iDataLenChunk, bComplex, pSurface)) - lua_pushboolean(L, 1); - else - lua_pushboolean(L, 0); + if (pSheet->load_from_th_file(pDataTable, iDataLenTable, pDataChunk, + iDataLenChunk, bComplex, pSurface)) + lua_pushboolean(L, 1); + else + lua_pushboolean(L, 0); - return 1; + return 1; } -int l_spritesheet_count(lua_State *L) -{ - sprite_sheet* pSheet = luaT_testuserdata(L); +int l_spritesheet_count(lua_State* L) { + sprite_sheet* pSheet = luaT_testuserdata(L); - lua_pushinteger(L, pSheet->get_sprite_count()); - return 1; + lua_pushinteger(L, pSheet->get_sprite_count()); + return 1; } -int l_spritesheet_size(lua_State *L) -{ - sprite_sheet* pSheet = luaT_testuserdata(L); - size_t iSprite = luaL_checkinteger(L, 2); // No array adjustment - if(iSprite < 0 || iSprite >= pSheet->get_sprite_count()) - return luaL_argerror(L, 2, "Sprite index out of bounds"); +int l_spritesheet_size(lua_State* L) { + sprite_sheet* pSheet = luaT_testuserdata(L); + size_t iSprite = luaL_checkinteger(L, 2); // No array adjustment + if (iSprite < 0 || iSprite >= pSheet->get_sprite_count()) + return luaL_argerror(L, 2, "Sprite index out of bounds"); - unsigned int iWidth, iHeight; - pSheet->get_sprite_size_unchecked(iSprite, &iWidth, &iHeight); + unsigned int iWidth, iHeight; + pSheet->get_sprite_size_unchecked(iSprite, &iWidth, &iHeight); - lua_pushinteger(L, iWidth); - lua_pushinteger(L, iHeight); - return 2; + lua_pushinteger(L, iWidth); + lua_pushinteger(L, iHeight); + return 2; } -int l_spritesheet_draw(lua_State *L) -{ - sprite_sheet* pSheet = luaT_testuserdata(L); - render_target* pCanvas = luaT_testuserdata(L, 2); - int iSprite = static_cast(luaL_checkinteger(L, 3)); // No array adjustment +int l_spritesheet_draw(lua_State* L) { + sprite_sheet* pSheet = luaT_testuserdata(L); + render_target* pCanvas = luaT_testuserdata(L, 2); + int iSprite = + static_cast(luaL_checkinteger(L, 3)); // No array adjustment - pSheet->draw_sprite(pCanvas, iSprite, static_cast(luaL_optinteger(L, 4, 0)), static_cast(luaL_optinteger(L, 5, 0)), static_cast(luaL_optinteger(L, 6, 0))); + pSheet->draw_sprite(pCanvas, iSprite, + static_cast(luaL_optinteger(L, 4, 0)), + static_cast(luaL_optinteger(L, 5, 0)), + static_cast(luaL_optinteger(L, 6, 0))); - lua_settop(L, 1); - return 1; + lua_settop(L, 1); + return 1; } -int l_spritesheet_hittest(lua_State *L) -{ - sprite_sheet* pSheet = luaT_testuserdata(L); - size_t iSprite = luaL_checkinteger(L, 2); - int iX = static_cast(luaL_checkinteger(L, 3)); - int iY = static_cast(luaL_checkinteger(L, 4)); - uint32_t iFlags = static_cast(luaL_optinteger(L, 5, 0)); - return pSheet->hit_test_sprite(iSprite, iX, iY, iFlags); +int l_spritesheet_hittest(lua_State* L) { + sprite_sheet* pSheet = luaT_testuserdata(L); + size_t iSprite = luaL_checkinteger(L, 2); + int iX = static_cast(luaL_checkinteger(L, 3)); + int iY = static_cast(luaL_checkinteger(L, 4)); + uint32_t iFlags = static_cast(luaL_optinteger(L, 5, 0)); + return pSheet->hit_test_sprite(iSprite, iX, iY, iFlags); } -int l_spritesheet_isvisible(lua_State *L) -{ - sprite_sheet* pSheet = luaT_testuserdata(L); - size_t iSprite = luaL_checkinteger(L, 2); - argb_colour oDummy; - lua_pushboolean(L, pSheet->get_sprite_average_colour(iSprite, &oDummy) ? 1:0); - return 1; +int l_spritesheet_isvisible(lua_State* L) { + sprite_sheet* pSheet = luaT_testuserdata(L); + size_t iSprite = luaL_checkinteger(L, 2); + argb_colour oDummy; + lua_pushboolean(L, + pSheet->get_sprite_average_colour(iSprite, &oDummy) ? 1 : 0); + return 1; } -int l_font_new(lua_State *L) -{ - return luaL_error(L, "Cannot instantiate an interface"); +int l_font_new(lua_State* L) { + return luaL_error(L, "Cannot instantiate an interface"); } -int l_bitmap_font_new(lua_State *L) -{ - luaT_stdnew(L, luaT_environindex, true); - return 1; +int l_bitmap_font_new(lua_State* L) { + luaT_stdnew(L, luaT_environindex, true); + return 1; } -int l_bitmap_font_set_spritesheet(lua_State *L) -{ - bitmap_font* pFont = luaT_testuserdata(L); - sprite_sheet* pSheet = luaT_testuserdata(L, 2); - lua_settop(L, 2); +int l_bitmap_font_set_spritesheet(lua_State* L) { + bitmap_font* pFont = luaT_testuserdata(L); + sprite_sheet* pSheet = luaT_testuserdata(L, 2); + lua_settop(L, 2); - pFont->set_sprite_sheet(pSheet); - luaT_setenvfield(L, 1, "sprites"); - return 1; + pFont->set_sprite_sheet(pSheet); + luaT_setenvfield(L, 1, "sprites"); + return 1; } -int l_bitmap_font_get_spritesheet(lua_State *L) -{ - luaT_testuserdata(L); - luaT_getenvfield(L, 1, "sprites"); - return 1; +int l_bitmap_font_get_spritesheet(lua_State* L) { + luaT_testuserdata(L); + luaT_getenvfield(L, 1, "sprites"); + return 1; } -int l_bitmap_font_set_sep(lua_State *L) -{ - bitmap_font* pFont = luaT_testuserdata(L); +int l_bitmap_font_set_sep(lua_State* L) { + bitmap_font* pFont = luaT_testuserdata(L); - pFont->set_separation(static_cast(luaL_checkinteger(L, 2)), static_cast(luaL_optinteger(L, 3, 0))); + pFont->set_separation(static_cast(luaL_checkinteger(L, 2)), + static_cast(luaL_optinteger(L, 3, 0))); - lua_settop(L, 1); - return 1; + lua_settop(L, 1); + return 1; } #ifdef CORSIX_TH_USE_FREETYPE2 -void l_freetype_throw_error_code(lua_State *L, FT_Error e) -{ - if(e != FT_Err_Ok) - { - switch(e) - { +void l_freetype_throw_error_code(lua_State* L, FT_Error e) { + if (e != FT_Err_Ok) { + switch (e) { #undef __FTERRORS_H__ -#define FT_ERRORDEF(e, v, s) case e: lua_pushliteral(L, s); break; +#define FT_ERRORDEF(e, v, s) \ + case e: \ + lua_pushliteral(L, s); \ + break; #define FT_ERROR_START_LIST #define FT_ERROR_END_LIST #include FT_ERRORS_H - default: - lua_pushliteral(L, "Unrecognised FreeType2 error"); - break; - }; - lua_error(L); - } + default: + lua_pushliteral(L, "Unrecognised FreeType2 error"); + break; + }; + lua_error(L); + } } -int l_freetype_font_new(lua_State *L) -{ - freetype_font *pFont = luaT_stdnew(L, luaT_environindex, - true); - l_freetype_throw_error_code(L, pFont->initialise()); - return 1; +int l_freetype_font_new(lua_State* L) { + freetype_font* pFont = luaT_stdnew(L, luaT_environindex, true); + l_freetype_throw_error_code(L, pFont->initialise()); + return 1; } -int l_freetype_font_set_spritesheet(lua_State *L) -{ - freetype_font* pFont = luaT_testuserdata(L); - sprite_sheet* pSheet = luaT_testuserdata(L, 2); - lua_settop(L, 2); +int l_freetype_font_set_spritesheet(lua_State* L) { + freetype_font* pFont = luaT_testuserdata(L); + sprite_sheet* pSheet = luaT_testuserdata(L, 2); + lua_settop(L, 2); - l_freetype_throw_error_code(L, pFont->match_bitmap_font(pSheet)); - lua_settop(L, 1); - return 1; + l_freetype_throw_error_code(L, pFont->match_bitmap_font(pSheet)); + lua_settop(L, 1); + return 1; } -int l_freetype_font_get_copyright(lua_State *L) -{ - lua_pushstring(L, freetype_font::get_copyright_notice()); - return 1; +int l_freetype_font_get_copyright(lua_State* L) { + lua_pushstring(L, freetype_font::get_copyright_notice()); + return 1; } -int l_freetype_font_set_face(lua_State *L) -{ - freetype_font* pFont = luaT_testuserdata(L); - size_t iLength; - const uint8_t* pData = luaT_checkfile(L, 2, &iLength); - lua_settop(L, 2); +int l_freetype_font_set_face(lua_State* L) { + freetype_font* pFont = luaT_testuserdata(L); + size_t iLength; + const uint8_t* pData = luaT_checkfile(L, 2, &iLength); + lua_settop(L, 2); - l_freetype_throw_error_code(L, pFont->set_face(pData, iLength)); - luaT_setenvfield(L, 1, "face"); - return 1; + l_freetype_throw_error_code(L, pFont->set_face(pData, iLength)); + luaT_setenvfield(L, 1, "face"); + return 1; } -int l_freetype_font_clear_cache(lua_State *L) -{ - freetype_font* pFont = luaT_testuserdata(L); - pFont->clear_cache(); - return 0; +int l_freetype_font_clear_cache(lua_State* L) { + freetype_font* pFont = luaT_testuserdata(L); + pFont->clear_cache(); + return 0; } #endif -int l_font_get_size(lua_State *L) -{ - font* pFont = luaT_testuserdata(L); - size_t iMsgLen; - const char* sMsg = luaT_checkstring(L, 2, &iMsgLen); +int l_font_get_size(lua_State* L) { + font* pFont = luaT_testuserdata(L); + size_t iMsgLen; + const char* sMsg = luaT_checkstring(L, 2, &iMsgLen); - int iMaxWidth = INT_MAX; - if(!lua_isnoneornil(L, 3)) - iMaxWidth = static_cast(luaL_checkinteger(L, 3)); + int iMaxWidth = INT_MAX; + if (!lua_isnoneornil(L, 3)) + iMaxWidth = static_cast(luaL_checkinteger(L, 3)); - text_layout oDrawArea = pFont->get_text_dimensions(sMsg, iMsgLen, iMaxWidth); + text_layout oDrawArea = pFont->get_text_dimensions(sMsg, iMsgLen, iMaxWidth); - lua_pushinteger(L, oDrawArea.end_x); - lua_pushinteger(L, oDrawArea.end_y); - lua_pushinteger(L, oDrawArea.row_count); + lua_pushinteger(L, oDrawArea.end_x); + lua_pushinteger(L, oDrawArea.end_y); + lua_pushinteger(L, oDrawArea.row_count); - return 3; + return 3; } -int l_font_draw(lua_State *L) -{ - font* pFont = luaT_testuserdata(L); - render_target* pCanvas = nullptr; - if(!lua_isnoneornil(L, 2)) - { - pCanvas = luaT_testuserdata(L, 2); - } - size_t iMsgLen; - const char* sMsg = luaT_checkstring(L, 3, &iMsgLen); - int iX = static_cast(luaL_checkinteger(L, 4)); - int iY = static_cast(luaL_checkinteger(L, 5)); +int l_font_draw(lua_State* L) { + font* pFont = luaT_testuserdata(L); + render_target* pCanvas = nullptr; + if (!lua_isnoneornil(L, 2)) { + pCanvas = luaT_testuserdata(L, 2); + } + size_t iMsgLen; + const char* sMsg = luaT_checkstring(L, 3, &iMsgLen); + int iX = static_cast(luaL_checkinteger(L, 4)); + int iY = static_cast(luaL_checkinteger(L, 5)); - text_alignment eAlign = text_alignment::center; - if(!lua_isnoneornil(L, 8)) { - const char* sAlign = luaL_checkstring(L, 8); - if(std::strcmp(sAlign, "right") == 0) { - eAlign = text_alignment::right; - } else if(std::strcmp(sAlign, "left") == 0) { - eAlign = text_alignment::left; - } else if(std::strcmp(sAlign, "center") == 0 || - std::strcmp(sAlign, "centre") == 0 || - std::strcmp(sAlign, "middle") == 0) { - eAlign = text_alignment::center; - } else { - return luaL_error(L, "Invalid alignment: \"%s\"", sAlign); - } + text_alignment eAlign = text_alignment::center; + if (!lua_isnoneornil(L, 8)) { + const char* sAlign = luaL_checkstring(L, 8); + if (std::strcmp(sAlign, "right") == 0) { + eAlign = text_alignment::right; + } else if (std::strcmp(sAlign, "left") == 0) { + eAlign = text_alignment::left; + } else if (std::strcmp(sAlign, "center") == 0 || + std::strcmp(sAlign, "centre") == 0 || + std::strcmp(sAlign, "middle") == 0) { + eAlign = text_alignment::center; + } else { + return luaL_error(L, "Invalid alignment: \"%s\"", sAlign); } + } - text_layout oDrawArea = pFont->get_text_dimensions(sMsg, iMsgLen); - if(!lua_isnoneornil(L, 7)) - { - int iW = static_cast(luaL_checkinteger(L, 6)); - int iH = static_cast(luaL_checkinteger(L, 7)); - if(iW > oDrawArea.end_x && eAlign != text_alignment::left) { - iX += (iW - oDrawArea.end_x) / ((eAlign == text_alignment::center) ? 2 : 1); - } - if(iH > oDrawArea.end_y) { - iY += (iH - oDrawArea.end_y) / 2; - } - } - if(pCanvas != nullptr) - { - pFont->draw_text(pCanvas, sMsg, iMsgLen, iX, iY); - } - lua_pushinteger(L, iY + oDrawArea.end_y); - lua_pushinteger(L, iX + oDrawArea.end_x); - - return 2; -} - -int l_font_draw_wrapped(lua_State *L) -{ - font* pFont = luaT_testuserdata(L); - render_target* pCanvas = nullptr; - if(!lua_isnoneornil(L, 2)) - { - pCanvas = luaT_testuserdata(L, 2); - } - size_t iMsgLen; - const char* sMsg = luaT_checkstring(L, 3, &iMsgLen); - int iX = static_cast(luaL_checkinteger(L, 4)); - int iY = static_cast(luaL_checkinteger(L, 5)); + text_layout oDrawArea = pFont->get_text_dimensions(sMsg, iMsgLen); + if (!lua_isnoneornil(L, 7)) { int iW = static_cast(luaL_checkinteger(L, 6)); - - text_alignment eAlign = text_alignment::left; - if(!lua_isnoneornil(L, 7)) { - const char* sAlign = luaL_checkstring(L, 7); - if(std::strcmp(sAlign, "right") == 0) { - eAlign = text_alignment::right; - } else if(std::strcmp(sAlign, "left") == 0) { - eAlign = text_alignment::left; - } else if(std::strcmp(sAlign, "center") == 0 || - std::strcmp(sAlign, "centre") == 0 || - std::strcmp(sAlign, "middle") == 0) { - eAlign = text_alignment::center; - } else { - return luaL_error(L, "Invalid alignment: \"%s\"", sAlign); - } + int iH = static_cast(luaL_checkinteger(L, 7)); + if (iW > oDrawArea.end_x && eAlign != text_alignment::left) { + iX += + (iW - oDrawArea.end_x) / ((eAlign == text_alignment::center) ? 2 : 1); } - - int iMaxRows = INT_MAX; - if(!lua_isnoneornil(L, 8)) - { - iMaxRows = static_cast(luaL_checkinteger(L, 8)); + if (iH > oDrawArea.end_y) { + iY += (iH - oDrawArea.end_y) / 2; } + } + if (pCanvas != nullptr) { + pFont->draw_text(pCanvas, sMsg, iMsgLen, iX, iY); + } + lua_pushinteger(L, iY + oDrawArea.end_y); + lua_pushinteger(L, iX + oDrawArea.end_x); - int iSkipRows = 0; - if(!lua_isnoneornil(L, 9)) - { - iSkipRows = static_cast(luaL_checkinteger(L, 9)); - } - - text_layout oDrawArea = pFont->draw_text_wrapped(pCanvas, sMsg, iMsgLen, iX, iY, - iW, iMaxRows, iSkipRows, eAlign); - lua_pushinteger(L, oDrawArea.end_y); - lua_pushinteger(L, oDrawArea.end_x); - lua_pushinteger(L, oDrawArea.row_count); - - return 3; + return 2; } -int l_font_draw_tooltip(lua_State *L) -{ - font* pFont = luaT_testuserdata(L); - render_target* pCanvas = luaT_testuserdata(L, 2); - size_t iMsgLen; - const char* sMsg = luaT_checkstring(L, 3, &iMsgLen); - int iX = static_cast(luaL_checkinteger(L, 4)); - int iY = static_cast(luaL_checkinteger(L, 5)); - int iScreenWidth = pCanvas->get_width(); +int l_font_draw_wrapped(lua_State* L) { + font* pFont = luaT_testuserdata(L); + render_target* pCanvas = nullptr; + if (!lua_isnoneornil(L, 2)) { + pCanvas = luaT_testuserdata(L, 2); + } + size_t iMsgLen; + const char* sMsg = luaT_checkstring(L, 3, &iMsgLen); + int iX = static_cast(luaL_checkinteger(L, 4)); + int iY = static_cast(luaL_checkinteger(L, 5)); + int iW = static_cast(luaL_checkinteger(L, 6)); - int iW = 200; // (for now) hardcoded width of tooltips - uint32_t iBlack = render_target::map_colour(0x00, 0x00, 0x00); - uint32_t iWhite = render_target::map_colour(0xFF, 0xFF, 0xFF); - text_layout oArea = pFont->draw_text_wrapped(nullptr, sMsg, iMsgLen, iX + 2, iY + 1, iW - 4, INT_MAX, 0); - int iLastX = iX + oArea.width + 3; - int iFirstY = iY - (oArea.end_y - iY) - 1; + text_alignment eAlign = text_alignment::left; + if (!lua_isnoneornil(L, 7)) { + const char* sAlign = luaL_checkstring(L, 7); + if (std::strcmp(sAlign, "right") == 0) { + eAlign = text_alignment::right; + } else if (std::strcmp(sAlign, "left") == 0) { + eAlign = text_alignment::left; + } else if (std::strcmp(sAlign, "center") == 0 || + std::strcmp(sAlign, "centre") == 0 || + std::strcmp(sAlign, "middle") == 0) { + eAlign = text_alignment::center; + } else { + return luaL_error(L, "Invalid alignment: \"%s\"", sAlign); + } + } - int iXOffset = iLastX > iScreenWidth ? iScreenWidth - iLastX : 0; - int iYOffset = iFirstY < 0 ? -iFirstY : 0; + int iMaxRows = INT_MAX; + if (!lua_isnoneornil(L, 8)) { + iMaxRows = static_cast(luaL_checkinteger(L, 8)); + } - pCanvas->fill_rect(iBlack, iX + iXOffset, iFirstY + iYOffset, oArea.width + 3, oArea.end_y - iY + 2); - pCanvas->fill_rect(iWhite, iX + iXOffset + 1, iFirstY + 1 + iYOffset, oArea.width + 1, oArea.end_y - iY); + int iSkipRows = 0; + if (!lua_isnoneornil(L, 9)) { + iSkipRows = static_cast(luaL_checkinteger(L, 9)); + } - pFont->draw_text_wrapped(pCanvas, sMsg, iMsgLen, iX + 2 + iXOffset, iFirstY + 1 + iYOffset, iW - 4); + text_layout oDrawArea = pFont->draw_text_wrapped( + pCanvas, sMsg, iMsgLen, iX, iY, iW, iMaxRows, iSkipRows, eAlign); + lua_pushinteger(L, oDrawArea.end_y); + lua_pushinteger(L, oDrawArea.end_x); + lua_pushinteger(L, oDrawArea.row_count); - lua_pushinteger(L, oArea.end_y); + return 3; +} +int l_font_draw_tooltip(lua_State* L) { + font* pFont = luaT_testuserdata(L); + render_target* pCanvas = luaT_testuserdata(L, 2); + size_t iMsgLen; + const char* sMsg = luaT_checkstring(L, 3, &iMsgLen); + int iX = static_cast(luaL_checkinteger(L, 4)); + int iY = static_cast(luaL_checkinteger(L, 5)); + int iScreenWidth = pCanvas->get_width(); + + int iW = 200; // (for now) hardcoded width of tooltips + uint32_t iBlack = render_target::map_colour(0x00, 0x00, 0x00); + uint32_t iWhite = render_target::map_colour(0xFF, 0xFF, 0xFF); + text_layout oArea = pFont->draw_text_wrapped(nullptr, sMsg, iMsgLen, iX + 2, + iY + 1, iW - 4, INT_MAX, 0); + int iLastX = iX + oArea.width + 3; + int iFirstY = iY - (oArea.end_y - iY) - 1; + + int iXOffset = iLastX > iScreenWidth ? iScreenWidth - iLastX : 0; + int iYOffset = iFirstY < 0 ? -iFirstY : 0; + + pCanvas->fill_rect(iBlack, iX + iXOffset, iFirstY + iYOffset, oArea.width + 3, + oArea.end_y - iY + 2); + pCanvas->fill_rect(iWhite, iX + iXOffset + 1, iFirstY + 1 + iYOffset, + oArea.width + 1, oArea.end_y - iY); + + pFont->draw_text_wrapped(pCanvas, sMsg, iMsgLen, iX + 2 + iXOffset, + iFirstY + 1 + iYOffset, iW - 4); + + lua_pushinteger(L, oArea.end_y); + + return 1; +} + +int l_layers_new(lua_State* L) { + layers* pLayers = luaT_stdnew(L, luaT_environindex, false); + for (int i = 0; i < 13; ++i) pLayers->layer_contents[i] = 0; + return 1; +} + +int l_layers_get(lua_State* L) { + layers* pLayers = luaT_testuserdata(L); + lua_Integer iLayer = luaL_checkinteger(L, 2); + if (0 <= iLayer && iLayer < 13) + lua_pushinteger(L, pLayers->layer_contents[iLayer]); + else + lua_pushnil(L); + return 1; +} + +int l_layers_set(lua_State* L) { + layers* pLayers = luaT_testuserdata(L); + lua_Integer iLayer = luaL_checkinteger(L, 2); + uint8_t iValue = static_cast(luaL_checkinteger(L, 3)); + if (0 <= iLayer && iLayer < 13) pLayers->layer_contents[iLayer] = iValue; + return 0; +} + +int l_layers_persist(lua_State* L) { + layers* pLayers = luaT_testuserdata(L); + lua_settop(L, 2); + lua_insert(L, 1); + lua_persist_writer* pWriter = (lua_persist_writer*)lua_touserdata(L, 1); + + int iNumLayers = 13; + for (; iNumLayers >= 1; --iNumLayers) { + if (pLayers->layer_contents[iNumLayers - 1] != 0) break; + } + pWriter->write_uint(iNumLayers); + pWriter->write_byte_stream(pLayers->layer_contents, iNumLayers); + return 0; +} + +int l_layers_depersist(lua_State* L) { + layers* pLayers = luaT_testuserdata(L); + lua_settop(L, 2); + lua_insert(L, 1); + lua_persist_reader* pReader = (lua_persist_reader*)lua_touserdata(L, 1); + + std::memset(pLayers->layer_contents, 0, sizeof(pLayers->layer_contents)); + int iNumLayers; + if (!pReader->read_uint(iNumLayers)) return 0; + if (iNumLayers > 13) { + if (!pReader->read_byte_stream(pLayers->layer_contents, 13)) return 0; + if (!pReader->read_byte_stream(nullptr, iNumLayers - 13)) return 0; + } else { + if (!pReader->read_byte_stream(pLayers->layer_contents, iNumLayers)) + return 0; + } + return 0; +} + +int l_cursor_new(lua_State* L) { + luaT_stdnew(L, luaT_environindex, false); + return 1; +} + +int l_cursor_load(lua_State* L) { + cursor* pCursor = luaT_testuserdata(L); + sprite_sheet* pSheet = luaT_testuserdata(L, 2); + if (pCursor->create_from_sprite(pSheet, + static_cast(luaL_checkinteger(L, 3)), + static_cast(luaL_optinteger(L, 4, 0)), + static_cast(luaL_optinteger(L, 5, 0)))) { + lua_settop(L, 1); return 1; -} - -int l_layers_new(lua_State *L) -{ - layers* pLayers = luaT_stdnew(L, luaT_environindex, false); - for(int i = 0; i < 13; ++i) - pLayers->layer_contents[i] = 0; + } else { + lua_pushboolean(L, 0); return 1; + } } -int l_layers_get(lua_State *L) -{ - layers* pLayers = luaT_testuserdata(L); - lua_Integer iLayer = luaL_checkinteger(L, 2); - if(0 <= iLayer && iLayer < 13) - lua_pushinteger(L, pLayers->layer_contents[iLayer]); - else - lua_pushnil(L); - return 1; +int l_cursor_use(lua_State* L) { + cursor* pCursor = luaT_testuserdata(L); + render_target* pCanvas = luaT_testuserdata(L, 2); + pCursor->use(pCanvas); + return 0; } -int l_layers_set(lua_State *L) -{ - layers* pLayers = luaT_testuserdata(L); - lua_Integer iLayer = luaL_checkinteger(L, 2); - uint8_t iValue = static_cast(luaL_checkinteger(L, 3)); - if(0 <= iLayer && iLayer < 13) - pLayers->layer_contents[iLayer] = iValue; - return 0; -} - -int l_layers_persist(lua_State *L) -{ - layers* pLayers = luaT_testuserdata(L); - lua_settop(L, 2); - lua_insert(L, 1); - lua_persist_writer* pWriter = (lua_persist_writer*)lua_touserdata(L, 1); - - int iNumLayers = 13; - for( ; iNumLayers >= 1; --iNumLayers) - { - if(pLayers->layer_contents[iNumLayers - 1] != 0) - break; - } - pWriter->write_uint(iNumLayers); - pWriter->write_byte_stream(pLayers->layer_contents, iNumLayers); - return 0; -} - -int l_layers_depersist(lua_State *L) -{ - layers* pLayers = luaT_testuserdata(L); - lua_settop(L, 2); - lua_insert(L, 1); - lua_persist_reader* pReader = (lua_persist_reader*)lua_touserdata(L, 1); - - std::memset(pLayers->layer_contents, 0, sizeof(pLayers->layer_contents)); - int iNumLayers; - if(!pReader->read_uint(iNumLayers)) - return 0; - if(iNumLayers > 13) - { - if(!pReader->read_byte_stream(pLayers->layer_contents, 13)) - return 0; - if(!pReader->read_byte_stream(nullptr, iNumLayers - 13)) - return 0; - } - else - { - if(!pReader->read_byte_stream(pLayers->layer_contents, iNumLayers)) - return 0; - } - return 0; -} - -int l_cursor_new(lua_State *L) -{ - luaT_stdnew(L, luaT_environindex, false); - return 1; -} - -int l_cursor_load(lua_State *L) -{ - cursor* pCursor = luaT_testuserdata(L); - sprite_sheet* pSheet = luaT_testuserdata(L, 2); - if(pCursor->create_from_sprite(pSheet, static_cast(luaL_checkinteger(L, 3)), - static_cast(luaL_optinteger(L, 4, 0)), static_cast(luaL_optinteger(L, 5, 0)))) - { - lua_settop(L, 1); - return 1; - } - else - { - lua_pushboolean(L, 0); - return 1; - } -} - -int l_cursor_use(lua_State *L) -{ - cursor* pCursor = luaT_testuserdata(L); - render_target* pCanvas = luaT_testuserdata(L, 2); - pCursor->use(pCanvas); - return 0; -} - -int l_cursor_position(lua_State *L) -{ - render_target* pCanvas = luaT_testuserdata(L, 1, luaT_upvalueindex(1)); - lua_pushboolean(L, cursor::set_position(pCanvas, static_cast(luaL_checkinteger(L, 2)), static_cast(luaL_checkinteger(L, 3))) ? 1 : 0); - return 1; +int l_cursor_position(lua_State* L) { + render_target* pCanvas = + luaT_testuserdata(L, 1, luaT_upvalueindex(1)); + lua_pushboolean(L, cursor::set_position( + pCanvas, static_cast(luaL_checkinteger(L, 2)), + static_cast(luaL_checkinteger(L, 3))) + ? 1 + : 0); + return 1; } /** Construct the helper structure for making a #THRenderTarget. */ -render_target_creation_params l_surface_creation_params(lua_State *L, int iArgStart) -{ - render_target_creation_params oParams; - oParams.width = static_cast(luaL_checkinteger(L, iArgStart)); - oParams.height = static_cast(luaL_checkinteger(L, iArgStart + 1)); +render_target_creation_params l_surface_creation_params(lua_State* L, + int iArgStart) { + render_target_creation_params oParams; + oParams.width = static_cast(luaL_checkinteger(L, iArgStart)); + oParams.height = static_cast(luaL_checkinteger(L, iArgStart + 1)); - oParams.fullscreen = false; - oParams.present_immediate = false; + oParams.fullscreen = false; + oParams.present_immediate = false; - // Parse string arguments, looking for matching parameter names. - for(int iArg = iArgStart + 2, iArgCount = lua_gettop(L); iArg <= iArgCount; ++iArg) - { - const char* sOption = luaL_checkstring(L, iArg); - if(sOption[0] == 0) - continue; + // Parse string arguments, looking for matching parameter names. + for (int iArg = iArgStart + 2, iArgCount = lua_gettop(L); iArg <= iArgCount; + ++iArg) { + const char* sOption = luaL_checkstring(L, iArg); + if (sOption[0] == 0) continue; - if (std::strcmp(sOption, "fullscreen") == 0) oParams.fullscreen = true; - if (std::strcmp(sOption, "present immediate") == 0) oParams.present_immediate = true; - } + if (std::strcmp(sOption, "fullscreen") == 0) oParams.fullscreen = true; + if (std::strcmp(sOption, "present immediate") == 0) + oParams.present_immediate = true; + } - return oParams; + return oParams; } -int l_surface_new(lua_State *L) -{ - lua_remove(L, 1); // Value inserted by __call +int l_surface_new(lua_State* L) { + lua_remove(L, 1); // Value inserted by __call - render_target_creation_params oParams = l_surface_creation_params(L, 1); - render_target* pCanvas = luaT_stdnew(L); - if(pCanvas->create(&oParams)) - return 1; + render_target_creation_params oParams = l_surface_creation_params(L, 1); + render_target* pCanvas = luaT_stdnew(L); + if (pCanvas->create(&oParams)) return 1; + lua_pushnil(L); + lua_pushstring(L, pCanvas->get_last_error()); + return 2; +} + +int l_surface_update(lua_State* L) { + render_target* pCanvas = luaT_testuserdata(L); + render_target_creation_params oParams = l_surface_creation_params(L, 2); + if (pCanvas->update(&oParams)) { lua_pushnil(L); - lua_pushstring(L, pCanvas->get_last_error()); - return 2; -} - -int l_surface_update(lua_State *L) -{ - render_target* pCanvas = luaT_testuserdata(L); - render_target_creation_params oParams = l_surface_creation_params(L, 2); - if(pCanvas->update(&oParams)) - { - lua_pushnil(L); - return 1; - } - - lua_pushstring(L, pCanvas->get_last_error()); return 1; + } + + lua_pushstring(L, pCanvas->get_last_error()); + return 1; } -int l_surface_destroy(lua_State *L) -{ - render_target* pCanvas = luaT_testuserdata(L); - pCanvas->end_frame(); - pCanvas->destroy(); - return 1; +int l_surface_destroy(lua_State* L) { + render_target* pCanvas = luaT_testuserdata(L); + pCanvas->end_frame(); + pCanvas->destroy(); + return 1; } -int l_surface_fill_black(lua_State *L) -{ - render_target* pCanvas = luaT_testuserdata(L); - lua_settop(L, 1); - if(pCanvas->fill_black()) - return 1; - lua_pushnil(L); - lua_pushstring(L, pCanvas->get_last_error()); - return 2; +int l_surface_fill_black(lua_State* L) { + render_target* pCanvas = luaT_testuserdata(L); + lua_settop(L, 1); + if (pCanvas->fill_black()) return 1; + lua_pushnil(L); + lua_pushstring(L, pCanvas->get_last_error()); + return 2; } -int l_surface_start_frame(lua_State *L) -{ - render_target* pCanvas = luaT_testuserdata(L); - lua_settop(L, 1); - if(pCanvas->start_frame()) - return 1; - lua_pushnil(L); - lua_pushstring(L, pCanvas->get_last_error()); - return 2; +int l_surface_start_frame(lua_State* L) { + render_target* pCanvas = luaT_testuserdata(L); + lua_settop(L, 1); + if (pCanvas->start_frame()) return 1; + lua_pushnil(L); + lua_pushstring(L, pCanvas->get_last_error()); + return 2; } -int l_surface_end_frame(lua_State *L) -{ - render_target* pCanvas = luaT_testuserdata(L); - lua_settop(L, 1); - if(pCanvas->end_frame()) - return 1; - lua_pushnil(L); - lua_pushstring(L, pCanvas->get_last_error()); - return 2; +int l_surface_end_frame(lua_State* L) { + render_target* pCanvas = luaT_testuserdata(L); + lua_settop(L, 1); + if (pCanvas->end_frame()) return 1; + lua_pushnil(L); + lua_pushstring(L, pCanvas->get_last_error()); + return 2; } -int l_surface_nonoverlapping(lua_State *L) -{ - render_target* pCanvas = luaT_testuserdata(L); - if(lua_isnone(L, 2) || lua_toboolean(L, 2) != 0) - pCanvas->start_nonoverlapping_draws(); - else - pCanvas->finish_nonoverlapping_draws(); +int l_surface_nonoverlapping(lua_State* L) { + render_target* pCanvas = luaT_testuserdata(L); + if (lua_isnone(L, 2) || lua_toboolean(L, 2) != 0) + pCanvas->start_nonoverlapping_draws(); + else + pCanvas->finish_nonoverlapping_draws(); + lua_settop(L, 1); + return 1; +} + +int l_surface_set_blue_filter_active(lua_State* L) { + render_target* pCanvas = luaT_testuserdata(L); + pCanvas->set_blue_filter_active( + (lua_isnoneornil(L, 2) != 0) ? false : (lua_toboolean(L, 2) != 0)); + return 1; +} + +int l_surface_map(lua_State* L) { + render_target* pCanvas = luaT_testuserdata(L); + lua_pushnumber( + L, (lua_Number)render_target::map_colour((Uint8)luaL_checkinteger(L, 2), + (Uint8)luaL_checkinteger(L, 3), + (Uint8)luaL_checkinteger(L, 4))); + return 1; +} + +int l_surface_rect(lua_State* L) { + render_target* pCanvas = luaT_testuserdata(L); + if (pCanvas->fill_rect(static_cast(luaL_checkinteger(L, 2)), + static_cast(luaL_checkinteger(L, 3)), + static_cast(luaL_checkinteger(L, 4)), + static_cast(luaL_checkinteger(L, 5)), + static_cast(luaL_checkinteger(L, 6)))) { lua_settop(L, 1); return 1; + } + lua_pushnil(L); + lua_pushstring(L, pCanvas->get_last_error()); + return 2; } -int l_surface_set_blue_filter_active(lua_State *L) -{ - render_target* pCanvas = luaT_testuserdata(L); - pCanvas->set_blue_filter_active((lua_isnoneornil(L, 2) != 0) ? false : (lua_toboolean(L, 2) != 0)); - return 1; -} - -int l_surface_map(lua_State *L) -{ - render_target* pCanvas = luaT_testuserdata(L); - lua_pushnumber(L, (lua_Number)render_target::map_colour( - (Uint8)luaL_checkinteger(L, 2), - (Uint8)luaL_checkinteger(L, 3), - (Uint8)luaL_checkinteger(L, 4))); - return 1; -} - -int l_surface_rect(lua_State *L) -{ - render_target* pCanvas = luaT_testuserdata(L); - if(pCanvas->fill_rect(static_cast(luaL_checkinteger(L, 2)), - static_cast(luaL_checkinteger(L, 3)), static_cast(luaL_checkinteger(L, 4)), static_cast(luaL_checkinteger(L, 5)), - static_cast(luaL_checkinteger(L, 6)))) - { - lua_settop(L, 1); - return 1; - } - lua_pushnil(L); - lua_pushstring(L, pCanvas->get_last_error()); - return 2; -} - -int l_surface_screenshot(lua_State *L) -{ - render_target* pCanvas = luaT_testuserdata(L); - const char *sFile = luaL_checkstring(L, 2); - if(pCanvas->take_screenshot(sFile)) - { - lua_settop(L, 1); - return 1; - } - lua_pushnil(L); - lua_pushstring(L, pCanvas->get_last_error()); - return 2; -} - -int l_surface_get_clip(lua_State *L) -{ - render_target* pCanvas = luaT_testuserdata(L); - clip_rect rcClip; - pCanvas->get_clip_rect(&rcClip); - lua_pushinteger(L, rcClip.x); - lua_pushinteger(L, rcClip.y); - lua_pushinteger(L, rcClip.w); - lua_pushinteger(L, rcClip.h); - return 4; -} - -int l_surface_set_clip(lua_State *L) -{ - render_target* pCanvas = luaT_testuserdata(L); - clip_rect rcClip; - rcClip.x = static_cast(luaL_checkinteger(L, 2)); - rcClip.y = static_cast(luaL_checkinteger(L, 3)); - rcClip.w = static_cast(luaL_checkinteger(L, 4)); - rcClip.h = static_cast(luaL_checkinteger(L, 5)); - if(lua_toboolean(L, 6) != 0) - { - clip_rect rcExistingClip; - pCanvas->get_clip_rect(&rcExistingClip); - clip_rect_intersection(rcClip, rcExistingClip); - } - pCanvas->set_clip_rect(&rcClip); +int l_surface_screenshot(lua_State* L) { + render_target* pCanvas = luaT_testuserdata(L); + const char* sFile = luaL_checkstring(L, 2); + if (pCanvas->take_screenshot(sFile)) { lua_settop(L, 1); return 1; + } + lua_pushnil(L); + lua_pushstring(L, pCanvas->get_last_error()); + return 2; } -int l_surface_scale(lua_State *L) -{ - render_target* pCanvas = luaT_testuserdata(L); - scaled_items eToScale = scaled_items::none; - if(lua_isnoneornil(L, 3)) - { - eToScale = scaled_items::all; - } - else - { - size_t iLength; - const char* sOption = lua_tolstring(L, 3, &iLength); - if(sOption && iLength >= 6 && std::memcmp(sOption, "bitmap", 6) == 0) - { - eToScale = scaled_items::bitmaps; - } - else - luaL_error(L, "Expected \"bitmap\" as 2nd argument"); - } - lua_pushboolean(L, pCanvas->set_scale_factor(static_cast( - luaL_checknumber(L, 2)), eToScale) ? 1 : 0); - return 1; +int l_surface_get_clip(lua_State* L) { + render_target* pCanvas = luaT_testuserdata(L); + clip_rect rcClip; + pCanvas->get_clip_rect(&rcClip); + lua_pushinteger(L, rcClip.x); + lua_pushinteger(L, rcClip.y); + lua_pushinteger(L, rcClip.w); + lua_pushinteger(L, rcClip.h); + return 4; } -int l_surface_set_caption(lua_State *L) -{ - render_target* pCanvas = luaT_testuserdata(L); - pCanvas->set_caption(luaL_checkstring(L, 2)); - - lua_settop(L, 1); - return 1; +int l_surface_set_clip(lua_State* L) { + render_target* pCanvas = luaT_testuserdata(L); + clip_rect rcClip; + rcClip.x = static_cast(luaL_checkinteger(L, 2)); + rcClip.y = static_cast(luaL_checkinteger(L, 3)); + rcClip.w = static_cast(luaL_checkinteger(L, 4)); + rcClip.h = static_cast(luaL_checkinteger(L, 5)); + if (lua_toboolean(L, 6) != 0) { + clip_rect rcExistingClip; + pCanvas->get_clip_rect(&rcExistingClip); + clip_rect_intersection(rcClip, rcExistingClip); + } + pCanvas->set_clip_rect(&rcClip); + lua_settop(L, 1); + return 1; } -int l_surface_get_renderer_details(lua_State *L) -{ - render_target* pCanvas = luaT_testuserdata(L); - lua_pushstring(L, pCanvas->get_renderer_details()); - return 1; +int l_surface_scale(lua_State* L) { + render_target* pCanvas = luaT_testuserdata(L); + scaled_items eToScale = scaled_items::none; + if (lua_isnoneornil(L, 3)) { + eToScale = scaled_items::all; + } else { + size_t iLength; + const char* sOption = lua_tolstring(L, 3, &iLength); + if (sOption && iLength >= 6 && std::memcmp(sOption, "bitmap", 6) == 0) { + eToScale = scaled_items::bitmaps; + } else + luaL_error(L, "Expected \"bitmap\" as 2nd argument"); + } + lua_pushboolean(L, pCanvas->set_scale_factor( + static_cast(luaL_checknumber(L, 2)), eToScale) + ? 1 + : 0); + return 1; +} + +int l_surface_set_caption(lua_State* L) { + render_target* pCanvas = luaT_testuserdata(L); + pCanvas->set_caption(luaL_checkstring(L, 2)); + + lua_settop(L, 1); + return 1; +} + +int l_surface_get_renderer_details(lua_State* L) { + render_target* pCanvas = luaT_testuserdata(L); + lua_pushstring(L, pCanvas->get_renderer_details()); + return 1; } // Lua to THRenderTarget->setWindowGrab -int l_surface_set_capture_mouse(lua_State *L) -{ - render_target* pCanvas = luaT_testuserdata(L); - pCanvas->set_window_grab((lua_isnoneornil(L, 2) != 0) ? false : (lua_toboolean(L, 2) != 0)); - return 0; +int l_surface_set_capture_mouse(lua_State* L) { + render_target* pCanvas = luaT_testuserdata(L); + pCanvas->set_window_grab( + (lua_isnoneornil(L, 2) != 0) ? false : (lua_toboolean(L, 2) != 0)); + return 0; } -int l_line_new(lua_State *L) -{ - luaT_stdnew(L); - return 1; +int l_line_new(lua_State* L) { + luaT_stdnew(L); + return 1; } -int l_move_to(lua_State *L) -{ - line* pLine = luaT_testuserdata(L); - pLine->move_to(luaL_optnumber(L, 2, 0), luaL_optnumber(L, 3, 0)); +int l_move_to(lua_State* L) { + line* pLine = luaT_testuserdata(L); + pLine->move_to(luaL_optnumber(L, 2, 0), luaL_optnumber(L, 3, 0)); - lua_settop(L, 1); - return 1; + lua_settop(L, 1); + return 1; } -int l_line_to(lua_State *L) -{ - line* pLine = luaT_testuserdata(L); - pLine->line_to(luaL_optnumber(L, 2, 0), luaL_optnumber(L, 3, 0)); +int l_line_to(lua_State* L) { + line* pLine = luaT_testuserdata(L); + pLine->line_to(luaL_optnumber(L, 2, 0), luaL_optnumber(L, 3, 0)); - lua_settop(L, 1); - return 1; + lua_settop(L, 1); + return 1; } -int l_set_width(lua_State *L) -{ - line* pLine = luaT_testuserdata(L); - pLine->set_width(luaL_optnumber(L, 2, 1)); +int l_set_width(lua_State* L) { + line* pLine = luaT_testuserdata(L); + pLine->set_width(luaL_optnumber(L, 2, 1)); - lua_settop(L, 1); - return 1; + lua_settop(L, 1); + return 1; } -int l_set_colour(lua_State *L) -{ - line* pLine = luaT_testuserdata(L); - pLine->set_colour(static_cast(luaL_optinteger(L, 2, 0)), - static_cast(luaL_optinteger(L, 3, 0)), - static_cast(luaL_optinteger(L, 4, 0)), - static_cast(luaL_optinteger(L, 5, 255))); +int l_set_colour(lua_State* L) { + line* pLine = luaT_testuserdata(L); + pLine->set_colour(static_cast(luaL_optinteger(L, 2, 0)), + static_cast(luaL_optinteger(L, 3, 0)), + static_cast(luaL_optinteger(L, 4, 0)), + static_cast(luaL_optinteger(L, 5, 255))); - lua_settop(L, 1); - return 1; + lua_settop(L, 1); + return 1; } -int l_line_draw(lua_State *L) -{ - line* pLine = luaT_testuserdata(L); - render_target* pCanvas = luaT_testuserdata(L, 2); - pLine->draw(pCanvas, static_cast(luaL_optinteger(L, 3, 0)), static_cast(luaL_optinteger(L, 4, 0))); +int l_line_draw(lua_State* L) { + line* pLine = luaT_testuserdata(L); + render_target* pCanvas = luaT_testuserdata(L, 2); + pLine->draw(pCanvas, static_cast(luaL_optinteger(L, 3, 0)), + static_cast(luaL_optinteger(L, 4, 0))); - lua_settop(L, 1); - return 1; + lua_settop(L, 1); + return 1; } -int l_line_persist(lua_State *L) -{ - line* pLine = luaT_testuserdata(L); - lua_settop(L, 2); - lua_insert(L, 1); - lua_persist_writer* pWriter = (lua_persist_writer*)lua_touserdata(L, 1); - pLine->persist(pWriter); - return 0; +int l_line_persist(lua_State* L) { + line* pLine = luaT_testuserdata(L); + lua_settop(L, 2); + lua_insert(L, 1); + lua_persist_writer* pWriter = (lua_persist_writer*)lua_touserdata(L, 1); + pLine->persist(pWriter); + return 0; } -int l_line_depersist(lua_State *L) -{ - line* pLine = luaT_testuserdata(L); - lua_settop(L, 2); - lua_insert(L, 1); - lua_persist_reader* pReader = (lua_persist_reader*)lua_touserdata(L, 1); - pLine->depersist(pReader); - return 0; +int l_line_depersist(lua_State* L) { + line* pLine = luaT_testuserdata(L); + lua_settop(L, 2); + lua_insert(L, 1); + lua_persist_reader* pReader = (lua_persist_reader*)lua_touserdata(L, 1); + pLine->depersist(pReader); + return 0; } -} // namespace +} // namespace -void lua_register_gfx(const lua_register_state *pState) -{ - // Palette - { - lua_class_binding lcb(pState, "palette", l_palette_new, lua_metatable::palette); - lcb.add_function(l_palette_load, "load"); - lcb.add_function(l_palette_set_entry, "setEntry"); - } +void lua_register_gfx(const lua_register_state* pState) { + // Palette + { + lua_class_binding lcb(pState, "palette", l_palette_new, + lua_metatable::palette); + lcb.add_function(l_palette_load, "load"); + lcb.add_function(l_palette_set_entry, "setEntry"); + } - // Raw bitmap - { - lua_class_binding lcb(pState, "bitmap", l_rawbitmap_new, lua_metatable::bitmap); - lcb.add_function(l_rawbitmap_load, "load", lua_metatable::surface); - lcb.add_function(l_rawbitmap_set_pal, "setPalette", lua_metatable::palette); - lcb.add_function(l_rawbitmap_draw, "draw", lua_metatable::surface); - } + // Raw bitmap + { + lua_class_binding lcb(pState, "bitmap", l_rawbitmap_new, + lua_metatable::bitmap); + lcb.add_function(l_rawbitmap_load, "load", lua_metatable::surface); + lcb.add_function(l_rawbitmap_set_pal, "setPalette", lua_metatable::palette); + lcb.add_function(l_rawbitmap_draw, "draw", lua_metatable::surface); + } - // Sprite sheet - { - lua_class_binding lcb(pState, "sheet", l_spritesheet_new, lua_metatable::sheet); - lcb.add_metamethod(l_spritesheet_count, "len"); - lcb.add_function(l_spritesheet_load, "load", lua_metatable::surface); - lcb.add_function(l_spritesheet_set_pal, "setPalette", lua_metatable::palette); - lcb.add_function(l_spritesheet_size, "size"); - lcb.add_function(l_spritesheet_draw, "draw", lua_metatable::surface); - lcb.add_function(l_spritesheet_hittest, "hitTest"); - lcb.add_function(l_spritesheet_isvisible, "isVisible"); - } + // Sprite sheet + { + lua_class_binding lcb(pState, "sheet", l_spritesheet_new, + lua_metatable::sheet); + lcb.add_metamethod(l_spritesheet_count, "len"); + lcb.add_function(l_spritesheet_load, "load", lua_metatable::surface); + lcb.add_function(l_spritesheet_set_pal, "setPalette", + lua_metatable::palette); + lcb.add_function(l_spritesheet_size, "size"); + lcb.add_function(l_spritesheet_draw, "draw", lua_metatable::surface); + lcb.add_function(l_spritesheet_hittest, "hitTest"); + lcb.add_function(l_spritesheet_isvisible, "isVisible"); + } - // Font - // Also adapt the font proxy meta table (font_proxy_mt) in graphics.lua. - { - lua_class_binding lcb(pState, "font", l_font_new, lua_metatable::font); - lcb.add_function(l_font_get_size, "sizeOf"); - lcb.add_function(l_font_draw, "draw", lua_metatable::surface); - lcb.add_function(l_font_draw_wrapped, "drawWrapped", lua_metatable::surface); - lcb.add_function(l_font_draw_tooltip, "drawTooltip", lua_metatable::surface); - } + // Font + // Also adapt the font proxy meta table (font_proxy_mt) in graphics.lua. + { + lua_class_binding lcb(pState, "font", l_font_new, + lua_metatable::font); + lcb.add_function(l_font_get_size, "sizeOf"); + lcb.add_function(l_font_draw, "draw", lua_metatable::surface); + lcb.add_function(l_font_draw_wrapped, "drawWrapped", + lua_metatable::surface); + lcb.add_function(l_font_draw_tooltip, "drawTooltip", + lua_metatable::surface); + } - // BitmapFont - { - lua_class_binding lcb(pState, "bitmap_font", l_bitmap_font_new, lua_metatable::bitmap_font); - lcb.set_superclass(lua_metatable::font); - lcb.add_function(l_bitmap_font_set_spritesheet, "setSheet", lua_metatable::sheet); - lcb.add_function(l_bitmap_font_get_spritesheet, "getSheet", lua_metatable::sheet); - lcb.add_function(l_bitmap_font_set_sep, "setSeparation"); - } + // BitmapFont + { + lua_class_binding lcb(pState, "bitmap_font", l_bitmap_font_new, + lua_metatable::bitmap_font); + lcb.set_superclass(lua_metatable::font); + lcb.add_function(l_bitmap_font_set_spritesheet, "setSheet", + lua_metatable::sheet); + lcb.add_function(l_bitmap_font_get_spritesheet, "getSheet", + lua_metatable::sheet); + lcb.add_function(l_bitmap_font_set_sep, "setSeparation"); + } #ifdef CORSIX_TH_USE_FREETYPE2 - // FreeTypeFont - { - lua_class_binding lcb(pState, "freetype_font", l_freetype_font_new, lua_metatable::freetype_font); - lcb.set_superclass(lua_metatable::font); - lcb.add_function(l_freetype_font_set_spritesheet, "setSheet", lua_metatable::sheet); - lcb.add_function(l_freetype_font_set_face, "setFace"); - lcb.add_function(l_freetype_font_get_copyright, "getCopyrightNotice"); - lcb.add_function(l_freetype_font_clear_cache, "clearCache"); - } + // FreeTypeFont + { + lua_class_binding lcb(pState, "freetype_font", + l_freetype_font_new, + lua_metatable::freetype_font); + lcb.set_superclass(lua_metatable::font); + lcb.add_function(l_freetype_font_set_spritesheet, "setSheet", + lua_metatable::sheet); + lcb.add_function(l_freetype_font_set_face, "setFace"); + lcb.add_function(l_freetype_font_get_copyright, "getCopyrightNotice"); + lcb.add_function(l_freetype_font_clear_cache, "clearCache"); + } #endif - // Layers - { - lua_class_binding lcb(pState, "layers", l_layers_new, lua_metatable::layers); - lcb.add_metamethod(l_layers_get, "index"); - lcb.add_metamethod(l_layers_set, "newindex"); - lcb.add_metamethod(l_layers_persist, "persist"); - lcb.add_metamethod(l_layers_depersist, "depersist"); - } + // Layers + { + lua_class_binding lcb(pState, "layers", l_layers_new, + lua_metatable::layers); + lcb.add_metamethod(l_layers_get, "index"); + lcb.add_metamethod(l_layers_set, "newindex"); + lcb.add_metamethod(l_layers_persist, "persist"); + lcb.add_metamethod(l_layers_depersist, "depersist"); + } - // Cursor - { - lua_class_binding lcb(pState, "cursor", l_cursor_new, lua_metatable::cursor); - lcb.add_function(l_cursor_load, "load", lua_metatable::sheet); - lcb.add_function(l_cursor_use, "use", lua_metatable::surface); - lcb.add_function(l_cursor_position, "setPosition", lua_metatable::surface); - } + // Cursor + { + lua_class_binding lcb(pState, "cursor", l_cursor_new, + lua_metatable::cursor); + lcb.add_function(l_cursor_load, "load", lua_metatable::sheet); + lcb.add_function(l_cursor_use, "use", lua_metatable::surface); + lcb.add_function(l_cursor_position, "setPosition", lua_metatable::surface); + } - // Surface - { - lua_class_binding lcb(pState, "surface", l_surface_new, lua_metatable::surface); - lcb.add_function(l_surface_update, "update"); - lcb.add_function(l_surface_destroy, "destroy"); - lcb.add_function(l_surface_fill_black, "fillBlack"); - lcb.add_function(l_surface_start_frame, "startFrame"); - lcb.add_function(l_surface_end_frame, "endFrame"); - lcb.add_function(l_surface_nonoverlapping, "nonOverlapping"); - lcb.add_function(l_surface_map, "mapRGB"); - lcb.add_function(l_surface_set_blue_filter_active, "setBlueFilterActive"); - lcb.add_function(l_surface_rect, "drawRect"); - lcb.add_function(l_surface_get_clip, "getClip"); - lcb.add_function(l_surface_set_clip, "setClip"); - lcb.add_function(l_surface_screenshot, "takeScreenshot"); - lcb.add_function(l_surface_scale, "scale"); - lcb.add_function(l_surface_set_caption, "setCaption"); - lcb.add_function(l_surface_get_renderer_details, "getRendererDetails"); - lcb.add_function(l_surface_set_capture_mouse, "setCaptureMouse"); - } + // Surface + { + lua_class_binding lcb(pState, "surface", l_surface_new, + lua_metatable::surface); + lcb.add_function(l_surface_update, "update"); + lcb.add_function(l_surface_destroy, "destroy"); + lcb.add_function(l_surface_fill_black, "fillBlack"); + lcb.add_function(l_surface_start_frame, "startFrame"); + lcb.add_function(l_surface_end_frame, "endFrame"); + lcb.add_function(l_surface_nonoverlapping, "nonOverlapping"); + lcb.add_function(l_surface_map, "mapRGB"); + lcb.add_function(l_surface_set_blue_filter_active, "setBlueFilterActive"); + lcb.add_function(l_surface_rect, "drawRect"); + lcb.add_function(l_surface_get_clip, "getClip"); + lcb.add_function(l_surface_set_clip, "setClip"); + lcb.add_function(l_surface_screenshot, "takeScreenshot"); + lcb.add_function(l_surface_scale, "scale"); + lcb.add_function(l_surface_set_caption, "setCaption"); + lcb.add_function(l_surface_get_renderer_details, "getRendererDetails"); + lcb.add_function(l_surface_set_capture_mouse, "setCaptureMouse"); + } - // Line - { - lua_class_binding lcb(pState, "line", l_line_new, lua_metatable::line); - lcb.add_function(l_move_to, "moveTo"); - lcb.add_function(l_line_to, "lineTo"); - lcb.add_function(l_set_width, "setWidth"); - lcb.add_function(l_set_colour, "setColour"); - lcb.add_function(l_line_draw, "draw", lua_metatable::surface); - lcb.add_metamethod(l_line_persist, "persist"); - lcb.add_metamethod(l_line_depersist, "depersist"); - } + // Line + { + lua_class_binding lcb(pState, "line", l_line_new, + lua_metatable::line); + lcb.add_function(l_move_to, "moveTo"); + lcb.add_function(l_line_to, "lineTo"); + lcb.add_function(l_set_width, "setWidth"); + lcb.add_function(l_set_colour, "setColour"); + lcb.add_function(l_line_draw, "draw", lua_metatable::surface); + lcb.add_metamethod(l_line_persist, "persist"); + lcb.add_metamethod(l_line_depersist, "depersist"); + } } diff --git a/CorsixTH/Src/th_lua_internal.h b/CorsixTH/Src/th_lua_internal.h index 43c37018..42af1b44 100644 --- a/CorsixTH/Src/th_lua_internal.h +++ b/CorsixTH/Src/th_lua_internal.h @@ -28,57 +28,58 @@ SOFTWARE. #include enum class lua_metatable { - map, - palette, - sheet, - font, - bitmap_font, + map, + palette, + sheet, + font, + bitmap_font, #ifdef CORSIX_TH_USE_FREETYPE2 - freetype_font, + freetype_font, #endif - layers, - anims, - anim, - pathfinder, - surface, - bitmap, - cursor, - lfs_ext, - sound_archive, - sound_fx, - movie, - string, - window_base, - sprite_list, - string_proxy, - line, - iso_fs, + layers, + anims, + anim, + pathfinder, + surface, + bitmap, + cursor, + lfs_ext, + sound_archive, + sound_fx, + movie, + string, + window_base, + sprite_list, + string_proxy, + line, + iso_fs, - count + count }; -struct lua_register_state -{ - lua_State *L; - int metatables[static_cast(lua_metatable::count)]; - int main_table; - int top; +struct lua_register_state { + lua_State* L; + int metatables[static_cast(lua_metatable::count)]; + int main_table; + int top; }; -void luaT_setclosure(const lua_register_state *pState, lua_CFunction fn, size_t iUps); +void luaT_setclosure(const lua_register_state* pState, lua_CFunction fn, + size_t iUps); -template -void luaT_setclosure(const lua_register_state *pState, lua_CFunction fn, size_t iUps, - lua_metatable eMetatable1, Args... args) { - lua_pushvalue(pState->L, pState->metatables[static_cast(eMetatable1)]); - luaT_setclosure(pState, fn, iUps + 1, args...); +template +void luaT_setclosure(const lua_register_state* pState, lua_CFunction fn, + size_t iUps, lua_metatable eMetatable1, Args... args) { + lua_pushvalue(pState->L, + pState->metatables[static_cast(eMetatable1)]); + luaT_setclosure(pState, fn, iUps + 1, args...); } -template -void luaT_setclosure(const lua_register_state *pState, lua_CFunction fn, size_t iUps, - const char* str, Args... args) { - lua_pushstring(pState->L, str); - luaT_setclosure(pState, fn, iUps + 1, args...); +template +void luaT_setclosure(const lua_register_state* pState, lua_CFunction fn, + size_t iUps, const char* str, Args... args) { + lua_pushstring(pState->L, str); + luaT_setclosure(pState, fn, iUps + 1, args...); } /** @@ -89,11 +90,11 @@ void luaT_setclosure(const lua_register_state *pState, lua_CFunction fn, size_t * @param name The name to use for the function in lua * @param args The upvalues to associate with the function in lua */ -template -void add_lua_function(const lua_register_state* pState, lua_CFunction fn, const char* name, Args... args) -{ - luaT_setclosure(pState, fn, 0, args...); - lua_setfield(pState->L, -2, name); +template +void add_lua_function(const lua_register_state* pState, lua_CFunction fn, + const char* name, Args... args) { + luaT_setclosure(pState, fn, 0, args...); + lua_setfield(pState->L, -2, name); } /** @@ -102,119 +103,115 @@ void add_lua_function(const lua_register_state* pState, lua_CFunction fn, const * This class should be immediately destructed after adding all of it's * functions, metamethods and constants to complete the creation of the bind. */ -template -class lua_class_binding final -{ -public: - lua_class_binding()=delete; - lua_class_binding(const lua_class_binding&)=delete; - lua_class_binding(lua_class_binding&&)=delete; - lua_class_binding& operator= (const lua_class_binding&)=delete; - lua_class_binding operator= (lua_class_binding&&)=delete; +template +class lua_class_binding final { + public: + lua_class_binding() = delete; + lua_class_binding(const lua_class_binding&) = delete; + lua_class_binding(lua_class_binding&&) = delete; + lua_class_binding& operator=(const lua_class_binding&) = delete; + lua_class_binding operator=(lua_class_binding&&) = delete; - /** - * Inititate class bindings for lua. - * - * @param pState The lua environment to bind to. - * @param name The name to give this lua 'class'. - * @param new_fn The fuction to call when a new class is created. - * @param mt The metatable id for the class - */ - lua_class_binding(const lua_register_state* pState, const char* name, lua_CFunction new_fn, lua_metatable mt) : - pState(pState), + /** + * Inititate class bindings for lua. + * + * @param pState The lua environment to bind to. + * @param name The name to give this lua 'class'. + * @param new_fn The fuction to call when a new class is created. + * @param mt The metatable id for the class + */ + lua_class_binding(const lua_register_state* pState, const char* name, + lua_CFunction new_fn, lua_metatable mt) + : pState(pState), class_name(name), - class_metatable(pState->metatables[static_cast(mt)]) - { - lua_settop(pState->L, pState->top); - /* Make metatable the environment for registered functions */ \ - lua_pushvalue(pState->L, class_metatable); - lua_replace(pState->L, luaT_environindex); - /* Set the __gc metamethod to C++ destructor */ - luaT_pushcclosure(pState->L, luaT_stdgc, 0); - lua_setfield(pState->L, class_metatable, "__gc"); - /* Set the depersist size */ - lua_pushinteger(pState->L, sizeof(T)); - lua_setfield(pState->L, class_metatable, "__depersist_size"); - /* Create the methods table; call it -> new instance */ - luaT_pushcclosuretable(pState->L, new_fn, 0); - /* Set __class_name on the methods metatable */ - lua_getmetatable(pState->L, -1); - lua_pushstring(pState->L, class_name); - lua_setfield(pState->L, -2, "__class_name"); - lua_pop(pState->L, 1); - /* Set __index to the methods table */ - lua_pushvalue(pState->L, -1); - lua_setfield(pState->L, class_metatable, "__index"); - } + class_metatable(pState->metatables[static_cast(mt)]) { + lua_settop(pState->L, pState->top); + /* Make metatable the environment for registered functions */ + lua_pushvalue(pState->L, class_metatable); + lua_replace(pState->L, luaT_environindex); + /* Set the __gc metamethod to C++ destructor */ + luaT_pushcclosure(pState->L, luaT_stdgc, 0); + lua_setfield(pState->L, class_metatable, "__gc"); + /* Set the depersist size */ + lua_pushinteger(pState->L, sizeof(T)); + lua_setfield(pState->L, class_metatable, "__depersist_size"); + /* Create the methods table; call it -> new instance */ + luaT_pushcclosuretable(pState->L, new_fn, 0); + /* Set __class_name on the methods metatable */ + lua_getmetatable(pState->L, -1); + lua_pushstring(pState->L, class_name); + lua_setfield(pState->L, -2, "__class_name"); + lua_pop(pState->L, 1); + /* Set __index to the methods table */ + lua_pushvalue(pState->L, -1); + lua_setfield(pState->L, class_metatable, "__index"); + } - /** - * Set another class as the superclass of this class. - * - * @param super_mt The metatable id of the super class. - */ - void set_superclass(lua_metatable super_mt) - { - lua_getmetatable(pState->L, -1); - lua_getfield(pState->L, pState->metatables[static_cast(super_mt)], "__index"); - lua_setfield(pState->L, -2, "__index"); - lua_pop(pState->L, 1); - /* Set metatable[1] to super_mt */ - lua_pushvalue(pState->L, pState->metatables[static_cast(super_mt)]); - lua_rawseti(pState->L, class_metatable, 1); - } + /** + * Set another class as the superclass of this class. + * + * @param super_mt The metatable id of the super class. + */ + void set_superclass(lua_metatable super_mt) { + lua_getmetatable(pState->L, -1); + lua_getfield(pState->L, pState->metatables[static_cast(super_mt)], + "__index"); + lua_setfield(pState->L, -2, "__index"); + lua_pop(pState->L, 1); + /* Set metatable[1] to super_mt */ + lua_pushvalue(pState->L, pState->metatables[static_cast(super_mt)]); + lua_rawseti(pState->L, class_metatable, 1); + } - /** - * Add a named constant to the lua interface. - * - * @param name (string literal) Name of the constant. - * @param value (tested with int) Value of the constant. - */ - template - void add_constant(const char* name, V value) - { - luaT_push(pState->L, value); - lua_setfield(pState->L, -2, name); - } + /** + * Add a named constant to the lua interface. + * + * @param name (string literal) Name of the constant. + * @param value (tested with int) Value of the constant. + */ + template + void add_constant(const char* name, V value) { + luaT_push(pState->L, value); + lua_setfield(pState->L, -2, name); + } - /** - * Add a C++ metamethod to the lua class. - * - * @param fn The C++ function to call. - * @param name The name of the metamethod (without the __ prefix). - * @param args The upvalues for the function. - */ - template - void add_metamethod(lua_CFunction fn, const char* name, Args... args) - { - luaT_setclosure(pState, fn, 0, args...); - lua_setfield(pState->L, class_metatable, std::string("__").append(name).c_str()); - } + /** + * Add a C++ metamethod to the lua class. + * + * @param fn The C++ function to call. + * @param name The name of the metamethod (without the __ prefix). + * @param args The upvalues for the function. + */ + template + void add_metamethod(lua_CFunction fn, const char* name, Args... args) { + luaT_setclosure(pState, fn, 0, args...); + lua_setfield(pState->L, class_metatable, + std::string("__").append(name).c_str()); + } - /** - * Add a C++ function to the lua class. - * - * @param fn The C++ function. - * @param name The name of the function in lua. - * @param args The upvalues for the function - */ - template - void add_function(lua_CFunction fn, const char* name, Args... args) - { - add_lua_function(pState, fn, name, args...); - } + /** + * Add a C++ function to the lua class. + * + * @param fn The C++ function. + * @param name The name of the function in lua. + * @param args The upvalues for the function + */ + template + void add_function(lua_CFunction fn, const char* name, Args... args) { + add_lua_function(pState, fn, name, args...); + } - /** - * Destructor which finalizes the lua binding - */ - ~lua_class_binding() - { - lua_setfield(pState->L, pState->main_table, class_name); - } + /** + * Destructor which finalizes the lua binding + */ + ~lua_class_binding() { + lua_setfield(pState->L, pState->main_table, class_name); + } -private: - const lua_register_state* pState; - const char* class_name; - int class_metatable; + private: + const lua_register_state* pState; + const char* class_name; + int class_metatable; }; #endif diff --git a/CorsixTH/Src/th_lua_iso.cpp b/CorsixTH/Src/th_lua_iso.cpp index 748d281c..630d4cfa 100644 --- a/CorsixTH/Src/th_lua_iso.cpp +++ b/CorsixTH/Src/th_lua_iso.cpp @@ -20,124 +20,110 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -#include "th_lua_internal.h" -#include "iso_fs.h" #include +#include "iso_fs.h" +#include "th_lua_internal.h" namespace { -int l_isofs_new(lua_State *L) -{ - luaT_stdnew(L, luaT_environindex, true); - return 1; +int l_isofs_new(lua_State* L) { + luaT_stdnew(L, luaT_environindex, true); + return 1; } -int l_isofs_set_path_separator(lua_State *L) -{ - iso_filesystem *pSelf = luaT_testuserdata(L); - pSelf->set_path_separator(luaL_checkstring(L, 2)[0]); +int l_isofs_set_path_separator(lua_State* L) { + iso_filesystem* pSelf = luaT_testuserdata(L); + pSelf->set_path_separator(luaL_checkstring(L, 2)[0]); + lua_settop(L, 1); + return 1; +} + +int l_isofs_set_root(lua_State* L) { + iso_filesystem* pSelf = luaT_testuserdata(L); + std::FILE* fIso = *luaT_testuserdata(L, 2, false); + if (pSelf->initialise(fIso)) { + lua_pushvalue(L, 2); + luaT_setenvfield(L, 1, "file"); lua_settop(L, 1); return 1; + } else { + lua_pushnil(L); + lua_pushstring(L, pSelf->get_error()); + return 2; + } } -int l_isofs_set_root(lua_State *L) -{ - iso_filesystem *pSelf = luaT_testuserdata(L); - std::FILE *fIso = *luaT_testuserdata(L, 2, false); - if(pSelf->initialise(fIso)) - { - lua_pushvalue(L, 2); - luaT_setenvfield(L, 1, "file"); - lua_settop(L, 1); - return 1; - } - else - { - lua_pushnil(L); - lua_pushstring(L, pSelf->get_error()); - return 2; - } +int l_isofs_file_exists(lua_State* L) { + iso_filesystem* pSelf = luaT_testuserdata(L); + const char* sFilename = luaL_checkstring(L, 2); + iso_filesystem::file_handle iFile = pSelf->find_file(sFilename); + if (!iso_filesystem::isHandleGood(iFile)) { + lua_pushnil(L); + lua_pushfstring(L, "Could not find \'%s\' in .iso image", sFilename); + return 2; + } + lua_pushboolean(L, true); + return 1; } -int l_isofs_file_exists(lua_State *L) -{ - iso_filesystem *pSelf = luaT_testuserdata(L); - const char* sFilename = luaL_checkstring(L, 2); - iso_filesystem::file_handle iFile = pSelf->find_file(sFilename); - if(!iso_filesystem::isHandleGood(iFile)) - { - lua_pushnil(L); - lua_pushfstring(L, "Could not find \'%s\' in .iso image", sFilename); - return 2; - } - lua_pushboolean(L, true); - return 1; +int l_isofs_file_size(lua_State* L) { + iso_filesystem* pSelf = luaT_testuserdata(L); + const char* sFilename = luaL_checkstring(L, 2); + iso_filesystem::file_handle iFile = pSelf->find_file(sFilename); + if (!iso_filesystem::isHandleGood(iFile)) { + lua_pushnil(L); + lua_pushfstring(L, "Could not find \'%s\' in .iso image", sFilename); + return 2; + } + lua_pushinteger(L, pSelf->get_file_size(iFile)); + return 1; } -int l_isofs_file_size(lua_State *L) -{ - iso_filesystem *pSelf = luaT_testuserdata(L); - const char* sFilename = luaL_checkstring(L, 2); - iso_filesystem::file_handle iFile = pSelf->find_file(sFilename); - if(!iso_filesystem::isHandleGood(iFile)) - { - lua_pushnil(L); - lua_pushfstring(L, "Could not find \'%s\' in .iso image", sFilename); - return 2; - } - lua_pushinteger(L, pSelf->get_file_size(iFile)); - return 1; +int l_isofs_read_contents(lua_State* L) { + iso_filesystem* pSelf = luaT_testuserdata(L); + const char* sFilename = luaL_checkstring(L, 2); + iso_filesystem::file_handle iFile = pSelf->find_file(sFilename); + if (!iso_filesystem::isHandleGood(iFile)) { + lua_pushnil(L); + lua_pushfstring(L, "Could not find \'%s\' in .iso image", sFilename); + return 2; + } + void* pBuffer = lua_newuserdata(L, pSelf->get_file_size(iFile)); + if (!pSelf->get_file_data(iFile, reinterpret_cast(pBuffer))) { + lua_pushnil(L); + lua_pushstring(L, pSelf->get_error()); + return 2; + } + lua_pushlstring(L, reinterpret_cast(pBuffer), + pSelf->get_file_size(iFile)); + return 1; } -int l_isofs_read_contents(lua_State *L) -{ - iso_filesystem *pSelf = luaT_testuserdata(L); - const char* sFilename = luaL_checkstring(L, 2); - iso_filesystem::file_handle iFile = pSelf->find_file(sFilename); - if(!iso_filesystem::isHandleGood(iFile)) - { - lua_pushnil(L); - lua_pushfstring(L, "Could not find \'%s\' in .iso image", sFilename); - return 2; - } - void* pBuffer = lua_newuserdata(L, pSelf->get_file_size(iFile)); - if(!pSelf->get_file_data(iFile, reinterpret_cast(pBuffer))) - { - lua_pushnil(L); - lua_pushstring(L, pSelf->get_error()); - return 2; - } - lua_pushlstring(L, reinterpret_cast(pBuffer), pSelf->get_file_size(iFile)); - return 1; +void l_isofs_list_files_callback(void* p, const char* name, const char* path) { + lua_State* L = reinterpret_cast(p); + lua_pushstring(L, name); + lua_pushstring(L, path); + lua_settable(L, 3); } -void l_isofs_list_files_callback(void *p, const char* name, const char* path) -{ - lua_State *L = reinterpret_cast(p); - lua_pushstring(L, name); - lua_pushstring(L, path); - lua_settable(L, 3); +int l_isofs_list_files(lua_State* L) { + iso_filesystem* pSelf = luaT_testuserdata(L); + const char* sPath = luaL_checkstring(L, 2); + lua_settop(L, 2); + lua_newtable(L); + pSelf->visit_directory_files(sPath, l_isofs_list_files_callback, L); + return 1; } -int l_isofs_list_files(lua_State *L) -{ - iso_filesystem *pSelf = luaT_testuserdata(L); - const char* sPath = luaL_checkstring(L, 2); - lua_settop(L, 2); - lua_newtable(L); - pSelf->visit_directory_files(sPath, l_isofs_list_files_callback, L); - return 1; -} +} // namespace -} // namespace - -void lua_register_iso_fs(const lua_register_state* pState) -{ - lua_class_binding lcb(pState, "iso_fs", l_isofs_new, lua_metatable::iso_fs); - lcb.add_function(l_isofs_set_path_separator, "setPathSeparator"); - lcb.add_function(l_isofs_set_root, "setRoot"); - lcb.add_function(l_isofs_file_exists, "fileExists"); - lcb.add_function(l_isofs_file_size, "fileSize"); - lcb.add_function(l_isofs_read_contents, "readContents"); - lcb.add_function(l_isofs_list_files, "listFiles"); +void lua_register_iso_fs(const lua_register_state* pState) { + lua_class_binding lcb(pState, "iso_fs", l_isofs_new, + lua_metatable::iso_fs); + lcb.add_function(l_isofs_set_path_separator, "setPathSeparator"); + lcb.add_function(l_isofs_set_root, "setRoot"); + lcb.add_function(l_isofs_file_exists, "fileExists"); + lcb.add_function(l_isofs_file_size, "fileSize"); + lcb.add_function(l_isofs_read_contents, "readContents"); + lcb.add_function(l_isofs_list_files, "listFiles"); } diff --git a/CorsixTH/Src/th_lua_lfs_ext.cpp b/CorsixTH/Src/th_lua_lfs_ext.cpp index e549e2e8..4817c964 100644 --- a/CorsixTH/Src/th_lua_lfs_ext.cpp +++ b/CorsixTH/Src/th_lua_lfs_ext.cpp @@ -21,8 +21,8 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -#include "th_lua_internal.h" #include "config.h" +#include "th_lua_internal.h" #ifdef CORSIX_TH_USE_WIN32_SDK #include #endif @@ -31,77 +31,68 @@ class lfs_ext {}; namespace { -int l_lfs_ext_new(lua_State *L) -{ - luaT_stdnew(L, luaT_environindex, true); - return 1; +int l_lfs_ext_new(lua_State* L) { + luaT_stdnew(L, luaT_environindex, true); + return 1; } #ifdef _WIN32 #ifdef CORSIX_TH_USE_WIN32_SDK -int l_volume_list(lua_State *L) -{ - /* Windows, using the Win32 API. */ - DWORD iDriveMask = GetLogicalDrives(); - int iNDrives = 0; - char cDrive; - lua_settop(L, 0); - lua_newtable(L); - for (cDrive = 'A'; cDrive <= 'Z'; ++cDrive) - { - if (iDriveMask & (1 << (cDrive - 'A'))) - { - char sName[4] = { cDrive, ':', '\\', 0 }; - if (GetDriveTypeA(sName) > DRIVE_NO_ROOT_DIR) - { - lua_pushlstring(L, sName, 2); - lua_rawseti(L, 1, ++iNDrives); - } - } +int l_volume_list(lua_State* L) { + /* Windows, using the Win32 API. */ + DWORD iDriveMask = GetLogicalDrives(); + int iNDrives = 0; + char cDrive; + lua_settop(L, 0); + lua_newtable(L); + for (cDrive = 'A'; cDrive <= 'Z'; ++cDrive) { + if (iDriveMask & (1 << (cDrive - 'A'))) { + char sName[4] = {cDrive, ':', '\\', 0}; + if (GetDriveTypeA(sName) > DRIVE_NO_ROOT_DIR) { + lua_pushlstring(L, sName, 2); + lua_rawseti(L, 1, ++iNDrives); + } } - return 1; + } + return 1; } #else -int l_volume_list(lua_State *L) -{ - /* Windows, without the Win32 API. */ - int iNDrives = 0; - char cDrive; - lua_settop(L, 0); - lua_newtable(L); - lua_getfield(L, luaT_upvalueindex(1), "attributes"); - for (cDrive = 'A'; cDrive <= 'Z'; ++cDrive) - { - lua_pushvalue(L, 2); - lua_pushfstring(L, "%c:\\", cDrive); - lua_pushliteral(L, "mode"); - lua_call(L, 2, 1); - if (lua_toboolean(L, 3) != 0) - { - lua_pushfstring(L, "%c:", cDrive); - lua_rawseti(L, 1, ++iNDrives); - } - lua_pop(L, 1); +int l_volume_list(lua_State* L) { + /* Windows, without the Win32 API. */ + int iNDrives = 0; + char cDrive; + lua_settop(L, 0); + lua_newtable(L); + lua_getfield(L, luaT_upvalueindex(1), "attributes"); + for (cDrive = 'A'; cDrive <= 'Z'; ++cDrive) { + lua_pushvalue(L, 2); + lua_pushfstring(L, "%c:\\", cDrive); + lua_pushliteral(L, "mode"); + lua_call(L, 2, 1); + if (lua_toboolean(L, 3) != 0) { + lua_pushfstring(L, "%c:", cDrive); + lua_rawseti(L, 1, ++iNDrives); } - return 1; + lua_pop(L, 1); + } + return 1; } #endif #else -int l_volume_list(lua_State *L) -{ - /* Non-Windows systems. Assume that / is the root of the filesystem. */ - lua_settop(L, 0); - lua_newtable(L); - lua_pushliteral(L, "/"); - lua_rawseti(L, 1, 1); - return 1; +int l_volume_list(lua_State* L) { + /* Non-Windows systems. Assume that / is the root of the filesystem. */ + lua_settop(L, 0); + lua_newtable(L); + lua_pushliteral(L, "/"); + lua_rawseti(L, 1, 1); + return 1; } #endif -} // namespace +} // namespace -void lua_register_lfs_ext(const lua_register_state *pState) -{ - lua_class_binding lcb(pState, "lfsExt", l_lfs_ext_new, lua_metatable::lfs_ext); - lcb.add_function(l_volume_list, "volumes"); +void lua_register_lfs_ext(const lua_register_state* pState) { + lua_class_binding lcb(pState, "lfsExt", l_lfs_ext_new, + lua_metatable::lfs_ext); + lcb.add_function(l_volume_list, "volumes"); } diff --git a/CorsixTH/Src/th_lua_map.cpp b/CorsixTH/Src/th_lua_map.cpp index 86627b5f..dfdeaae4 100644 --- a/CorsixTH/Src/th_lua_map.cpp +++ b/CorsixTH/Src/th_lua_map.cpp @@ -20,485 +20,472 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +#include +#include +#include #include "th_lua_internal.h" #include "th_map.h" #include "th_pathfind.h" -#include -#include -#include namespace { constexpr int player_max = 4; -int l_map_new(lua_State *L) -{ - luaT_stdnew(L, luaT_environindex, true); - return 1; +int l_map_new(lua_State* L) { + luaT_stdnew(L, luaT_environindex, true); + return 1; } -int l_map_set_sheet(lua_State *L) -{ - level_map* pMap = luaT_testuserdata(L); - sprite_sheet* pSheet = luaT_testuserdata(L, 2); - lua_settop(L, 2); +int l_map_set_sheet(lua_State* L) { + level_map* pMap = luaT_testuserdata(L); + sprite_sheet* pSheet = luaT_testuserdata(L, 2); + lua_settop(L, 2); - pMap->set_block_sheet(pSheet); - luaT_setenvfield(L, 1, "sprites"); - return 1; + pMap->set_block_sheet(pSheet); + luaT_setenvfield(L, 1, "sprites"); + return 1; } -int l_map_persist(lua_State *L) -{ - level_map* pMap = luaT_testuserdata(L); - lua_settop(L, 2); - lua_insert(L, 1); - pMap->persist((lua_persist_writer*)lua_touserdata(L, 1)); - return 0; +int l_map_persist(lua_State* L) { + level_map* pMap = luaT_testuserdata(L); + lua_settop(L, 2); + lua_insert(L, 1); + pMap->persist((lua_persist_writer*)lua_touserdata(L, 1)); + return 0; } -int l_map_depersist(lua_State *L) -{ - level_map* pMap = luaT_testuserdata(L); - lua_settop(L, 2); - lua_insert(L, 1); - lua_persist_reader* pReader = (lua_persist_reader*)lua_touserdata(L, 1); +int l_map_depersist(lua_State* L) { + level_map* pMap = luaT_testuserdata(L); + lua_settop(L, 2); + lua_insert(L, 1); + lua_persist_reader* pReader = (lua_persist_reader*)lua_touserdata(L, 1); - pMap->depersist(pReader); - luaT_getenvfield(L, 2, "sprites"); - pMap->set_block_sheet((sprite_sheet*)lua_touserdata(L, -1)); + pMap->depersist(pReader); + luaT_getenvfield(L, 2, "sprites"); + pMap->set_block_sheet((sprite_sheet*)lua_touserdata(L, -1)); + lua_pop(L, 1); + return 0; +} + +void l_map_load_obj_cb(void* pL, int iX, int iY, object_type eTHOB, + uint8_t iFlags) { + lua_State* L = reinterpret_cast(pL); + lua_createtable(L, 4, 0); + + lua_pushinteger(L, 1 + (lua_Integer)iX); + lua_rawseti(L, -2, 1); + lua_pushinteger(L, 1 + (lua_Integer)iY); + lua_rawseti(L, -2, 2); + lua_pushinteger(L, (lua_Integer)eTHOB); + lua_rawseti(L, -2, 3); + lua_pushinteger(L, (lua_Integer)iFlags); + lua_rawseti(L, -2, 4); + + lua_rawseti(L, 3, static_cast(lua_objlen(L, 3)) + 1); +} + +int l_map_load(lua_State* L) { + level_map* pMap = luaT_testuserdata(L); + size_t iDataLen; + const uint8_t* pData = luaT_checkfile(L, 2, &iDataLen); + lua_settop(L, 2); + lua_newtable(L); + if (pMap->load_from_th_file(pData, iDataLen, l_map_load_obj_cb, (void*)L)) { + lua_pushboolean(L, 1); + } else { + lua_pushboolean(L, 0); + } + lua_insert(L, -2); + return 2; +} + +int l_map_loadblank(lua_State* L) { + level_map* pMap = luaT_testuserdata(L); + if (pMap->load_blank()) { + lua_pushboolean(L, 1); + } else { + lua_pushboolean(L, 0); + } + lua_newtable(L); + return 2; +} + +int l_map_save(lua_State* L) { + level_map* pMap = luaT_testuserdata(L); + std::string filename(luaL_checkstring(L, 2)); + pMap->save(filename); + return 0; +} + +animation* l_map_updateblueprint_getnextanim(lua_State* L, int& iIndex) { + animation* pAnim; + lua_rawgeti(L, 11, iIndex); + if (lua_type(L, -1) == LUA_TNIL) { lua_pop(L, 1); - return 0; + pAnim = luaT_new(L); + lua_pushvalue(L, luaT_upvalueindex(2)); + lua_setmetatable(L, -2); + lua_createtable(L, 0, 2); + lua_pushvalue(L, 1); + lua_setfield(L, -2, "map"); + lua_pushvalue(L, 12); + lua_setfield(L, -2, "animator"); + lua_setfenv(L, -2); + lua_rawseti(L, 11, iIndex); + } else { + pAnim = luaT_testuserdata(L, -1, luaT_upvalueindex(2)); + lua_pop(L, 1); + } + ++iIndex; + return pAnim; } -void l_map_load_obj_cb(void *pL, int iX, int iY, object_type eTHOB, uint8_t iFlags) -{ - lua_State *L = reinterpret_cast(pL); - lua_createtable(L, 4, 0); - - lua_pushinteger(L, 1 + (lua_Integer)iX); - lua_rawseti(L, -2, 1); - lua_pushinteger(L, 1 + (lua_Integer)iY); - lua_rawseti(L, -2, 2); - lua_pushinteger(L, (lua_Integer)eTHOB); - lua_rawseti(L, -2, 3); - lua_pushinteger(L, (lua_Integer)iFlags); - lua_rawseti(L, -2, 4); - - lua_rawseti(L, 3, static_cast(lua_objlen(L, 3)) + 1); +uint16_t l_check_temp(lua_State* L, int iArg) { + lua_Number n = luaL_checknumber(L, iArg); + if (n < static_cast(0) || static_cast(1) < n) { + luaL_argerror(L, iArg, "temperature (number in [0,1])"); + } + return static_cast(n * static_cast(65535)); } -int l_map_load(lua_State *L) -{ - level_map* pMap = luaT_testuserdata(L); - size_t iDataLen; - const uint8_t* pData = luaT_checkfile(L, 2, &iDataLen); - lua_settop(L, 2); - lua_newtable(L); - if(pMap->load_from_th_file(pData, iDataLen, l_map_load_obj_cb, (void*)L)) - lua_pushboolean(L, 1); - else - lua_pushboolean(L, 0); - lua_insert(L, -2); - return 2; +int l_map_settemperaturedisplay(lua_State* L) { + level_map* pMap = luaT_testuserdata(L); + lua_Integer iTD = luaL_checkinteger(L, 2); + + temperature_theme temperatureDisplay; + switch (iTD) { + case 1: + temperatureDisplay = temperature_theme::red; + break; + case 2: + temperatureDisplay = temperature_theme::multi_colour; + break; + case 3: + temperatureDisplay = temperature_theme::yellow_red; + break; + default: + return luaL_argerror(L, 2, "TemperatureDisplay index out of bounds"); + } + + pMap->set_temperature_display(temperatureDisplay); + + return 1; } -int l_map_loadblank(lua_State *L) -{ - level_map* pMap = luaT_testuserdata(L); - if(pMap->load_blank()) - lua_pushboolean(L, 1); - else - lua_pushboolean(L, 0); - lua_newtable(L); - return 2; +int l_map_updatetemperature(lua_State* L) { + level_map* pMap = luaT_testuserdata(L); + uint16_t iAir = l_check_temp(L, 2); + uint16_t iRadiator = l_check_temp(L, 3); + pMap->update_temperatures(iAir, iRadiator); + lua_settop(L, 1); + return 1; } -int l_map_save(lua_State *L) -{ - level_map* pMap = luaT_testuserdata(L); - std::string filename(luaL_checkstring(L, 2)); - pMap->save(filename); - return 0; -} - -animation* l_map_updateblueprint_getnextanim(lua_State *L, int& iIndex) -{ - animation *pAnim; - lua_rawgeti(L, 11, iIndex); - if(lua_type(L, -1) == LUA_TNIL) - { - lua_pop(L, 1); - pAnim = luaT_new(L); - lua_pushvalue(L, luaT_upvalueindex(2)); - lua_setmetatable(L, -2); - lua_createtable(L, 0, 2); - lua_pushvalue(L, 1); - lua_setfield(L, -2, "map"); - lua_pushvalue(L, 12); - lua_setfield(L, -2, "animator"); - lua_setfenv(L, -2); - lua_rawseti(L, 11, iIndex); - } - else - { - pAnim = luaT_testuserdata(L, -1, luaT_upvalueindex(2)); - lua_pop(L, 1); - } - ++iIndex; - return pAnim; -} - -uint16_t l_check_temp(lua_State *L, int iArg) -{ - lua_Number n = luaL_checknumber(L, iArg); - if(n < static_cast(0) || static_cast(1) < n) - luaL_argerror(L, iArg, "temperature (number in [0,1])"); - return static_cast(n * static_cast(65535)); -} - -int l_map_settemperaturedisplay(lua_State *L) -{ - level_map* pMap = luaT_testuserdata(L); - lua_Integer iTD = luaL_checkinteger(L, 2); - - temperature_theme temperatureDisplay; - switch(iTD) { - case 1: - temperatureDisplay = temperature_theme::red; - break; - case 2: - temperatureDisplay = temperature_theme::multi_colour; - break; - case 3: - temperatureDisplay = temperature_theme::yellow_red; - break; - default: - return luaL_argerror(L, 2, "TemperatureDisplay index out of bounds"); - } - - pMap->set_temperature_display(temperatureDisplay); - - return 1; -} - -int l_map_updatetemperature(lua_State *L) -{ - level_map* pMap = luaT_testuserdata(L); - uint16_t iAir = l_check_temp(L, 2); - uint16_t iRadiator = l_check_temp(L, 3); - pMap->update_temperatures(iAir, iRadiator); - lua_settop(L, 1); - return 1; -} - -int l_map_gettemperature(lua_State *L) -{ - level_map* pMap = luaT_testuserdata(L); - int iX = static_cast(luaL_checkinteger(L, 2)) - 1; - int iY = static_cast(luaL_checkinteger(L, 3)) - 1; - const map_tile* pNode = pMap->get_tile(iX, iY); - uint16_t iTemp = pMap->get_tile_temperature(pNode); - lua_pushnumber(L, static_cast(iTemp) / static_cast(65535)); - return 1; +int l_map_gettemperature(lua_State* L) { + level_map* pMap = luaT_testuserdata(L); + int iX = static_cast(luaL_checkinteger(L, 2)) - 1; + int iY = static_cast(luaL_checkinteger(L, 3)) - 1; + const map_tile* pNode = pMap->get_tile(iX, iY); + uint16_t iTemp = pMap->get_tile_temperature(pNode); + lua_pushnumber( + L, static_cast(iTemp) / static_cast(65535)); + return 1; } /** * Is the tile position valid for a new room? - * @param entire_invalid Entire blueprint is invalid (eg wrong position or too small). + * @param entire_invalid Entire blueprint is invalid (eg wrong position or too + * small). * @param pNode Tile to examine. * @param pMap The world map. * @param player_id The player to check for. * @return Whether the tile position is valid for a new room. */ -inline bool is_valid( - bool entire_invalid, const map_tile *pNode, - const level_map* pMap, int player_id) -{ - return !entire_invalid && !pNode->flags.room && pNode->flags.buildable && - (player_id == 0 || pMap->get_tile_owner(pNode) == player_id); +inline bool is_valid(bool entire_invalid, const map_tile* pNode, + const level_map* pMap, int player_id) { + return !entire_invalid && !pNode->flags.room && pNode->flags.buildable && + (player_id == 0 || pMap->get_tile_owner(pNode) == player_id); } -int l_map_updateblueprint(lua_State *L) -{ - // NB: This function can be implemented in Lua, but is implemented in C for - // efficiency. - const unsigned short iFloorTileGood = 24 + (thdf_alpha_50 << 8); - const unsigned short iFloorTileGoodCenter = 37 + (thdf_alpha_50 << 8); - const unsigned short iFloorTileBad = 67 + (thdf_alpha_50 << 8); - const unsigned int iWallAnimTopCorner = 124; - const unsigned int iWallAnim = 120; +int l_map_updateblueprint(lua_State* L) { + // NB: This function can be implemented in Lua, but is implemented in C for + // efficiency. + const unsigned short iFloorTileGood = 24 + (thdf_alpha_50 << 8); + const unsigned short iFloorTileGoodCenter = 37 + (thdf_alpha_50 << 8); + const unsigned short iFloorTileBad = 67 + (thdf_alpha_50 << 8); + const unsigned int iWallAnimTopCorner = 124; + const unsigned int iWallAnim = 120; - level_map* pMap = luaT_testuserdata(L); - int iOldX = static_cast(luaL_checkinteger(L, 2)) - 1; - int iOldY = static_cast(luaL_checkinteger(L, 3)) - 1; - int iOldW = static_cast(luaL_checkinteger(L, 4)); - int iOldH = static_cast(luaL_checkinteger(L, 5)); - int iNewX = static_cast(luaL_checkinteger(L, 6)) - 1; - int iNewY = static_cast(luaL_checkinteger(L, 7)) - 1; - int iNewW = static_cast(luaL_checkinteger(L, 8)); - int iNewH = static_cast(luaL_checkinteger(L, 9)); - int player_id = static_cast(luaL_checkinteger(L, 10)); + level_map* pMap = luaT_testuserdata(L); + int iOldX = static_cast(luaL_checkinteger(L, 2)) - 1; + int iOldY = static_cast(luaL_checkinteger(L, 3)) - 1; + int iOldW = static_cast(luaL_checkinteger(L, 4)); + int iOldH = static_cast(luaL_checkinteger(L, 5)); + int iNewX = static_cast(luaL_checkinteger(L, 6)) - 1; + int iNewY = static_cast(luaL_checkinteger(L, 7)) - 1; + int iNewW = static_cast(luaL_checkinteger(L, 8)); + int iNewH = static_cast(luaL_checkinteger(L, 9)); + int player_id = static_cast(luaL_checkinteger(L, 10)); - luaL_checktype(L, 11, LUA_TTABLE); // Animation list - animation_manager* pAnims = luaT_testuserdata(L, 12, luaT_upvalueindex(1)); - bool entire_invalid = lua_toboolean(L, 13) != 0; - bool valid = !entire_invalid; + luaL_checktype(L, 11, LUA_TTABLE); // Animation list + animation_manager* pAnims = + luaT_testuserdata(L, 12, luaT_upvalueindex(1)); + bool entire_invalid = lua_toboolean(L, 13) != 0; + bool valid = !entire_invalid; - if(iOldX < 0 || iOldY < 0 || (iOldX + iOldW) > pMap->get_width() || (iOldY + iOldH) > pMap->get_height()) - luaL_argerror(L, 2, "Old rectangle is out of bounds"); - if(iNewX < 0 || iNewY < 0 || (iNewX + iNewW) >= pMap->get_width() || (iNewY + iNewH) >= pMap->get_height()) - luaL_argerror(L, 6, "New rectangle is out of bounds"); + if (iOldX < 0 || iOldY < 0 || (iOldX + iOldW) > pMap->get_width() || + (iOldY + iOldH) > pMap->get_height()) + luaL_argerror(L, 2, "Old rectangle is out of bounds"); + if (iNewX < 0 || iNewY < 0 || (iNewX + iNewW) >= pMap->get_width() || + (iNewY + iNewH) >= pMap->get_height()) + luaL_argerror(L, 6, "New rectangle is out of bounds"); - // Clear blueprint flag from previous selected floor tiles (copying it to the passable flag). - for(int iY = iOldY; iY < iOldY + iOldH; ++iY) - { - for(int iX = iOldX; iX < iOldX + iOldW; ++iX) - { - map_tile *pNode = pMap->get_tile_unchecked(iX, iY); - pNode->iBlock[3] = 0; - pNode->flags.passable |= pNode->flags.passable_if_not_for_blueprint; - pNode->flags.passable_if_not_for_blueprint = false; - } + // Clear blueprint flag from previous selected floor tiles (copying it to + // the passable flag). + for (int iY = iOldY; iY < iOldY + iOldH; ++iY) { + for (int iX = iOldX; iX < iOldX + iOldW; ++iX) { + map_tile* pNode = pMap->get_tile_unchecked(iX, iY); + pNode->iBlock[3] = 0; + pNode->flags.passable |= pNode->flags.passable_if_not_for_blueprint; + pNode->flags.passable_if_not_for_blueprint = false; } + } - // Add blueprint flag to new floor tiles. - for(int iY = iNewY; iY < iNewY + iNewH; ++iY) - { - for(int iX = iNewX; iX < iNewX + iNewW; ++iX) - { - map_tile *pNode = pMap->get_tile_unchecked(iX, iY); - if(is_valid(entire_invalid, pNode, pMap, player_id)) - pNode->iBlock[3] = iFloorTileGood; - else - { - pNode->iBlock[3] = iFloorTileBad; - valid = false; - } - pNode->flags.passable_if_not_for_blueprint = pNode->flags.passable; - } + // Add blueprint flag to new floor tiles. + for (int iY = iNewY; iY < iNewY + iNewH; ++iY) { + for (int iX = iNewX; iX < iNewX + iNewW; ++iX) { + map_tile* pNode = pMap->get_tile_unchecked(iX, iY); + if (is_valid(entire_invalid, pNode, pMap, player_id)) { + pNode->iBlock[3] = iFloorTileGood; + } else { + pNode->iBlock[3] = iFloorTileBad; + valid = false; + } + pNode->flags.passable_if_not_for_blueprint = pNode->flags.passable; } + } - // Set center floor tiles - if(iNewW >= 2 && iNewH >= 2) - { - int iCenterX = iNewX + (iNewW - 2) / 2; - int iCenterY = iNewY + (iNewH - 2) / 2; + // Set center floor tiles + if (iNewW >= 2 && iNewH >= 2) { + int iCenterX = iNewX + (iNewW - 2) / 2; + int iCenterY = iNewY + (iNewH - 2) / 2; - map_tile *pNode = pMap->get_tile_unchecked(iCenterX, iCenterY); - if(pNode->iBlock[3] == iFloorTileGood) - pNode->iBlock[3] = iFloorTileGoodCenter + 2; + map_tile* pNode = pMap->get_tile_unchecked(iCenterX, iCenterY); + if (pNode->iBlock[3] == iFloorTileGood) + pNode->iBlock[3] = iFloorTileGoodCenter + 2; - pNode = pMap->get_tile_unchecked(iCenterX + 1, iCenterY); - if(pNode->iBlock[3] == iFloorTileGood) - pNode->iBlock[3] = iFloorTileGoodCenter + 1; + pNode = pMap->get_tile_unchecked(iCenterX + 1, iCenterY); + if (pNode->iBlock[3] == iFloorTileGood) + pNode->iBlock[3] = iFloorTileGoodCenter + 1; - pNode = pMap->get_tile_unchecked(iCenterX, iCenterY + 1); - if(pNode->iBlock[3] == iFloorTileGood) - pNode->iBlock[3] = iFloorTileGoodCenter + 0; + pNode = pMap->get_tile_unchecked(iCenterX, iCenterY + 1); + if (pNode->iBlock[3] == iFloorTileGood) + pNode->iBlock[3] = iFloorTileGoodCenter + 0; - pNode = pMap->get_tile_unchecked(iCenterX + 1, iCenterY + 1); - if(pNode->iBlock[3] == iFloorTileGood) - pNode->iBlock[3] = iFloorTileGoodCenter + 3; + pNode = pMap->get_tile_unchecked(iCenterX + 1, iCenterY + 1); + if (pNode->iBlock[3] == iFloorTileGood) + pNode->iBlock[3] = iFloorTileGoodCenter + 3; + } + + // Set wall animations + int iNextAnim = 1; + animation* pAnim = l_map_updateblueprint_getnextanim(L, iNextAnim); + map_tile* pNode = pMap->get_tile_unchecked(iNewX, iNewY); + pAnim->set_animation(pAnims, iWallAnimTopCorner); + pAnim->set_flags(thdf_list_bottom | + (is_valid(entire_invalid, pNode, pMap, player_id) + ? 0 + : thdf_alt_palette)); + pAnim->attach_to_tile(pNode, 0); + + for (int iX = iNewX; iX < iNewX + iNewW; ++iX) { + if (iX != iNewX) { + pAnim = l_map_updateblueprint_getnextanim(L, iNextAnim); + pNode = pMap->get_tile_unchecked(iX, iNewY); + pAnim->set_animation(pAnims, iWallAnim); + pAnim->set_flags(thdf_list_bottom | + (is_valid(entire_invalid, pNode, pMap, player_id) + ? 0 + : thdf_alt_palette)); + pAnim->attach_to_tile(pNode, 0); + pAnim->set_position(0, 0); } - - // Set wall animations - int iNextAnim = 1; - animation *pAnim = l_map_updateblueprint_getnextanim(L, iNextAnim); - map_tile *pNode = pMap->get_tile_unchecked(iNewX, iNewY); - pAnim->set_animation(pAnims, iWallAnimTopCorner); - pAnim->set_flags(thdf_list_bottom | (is_valid(entire_invalid, pNode, pMap, player_id) ? 0 : thdf_alt_palette)); + pAnim = l_map_updateblueprint_getnextanim(L, iNextAnim); + pNode = pMap->get_tile_unchecked(iX, iNewY + iNewH - 1); + pAnim->set_animation(pAnims, iWallAnim); + pAnim->set_flags(thdf_list_bottom | + (is_valid(entire_invalid, pNode, pMap, player_id) + ? 0 + : thdf_alt_palette)); + pNode = pMap->get_tile_unchecked(iX, iNewY + iNewH); pAnim->attach_to_tile(pNode, 0); - - for(int iX = iNewX; iX < iNewX + iNewW; ++iX) - { - if(iX != iNewX) - { - pAnim = l_map_updateblueprint_getnextanim(L, iNextAnim); - pNode = pMap->get_tile_unchecked(iX, iNewY); - pAnim->set_animation(pAnims, iWallAnim); - pAnim->set_flags(thdf_list_bottom | (is_valid(entire_invalid, pNode, pMap, player_id) ? 0 : thdf_alt_palette)); - pAnim->attach_to_tile(pNode, 0); - pAnim->set_position(0, 0); - } - pAnim = l_map_updateblueprint_getnextanim(L, iNextAnim); - pNode = pMap->get_tile_unchecked(iX, iNewY + iNewH - 1); - pAnim->set_animation(pAnims, iWallAnim); - pAnim->set_flags(thdf_list_bottom | (is_valid(entire_invalid, pNode, pMap, player_id) ? 0 : thdf_alt_palette)); - pNode = pMap->get_tile_unchecked(iX, iNewY + iNewH); - pAnim->attach_to_tile(pNode, 0); - pAnim->set_position(0, -1); - } - for(int iY = iNewY; iY < iNewY + iNewH; ++iY) - { - if(iY != iNewY) - { - pAnim = l_map_updateblueprint_getnextanim(L, iNextAnim); - pNode = pMap->get_tile_unchecked(iNewX, iY); - pAnim->set_animation(pAnims, iWallAnim); - pAnim->set_flags(thdf_list_bottom | thdf_flip_horizontal | (is_valid(entire_invalid, pNode, pMap, player_id) ? 0 : thdf_alt_palette)); - pAnim->attach_to_tile(pNode, 0); - pAnim->set_position(2, 0); - } - pAnim = l_map_updateblueprint_getnextanim(L, iNextAnim); - pNode = pMap->get_tile_unchecked(iNewX + iNewW - 1, iY); - pAnim->set_animation(pAnims, iWallAnim); - pAnim->set_flags(thdf_list_bottom | thdf_flip_horizontal | (is_valid(entire_invalid, pNode, pMap, player_id) ? 0 : thdf_alt_palette)); - pNode = pMap->get_tile_unchecked(iNewX + iNewW, iY); - pAnim->attach_to_tile(pNode, 0); - pAnim->set_position(2, -1); + pAnim->set_position(0, -1); + } + for (int iY = iNewY; iY < iNewY + iNewH; ++iY) { + if (iY != iNewY) { + pAnim = l_map_updateblueprint_getnextanim(L, iNextAnim); + pNode = pMap->get_tile_unchecked(iNewX, iY); + pAnim->set_animation(pAnims, iWallAnim); + pAnim->set_flags(thdf_list_bottom | thdf_flip_horizontal | + (is_valid(entire_invalid, pNode, pMap, player_id) + ? 0 + : thdf_alt_palette)); + pAnim->attach_to_tile(pNode, 0); + pAnim->set_position(2, 0); } + pAnim = l_map_updateblueprint_getnextanim(L, iNextAnim); + pNode = pMap->get_tile_unchecked(iNewX + iNewW - 1, iY); + pAnim->set_animation(pAnims, iWallAnim); + pAnim->set_flags(thdf_list_bottom | thdf_flip_horizontal | + (is_valid(entire_invalid, pNode, pMap, player_id) + ? 0 + : thdf_alt_palette)); + pNode = pMap->get_tile_unchecked(iNewX + iNewW, iY); + pAnim->attach_to_tile(pNode, 0); + pAnim->set_position(2, -1); + } - // Clear away extra animations - int iAnimCount = (int)lua_objlen(L, 11); - if(iAnimCount >= iNextAnim) - { - for(int i = iNextAnim; i <= iAnimCount; ++i) - { - pAnim = l_map_updateblueprint_getnextanim(L, iNextAnim); - pAnim->remove_from_tile(); - lua_pushnil(L); - lua_rawseti(L, 11, i); - } + // Clear away extra animations + int iAnimCount = (int)lua_objlen(L, 11); + if (iAnimCount >= iNextAnim) { + for (int i = iNextAnim; i <= iAnimCount; ++i) { + pAnim = l_map_updateblueprint_getnextanim(L, iNextAnim); + pAnim->remove_from_tile(); + lua_pushnil(L); + lua_rawseti(L, 11, i); } + } - lua_pushboolean(L, valid ? 1 : 0); + lua_pushboolean(L, valid ? 1 : 0); + return 1; +} + +int l_map_getsize(lua_State* L) { + level_map* pMap = luaT_testuserdata(L); + lua_pushinteger(L, pMap->get_width()); + lua_pushinteger(L, pMap->get_height()); + return 2; +} + +int l_map_get_player_count(lua_State* L) { + level_map* pMap = luaT_testuserdata(L); + lua_pushinteger(L, pMap->get_player_count()); + return 1; +} + +int l_map_set_player_count(lua_State* L) { + level_map* pMap = luaT_testuserdata(L); + int count = static_cast(luaL_checkinteger(L, 2)); + + try { + pMap->set_player_count(count); + } catch (std::out_of_range) { + return luaL_error(L, "Player count out of range %d", count); + } + return 0; +} + +int l_map_get_player_camera(lua_State* L) { + level_map* pMap = luaT_testuserdata(L); + int iX, iY; + int iPlayer = static_cast(luaL_optinteger(L, 2, 1)); + bool bGood = pMap->get_player_camera_tile(iPlayer - 1, &iX, &iY); + if (!bGood) { + return luaL_error(L, "Player index out of range: %d", iPlayer); + } + lua_pushinteger(L, iX + 1); + lua_pushinteger(L, iY + 1); + return 2; +} + +int l_map_set_player_camera(lua_State* L) { + level_map* pMap = luaT_testuserdata(L); + int iX = static_cast(luaL_checkinteger(L, 2) - 1); + int iY = static_cast(luaL_checkinteger(L, 3) - 1); + int iPlayer = static_cast(luaL_optinteger(L, 4, 1)); + + if (iPlayer < 1 || iPlayer > player_max) { + return luaL_error(L, "Player index out of range: %i", iPlayer); + } + + pMap->set_player_camera_tile(iPlayer - 1, iX, iY); + return 0; +} + +int l_map_get_player_heliport(lua_State* L) { + level_map* pMap = luaT_testuserdata(L); + int iX, iY; + int iPlayer = static_cast(luaL_optinteger(L, 2, 1)); + bool bGood = pMap->get_player_heliport_tile(iPlayer - 1, &iX, &iY); + if (!bGood) { + return luaL_error(L, "Player index out of range: %d", iPlayer); + } + lua_pushinteger(L, iX + 1); + lua_pushinteger(L, iY + 1); + return 2; +} + +int l_map_set_player_heliport(lua_State* L) { + level_map* pMap = luaT_testuserdata(L); + int iX = static_cast(luaL_checkinteger(L, 2) - 1); + int iY = static_cast(luaL_checkinteger(L, 3) - 1); + int iPlayer = static_cast(luaL_optinteger(L, 4, 1)); + + if (iPlayer < 1 || iPlayer > player_max) { + return luaL_error(L, "Player index out of range: %i", iPlayer); + } + + pMap->set_player_heliport_tile(iPlayer - 1, iX, iY); + return 0; +} + +int l_map_getcell(lua_State* L) { + level_map* pMap = luaT_testuserdata(L); + int iX = static_cast(luaL_checkinteger(L, 2) - + 1); // Lua arrays start at 1 - pretend + int iY = static_cast(luaL_checkinteger(L, 3) - 1); // the map does too. + map_tile* pNode = pMap->get_tile(iX, iY); + if (pNode == nullptr) { + return luaL_argerror(L, 2, + lua_pushfstring(L, + "Map co-ordinates out " + "of bounds (%d, %d)", + iX + 1, iY + 1)); + } + if (lua_isnoneornil(L, 4)) { + lua_pushinteger(L, pNode->iBlock[0]); + lua_pushinteger(L, pNode->iBlock[1]); + lua_pushinteger(L, pNode->iBlock[2]); + lua_pushinteger(L, pNode->iBlock[3]); + return 4; + } else { + lua_Integer iLayer = luaL_checkinteger(L, 4) - 1; + if (iLayer < 0 || iLayer >= 4) + return luaL_argerror(L, 4, "Layer index is out of bounds (1-4)"); + lua_pushinteger(L, pNode->iBlock[iLayer]); return 1; -} - -int l_map_getsize(lua_State *L) -{ - level_map* pMap = luaT_testuserdata(L); - lua_pushinteger(L, pMap->get_width()); - lua_pushinteger(L, pMap->get_height()); - return 2; -} - -int l_map_get_player_count(lua_State *L) -{ - level_map* pMap = luaT_testuserdata(L); - lua_pushinteger(L, pMap->get_player_count()); - return 1; -} - -int l_map_set_player_count(lua_State *L) -{ - level_map* pMap = luaT_testuserdata(L); - int count = static_cast(luaL_checkinteger(L, 2)); - - try - { - pMap->set_player_count(count); - } - catch (std::out_of_range) - { - return luaL_error(L, "Player count out of range %d", count); - } - return 0; -} - -int l_map_get_player_camera(lua_State *L) -{ - level_map* pMap = luaT_testuserdata(L); - int iX, iY; - int iPlayer = static_cast(luaL_optinteger(L, 2, 1)); - bool bGood = pMap->get_player_camera_tile(iPlayer - 1, &iX, &iY); - if(!bGood) - return luaL_error(L, "Player index out of range: %d", iPlayer); - lua_pushinteger(L, iX + 1); - lua_pushinteger(L, iY + 1); - return 2; -} - -int l_map_set_player_camera(lua_State *L) -{ - level_map* pMap = luaT_testuserdata(L); - int iX = static_cast(luaL_checkinteger(L, 2) - 1); - int iY = static_cast(luaL_checkinteger(L, 3) - 1); - int iPlayer = static_cast(luaL_optinteger(L, 4, 1)); - - if (iPlayer < 1 || iPlayer > player_max) - return luaL_error(L, "Player index out of range: %i", iPlayer); - - pMap->set_player_camera_tile(iPlayer - 1, iX, iY); - return 0; -} - -int l_map_get_player_heliport(lua_State *L) -{ - level_map* pMap = luaT_testuserdata(L); - int iX, iY; - int iPlayer = static_cast(luaL_optinteger(L, 2, 1)); - bool bGood = pMap->get_player_heliport_tile(iPlayer - 1, &iX, &iY); - if(!bGood) - return luaL_error(L, "Player index out of range: %d", iPlayer); - lua_pushinteger(L, iX + 1); - lua_pushinteger(L, iY + 1); - return 2; -} - -int l_map_set_player_heliport(lua_State *L) -{ - level_map* pMap = luaT_testuserdata(L); - int iX = static_cast(luaL_checkinteger(L, 2) - 1); - int iY = static_cast(luaL_checkinteger(L, 3) - 1); - int iPlayer = static_cast(luaL_optinteger(L, 4, 1)); - - if (iPlayer < 1 || iPlayer > player_max) - return luaL_error(L, "Player index out of range: %i", iPlayer); - - pMap->set_player_heliport_tile(iPlayer - 1, iX, iY); - return 0; -} - -int l_map_getcell(lua_State *L) -{ - level_map* pMap = luaT_testuserdata(L); - int iX = static_cast(luaL_checkinteger(L, 2) - 1); // Lua arrays start at 1 - pretend - int iY = static_cast(luaL_checkinteger(L, 3) - 1); // the map does too. - map_tile* pNode = pMap->get_tile(iX, iY); - if(pNode == nullptr) - { - return luaL_argerror(L, 2, lua_pushfstring(L, "Map co-ordinates out " - "of bounds (%d, %d)", iX + 1, iY + 1)); - } - if(lua_isnoneornil(L, 4)) - { - lua_pushinteger(L, pNode->iBlock[0]); - lua_pushinteger(L, pNode->iBlock[1]); - lua_pushinteger(L, pNode->iBlock[2]); - lua_pushinteger(L, pNode->iBlock[3]); - return 4; - } - else - { - lua_Integer iLayer = luaL_checkinteger(L, 4) - 1; - if(iLayer < 0 || iLayer >= 4) - return luaL_argerror(L, 4, "Layer index is out of bounds (1-4)"); - lua_pushinteger(L, pNode->iBlock[iLayer]); - return 1; - } + } } /** Recognized tile flags by Lua. */ -const std::map lua_tile_flag_map { - {"passable", map_tile_flags::key::passable_mask}, - {"hospital", map_tile_flags::key::hospital_mask}, - {"buildable", map_tile_flags::key::buildable_mask}, - {"room", map_tile_flags::key::room_mask}, - {"doorWest", map_tile_flags::key::door_west_mask}, - {"doorNorth", map_tile_flags::key::door_north_mask}, - {"tallWest", map_tile_flags::key::tall_west_mask}, - {"tallNorth", map_tile_flags::key::tall_north_mask}, - {"travelNorth", map_tile_flags::key::can_travel_n_mask}, - {"travelEast", map_tile_flags::key::can_travel_e_mask}, - {"travelSouth", map_tile_flags::key::can_travel_s_mask}, - {"travelWest", map_tile_flags::key::can_travel_w_mask}, - {"doNotIdle", map_tile_flags::key::do_not_idle_mask}, +const std::map lua_tile_flag_map{ + {"passable", map_tile_flags::key::passable_mask}, + {"hospital", map_tile_flags::key::hospital_mask}, + {"buildable", map_tile_flags::key::buildable_mask}, + {"room", map_tile_flags::key::room_mask}, + {"doorWest", map_tile_flags::key::door_west_mask}, + {"doorNorth", map_tile_flags::key::door_north_mask}, + {"tallWest", map_tile_flags::key::tall_west_mask}, + {"tallNorth", map_tile_flags::key::tall_north_mask}, + {"travelNorth", map_tile_flags::key::can_travel_n_mask}, + {"travelEast", map_tile_flags::key::can_travel_e_mask}, + {"travelSouth", map_tile_flags::key::can_travel_s_mask}, + {"travelWest", map_tile_flags::key::can_travel_w_mask}, + {"doNotIdle", map_tile_flags::key::do_not_idle_mask}, {"buildableNorth", map_tile_flags::key::buildable_n_mask}, - {"buildableEast", map_tile_flags::key::buildable_e_mask}, + {"buildableEast", map_tile_flags::key::buildable_e_mask}, {"buildableSouth", map_tile_flags::key::buildable_s_mask}, - {"buildableWest", map_tile_flags::key::buildable_w_mask}, + {"buildableWest", map_tile_flags::key::buildable_w_mask}, }; /** @@ -508,12 +495,11 @@ const std::map lua_tile_flag_map { * @param flag Flag of the tile to check (and report). * @param name Name of the flag in Lua code. */ -inline void add_cellflag(lua_State *L, const map_tile *tile, - map_tile_flags::key flag, const std::string &name) -{ - lua_pushlstring(L, name.c_str(), name.size()); - lua_pushboolean(L, tile->flags[flag] ? 1 : 0); - lua_settable(L, 4); +inline void add_cellflag(lua_State* L, const map_tile* tile, + map_tile_flags::key flag, const std::string& name) { + lua_pushlstring(L, name.c_str(), name.size()); + lua_pushboolean(L, tile->flags[flag] ? 1 : 0); + lua_settable(L, 4); } /** @@ -522,11 +508,10 @@ inline void add_cellflag(lua_State *L, const map_tile *tile, * @param value Value of the tile field to add. * @param name Name of the field in Lua code. */ -inline void add_cellint(lua_State *L, int value, const std::string &name) -{ - lua_pushlstring(L, name.c_str(), name.size()); - lua_pushinteger(L, value); - lua_settable(L, 4); +inline void add_cellint(lua_State* L, int value, const std::string& name) { + lua_pushlstring(L, name.c_str(), name.size()); + lua_pushinteger(L, value); + lua_settable(L, 4); } /** @@ -534,525 +519,515 @@ inline void add_cellint(lua_State *L, int value, const std::string &name) * @param L Lua context. * @return Number of results of the call. */ -int l_map_getcellflags(lua_State *L) -{ - level_map* pMap = luaT_testuserdata(L); - int iX = static_cast(luaL_checkinteger(L, 2) - 1); // Lua arrays start at 1 - pretend - int iY = static_cast(luaL_checkinteger(L, 3) - 1); // the map does too. - map_tile* pNode = pMap->get_tile(iX, iY); - if(pNode == nullptr) - return luaL_argerror(L, 2, "Map co-ordinates out of bounds"); - if(lua_type(L, 4) != LUA_TTABLE) - { - lua_settop(L, 3); - lua_createtable(L, 0, 1); - } - else - { - lua_settop(L, 4); - } +int l_map_getcellflags(lua_State* L) { + level_map* pMap = luaT_testuserdata(L); + int iX = static_cast(luaL_checkinteger(L, 2) - + 1); // Lua arrays start at 1 - pretend + int iY = static_cast(luaL_checkinteger(L, 3) - 1); // the map does too. + map_tile* pNode = pMap->get_tile(iX, iY); + if (pNode == nullptr) { + return luaL_argerror(L, 2, "Map co-ordinates out of bounds"); + } + if (lua_type(L, 4) != LUA_TTABLE) { + lua_settop(L, 3); + lua_createtable(L, 0, 1); + } else { + lua_settop(L, 4); + } - // Fill Lua table with the flags and numbers of the tile. - for (auto val : lua_tile_flag_map) - { - add_cellflag(L, pNode, val.second, val.first); - } - add_cellint(L, pNode->iRoomId, "roomId"); - add_cellint(L, pNode->iParcelId, "parcelId"); - add_cellint(L, pMap->get_tile_owner(pNode), "owner"); - add_cellint(L, static_cast(pNode->objects.empty() ? object_type::no_object : pNode->objects.front()), "thob"); - return 1; + // Fill Lua table with the flags and numbers of the tile. + for (auto val : lua_tile_flag_map) { + add_cellflag(L, pNode, val.second, val.first); + } + add_cellint(L, pNode->iRoomId, "roomId"); + add_cellint(L, pNode->iParcelId, "parcelId"); + add_cellint(L, pMap->get_tile_owner(pNode), "owner"); + add_cellint(L, + static_cast(pNode->objects.empty() ? object_type::no_object + : pNode->objects.front()), + "thob"); + return 1; } /* because all the thobs are not retrieved when the map is loaded in c lua objects use the afterLoad function to be registered after a load, if the object list would not be cleared it would result in duplication of thobs in the object list. */ -int l_map_erase_thobs(lua_State *L) -{ - level_map* pMap = luaT_testuserdata(L); - int iX = static_cast(luaL_checkinteger(L, 2) - 1); // Lua arrays start at 1 - pretend - int iY = static_cast(luaL_checkinteger(L, 3) - 1); // the map does too. - map_tile* pNode = pMap->get_tile(iX, iY); - if(pNode == nullptr) - return luaL_argerror(L, 2, "Map co-ordinates out of bounds"); - pNode->objects.clear(); - return 1; +int l_map_erase_thobs(lua_State* L) { + level_map* pMap = luaT_testuserdata(L); + int iX = static_cast(luaL_checkinteger(L, 2) - + 1); // Lua arrays start at 1 - pretend + int iY = static_cast(luaL_checkinteger(L, 3) - 1); // the map does too. + map_tile* pNode = pMap->get_tile(iX, iY); + if (pNode == nullptr) { + return luaL_argerror(L, 2, "Map co-ordinates out of bounds"); + } + pNode->objects.clear(); + return 1; } -int l_map_remove_cell_thob(lua_State *L) -{ - level_map* pMap = luaT_testuserdata(L); - int iX = static_cast(luaL_checkinteger(L, 2) - 1); // Lua arrays start at 1 - pretend - int iY = static_cast(luaL_checkinteger(L, 3) - 1); // the map does too. - map_tile* pNode = pMap->get_tile(iX, iY); - if(pNode == nullptr) - return luaL_argerror(L, 2, "Map co-ordinates out of bounds"); - auto thob = static_cast(luaL_checkinteger(L, 4)); - for(auto iter = pNode->objects.begin(); iter != pNode->objects.end(); iter++) - { - if(*iter == thob) - { - pNode->objects.erase(iter); - break; - } +int l_map_remove_cell_thob(lua_State* L) { + level_map* pMap = luaT_testuserdata(L); + int iX = static_cast(luaL_checkinteger(L, 2) - + 1); // Lua arrays start at 1 - pretend + int iY = static_cast(luaL_checkinteger(L, 3) - 1); // the map does too. + map_tile* pNode = pMap->get_tile(iX, iY); + if (pNode == nullptr) { + return luaL_argerror(L, 2, "Map co-ordinates out of bounds"); + } + auto thob = static_cast(luaL_checkinteger(L, 4)); + for (auto iter = pNode->objects.begin(); iter != pNode->objects.end(); + iter++) { + if (*iter == thob) { + pNode->objects.erase(iter); + break; } - return 1; + } + return 1; } -int l_map_setcellflags(lua_State *L) -{ - level_map* pMap = luaT_testuserdata(L); - int iX = static_cast(luaL_checkinteger(L, 2) - 1); // Lua arrays start at 1 - pretend - int iY = static_cast(luaL_checkinteger(L, 3) - 1); // the map does too. - map_tile* pNode = pMap->get_tile(iX, iY); - if(pNode == nullptr) - return luaL_argerror(L, 2, "Map co-ordinates out of bounds"); - luaL_checktype(L, 4, LUA_TTABLE); +int l_map_setcellflags(lua_State* L) { + level_map* pMap = luaT_testuserdata(L); + int iX = static_cast(luaL_checkinteger(L, 2) - + 1); // Lua arrays start at 1 - pretend + int iY = static_cast(luaL_checkinteger(L, 3) - 1); // the map does too. + map_tile* pNode = pMap->get_tile(iX, iY); + if (pNode == nullptr) { + return luaL_argerror(L, 2, "Map co-ordinates out of bounds"); + } + luaL_checktype(L, 4, LUA_TTABLE); + lua_settop(L, 4); + + lua_pushnil(L); + + while (lua_next(L, 4)) { + if (lua_type(L, 5) == LUA_TSTRING) { + const char* field = lua_tostring(L, 5); + + auto iter = lua_tile_flag_map.find(field); + if (iter != lua_tile_flag_map.end()) { + if (lua_toboolean(L, 6) == 0) { + pNode->flags[(*iter).second] = false; + } else { + pNode->flags[(*iter).second] = true; + } + } else if (std::strcmp(field, "thob") == 0) { + auto thob = static_cast(lua_tointeger(L, 6)); + pNode->objects.push_back(thob); + } else if (std::strcmp(field, "parcelId") == 0) { + pNode->iParcelId = static_cast(lua_tointeger(L, 6)); + } else if (std::strcmp(field, "roomId") == 0) { + pNode->iRoomId = static_cast(lua_tointeger(L, 6)); + } else { + luaL_error(L, "Invalid flag \'%s\'", field); + } + } + lua_settop(L, 5); + } + return 0; +} + +int l_map_setwallflags(lua_State* L) { + level_map* pMap = luaT_testuserdata(L); + pMap->set_all_wall_draw_flags((uint8_t)luaL_checkinteger(L, 2)); + lua_settop(L, 1); + return 1; +} + +int l_map_setcell(lua_State* L) { + level_map* pMap = luaT_testuserdata(L); + int iX = static_cast(luaL_checkinteger(L, 2) - + 1); // Lua arrays start at 1 - pretend + int iY = static_cast(luaL_checkinteger(L, 3) - 1); // the map does too. + map_tile* pNode = pMap->get_tile(iX, iY); + if (pNode == nullptr) { + return luaL_argerror(L, 2, "Map co-ordinates out of bounds"); + } + if (lua_gettop(L) >= 7) { + pNode->iBlock[0] = (uint16_t)luaL_checkinteger(L, 4); + pNode->iBlock[1] = (uint16_t)luaL_checkinteger(L, 5); + pNode->iBlock[2] = (uint16_t)luaL_checkinteger(L, 6); + pNode->iBlock[3] = (uint16_t)luaL_checkinteger(L, 7); + } else { + lua_Integer iLayer = luaL_checkinteger(L, 4) - 1; + if (iLayer < 0 || iLayer >= 4) + return luaL_argerror(L, 4, "Layer index is out of bounds (1-4)"); + uint16_t iBlock = static_cast(luaL_checkinteger(L, 5)); + pNode->iBlock[iLayer] = iBlock; + } + + lua_settop(L, 1); + return 1; +} + +int l_map_updateshadows(lua_State* L) { + level_map* pMap = luaT_testuserdata(L); + pMap->update_shadows(); + lua_settop(L, 1); + return 1; +} + +int l_map_updatepathfinding(lua_State* L) { + level_map* pMap = luaT_testuserdata(L); + pMap->update_pathfinding(); + lua_settop(L, 1); + return 1; +} + +int l_map_mark_room(lua_State* L) { + level_map* pMap = luaT_testuserdata(L); + int iX_ = static_cast(luaL_checkinteger(L, 2) - 1); + int iY_ = static_cast(luaL_checkinteger(L, 3) - 1); + int iW = static_cast(luaL_checkinteger(L, 4)); + int iH = static_cast(luaL_checkinteger(L, 5)); + uint16_t iTile = static_cast(luaL_checkinteger(L, 6)); + uint16_t iRoomId = static_cast(luaL_optinteger(L, 7, 0)); + + if (iX_ < 0 || iY_ < 0 || (iX_ + iW) > pMap->get_width() || + (iY_ + iH) > pMap->get_height()) { + luaL_argerror(L, 2, "Rectangle is out of bounds"); + } + + for (int iY = iY_; iY < iY_ + iH; ++iY) { + for (int iX = iX_; iX < iX_ + iW; ++iX) { + map_tile* pNode = pMap->get_tile_unchecked(iX, iY); + pNode->iBlock[0] = iTile; + pNode->iBlock[3] = 0; + pNode->flags.room = true; + pNode->flags.passable |= pNode->flags.passable_if_not_for_blueprint; + pNode->flags.passable_if_not_for_blueprint = false; + pNode->iRoomId = iRoomId; + } + } + + pMap->update_pathfinding(); + pMap->update_shadows(); + lua_settop(L, 1); + return 1; +} + +int l_map_unmark_room(lua_State* L) { + level_map* pMap = luaT_testuserdata(L); + int iX_ = static_cast(luaL_checkinteger(L, 2) - 1); + int iY_ = static_cast(luaL_checkinteger(L, 3) - 1); + int iW = static_cast(luaL_checkinteger(L, 4)); + int iH = static_cast(luaL_checkinteger(L, 5)); + + if (iX_ < 0 || iY_ < 0 || (iX_ + iW) > pMap->get_width() || + (iY_ + iH) > pMap->get_height()) { + luaL_argerror(L, 2, "Rectangle is out of bounds"); + } + + for (int iY = iY_; iY < iY_ + iH; ++iY) { + for (int iX = iX_; iX < iX_ + iW; ++iX) { + map_tile* pNode = pMap->get_tile_unchecked(iX, iY); + pNode->iBlock[0] = pMap->get_original_tile_unchecked(iX, iY)->iBlock[0]; + pNode->flags.room = false; + pNode->iRoomId = 0; + } + } + + pMap->update_pathfinding(); + pMap->update_shadows(); + + lua_settop(L, 1); + return 1; +} + +int l_map_draw(lua_State* L) { + level_map* pMap = luaT_testuserdata(L); + render_target* pCanvas = luaT_testuserdata(L, 2); + + pMap->draw(pCanvas, static_cast(luaL_checkinteger(L, 3)), + static_cast(luaL_checkinteger(L, 4)), + static_cast(luaL_checkinteger(L, 5)), + static_cast(luaL_checkinteger(L, 6)), + static_cast(luaL_optinteger(L, 7, 0)), + static_cast(luaL_optinteger(L, 8, 0))); + + lua_settop(L, 1); + return 1; +} + +int l_map_hittest(lua_State* L) { + level_map* pMap = luaT_testuserdata(L); + drawable* pObject = pMap->hit_test(static_cast(luaL_checkinteger(L, 2)), + static_cast(luaL_checkinteger(L, 3))); + if (pObject == nullptr) { + return 0; + } + lua_rawgeti(L, luaT_upvalueindex(1), 1); + lua_pushlightuserdata(L, pObject); + lua_gettable(L, -2); + return 1; +} + +int l_map_get_parcel_tilecount(lua_State* L) { + level_map* pMap = luaT_testuserdata(L); + int iParcel = static_cast(luaL_checkinteger(L, 2)); + lua_Integer iCount = pMap->get_parcel_tile_count(iParcel); + lua_pushinteger(L, iCount); + return 1; +} + +int l_map_get_parcel_count(lua_State* L) { + level_map* pMap = luaT_testuserdata(L); + lua_pushinteger(L, pMap->get_parcel_count()); + return 1; +} + +int l_map_set_parcel_owner(lua_State* L) { + level_map* pMap = luaT_testuserdata(L); + int parcelId = static_cast(luaL_checkinteger(L, 2)); + int player = static_cast(luaL_checkinteger(L, 3)); + if (lua_type(L, 4) != LUA_TTABLE) { + lua_settop(L, 3); + lua_newtable(L); + } else { lua_settop(L, 4); + } + std::vector> vSplitTiles = + pMap->set_parcel_owner(parcelId, player); + for (std::vector>::size_type i = 0; + i != vSplitTiles.size(); i++) { + lua_pushinteger(L, i + 1); + lua_createtable(L, 0, 2); + lua_pushinteger(L, 1); + lua_pushinteger(L, vSplitTiles[i].first + 1); + lua_settable(L, 6); + lua_pushinteger(L, 2); + lua_pushinteger(L, vSplitTiles[i].second + 1); + lua_settable(L, 6); + lua_settable(L, 4); + } + return 1; +} - lua_pushnil(L); +int l_map_get_parcel_owner(lua_State* L) { + level_map* pMap = luaT_testuserdata(L); + lua_pushinteger( + L, pMap->get_parcel_owner(static_cast(luaL_checkinteger(L, 2)))); + return 1; +} - while(lua_next(L, 4)) - { - if(lua_type(L, 5) == LUA_TSTRING) - { - const char *field = lua_tostring(L, 5); +int l_map_is_parcel_purchasable(lua_State* L) { + level_map* pMap = luaT_testuserdata(L); + lua_pushboolean( + L, pMap->is_parcel_purchasable(static_cast(luaL_checkinteger(L, 2)), + static_cast(luaL_checkinteger(L, 3))) + ? 1 + : 0); + return 1; +} - auto iter = lua_tile_flag_map.find(field); - if(iter != lua_tile_flag_map.end()) - { - if (lua_toboolean(L, 6) == 0) - pNode->flags[(*iter).second] = false; - else - pNode->flags[(*iter).second] = true; - } - else if (std::strcmp(field, "thob") == 0) - { - auto thob = static_cast(lua_tointeger(L, 6)); - pNode->objects.push_back(thob); - } - else if(std::strcmp(field, "parcelId") == 0) - { - pNode->iParcelId = static_cast(lua_tointeger(L, 6)); - } - else if(std::strcmp(field, "roomId") == 0) - { - pNode->iRoomId = static_cast(lua_tointeger(L,6)); - } - else - { - luaL_error(L, "Invalid flag \'%s\'", field); - } +/* Compute the fraction of corridor tiles with litter, of the parcels owned by + * the given player. */ +int l_map_get_litter_fraction(lua_State* L) { + level_map* pMap = luaT_testuserdata(L); + int owner = static_cast(luaL_checkinteger(L, 2)); + if (owner == 0) { + lua_pushnumber(L, 0.0); // Outside has no litter. + return 1; + } + + double tile_count = 0; + double litter_count = 0; + for (int x = 0; x < pMap->get_width(); x++) { + for (int y = 0; y < pMap->get_height(); y++) { + const map_tile* pNode = pMap->get_tile_unchecked(x, y); + if (pNode->iParcelId == 0 || + owner != pMap->get_parcel_owner(pNode->iParcelId) || + pNode->iRoomId != 0) { + continue; + } + + tile_count++; + for (auto iter = pNode->objects.begin(); iter != pNode->objects.end(); + iter++) { + if (*iter == object_type::litter) { + litter_count++; + break; } - lua_settop(L, 5); + } } - return 0; + } + + double fraction = (tile_count == 0) ? 0.0 : litter_count / tile_count; + lua_pushnumber(L, fraction); + return 1; } -int l_map_setwallflags(lua_State *L) -{ - level_map* pMap = luaT_testuserdata(L); - pMap->set_all_wall_draw_flags((uint8_t)luaL_checkinteger(L, 2)); - lua_settop(L, 1); - return 1; +int l_path_new(lua_State* L) { + luaT_stdnew(L, luaT_environindex, true); + return 1; } -int l_map_setcell(lua_State *L) -{ - level_map* pMap = luaT_testuserdata(L); - int iX = static_cast(luaL_checkinteger(L, 2) - 1); // Lua arrays start at 1 - pretend - int iY = static_cast(luaL_checkinteger(L, 3) - 1); // the map does too. - map_tile* pNode = pMap->get_tile(iX, iY); - if(pNode == nullptr) - return luaL_argerror(L, 2, "Map co-ordinates out of bounds"); - if(lua_gettop(L) >= 7) - { - pNode->iBlock[0] = (uint16_t)luaL_checkinteger(L, 4); - pNode->iBlock[1] = (uint16_t)luaL_checkinteger(L, 5); - pNode->iBlock[2] = (uint16_t)luaL_checkinteger(L, 6); - pNode->iBlock[3] = (uint16_t)luaL_checkinteger(L, 7); - } - else - { - lua_Integer iLayer = luaL_checkinteger(L, 4) - 1; - if(iLayer < 0 || iLayer >= 4) - return luaL_argerror(L, 4, "Layer index is out of bounds (1-4)"); - uint16_t iBlock = static_cast(luaL_checkinteger(L, 5)); - pNode->iBlock[iLayer] = iBlock; - } +int l_path_set_map(lua_State* L) { + pathfinder* pPathfinder = luaT_testuserdata(L); + level_map* pMap = luaT_testuserdata(L, 2); + lua_settop(L, 2); - lua_settop(L, 1); - return 1; + pPathfinder->set_default_map(pMap); + luaT_setenvfield(L, 1, "map"); + return 1; } -int l_map_updateshadows(lua_State *L) -{ - level_map* pMap = luaT_testuserdata(L); - pMap->update_shadows(); - lua_settop(L, 1); - return 1; +int l_path_persist(lua_State* L) { + pathfinder* pPathfinder = luaT_testuserdata(L); + lua_settop(L, 2); + lua_insert(L, 1); + pPathfinder->persist((lua_persist_writer*)lua_touserdata(L, 1)); + return 0; } -int l_map_updatepathfinding(lua_State *L) -{ - level_map* pMap = luaT_testuserdata(L); - pMap->update_pathfinding(); - lua_settop(L, 1); - return 1; +int l_path_depersist(lua_State* L) { + pathfinder* pPathfinder = luaT_testuserdata(L); + lua_settop(L, 2); + lua_insert(L, 1); + lua_persist_reader* pReader = (lua_persist_reader*)lua_touserdata(L, 1); + + pPathfinder->depersist(pReader); + luaT_getenvfield(L, 2, "map"); + pPathfinder->set_default_map( + reinterpret_cast(lua_touserdata(L, -1))); + return 0; } -int l_map_mark_room(lua_State *L) -{ - level_map* pMap = luaT_testuserdata(L); - int iX_ = static_cast(luaL_checkinteger(L, 2) - 1); - int iY_ = static_cast(luaL_checkinteger(L, 3) - 1); - int iW = static_cast(luaL_checkinteger(L, 4)); - int iH = static_cast(luaL_checkinteger(L, 5)); - uint16_t iTile = static_cast(luaL_checkinteger(L, 6)); - uint16_t iRoomId = static_cast(luaL_optinteger(L, 7, 0)); +int l_path_is_reachable_from_hospital(lua_State* L) { + pathfinder* pPathfinder = luaT_testuserdata(L); - if(iX_ < 0 || iY_ < 0 || (iX_ + iW) > pMap->get_width() || (iY_ + iH) > pMap->get_height()) - luaL_argerror(L, 2, "Rectangle is out of bounds"); - - for(int iY = iY_; iY < iY_ + iH; ++iY) - { - for(int iX = iX_; iX < iX_ + iW; ++iX) - { - map_tile *pNode = pMap->get_tile_unchecked(iX, iY); - pNode->iBlock[0] = iTile; - pNode->iBlock[3] = 0; - pNode->flags.room = true; - pNode->flags.passable |= pNode->flags.passable_if_not_for_blueprint; - pNode->flags.passable_if_not_for_blueprint = false; - pNode->iRoomId = iRoomId; - } - } - - pMap->update_pathfinding(); - pMap->update_shadows(); - lua_settop(L, 1); - return 1; -} - -int l_map_unmark_room(lua_State *L) -{ - level_map* pMap = luaT_testuserdata(L); - int iX_ = static_cast(luaL_checkinteger(L, 2) - 1); - int iY_ = static_cast(luaL_checkinteger(L, 3) - 1); - int iW = static_cast(luaL_checkinteger(L, 4)); - int iH = static_cast(luaL_checkinteger(L, 5)); - - if(iX_ < 0 || iY_ < 0 || (iX_ + iW) > pMap->get_width() || (iY_ + iH) > pMap->get_height()) - luaL_argerror(L, 2, "Rectangle is out of bounds"); - - for(int iY = iY_; iY < iY_ + iH; ++iY) - { - for(int iX = iX_; iX < iX_ + iW; ++iX) - { - map_tile *pNode = pMap->get_tile_unchecked(iX, iY); - pNode->iBlock[0] = pMap->get_original_tile_unchecked(iX, iY)->iBlock[0]; - pNode->flags.room = false; - pNode->iRoomId = 0; - } - } - - pMap->update_pathfinding(); - pMap->update_shadows(); - - lua_settop(L, 1); - return 1; -} - -int l_map_draw(lua_State *L) -{ - level_map* pMap = luaT_testuserdata(L); - render_target* pCanvas = luaT_testuserdata(L, 2); - - pMap->draw(pCanvas, static_cast(luaL_checkinteger(L, 3)), static_cast(luaL_checkinteger(L, 4)), static_cast(luaL_checkinteger(L, 5)), - static_cast(luaL_checkinteger(L, 6)), static_cast(luaL_optinteger(L, 7, 0)), static_cast(luaL_optinteger(L, 8, 0))); - - lua_settop(L, 1); - return 1; -} - -int l_map_hittest(lua_State *L) -{ - level_map* pMap = luaT_testuserdata(L); - drawable* pObject = pMap->hit_test(static_cast(luaL_checkinteger(L, 2)), static_cast(luaL_checkinteger(L, 3))); - if(pObject == nullptr) - return 0; - lua_rawgeti(L, luaT_upvalueindex(1), 1); - lua_pushlightuserdata(L, pObject); - lua_gettable(L, -2); - return 1; -} - -int l_map_get_parcel_tilecount(lua_State *L) -{ - level_map* pMap = luaT_testuserdata(L); - int iParcel = static_cast(luaL_checkinteger(L, 2)); - lua_Integer iCount = pMap->get_parcel_tile_count(iParcel); - lua_pushinteger(L, iCount); - return 1; -} - -int l_map_get_parcel_count(lua_State *L) -{ - level_map* pMap = luaT_testuserdata(L); - lua_pushinteger(L, pMap->get_parcel_count()); - return 1; -} - -int l_map_set_parcel_owner(lua_State *L) -{ - level_map* pMap = luaT_testuserdata(L); - int parcelId = static_cast(luaL_checkinteger(L, 2)); - int player = static_cast(luaL_checkinteger(L, 3)); - if(lua_type(L, 4) != LUA_TTABLE) - { - lua_settop(L, 3); - lua_newtable(L); - } - else - { - lua_settop(L, 4); - } - std::vector> vSplitTiles = pMap->set_parcel_owner(parcelId, player); - for (std::vector>::size_type i = 0; i != vSplitTiles.size(); i++) - { - lua_pushinteger(L, i + 1); - lua_createtable(L, 0, 2); - lua_pushinteger(L, 1); - lua_pushinteger(L, vSplitTiles[i].first + 1); - lua_settable(L, 6); - lua_pushinteger(L, 2); - lua_pushinteger(L, vSplitTiles[i].second + 1); - lua_settable(L, 6); - lua_settable(L, 4); - } - return 1; -} - -int l_map_get_parcel_owner(lua_State *L) -{ - level_map* pMap = luaT_testuserdata(L); - lua_pushinteger(L, pMap->get_parcel_owner(static_cast(luaL_checkinteger(L, 2)))); - return 1; -} - -int l_map_is_parcel_purchasable(lua_State *L) -{ - level_map* pMap = luaT_testuserdata(L); - lua_pushboolean(L, pMap->is_parcel_purchasable(static_cast(luaL_checkinteger(L, 2)), - static_cast(luaL_checkinteger(L, 3))) ? 1 : 0); - return 1; -} - -/* Compute the fraction of corridor tiles with litter, of the parcels owned by the given player. */ -int l_map_get_litter_fraction(lua_State *L) -{ - level_map* pMap = luaT_testuserdata(L); - int owner = static_cast(luaL_checkinteger(L, 2)); - if (owner == 0) - { - lua_pushnumber(L, 0.0); // Outside has no litter. - return 1; - } - - double tile_count = 0; - double litter_count = 0; - for (int x = 0; x < pMap->get_width(); x++) - { - for (int y = 0; y < pMap->get_height(); y++) - { - const map_tile* pNode = pMap->get_tile_unchecked(x, y); - if (pNode->iParcelId == 0 || owner != pMap->get_parcel_owner(pNode->iParcelId) || - pNode->iRoomId != 0) - { - continue; - } - - tile_count++; - for(auto iter = pNode->objects.begin(); iter != pNode->objects.end(); iter++) - { - if(*iter == object_type::litter) - { - litter_count++; - break; - } - } - } - } - - double fraction = (tile_count == 0) ? 0.0 : litter_count / tile_count; - lua_pushnumber(L, fraction); - return 1; -} - -int l_path_new(lua_State *L) -{ - luaT_stdnew(L, luaT_environindex, true); - return 1; -} - -int l_path_set_map(lua_State *L) -{ - pathfinder* pPathfinder = luaT_testuserdata(L); - level_map* pMap = luaT_testuserdata(L, 2); - lua_settop(L, 2); - - pPathfinder->set_default_map(pMap); - luaT_setenvfield(L, 1, "map"); - return 1; -} - -int l_path_persist(lua_State *L) -{ - pathfinder* pPathfinder = luaT_testuserdata(L); - lua_settop(L, 2); - lua_insert(L, 1); - pPathfinder->persist((lua_persist_writer*)lua_touserdata(L, 1)); - return 0; -} - -int l_path_depersist(lua_State *L) -{ - pathfinder* pPathfinder = luaT_testuserdata(L); - lua_settop(L, 2); - lua_insert(L, 1); - lua_persist_reader* pReader = (lua_persist_reader*)lua_touserdata(L, 1); - - pPathfinder->depersist(pReader); - luaT_getenvfield(L, 2, "map"); - pPathfinder->set_default_map(reinterpret_cast(lua_touserdata(L, -1))); - return 0; -} - -int l_path_is_reachable_from_hospital(lua_State *L) -{ - pathfinder* pPathfinder = luaT_testuserdata(L); - if(pPathfinder->find_path_to_hospital(nullptr, static_cast(luaL_checkinteger(L, 2) - 1), - static_cast(luaL_checkinteger(L, 3) - 1))) - { - lua_pushboolean(L, 1); - int iX, iY; - pPathfinder->get_path_end(&iX, &iY); - lua_pushinteger(L, iX + 1); - lua_pushinteger(L, iY + 1); - return 3; - } - else - { - lua_pushboolean(L, 0); - return 1; - } -} - -int l_path_distance(lua_State *L) -{ - pathfinder* pPathfinder = luaT_testuserdata(L); - if(pPathfinder->find_path(nullptr, static_cast(luaL_checkinteger(L, 2)) - 1, static_cast(luaL_checkinteger(L, 3)) - 1, - static_cast(luaL_checkinteger(L, 4)) - 1, static_cast(luaL_checkinteger(L, 5)) - 1)) - { - lua_pushinteger(L, pPathfinder->get_path_length()); - } - else - { - lua_pushboolean(L, 0); - } - return 1; -} - -int l_path_path(lua_State *L) -{ - pathfinder* pPathfinder = luaT_testuserdata(L); - pPathfinder->find_path(nullptr, static_cast(luaL_checkinteger(L, 2)) - 1, static_cast(luaL_checkinteger(L, 3)) - 1, - static_cast(luaL_checkinteger(L, 4)) - 1, static_cast(luaL_checkinteger(L, 5)) - 1); - pPathfinder->push_result(L); - return 2; -} - -int l_path_idle(lua_State *L) -{ - pathfinder* pPathfinder = luaT_testuserdata(L); - if(!pPathfinder->find_idle_tile(nullptr, static_cast(luaL_checkinteger(L, 2)) - 1, - static_cast(luaL_checkinteger(L, 3)) - 1, static_cast(luaL_optinteger(L, 4, 0)))) - { - return 0; - } + bool found = pPathfinder->find_path_to_hospital( + nullptr, static_cast(luaL_checkinteger(L, 2) - 1), + static_cast(luaL_checkinteger(L, 3) - 1)); + if (found) { + lua_pushboolean(L, 1); int iX, iY; pPathfinder->get_path_end(&iX, &iY); lua_pushinteger(L, iX + 1); lua_pushinteger(L, iY + 1); - return 2; -} - -int l_path_visit(lua_State *L) -{ - pathfinder* pPathfinder = luaT_testuserdata(L); - luaL_checktype(L, 6, LUA_TFUNCTION); - lua_pushboolean(L, pPathfinder->visit_objects(nullptr, static_cast(luaL_checkinteger(L, 2)) - 1, - static_cast(luaL_checkinteger(L, 3)) - 1, static_cast(luaL_checkinteger(L, 4)), - static_cast(luaL_checkinteger(L, 5)), L, 6, luaL_checkinteger(L, 4) == 0 ? true : false) ? 1 : 0); + return 3; + } else { + lua_pushboolean(L, 0); return 1; + } } -} // namespace +int l_path_distance(lua_State* L) { + pathfinder* pPathfinder = luaT_testuserdata(L); -void lua_register_map(const lua_register_state *pState) -{ - // Map - { - lua_class_binding lcb(pState, "map", l_map_new, lua_metatable::map); - lcb.add_metamethod(l_map_persist, "persist", lua_metatable::anim); - lcb.add_metamethod(l_map_depersist, "depersist", lua_metatable::anim); - lcb.add_function(l_map_load, "load"); - lcb.add_function(l_map_loadblank, "loadBlank"); - lcb.add_function(l_map_save, "save"); - lcb.add_function(l_map_getsize, "size"); - lcb.add_function(l_map_get_player_count, "getPlayerCount"); - lcb.add_function(l_map_set_player_count, "setPlayerCount"); - lcb.add_function(l_map_get_player_camera, "getCameraTile"); - lcb.add_function(l_map_set_player_camera, "setCameraTile"); - lcb.add_function(l_map_get_player_heliport, "getHeliportTile"); - lcb.add_function(l_map_set_player_heliport, "setHeliportTile"); - lcb.add_function(l_map_getcell, "getCell"); - lcb.add_function(l_map_gettemperature, "getCellTemperature"); - lcb.add_function(l_map_getcellflags, "getCellFlags"); - lcb.add_function(l_map_setcellflags, "setCellFlags"); - lcb.add_function(l_map_setcell, "setCell"); - lcb.add_function(l_map_setwallflags, "setWallDrawFlags"); - lcb.add_function(l_map_settemperaturedisplay, "setTemperatureDisplay"); - lcb.add_function(l_map_updatetemperature, "updateTemperatures"); - lcb.add_function(l_map_updateblueprint, "updateRoomBlueprint", lua_metatable::anims, lua_metatable::anim); - lcb.add_function(l_map_updateshadows, "updateShadows"); - lcb.add_function(l_map_updatepathfinding, "updatePathfinding"); - lcb.add_function(l_map_mark_room, "markRoom"); - lcb.add_function(l_map_unmark_room, "unmarkRoom"); - lcb.add_function(l_map_set_sheet, "setSheet", lua_metatable::sheet); - lcb.add_function(l_map_draw, "draw", lua_metatable::surface); - lcb.add_function(l_map_hittest, "hitTestObjects", lua_metatable::anim); - lcb.add_function(l_map_get_parcel_tilecount, "getParcelTileCount"); - lcb.add_function(l_map_get_parcel_count, "getPlotCount"); - lcb.add_function(l_map_set_parcel_owner, "setPlotOwner"); - lcb.add_function(l_map_get_parcel_owner, "getPlotOwner"); - lcb.add_function(l_map_is_parcel_purchasable, "isParcelPurchasable"); - lcb.add_function(l_map_erase_thobs, "eraseObjectTypes"); - lcb.add_function(l_map_remove_cell_thob, "removeObjectType"); - lcb.add_function(l_map_get_litter_fraction, "getLitterFraction"); - } - - // Pathfinder - { - lua_class_binding lcb(pState, "pathfinder", l_path_new, lua_metatable::pathfinder); - lcb.add_metamethod(l_path_persist, "persist"); - lcb.add_metamethod(l_path_depersist, "depersist"); - lcb.add_function(l_path_distance, "findDistance"); - lcb.add_function(l_path_is_reachable_from_hospital, "isReachableFromHospital"); - lcb.add_function(l_path_path, "findPath"); - lcb.add_function(l_path_idle, "findIdleTile"); - lcb.add_function(l_path_visit, "findObject"); - lcb.add_function(l_path_set_map, "setMap", lua_metatable::map); - } + bool found = pPathfinder->find_path( + nullptr, static_cast(luaL_checkinteger(L, 2)) - 1, + static_cast(luaL_checkinteger(L, 3)) - 1, + static_cast(luaL_checkinteger(L, 4)) - 1, + static_cast(luaL_checkinteger(L, 5)) - 1); + if (found) { + lua_pushinteger(L, pPathfinder->get_path_length()); + } else { + lua_pushboolean(L, 0); + } + return 1; +} + +int l_path_path(lua_State* L) { + pathfinder* pPathfinder = luaT_testuserdata(L); + pPathfinder->find_path(nullptr, static_cast(luaL_checkinteger(L, 2)) - 1, + static_cast(luaL_checkinteger(L, 3)) - 1, + static_cast(luaL_checkinteger(L, 4)) - 1, + static_cast(luaL_checkinteger(L, 5)) - 1); + pPathfinder->push_result(L); + return 2; +} + +int l_path_idle(lua_State* L) { + pathfinder* pPathfinder = luaT_testuserdata(L); + + bool found = pPathfinder->find_idle_tile( + nullptr, static_cast(luaL_checkinteger(L, 2)) - 1, + static_cast(luaL_checkinteger(L, 3)) - 1, + static_cast(luaL_optinteger(L, 4, 0))); + if (!found) { + return 0; + } + int iX, iY; + pPathfinder->get_path_end(&iX, &iY); + lua_pushinteger(L, iX + 1); + lua_pushinteger(L, iY + 1); + return 2; +} + +int l_path_visit(lua_State* L) { + pathfinder* pPathfinder = luaT_testuserdata(L); + luaL_checktype(L, 6, LUA_TFUNCTION); + + bool found = pPathfinder->visit_objects( + nullptr, static_cast(luaL_checkinteger(L, 2)) - 1, + static_cast(luaL_checkinteger(L, 3)) - 1, + static_cast(luaL_checkinteger(L, 4)), + static_cast(luaL_checkinteger(L, 5)), L, 6, + luaL_checkinteger(L, 4) == 0 ? true : false); + + lua_pushboolean(L, found); + return 1; +} + +} // namespace + +void lua_register_map(const lua_register_state* pState) { + // Map + { + lua_class_binding lcb(pState, "map", l_map_new, + lua_metatable::map); + lcb.add_metamethod(l_map_persist, "persist", lua_metatable::anim); + lcb.add_metamethod(l_map_depersist, "depersist", lua_metatable::anim); + lcb.add_function(l_map_load, "load"); + lcb.add_function(l_map_loadblank, "loadBlank"); + lcb.add_function(l_map_save, "save"); + lcb.add_function(l_map_getsize, "size"); + lcb.add_function(l_map_get_player_count, "getPlayerCount"); + lcb.add_function(l_map_set_player_count, "setPlayerCount"); + lcb.add_function(l_map_get_player_camera, "getCameraTile"); + lcb.add_function(l_map_set_player_camera, "setCameraTile"); + lcb.add_function(l_map_get_player_heliport, "getHeliportTile"); + lcb.add_function(l_map_set_player_heliport, "setHeliportTile"); + lcb.add_function(l_map_getcell, "getCell"); + lcb.add_function(l_map_gettemperature, "getCellTemperature"); + lcb.add_function(l_map_getcellflags, "getCellFlags"); + lcb.add_function(l_map_setcellflags, "setCellFlags"); + lcb.add_function(l_map_setcell, "setCell"); + lcb.add_function(l_map_setwallflags, "setWallDrawFlags"); + lcb.add_function(l_map_settemperaturedisplay, "setTemperatureDisplay"); + lcb.add_function(l_map_updatetemperature, "updateTemperatures"); + lcb.add_function(l_map_updateblueprint, "updateRoomBlueprint", + lua_metatable::anims, lua_metatable::anim); + lcb.add_function(l_map_updateshadows, "updateShadows"); + lcb.add_function(l_map_updatepathfinding, "updatePathfinding"); + lcb.add_function(l_map_mark_room, "markRoom"); + lcb.add_function(l_map_unmark_room, "unmarkRoom"); + lcb.add_function(l_map_set_sheet, "setSheet", lua_metatable::sheet); + lcb.add_function(l_map_draw, "draw", lua_metatable::surface); + lcb.add_function(l_map_hittest, "hitTestObjects", lua_metatable::anim); + lcb.add_function(l_map_get_parcel_tilecount, "getParcelTileCount"); + lcb.add_function(l_map_get_parcel_count, "getPlotCount"); + lcb.add_function(l_map_set_parcel_owner, "setPlotOwner"); + lcb.add_function(l_map_get_parcel_owner, "getPlotOwner"); + lcb.add_function(l_map_is_parcel_purchasable, "isParcelPurchasable"); + lcb.add_function(l_map_erase_thobs, "eraseObjectTypes"); + lcb.add_function(l_map_remove_cell_thob, "removeObjectType"); + lcb.add_function(l_map_get_litter_fraction, "getLitterFraction"); + } + + // Pathfinder + { + lua_class_binding lcb(pState, "pathfinder", l_path_new, + lua_metatable::pathfinder); + lcb.add_metamethod(l_path_persist, "persist"); + lcb.add_metamethod(l_path_depersist, "depersist"); + lcb.add_function(l_path_distance, "findDistance"); + lcb.add_function(l_path_is_reachable_from_hospital, + "isReachableFromHospital"); + lcb.add_function(l_path_path, "findPath"); + lcb.add_function(l_path_idle, "findIdleTile"); + lcb.add_function(l_path_visit, "findObject"); + lcb.add_function(l_path_set_map, "setMap", lua_metatable::map); + } } diff --git a/CorsixTH/Src/th_lua_movie.cpp b/CorsixTH/Src/th_lua_movie.cpp index 735a5223..62a16b10 100644 --- a/CorsixTH/Src/th_lua_movie.cpp +++ b/CorsixTH/Src/th_lua_movie.cpp @@ -20,134 +20,120 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +#include "th_gfx.h" #include "th_lua_internal.h" #include "th_movie.h" -#include "th_gfx.h" namespace { -int l_movie_new(lua_State *L) -{ - luaT_stdnew(L, luaT_environindex, true); - return 1; +int l_movie_new(lua_State* L) { + luaT_stdnew(L, luaT_environindex, true); + return 1; } -int l_movie_set_renderer(lua_State *L) -{ - movie_player *pMovie = luaT_testuserdata(L); - render_target *pRenderTarget = luaT_testuserdata(L, 2); - pMovie->set_renderer(pRenderTarget->get_renderer()); - return 0; +int l_movie_set_renderer(lua_State* L) { + movie_player* pMovie = luaT_testuserdata(L); + render_target* pRenderTarget = luaT_testuserdata(L, 2); + pMovie->set_renderer(pRenderTarget->get_renderer()); + return 0; } -int l_movie_enabled(lua_State *L) -{ - movie_player *pMovie = luaT_testuserdata(L); - lua_pushboolean(L, pMovie->movies_enabled()); - return 1; +int l_movie_enabled(lua_State* L) { + movie_player* pMovie = luaT_testuserdata(L); + lua_pushboolean(L, pMovie->movies_enabled()); + return 1; } -int l_movie_load(lua_State *L) -{ - bool loaded; - const char* warning; - movie_player *pMovie = luaT_testuserdata(L); - const char* filepath = lua_tolstring(L, 2, nullptr); - pMovie->clear_last_error(); - loaded = pMovie->load(filepath); - warning = pMovie->get_last_error(); - lua_pushboolean(L, loaded); - lua_pushstring(L, warning); - return 2; +int l_movie_load(lua_State* L) { + bool loaded; + const char* warning; + movie_player* pMovie = luaT_testuserdata(L); + const char* filepath = lua_tolstring(L, 2, nullptr); + pMovie->clear_last_error(); + loaded = pMovie->load(filepath); + warning = pMovie->get_last_error(); + lua_pushboolean(L, loaded); + lua_pushstring(L, warning); + return 2; } -int l_movie_unload(lua_State *L) -{ - movie_player *pMovie = luaT_testuserdata(L); - pMovie->unload(); - return 0; +int l_movie_unload(lua_State* L) { + movie_player* pMovie = luaT_testuserdata(L); + pMovie->unload(); + return 0; } -int l_movie_play(lua_State *L) -{ - const char* warning; - movie_player *pMovie = luaT_testuserdata(L); - pMovie->clear_last_error(); - pMovie->play( - static_cast(luaL_checkinteger(L, 2))); - warning = pMovie->get_last_error(); - lua_pushstring(L, warning); - return 1; +int l_movie_play(lua_State* L) { + const char* warning; + movie_player* pMovie = luaT_testuserdata(L); + pMovie->clear_last_error(); + pMovie->play(static_cast(luaL_checkinteger(L, 2))); + warning = pMovie->get_last_error(); + lua_pushstring(L, warning); + return 1; } -int l_movie_stop(lua_State *L) -{ - movie_player *pVideo = luaT_testuserdata(L); - pVideo->stop(); - return 0; +int l_movie_stop(lua_State* L) { + movie_player* pVideo = luaT_testuserdata(L); + pVideo->stop(); + return 0; } -int l_movie_get_native_height(lua_State *L) -{ - movie_player *pMovie = luaT_testuserdata(L); - lua_pushinteger(L, pMovie->get_native_height()); - return 1; +int l_movie_get_native_height(lua_State* L) { + movie_player* pMovie = luaT_testuserdata(L); + lua_pushinteger(L, pMovie->get_native_height()); + return 1; } -int l_movie_get_native_width(lua_State *L) -{ - movie_player *pMovie = luaT_testuserdata(L); - lua_pushinteger(L, pMovie->get_native_width()); - return 1; +int l_movie_get_native_width(lua_State* L) { + movie_player* pMovie = luaT_testuserdata(L); + lua_pushinteger(L, pMovie->get_native_width()); + return 1; } -int l_movie_has_audio_track(lua_State *L) -{ - movie_player *pMovie = luaT_testuserdata(L); - lua_pushboolean(L, pMovie->has_audio_track()); - return 1; +int l_movie_has_audio_track(lua_State* L) { + movie_player* pMovie = luaT_testuserdata(L); + lua_pushboolean(L, pMovie->has_audio_track()); + return 1; } -int l_movie_refresh(lua_State *L) -{ - movie_player *pMovie = luaT_testuserdata(L); - pMovie->refresh(SDL_Rect{ - static_cast(luaL_checkinteger(L, 2)), - static_cast(luaL_checkinteger(L, 3)), - static_cast(luaL_checkinteger(L, 4)), - static_cast(luaL_checkinteger(L, 5)) }); - return 0; +int l_movie_refresh(lua_State* L) { + movie_player* pMovie = luaT_testuserdata(L); + pMovie->refresh(SDL_Rect{static_cast(luaL_checkinteger(L, 2)), + static_cast(luaL_checkinteger(L, 3)), + static_cast(luaL_checkinteger(L, 4)), + static_cast(luaL_checkinteger(L, 5))}); + return 0; } -int l_movie_allocate_picture_buffer(lua_State *L) -{ - movie_player *pMovie = luaT_testuserdata(L); - pMovie->allocate_picture_buffer(); - return 0; +int l_movie_allocate_picture_buffer(lua_State* L) { + movie_player* pMovie = luaT_testuserdata(L); + pMovie->allocate_picture_buffer(); + return 0; } -int l_movie_deallocate_picture_buffer(lua_State *L) -{ - movie_player *pMovie = luaT_testuserdata(L); - pMovie->deallocate_picture_buffer(); - return 0; +int l_movie_deallocate_picture_buffer(lua_State* L) { + movie_player* pMovie = luaT_testuserdata(L); + pMovie->deallocate_picture_buffer(); + return 0; } -} // namespace +} // namespace -void lua_register_movie(const lua_register_state *pState) -{ - lua_class_binding lcb(pState, "moviePlayer", l_movie_new, lua_metatable::movie); - lcb.add_function(l_movie_set_renderer, "setRenderer", lua_metatable::surface); - lcb.add_function(l_movie_enabled, "getEnabled"); - lcb.add_function(l_movie_load, "load"); - lcb.add_function(l_movie_unload, "unload"); - lcb.add_function(l_movie_play, "play"); - lcb.add_function(l_movie_stop, "stop"); - lcb.add_function(l_movie_get_native_height, "getNativeHeight"); - lcb.add_function(l_movie_get_native_width, "getNativeWidth"); - lcb.add_function(l_movie_has_audio_track, "hasAudioTrack"); - lcb.add_function(l_movie_refresh, "refresh"); - lcb.add_function(l_movie_allocate_picture_buffer, "allocatePictureBuffer"); - lcb.add_function(l_movie_deallocate_picture_buffer, "deallocatePictureBuffer"); +void lua_register_movie(const lua_register_state* pState) { + lua_class_binding lcb(pState, "moviePlayer", l_movie_new, + lua_metatable::movie); + lcb.add_function(l_movie_set_renderer, "setRenderer", lua_metatable::surface); + lcb.add_function(l_movie_enabled, "getEnabled"); + lcb.add_function(l_movie_load, "load"); + lcb.add_function(l_movie_unload, "unload"); + lcb.add_function(l_movie_play, "play"); + lcb.add_function(l_movie_stop, "stop"); + lcb.add_function(l_movie_get_native_height, "getNativeHeight"); + lcb.add_function(l_movie_get_native_width, "getNativeWidth"); + lcb.add_function(l_movie_has_audio_track, "hasAudioTrack"); + lcb.add_function(l_movie_refresh, "refresh"); + lcb.add_function(l_movie_allocate_picture_buffer, "allocatePictureBuffer"); + lcb.add_function(l_movie_deallocate_picture_buffer, + "deallocatePictureBuffer"); } diff --git a/CorsixTH/Src/th_lua_sound.cpp b/CorsixTH/Src/th_lua_sound.cpp index a2b6f862..a33ef5bd 100644 --- a/CorsixTH/Src/th_lua_sound.cpp +++ b/CorsixTH/Src/th_lua_sound.cpp @@ -20,44 +20,41 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -#include "th_lua_internal.h" -#include "th_sound.h" -#include "th_lua.h" -#include "lua_sdl.h" +#include #include #include -#include +#include "lua_sdl.h" +#include "th_lua.h" +#include "th_lua_internal.h" +#include "th_sound.h" namespace { int played_sound_callback_ids[1000]; int played_sound_callback_index = 0; -std::map map_sound_timers; +std::map map_sound_timers; -int l_soundarc_new(lua_State *L) -{ - luaT_stdnew(L, luaT_environindex, true); - return 1; +int l_soundarc_new(lua_State* L) { + luaT_stdnew(L, luaT_environindex, true); + return 1; } -int l_soundarc_load(lua_State *L) -{ - sound_archive* pArchive = luaT_testuserdata(L); - size_t iDataLen; - const uint8_t* pData = luaT_checkfile(L, 2, &iDataLen); +int l_soundarc_load(lua_State* L) { + sound_archive* pArchive = luaT_testuserdata(L); + size_t iDataLen; + const uint8_t* pData = luaT_checkfile(L, 2, &iDataLen); - if(pArchive->load_from_th_file(pData, iDataLen)) - lua_pushboolean(L, 1); - else - lua_pushboolean(L, 0); - return 1; + if (pArchive->load_from_th_file(pData, iDataLen)) + lua_pushboolean(L, 1); + else + lua_pushboolean(L, 0); + return 1; } -int l_soundarc_count(lua_State *L) -{ - sound_archive* pArchive = luaT_testuserdata(L); - lua_pushnumber(L, (lua_Number)pArchive->get_number_of_sounds()); - return 1; +int l_soundarc_count(lua_State* L) { + sound_archive* pArchive = luaT_testuserdata(L); + lua_pushnumber(L, (lua_Number)pArchive->get_number_of_sounds()); + return 1; } /** @@ -67,258 +64,240 @@ int l_soundarc_count(lua_State *L) * @return Negative number when \a s1 should be before \a s2, zero if both * string are equal, else a positive number. */ -int ignorecase_cmp(const char *s1, const char *s2) -{ - while(*s1 && *s2) - { - if (std::tolower(*s1) != std::tolower(*s2)) - break; +int ignorecase_cmp(const char* s1, const char* s2) { + while (*s1 && *s2) { + if (std::tolower(*s1) != std::tolower(*s2)) break; - s1++; - s2++; - } - return std::tolower(*s1) - std::tolower(*s2); + s1++; + s2++; + } + return std::tolower(*s1) - std::tolower(*s2); } -size_t l_soundarc_checkidx(lua_State *L, int iArg, sound_archive* pArchive) -{ - if(lua_isnumber(L, iArg)) - { - size_t iIndex = (size_t)lua_tonumber(L, iArg); - if(iIndex >= pArchive->get_number_of_sounds()) - { - lua_pushnil(L); - lua_pushfstring(L, "Sound index out of " - "bounds (%f is not in range [0, %d])", lua_tonumber(L, iArg), - static_cast(pArchive->get_number_of_sounds()) - 1); - return pArchive->get_number_of_sounds(); - } - return iIndex; - } - const char* sName = luaL_checkstring(L, iArg); - lua_getfenv(L, 1); - lua_pushvalue(L, iArg); - lua_rawget(L, -2); - if(lua_type(L, -1) == LUA_TLIGHTUSERDATA) - { - size_t iIndex = (size_t)lua_topointer(L, -1); - lua_pop(L, 2); - return iIndex; +size_t l_soundarc_checkidx(lua_State* L, int iArg, sound_archive* pArchive) { + if (lua_isnumber(L, iArg)) { + size_t iIndex = (size_t)lua_tonumber(L, iArg); + if (iIndex >= pArchive->get_number_of_sounds()) { + lua_pushnil(L); + lua_pushfstring(L, + "Sound index out of " + "bounds (%f is not in range [0, %d])", + lua_tonumber(L, iArg), + static_cast(pArchive->get_number_of_sounds()) - 1); + return pArchive->get_number_of_sounds(); } + return iIndex; + } + const char* sName = luaL_checkstring(L, iArg); + lua_getfenv(L, 1); + lua_pushvalue(L, iArg); + lua_rawget(L, -2); + if (lua_type(L, -1) == LUA_TLIGHTUSERDATA) { + size_t iIndex = (size_t)lua_topointer(L, -1); lua_pop(L, 2); - size_t iCount = pArchive->get_number_of_sounds(); - for(size_t i = 0; i < iCount; ++i) - { - if(ignorecase_cmp(sName, pArchive->get_sound_name(i)) == 0) - { - lua_getfenv(L, 1); - lua_pushvalue(L, iArg); - lua_pushlightuserdata(L, (void*)i); - lua_settable(L, -3); - lua_pop(L, 1); - return i; - } + return iIndex; + } + lua_pop(L, 2); + size_t iCount = pArchive->get_number_of_sounds(); + for (size_t i = 0; i < iCount; ++i) { + if (ignorecase_cmp(sName, pArchive->get_sound_name(i)) == 0) { + lua_getfenv(L, 1); + lua_pushvalue(L, iArg); + lua_pushlightuserdata(L, (void*)i); + lua_settable(L, -3); + lua_pop(L, 1); + return i; } - lua_pushnil(L); - lua_pushliteral(L, "File not found in sound archive: "); - lua_pushvalue(L, iArg); - lua_concat(L, 2); - return pArchive->get_number_of_sounds(); + } + lua_pushnil(L); + lua_pushliteral(L, "File not found in sound archive: "); + lua_pushvalue(L, iArg); + lua_concat(L, 2); + return pArchive->get_number_of_sounds(); } -int l_soundarc_sound_name(lua_State *L) -{ - sound_archive* pArchive = luaT_testuserdata(L); - size_t iIndex = l_soundarc_checkidx(L, 2, pArchive); - if(iIndex == pArchive->get_number_of_sounds()) - return 2; - lua_pushstring(L, pArchive->get_sound_name(iIndex)); - return 1; +int l_soundarc_sound_name(lua_State* L) { + sound_archive* pArchive = luaT_testuserdata(L); + size_t iIndex = l_soundarc_checkidx(L, 2, pArchive); + if (iIndex == pArchive->get_number_of_sounds()) return 2; + lua_pushstring(L, pArchive->get_sound_name(iIndex)); + return 1; } -int l_soundarc_duration(lua_State *L) -{ - sound_archive* pArchive = luaT_testuserdata(L); - size_t iIndex = l_soundarc_checkidx(L, 2, pArchive); - if(iIndex == pArchive->get_number_of_sounds()) - return 2; - size_t iDuration = pArchive->get_sound_duration(iIndex); - lua_pushnumber(L, static_cast(iDuration) / static_cast(1000)); - return 1; +int l_soundarc_duration(lua_State* L) { + sound_archive* pArchive = luaT_testuserdata(L); + size_t iIndex = l_soundarc_checkidx(L, 2, pArchive); + if (iIndex == pArchive->get_number_of_sounds()) return 2; + size_t iDuration = pArchive->get_sound_duration(iIndex); + lua_pushnumber( + L, static_cast(iDuration) / static_cast(1000)); + return 1; } -int l_soundarc_data(lua_State *L) -{ - sound_archive* pArchive = luaT_testuserdata(L); - size_t iIndex = l_soundarc_checkidx(L, 2, pArchive); - if(iIndex == pArchive->get_number_of_sounds()) - return 2; - SDL_RWops *pRWops = pArchive->load_sound(iIndex); - if(!pRWops) - return 0; - size_t iLength = SDL_RWseek(pRWops, 0, SEEK_END); - SDL_RWseek(pRWops, 0, SEEK_SET); - // There is a potential leak of pRWops if either of these Lua calls cause - // a memory error, but it isn't very likely, and this a debugging function - // anyway, so it isn't very important. - void *pBuffer = lua_newuserdata(L, iLength); - lua_pushlstring(L, (const char*)pBuffer, - SDL_RWread(pRWops, pBuffer, 1, iLength)); - SDL_RWclose(pRWops); - return 1; +int l_soundarc_data(lua_State* L) { + sound_archive* pArchive = luaT_testuserdata(L); + size_t iIndex = l_soundarc_checkidx(L, 2, pArchive); + if (iIndex == pArchive->get_number_of_sounds()) return 2; + SDL_RWops* pRWops = pArchive->load_sound(iIndex); + if (!pRWops) return 0; + size_t iLength = SDL_RWseek(pRWops, 0, SEEK_END); + SDL_RWseek(pRWops, 0, SEEK_SET); + // There is a potential leak of pRWops if either of these Lua calls cause + // a memory error, but it isn't very likely, and this a debugging function + // anyway, so it isn't very important. + void* pBuffer = lua_newuserdata(L, iLength); + lua_pushlstring(L, (const char*)pBuffer, + SDL_RWread(pRWops, pBuffer, 1, iLength)); + SDL_RWclose(pRWops); + return 1; } -int l_soundarc_sound_exists(lua_State *L) -{ - sound_archive* pArchive = luaT_testuserdata(L); - size_t iIndex = l_soundarc_checkidx(L, 2, pArchive); - if(iIndex == pArchive->get_number_of_sounds()) - lua_pushboolean(L, 0); - else - lua_pushboolean(L, 1); - return 1; -} - -int l_soundfx_new(lua_State *L) -{ - luaT_stdnew(L, luaT_environindex, true); - return 1; -} - -int l_soundfx_set_archive(lua_State *L) -{ - sound_player *pEffects = luaT_testuserdata(L); - sound_archive *pArchive = luaT_testuserdata(L, 2); - pEffects->populate_from(pArchive); - lua_settop(L, 2); - luaT_setenvfield(L, 1, "archive"); - return 1; -} - -int l_soundfx_set_sound_volume(lua_State *L) -{ - sound_player *pEffects = luaT_testuserdata(L); - pEffects->set_sound_effect_volume(luaL_checknumber(L, 2)); - return 1; -} - -int l_soundfx_set_sound_effects_on(lua_State *L) -{ - sound_player *pEffects = luaT_testuserdata(L); - pEffects->set_sound_effects_enabled(lua_toboolean(L, 2) != 0); - return 1; -} - -Uint32 played_sound_callback(Uint32 interval, void* param) -{ - SDL_Event e; - e.type = SDL_USEREVENT_SOUND_OVER; - e.user.data1 = param; - int iSoundID = *(static_cast(param)); - SDL_RemoveTimer(map_sound_timers[iSoundID]); - map_sound_timers.erase(iSoundID); - SDL_PushEvent(&e); - - return interval; -} - -int l_soundfx_play(lua_State *L) -{ - sound_player *pEffects = luaT_testuserdata(L); - lua_settop(L, 7); - lua_getfenv(L, 1); - lua_pushliteral(L, "archive"); - lua_rawget(L,8); - sound_archive *pArchive = (sound_archive*)lua_touserdata(L, 9); - if(pArchive == nullptr) - { - return 0; - } - // l_soundarc_checkidx requires the archive at the bottom of the stack - lua_replace(L, 1); - size_t iIndex = l_soundarc_checkidx(L, 2, pArchive); - if(iIndex == pArchive->get_number_of_sounds()) - return 2; - if(lua_isnil(L, 4)) - { - pEffects->play(iIndex, luaL_checknumber(L, 3)); - } - else - { - pEffects->play_at(iIndex, luaL_checknumber(L, 3), static_cast(luaL_checkinteger(L, 4)), static_cast(luaL_checkinteger(L, 5))); - } - //SDL SOUND_OVER Callback Timer: - //6: unusedPlayedCallbackID - if(!lua_isnil(L, 6)) - { - //7: Callback delay - int iPlayedCallbackDelay = 0; //ms - if(!lua_isnil(L, 7)) - iPlayedCallbackDelay = static_cast(luaL_checknumber(L, 7)); - - if(played_sound_callback_index == sizeof(played_sound_callback_ids)) - played_sound_callback_index = 0; - - played_sound_callback_ids[played_sound_callback_index] = static_cast(luaL_checkinteger(L, 6)); - size_t interval = pArchive->get_sound_duration(iIndex) + iPlayedCallbackDelay; - SDL_TimerID timersID = SDL_AddTimer(static_cast(interval), - played_sound_callback, - &(played_sound_callback_ids[played_sound_callback_index])); - map_sound_timers.insert(std::pair(played_sound_callback_ids[played_sound_callback_index], timersID)); - played_sound_callback_index++; - } - +int l_soundarc_sound_exists(lua_State* L) { + sound_archive* pArchive = luaT_testuserdata(L); + size_t iIndex = l_soundarc_checkidx(L, 2, pArchive); + if (iIndex == pArchive->get_number_of_sounds()) + lua_pushboolean(L, 0); + else lua_pushboolean(L, 1); - return 1; + return 1; } -int l_soundfx_set_camera(lua_State *L) -{ - sound_player *pEffects = luaT_testuserdata(L); - pEffects->set_camera(static_cast(luaL_checkinteger(L, 2)), static_cast(luaL_checkinteger(L, 3)), static_cast(luaL_checkinteger(L, 4))); +int l_soundfx_new(lua_State* L) { + luaT_stdnew(L, luaT_environindex, true); + return 1; +} + +int l_soundfx_set_archive(lua_State* L) { + sound_player* pEffects = luaT_testuserdata(L); + sound_archive* pArchive = luaT_testuserdata(L, 2); + pEffects->populate_from(pArchive); + lua_settop(L, 2); + luaT_setenvfield(L, 1, "archive"); + return 1; +} + +int l_soundfx_set_sound_volume(lua_State* L) { + sound_player* pEffects = luaT_testuserdata(L); + pEffects->set_sound_effect_volume(luaL_checknumber(L, 2)); + return 1; +} + +int l_soundfx_set_sound_effects_on(lua_State* L) { + sound_player* pEffects = luaT_testuserdata(L); + pEffects->set_sound_effects_enabled(lua_toboolean(L, 2) != 0); + return 1; +} + +Uint32 played_sound_callback(Uint32 interval, void* param) { + SDL_Event e; + e.type = SDL_USEREVENT_SOUND_OVER; + e.user.data1 = param; + int iSoundID = *(static_cast(param)); + SDL_RemoveTimer(map_sound_timers[iSoundID]); + map_sound_timers.erase(iSoundID); + SDL_PushEvent(&e); + + return interval; +} + +int l_soundfx_play(lua_State* L) { + sound_player* pEffects = luaT_testuserdata(L); + lua_settop(L, 7); + lua_getfenv(L, 1); + lua_pushliteral(L, "archive"); + lua_rawget(L, 8); + sound_archive* pArchive = (sound_archive*)lua_touserdata(L, 9); + if (pArchive == nullptr) { return 0; + } + // l_soundarc_checkidx requires the archive at the bottom of the stack + lua_replace(L, 1); + size_t iIndex = l_soundarc_checkidx(L, 2, pArchive); + if (iIndex == pArchive->get_number_of_sounds()) return 2; + if (lua_isnil(L, 4)) { + pEffects->play(iIndex, luaL_checknumber(L, 3)); + } else { + pEffects->play_at(iIndex, luaL_checknumber(L, 3), + static_cast(luaL_checkinteger(L, 4)), + static_cast(luaL_checkinteger(L, 5))); + } + // SDL SOUND_OVER Callback Timer: + // 6: unusedPlayedCallbackID + if (!lua_isnil(L, 6)) { + // 7: Callback delay + int iPlayedCallbackDelay = 0; // ms + if (!lua_isnil(L, 7)) + iPlayedCallbackDelay = static_cast(luaL_checknumber(L, 7)); + + if (played_sound_callback_index == sizeof(played_sound_callback_ids)) + played_sound_callback_index = 0; + + played_sound_callback_ids[played_sound_callback_index] = + static_cast(luaL_checkinteger(L, 6)); + size_t interval = + pArchive->get_sound_duration(iIndex) + iPlayedCallbackDelay; + SDL_TimerID timersID = + SDL_AddTimer(static_cast(interval), played_sound_callback, + &(played_sound_callback_ids[played_sound_callback_index])); + map_sound_timers.insert(std::pair( + played_sound_callback_ids[played_sound_callback_index], timersID)); + played_sound_callback_index++; + } + + lua_pushboolean(L, 1); + return 1; } -int l_soundfx_reserve_channel(lua_State *L) -{ - int iChannel; - sound_player *pEffects = luaT_testuserdata(L); - iChannel = pEffects->reserve_channel(); - lua_pushinteger(L, iChannel); - return 1; +int l_soundfx_set_camera(lua_State* L) { + sound_player* pEffects = luaT_testuserdata(L); + pEffects->set_camera(static_cast(luaL_checkinteger(L, 2)), + static_cast(luaL_checkinteger(L, 3)), + static_cast(luaL_checkinteger(L, 4))); + return 0; } -int l_soundfx_release_channel(lua_State *L) -{ - sound_player *pEffects = luaT_testuserdata(L); - pEffects->release_channel(static_cast(luaL_checkinteger(L, 2))); - return 1; +int l_soundfx_reserve_channel(lua_State* L) { + int iChannel; + sound_player* pEffects = luaT_testuserdata(L); + iChannel = pEffects->reserve_channel(); + lua_pushinteger(L, iChannel); + return 1; } -} // namespace - -void lua_register_sound(const lua_register_state *pState) -{ - // Sound Archive - { - lua_class_binding lcb(pState, "soundArchive", l_soundarc_new, lua_metatable::sound_archive); - lcb.add_metamethod(l_soundarc_count, "len"); - lcb.add_function(l_soundarc_load, "load"); - lcb.add_function(l_soundarc_sound_name, "getFilename"); // Bad name, doesn't represent a file - lcb.add_function(l_soundarc_duration, "getDuration"); - lcb.add_function(l_soundarc_data, "getFileData"); // Bad name, doesn't represent a file - lcb.add_function(l_soundarc_sound_exists, "soundExists"); - } - - // Sound Effects - { - lua_class_binding lcb(pState, "soundEffects", l_soundfx_new, lua_metatable::sound_fx); - lcb.add_function(l_soundfx_set_archive, "setSoundArchive", lua_metatable::sound_archive); - lcb.add_function(l_soundfx_play, "play"); - lcb.add_function(l_soundfx_set_sound_volume, "setSoundVolume"); - lcb.add_function(l_soundfx_set_sound_effects_on, "setSoundEffectsOn"); - lcb.add_function(l_soundfx_set_camera, "setCamera"); - lcb.add_function(l_soundfx_reserve_channel, "reserveChannel"); - lcb.add_function(l_soundfx_release_channel, "releaseChannel"); - } +int l_soundfx_release_channel(lua_State* L) { + sound_player* pEffects = luaT_testuserdata(L); + pEffects->release_channel(static_cast(luaL_checkinteger(L, 2))); + return 1; +} + +} // namespace + +void lua_register_sound(const lua_register_state* pState) { + // Sound Archive + { + lua_class_binding lcb(pState, "soundArchive", l_soundarc_new, + lua_metatable::sound_archive); + lcb.add_metamethod(l_soundarc_count, "len"); + lcb.add_function(l_soundarc_load, "load"); + lcb.add_function(l_soundarc_sound_name, + "getFilename"); // Bad name, doesn't represent a file + lcb.add_function(l_soundarc_duration, "getDuration"); + lcb.add_function(l_soundarc_data, + "getFileData"); // Bad name, doesn't represent a file + lcb.add_function(l_soundarc_sound_exists, "soundExists"); + } + + // Sound Effects + { + lua_class_binding lcb(pState, "soundEffects", l_soundfx_new, + lua_metatable::sound_fx); + lcb.add_function(l_soundfx_set_archive, "setSoundArchive", + lua_metatable::sound_archive); + lcb.add_function(l_soundfx_play, "play"); + lcb.add_function(l_soundfx_set_sound_volume, "setSoundVolume"); + lcb.add_function(l_soundfx_set_sound_effects_on, "setSoundEffectsOn"); + lcb.add_function(l_soundfx_set_camera, "setCamera"); + lcb.add_function(l_soundfx_reserve_channel, "reserveChannel"); + lcb.add_function(l_soundfx_release_channel, "releaseChannel"); + } } diff --git a/CorsixTH/Src/th_lua_strings.cpp b/CorsixTH/Src/th_lua_strings.cpp index e950c478..fefccfbd 100644 --- a/CorsixTH/Src/th_lua_strings.cpp +++ b/CorsixTH/Src/th_lua_strings.cpp @@ -20,9 +20,9 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -#include "th_lua_internal.h" -#include "persist_lua.h" #include +#include "persist_lua.h" +#include "th_lua_internal.h" /* This file implements a string proxy system. A string proxy is a userdata @@ -59,367 +59,333 @@ namespace { // which we get by having 2 bytes of dummy global variables. uint8_t weak_table_keys[2] = {0}; -inline void aux_push_weak_table(lua_State *L, int iIndex) -{ - lua_pushlightuserdata(L, &weak_table_keys[iIndex]); - lua_rawget(L, LUA_REGISTRYINDEX); +inline void aux_push_weak_table(lua_State* L, int iIndex) { + lua_pushlightuserdata(L, &weak_table_keys[iIndex]); + lua_rawget(L, LUA_REGISTRYINDEX); } // Replace the value at the top of the stack with a userdata proxy -int l_str_new_aux(lua_State *L) -{ - luaT_stdnew(L); - aux_push_weak_table(L, 0); - lua_pushvalue(L, -2); - lua_pushvalue(L, -4); - lua_rawset(L, -3); - lua_pop(L, 1); - lua_replace(L, -2); - return 1; +int l_str_new_aux(lua_State* L) { + luaT_stdnew(L); + aux_push_weak_table(L, 0); + lua_pushvalue(L, -2); + lua_pushvalue(L, -4); + lua_rawset(L, -3); + lua_pop(L, 1); + lua_replace(L, -2); + return 1; } // Create a new root-level userdata proxy -int l_str_new(lua_State *L) -{ - // Pack extra arguments into a table - int iNArgs = lua_gettop(L); - lua_createtable(L, iNArgs - 2, 0); - lua_replace(L, 1); // Value inserted by __call - for(int i = iNArgs; i >= 3; --i) - lua_rawseti(L, 1, i - 2); +int l_str_new(lua_State* L) { + // Pack extra arguments into a table + int iNArgs = lua_gettop(L); + lua_createtable(L, iNArgs - 2, 0); + lua_replace(L, 1); // Value inserted by __call + for (int i = iNArgs; i >= 3; --i) lua_rawseti(L, 1, i - 2); - // Make proxy - luaL_checkany(L, 2); - l_str_new_aux(L); + // Make proxy + luaL_checkany(L, 2); + l_str_new_aux(L); - // Save extra arguments as reconstruction information - lua_insert(L, 1); - lua_setfenv(L, 1); - return 1; + // Save extra arguments as reconstruction information + lua_insert(L, 1); + lua_setfenv(L, 1); + return 1; } // Helper function to make an array in Lua -void aux_mk_table(lua_State *L, int nliterals, int nvalues, ...) -{ - lua_createtable(L, nliterals + nvalues, 0); - va_list args; - va_start(args, nvalues); - for(int i = 1; i <= nliterals; ++i) - { - const char *sStr = va_arg(args, const char*); - lua_pushstring(L, sStr); - lua_rawseti(L, -2, i); - } - for(int i = nliterals + 1; i <= nliterals + nvalues; ++i) - { - int iValue = va_arg(args, int); - if(0 > iValue && iValue > LUA_REGISTRYINDEX) - --iValue; - lua_pushvalue(L, iValue); - lua_rawseti(L, -2, i); - } - va_end(args); +void aux_mk_table(lua_State* L, int nliterals, int nvalues, ...) { + lua_createtable(L, nliterals + nvalues, 0); + va_list args; + va_start(args, nvalues); + for (int i = 1; i <= nliterals; ++i) { + const char* sStr = va_arg(args, const char*); + lua_pushstring(L, sStr); + lua_rawseti(L, -2, i); + } + for (int i = nliterals + 1; i <= nliterals + nvalues; ++i) { + int iValue = va_arg(args, int); + if (0 > iValue && iValue > LUA_REGISTRYINDEX) --iValue; + lua_pushvalue(L, iValue); + lua_rawseti(L, -2, i); + } + va_end(args); } // Helper function which pushes onto the stack a random key from the table // (previously) on the top of the stack. -void aux_push_random_key(lua_State *L) -{ - int iNKeys = 0; - lua_newtable(L); - lua_getglobal(L, "pairs"); +void aux_push_random_key(lua_State* L) { + int iNKeys = 0; + lua_newtable(L); + lua_getglobal(L, "pairs"); + lua_pushvalue(L, -3); + lua_call(L, 1, 3); + while (true) { lua_pushvalue(L, -3); - lua_call(L, 1, 3); - while(true) - { - lua_pushvalue(L, -3); - lua_pushvalue(L, -3); - lua_pushvalue(L, -3); - lua_remove(L, -4); - lua_call(L, 2, 1); - if(lua_isnil(L, -1)) - break; - ++iNKeys; - lua_pushvalue(L, -1); - lua_rawseti(L, -5, iNKeys); - } - lua_pop(L, 3); - lua_getglobal(L, "math"); - lua_getfield(L, -1, "random"); - lua_pushinteger(L, 1); - lua_pushinteger(L, iNKeys); + lua_pushvalue(L, -3); + lua_pushvalue(L, -3); + lua_remove(L, -4); lua_call(L, 2, 1); - lua_gettable(L, -3); - lua_replace(L, -3); - lua_pop(L, 1); + if (lua_isnil(L, -1)) break; + ++iNKeys; + lua_pushvalue(L, -1); + lua_rawseti(L, -5, iNKeys); + } + lua_pop(L, 3); + lua_getglobal(L, "math"); + lua_getfield(L, -1, "random"); + lua_pushinteger(L, 1); + lua_pushinteger(L, iNKeys); + lua_call(L, 2, 1); + lua_gettable(L, -3); + lua_replace(L, -3); + lua_pop(L, 1); } // __index metamethod handler. // For proxied tables, return proxies of children, preferably cached // For proxied strings, return methods -int l_str_index(lua_State *L) -{ - // Look up cached value, and return it if present - aux_push_weak_table(L, 1); - lua_pushvalue(L, 1); - lua_gettable(L, 3); - lua_replace(L, 3); +int l_str_index(lua_State* L) { + // Look up cached value, and return it if present + aux_push_weak_table(L, 1); + lua_pushvalue(L, 1); + lua_gettable(L, 3); + lua_replace(L, 3); + lua_pushvalue(L, 2); + lua_rawget(L, 3); + if (!lua_isnil(L, 4)) return 1; + lua_pop(L, 1); + + // Fetch the proxied value + aux_push_weak_table(L, 0); + lua_pushvalue(L, 1); + lua_rawget(L, 4); + lua_replace(L, 4); + + // Handle string methods + if (lua_type(L, 4) == LUA_TSTRING) { + lua_rawgeti(L, luaT_environindex, 4); lua_pushvalue(L, 2); - lua_rawget(L, 3); - if(!lua_isnil(L, 4)) - return 1; - lua_pop(L, 1); - - // Fetch the proxied value - aux_push_weak_table(L, 0); - lua_pushvalue(L, 1); - lua_rawget(L, 4); - lua_replace(L, 4); - - // Handle string methods - if(lua_type(L, 4) == LUA_TSTRING) - { - lua_rawgeti(L, luaT_environindex, 4); - lua_pushvalue(L, 2); - lua_gettable(L, 5); - return 1; - } - - // Handle __random, as it shouldn't be cached - if(lua_type(L, 2) == LUA_TSTRING) - { - size_t iLen; - const char* sKey = lua_tolstring(L, 2, &iLen); - if(iLen == 8 && std::strcmp(sKey, "__random") == 0) - { - aux_push_random_key(L); - lua_replace(L, 2); - lua_settop(L, 2); - return l_str_index(L); - } - } - - // Fetch desired value - lua_pushvalue(L, 2); - lua_gettable(L, 4); - lua_replace(L, 4); - - // Create new userdata proxy - l_str_new_aux(L); - aux_mk_table(L, 0, 2, 1, 2); - lua_setfenv(L, 4); - - // Save to cache and return - lua_pushvalue(L, 2); - lua_pushvalue(L, 4); - lua_rawset(L, 3); + lua_gettable(L, 5); return 1; + } + + // Handle __random, as it shouldn't be cached + if (lua_type(L, 2) == LUA_TSTRING) { + size_t iLen; + const char* sKey = lua_tolstring(L, 2, &iLen); + if (iLen == 8 && std::strcmp(sKey, "__random") == 0) { + aux_push_random_key(L); + lua_replace(L, 2); + lua_settop(L, 2); + return l_str_index(L); + } + } + + // Fetch desired value + lua_pushvalue(L, 2); + lua_gettable(L, 4); + lua_replace(L, 4); + + // Create new userdata proxy + l_str_new_aux(L); + aux_mk_table(L, 0, 2, 1, 2); + lua_setfenv(L, 4); + + // Save to cache and return + lua_pushvalue(L, 2); + lua_pushvalue(L, 4); + lua_rawset(L, 3); + return 1; } // __newindex metamethod handler -int l_str_newindex(lua_State *L) -{ - return luaL_error(L, "String tables are read-only"); +int l_str_newindex(lua_State* L) { + return luaL_error(L, "String tables are read-only"); } // Generic string method handler // The name of the method is stored at upvalue 1 -int l_str_func(lua_State *L) -{ - int iArgCount = lua_gettop(L); - lua_checkstack(L, iArgCount + 10); +int l_str_func(lua_State* L) { + int iArgCount = lua_gettop(L); + lua_checkstack(L, iArgCount + 10); - int iUserdataCount = 0; + int iUserdataCount = 0; - // Construct the resulting value - aux_push_weak_table(L, 0); - for(int i = 1; i <= iArgCount; ++i) - { - lua_pushvalue(L, i); - if(lua_type(L, i) == LUA_TUSERDATA) - { - lua_rawget(L, iArgCount + 1); - ++iUserdataCount; - } + // Construct the resulting value + aux_push_weak_table(L, 0); + for (int i = 1; i <= iArgCount; ++i) { + lua_pushvalue(L, i); + if (lua_type(L, i) == LUA_TUSERDATA) { + lua_rawget(L, iArgCount + 1); + ++iUserdataCount; } - lua_pushvalue(L, luaT_upvalueindex(1)); - lua_gettable(L, iArgCount + 2); - lua_replace(L, iArgCount + 1); - lua_call(L, iArgCount, 1); + } + lua_pushvalue(L, luaT_upvalueindex(1)); + lua_gettable(L, iArgCount + 2); + lua_replace(L, iArgCount + 1); + lua_call(L, iArgCount, 1); - // Trivial case of result not depending upon any proxies - if(iUserdataCount == 0) - return 1; + // Trivial case of result not depending upon any proxies + if (iUserdataCount == 0) return 1; - // Wrap result in a proxy - l_str_new_aux(L); + // Wrap result in a proxy + l_str_new_aux(L); - // Create and save reconstruction information - lua_createtable(L, iArgCount + 1, 0); - lua_pushvalue(L, luaT_upvalueindex(1)); - lua_rawseti(L, -2, 1); - for(int i = 1; i <= iArgCount; ++i) - { - lua_pushvalue(L, i); - lua_rawseti(L, -2, i + 1); - } - lua_setfenv(L, -2); + // Create and save reconstruction information + lua_createtable(L, iArgCount + 1, 0); + lua_pushvalue(L, luaT_upvalueindex(1)); + lua_rawseti(L, -2, 1); + for (int i = 1; i <= iArgCount; ++i) { + lua_pushvalue(L, i); + lua_rawseti(L, -2, i + 1); + } + lua_setfenv(L, -2); - return 1; + return 1; } // __concat metamethod handler // Simple (but inefficient) handling by converting concat into format -int l_str_concat(lua_State *L) -{ - int iParent = (lua_type(L, 1) == LUA_TUSERDATA) ? 1 : 2; - lua_getfield(L, iParent, "format"); - lua_insert(L, 1); - lua_pushliteral(L, "%s%s"); - lua_insert(L, 2); - lua_call(L, 3, 1); - return 1; +int l_str_concat(lua_State* L) { + int iParent = (lua_type(L, 1) == LUA_TUSERDATA) ? 1 : 2; + lua_getfield(L, iParent, "format"); + lua_insert(L, 1); + lua_pushliteral(L, "%s%s"); + lua_insert(L, 2); + lua_call(L, 3, 1); + return 1; } // pairs() metamethod handler -int l_str_pairs(lua_State *L) -{ - lua_settop(L, 1); - lua_getfield(L, luaT_environindex, "__next"); - lua_pushvalue(L, 1); - lua_pushnil(L); - return 3; +int l_str_pairs(lua_State* L) { + lua_settop(L, 1); + lua_getfield(L, luaT_environindex, "__next"); + lua_pushvalue(L, 1); + lua_pushnil(L); + return 3; } // ipairs() metamethod handler -int l_str_ipairs(lua_State *L) -{ - lua_settop(L, 1); - lua_getfield(L, luaT_environindex, "__inext"); - lua_pushvalue(L, 1); - lua_pushinteger(L, 0); - return 3; +int l_str_ipairs(lua_State* L) { + lua_settop(L, 1); + lua_getfield(L, luaT_environindex, "__inext"); + lua_pushvalue(L, 1); + lua_pushinteger(L, 0); + return 3; } // pairs() iterator function -int l_str_next(lua_State *L) -{ - luaL_checktype(L, 1, LUA_TUSERDATA); - lua_settop(L, 2); +int l_str_next(lua_State* L) { + luaL_checktype(L, 1, LUA_TUSERDATA); + lua_settop(L, 2); - // Fetch proxied value - aux_push_weak_table(L, 0); - lua_pushvalue(L, 1); - lua_rawget(L, 3); + // Fetch proxied value + aux_push_weak_table(L, 0); + lua_pushvalue(L, 1); + lua_rawget(L, 3); - // Remove layer of proxying done in Lua - // NB: Assumes that pairs(t) returns 3 values: next, t, nil - // In our case, the returned t should be the raw (unproxied) one - lua_getglobal(L, "pairs"); - lua_replace(L, 3); - lua_call(L, 1, 2); + // Remove layer of proxying done in Lua + // NB: Assumes that pairs(t) returns 3 values: next, t, nil + // In our case, the returned t should be the raw (unproxied) one + lua_getglobal(L, "pairs"); + lua_replace(L, 3); + lua_call(L, 1, 2); - // Get the next key - lua_pushvalue(L, 2); - lua_call(L, 2, 1); - if(lua_isnil(L, -1)) - return 0; + // Get the next key + lua_pushvalue(L, 2); + lua_call(L, 2, 1); + if (lua_isnil(L, -1)) return 0; - // Get the (proxied) value which goes with the key - lua_pushvalue(L, -1); - lua_gettable(L, 1); - return 2; + // Get the (proxied) value which goes with the key + lua_pushvalue(L, -1); + lua_gettable(L, 1); + return 2; } // __len metamethod handler -int l_str_len(lua_State *L) -{ - luaL_checktype(L, 1, LUA_TUSERDATA); +int l_str_len(lua_State* L) { + luaL_checktype(L, 1, LUA_TUSERDATA); - // Fetch proxied value - aux_push_weak_table(L, 0); - lua_pushvalue(L, 1); - lua_gettable(L, -2); + // Fetch proxied value + aux_push_weak_table(L, 0); + lua_pushvalue(L, 1); + lua_gettable(L, -2); - // String tables are proxied in Lua, and Lua tables do not honour __len - // so use ipairs to get the unproxied table to call __len on. - if(lua_type(L, -1) == LUA_TTABLE) - { - lua_getglobal(L, "ipairs"); - lua_insert(L, -2); - lua_call(L, 1, 2); - lua_replace(L, -2); - } + // String tables are proxied in Lua, and Lua tables do not honour __len + // so use ipairs to get the unproxied table to call __len on. + if (lua_type(L, -1) == LUA_TTABLE) { + lua_getglobal(L, "ipairs"); + lua_insert(L, -2); + lua_call(L, 1, 2); + lua_replace(L, -2); + } - lua_pushinteger(L, (lua_Integer)lua_objlen(L, -1)); - return 1; + lua_pushinteger(L, (lua_Integer)lua_objlen(L, -1)); + return 1; } // ipairs() iterator function -int l_str_inext(lua_State *L) -{ - lua_Integer n = luaL_checkinteger(L, 2) + 1; - lua_settop(L, 1); - l_str_len(L); - lua_Integer len = lua_tointeger(L, -1); +int l_str_inext(lua_State* L) { + lua_Integer n = luaL_checkinteger(L, 2) + 1; + lua_settop(L, 1); + l_str_len(L); + lua_Integer len = lua_tointeger(L, -1); - if(n > len) - return 0; + if (n > len) return 0; - // Fetch proxied value - lua_settop(L, 2); - lua_pushvalue(L, 1); - lua_gettable(L, 2); + // Fetch proxied value + lua_settop(L, 2); + lua_pushvalue(L, 1); + lua_gettable(L, 2); - // Return new N and the proxied value which goes it - lua_pushinteger(L, n); - lua_pushinteger(L, n); - lua_gettable(L, 1); - return 2; + // Return new N and the proxied value which goes it + lua_pushinteger(L, n); + lua_pushinteger(L, n); + lua_gettable(L, 1); + return 2; } // tostring() metamethod handler for debugging / diagnostics -int l_str_tostring(lua_State *L) -{ - // Convert the proxy to a string, recursively calling tostring() - lua_settop(L, 1); - aux_push_weak_table(L, 0); - lua_pushvalue(L, 1); - lua_rawget(L, 2); - if(lua_isnil(L, 3)) - lua_pop(L, 2); - else - { - lua_replace(L, 1); - lua_pop(L, 1); - } - lua_getglobal(L, "tostring"); - lua_insert(L, 1); - lua_call(L, 1, 1); +int l_str_tostring(lua_State* L) { + // Convert the proxy to a string, recursively calling tostring() + lua_settop(L, 1); + aux_push_weak_table(L, 0); + lua_pushvalue(L, 1); + lua_rawget(L, 2); + if (lua_isnil(L, 3)) + lua_pop(L, 2); + else { + lua_replace(L, 1); + lua_pop(L, 1); + } + lua_getglobal(L, "tostring"); + lua_insert(L, 1); + lua_call(L, 1, 1); - // Prepend a nice message indicating that proxying is being done - lua_pushliteral(L, " Current value:"); - lua_insert(L, 1); - lua_concat(L, 2); - return 1; + // Prepend a nice message indicating that proxying is being done + lua_pushliteral(L, " Current value:"); + lua_insert(L, 1); + lua_concat(L, 2); + return 1; } // __call metamethod handler // Required to support the compatibility hack for calling _S -int l_str_call(lua_State *L) -{ - luaL_checkany(L, 1); +int l_str_call(lua_State* L) { + luaL_checkany(L, 1); - // Fetch the proxied value - aux_push_weak_table(L, 0); - lua_pushvalue(L, 1); - lua_rawget(L, -2); + // Fetch the proxied value + aux_push_weak_table(L, 0); + lua_pushvalue(L, 1); + lua_rawget(L, -2); - // Forward the call onto the proxied value - lua_replace(L, 1); - lua_pop(L, 1); - lua_call(L, lua_gettop(L) - 1, LUA_MULTRET); - return lua_gettop(L); + // Forward the call onto the proxied value + lua_replace(L, 1); + lua_pop(L, 1); + lua_call(L, lua_gettop(L) - 1, LUA_MULTRET); + return lua_gettop(L); } // __lt (less-than) metamethod handler @@ -427,333 +393,295 @@ int l_str_call(lua_State *L) // to create nice user-interface listing of strings. Note that this will mean // that persist->change language->depersist will result in a "random" ordering // of the resulting list, but this is generally acceptable. -int l_str_lt(lua_State *L) -{ - luaL_checkany(L, 1); - luaL_checkany(L, 2); - lua_settop(L, 2); - aux_push_weak_table(L, 0); - lua_pushvalue(L, 1); - lua_rawget(L, 3); - lua_pushvalue(L, 2); - lua_rawget(L, 3); - lua_pushboolean(L, lua_lessthan(L, 4, 5)); - return 1; +int l_str_lt(lua_State* L) { + luaL_checkany(L, 1); + luaL_checkany(L, 2); + lua_settop(L, 2); + aux_push_weak_table(L, 0); + lua_pushvalue(L, 1); + lua_rawget(L, 3); + lua_pushvalue(L, 2); + lua_rawget(L, 3); + lua_pushboolean(L, lua_lessthan(L, 4, 5)); + return 1; } // __persist metamethod handler -int l_str_persist(lua_State *L) -{ - lua_settop(L, 2); - lua_insert(L, 1); - lua_persist_writer *pWriter = (lua_persist_writer*)lua_touserdata(L, 1); +int l_str_persist(lua_State* L) { + lua_settop(L, 2); + lua_insert(L, 1); + lua_persist_writer* pWriter = (lua_persist_writer*)lua_touserdata(L, 1); - // Recreation instructions are stored in the environment, which is written - // automatically. For compatibility, we write a simple boolean. - lua_pushboolean(L, 1); - pWriter->write_stack_object(3); - lua_getfenv(L, 2); + // Recreation instructions are stored in the environment, which is written + // automatically. For compatibility, we write a simple boolean. + lua_pushboolean(L, 1); + pWriter->write_stack_object(3); + lua_getfenv(L, 2); - // If there were no instructions (i.e. for the root object) then write the - // value as well. - if(lua_objlen(L, -1) == 0) - { - lua_pop(L, 2); - aux_push_weak_table(L, 0); - lua_pushvalue(L, 2); - lua_rawget(L, 3); - pWriter->write_stack_object(4); - } - return 0; + // If there were no instructions (i.e. for the root object) then write the + // value as well. + if (lua_objlen(L, -1) == 0) { + lua_pop(L, 2); + aux_push_weak_table(L, 0); + lua_pushvalue(L, 2); + lua_rawget(L, 3); + pWriter->write_stack_object(4); + } + return 0; } // __depersist metamethod handler -int l_str_depersist(lua_State *L) -{ - lua_settop(L, 2); - lua_insert(L, 1); - lua_persist_reader *pReader = (lua_persist_reader*)lua_touserdata(L, 1); +int l_str_depersist(lua_State* L) { + lua_settop(L, 2); + lua_insert(L, 1); + lua_persist_reader* pReader = (lua_persist_reader*)lua_touserdata(L, 1); - // Read the instructions for re-creating the value - if(!pReader->read_stack_object()) - return 0; - if(lua_type(L, 3) == LUA_TBOOLEAN && lua_toboolean(L, 3) == 1) - { - // The current code uses a boolean marker to indicate that the - // instructions were stored in the environment. Replace the marker - // with them. - lua_getfenv(L, 2); - lua_replace(L, 3); - } - else - { - // Older versions of the code wrote the instructions here, or nil for - // no instructions. Convert nil to the empty table, and store the - // instructions as the userdata's environment. - if(lua_type(L, 3) == LUA_TNIL) - { - lua_newtable(L); - lua_replace(L, 3); - } - lua_pushvalue(L, 3); - lua_setfenv(L, 2); - } - - // Prepare t, k for saving the value - aux_push_weak_table(L, 0); - lua_pushvalue(L, 2); - - if(lua_objlen(L, 3) == 0) - { - // No instructions provided, so read the value itself - if(!pReader->read_stack_object()) - return 0; - } - else - { - // The instructions are a table of values; unpack them and replace - // proxies with their values. - bool bIsIndexOperation = false; - int iCount = (int)lua_objlen(L, 3); - lua_checkstack(L, iCount + 1); - for(int i = 1; i <= iCount; ++i) - { - lua_rawgeti(L, 3, i); - if(lua_type(L, -1) == LUA_TUSERDATA) - { - if(i == 1) - bIsIndexOperation = true; - lua_rawget(L, 4); - } - } - - if(iCount == 2 && bIsIndexOperation) - { - // If there were two values, and the first was a proxy, then the - // instruction is to perform a table lookup. - lua_gettable(L, -2); - lua_replace(L, -2); - } - else - { - // Otherwise, the first value was a method or method name. - if(lua_type(L, 6) != LUA_TFUNCTION) - { - lua_pushvalue(L, 6); - lua_gettable(L, 7); - lua_replace(L, 6); - } - lua_call(L, iCount - 1, 1); - } - } - - // Save the value - lua_rawset(L, 4); - return 0; -} - -int l_str_reload_actual(lua_State *L) -{ - // Reload a single string proxy - // Stack: reload_cache proxy_to_reload read_stack_object()) return 0; + if (lua_type(L, 3) == LUA_TBOOLEAN && lua_toboolean(L, 3) == 1) { + // The current code uses a boolean marker to indicate that the + // instructions were stored in the environment. Replace the marker + // with them. lua_getfenv(L, 2); + lua_replace(L, 3); + } else { + // Older versions of the code wrote the instructions here, or nil for + // no instructions. Convert nil to the empty table, and store the + // instructions as the userdata's environment. + if (lua_type(L, 3) == LUA_TNIL) { + lua_newtable(L); + lua_replace(L, 3); + } + lua_pushvalue(L, 3); + lua_setfenv(L, 2); + } + + // Prepare t, k for saving the value + aux_push_weak_table(L, 0); + lua_pushvalue(L, 2); + + if (lua_objlen(L, 3) == 0) { + // No instructions provided, so read the value itself + if (!pReader->read_stack_object()) return 0; + } else { + // The instructions are a table of values; unpack them and replace + // proxies with their values. bool bIsIndexOperation = false; int iCount = (int)lua_objlen(L, 3); - aux_push_weak_table(L, 0); - lua_pushvalue(L, 2); - if(iCount != 0) - { - // Fetch reconstruction information, reloading any de-proxying any - // string proxies which we come across. Also replace any references - // to the root with the new root. - lua_checkstack(L, iCount + 1); - for(int i = 1; i <= iCount; ++i) - { - lua_rawgeti(L, 3, i); - if(lua_type(L, -1) == LUA_TUSERDATA) - { - if(i == 1) - bIsIndexOperation = true; - lua_gettable(L, 1); // reload / change root - lua_pushvalue(L, -1); - lua_rawseti(L, 3, i); - lua_rawget(L, 4); // de-proxy - } - } + lua_checkstack(L, iCount + 1); + for (int i = 1; i <= iCount; ++i) { + lua_rawgeti(L, 3, i); + if (lua_type(L, -1) == LUA_TUSERDATA) { + if (i == 1) bIsIndexOperation = true; + lua_rawget(L, 4); + } + } - if(iCount == 2 && bIsIndexOperation) - { - // If there were two values, and the first was a proxy, then the - // instruction is to perform a table lookup. - lua_gettable(L, -2); - lua_replace(L, -2); - } - else - { - // Otherwise, the first value was a method or method name. - if(lua_type(L, 6) != LUA_TFUNCTION) - { - lua_pushvalue(L, 6); - lua_gettable(L, 7); - lua_replace(L, 6); - } - lua_call(L, iCount - 1, 1); - } + if (iCount == 2 && bIsIndexOperation) { + // If there were two values, and the first was a proxy, then the + // instruction is to perform a table lookup. + lua_gettable(L, -2); + lua_replace(L, -2); + } else { + // Otherwise, the first value was a method or method name. + if (lua_type(L, 6) != LUA_TFUNCTION) { + lua_pushvalue(L, 6); + lua_gettable(L, 7); + lua_replace(L, 6); + } + lua_call(L, iCount - 1, 1); } - else - { - // Root object - lua_settop(L, 2); - return 1; - } - // Update value - lua_rawset(L, -3); - lua_pop(L, 2); - return 1; + } + + // Save the value + lua_rawset(L, 4); + return 0; } -int l_str_unwrap(lua_State *L) -{ - luaL_checkany(L, 1); +int l_str_reload_actual(lua_State* L) { + // Reload a single string proxy + // Stack: reload_cache proxy_to_reload idx && idx > LUA_REGISTRYINDEX); + lua_pushvalue(L, bRel ? (idx - 1) : idx); + lua_rawget(L, -2); + lua_replace(L, bRel ? (idx - 2) : idx); + lua_pop(L, 1); + } + return luaL_checklstring(L, idx, pLength); +} + +void lua_register_strings(const lua_register_state* pState) { + lua_State* L = pState->L; + + // Create Value, and Cache weak tables for inside-out objects. + for (int i = 0; i <= 1; ++i) { + lua_pushlightuserdata(L, &weak_table_keys[i]); lua_newtable(L); - lua_insert(L, 1); - lua_settop(L, 3); - lua_rawset(L, 1); lua_createtable(L, 0, 1); - lua_pushcfunction(L, l_str_reload_actual); - lua_setfield(L, -2, "__index"); - lua_setmetatable(L, -2); - - aux_push_weak_table(L, 0); - luaL_loadstring(L, - "local reload, all_proxies, _ = ...\n" - // Make a copy of all_proxies which isn't a weak table - "local proxies_copy = {}\n" - "for k, v in pairs(all_proxies) do proxies_copy[k] = v end\n" - // Do the reloading - "for k in pairs(proxies_copy) do _ = reload[k] end\n" - ); - lua_insert(L, 1); - lua_call(L, 2, 0); - - return 0; -} - -int l_mk_cache(lua_State *L) -{ - lua_newtable(L); - lua_pushvalue(L, luaT_upvalueindex(1)); - lua_setmetatable(L, -2); - lua_pushvalue(L, 2); - lua_pushvalue(L, 3); - lua_settable(L, 1); - return 1; -} - -} // namespace - -const char* luaT_checkstring(lua_State *L, int idx, size_t* pLength) -{ - if(lua_isuserdata(L, idx)) - { - aux_push_weak_table(L, 0); - bool bRel = (0 > idx && idx > LUA_REGISTRYINDEX); - lua_pushvalue(L, bRel ? (idx - 1) : idx); - lua_rawget(L, -2); - lua_replace(L, bRel ? (idx - 2) : idx); - lua_pop(L, 1); + lua_pushliteral(L, "__mode"); + lua_pushliteral(L, "k"); + lua_rawset(L, -3); + if (i == 1) { + // Have the cache weak table automatically create caches on demand + lua_pushliteral(L, "__index"); + lua_createtable(L, 0, 1); + lua_pushliteral(L, "__mode"); + lua_pushliteral(L, "kv"); + lua_rawset(L, -3); + luaT_pushcclosure(L, l_mk_cache, 1); + lua_rawset(L, -3); } - return luaL_checklstring(L, idx, pLength); -} - -void lua_register_strings(const lua_register_state *pState) -{ - lua_State *L = pState->L; - - // Create Value, and Cache weak tables for inside-out objects. - for(int i = 0; i <= 1; ++i) - { - lua_pushlightuserdata(L, &weak_table_keys[i]); - lua_newtable(L); - lua_createtable(L, 0, 1); - lua_pushliteral(L, "__mode"); - lua_pushliteral(L, "k"); - lua_rawset(L, -3); - if(i == 1) - { - // Have the cache weak table automatically create caches on demand - lua_pushliteral(L, "__index"); - lua_createtable(L, 0, 1); - lua_pushliteral(L, "__mode"); - lua_pushliteral(L, "kv"); - lua_rawset(L, -3); - luaT_pushcclosure(L, l_mk_cache, 1); - lua_rawset(L, -3); - } - lua_setmetatable(L, -2); - lua_rawset(L, LUA_REGISTRYINDEX); - } - // Give the Value weak table a friendly name for Lua code to use - lua_pushliteral(L, "StringProxyValues"); - lua_pushlightuserdata(L, &weak_table_keys[0]); - lua_rawget(L, LUA_REGISTRYINDEX); + lua_setmetatable(L, -2); lua_rawset(L, LUA_REGISTRYINDEX); + } + // Give the Value weak table a friendly name for Lua code to use + lua_pushliteral(L, "StringProxyValues"); + lua_pushlightuserdata(L, &weak_table_keys[0]); + lua_rawget(L, LUA_REGISTRYINDEX); + lua_rawset(L, LUA_REGISTRYINDEX); - lua_class_binding lcb(pState, "stringProxy", l_str_new, lua_metatable::string_proxy); - // As we overwrite __index, move methods to lua_metatable::string_proxy[4] - lua_getfield(L, pState->metatables[static_cast(lua_metatable::string_proxy)], "__index"); - lua_rawseti(L, pState->metatables[static_cast(lua_metatable::string_proxy)], 4); - lcb.add_metamethod(l_str_index, "index"); - lcb.add_metamethod(l_str_newindex, "newindex"); - lcb.add_metamethod(l_str_concat, "concat"); - lcb.add_metamethod(l_str_len, "len"); - lcb.add_metamethod(l_str_tostring, "tostring"); - lcb.add_metamethod(l_str_persist, "persist"); - lcb.add_metamethod(l_str_depersist, "depersist"); - lcb.add_metamethod(l_str_call, "call"); - lcb.add_metamethod(l_str_lt, "lt"); - lcb.add_metamethod(l_str_pairs, "pairs"); - lcb.add_metamethod(l_str_ipairs, "ipairs"); - lcb.add_metamethod(l_str_next, "next"); - lcb.add_metamethod(l_str_inext, "inext"); - lcb.add_function(l_str_func, "format", "format"); - lcb.add_function(l_str_func, "lower", "lower"); - lcb.add_function(l_str_func, "rep", "rep"); - lcb.add_function(l_str_func, "reverse", "reverse"); - lcb.add_function(l_str_func, "upper", "upper"); - lcb.add_function(l_str_unwrap, "_unwrap"); - lcb.add_function(l_str_reload, "reload"); + lua_class_binding lcb(pState, "stringProxy", l_str_new, + lua_metatable::string_proxy); + // As we overwrite __index, move methods to lua_metatable::string_proxy[4] + lua_getfield( + L, pState->metatables[static_cast(lua_metatable::string_proxy)], + "__index"); + lua_rawseti( + L, pState->metatables[static_cast(lua_metatable::string_proxy)], + 4); + lcb.add_metamethod(l_str_index, "index"); + lcb.add_metamethod(l_str_newindex, "newindex"); + lcb.add_metamethod(l_str_concat, "concat"); + lcb.add_metamethod(l_str_len, "len"); + lcb.add_metamethod(l_str_tostring, "tostring"); + lcb.add_metamethod(l_str_persist, "persist"); + lcb.add_metamethod(l_str_depersist, "depersist"); + lcb.add_metamethod(l_str_call, "call"); + lcb.add_metamethod(l_str_lt, "lt"); + lcb.add_metamethod(l_str_pairs, "pairs"); + lcb.add_metamethod(l_str_ipairs, "ipairs"); + lcb.add_metamethod(l_str_next, "next"); + lcb.add_metamethod(l_str_inext, "inext"); + lcb.add_function(l_str_func, "format", "format"); + lcb.add_function(l_str_func, "lower", "lower"); + lcb.add_function(l_str_func, "rep", "rep"); + lcb.add_function(l_str_func, "reverse", "reverse"); + lcb.add_function(l_str_func, "upper", "upper"); + lcb.add_function(l_str_unwrap, "_unwrap"); + lcb.add_function(l_str_reload, "reload"); } diff --git a/CorsixTH/Src/th_lua_ui.cpp b/CorsixTH/Src/th_lua_ui.cpp index 2c4f2d20..445e79e3 100644 --- a/CorsixTH/Src/th_lua_ui.cpp +++ b/CorsixTH/Src/th_lua_ui.cpp @@ -20,156 +20,152 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -#include "th_lua_internal.h" -#include "th_gfx.h" -#include "th_map.h" #include +#include "th_gfx.h" +#include "th_lua_internal.h" +#include "th_map.h" class abstract_window {}; namespace { -int l_abstract_window_new(lua_State *L) -{ - return luaL_error(L, "windowBase can only be used a base class - " - " do not create a windowBase directly."); +int l_abstract_window_new(lua_State* L) { + return luaL_error(L, + "windowBase can only be used a base class - " + " do not create a windowBase directly."); } -uint8_t range_scale(uint16_t low, uint16_t high, uint16_t val, uint16_t start, uint16_t end) -{ - return static_cast(std::max(start + (end - start) * (val - low) / (high - low), 0xFF)); +uint8_t range_scale(uint16_t low, uint16_t high, uint16_t val, uint16_t start, + uint16_t end) { + return static_cast( + std::max(start + (end - start) * (val - low) / (high - low), 0xFF)); } -inline bool is_wall(uint16_t blk) -{ - return ((82 <= ((blk) & 0xFF)) && (((blk) & 0xFF) <= 164)); +inline bool is_wall(uint16_t blk) { + return ((82 <= ((blk)&0xFF)) && (((blk)&0xFF) <= 164)); } -inline bool is_wall_drawn(const level_map &map, const map_tile &node, const map_tile &original_node, size_t n) -{ - return map.get_tile_owner(&node) != 0 ? is_wall(node.iBlock[n]) : is_wall(original_node.iBlock[n]); +inline bool is_wall_drawn(const level_map& map, const map_tile& node, + const map_tile& original_node, size_t n) { + return map.get_tile_owner(&node) != 0 ? is_wall(node.iBlock[n]) + : is_wall(original_node.iBlock[n]); } -int l_town_map_draw(lua_State *L) -{ - luaL_checktype(L, 1, LUA_TTABLE); - level_map* pMap = luaT_testuserdata(L, 2); - render_target *pCanvas = luaT_testuserdata(L, 3); - int iCanvasXBase = static_cast(luaL_checkinteger(L, 4)); - int iCanvasYBase = static_cast(luaL_checkinteger(L, 5)); - bool bShowHeat = lua_toboolean(L, 6) != 0; +int l_town_map_draw(lua_State* L) { + luaL_checktype(L, 1, LUA_TTABLE); + level_map* pMap = luaT_testuserdata(L, 2); + render_target* pCanvas = luaT_testuserdata(L, 3); + int iCanvasXBase = static_cast(luaL_checkinteger(L, 4)); + int iCanvasYBase = static_cast(luaL_checkinteger(L, 5)); + bool bShowHeat = lua_toboolean(L, 6) != 0; - uint32_t iColourMyHosp = render_target::map_colour(0, 0, 70); - uint32_t iColourWall = render_target::map_colour(255, 255, 255); - uint32_t iColourDoor = render_target::map_colour(200, 200, 200); - uint32_t iColourPurchasable = render_target::map_colour(255, 0, 0); + uint32_t iColourMyHosp = render_target::map_colour(0, 0, 70); + uint32_t iColourWall = render_target::map_colour(255, 255, 255); + uint32_t iColourDoor = render_target::map_colour(200, 200, 200); + uint32_t iColourPurchasable = render_target::map_colour(255, 0, 0); - const map_tile *pNode = pMap->get_tile_unchecked(0, 0); - const map_tile *pOriginalNode = pMap->get_original_tile_unchecked(0, 0); - int iCanvasY = iCanvasYBase + 3; - int iMapWidth = pMap->get_width(); - for(int iY = 0; iY < pMap->get_height(); ++iY, iCanvasY += 3) - { - int iCanvasX = iCanvasXBase; - for(int iX = 0; iX < iMapWidth; ++iX, ++pNode, ++pOriginalNode, iCanvasX += 3) - { - if(pOriginalNode->flags.hospital) - { - uint32_t iColour = iColourMyHosp; - if(!(pNode->flags.hospital)) - { - // TODO: Replace 1 with player number - if(pMap->is_parcel_purchasable(pNode->iParcelId, 1)) - iColour = iColourPurchasable; - else - goto dont_paint_tile; - } - else if(bShowHeat) - { - uint16_t iTemp = pMap->get_tile_temperature(pNode); - if(iTemp < 5200) // Less than 4 degrees - iTemp = 0; - else if(iTemp > 32767) // More than 25 degrees - iTemp = 255; - else // NB: 108 == (32767 - 5200) / 255 - iTemp = static_cast((iTemp - 5200) / 108); + const map_tile* pNode = pMap->get_tile_unchecked(0, 0); + const map_tile* pOriginalNode = pMap->get_original_tile_unchecked(0, 0); + int iCanvasY = iCanvasYBase + 3; + int iMapWidth = pMap->get_width(); + for (int iY = 0; iY < pMap->get_height(); ++iY, iCanvasY += 3) { + int iCanvasX = iCanvasXBase; + for (int iX = 0; iX < iMapWidth; + ++iX, ++pNode, ++pOriginalNode, iCanvasX += 3) { + if (pOriginalNode->flags.hospital) { + uint32_t iColour = iColourMyHosp; + if (!(pNode->flags.hospital)) { + // TODO: Replace 1 with player number + if (pMap->is_parcel_purchasable(pNode->iParcelId, 1)) + iColour = iColourPurchasable; + else + goto dont_paint_tile; + } else if (bShowHeat) { + uint16_t iTemp = pMap->get_tile_temperature(pNode); + if (iTemp < 5200) // Less than 4 degrees + iTemp = 0; + else if (iTemp > 32767) // More than 25 degrees + iTemp = 255; + else // NB: 108 == (32767 - 5200) / 255 + iTemp = static_cast((iTemp - 5200) / 108); - const uint16_t minOkTemp = 140; - const uint16_t maxOkTemp = 180; + const uint16_t minOkTemp = 140; + const uint16_t maxOkTemp = 180; - uint8_t iR = 0; - uint8_t iG = 0; - uint8_t iB = 0; - switch(pMap->get_temperature_display()) - { - case temperature_theme::multi_colour: - iB = 70; - if(iTemp < minOkTemp) { - iB = range_scale(0, minOkTemp - 1, iTemp, 200, 60); - } else if(iTemp < maxOkTemp) { - iG = range_scale(minOkTemp, maxOkTemp - 1, iTemp, 140, 224); - } else { - iR = range_scale(maxOkTemp, 255, iTemp, 224, 255); - } - break; - case temperature_theme::yellow_red: - if(iTemp < minOkTemp) { // Below 11 degrees - iR = range_scale(0, minOkTemp - 1, iTemp, 100, 213); - iG = range_scale(0, minOkTemp - 1, iTemp, 80, 180); - } else { - iR = range_scale(minOkTemp, 255, iTemp, 223, 235); - iG = range_scale(minOkTemp, 255, iTemp, 184, 104); - iB = range_scale(minOkTemp, 255, iTemp, 0, 53); - } - break; - case temperature_theme::red: - iR = static_cast(iTemp); - iB = 70; - break; - } + uint8_t iR = 0; + uint8_t iG = 0; + uint8_t iB = 0; + switch (pMap->get_temperature_display()) { + case temperature_theme::multi_colour: + iB = 70; + if (iTemp < minOkTemp) { + iB = range_scale(0, minOkTemp - 1, iTemp, 200, 60); + } else if (iTemp < maxOkTemp) { + iG = range_scale(minOkTemp, maxOkTemp - 1, iTemp, 140, 224); + } else { + iR = range_scale(maxOkTemp, 255, iTemp, 224, 255); + } + break; + case temperature_theme::yellow_red: + if (iTemp < minOkTemp) { // Below 11 degrees + iR = range_scale(0, minOkTemp - 1, iTemp, 100, 213); + iG = range_scale(0, minOkTemp - 1, iTemp, 80, 180); + } else { + iR = range_scale(minOkTemp, 255, iTemp, 223, 235); + iG = range_scale(minOkTemp, 255, iTemp, 184, 104); + iB = range_scale(minOkTemp, 255, iTemp, 0, 53); + } + break; + case temperature_theme::red: + iR = static_cast(iTemp); + iB = 70; + break; + } - iColour = render_target::map_colour(iR, iG, iB); - } - pCanvas->fill_rect(iColour, iCanvasX, iCanvasY, 3, 3); - } - dont_paint_tile: - if(is_wall_drawn(*pMap, *pNode, *pOriginalNode, 1)) { - pCanvas->fill_rect(iColourWall, iCanvasX, iCanvasY, 3, 1); - - // Draw entrance door - auto l = (pNode - 1)->objects; - if(!l.empty() && l.front() == object_type::entrance_right_door) { - if (pNode->flags.hospital) { - pCanvas->fill_rect(iColourDoor, iCanvasX-6, iCanvasY-2, 9, 3); - } else { - pCanvas->fill_rect(iColourDoor, iCanvasX-6, iCanvasY, 9, 3); - } - } - } - if(is_wall_drawn(*pMap, *pNode, *pOriginalNode, 2)) { - pCanvas->fill_rect(iColourWall, iCanvasX, iCanvasY, 1, 3); - - // Draw entrance door - auto l = (pNode - iMapWidth)->objects; - if(!l.empty() && l.front() == object_type::entrance_right_door) { - if (pNode->flags.hospital) { - pCanvas->fill_rect(iColourDoor, iCanvasX-2, iCanvasY-6, 3, 9); - } else { - pCanvas->fill_rect(iColourDoor, iCanvasX, iCanvasY-6, 3, 9); - } - } - } + iColour = render_target::map_colour(iR, iG, iB); } + pCanvas->fill_rect(iColour, iCanvasX, iCanvasY, 3, 3); + } + dont_paint_tile: + if (is_wall_drawn(*pMap, *pNode, *pOriginalNode, 1)) { + pCanvas->fill_rect(iColourWall, iCanvasX, iCanvasY, 3, 1); + + // Draw entrance door + auto l = (pNode - 1)->objects; + if (!l.empty() && l.front() == object_type::entrance_right_door) { + if (pNode->flags.hospital) { + pCanvas->fill_rect(iColourDoor, iCanvasX - 6, iCanvasY - 2, 9, 3); + } else { + pCanvas->fill_rect(iColourDoor, iCanvasX - 6, iCanvasY, 9, 3); + } + } + } + if (is_wall_drawn(*pMap, *pNode, *pOriginalNode, 2)) { + pCanvas->fill_rect(iColourWall, iCanvasX, iCanvasY, 1, 3); + + // Draw entrance door + auto l = (pNode - iMapWidth)->objects; + if (!l.empty() && l.front() == object_type::entrance_right_door) { + if (pNode->flags.hospital) { + pCanvas->fill_rect(iColourDoor, iCanvasX - 2, iCanvasY - 6, 3, 9); + } else { + pCanvas->fill_rect(iColourDoor, iCanvasX, iCanvasY - 6, 3, 9); + } + } + } } + } - return 0; + return 0; } -} // namespace +} // namespace -void lua_register_ui(const lua_register_state *pState) -{ - // WindowBase - lua_class_binding lcb(pState, "windowHelpers", l_abstract_window_new, lua_metatable::window_base); - lcb.add_function(l_town_map_draw, "townMapDraw", lua_metatable::map, lua_metatable::surface); +void lua_register_ui(const lua_register_state* pState) { + // WindowBase + lua_class_binding lcb(pState, "windowHelpers", + l_abstract_window_new, + lua_metatable::window_base); + lcb.add_function(l_town_map_draw, "townMapDraw", lua_metatable::map, + lua_metatable::surface); } diff --git a/CorsixTH/Src/th_map.cpp b/CorsixTH/Src/th_map.cpp index e27a1288..ec78b193 100644 --- a/CorsixTH/Src/th_map.cpp +++ b/CorsixTH/Src/th_map.cpp @@ -20,262 +20,300 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -#include "config.h" #include "th_map.h" -#include "th_map_overlays.h" -#include "th_gfx.h" -#include "run_length_encoder.h" +#include "config.h" #include -#include #include -#include #include -#include -#include +#include #include +#include +#include +#include +#include "run_length_encoder.h" +#include "th.h" +#include "th_gfx.h" +#include "th_map_overlays.h" -map_tile_flags& map_tile_flags::operator=(uint32_t raw) -{ - passable = (raw & static_cast(map_tile_flags::key::passable_mask)) != 0; - can_travel_n = (raw & static_cast(map_tile_flags::key::can_travel_n_mask)) != 0; - can_travel_e = (raw & static_cast(map_tile_flags::key::can_travel_e_mask)) != 0; - can_travel_s = (raw & static_cast(map_tile_flags::key::can_travel_s_mask)) != 0; - can_travel_w = (raw & static_cast(map_tile_flags::key::can_travel_w_mask)) != 0; - hospital = (raw & static_cast(map_tile_flags::key::hospital_mask)) != 0; - buildable = (raw & static_cast(map_tile_flags::key::buildable_mask)) != 0; - passable_if_not_for_blueprint = (raw & static_cast(map_tile_flags::key::passable_if_not_for_blueprint_mask)) != 0; - room = (raw & static_cast(map_tile_flags::key::room_mask)) != 0; - shadow_half = (raw & static_cast(map_tile_flags::key::shadow_half_mask)) != 0; - shadow_full = (raw & static_cast(map_tile_flags::key::shadow_full_mask)) != 0; - shadow_wall = (raw & static_cast(map_tile_flags::key::shadow_wall_mask)) != 0; - door_north = (raw & static_cast(map_tile_flags::key::door_north_mask)) != 0; - door_west = (raw & static_cast(map_tile_flags::key::door_west_mask)) != 0; - do_not_idle = (raw & static_cast(map_tile_flags::key::do_not_idle_mask)) != 0; - tall_north = (raw & static_cast(map_tile_flags::key::tall_north_mask)) != 0; - tall_west = (raw & static_cast(map_tile_flags::key::tall_west_mask)) != 0; - buildable_n = (raw & static_cast(map_tile_flags::key::buildable_n_mask)) != 0; - buildable_e = (raw & static_cast(map_tile_flags::key::buildable_e_mask)) != 0; - buildable_s = (raw & static_cast(map_tile_flags::key::buildable_s_mask)) != 0; - buildable_w = (raw & static_cast(map_tile_flags::key::buildable_w_mask)) != 0; +constexpr size_t max_player_count = 4; - return *this; +map_tile_flags& map_tile_flags::operator=(uint32_t raw) { + using flags = map_tile_flags::key; + + passable = (raw & static_cast(flags::passable_mask)) != 0; + can_travel_n = (raw & static_cast(flags::can_travel_n_mask)) != 0; + can_travel_e = (raw & static_cast(flags::can_travel_e_mask)) != 0; + can_travel_s = (raw & static_cast(flags::can_travel_s_mask)) != 0; + can_travel_w = (raw & static_cast(flags::can_travel_w_mask)) != 0; + hospital = (raw & static_cast(flags::hospital_mask)) != 0; + buildable = (raw & static_cast(flags::buildable_mask)) != 0; + passable_if_not_for_blueprint = + (raw & + static_cast(flags::passable_if_not_for_blueprint_mask)) != 0; + room = (raw & static_cast(flags::room_mask)) != 0; + shadow_half = (raw & static_cast(flags::shadow_half_mask)) != 0; + shadow_full = (raw & static_cast(flags::shadow_full_mask)) != 0; + shadow_wall = (raw & static_cast(flags::shadow_wall_mask)) != 0; + door_north = (raw & static_cast(flags::door_north_mask)) != 0; + door_west = (raw & static_cast(flags::door_west_mask)) != 0; + do_not_idle = (raw & static_cast(flags::do_not_idle_mask)) != 0; + tall_north = (raw & static_cast(flags::tall_north_mask)) != 0; + tall_west = (raw & static_cast(flags::tall_west_mask)) != 0; + buildable_n = (raw & static_cast(flags::buildable_n_mask)) != 0; + buildable_e = (raw & static_cast(flags::buildable_e_mask)) != 0; + buildable_s = (raw & static_cast(flags::buildable_s_mask)) != 0; + buildable_w = (raw & static_cast(flags::buildable_w_mask)) != 0; + + return *this; } -bool& map_tile_flags::operator[](map_tile_flags::key key) -{ - switch(key) - { - case map_tile_flags::key::passable_mask: - return passable; - case map_tile_flags::key::can_travel_n_mask: - return can_travel_n; - case map_tile_flags::key::can_travel_e_mask: - return can_travel_e; - case map_tile_flags::key::can_travel_s_mask: - return can_travel_s; - case map_tile_flags::key::can_travel_w_mask: - return can_travel_w; - case map_tile_flags::key::hospital_mask: - return hospital; - case map_tile_flags::key::buildable_mask: - return buildable; - case map_tile_flags::key::passable_if_not_for_blueprint_mask: - return passable_if_not_for_blueprint; - case map_tile_flags::key::room_mask: - return room; - case map_tile_flags::key::shadow_half_mask: - return shadow_half; - case map_tile_flags::key::shadow_full_mask: - return shadow_full; - case map_tile_flags::key::shadow_wall_mask: - return shadow_wall; - case map_tile_flags::key::door_north_mask: - return door_north; - case map_tile_flags::key::door_west_mask: - return door_west; - case map_tile_flags::key::do_not_idle_mask: - return do_not_idle; - case map_tile_flags::key::tall_north_mask: - return tall_north; - case map_tile_flags::key::tall_west_mask: - return tall_west; - case map_tile_flags::key::buildable_n_mask: - return buildable_n; - case map_tile_flags::key::buildable_e_mask: - return buildable_e; - case map_tile_flags::key::buildable_s_mask: - return buildable_s; - case map_tile_flags::key::buildable_w_mask: - return buildable_w; +bool& map_tile_flags::operator[](map_tile_flags::key key) { + using flags = map_tile_flags::key; + + switch (key) { + case flags::passable_mask: + return passable; + case flags::can_travel_n_mask: + return can_travel_n; + case flags::can_travel_e_mask: + return can_travel_e; + case flags::can_travel_s_mask: + return can_travel_s; + case flags::can_travel_w_mask: + return can_travel_w; + case flags::hospital_mask: + return hospital; + case flags::buildable_mask: + return buildable; + case flags::passable_if_not_for_blueprint_mask: + return passable_if_not_for_blueprint; + case flags::room_mask: + return room; + case flags::shadow_half_mask: + return shadow_half; + case flags::shadow_full_mask: + return shadow_full; + case flags::shadow_wall_mask: + return shadow_wall; + case flags::door_north_mask: + return door_north; + case flags::door_west_mask: + return door_west; + case flags::do_not_idle_mask: + return do_not_idle; + case flags::tall_north_mask: + return tall_north; + case flags::tall_west_mask: + return tall_west; + case flags::buildable_n_mask: + return buildable_n; + case flags::buildable_e_mask: + return buildable_e; + case flags::buildable_s_mask: + return buildable_s; + case flags::buildable_w_mask: + return buildable_w; default: - throw std::out_of_range("map tile flag is invalid"); - } + throw std::out_of_range("map tile flag is invalid"); + } } -const bool& map_tile_flags::operator[](map_tile_flags::key key) const -{ - switch(key) - { - case map_tile_flags::key::passable_mask: - return passable; - case map_tile_flags::key::can_travel_n_mask: - return can_travel_n; - case map_tile_flags::key::can_travel_e_mask: - return can_travel_e; - case map_tile_flags::key::can_travel_s_mask: - return can_travel_s; - case map_tile_flags::key::can_travel_w_mask: - return can_travel_w; - case map_tile_flags::key::hospital_mask: - return hospital; - case map_tile_flags::key::buildable_mask: - return buildable; - case map_tile_flags::key::passable_if_not_for_blueprint_mask: - return passable_if_not_for_blueprint; - case map_tile_flags::key::room_mask: - return room; - case map_tile_flags::key::shadow_half_mask: - return shadow_half; - case map_tile_flags::key::shadow_full_mask: - return shadow_full; - case map_tile_flags::key::shadow_wall_mask: - return shadow_wall; - case map_tile_flags::key::door_north_mask: - return door_north; - case map_tile_flags::key::door_west_mask: - return door_west; - case map_tile_flags::key::do_not_idle_mask: - return do_not_idle; - case map_tile_flags::key::tall_north_mask: - return tall_north; - case map_tile_flags::key::tall_west_mask: - return tall_west; - case map_tile_flags::key::buildable_n_mask: - return buildable_n; - case map_tile_flags::key::buildable_e_mask: - return buildable_e; - case map_tile_flags::key::buildable_s_mask: - return buildable_s; - case map_tile_flags::key::buildable_w_mask: - return buildable_w; +const bool& map_tile_flags::operator[](map_tile_flags::key key) const { + using flags = map_tile_flags::key; + + switch (key) { + case flags::passable_mask: + return passable; + case flags::can_travel_n_mask: + return can_travel_n; + case flags::can_travel_e_mask: + return can_travel_e; + case flags::can_travel_s_mask: + return can_travel_s; + case flags::can_travel_w_mask: + return can_travel_w; + case flags::hospital_mask: + return hospital; + case flags::buildable_mask: + return buildable; + case flags::passable_if_not_for_blueprint_mask: + return passable_if_not_for_blueprint; + case flags::room_mask: + return room; + case flags::shadow_half_mask: + return shadow_half; + case flags::shadow_full_mask: + return shadow_full; + case flags::shadow_wall_mask: + return shadow_wall; + case flags::door_north_mask: + return door_north; + case flags::door_west_mask: + return door_west; + case flags::do_not_idle_mask: + return do_not_idle; + case flags::tall_north_mask: + return tall_north; + case flags::tall_west_mask: + return tall_west; + case flags::buildable_n_mask: + return buildable_n; + case flags::buildable_e_mask: + return buildable_e; + case flags::buildable_s_mask: + return buildable_s; + case flags::buildable_w_mask: + return buildable_w; default: - throw std::out_of_range("map tile flag is invalid"); - } + throw std::out_of_range("map tile flag is invalid"); + } } -map_tile_flags::operator uint32_t() const -{ - uint32_t raw = 0; - if(passable) { raw |= static_cast(map_tile_flags::key::passable_mask); } - if(can_travel_n) { raw |= static_cast(map_tile_flags::key::can_travel_n_mask); } - if(can_travel_e) { raw |= static_cast(map_tile_flags::key::can_travel_e_mask); } - if(can_travel_s) { raw |= static_cast(map_tile_flags::key::can_travel_s_mask); } - if(can_travel_w) { raw |= static_cast(map_tile_flags::key::can_travel_w_mask); } - if(hospital) { raw |= static_cast(map_tile_flags::key::hospital_mask); } - if(buildable) { raw |= static_cast(map_tile_flags::key::buildable_mask); } - if(passable_if_not_for_blueprint) { raw |= static_cast(map_tile_flags::key::passable_if_not_for_blueprint_mask); } - if(room) { raw |= static_cast(map_tile_flags::key::room_mask); } - if(shadow_half) { raw |= static_cast(map_tile_flags::key::shadow_half_mask); } - if(shadow_full) { raw |= static_cast(map_tile_flags::key::shadow_full_mask); } - if(shadow_wall) { raw |= static_cast(map_tile_flags::key::shadow_wall_mask); } - if(door_north) { raw |= static_cast(map_tile_flags::key::door_north_mask); } - if(door_west) { raw |= static_cast(map_tile_flags::key::door_west_mask); } - if(do_not_idle) { raw |= static_cast(map_tile_flags::key::do_not_idle_mask); } - if(tall_north) { raw |= static_cast(map_tile_flags::key::tall_north_mask); } - if(tall_west) { raw |= static_cast(map_tile_flags::key::tall_west_mask); } - if(buildable_n) { raw |= static_cast(map_tile_flags::key::buildable_n_mask); } - if(buildable_e) { raw |= static_cast(map_tile_flags::key::buildable_e_mask); } - if(buildable_s) { raw |= static_cast(map_tile_flags::key::buildable_s_mask); } - if(buildable_w) { raw |= static_cast(map_tile_flags::key::buildable_w_mask); } +map_tile_flags::operator uint32_t() const { + using flags = map_tile_flags::key; - return raw; + uint32_t raw = 0; + if (passable) { + raw |= static_cast(flags::passable_mask); + } + if (can_travel_n) { + raw |= static_cast(flags::can_travel_n_mask); + } + if (can_travel_e) { + raw |= static_cast(flags::can_travel_e_mask); + } + if (can_travel_s) { + raw |= static_cast(flags::can_travel_s_mask); + } + if (can_travel_w) { + raw |= static_cast(flags::can_travel_w_mask); + } + if (hospital) { + raw |= static_cast(flags::hospital_mask); + } + if (buildable) { + raw |= static_cast(flags::buildable_mask); + } + if (passable_if_not_for_blueprint) { + raw |= static_cast(flags::passable_if_not_for_blueprint_mask); + } + if (room) { + raw |= static_cast(flags::room_mask); + } + if (shadow_half) { + raw |= static_cast(flags::shadow_half_mask); + } + if (shadow_full) { + raw |= static_cast(flags::shadow_full_mask); + } + if (shadow_wall) { + raw |= static_cast(flags::shadow_wall_mask); + } + if (door_north) { + raw |= static_cast(flags::door_north_mask); + } + if (door_west) { + raw |= static_cast(flags::door_west_mask); + } + if (do_not_idle) { + raw |= static_cast(flags::do_not_idle_mask); + } + if (tall_north) { + raw |= static_cast(flags::tall_north_mask); + } + if (tall_west) { + raw |= static_cast(flags::tall_west_mask); + } + if (buildable_n) { + raw |= static_cast(flags::buildable_n_mask); + } + if (buildable_e) { + raw |= static_cast(flags::buildable_e_mask); + } + if (buildable_s) { + raw |= static_cast(flags::buildable_s_mask); + } + if (buildable_w) { + raw |= static_cast(flags::buildable_w_mask); + } + + return raw; } -map_tile::map_tile() : - iParcelId(0), - iRoomId(0), - objects() -{ - iBlock[0] = 0; - iBlock[1] = 0; - iBlock[2] = 0; - iBlock[3] = 0; - aiTemperature[0] = aiTemperature[1] = 8192; - flags = {}; +map_tile::map_tile() : iParcelId(0), iRoomId(0), flags({}), objects() { + iBlock[0] = 0; + iBlock[1] = 0; + iBlock[2] = 0; + iBlock[3] = 0; + aiTemperature[0] = aiTemperature[1] = 8192; } -map_tile::~map_tile() -{ -} +map_tile::~map_tile() {} level_map::level_map() -{ + : cells(nullptr), + original_cells(nullptr), + blocks(nullptr), + overlay(nullptr), + owns_overlay(false), + plot_owner(nullptr), + width(0), + height(0), + player_count(0), + parcel_count(0), + current_temperature_index(0), + current_temperature_theme(temperature_theme::red), + parcel_tile_counts(nullptr), + parcel_adjacency_matrix(nullptr), + purchasable_matrix(nullptr) {} + +level_map::~level_map() { + set_overlay(nullptr, false); + delete[] cells; + delete[] original_cells; + delete[] plot_owner; + delete[] parcel_tile_counts; + delete[] parcel_adjacency_matrix; + delete[] purchasable_matrix; +} + +void level_map::set_overlay(map_overlay* pOverlay, bool bTakeOwnership) { + if (overlay && owns_overlay) { + delete overlay; + } + overlay = pOverlay; + owns_overlay = bTakeOwnership; +} + +bool level_map::set_size(int iWidth, int iHeight) { + if (iWidth <= 0 || iHeight <= 0) { + return false; + } + + delete[] cells; + delete[] original_cells; + delete[] parcel_adjacency_matrix; + delete[] purchasable_matrix; + width = iWidth; + height = iHeight; + cells = nullptr; + cells = new (std::nothrow) map_tile[iWidth * iHeight]; + original_cells = nullptr; + original_cells = new (std::nothrow) map_tile[iWidth * iHeight]; + parcel_adjacency_matrix = nullptr; + purchasable_matrix = nullptr; + + if (cells == nullptr || original_cells == nullptr) { + delete[] cells; + delete[] original_cells; + original_cells = nullptr; + cells = nullptr; width = 0; height = 0; - player_count = 0; - current_temperature_index = 0; - current_temperature_theme = temperature_theme::red; - parcel_count = 0; - cells = nullptr; - original_cells = nullptr; - blocks = nullptr; - overlay = nullptr; - owns_overlay = false; - plot_owner = nullptr; - parcel_tile_counts = nullptr; - parcel_adjacency_matrix = nullptr; - purchasable_matrix = nullptr; -} + return false; + } -level_map::~level_map() -{ - set_overlay(nullptr, false); - delete[] cells; - delete[] original_cells; - delete[] plot_owner; - delete[] parcel_tile_counts; - delete[] parcel_adjacency_matrix; - delete[] purchasable_matrix; -} - -void level_map::set_overlay(map_overlay *pOverlay, bool bTakeOwnership) -{ - if(overlay && owns_overlay) - delete overlay; - overlay = pOverlay; - owns_overlay = bTakeOwnership; -} - -bool level_map::set_size(int iWidth, int iHeight) -{ - if(iWidth <= 0 || iHeight <= 0) - return false; - - delete[] cells; - delete[] original_cells; - delete[] parcel_adjacency_matrix; - delete[] purchasable_matrix; - width = iWidth; - height = iHeight; - cells = nullptr; - cells = new (std::nothrow) map_tile[iWidth * iHeight]; - original_cells = nullptr; - original_cells = new (std::nothrow) map_tile[iWidth * iHeight]; - parcel_adjacency_matrix = nullptr; - purchasable_matrix = nullptr; - - if(cells == nullptr || original_cells == nullptr) - { - delete[] cells; - delete[] original_cells; - original_cells = nullptr; - cells = nullptr; - width = 0; - height = 0; - return false; - } - - return true; + return true; } namespace { @@ -304,1086 +342,1076 @@ constexpr uint8_t gs_iTHMapBlockLUT[256] = { 0xA1, 0xA2, 0xA3, 0xA4, 0xD7, 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF, 0xE0, 0xE1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00 -}; + 0x00, 0x00, 0x00, 0x00}; -} // namespace +} // namespace -void level_map::read_tile_index(const uint8_t* pData, int& iX, int &iY) const -{ - unsigned int iIndex = static_cast(pData[1]); - iIndex = iIndex * 0x100 + static_cast(pData[0]); - iX = iIndex % width; - iY = iIndex / width; +void level_map::read_tile_index(const uint8_t* pData, int& iX, int& iY) const { + unsigned int iIndex = static_cast(pData[1]); + iIndex = iIndex * 0x100 + static_cast(pData[0]); + iX = iIndex % width; + iY = iIndex / width; } -void level_map::write_tile_index(uint8_t* pData, int iX, int iY) const -{ - uint16_t iIndex = static_cast(iY * width + iX); - pData[0] = static_cast(iIndex & 0xFF); - pData[1] = static_cast(iIndex >> 8); +void level_map::write_tile_index(uint8_t* pData, int iX, int iY) const { + uint16_t iIndex = static_cast(iY * width + iX); + pData[0] = static_cast(iIndex & 0xFF); + pData[1] = static_cast(iIndex >> 8); } -bool level_map::load_blank() -{ - if(!set_size(128, 128)) - return false; +bool level_map::load_blank() { + if (!set_size(128, 128)) { + return false; + } - player_count = 1; - initial_camera_x[0] = initial_camera_y[0] = 63; - heliport_x[0] = heliport_y[0] = 0; - parcel_count = 1; - delete[] plot_owner; - delete[] parcel_tile_counts; - plot_owner = nullptr; - parcel_tile_counts = nullptr; - map_tile *pNode = cells; - map_tile *pOriginalNode = original_cells; - for(int iY = 0; iY < 128; ++iY) - { - for(int iX = 0; iX < 128; ++iX, ++pNode, ++pOriginalNode) - { - pNode->iBlock[0] = static_cast(2 + (iX % 2)); - } + player_count = 1; + initial_camera_x[0] = initial_camera_y[0] = 63; + heliport_x[0] = heliport_y[0] = 0; + parcel_count = 1; + delete[] plot_owner; + delete[] parcel_tile_counts; + plot_owner = nullptr; + parcel_tile_counts = nullptr; + map_tile* pNode = cells; + map_tile* pOriginalNode = original_cells; + for (int iY = 0; iY < height; ++iY) { + for (int iX = 0; iX < width; ++iX, ++pNode, ++pOriginalNode) { + pNode->iBlock[0] = static_cast(2 + (iX % 2)); } - plot_owner = new int[1]; - plot_owner[0] = 0; - parcel_tile_counts = new int[1]; - parcel_tile_counts[0] = 128 * 128; + } + plot_owner = new int[1]; + plot_owner[0] = 0; + parcel_tile_counts = new int[1]; + parcel_tile_counts[0] = height * width; - return true; + return true; } namespace { -inline bool is_divider_wall(const uint8_t byte) -{ - return (byte >> 1) == 70; -} +inline bool is_divider_wall(const uint8_t byte) { return (byte >> 1) == 70; } -} // namespace +} // namespace bool level_map::load_from_th_file(const uint8_t* pData, size_t iDataLength, - map_load_object_callback_fn fnObjectCallback, - void* pCallbackToken) -{ - if(iDataLength < 163948 || !set_size(128, 128)) - return false; + map_load_object_callback_fn fnObjectCallback, + void* pCallbackToken) { + const size_t camera_offset = 163876; + const size_t heliport_offset = 163884; + const size_t parcel_offset = 131106; - player_count = pData[0]; - for(int i = 0; i < player_count; ++i) - { - read_tile_index(pData + 163876 + (i % 4) * 2, - initial_camera_x[i], initial_camera_y[i]); - read_tile_index(pData + 163884 + (i % 4) * 2, - heliport_x[i], heliport_y[i]); - } - parcel_count = 0; - delete[] plot_owner; - delete[] parcel_tile_counts; - plot_owner = nullptr; - parcel_tile_counts = nullptr; + if (iDataLength < 163948 || !set_size(128, 128)) { + return false; + } - map_tile *pNode = cells; - map_tile *pOriginalNode = original_cells; - const uint16_t *pParcel = reinterpret_cast(pData + 131106); - pData += 34; + player_count = pData[0] % max_player_count; + for (int i = 0; i < player_count; ++i) { + read_tile_index(pData + camera_offset + (i * 2), initial_camera_x[i], + initial_camera_y[i]); + read_tile_index(pData + heliport_offset + (i * 2), heliport_x[i], + heliport_y[i]); + } + parcel_count = 0; + delete[] plot_owner; + delete[] parcel_tile_counts; + plot_owner = nullptr; + parcel_tile_counts = nullptr; - pNode->objects.clear(); - for(int iY = 0; iY < 128; ++iY) - { - for(int iX = 0; iX < 128; ++iX, ++pNode, ++pOriginalNode, pData += 8, ++pParcel) - { - uint8_t iBaseTile = gs_iTHMapBlockLUT[pData[2]]; - pNode->flags.can_travel_n = true; - pNode->flags.can_travel_e = true; - pNode->flags.can_travel_s = true; - pNode->flags.can_travel_w = true; - if(iX == 0) - pNode->flags.can_travel_w = false; - else if(iX == 127) - pNode->flags.can_travel_e = false; - if(iY == 0) - pNode->flags.can_travel_n = false; - else if(iY == 127) - pNode->flags.can_travel_s = false; - pNode->iBlock[0] = iBaseTile; - if(pData[3] == 0 || is_divider_wall(pData[3])) - { - // Tiles 71, 72 and 73 (pond foliage) are used as floor tiles, - // but are too tall to be floor tiles, so move them to a wall, - // and replace the floor with something similar (pond base). - if(71 <= iBaseTile && iBaseTile <= 73) - { - pNode->iBlock[1] = iBaseTile; - pNode->iBlock[0] = iBaseTile = 69; - } - else - pNode->iBlock[1] = 0; - } - else - { - pNode->iBlock[1] = gs_iTHMapBlockLUT[pData[3]]; - pNode->flags.can_travel_n = false; - if(iY != 0) - { - pNode[-128].flags.can_travel_s = false; - } - } - if(pData[4] == 0 || is_divider_wall(pData[4])) - pNode->iBlock[2] = 0; - else - { - pNode->iBlock[2] = gs_iTHMapBlockLUT[pData[4]]; - pNode->flags.can_travel_w = false; - if(iX != 0) - { - pNode[-1].flags.can_travel_e = false; - } - } + map_tile* pNode = cells; + map_tile* pOriginalNode = original_cells; + const uint8_t* pParcel = pData + parcel_offset; + pData += 34; - pNode->iRoomId = 0; - pNode->iParcelId = *pParcel; - if(*pParcel >= parcel_count) - parcel_count = *pParcel + 1; + pNode->objects.clear(); + for (int iY = 0; iY < height; ++iY) { + for (int iX = 0; iX < width; ++iX) { + uint8_t iBaseTile = gs_iTHMapBlockLUT[pData[2]]; + pNode->flags.can_travel_n = true; + pNode->flags.can_travel_e = true; + pNode->flags.can_travel_s = true; + pNode->flags.can_travel_w = true; + if (iX == 0) { + pNode->flags.can_travel_w = false; + } else if (iX == width - 1) { + pNode->flags.can_travel_e = false; + } - if(!(pData[5] & 1)) - { - pNode->flags.passable = true; - if(!(pData[7] & 16)) - { - pNode->flags.hospital = true; - if(!(pData[5] & 2)) { - pNode->flags.buildable = true; - } - if(!(pData[5] & 4) || pData[1] == 0) { - pNode->flags.buildable_n = true; - } - if(!(pData[5] & 8) || pData[1] == 0) { - pNode->flags.buildable_e = true; - } - if(!(pData[5] & 16) || pData[1] == 0) { - pNode->flags.buildable_s = true; - } - if(!(pData[5] & 32) || pData[1] == 0) { - pNode->flags.buildable_w = true; - } - } - } + if (iY == 0) { + pNode->flags.can_travel_n = false; + } else if (iY == height - 1) { + pNode->flags.can_travel_s = false; + } - *pOriginalNode = *pNode; - if(is_divider_wall(pData[3])) - pOriginalNode->iBlock[1] = gs_iTHMapBlockLUT[pData[3]]; - if(is_divider_wall(pData[4])) - pOriginalNode->iBlock[2] = gs_iTHMapBlockLUT[pData[4]]; - - if(pData[1] != 0 && fnObjectCallback != nullptr) - { - fnObjectCallback(pCallbackToken, iX, iY, (object_type)pData[1], pData[0]); - } + pNode->iBlock[0] = iBaseTile; + if (pData[3] == 0 || is_divider_wall(pData[3])) { + // Tiles 71, 72 and 73 (pond foliage) are used as floor tiles, + // but are too tall to be floor tiles, so move them to a wall, + // and replace the floor with something similar (pond base). + if (71 <= iBaseTile && iBaseTile <= 73) { + pNode->iBlock[1] = iBaseTile; + pNode->iBlock[0] = iBaseTile = 69; + } else { + pNode->iBlock[1] = 0; } + } else { + pNode->iBlock[1] = gs_iTHMapBlockLUT[pData[3]]; + pNode->flags.can_travel_n = false; + if (iY != 0) { + pNode[-128].flags.can_travel_s = false; + } + } + if (pData[4] == 0 || is_divider_wall(pData[4])) { + pNode->iBlock[2] = 0; + } else { + pNode->iBlock[2] = gs_iTHMapBlockLUT[pData[4]]; + pNode->flags.can_travel_w = false; + if (iX != 0) { + pNode[-1].flags.can_travel_e = false; + } + } + + pNode->iRoomId = 0; + pNode->iParcelId = bytes_to_uint16_le(pParcel); + if (pNode->iParcelId >= parcel_count) { + parcel_count = pNode->iParcelId + 1; + } + + if (!(pData[5] & 1)) { + pNode->flags.passable = true; + if (!(pData[7] & 16)) { + pNode->flags.hospital = true; + if (!(pData[5] & 2)) { + pNode->flags.buildable = true; + } + if (!(pData[5] & 4) || pData[1] == 0) { + pNode->flags.buildable_n = true; + } + if (!(pData[5] & 8) || pData[1] == 0) { + pNode->flags.buildable_e = true; + } + if (!(pData[5] & 16) || pData[1] == 0) { + pNode->flags.buildable_s = true; + } + if (!(pData[5] & 32) || pData[1] == 0) { + pNode->flags.buildable_w = true; + } + } + } + + *pOriginalNode = *pNode; + if (is_divider_wall(pData[3])) { + pOriginalNode->iBlock[1] = gs_iTHMapBlockLUT[pData[3]]; + } + if (is_divider_wall(pData[4])) { + pOriginalNode->iBlock[2] = gs_iTHMapBlockLUT[pData[4]]; + } + + if (pData[1] != 0 && fnObjectCallback != nullptr) { + fnObjectCallback(pCallbackToken, iX, iY, (object_type)pData[1], + pData[0]); + } + + ++pNode; + ++pOriginalNode; + pData += 8; + pParcel += 2; } - plot_owner = new int[parcel_count]; - plot_owner[0] = 0; - for(int i = 1; i < parcel_count; ++i) - plot_owner[i] = 1; + } - update_shadows(); + plot_owner = new int[parcel_count]; + plot_owner[0] = 0; + for (int i = 1; i < parcel_count; ++i) { + plot_owner[i] = 1; + } - parcel_tile_counts = new int[parcel_count]; - parcel_tile_counts[0] = 0; - for(int i = 1; i < parcel_count; ++i) - parcel_tile_counts[i] = count_parcel_tiles(i); + update_shadows(); - return true; + parcel_tile_counts = new int[parcel_count]; + parcel_tile_counts[0] = 0; + for (int i = 1; i < parcel_count; ++i) { + parcel_tile_counts[i] = count_parcel_tiles(i); + } + + return true; } -void level_map::save(std::string filename) -{ - uint8_t aBuffer[256] = {0}; - int iBufferNext = 0; - std::ofstream os(filename, std::ios_base::trunc | std::ios_base::binary); +void level_map::save(std::string filename) { + uint8_t aBuffer[256] = {0}; + int iBufferNext = 0; + std::ofstream os(filename, std::ios_base::trunc | std::ios_base::binary); - // Header - aBuffer[0] = static_cast(player_count); - // TODO: Determine correct contents for the next 33 bytes - os.write(reinterpret_cast(aBuffer), 34); + // Header + aBuffer[0] = static_cast(player_count); + // TODO: Determine correct contents for the next 33 bytes + os.write(reinterpret_cast(aBuffer), 34); - uint8_t aReverseBlockLUT[256] = {0}; - for(int i = 0; i < 256; ++i) - { - aReverseBlockLUT[gs_iTHMapBlockLUT[i]] = static_cast(i); + uint8_t aReverseBlockLUT[256] = {0}; + for (int i = 0; i < 256; ++i) { + aReverseBlockLUT[gs_iTHMapBlockLUT[i]] = static_cast(i); + } + aReverseBlockLUT[0] = 0; + + for (map_tile *pNode = cells, *pLimitNode = pNode + width * height; + pNode != pLimitNode; ++pNode) { + // TODO: Nicer system for saving object data + aBuffer[iBufferNext++] = pNode->flags.tall_west ? 1 : 0; + aBuffer[iBufferNext++] = + static_cast(pNode->objects.empty() ? object_type::no_object + : pNode->objects.front()); + + // Blocks + aBuffer[iBufferNext++] = aReverseBlockLUT[pNode->iBlock[0] & 0xFF]; + aBuffer[iBufferNext++] = aReverseBlockLUT[pNode->iBlock[1] & 0xFF]; + aBuffer[iBufferNext++] = aReverseBlockLUT[pNode->iBlock[2] & 0xFF]; + + // Flags (TODO: Set a few more flag bits?) + uint8_t iFlags = 63; + if (pNode->flags.passable) { + iFlags ^= 1; } - aReverseBlockLUT[0] = 0; - - for(map_tile *pNode = cells, *pLimitNode = pNode + width * height; - pNode != pLimitNode; ++pNode) - { - // TODO: Nicer system for saving object data - aBuffer[iBufferNext++] = pNode->flags.tall_west ? 1 : 0; - aBuffer[iBufferNext++] = static_cast(pNode->objects.empty() ? object_type::no_object : pNode->objects.front()); - - // Blocks - aBuffer[iBufferNext++] = aReverseBlockLUT[pNode->iBlock[0] & 0xFF]; - aBuffer[iBufferNext++] = aReverseBlockLUT[pNode->iBlock[1] & 0xFF]; - aBuffer[iBufferNext++] = aReverseBlockLUT[pNode->iBlock[2] & 0xFF]; - - // Flags (TODO: Set a few more flag bits?) - uint8_t iFlags = 63; - if(pNode->flags.passable) - iFlags ^= 1; - if(pNode->flags.buildable) - iFlags ^= 2; - if(pNode->flags.buildable_n) - iFlags ^= 4; - if(pNode->flags.buildable_e) - iFlags ^= 8; - if(pNode->flags.buildable_s) - iFlags ^= 16; - if(pNode->flags.buildable_w) - iFlags ^= 32; - - aBuffer[iBufferNext++] = iFlags; - - aBuffer[iBufferNext++] = 0; - iFlags = 16; - if(pNode->flags.hospital) - iFlags ^= 16; - aBuffer[iBufferNext++] = iFlags; - - if(iBufferNext == sizeof(aBuffer)) - { - os.write(reinterpret_cast(aBuffer), sizeof(aBuffer)); - iBufferNext = 0; - } + if (pNode->flags.buildable) { + iFlags ^= 2; } - for(map_tile *pNode = cells, *pLimitNode = pNode + width * height; - pNode != pLimitNode; ++pNode) - { - aBuffer[iBufferNext++] = static_cast(pNode->iParcelId & 0xFF); - aBuffer[iBufferNext++] = static_cast(pNode->iParcelId >> 8); - if(iBufferNext == sizeof(aBuffer)) - { - os.write(reinterpret_cast(aBuffer), sizeof(aBuffer)); - iBufferNext = 0; - } + if (pNode->flags.buildable_n) { + iFlags ^= 4; + } + if (pNode->flags.buildable_e) { + iFlags ^= 8; + } + if (pNode->flags.buildable_s) { + iFlags ^= 16; + } + if (pNode->flags.buildable_w) { + iFlags ^= 32; } - // TODO: What are these two bytes? - aBuffer[iBufferNext++] = 3; + aBuffer[iBufferNext++] = iFlags; + aBuffer[iBufferNext++] = 0; - os.write(reinterpret_cast(aBuffer), iBufferNext); - iBufferNext = 0; - - std::memset(aBuffer, 0, 56); - for(int i = 0; i < player_count; ++i) - { - write_tile_index(aBuffer + iBufferNext, - initial_camera_x[i], initial_camera_y[i]); - write_tile_index(aBuffer + iBufferNext + 8, - heliport_x[i], heliport_y[i]); - iBufferNext += 2; + iFlags = 16; + if (pNode->flags.hospital) { + iFlags ^= 16; } - os.write(reinterpret_cast(aBuffer), 16); - std::memset(aBuffer, 0, 16); - // TODO: What are these 56 bytes? - os.write(reinterpret_cast(aBuffer), 56); - os.close(); + aBuffer[iBufferNext++] = iFlags; + + if (iBufferNext == sizeof(aBuffer)) { + os.write(reinterpret_cast(aBuffer), sizeof(aBuffer)); + iBufferNext = 0; + } + } + for (map_tile *pNode = cells, *pLimitNode = pNode + width * height; + pNode != pLimitNode; ++pNode) { + aBuffer[iBufferNext++] = static_cast(pNode->iParcelId & 0xFF); + aBuffer[iBufferNext++] = static_cast(pNode->iParcelId >> 8); + if (iBufferNext == sizeof(aBuffer)) { + os.write(reinterpret_cast(aBuffer), sizeof(aBuffer)); + iBufferNext = 0; + } + } + + // TODO: What are these two bytes? + aBuffer[iBufferNext++] = 3; + aBuffer[iBufferNext++] = 0; + os.write(reinterpret_cast(aBuffer), iBufferNext); + iBufferNext = 0; + + std::memset(aBuffer, 0, 56); + for (int i = 0; i < player_count; ++i) { + write_tile_index(aBuffer + iBufferNext, initial_camera_x[i], + initial_camera_y[i]); + write_tile_index(aBuffer + iBufferNext + 8, heliport_x[i], heliport_y[i]); + iBufferNext += 2; + } + os.write(reinterpret_cast(aBuffer), 16); + std::memset(aBuffer, 0, 16); + // TODO: What are these 56 bytes? + os.write(reinterpret_cast(aBuffer), 56); + os.close(); } namespace { //! Add or remove divider wall for the given tile /*! - If the given 'pNode' has an indoor border to another parcel in the 'delta' direction: + If the given 'pNode' has an indoor border to another parcel in the 'delta' + direction: * A divider wall is added in the layer specified by 'block' if the owners of the two parcels are not the same, or - * A divider wall is removed if the owners are the same and 'iParcelId' is involved. - \return True if a border was removed, false otherwise + * A divider wall is removed if the owners are the same and 'iParcelId' is + involved. \return True if a border was removed, false otherwise */ -bool addRemoveDividerWalls(level_map* pMap, map_tile* pNode, const map_tile* pOriginalNode, - int iXY, int delta, int block, int iParcelId) -{ - if (iXY > 0 && pOriginalNode->flags.hospital && - pOriginalNode[-delta].flags.hospital && - pNode->iParcelId != pNode[-delta].iParcelId) - { - int iOwner = pMap->get_parcel_owner(pNode->iParcelId); - int iOtherOwner = pMap->get_parcel_owner(pNode[-delta].iParcelId); - if (iOwner != iOtherOwner) - { - pNode->iBlock[block] = block + (iOwner ? 143 : 141); - } - else if (pNode->iParcelId == iParcelId || pNode[-delta].iParcelId == iParcelId) - { - pNode->iBlock[block] = 0; - return true; - } +bool addRemoveDividerWalls(level_map* pMap, map_tile* pNode, + const map_tile* pOriginalNode, int iXY, int delta, + int block, int iParcelId) { + if (iXY > 0 && pOriginalNode->flags.hospital && + pOriginalNode[-delta].flags.hospital && + pNode->iParcelId != pNode[-delta].iParcelId) { + int iOwner = pMap->get_parcel_owner(pNode->iParcelId); + int iOtherOwner = pMap->get_parcel_owner(pNode[-delta].iParcelId); + if (iOwner != iOtherOwner) { + pNode->iBlock[block] = block + (iOwner ? 143 : 141); + } else if (pNode->iParcelId == iParcelId || + pNode[-delta].iParcelId == iParcelId) { + pNode->iBlock[block] = 0; + return true; } - return false; + } + return false; } -} // namespace +} // namespace -std::vector> level_map::set_parcel_owner(int iParcelId, int iOwner) -{ - std::vector> vSplitTiles; - if(iParcelId <= 0 || parcel_count <= iParcelId || iOwner < 0) - return vSplitTiles; - plot_owner[iParcelId] = iOwner; - - map_tile *pNode = cells; - const map_tile *pOriginalNode = original_cells; - - for(int iY = 0; iY < 128; ++iY) - { - for(int iX = 0; iX < 128; ++iX, ++pNode, ++pOriginalNode) - { - if(pNode->iParcelId == iParcelId) - { - if(iOwner != 0) - { - pNode->iBlock[0] = pOriginalNode->iBlock[0]; - pNode->iBlock[1] = pOriginalNode->iBlock[1]; - pNode->iBlock[2] = pOriginalNode->iBlock[2]; - pNode->flags = pOriginalNode->flags; - } - else - { - // Nicely mown grass pattern - pNode->iBlock[0] = static_cast(((iX & 1) << 1) + 1); - - pNode->iBlock[1] = 0; - pNode->iBlock[2] = 0; - pNode->flags = {}; - - // Random decoration - if(((iX | iY) & 0x7) == 0) - { - int iWhich = (iX ^ iY) % 9; - pNode->iBlock[1] = static_cast(192 + iWhich); - } - } - } - if (addRemoveDividerWalls(this, pNode, pOriginalNode, iX, 1, 2, iParcelId)) - { - vSplitTiles.push_back(std::make_pair(iX, iY)); - } - if (addRemoveDividerWalls(this, pNode, pOriginalNode, iY, 128, 1, iParcelId)) - { - vSplitTiles.push_back(std::make_pair(iX, iY)); - } - } - } - - update_pathfinding(); - update_shadows(); - update_purchase_matrix(); +std::vector> level_map::set_parcel_owner(int iParcelId, + int iOwner) { + std::vector> vSplitTiles; + if (iParcelId <= 0 || parcel_count <= iParcelId || iOwner < 0) { return vSplitTiles; + } + plot_owner[iParcelId] = iOwner; + + map_tile* pNode = cells; + const map_tile* pOriginalNode = original_cells; + + for (int iY = 0; iY < 128; ++iY) { + for (int iX = 0; iX < 128; ++iX, ++pNode, ++pOriginalNode) { + if (pNode->iParcelId == iParcelId) { + if (iOwner != 0) { + pNode->iBlock[0] = pOriginalNode->iBlock[0]; + pNode->iBlock[1] = pOriginalNode->iBlock[1]; + pNode->iBlock[2] = pOriginalNode->iBlock[2]; + pNode->flags = pOriginalNode->flags; + } else { + // Nicely mown grass pattern + pNode->iBlock[0] = static_cast(((iX & 1) << 1) + 1); + + pNode->iBlock[1] = 0; + pNode->iBlock[2] = 0; + pNode->flags = {}; + + // Random decoration + if (((iX | iY) & 0x7) == 0) { + int iWhich = (iX ^ iY) % 9; + pNode->iBlock[1] = static_cast(192 + iWhich); + } + } + } + if (addRemoveDividerWalls(this, pNode, pOriginalNode, iX, 1, 2, + iParcelId)) { + vSplitTiles.push_back(std::make_pair(iX, iY)); + } + if (addRemoveDividerWalls(this, pNode, pOriginalNode, iY, 128, 1, + iParcelId)) { + vSplitTiles.push_back(std::make_pair(iX, iY)); + } + } + } + + update_pathfinding(); + update_shadows(); + update_purchase_matrix(); + return vSplitTiles; } namespace { -void test_adj(bool* parcel_adjacency_matrix, int parcel_count, const map_tile *original_node, int xy, ptrdiff_t delta) -{ - if(xy > 0 && - original_node->iParcelId != original_node[-delta].iParcelId && - original_node->flags.passable && original_node[-delta].flags.passable) - { - parcel_adjacency_matrix[original_node->iParcelId * parcel_count + original_node[-delta].iParcelId] = true; - parcel_adjacency_matrix[original_node->iParcelId + original_node[-delta].iParcelId * parcel_count] = true; - } +void test_adj(bool* parcel_adjacency_matrix, int parcel_count, + const map_tile* original_node, int xy, ptrdiff_t delta) { + if (xy > 0 && original_node->iParcelId != original_node[-delta].iParcelId && + original_node->flags.passable && original_node[-delta].flags.passable) { + parcel_adjacency_matrix[original_node->iParcelId * parcel_count + + original_node[-delta].iParcelId] = true; + parcel_adjacency_matrix[original_node->iParcelId + + original_node[-delta].iParcelId * parcel_count] = + true; + } } -} // namespace +} // namespace -void level_map::make_adjacency_matrix() -{ - if(parcel_adjacency_matrix != nullptr) - return; +void level_map::make_adjacency_matrix() { + if (parcel_adjacency_matrix != nullptr) { + return; + } - parcel_adjacency_matrix = new bool[parcel_count * parcel_count]; - for(int i = 0; i < parcel_count; ++i) - { - for(int j = 0; j < parcel_count; ++j) - { - parcel_adjacency_matrix[i * parcel_count + j] = (i == j); - } + parcel_adjacency_matrix = new bool[parcel_count * parcel_count]; + for (int i = 0; i < parcel_count; ++i) { + for (int j = 0; j < parcel_count; ++j) { + parcel_adjacency_matrix[i * parcel_count + j] = (i == j); } + } - const map_tile *pOriginalNode = original_cells; - for(int iY = 0; iY < 128; ++iY) - { - for(int iX = 0; iX < 128; ++iX) - { - ++pOriginalNode; - test_adj(parcel_adjacency_matrix, parcel_count, pOriginalNode, iX, 1); - test_adj(parcel_adjacency_matrix, parcel_count, pOriginalNode, iY, 128); - } + const map_tile* pOriginalNode = original_cells; + for (int iY = 0; iY < 128; ++iY) { + for (int iX = 0; iX < 128; ++iX) { + ++pOriginalNode; + test_adj(parcel_adjacency_matrix, parcel_count, pOriginalNode, iX, 1); + test_adj(parcel_adjacency_matrix, parcel_count, pOriginalNode, iY, 128); } + } } -void level_map::make_purchase_matrix() -{ - if(purchasable_matrix != nullptr) - return; // Already made - purchasable_matrix = new bool[4 * parcel_count]; - update_purchase_matrix(); +void level_map::make_purchase_matrix() { + if (purchasable_matrix != nullptr) { + return; // Already made + } + + purchasable_matrix = new bool[max_player_count * parcel_count]; + update_purchase_matrix(); } -void level_map::update_purchase_matrix() -{ - if(purchasable_matrix == nullptr) - return; // Nothing to update - for(int iPlayer = 1; iPlayer <= 4; ++iPlayer) - { - for(int iParcel = 0; iParcel < parcel_count; ++iParcel) - { - bool bPurchasable = false; - if(iParcel != 0 && plot_owner[iParcel] == 0) - { - for(int iParcel2 = 0; iParcel2 < parcel_count; ++iParcel2) - { - if((plot_owner[iParcel2] == iPlayer) || (iParcel2 == 0)) - { - if(are_parcels_adjacent(iParcel, iParcel2)) - { - bPurchasable = true; - break; - } - } - } +void level_map::update_purchase_matrix() { + if (purchasable_matrix == nullptr) { + return; // Nothing to update + } + + for (int iPlayer = 1; iPlayer <= max_player_count; ++iPlayer) { + for (int iParcel = 0; iParcel < parcel_count; ++iParcel) { + bool bPurchasable = false; + if (iParcel != 0 && plot_owner[iParcel] == 0) { + for (int iParcel2 = 0; iParcel2 < parcel_count; ++iParcel2) { + if ((plot_owner[iParcel2] == iPlayer) || (iParcel2 == 0)) { + if (are_parcels_adjacent(iParcel, iParcel2)) { + bPurchasable = true; + break; } - purchasable_matrix[iParcel * 4 + iPlayer - 1] = bPurchasable; + } } + } + purchasable_matrix[iParcel * max_player_count + iPlayer - 1] = + bPurchasable; } + } } -bool level_map::are_parcels_adjacent(int iParcel1, int iParcel2) -{ - if(0 <= iParcel1 && iParcel1 < parcel_count - && 0 <= iParcel2 && iParcel2 < parcel_count) - { - make_adjacency_matrix(); - return parcel_adjacency_matrix[iParcel1 * parcel_count + iParcel2]; +bool level_map::are_parcels_adjacent(int iParcel1, int iParcel2) { + if (0 <= iParcel1 && iParcel1 < parcel_count && 0 <= iParcel2 && + iParcel2 < parcel_count) { + make_adjacency_matrix(); + return parcel_adjacency_matrix[iParcel1 * parcel_count + iParcel2]; + } + return false; +} + +bool level_map::is_parcel_purchasable(int iParcelId, int iPlayer) { + if (0 <= iParcelId && iParcelId < parcel_count && 1 <= iPlayer && + iPlayer <= max_player_count) { + make_purchase_matrix(); + return purchasable_matrix[iParcelId * max_player_count + iPlayer - 1]; + } + return false; +} + +void level_map::set_player_count(int count) { + if (count < 1 || count > max_player_count) { + throw std::out_of_range("Player count must be between 1 and 4"); + } + + player_count = count; +} + +bool level_map::get_player_camera_tile(int iPlayer, int* pX, int* pY) const { + if (iPlayer < 0 || iPlayer >= get_player_count()) { + if (pX) { + *pX = 0; + } + if (pY) { + *pY = 0; } return false; + } + if (pX) { + *pX = initial_camera_x[iPlayer]; + } + if (pY) { + *pY = initial_camera_y[iPlayer]; + } + return true; } -bool level_map::is_parcel_purchasable(int iParcelId, int iPlayer) -{ - if(0 <= iParcelId && iParcelId < parcel_count - && 1 <= iPlayer && iPlayer <= 4) - { - make_purchase_matrix(); - return purchasable_matrix[iParcelId * 4 + iPlayer - 1]; +bool level_map::get_player_heliport_tile(int iPlayer, int* pX, int* pY) const { + if (iPlayer < 0 || iPlayer >= get_player_count()) { + if (pX) { + *pX = 0; + } + if (pY) { + *pY = 0; } return false; + } + if (pX) { + *pX = heliport_x[iPlayer]; + } + if (pY) { + *pY = heliport_y[iPlayer]; + } + return true; } -void level_map::set_player_count(int count) -{ - if (count < 1 || count > 4) - throw std::out_of_range("Player count must be between 1 and 4"); - - player_count = count; +void level_map::set_player_camera_tile(int iPlayer, int iX, int iY) { + if (0 <= iPlayer && iPlayer < get_player_count()) { + initial_camera_x[iPlayer] = iX; + initial_camera_y[iPlayer] = iY; + } } -bool level_map::get_player_camera_tile(int iPlayer, int* pX, int* pY) const -{ - if(iPlayer < 0 || iPlayer >= get_player_count()) - { - if(pX) *pX = 0; - if(pY) *pY = 0; - return false; +void level_map::set_player_heliport_tile(int iPlayer, int iX, int iY) { + if (0 <= iPlayer && iPlayer < get_player_count()) { + heliport_x[iPlayer] = iX; + heliport_y[iPlayer] = iY; + } +} + +int level_map::get_parcel_tile_count(int iParcelId) const { + if (iParcelId < 1 || iParcelId >= parcel_count) { + return 0; + } + return parcel_tile_counts[iParcelId]; +} + +int level_map::count_parcel_tiles(int iParcelId) const { + int iTiles = 0; + for (int iY = 0; iY < height; ++iY) { + for (int iX = 0; iX < width; ++iX) { + const map_tile* pNode = get_tile_unchecked(iX, iY); + if (pNode->iParcelId == iParcelId) { + iTiles++; + } } - if(pX) *pX = initial_camera_x[iPlayer]; - if(pY) *pY = initial_camera_y[iPlayer]; - return true; + } + return iTiles; } -bool level_map::get_player_heliport_tile(int iPlayer, int* pX, int* pY) const -{ - if(iPlayer < 0 || iPlayer >= get_player_count()) - { - if(pX) *pX = 0; - if(pY) *pY = 0; - return false; - } - if(pX) *pX = heliport_x[iPlayer]; - if(pY) *pY = heliport_y[iPlayer]; - return true; +map_tile* level_map::get_tile(int iX, int iY) { + if (0 <= iX && iX < width && 0 <= iY && iY < height) { + return get_tile_unchecked(iX, iY); + } else { + return nullptr; + } } -void level_map::set_player_camera_tile(int iPlayer, int iX, int iY) -{ - if(0 <= iPlayer && iPlayer < get_player_count()) - { - initial_camera_x[iPlayer] = iX; - initial_camera_y[iPlayer] = iY; - } +const map_tile* level_map::get_tile(int iX, int iY) const { + if (0 <= iX && iX < width && 0 <= iY && iY < height) { + return get_tile_unchecked(iX, iY); + } else { + return nullptr; + } } -void level_map::set_player_heliport_tile(int iPlayer, int iX, int iY) -{ - if(0 <= iPlayer && iPlayer < get_player_count()) - { - heliport_x[iPlayer] = iX; - heliport_y[iPlayer] = iY; - } +const map_tile* level_map::get_original_tile(int iX, int iY) const { + if (0 <= iX && iX < width && 0 <= iY && iY < height) { + return get_original_tile_unchecked(iX, iY); + } else { + return nullptr; + } } -int level_map::get_parcel_tile_count(int iParcelId) const -{ - if(iParcelId < 1 || iParcelId >= parcel_count) - { - return 0; - } - return parcel_tile_counts[iParcelId]; +map_tile* level_map::get_tile_unchecked(int iX, int iY) { + return cells + iY * width + iX; } -int level_map::count_parcel_tiles(int iParcelId) const -{ - int iTiles = 0; - for(int iY = 0; iY < height; ++iY) - { - for(int iX = 0; iX < width; ++iX) - { - const map_tile* pNode = get_tile_unchecked(iX, iY); - if(pNode->iParcelId == iParcelId) iTiles++; - } - } - return iTiles; +const map_tile* level_map::get_tile_unchecked(int iX, int iY) const { + return cells + iY * width + iX; } -map_tile* level_map::get_tile(int iX, int iY) -{ - if(0 <= iX && iX < width && 0 <= iY && iY < height) - return get_tile_unchecked(iX, iY); - else - return nullptr; +const map_tile* level_map::get_original_tile_unchecked(int iX, int iY) const { + return original_cells + iY * width + iX; } -const map_tile* level_map::get_tile(int iX, int iY) const -{ - if(0 <= iX && iX < width && 0 <= iY && iY < height) - return get_tile_unchecked(iX, iY); - else - return nullptr; -} +void level_map::set_block_sheet(sprite_sheet* pSheet) { blocks = pSheet; } -const map_tile* level_map::get_original_tile(int iX, int iY) const -{ - if(0 <= iX && iX < width && 0 <= iY && iY < height) - return get_original_tile_unchecked(iX, iY); - else - return nullptr; -} - -map_tile* level_map::get_tile_unchecked(int iX, int iY) -{ - return cells + iY * width + iX; -} - -const map_tile* level_map::get_tile_unchecked(int iX, int iY) const -{ - return cells + iY * width + iX; -} - -const map_tile* level_map::get_original_tile_unchecked(int iX, int iY) const -{ - return original_cells + iY * width + iX; -} - -void level_map::set_block_sheet(sprite_sheet* pSheet) -{ - blocks = pSheet; -} - -void level_map::set_all_wall_draw_flags(uint8_t iFlags) -{ - uint16_t iBlockOr = static_cast(iFlags << 8); - map_tile *pNode = cells; - for(int i = 0; i < width * height; ++i, ++pNode) - { - pNode->iBlock[1] = static_cast((pNode->iBlock[1] & 0xFF) | iBlockOr); - pNode->iBlock[2] = static_cast((pNode->iBlock[2] & 0xFF) | iBlockOr); - } +void level_map::set_all_wall_draw_flags(uint8_t iFlags) { + uint16_t iBlockOr = static_cast(iFlags << 8); + map_tile* pNode = cells; + for (int i = 0; i < width * height; ++i, ++pNode) { + pNode->iBlock[1] = + static_cast((pNode->iBlock[1] & 0xFF) | iBlockOr); + pNode->iBlock[2] = + static_cast((pNode->iBlock[2] & 0xFF) | iBlockOr); + } } // Definition is in th_gfx.h so this should move -void clip_rect_intersection(clip_rect& rcClip,const clip_rect& rcIntersect) -{ - // The intersection of the rectangles is the higher of the lower bounds and the lower of the higher bounds, clamped to a zero size. - clip_rect::x_y_type maxX = static_cast(std::min(rcClip.x + rcClip.w, rcIntersect.x + rcIntersect.w)); - clip_rect::x_y_type maxY = static_cast(std::min(rcClip.y + rcClip.h, rcIntersect.y + rcIntersect.h)); - rcClip.x = std::max(rcClip.x, rcIntersect.x); - rcClip.y = std::max(rcClip.y, rcIntersect.y); - rcClip.w = maxX - rcClip.x; - rcClip.h = maxY - rcClip.y; +void clip_rect_intersection(clip_rect& rcClip, const clip_rect& rcIntersect) { + // The intersection of the rectangles is the higher of the lower bounds and + // the lower of the higher bounds, clamped to a zero size. + clip_rect::x_y_type maxX = static_cast( + std::min(rcClip.x + rcClip.w, rcIntersect.x + rcIntersect.w)); + clip_rect::x_y_type maxY = static_cast( + std::min(rcClip.y + rcClip.h, rcIntersect.y + rcIntersect.h)); + rcClip.x = std::max(rcClip.x, rcIntersect.x); + rcClip.y = std::max(rcClip.y, rcIntersect.y); + rcClip.w = maxX - rcClip.x; + rcClip.h = maxY - rcClip.y; - // Make sure that we clamp the values to 0. - if (rcClip.w <= 0) - { - rcClip.w = rcClip.h = 0; - } - else if (rcClip.h <= 0) - { - rcClip.w = rcClip.h = 0; - } + // Make sure that we clamp the values to 0. + if (rcClip.w <= 0) { + rcClip.w = rcClip.h = 0; + } else if (rcClip.h <= 0) { + rcClip.w = rcClip.h = 0; + } } void level_map::draw(render_target* pCanvas, int iScreenX, int iScreenY, - int iWidth, int iHeight, int iCanvasX, int iCanvasY) const -{ - /* - The map is drawn in two passes, with each pass done one scanline at a - time (a scanline is a list of tiles with the same screen Y co-ordinate). - The first pass does floor tiles, as the entire floor needs to be painted - below anything else (for example, see the walking north through a door - animation, which needs to paint over the floor of the scanline below the - animation). On the second pass, walls and entities are drawn, with the - order controlled such that entites appear in the right order relative to - the walls around them. For each scanline, the following is done: + int iWidth, int iHeight, int iCanvasX, + int iCanvasY) const { + /* + The map is drawn in two passes, with each pass done one scanline at a + time (a scanline is a list of tiles with the same screen Y co-ordinate). + The first pass does floor tiles, as the entire floor needs to be painted + below anything else (for example, see the walking north through a door + animation, which needs to paint over the floor of the scanline below the + animation). On the second pass, walls and entities are drawn, with the + order controlled such that entites appear in the right order relative to + the walls around them. For each scanline, the following is done: - 1st pass: - 1) For each tile, left to right, the floor tile (layer 0) - 2nd pass: - 1) For each tile, right to left, the north wall, then the early entities - 2) For each tile, left to right, the west wall, then the late entities - */ + 1st pass: + 1) For each tile, left to right, the floor tile (layer 0) + 2nd pass: + 1) For each tile, right to left, the north wall, then the early entities + 2) For each tile, left to right, the west wall, then the late entities + */ - if(blocks == nullptr || cells == nullptr) - return; + if (blocks == nullptr || cells == nullptr) { + return; + } - clip_rect rcClip; - rcClip.x = static_cast(iCanvasX); - rcClip.y = static_cast(iCanvasY); - rcClip.w = static_cast(iWidth); - rcClip.h = static_cast(iHeight); - pCanvas->set_clip_rect(&rcClip); + clip_rect rcClip; + rcClip.x = static_cast(iCanvasX); + rcClip.y = static_cast(iCanvasY); + rcClip.w = static_cast(iWidth); + rcClip.h = static_cast(iHeight); + pCanvas->set_clip_rect(&rcClip); - // 1st pass - pCanvas->start_nonoverlapping_draws(); - for(map_tile_iterator itrNode1(this, iScreenX, iScreenY, iWidth, iHeight); itrNode1; ++itrNode1) - { - unsigned int iH = 32; - unsigned int iBlock = itrNode1->iBlock[0]; - blocks->get_sprite_size(iBlock & 0xFF, nullptr, &iH); - blocks->draw_sprite(pCanvas, iBlock & 0xFF, - itrNode1.tile_x_position_on_screen() + iCanvasX - 32, - itrNode1.tile_y_position_on_screen() + iCanvasY - iH + 32, iBlock >> 8); + // 1st pass + pCanvas->start_nonoverlapping_draws(); + for (map_tile_iterator itrNode1(this, iScreenX, iScreenY, iWidth, iHeight); + itrNode1; ++itrNode1) { + unsigned int iH = 32; + unsigned int iBlock = itrNode1->iBlock[0]; + blocks->get_sprite_size(iBlock & 0xFF, nullptr, &iH); + blocks->draw_sprite( + pCanvas, iBlock & 0xFF, + itrNode1.tile_x_position_on_screen() + iCanvasX - 32, + itrNode1.tile_y_position_on_screen() + iCanvasY - iH + 32, iBlock >> 8); + } + pCanvas->finish_nonoverlapping_draws(); + + bool bFirst = true; + map_scanline_iterator formerIterator; + // 2nd pass + for (map_tile_iterator itrNode2(this, iScreenX, iScreenY, iWidth, iHeight); + itrNode2; ++itrNode2) { + if (itrNode2->flags.shadow_full) { + blocks->draw_sprite( + pCanvas, 74, itrNode2.tile_x_position_on_screen() + iCanvasX - 32, + itrNode2.tile_y_position_on_screen() + iCanvasY, thdf_alpha_75); + } else if (itrNode2->flags.shadow_half) { + blocks->draw_sprite( + pCanvas, 75, itrNode2.tile_x_position_on_screen() + iCanvasX - 32, + itrNode2.tile_y_position_on_screen() + iCanvasY, thdf_alpha_75); } - pCanvas->finish_nonoverlapping_draws(); - bool bFirst = true; - map_scanline_iterator formerIterator; - // 2nd pass - for(map_tile_iterator itrNode2(this, iScreenX, iScreenY, iWidth, iHeight); itrNode2; ++itrNode2) - { - if(itrNode2->flags.shadow_full) - { - blocks->draw_sprite(pCanvas, 74, itrNode2.tile_x_position_on_screen() + iCanvasX - 32, - itrNode2.tile_y_position_on_screen() + iCanvasY, thdf_alpha_75); - } - else if(itrNode2->flags.shadow_half) - { - blocks->draw_sprite(pCanvas, 75, itrNode2.tile_x_position_on_screen() + iCanvasX - 32, - itrNode2.tile_y_position_on_screen() + iCanvasY, thdf_alpha_75); - } + if (!itrNode2.is_last_on_scanline()) { + continue; + } - if(!itrNode2.is_last_on_scanline()) - continue; - - for(map_scanline_iterator itrNode(itrNode2, map_scanline_iterator_direction::backward, iCanvasX, iCanvasY); itrNode; ++itrNode) - { - unsigned int iH; - unsigned int iBlock = itrNode->iBlock[1]; - if(iBlock != 0 && blocks->get_sprite_size(iBlock & 0xFF, - nullptr, &iH) && iH > 0) - { - blocks->draw_sprite(pCanvas, iBlock & 0xFF, itrNode.x() - 32, - itrNode.y() - iH + 32, iBlock >> 8); - if(itrNode->flags.shadow_wall) - { - clip_rect rcOldClip, rcNewClip; - pCanvas->get_clip_rect(&rcOldClip); - rcNewClip.x = static_cast(itrNode.x() - 32); - rcNewClip.y = static_cast(itrNode.y() - iH + 32 + 4); - rcNewClip.w = static_cast(64); - rcNewClip.h = static_cast(86 - 4); - clip_rect_intersection(rcNewClip, rcOldClip); - pCanvas->set_clip_rect(&rcNewClip); - blocks->draw_sprite(pCanvas, 156, itrNode.x() - 32, - itrNode.y() - 56, thdf_alpha_75); - pCanvas->set_clip_rect(&rcOldClip); - } - } - drawable *pItem = (drawable*)(itrNode->oEarlyEntities.next); - while(pItem) - { - pItem->draw_fn(pItem, pCanvas, itrNode.x(), itrNode.y()); - pItem = (drawable*)(pItem->next); - } - } - - map_scanline_iterator itrNode(itrNode2, map_scanline_iterator_direction::forward, iCanvasX, iCanvasY); - if(!bFirst) { - //since the scanline count from one THMapScanlineIterator to another can differ - //synchronization between the current iterator and the former one is neeeded - if(itrNode.x() < -64) - ++itrNode; - while(formerIterator.x() < itrNode.x()) - ++formerIterator; - } - bool bPreviousTileNeedsRedraw = false; - for(; itrNode; ++itrNode) - { - bool bNeedsRedraw = false; - unsigned int iH; - unsigned int iBlock = itrNode->iBlock[2]; - if(iBlock != 0 && blocks->get_sprite_size(iBlock & 0xFF, - nullptr, &iH) && iH > 0) - { - blocks->draw_sprite(pCanvas, iBlock & 0xFF, itrNode.x() - 32, - itrNode.y() - iH + 32, iBlock >> 8); - } - iBlock = itrNode->iBlock[3]; - if(iBlock != 0 && blocks->get_sprite_size(iBlock & 0xFF, - nullptr, &iH) && iH > 0) - { - blocks->draw_sprite(pCanvas, iBlock & 0xFF, itrNode.x() - 32, - itrNode.y() - iH + 32, iBlock >> 8); - } - iBlock = itrNode->iBlock[1]; - if(iBlock != 0 && blocks->get_sprite_size(iBlock & 0xFF, - nullptr, &iH) && iH > 0) - bNeedsRedraw = true; - if(itrNode->oEarlyEntities.next) - bNeedsRedraw = true; - - bool bRedrawAnimations = false; - - drawable *pItem = (drawable*)(itrNode->next); - while(pItem) - { - pItem->draw_fn(pItem, pCanvas, itrNode.x(), itrNode.y()); - if(pItem->is_multiple_frame_animation_fn(pItem)) - bRedrawAnimations = true; - if(pItem->get_drawing_layer() == 1) - bNeedsRedraw = true; - pItem = (drawable*)(pItem->next); - } - - //if the current tile contained a multiple frame animation (e.g. a doctor walking) - //check to see if in the tile to its left and above it there are items that need to - //be redrawn (i.e. in the tile to its left side objects to the south of the tile and - //in the tile above it side objects to the east of the tile). - if(bRedrawAnimations && !bFirst) - { - bool bTileNeedsRedraw = bPreviousTileNeedsRedraw; - - //check if an object in the adjacent tile to the left of the current tile needs to be redrawn - //and if necessary draw it - pItem = (drawable*)(formerIterator.get_previous_tile()->next); - while(pItem) - { - if (pItem->get_drawing_layer() == 9) - { - pItem->draw_fn(pItem, pCanvas, formerIterator.x() - 64, formerIterator.y()); - bTileNeedsRedraw = true; - } - pItem = (drawable*)(pItem->next); - } - - //check if an object in the adjacent tile above the current tile needs to be redrawn - //and if necessary draw it - pItem = formerIterator ? (drawable*)(formerIterator->next) : nullptr; - while(pItem) - { - if(pItem->get_drawing_layer() == 8) - pItem->draw_fn(pItem, pCanvas, formerIterator.x(), formerIterator.y()); - pItem = (drawable*)(pItem->next); - } - - - //if an object was redrawn in the tile to the left of the current tile - //or if the tile below it had an object in the north side or a wall to the north - //redraw that tile - if(bTileNeedsRedraw) - { - //redraw the north wall - unsigned int iBlock = itrNode.get_previous_tile()->iBlock[1]; - if(iBlock != 0 && blocks->get_sprite_size(iBlock & 0xFF, - nullptr, &iH) && iH > 0) - { - blocks->draw_sprite(pCanvas, iBlock & 0xFF, itrNode.x() - 96, + for (map_scanline_iterator itrNode( + itrNode2, map_scanline_iterator_direction::backward, iCanvasX, + iCanvasY); + itrNode; ++itrNode) { + unsigned int iH; + unsigned int iBlock = itrNode->iBlock[1]; + if (iBlock != 0 && blocks->get_sprite_size(iBlock & 0xFF, nullptr, &iH) && + iH > 0) { + blocks->draw_sprite(pCanvas, iBlock & 0xFF, itrNode.x() - 32, itrNode.y() - iH + 32, iBlock >> 8); - if(itrNode.get_previous_tile()->flags.shadow_wall) - { - clip_rect rcOldClip, rcNewClip; - pCanvas->get_clip_rect(&rcOldClip); - rcNewClip.x = static_cast(itrNode.x() - 96); - rcNewClip.y = static_cast(itrNode.y() - iH + 32 + 4); - rcNewClip.w = static_cast(64); - rcNewClip.h = static_cast(86 - 4); - clip_rect_intersection(rcNewClip, rcOldClip); - pCanvas->set_clip_rect(&rcNewClip); - blocks->draw_sprite(pCanvas, 156, itrNode.x() - 96, - itrNode.y() - 56, thdf_alpha_75); - pCanvas->set_clip_rect(&rcOldClip); - } - } - pItem = (drawable*)(itrNode.get_previous_tile()->oEarlyEntities.next); - while(pItem) - { - pItem->draw_fn(pItem, pCanvas, itrNode.x() - 64, itrNode.y()); - pItem = (drawable*)(pItem->next); - } + if (itrNode->flags.shadow_wall) { + clip_rect rcOldClip, rcNewClip; + pCanvas->get_clip_rect(&rcOldClip); + rcNewClip.x = static_cast(itrNode.x() - 32); + rcNewClip.y = + static_cast(itrNode.y() - iH + 32 + 4); + rcNewClip.w = static_cast(64); + rcNewClip.h = static_cast(86 - 4); + clip_rect_intersection(rcNewClip, rcOldClip); + pCanvas->set_clip_rect(&rcNewClip); + blocks->draw_sprite(pCanvas, 156, itrNode.x() - 32, itrNode.y() - 56, + thdf_alpha_75); + pCanvas->set_clip_rect(&rcOldClip); + } + } + drawable* pItem = (drawable*)(itrNode->oEarlyEntities.next); + while (pItem) { + pItem->draw_fn(pItem, pCanvas, itrNode.x(), itrNode.y()); + pItem = (drawable*)(pItem->next); + } + } - pItem = (drawable*)(itrNode.get_previous_tile())->next; - for(; pItem; pItem = (drawable*)(pItem->next)) - pItem->draw_fn(pItem, pCanvas, itrNode.x() - 64, itrNode.y()); - } + map_scanline_iterator itrNode( + itrNode2, map_scanline_iterator_direction::forward, iCanvasX, iCanvasY); + if (!bFirst) { + // since the scanline count from one THMapScanlineIterator to + // another can differ synchronization between the current iterator + // and the former one is neeeded + if (itrNode.x() < -64) { + ++itrNode; + } + while (formerIterator.x() < itrNode.x()) { + ++formerIterator; + } + } + bool bPreviousTileNeedsRedraw = false; + for (; itrNode; ++itrNode) { + bool bNeedsRedraw = false; + unsigned int iH; + unsigned int iBlock = itrNode->iBlock[2]; + if (iBlock != 0 && blocks->get_sprite_size(iBlock & 0xFF, nullptr, &iH) && + iH > 0) { + blocks->draw_sprite(pCanvas, iBlock & 0xFF, itrNode.x() - 32, + itrNode.y() - iH + 32, iBlock >> 8); + } + iBlock = itrNode->iBlock[3]; + if (iBlock != 0 && blocks->get_sprite_size(iBlock & 0xFF, nullptr, &iH) && + iH > 0) { + blocks->draw_sprite(pCanvas, iBlock & 0xFF, itrNode.x() - 32, + itrNode.y() - iH + 32, iBlock >> 8); + } + iBlock = itrNode->iBlock[1]; + if (iBlock != 0 && blocks->get_sprite_size(iBlock & 0xFF, nullptr, &iH) && + iH > 0) { + bNeedsRedraw = true; + } + if (itrNode->oEarlyEntities.next) { + bNeedsRedraw = true; + } + + bool bRedrawAnimations = false; + + drawable* pItem = (drawable*)(itrNode->next); + while (pItem) { + pItem->draw_fn(pItem, pCanvas, itrNode.x(), itrNode.y()); + if (pItem->is_multiple_frame_animation_fn(pItem)) { + bRedrawAnimations = true; + } + if (pItem->get_drawing_layer() == 1) { + bNeedsRedraw = true; + } + pItem = (drawable*)(pItem->next); + } + + // if the current tile contained a multiple frame animation (e.g. a + // doctor walking) check to see if in the tile to its left and above + // it there are items that need to be redrawn (i.e. in the tile to + // its left side objects to the south of the tile and in the tile + // above it side objects to the east of the tile). + if (bRedrawAnimations && !bFirst) { + bool bTileNeedsRedraw = bPreviousTileNeedsRedraw; + + // check if an object in the adjacent tile to the left of the + // current tile needs to be redrawn and if necessary draw it + pItem = (drawable*)(formerIterator.get_previous_tile()->next); + while (pItem) { + if (pItem->get_drawing_layer() == 9) { + pItem->draw_fn(pItem, pCanvas, formerIterator.x() - 64, + formerIterator.y()); + bTileNeedsRedraw = true; + } + pItem = (drawable*)(pItem->next); + } + + // check if an object in the adjacent tile above the current + // tile needs to be redrawn and if necessary draw it + pItem = formerIterator ? (drawable*)(formerIterator->next) : nullptr; + while (pItem) { + if (pItem->get_drawing_layer() == 8) { + pItem->draw_fn(pItem, pCanvas, formerIterator.x(), + formerIterator.y()); + } + pItem = (drawable*)(pItem->next); + } + + // if an object was redrawn in the tile to the left of the + // current tile or if the tile below it had an object in the + // north side or a wall to the north redraw that tile + if (bTileNeedsRedraw) { + // redraw the north wall + unsigned int iBlock = itrNode.get_previous_tile()->iBlock[1]; + if (iBlock != 0 && + blocks->get_sprite_size(iBlock & 0xFF, nullptr, &iH) && iH > 0) { + blocks->draw_sprite(pCanvas, iBlock & 0xFF, itrNode.x() - 96, + itrNode.y() - iH + 32, iBlock >> 8); + if (itrNode.get_previous_tile()->flags.shadow_wall) { + clip_rect rcOldClip, rcNewClip; + pCanvas->get_clip_rect(&rcOldClip); + rcNewClip.x = static_cast(itrNode.x() - 96); + rcNewClip.y = + static_cast(itrNode.y() - iH + 32 + 4); + rcNewClip.w = static_cast(64); + rcNewClip.h = static_cast(86 - 4); + clip_rect_intersection(rcNewClip, rcOldClip); + pCanvas->set_clip_rect(&rcNewClip); + blocks->draw_sprite(pCanvas, 156, itrNode.x() - 96, + itrNode.y() - 56, thdf_alpha_75); + pCanvas->set_clip_rect(&rcOldClip); } - bPreviousTileNeedsRedraw = bNeedsRedraw; - if (!bFirst) ++formerIterator; - } + } + pItem = (drawable*)(itrNode.get_previous_tile()->oEarlyEntities.next); + while (pItem) { + pItem->draw_fn(pItem, pCanvas, itrNode.x() - 64, itrNode.y()); + pItem = (drawable*)(pItem->next); + } - formerIterator = itrNode; - bFirst = false; + pItem = (drawable*)(itrNode.get_previous_tile())->next; + for (; pItem; pItem = (drawable*)(pItem->next)) { + pItem->draw_fn(pItem, pCanvas, itrNode.x() - 64, itrNode.y()); + } + } + } + bPreviousTileNeedsRedraw = bNeedsRedraw; + if (!bFirst) { + ++formerIterator; + } } - if(overlay) - { - for(map_tile_iterator itrNode(this, iScreenX, iScreenY, iWidth, iHeight); itrNode; ++itrNode) - { - overlay->draw_cell(pCanvas, itrNode.tile_x_position_on_screen() + iCanvasX - 32, - itrNode.tile_y_position_on_screen() + iCanvasY, this, itrNode.tile_x(), itrNode.tile_y()); - } - } + formerIterator = itrNode; + bFirst = false; + } - pCanvas->set_clip_rect(nullptr); + if (overlay) { + for (map_tile_iterator itrNode(this, iScreenX, iScreenY, iWidth, iHeight); + itrNode; ++itrNode) { + overlay->draw_cell(pCanvas, + itrNode.tile_x_position_on_screen() + iCanvasX - 32, + itrNode.tile_y_position_on_screen() + iCanvasY, this, + itrNode.tile_x(), itrNode.tile_y()); + } + } + + pCanvas->set_clip_rect(nullptr); } -drawable* level_map::hit_test(int iTestX, int iTestY) const -{ - // This function needs to hitTest each drawable object, in the reverse - // order to that in which they would be drawn. +drawable* level_map::hit_test(int iTestX, int iTestY) const { + // This function needs to hitTest each drawable object, in the reverse + // order to that in which they would be drawn. - if(blocks == nullptr || cells == nullptr) - return nullptr; + if (blocks == nullptr || cells == nullptr) { + return nullptr; + } - for(map_tile_iterator itrNode2(this, iTestX, iTestY, 1, 1, map_scanline_iterator_direction::backward); itrNode2; ++itrNode2) - { - if(!itrNode2.is_last_on_scanline()) - continue; - - for(map_scanline_iterator itrNode(itrNode2, map_scanline_iterator_direction::backward); itrNode; ++itrNode) - { - if(itrNode->next != nullptr) - { - drawable* pResult = hit_test_drawables(itrNode->next, - itrNode.x(), itrNode.y(), 0, 0); - if(pResult) - return pResult; - } - } - for(map_scanline_iterator itrNode(itrNode2, map_scanline_iterator_direction::forward); itrNode; ++itrNode) - { - if(itrNode->oEarlyEntities.next != nullptr) - { - drawable* pResult = hit_test_drawables(itrNode->oEarlyEntities.next, - itrNode.x(), itrNode.y(), 0, 0); - if(pResult) - return pResult; - } - } + for (map_tile_iterator itrNode2(this, iTestX, iTestY, 1, 1, + map_scanline_iterator_direction::backward); + itrNode2; ++itrNode2) { + if (!itrNode2.is_last_on_scanline()) { + continue; } - return nullptr; + for (map_scanline_iterator itrNode( + itrNode2, map_scanline_iterator_direction::backward); + itrNode; ++itrNode) { + if (itrNode->next != nullptr) { + drawable* pResult = + hit_test_drawables(itrNode->next, itrNode.x(), itrNode.y(), 0, 0); + if (pResult) { + return pResult; + } + } + } + for (map_scanline_iterator itrNode( + itrNode2, map_scanline_iterator_direction::forward); + itrNode; ++itrNode) { + if (itrNode->oEarlyEntities.next != nullptr) { + drawable* pResult = hit_test_drawables(itrNode->oEarlyEntities.next, + itrNode.x(), itrNode.y(), 0, 0); + if (pResult) { + return pResult; + } + } + } + } + + return nullptr; } drawable* level_map::hit_test_drawables(link_list* pListStart, int iXs, int iYs, - int iTestX, int iTestY) const -{ - link_list* pListEnd = pListStart; - while(pListEnd->next) - pListEnd = pListEnd->next; - drawable* pList = (drawable*)pListEnd; + int iTestX, int iTestY) const { + link_list* pListEnd = pListStart; + while (pListEnd->next) { + pListEnd = pListEnd->next; + } + drawable* pList = (drawable*)pListEnd; - while(true) - { - if(pList->hit_test_fn(pList, iXs, iYs, iTestX, iTestY)) - return pList; + while (true) { + if (pList->hit_test_fn(pList, iXs, iYs, iTestX, iTestY)) return pList; - if(pList == pListStart) - return nullptr; - else - pList = (drawable*)pList->prev; - } -} - -int level_map::get_tile_owner(const map_tile* pNode) const -{ - return plot_owner[pNode->iParcelId]; -} - -int level_map::get_parcel_owner(int iParcel) const -{ - if(0 <= iParcel && iParcel < parcel_count) - return plot_owner[iParcel]; - else - return 0; -} - - -uint16_t level_map::get_tile_temperature(const map_tile* pNode) const -{ - return pNode->aiTemperature[current_temperature_index]; -} - -void level_map::set_temperature_display(temperature_theme eTempDisplay) -{ - current_temperature_theme = eTempDisplay; -} - -uint32_t level_map::thermal_neighbour(uint32_t &iNeighbourSum, bool canTravel, std::ptrdiff_t relative_idx, map_tile* pNode, int prevTemp) const -{ - int iNeighbourCount = 0; - - map_tile* pNeighbour = pNode + relative_idx; - - // Ensure the neighbour is within the map bounds - map_tile* pLimitNode = cells + width * height; - if (pNeighbour < cells || pNeighbour >= pLimitNode) { - return 0; - } - - if (canTravel) { - iNeighbourCount += 4; - iNeighbourSum += pNeighbour->aiTemperature[prevTemp] * 4; + if (pList == pListStart) { + return nullptr; } else { - bool bObjectPresent = false; - int iHospital1 = pNeighbour->flags.hospital; - int iHospital2 = pNode->flags.hospital; - if (iHospital1 == iHospital2) { - if (pNeighbour->flags.room == pNode->flags.room) { - bObjectPresent = true; - } - } - if (bObjectPresent) { - iNeighbourCount += 4; - iNeighbourSum += pNeighbour->aiTemperature[prevTemp] * 4; - } else { - iNeighbourCount += 1; - iNeighbourSum += pNeighbour->aiTemperature[prevTemp]; - } + pList = (drawable*)pList->prev; } + } +} - return iNeighbourCount; +int level_map::get_tile_owner(const map_tile* pNode) const { + return plot_owner[pNode->iParcelId]; +} + +int level_map::get_parcel_owner(int iParcel) const { + if (0 <= iParcel && iParcel < parcel_count) { + return plot_owner[iParcel]; + } else { + return 0; + } +} + +uint16_t level_map::get_tile_temperature(const map_tile* pNode) const { + return pNode->aiTemperature[current_temperature_index]; +} + +void level_map::set_temperature_display(temperature_theme eTempDisplay) { + current_temperature_theme = eTempDisplay; +} + +uint32_t level_map::thermal_neighbour(uint32_t& iNeighbourSum, bool canTravel, + std::ptrdiff_t relative_idx, + map_tile* pNode, int prevTemp) const { + int iNeighbourCount = 0; + + map_tile* pNeighbour = pNode + relative_idx; + + // Ensure the neighbour is within the map bounds + map_tile* pLimitNode = cells + width * height; + if (pNeighbour < cells || pNeighbour >= pLimitNode) { + return 0; + } + + if (canTravel) { + iNeighbourCount += 4; + iNeighbourSum += pNeighbour->aiTemperature[prevTemp] * 4; + } else { + bool bObjectPresent = false; + int iHospital1 = pNeighbour->flags.hospital; + int iHospital2 = pNode->flags.hospital; + if (iHospital1 == iHospital2) { + if (pNeighbour->flags.room == pNode->flags.room) { + bObjectPresent = true; + } + } + if (bObjectPresent) { + iNeighbourCount += 4; + iNeighbourSum += pNeighbour->aiTemperature[prevTemp] * 4; + } else { + iNeighbourCount += 1; + iNeighbourSum += pNeighbour->aiTemperature[prevTemp]; + } + } + + return iNeighbourCount; } namespace { -void merge_temperatures(map_tile& node, size_t new_temp_idx, int other_temp, double ratio) -{ - const uint32_t node_temp = node.aiTemperature[new_temp_idx]; - node.aiTemperature[new_temp_idx] = static_cast(((node_temp * (ratio - 1)) + other_temp) / ratio); +void merge_temperatures(map_tile& node, size_t new_temp_idx, int other_temp, + double ratio) { + const uint32_t node_temp = node.aiTemperature[new_temp_idx]; + node.aiTemperature[new_temp_idx] = + static_cast(((node_temp * (ratio - 1)) + other_temp) / ratio); } -} // namespace +} // namespace void level_map::update_temperatures(uint16_t iAirTemperature, - uint16_t iRadiatorTemperature) -{ - if(iRadiatorTemperature < iAirTemperature) { - iRadiatorTemperature = iAirTemperature; - } - const int iPrevTemp = current_temperature_index; - current_temperature_index ^= 1; - const int iNewTemp = current_temperature_index; + uint16_t iRadiatorTemperature) { + if (iRadiatorTemperature < iAirTemperature) { + iRadiatorTemperature = iAirTemperature; + } + const int iPrevTemp = current_temperature_index; + current_temperature_index ^= 1; + const int iNewTemp = current_temperature_index; - map_tile* pLimitNode = cells + width * height; - for(map_tile *pNode = cells; pNode != pLimitNode; ++pNode) { - // Get average temperature of neighbour cells - uint32_t iNeighbourSum = 0; - uint32_t iNeighbourCount = 0; + map_tile* pLimitNode = cells + width * height; + for (map_tile* pNode = cells; pNode != pLimitNode; ++pNode) { + // Get average temperature of neighbour cells + uint32_t iNeighbourSum = 0; + uint32_t iNeighbourCount = 0; - iNeighbourCount += thermal_neighbour(iNeighbourSum, pNode->flags.can_travel_n, -width, pNode, iPrevTemp); - iNeighbourCount += thermal_neighbour(iNeighbourSum, pNode->flags.can_travel_s, width, pNode, iPrevTemp); - iNeighbourCount += thermal_neighbour(iNeighbourSum, pNode->flags.can_travel_e, 1, pNode, iPrevTemp); - iNeighbourCount += thermal_neighbour(iNeighbourSum, pNode->flags.can_travel_w, -1, pNode, iPrevTemp); + iNeighbourCount += thermal_neighbour( + iNeighbourSum, pNode->flags.can_travel_n, -width, pNode, iPrevTemp); + iNeighbourCount += thermal_neighbour( + iNeighbourSum, pNode->flags.can_travel_s, width, pNode, iPrevTemp); + iNeighbourCount += thermal_neighbour( + iNeighbourSum, pNode->flags.can_travel_e, 1, pNode, iPrevTemp); + iNeighbourCount += thermal_neighbour( + iNeighbourSum, pNode->flags.can_travel_w, -1, pNode, iPrevTemp); - uint32_t iRadiatorNumber = 0; - // Merge 1% against air temperature - // or 50% against radiator temperature - // or generally dissipate 0.1% of temperature. - uint32_t iMergeTemp = 0; - double iMergeRatio = 100; - if(pNode->flags.hospital) { - for(auto thob : pNode->objects) { - if(thob == object_type::radiator) { - iRadiatorNumber++; - } - } - if(iRadiatorNumber > 0) { - iMergeTemp = iRadiatorTemperature; - iMergeRatio = 2 - (iRadiatorNumber - 1) * 0.5; - } else { - iMergeRatio = 1000; - } - } else { - iMergeTemp = iAirTemperature; + uint32_t iRadiatorNumber = 0; + // Merge 1% against air temperature + // or 50% against radiator temperature + // or generally dissipate 0.1% of temperature. + uint32_t iMergeTemp = 0; + double iMergeRatio = 100; + if (pNode->flags.hospital) { + for (auto thob : pNode->objects) { + if (thob == object_type::radiator) { + iRadiatorNumber++; } - - // Diffuse 25% with neighbours - pNode->aiTemperature[iNewTemp] = pNode->aiTemperature[iPrevTemp]; - if(iNeighbourCount != 0) { - merge_temperatures(*pNode, iNewTemp, iNeighbourSum / iNeighbourCount, 4 - (iRadiatorNumber > 0 ? (iRadiatorNumber - 1) * 1.5 : 0)); - } - - merge_temperatures(*pNode, iNewTemp, iMergeTemp, iMergeRatio); + } + if (iRadiatorNumber > 0) { + iMergeTemp = iRadiatorTemperature; + iMergeRatio = 2 - (iRadiatorNumber - 1) * 0.5; + } else { + iMergeRatio = 1000; + } + } else { + iMergeTemp = iAirTemperature; } + + // Diffuse 25% with neighbours + pNode->aiTemperature[iNewTemp] = pNode->aiTemperature[iPrevTemp]; + if (iNeighbourCount != 0) { + merge_temperatures( + *pNode, iNewTemp, iNeighbourSum / iNeighbourCount, + 4 - (iRadiatorNumber > 0 ? (iRadiatorNumber - 1) * 1.5 : 0)); + } + + merge_temperatures(*pNode, iNewTemp, iMergeTemp, iMergeRatio); + } } -void level_map::update_pathfinding() -{ - map_tile *pNode = cells; - for(int iY = 0; iY < 128; ++iY) - { - for(int iX = 0; iX < 128; ++iX, ++pNode) - { - pNode->flags.can_travel_n = true; - pNode->flags.can_travel_e = true; - pNode->flags.can_travel_s = true; - pNode->flags.can_travel_w = true; - if(iX == 0) - pNode->flags.can_travel_w = false; - else if(iX == 127) - pNode->flags.can_travel_e = false; - if(iY == 0) - pNode->flags.can_travel_n = false; - else if(iY == 127) - pNode->flags.can_travel_s = false; - if(pNode->iBlock[1] & 0xFF) - { - pNode->flags.can_travel_n = false; - if(iY != 0) - { - pNode[-128].flags.can_travel_s = false; - } - } - if(pNode->iBlock[2] & 0xFF) - { - pNode->flags.can_travel_w = false; - if(iX != 0) - { - pNode[-1].flags.can_travel_e = false; - } - } +void level_map::update_pathfinding() { + map_tile* pNode = cells; + for (int iY = 0; iY < 128; ++iY) { + for (int iX = 0; iX < 128; ++iX, ++pNode) { + pNode->flags.can_travel_n = true; + pNode->flags.can_travel_e = true; + pNode->flags.can_travel_s = true; + pNode->flags.can_travel_w = true; + + if (iX == 0) { + pNode->flags.can_travel_w = false; + } else if (iX == 127) { + pNode->flags.can_travel_e = false; + } + + if (iY == 0) { + pNode->flags.can_travel_n = false; + } else if (iY == 127) { + pNode->flags.can_travel_s = false; + } + + if (pNode->iBlock[1] & 0xFF) { + pNode->flags.can_travel_n = false; + if (iY != 0) { + pNode[-128].flags.can_travel_s = false; } + } + if (pNode->iBlock[2] & 0xFF) { + pNode->flags.can_travel_w = false; + if (iX != 0) { + pNode[-1].flags.can_travel_e = false; + } + } } + } } namespace { @@ -1391,419 +1419,375 @@ namespace { //! For shadow casting, a tile is considered to have a wall on a direction //! if it has a door in that direction, or the block is from the hardcoded //! range of wall-like blocks. -bool is_wall(map_tile *tile, size_t block, bool flag) -{ - return flag || (82 <= (tile->iBlock[block] & 0xFF) && (tile->iBlock[block] & 0xFF) <= 164); +bool is_wall(map_tile* tile, size_t block, bool flag) { + return flag || (82 <= (tile->iBlock[block] & 0xFF) && + (tile->iBlock[block] & 0xFF) <= 164); } -} // namespace +} // namespace -void level_map::update_shadows() -{ - map_tile *pNode = cells; - for(int iY = 0; iY < 128; ++iY) - { - for(int iX = 0; iX < 128; ++iX, ++pNode) - { - pNode->flags.shadow_full = false; - pNode->flags.shadow_half = false; - pNode->flags.shadow_wall = false; - if(is_wall(pNode, 2, pNode->flags.tall_west)) - { - pNode->flags.shadow_half = true; - if(is_wall(pNode, 1, pNode->flags.tall_north)) - { - pNode->flags.shadow_wall = true; - } - else if(iY != 0) - { - map_tile *pNeighbour = pNode - 128; - pNeighbour->flags.shadow_full = true; - if(iX != 0 && !is_wall(pNeighbour, 2, pNode->flags.tall_west)) - { - // Wrap the shadow around a corner (no need to continue - // all the way along the wall, as the shadow would be - // occluded by the wall. If Debug->Transparent Walls is - // toggled on, then this optimisation becomes very - // visible, but it's a debug option, so it doesn't - // matter). - pNeighbour[-1].flags.shadow_full = true; - } - } - } +void level_map::update_shadows() { + map_tile* pNode = cells; + for (int iY = 0; iY < 128; ++iY) { + for (int iX = 0; iX < 128; ++iX, ++pNode) { + pNode->flags.shadow_full = false; + pNode->flags.shadow_half = false; + pNode->flags.shadow_wall = false; + if (is_wall(pNode, 2, pNode->flags.tall_west)) { + pNode->flags.shadow_half = true; + if (is_wall(pNode, 1, pNode->flags.tall_north)) { + pNode->flags.shadow_wall = true; + } else if (iY != 0) { + map_tile* pNeighbour = pNode - 128; + pNeighbour->flags.shadow_full = true; + if (iX != 0 && !is_wall(pNeighbour, 2, pNode->flags.tall_west)) { + // Wrap the shadow around a corner (no need to continue + // all the way along the wall, as the shadow would be + // occluded by the wall. If Debug->Transparent Walls is + // toggled on, then this optimisation becomes very + // visible, but it's a debug option, so it doesn't + // matter). + pNeighbour[-1].flags.shadow_full = true; + } } + } } + } } -void level_map::persist(lua_persist_writer *pWriter) const -{ - lua_State *L = pWriter->get_stack(); - integer_run_length_encoder oEncoder; +void level_map::persist(lua_persist_writer* pWriter) const { + lua_State* L = pWriter->get_stack(); + integer_run_length_encoder oEncoder; - uint32_t iVersion = 4; - pWriter->write_uint(iVersion); - pWriter->write_uint(player_count); - for(int i = 0; i < player_count; ++i) - { - pWriter->write_uint(initial_camera_x[i]); - pWriter->write_uint(initial_camera_y[i]); - pWriter->write_uint(heliport_x[i]); - pWriter->write_uint(heliport_y[i]); - } - pWriter->write_uint(parcel_count); - for(int i = 0; i < parcel_count; ++i) - { - pWriter->write_uint(plot_owner[i]); - } - for(int i = 0; i < parcel_count; ++i) - { - pWriter->write_uint(parcel_tile_counts[i]); - } - pWriter->write_uint(width); - pWriter->write_uint(height); - pWriter->write_uint(current_temperature_index); - oEncoder.initialise(6); - for(map_tile *pNode = cells, *pLimitNode = cells + width * height; - pNode != pLimitNode; ++pNode) - { - oEncoder.write(pNode->iBlock[0]); - oEncoder.write(pNode->iBlock[1]); - oEncoder.write(pNode->iBlock[2]); - oEncoder.write(pNode->iBlock[3]); - oEncoder.write(pNode->iParcelId); - oEncoder.write(pNode->iRoomId); - // Flags include THOB values, and other things which do not work - // well with run-length encoding. - pWriter->write_uint(static_cast(pNode->flags)); - pWriter->write_uint(pNode->aiTemperature[0]); - pWriter->write_uint(pNode->aiTemperature[1]); + uint32_t iVersion = 4; + pWriter->write_uint(iVersion); + pWriter->write_uint(player_count); + for (int i = 0; i < player_count; ++i) { + pWriter->write_uint(initial_camera_x[i]); + pWriter->write_uint(initial_camera_y[i]); + pWriter->write_uint(heliport_x[i]); + pWriter->write_uint(heliport_y[i]); + } + pWriter->write_uint(parcel_count); + for (int i = 0; i < parcel_count; ++i) { + pWriter->write_uint(plot_owner[i]); + } + for (int i = 0; i < parcel_count; ++i) { + pWriter->write_uint(parcel_tile_counts[i]); + } + pWriter->write_uint(width); + pWriter->write_uint(height); + pWriter->write_uint(current_temperature_index); + oEncoder.initialise(6); + for (map_tile *pNode = cells, *pLimitNode = cells + width * height; + pNode != pLimitNode; ++pNode) { + oEncoder.write(pNode->iBlock[0]); + oEncoder.write(pNode->iBlock[1]); + oEncoder.write(pNode->iBlock[2]); + oEncoder.write(pNode->iBlock[3]); + oEncoder.write(pNode->iParcelId); + oEncoder.write(pNode->iRoomId); + // Flags include THOB values, and other things which do not work + // well with run-length encoding. + pWriter->write_uint(static_cast(pNode->flags)); + pWriter->write_uint(pNode->aiTemperature[0]); + pWriter->write_uint(pNode->aiTemperature[1]); - lua_rawgeti(L, luaT_upvalueindex(1), 2); - lua_pushlightuserdata(L, pNode->next); - lua_rawget(L, -2); - pWriter->write_stack_object(-1); - lua_pop(L, 1); - lua_pushlightuserdata(L, pNode->oEarlyEntities.next); - lua_rawget(L, -2); - pWriter->write_stack_object(-1); - lua_pop(L, 2); - } - oEncoder.finish(); - oEncoder.pump_output(pWriter); + lua_rawgeti(L, luaT_upvalueindex(1), 2); + lua_pushlightuserdata(L, pNode->next); + lua_rawget(L, -2); + pWriter->write_stack_object(-1); + lua_pop(L, 1); + lua_pushlightuserdata(L, pNode->oEarlyEntities.next); + lua_rawget(L, -2); + pWriter->write_stack_object(-1); + lua_pop(L, 2); + } + oEncoder.finish(); + oEncoder.pump_output(pWriter); - oEncoder.initialise(5); - for(map_tile *pNode = original_cells, *pLimitNode = original_cells + width * height; - pNode != pLimitNode; ++pNode) - { - oEncoder.write(pNode->iBlock[0]); - oEncoder.write(pNode->iBlock[1]); - oEncoder.write(pNode->iBlock[2]); - oEncoder.write(pNode->iParcelId); - oEncoder.write(static_cast(pNode->flags)); - } - oEncoder.finish(); - oEncoder.pump_output(pWriter); + oEncoder.initialise(5); + for (map_tile *pNode = original_cells, + *pLimitNode = original_cells + width * height; + pNode != pLimitNode; ++pNode) { + oEncoder.write(pNode->iBlock[0]); + oEncoder.write(pNode->iBlock[1]); + oEncoder.write(pNode->iBlock[2]); + oEncoder.write(pNode->iParcelId); + oEncoder.write(static_cast(pNode->flags)); + } + oEncoder.finish(); + oEncoder.pump_output(pWriter); } -void level_map::depersist(lua_persist_reader *pReader) -{ - new (this) level_map; // Call constructor +void level_map::depersist(lua_persist_reader* pReader) { + new (this) level_map; // Call constructor - lua_State *L = pReader->get_stack(); - int iWidth, iHeight; - integer_run_length_decoder oDecoder; + lua_State* L = pReader->get_stack(); + int iWidth, iHeight; + integer_run_length_decoder oDecoder; - uint32_t iVersion; - if(!pReader->read_uint(iVersion)) return; - if(iVersion != 4) - { - if(iVersion < 2 || iVersion == 128) - { - luaL_error(L, "TODO: Write code to load map data from earlier " - "savegame versions (if really necessary)."); - } - else if(iVersion > 4) - { - luaL_error(L, "Cannot load savegame from a newer version."); - } + uint32_t iVersion; + if (!pReader->read_uint(iVersion)) return; + if (iVersion != 4) { + if (iVersion < 2 || iVersion == 128) { + luaL_error(L, + "TODO: Write code to load map data from earlier " + "savegame versions (if really necessary)."); + } else if (iVersion > 4) { + luaL_error(L, "Cannot load savegame from a newer version."); } - if(!pReader->read_uint(player_count)) return; - for(int i = 0; i < player_count; ++i) - { - if(!pReader->read_uint(initial_camera_x[i])) return; - if(!pReader->read_uint(initial_camera_y[i])) return; - if(!pReader->read_uint(heliport_x[i])) return; - if(!pReader->read_uint(heliport_y[i])) return; + } + if (!pReader->read_uint(player_count)) return; + for (int i = 0; i < player_count; ++i) { + if (!pReader->read_uint(initial_camera_x[i])) return; + if (!pReader->read_uint(initial_camera_y[i])) return; + if (!pReader->read_uint(heliport_x[i])) return; + if (!pReader->read_uint(heliport_y[i])) return; + } + if (!pReader->read_uint(parcel_count)) { + return; + } + delete[] plot_owner; + plot_owner = new int[parcel_count]; + for (int i = 0; i < parcel_count; ++i) { + if (!pReader->read_uint(plot_owner[i])) { + return; } - if(!pReader->read_uint(parcel_count)) return; - delete[] plot_owner; - plot_owner = new int[parcel_count]; - for(int i = 0; i < parcel_count; ++i) - { - if(!pReader->read_uint(plot_owner[i])) return; - } - delete[] parcel_tile_counts; - parcel_tile_counts = new int[parcel_count]; - parcel_tile_counts[0] = 0; + } + delete[] parcel_tile_counts; + parcel_tile_counts = new int[parcel_count]; + parcel_tile_counts[0] = 0; - if(iVersion >= 3) - { - for(int i = 0; i < parcel_count; ++i) - { - if(!pReader->read_uint(parcel_tile_counts[i])) return; - } - } - - if(!pReader->read_uint(iWidth) || !pReader->read_uint(iHeight)) - return; - if(!set_size(iWidth, iHeight)) - { - pReader->set_error("Unable to set size while depersisting map"); + if (iVersion >= 3) { + for (int i = 0; i < parcel_count; ++i) { + if (!pReader->read_uint(parcel_tile_counts[i])) { return; + } } - if(iVersion >= 4) - { - if(!pReader->read_uint(current_temperature_index)) - return; - } - for(map_tile *pNode = cells, *pLimitNode = cells + width * height; - pNode != pLimitNode; ++pNode) - { - uint32_t f; - if(!pReader->read_uint(f)) return; - pNode->flags = f; - if(iVersion >= 4) - { - if(!pReader->read_uint(pNode->aiTemperature[0]) - || !pReader->read_uint(pNode->aiTemperature[1])) return; - } - if(!pReader->read_stack_object()) - return; - pNode->next = (link_list*)lua_touserdata(L, -1); - if(pNode->next) - { - if(pNode->next->prev != nullptr) - std::fprintf(stderr, "Warning: THMap linked-lists are corrupted.\n"); - pNode->next->prev = pNode; - } - lua_pop(L, 1); - if(!pReader->read_stack_object()) - return; - pNode->oEarlyEntities.next = (link_list*)lua_touserdata(L, -1); - if(pNode->oEarlyEntities.next) - { - if(pNode->oEarlyEntities.next->prev != nullptr) - std::fprintf(stderr, "Warning: THMap linked-lists are corrupted.\n"); - pNode->oEarlyEntities.next->prev = &pNode->oEarlyEntities; - } - lua_pop(L, 1); - } - oDecoder.initialise(6, pReader); - for(map_tile *pNode = cells, *pLimitNode = cells + width * height; - pNode != pLimitNode; ++pNode) - { - pNode->iBlock[0] = static_cast(oDecoder.read()); - pNode->iBlock[1] = static_cast(oDecoder.read()); - pNode->iBlock[2] = static_cast(oDecoder.read()); - pNode->iBlock[3] = static_cast(oDecoder.read()); - pNode->iParcelId = static_cast(oDecoder.read()); - pNode->iRoomId = static_cast(oDecoder.read()); - } - oDecoder.initialise(5, pReader); - for(map_tile *pNode = original_cells, *pLimitNode = original_cells + width * height; - pNode != pLimitNode; ++pNode) - { - pNode->iBlock[0] = static_cast(oDecoder.read()); - pNode->iBlock[1] = static_cast(oDecoder.read()); - pNode->iBlock[2] = static_cast(oDecoder.read()); - pNode->iParcelId = static_cast(oDecoder.read()); - pNode->flags = oDecoder.read(); - } + } - if(iVersion < 3) - { - for(int i = 1; i < parcel_count; ++i) - parcel_tile_counts[i] = get_parcel_tile_count(i); + if (!pReader->read_uint(iWidth) || !pReader->read_uint(iHeight)) { + return; + } + if (!set_size(iWidth, iHeight)) { + pReader->set_error("Unable to set size while depersisting map"); + return; + } + if (iVersion >= 4) { + if (!pReader->read_uint(current_temperature_index)) { + return; } + } + for (map_tile *pNode = cells, *pLimitNode = cells + width * height; + pNode != pLimitNode; ++pNode) { + uint32_t f; + if (!pReader->read_uint(f)) return; + pNode->flags = f; + if (iVersion >= 4) { + if (!pReader->read_uint(pNode->aiTemperature[0]) || + !pReader->read_uint(pNode->aiTemperature[1])) { + return; + } + } + if (!pReader->read_stack_object()) { + return; + } + pNode->next = (link_list*)lua_touserdata(L, -1); + if (pNode->next) { + if (pNode->next->prev != nullptr) { + std::fprintf(stderr, "Warning: THMap linked-lists are corrupted.\n"); + } + pNode->next->prev = pNode; + } + lua_pop(L, 1); + if (!pReader->read_stack_object()) return; + pNode->oEarlyEntities.next = (link_list*)lua_touserdata(L, -1); + if (pNode->oEarlyEntities.next) { + if (pNode->oEarlyEntities.next->prev != nullptr) { + std::fprintf(stderr, "Warning: THMap linked-lists are corrupted.\n"); + } + pNode->oEarlyEntities.next->prev = &pNode->oEarlyEntities; + } + lua_pop(L, 1); + } + oDecoder.initialise(6, pReader); + for (map_tile *pNode = cells, *pLimitNode = cells + width * height; + pNode != pLimitNode; ++pNode) { + pNode->iBlock[0] = static_cast(oDecoder.read()); + pNode->iBlock[1] = static_cast(oDecoder.read()); + pNode->iBlock[2] = static_cast(oDecoder.read()); + pNode->iBlock[3] = static_cast(oDecoder.read()); + pNode->iParcelId = static_cast(oDecoder.read()); + pNode->iRoomId = static_cast(oDecoder.read()); + } + oDecoder.initialise(5, pReader); + for (map_tile *pNode = original_cells, + *pLimitNode = original_cells + width * height; + pNode != pLimitNode; ++pNode) { + pNode->iBlock[0] = static_cast(oDecoder.read()); + pNode->iBlock[1] = static_cast(oDecoder.read()); + pNode->iBlock[2] = static_cast(oDecoder.read()); + pNode->iParcelId = static_cast(oDecoder.read()); + pNode->flags = oDecoder.read(); + } + + if (iVersion < 3) { + for (int i = 1; i < parcel_count; ++i) { + parcel_tile_counts[i] = get_parcel_tile_count(i); + } + } } map_tile_iterator::map_tile_iterator() - : tile(nullptr) - , container(nullptr) - , screen_offset_x(0) - , screen_offset_y(0) - , screen_width(0) - , screen_height(0) -{ + : tile(nullptr), + container(nullptr), + screen_offset_x(0), + screen_offset_y(0), + screen_width(0), + screen_height(0) {} + +map_tile_iterator::map_tile_iterator( + const level_map* pMap, int iScreenX, int iScreenY, int iWidth, int iHeight, + map_scanline_iterator_direction eScanlineDirection) + : container(pMap), + screen_offset_x(iScreenX), + screen_offset_y(iScreenY), + screen_width(iWidth), + screen_height(iHeight), + scanline_count(0), + direction(eScanlineDirection) { + if (direction == map_scanline_iterator_direction::forward) { + base_x = 0; + base_y = (iScreenY - 32) / 16; + if (base_y < 0) { + base_y = 0; + } else if (base_y >= container->get_height()) { + base_x = base_y - container->get_height() + 1; + base_y = container->get_height() - 1; + if (base_x >= container->get_width()) base_x = container->get_width() - 1; + } + } else { + base_x = container->get_width() - 1; + base_y = container->get_height() - 1; + } + world_x = base_x; + world_y = base_y; + advance_until_visible(); } -map_tile_iterator::map_tile_iterator(const level_map* pMap, int iScreenX, int iScreenY, - int iWidth, int iHeight, - map_scanline_iterator_direction eScanlineDirection) - : container(pMap) - , screen_offset_x(iScreenX) - , screen_offset_y(iScreenY) - , screen_width(iWidth) - , screen_height(iHeight) - , scanline_count(0) - , direction(eScanlineDirection) -{ - if(direction == map_scanline_iterator_direction::forward) - { - base_x = 0; - base_y = (iScreenY - 32) / 16; - if(base_y < 0) - base_y = 0; - else if(base_y >= container->get_height()) - { - base_x = base_y - container->get_height() + 1; - base_y = container->get_height() - 1; - if(base_x >= container->get_width()) - base_x = container->get_width() - 1; - } +map_tile_iterator& map_tile_iterator::operator++() { + --world_y; + ++world_x; + advance_until_visible(); + return *this; +} + +void map_tile_iterator::advance_until_visible() { + tile = nullptr; + + while (true) { + x_relative_to_screen = world_x; + y_relative_to_screen = world_y; + container->world_to_screen(x_relative_to_screen, y_relative_to_screen); + x_relative_to_screen -= screen_offset_x; + y_relative_to_screen -= screen_offset_y; + if (direction == map_scanline_iterator_direction::forward + ? y_relative_to_screen >= screen_height + margin_bottom + : y_relative_to_screen < -margin_top) { + return; } - else - { - base_x = container->get_width() - 1; - base_y = container->get_height() - 1; + if (direction == map_scanline_iterator_direction::forward + ? (y_relative_to_screen > -margin_top) + : (y_relative_to_screen < screen_height + margin_bottom)) { + while (world_y >= 0 && world_x < container->get_width()) { + if (x_relative_to_screen < -margin_left) { + // Nothing to do + } else if (x_relative_to_screen < screen_width + margin_right) { + ++scanline_count; + tile = container->get_tile_unchecked(world_x, world_y); + return; + } else { + break; + } + --world_y; + ++world_x; + x_relative_to_screen += 64; + } + } + scanline_count = 0; + if (direction == map_scanline_iterator_direction::forward) { + if (base_y == container->get_height() - 1) { + if (++base_x == container->get_width()) { + break; + } + } else { + ++base_y; + } + } else { + if (base_x == 0) { + if (base_y == 0) { + break; + } else { + --base_y; + } + } else { + --base_x; + } } world_x = base_x; world_y = base_y; - advance_until_visible(); + } } -map_tile_iterator& map_tile_iterator::operator ++ () -{ - --world_y; - ++world_x; - advance_until_visible(); - return *this; -} - -void map_tile_iterator::advance_until_visible() -{ - tile = nullptr; - - while(true) - { - x_relative_to_screen = world_x; - y_relative_to_screen = world_y; - container->world_to_screen(x_relative_to_screen, y_relative_to_screen); - x_relative_to_screen -= screen_offset_x; - y_relative_to_screen -= screen_offset_y; - if(direction == map_scanline_iterator_direction::forward ? - y_relative_to_screen >= screen_height + margin_bottom : - y_relative_to_screen < -margin_top) - { - return; - } - if(direction == map_scanline_iterator_direction::forward ? - (y_relative_to_screen > -margin_top) : - (y_relative_to_screen < screen_height + margin_bottom)) - { - while(world_y >= 0 && world_x < container->get_width()) - { - if(x_relative_to_screen < -margin_left) - { - // Nothing to do - } - else if(x_relative_to_screen < screen_width + margin_right) - { - ++scanline_count; - tile = container->get_tile_unchecked(world_x, world_y); - return; - } - else - break; - --world_y; - ++world_x; - x_relative_to_screen += 64; - } - } - scanline_count = 0; - if(direction == map_scanline_iterator_direction::forward) - { - if(base_y == container->get_height() - 1) - { - if(++base_x == container->get_width()) - break; - } - else - ++base_y; - } - else - { - if(base_x == 0) - { - if(base_y == 0) - break; - else - --base_y; - } - else - --base_x; - } - world_x = base_x; - world_y = base_y; - } -} - -bool map_tile_iterator::is_last_on_scanline() const -{ - return world_y <= 0 || world_x + 1 >= container->get_width() || - x_relative_to_screen + 64 >= screen_width + margin_right; +bool map_tile_iterator::is_last_on_scanline() const { + return world_y <= 0 || world_x + 1 >= container->get_width() || + x_relative_to_screen + 64 >= screen_width + margin_right; } map_scanline_iterator::map_scanline_iterator() - : tile_step(0) - , x_step(0) - , steps_taken(0) -{ + : tile_step(0), x_step(0), steps_taken(0) {} + +map_scanline_iterator::map_scanline_iterator( + const map_tile_iterator& itrNodes, + map_scanline_iterator_direction eDirection, int iXOffset, int iYOffset) + : tile_step((static_cast(eDirection) - 1) * + (1 - itrNodes.container->get_width())), + x_step((static_cast(eDirection) - 1) * 64), + steps_taken(0) { + if (eDirection == map_scanline_iterator_direction::backward) { + tile = itrNodes.tile; + x_relative_to_screen = itrNodes.tile_x_position_on_screen(); + } else { + tile = itrNodes.tile - tile_step * (itrNodes.scanline_count - 1); + x_relative_to_screen = itrNodes.tile_x_position_on_screen() - + x_step * (itrNodes.scanline_count - 1); + } + + x_relative_to_screen += iXOffset; + y_relative_to_screen = itrNodes.tile_y_position_on_screen() + iYOffset; + + end_tile = tile + tile_step * itrNodes.scanline_count; + first_tile = tile; } -map_scanline_iterator::map_scanline_iterator(const map_tile_iterator& itrNodes, - map_scanline_iterator_direction eDirection, - int iXOffset, int iYOffset) - : tile_step((static_cast(eDirection) - 1) * (1 - itrNodes.container->get_width())) - , x_step((static_cast(eDirection) - 1) * 64) - , steps_taken(0) -{ - if(eDirection == map_scanline_iterator_direction::backward) - { - tile = itrNodes.tile; - x_relative_to_screen = itrNodes.tile_x_position_on_screen(); - } - else - { - tile = itrNodes.tile - tile_step * (itrNodes.scanline_count - 1); - x_relative_to_screen = itrNodes.tile_x_position_on_screen() - x_step * (itrNodes.scanline_count - 1); - } - - x_relative_to_screen += iXOffset; - y_relative_to_screen = itrNodes.tile_y_position_on_screen() + iYOffset; - - end_tile = tile + tile_step * itrNodes.scanline_count; - first_tile = tile; - +map_scanline_iterator& map_scanline_iterator::operator++() { + tile += tile_step; + x_relative_to_screen += x_step; + steps_taken++; + return *this; } -map_scanline_iterator& map_scanline_iterator::operator ++ () -{ - tile += tile_step; - x_relative_to_screen += x_step; - steps_taken++; - return *this; +// copies the members of the given THMapScanlineIterator and resets the tile +// member to the first element. +map_scanline_iterator map_scanline_iterator::operator=( + const map_scanline_iterator& iterator) { + tile = iterator.first_tile; + end_tile = iterator.end_tile; + x_relative_to_screen = + iterator.x_relative_to_screen - iterator.steps_taken * iterator.x_step; + y_relative_to_screen = iterator.y_relative_to_screen; + x_step = iterator.x_step; + tile_step = iterator.tile_step; + return *this; } - -//copies the members of the given THMapScanlineIterator and resets the tile member to the -//first element. -map_scanline_iterator map_scanline_iterator::operator= (const map_scanline_iterator &iterator) - { - tile = iterator.first_tile; - end_tile = iterator.end_tile; - x_relative_to_screen = iterator.x_relative_to_screen - iterator.steps_taken * iterator.x_step; - y_relative_to_screen = iterator.y_relative_to_screen; - x_step = iterator.x_step; - tile_step = iterator.tile_step; - return *this; - } diff --git a/CorsixTH/Src/th_map.h b/CorsixTH/Src/th_map.h index 32836234..8c890abb 100644 --- a/CorsixTH/Src/th_map.h +++ b/CorsixTH/Src/th_map.h @@ -22,197 +22,194 @@ SOFTWARE. #ifndef CORSIX_TH_TH_MAP_H_ #define CORSIX_TH_TH_MAP_H_ -#include "th_gfx.h" #include #include +#include "th_gfx.h" /* Object type enumeration uses same values as original TH does. See game string table section 39 for proof. Section 1 also has names in this order. */ -enum class object_type : uint8_t -{ - no_object = 0, - desk = 1, - cabinet = 2, - door = 3, - bench = 4, - table = 5, // Not in game - chair = 6, - drinks_machine = 7, - bed = 8, - inflator = 9, - pool_table = 10, - reception_desk = 11, - b_table = 12, // Not in game? - cardio = 13, - scanner = 14, - scanner_console = 15, - screen = 16, - litter_bomb = 17, - couch = 18, - sofa = 19, - crash = 20, // The trolley in general diagnosis - tv = 21, - ultrascan = 22, - dna_fixer = 23, - cast_remover = 24, - hair_restorer = 25, - slicer = 26, - xray = 27, - radiation_shield = 28, - xray_viewer = 29, - op_table = 30, - lamp = 31, // Not in game? - sink = 32, - op_sink1 = 33, - op_sink2 = 34, - surgeon_screen = 35, - lecture_chair = 36, - projector = 37, - // 38 is unused - pharmacy = 39, - computer = 40, - chemical_mixer = 41, - blood_machine = 42, - extinguisher = 43, - radiator = 44, - plant = 45, - electro = 46, - jelly_vat = 47, - hell = 48, - // 49 is unused - bin = 50, - loo = 51, - double_door1 = 52, - double_door2 = 53, - decon_shower = 54, - autopsy = 55, - bookcase = 56, - video_game = 57, - entrance_left_door = 58, - entrance_right_door = 59, - skeleton = 60, - comfy_chair = 61, - litter = 62, - helicopter = 63, - rathole = 64, - // 65 through 255 are unused +enum class object_type : uint8_t { + no_object = 0, + desk = 1, + cabinet = 2, + door = 3, + bench = 4, + table = 5, // Not in game + chair = 6, + drinks_machine = 7, + bed = 8, + inflator = 9, + pool_table = 10, + reception_desk = 11, + b_table = 12, // Not in game? + cardio = 13, + scanner = 14, + scanner_console = 15, + screen = 16, + litter_bomb = 17, + couch = 18, + sofa = 19, + crash = 20, // The trolley in general diagnosis + tv = 21, + ultrascan = 22, + dna_fixer = 23, + cast_remover = 24, + hair_restorer = 25, + slicer = 26, + xray = 27, + radiation_shield = 28, + xray_viewer = 29, + op_table = 30, + lamp = 31, // Not in game? + sink = 32, + op_sink1 = 33, + op_sink2 = 34, + surgeon_screen = 35, + lecture_chair = 36, + projector = 37, + // 38 is unused + pharmacy = 39, + computer = 40, + chemical_mixer = 41, + blood_machine = 42, + extinguisher = 43, + radiator = 44, + plant = 45, + electro = 46, + jelly_vat = 47, + hell = 48, + // 49 is unused + bin = 50, + loo = 51, + double_door1 = 52, + double_door2 = 53, + decon_shower = 54, + autopsy = 55, + bookcase = 56, + video_game = 57, + entrance_left_door = 58, + entrance_right_door = 59, + skeleton = 60, + comfy_chair = 61, + litter = 62, + helicopter = 63, + rathole = 64, + // 65 through 255 are unused }; //! Map flags and object type //! The point of storing the object type here is to allow pathfinding code //! to use object types as pathfinding goals. -struct map_tile_flags -{ - enum class key : uint32_t { - passable_mask = 1 << 0, - can_travel_n_mask = 1 << 1, - can_travel_e_mask = 1 << 2, - can_travel_s_mask = 1 << 3, - can_travel_w_mask = 1 << 4, - hospital_mask = 1 << 5, - buildable_mask = 1 << 6, - passable_if_not_for_blueprint_mask = 1 << 7, - room_mask = 1 << 8, - shadow_half_mask = 1 << 9, - shadow_full_mask = 1 << 10, - shadow_wall_mask = 1 << 11, - door_north_mask = 1 << 12, - door_west_mask = 1 << 13, - do_not_idle_mask = 1 << 14, - tall_north_mask = 1 << 15, - tall_west_mask = 1 << 16, - buildable_n_mask = 1 << 17, - buildable_e_mask = 1 << 18, - buildable_s_mask = 1 << 19, - buildable_w_mask = 1 << 20, - }; +struct map_tile_flags { + enum class key : uint32_t { + passable_mask = 1 << 0, + can_travel_n_mask = 1 << 1, + can_travel_e_mask = 1 << 2, + can_travel_s_mask = 1 << 3, + can_travel_w_mask = 1 << 4, + hospital_mask = 1 << 5, + buildable_mask = 1 << 6, + passable_if_not_for_blueprint_mask = 1 << 7, + room_mask = 1 << 8, + shadow_half_mask = 1 << 9, + shadow_full_mask = 1 << 10, + shadow_wall_mask = 1 << 11, + door_north_mask = 1 << 12, + door_west_mask = 1 << 13, + do_not_idle_mask = 1 << 14, + tall_north_mask = 1 << 15, + tall_west_mask = 1 << 16, + buildable_n_mask = 1 << 17, + buildable_e_mask = 1 << 18, + buildable_s_mask = 1 << 19, + buildable_w_mask = 1 << 20, + }; - bool passable; //!< Pathfinding: Can walk on this tile - bool can_travel_n; //!< Pathfinding: Can walk to the north - bool can_travel_e; //!< Pathfinding: Can walk to the east - bool can_travel_s; //!< Pathfinding: Can walk to the south - bool can_travel_w; //!< Pathfinding: Can walk to the west - bool hospital; //!< World: Tile is inside a hospital building - bool buildable; //!< Player: Can build on this tile - bool passable_if_not_for_blueprint; - bool room; //!< World: Tile is inside a room - bool shadow_half; //!< Rendering: Put block 75 over floor - bool shadow_full; //!< Rendering: Put block 74 over floor - bool shadow_wall; //!< Rendering: Put block 156 over east wall - bool door_north; //!< World: Door on north wall of tile - bool door_west; //!< World: Door on west wall of tile - bool do_not_idle; //!< World: Humanoids should not idle on tile - bool tall_north; //!< Shadows: Wall-like object on north wall - bool tall_west; //!< Shadows: Wall-like object on west wall - bool buildable_n; //!< Can build on the north side of the tile - bool buildable_e; //!< Can build on the east side of the tile - bool buildable_s; //!< Can build on the south side of the tile - bool buildable_w; //!< Can build on the west side of the tile + bool passable; //!< Pathfinding: Can walk on this tile + bool can_travel_n; //!< Pathfinding: Can walk to the north + bool can_travel_e; //!< Pathfinding: Can walk to the east + bool can_travel_s; //!< Pathfinding: Can walk to the south + bool can_travel_w; //!< Pathfinding: Can walk to the west + bool hospital; //!< World: Tile is inside a hospital building + bool buildable; //!< Player: Can build on this tile + bool passable_if_not_for_blueprint; + bool room; //!< World: Tile is inside a room + bool shadow_half; //!< Rendering: Put block 75 over floor + bool shadow_full; //!< Rendering: Put block 74 over floor + bool shadow_wall; //!< Rendering: Put block 156 over east wall + bool door_north; //!< World: Door on north wall of tile + bool door_west; //!< World: Door on west wall of tile + bool do_not_idle; //!< World: Humanoids should not idle on tile + bool tall_north; //!< Shadows: Wall-like object on north wall + bool tall_west; //!< Shadows: Wall-like object on west wall + bool buildable_n; //!< Can build on the north side of the tile + bool buildable_e; //!< Can build on the east side of the tile + bool buildable_s; //!< Can build on the south side of the tile + bool buildable_w; //!< Can build on the west side of the tile - //! Convert the given uint32_t reprentation of the map_tile flags - //! to a map_tile_flags instance. - map_tile_flags& operator =(uint32_t raw); + //! Convert the given uint32_t reprentation of the map_tile flags + //! to a map_tile_flags instance. + map_tile_flags& operator=(uint32_t raw); - //! Get/set the flag with the given key - bool& operator[] (map_tile_flags::key key); + //! Get/set the flag with the given key + bool& operator[](map_tile_flags::key key); - //! Get the flag with the given key - const bool& operator[](map_tile_flags::key key) const; + //! Get the flag with the given key + const bool& operator[](map_tile_flags::key key) const; - //! Convert map_tile_flags into it's uint32_t representation - operator uint32_t() const; + //! Convert map_tile_flags into it's uint32_t representation + operator uint32_t() const; }; enum class temperature_theme { - red, //!< Default warmth colouring (red gradients) - multi_colour, //!< Different colours (blue, green, red) - yellow_red //!< Gradients of yellow, orange, and red + red, //!< Default warmth colouring (red gradients) + multi_colour, //!< Different colours (blue, green, red) + yellow_red //!< Gradients of yellow, orange, and red }; -struct map_tile : public link_list -{ - map_tile(); - ~map_tile(); +struct map_tile : public link_list { + map_tile(); + ~map_tile(); - // Linked list for entities rendered at this tile - // THLinkList::pPrev (will always be nullptr) - // THLinkList::pNext + // Linked list for entities rendered at this tile + // THLinkList::pPrev (will always be nullptr) + // THLinkList::pNext - //! Linked list for entities rendered in an early (right-to-left) pass - link_list oEarlyEntities; + //! Linked list for entities rendered in an early (right-to-left) pass + link_list oEarlyEntities; - //! Block tiles for rendering - //! For each tile, the lower byte is the index in the sprite sheet, and the - //! upper byte is for the drawing flags. - //! Layer 0 is for the floor - //! Layer 1 is for the north wall - //! Layer 2 is for the west wall - //! Layer 3 is for the UI - //! NB: In Lua, layers are numbered 1 - 4 rather than 0 - 3 - uint16_t iBlock[4]; + //! Block tiles for rendering + //! For each tile, the lower byte is the index in the sprite sheet, and the + //! upper byte is for the drawing flags. + //! Layer 0 is for the floor + //! Layer 1 is for the north wall + //! Layer 2 is for the west wall + //! Layer 3 is for the UI + //! NB: In Lua, layers are numbered 1 - 4 rather than 0 - 3 + uint16_t iBlock[4]; - //! Parcels (plots) of land have an ID, with each tile in the plot having - //! that ID. Parcel 0 is the outside. - uint16_t iParcelId; + //! Parcels (plots) of land have an ID, with each tile in the plot having + //! that ID. Parcel 0 is the outside. + uint16_t iParcelId; - //! Rooms have an ID, with room #0 being the corridor (and the outside). - uint16_t iRoomId; + //! Rooms have an ID, with room #0 being the corridor (and the outside). + uint16_t iRoomId; - //! A value between 0 (extreme cold) and 65535 (extreme heat) representing - //! the temperature of the tile. To allow efficient calculation of a tile's - //! heat based on the previous tick's heat of the surrounding tiles, the - //! previous temperature is also stored, with the array indices switching - //! every tick. - uint16_t aiTemperature[2]; + //! A value between 0 (extreme cold) and 65535 (extreme heat) representing + //! the temperature of the tile. To allow efficient calculation of a tile's + //! heat based on the previous tick's heat of the surrounding tiles, the + //! previous temperature is also stored, with the array indices switching + //! every tick. + uint16_t aiTemperature[2]; - //! Flags for information and object type - map_tile_flags flags; + //! Flags for information and object type + map_tile_flags flags; - //! objects in this tile - std::list objects; + //! objects in this tile + std::list objects; }; class sprite_sheet; @@ -226,218 +223,223 @@ class sprite_sheet; * The object flags present in the map data. The meaning of this value is left unspecified. */ -typedef void (*map_load_object_callback_fn)(void*, int, int, object_type, uint8_t); +typedef void (*map_load_object_callback_fn)(void*, int, int, object_type, + uint8_t); class map_overlay; -class level_map -{ -public: - level_map(); - ~level_map(); +class level_map { + public: + level_map(); + ~level_map(); - bool set_size(int iWidth, int iHeight); - bool load_blank(); - bool load_from_th_file(const uint8_t* pData, size_t iDataLength, - map_load_object_callback_fn fnObjectCallback, - void* pCallbackToken); + bool set_size(int iWidth, int iHeight); + bool load_blank(); + bool load_from_th_file(const uint8_t* pData, size_t iDataLength, + map_load_object_callback_fn fnObjectCallback, + void* pCallbackToken); - void save(std::string filename); + void save(std::string filename); - //! Set the sprite sheet to be used for drawing the map - /*! - The sprites for map floor tiles, wall tiles, and map decorators - all come from the given sheet. - */ - void set_block_sheet(sprite_sheet* pSheet); + //! Set the sprite sheet to be used for drawing the map + /*! + The sprites for map floor tiles, wall tiles, and map decorators + all come from the given sheet. + */ + void set_block_sheet(sprite_sheet* pSheet); - //! Set the draw flags on all wall blocks - /*! - This is typically called with THDF_Alpha50 to draw walls transparently, - or with 0 to draw them opaque again. - */ - void set_all_wall_draw_flags(uint8_t iFlags); + //! Set the draw flags on all wall blocks + /*! + This is typically called with THDF_Alpha50 to draw walls transparently, + or with 0 to draw them opaque again. + */ + void set_all_wall_draw_flags(uint8_t iFlags); - void update_pathfinding(); - void update_shadows(); - void set_temperature_display(temperature_theme eTempDisplay); - inline temperature_theme get_temperature_display() const {return current_temperature_theme;} - void update_temperatures(uint16_t iAirTemperature, - uint16_t iRadiatorTemperature); + void update_pathfinding(); + void update_shadows(); + void set_temperature_display(temperature_theme eTempDisplay); + inline temperature_theme get_temperature_display() const { + return current_temperature_theme; + } + void update_temperatures(uint16_t iAirTemperature, + uint16_t iRadiatorTemperature); - //! Get the map width (in tiles) - inline int get_width() const {return width;} + //! Get the map width (in tiles) + inline int get_width() const { return width; } - //! Get the map height (in tiles) - inline int get_height() const {return height;} + //! Get the map height (in tiles) + inline int get_height() const { return height; } - //! Get the number of plots of land in this map - inline int get_parcel_count() const {return parcel_count - 1;} + //! Get the number of plots of land in this map + inline int get_parcel_count() const { return parcel_count - 1; } - inline int get_player_count() const {return player_count;} + inline int get_player_count() const { return player_count; } - void set_player_count(int count); + void set_player_count(int count); - bool get_player_camera_tile(int iPlayer, int* pX, int* pY) const; - bool get_player_heliport_tile(int iPlayer, int* pX, int* pY) const; - void set_player_camera_tile(int iPlayer, int iX, int iY); - void set_player_heliport_tile(int iPlayer, int iX, int iY); + bool get_player_camera_tile(int iPlayer, int* pX, int* pY) const; + bool get_player_heliport_tile(int iPlayer, int* pX, int* pY) const; + void set_player_camera_tile(int iPlayer, int iX, int iY); + void set_player_heliport_tile(int iPlayer, int iX, int iY); - //! Get the number of tiles inside a given parcel - int get_parcel_tile_count(int iParcelId) const; + //! Get the number of tiles inside a given parcel + int get_parcel_tile_count(int iParcelId) const; - //! Change the owner of a particular parcel - /*! - \param iParcelId The parcel of land to change ownership of. Should be - an integer between 1 and getParcelCount() inclusive (parcel 0 is - the outside, and should never have its ownership changed). - \param iOwner The number of the player who should own the parcel, or - zero if no player should own the parcel. - \return vSplitTiles A vector that contains tile coordinates where - iParcelId is adjacent to another part of the hospital. - */ - std::vector> set_parcel_owner(int iParcelId, int iOwner); + //! Change the owner of a particular parcel + /*! + \param iParcelId The parcel of land to change ownership of. Should be + an integer between 1 and getParcelCount() inclusive (parcel 0 is + the outside, and should never have its ownership changed). + \param iOwner The number of the player who should own the parcel, or + zero if no player should own the parcel. + \return vSplitTiles A vector that contains tile coordinates where + iParcelId is adjacent to another part of the hospital. + */ + std::vector> set_parcel_owner(int iParcelId, int iOwner); - //! Get the owner of a particular parcel of land - /*! - \param iParcelId An integer between 0 and getParcelCount() inclusive. - \return 0 if the parcel is unowned, otherwise the number of the owning - player. - */ - int get_parcel_owner(int iParcelId) const; + //! Get the owner of a particular parcel of land + /*! + \param iParcelId An integer between 0 and getParcelCount() inclusive. + \return 0 if the parcel is unowned, otherwise the number of the owning + player. + */ + int get_parcel_owner(int iParcelId) const; - //! Query if two parcels are directly connected - /*! - \param iParcel1 An integer between 0 and getParcelCount() inclusive. - \param iParcel2 An integer between 0 and getParcelCount() inclusive. - \return true if there is a path between the two parcels which does not - go into any other parcels. false otherwise. - */ - bool are_parcels_adjacent(int iParcel1, int iParcel2); + //! Query if two parcels are directly connected + /*! + \param iParcel1 An integer between 0 and getParcelCount() inclusive. + \param iParcel2 An integer between 0 and getParcelCount() inclusive. + \return true if there is a path between the two parcels which does not + go into any other parcels. false otherwise. + */ + bool are_parcels_adjacent(int iParcel1, int iParcel2); - //! Query if a given player is in a position to purchase a given parcel - /*! - \param iParcelId The parcel of land to query. Should be an integer - between 1 and getParcelCount() inclusive. - \param iPlayer The number of the player to perform the query on behalf - of. Should be a strictly positive integer. - \return true if the parcel has a door to the outside, or is directly - connected to a parcel already owned by the given player. false - otherwise. - */ - bool is_parcel_purchasable(int iParcelId, int iPlayer); + //! Query if a given player is in a position to purchase a given parcel + /*! + \param iParcelId The parcel of land to query. Should be an integer + between 1 and getParcelCount() inclusive. + \param iPlayer The number of the player to perform the query on behalf + of. Should be a strictly positive integer. + \return true if the parcel has a door to the outside, or is directly + connected to a parcel already owned by the given player. false + otherwise. + */ + bool is_parcel_purchasable(int iParcelId, int iPlayer); - //! Draw the map (and any attached animations) - /*! - Draws the world pixel rectangle (iScreenX, iScreenY, iWidth, iHeight) - to the rectangle (iCanvasX, iCanvasY, iWidth, iHeight) on pCanvas. Note - that world pixel co-ordinates are also known as absolute screen - co-ordinates - they are not world (tile) co-ordinates, nor (relative) - screen co-ordinates. - */ - void draw(render_target* pCanvas, int iScreenX, int iScreenY, int iWidth, - int iHeight, int iCanvasX, int iCanvasY) const; + //! Draw the map (and any attached animations) + /*! + Draws the world pixel rectangle (iScreenX, iScreenY, iWidth, iHeight) + to the rectangle (iCanvasX, iCanvasY, iWidth, iHeight) on pCanvas. Note + that world pixel co-ordinates are also known as absolute screen + co-ordinates - they are not world (tile) co-ordinates, nor (relative) + screen co-ordinates. + */ + void draw(render_target* pCanvas, int iScreenX, int iScreenY, int iWidth, + int iHeight, int iCanvasX, int iCanvasY) const; - //! Perform a hit-test against the animations attached to the map - /*! - If there is an animation at world pixel co-ordinates (iTestX, iTestY), - then it is returned. Otherwise nullptr is returned. - To perform a hit-test using world (tile) co-ordinates, get the tile - itself and query the top 8 bits of map_tile::flags, or traverse the - tile's animation lists. - */ - drawable* hit_test(int iTestX, int iTestY) const; + //! Perform a hit-test against the animations attached to the map + /*! + If there is an animation at world pixel co-ordinates (iTestX, iTestY), + then it is returned. Otherwise nullptr is returned. + To perform a hit-test using world (tile) co-ordinates, get the tile + itself and query the top 8 bits of map_tile::flags, or traverse the + tile's animation lists. + */ + drawable* hit_test(int iTestX, int iTestY) const; - // When using the unchecked versions, the map co-ordinates MUST be valid. - // When using the normal versions, nullptr is returned for invalid co-ords. - map_tile* get_tile(int iX, int iY); - const map_tile* get_tile(int iX, int iY) const; - const map_tile* get_original_tile(int iX, int iY) const; - map_tile* get_tile_unchecked(int iX, int iY); - const map_tile* get_tile_unchecked(int iX, int iY) const; - const map_tile* get_original_tile_unchecked(int iX, int iY) const; + // When using the unchecked versions, the map co-ordinates MUST be valid. + // When using the normal versions, nullptr is returned for invalid co-ords. + map_tile* get_tile(int iX, int iY); + const map_tile* get_tile(int iX, int iY) const; + const map_tile* get_original_tile(int iX, int iY) const; + map_tile* get_tile_unchecked(int iX, int iY); + const map_tile* get_tile_unchecked(int iX, int iY) const; + const map_tile* get_original_tile_unchecked(int iX, int iY) const; - uint16_t get_tile_temperature(const map_tile* pNode) const; - int get_tile_owner(const map_tile* pNode) const; + uint16_t get_tile_temperature(const map_tile* pNode) const; + int get_tile_owner(const map_tile* pNode) const; - //! Convert world (tile) co-ordinates to absolute screen co-ordinates - template - static inline void world_to_screen(T& x, T& y) - { - T x_(x); - x = (T)32 * (x_ - y); - y = (T)16 * (x_ + y); - } + //! Convert world (tile) co-ordinates to absolute screen co-ordinates + template + static inline void world_to_screen(T& x, T& y) { + T x_(x); + x = (T)32 * (x_ - y); + y = (T)16 * (x_ + y); + } - //! Convert absolute screen co-ordinates to world (tile) co-ordinates - template - static inline void screen_to_world(T& x, T& y) - { - T x_(x); - x = y / (T)32 + x_ / (T)64; - y = y / (T)32 - x_ / (T)64; - } + //! Convert absolute screen co-ordinates to world (tile) co-ordinates + template + static inline void screen_to_world(T& x, T& y) { + T x_(x); + x = y / (T)32 + x_ / (T)64; + y = y / (T)32 - x_ / (T)64; + } - void persist(lua_persist_writer *pWriter) const; - void depersist(lua_persist_reader *pReader); + void persist(lua_persist_writer* pWriter) const; + void depersist(lua_persist_reader* pReader); - void set_overlay(map_overlay *pOverlay, bool bTakeOwnership); + void set_overlay(map_overlay* pOverlay, bool bTakeOwnership); -private: - drawable* hit_test_drawables(link_list* pListStart, int iXs, int iYs, - int iTestX, int iTestY) const; - void read_tile_index(const uint8_t* pData, int& iX, int &iY) const; - void write_tile_index(uint8_t* pData, int iX, int iY) const; + private: + drawable* hit_test_drawables(link_list* pListStart, int iXs, int iYs, + int iTestX, int iTestY) const; + void read_tile_index(const uint8_t* pData, int& iX, int& iY) const; + void write_tile_index(uint8_t* pData, int iX, int iY) const; - //! Calculate a weighted impact of a neighbour tile on the temperature of the current tile. - //! \param iNeighbourSum Incremented by the temperature of the tile multiplied by the weight of the connection. - //! \param canTravel A tile flag indicating whether travel between this tile and it's neighbour is allowed. - //! \param relative_idx The index of the neighbour tile, relative to this tile into cells. - //! \param pNode A pointer to the current tile being tested. - //! \param prevTemp The array index into map_tile::temperature that currently stores the temperature of the tile (prior to this calculation). - //! \return The weight of the connection, 0 if there is no neighbour, 1 through walls, and 4 through air. - uint32_t thermal_neighbour(uint32_t &iNeighbourSum, bool canTravel, std::ptrdiff_t relative_idx, map_tile* pNode, int prevTemp) const; + //! Calculate a weighted impact of a neighbour tile on the temperature of + //! the current tile. \param iNeighbourSum Incremented by the temperature of + //! the tile multiplied by the weight of the connection. \param canTravel A + //! tile flag indicating whether travel between this tile and it's neighbour + //! is allowed. \param relative_idx The index of the neighbour tile, + //! relative to this tile into cells. \param pNode A pointer to the current + //! tile being tested. \param prevTemp The array index into + //! map_tile::temperature that currently stores the temperature of the tile + //! (prior to this calculation). \return The weight of the connection, 0 if + //! there is no neighbour, 1 through walls, and 4 through air. + uint32_t thermal_neighbour(uint32_t& iNeighbourSum, bool canTravel, + std::ptrdiff_t relative_idx, map_tile* pNode, + int prevTemp) const; - //! Create the adjacency matrix if it doesn't already exist - void make_adjacency_matrix(); + //! Create the adjacency matrix if it doesn't already exist + void make_adjacency_matrix(); - //! Create the purchasability matrix if it doesn't already exist - void make_purchase_matrix(); + //! Create the purchasability matrix if it doesn't already exist + void make_purchase_matrix(); - //! If it exists, update the purchasability matrix. - void update_purchase_matrix(); + //! If it exists, update the purchasability matrix. + void update_purchase_matrix(); - int count_parcel_tiles(int iParcelId) const; + int count_parcel_tiles(int iParcelId) const; - map_tile* cells; - map_tile* original_cells; // Cells at map load time, before any changes - sprite_sheet* blocks; - map_overlay* overlay; - bool owns_overlay; - int* plot_owner; // 0 for unowned, 1 for player 1, etc. - int width; - int height; - int player_count; - int initial_camera_x[4]; - int initial_camera_y[4]; - int heliport_x[4]; - int heliport_y[4]; - int parcel_count; - int current_temperature_index; - temperature_theme current_temperature_theme; - int* parcel_tile_counts; + map_tile* cells; + map_tile* original_cells; // Cells at map load time, before any changes + sprite_sheet* blocks; + map_overlay* overlay; + bool owns_overlay; + int* plot_owner; // 0 for unowned, 1 for player 1, etc. + int width; + int height; + int player_count; + int initial_camera_x[4]; + int initial_camera_y[4]; + int heliport_x[4]; + int heliport_y[4]; + int parcel_count; + int current_temperature_index; + temperature_theme current_temperature_theme; + int* parcel_tile_counts; - // 2D symmetric array giving true if there is a path between two parcels - // which doesn't go into any other parcels. - bool* parcel_adjacency_matrix; + // 2D symmetric array giving true if there is a path between two parcels + // which doesn't go into any other parcels. + bool* parcel_adjacency_matrix; - // 4 by N matrix giving true if player can purchase parcel. - bool* purchasable_matrix; + // 4 by N matrix giving true if player can purchase parcel. + bool* purchasable_matrix; }; enum class map_scanline_iterator_direction { - forward = 2, - backward = 0, + forward = 2, + backward = 0, }; //! Utility class for iterating over map tiles within a screen rectangle @@ -451,126 +453,129 @@ enum class map_scanline_iterator_direction { tiles right-to-left, wait until isLastOnScanline() returns true, then use an instance of THMapScanlineIterator. */ -class map_tile_iterator -{ -public: - map_tile_iterator(); +class map_tile_iterator { + public: + map_tile_iterator(); - /*! - @arg pMap The map whose tiles should be iterated - @arg iScreenX The X co-ordinate of the top-left corner of the - screen-space rectangle to iterate. - @arg iScreenY The Y co-ordinate of the top-left corner of the - screen-space rectangle to iterate. - @arg iWidth The width of the screen-space rectangle to iterate. - @arg iHeight The width of the screen-space rectangle to iterate. - @arg eScanlineDirection The direction in which to iterate scanlines; - forward for top-to-bottom, backward for bottom-to-top. - */ - map_tile_iterator(const level_map*pMap, int iScreenX, int iScreenY, - int iWidth, int iHeight, - map_scanline_iterator_direction eScanlineDirection = map_scanline_iterator_direction::forward); + /*! + @arg pMap The map whose tiles should be iterated + @arg iScreenX The X co-ordinate of the top-left corner of the + screen-space rectangle to iterate. + @arg iScreenY The Y co-ordinate of the top-left corner of the + screen-space rectangle to iterate. + @arg iWidth The width of the screen-space rectangle to iterate. + @arg iHeight The width of the screen-space rectangle to iterate. + @arg eScanlineDirection The direction in which to iterate scanlines; + forward for top-to-bottom, backward for bottom-to-top. + */ + map_tile_iterator(const level_map* pMap, int iScreenX, int iScreenY, + int iWidth, int iHeight, + map_scanline_iterator_direction eScanlineDirection = + map_scanline_iterator_direction::forward); - //! Returns false iff the iterator has exhausted its tiles - inline operator bool () const {return tile != nullptr;} + //! Returns false iff the iterator has exhausted its tiles + inline operator bool() const { return tile != nullptr; } - //! Advances the iterator to the next tile - inline map_tile_iterator& operator ++ (); + //! Advances the iterator to the next tile + inline map_tile_iterator& operator++(); - //! Accessor for the current tile - inline const map_tile* operator -> () const {return tile;} + //! Accessor for the current tile + inline const map_tile* operator->() const { return tile; } - //! Get the X position of the tile relative to the top-left corner of the screen-space rectangle - inline int tile_x_position_on_screen() const {return x_relative_to_screen;} + //! Get the X position of the tile relative to the top-left corner of the + //! screen-space rectangle + inline int tile_x_position_on_screen() const { return x_relative_to_screen; } - //! Get the Y position of the tile relative to the top-left corner of the screen-space rectangle - inline int tile_y_position_on_screen() const {return y_relative_to_screen;} + //! Get the Y position of the tile relative to the top-left corner of the + //! screen-space rectangle + inline int tile_y_position_on_screen() const { return y_relative_to_screen; } - inline int tile_x() const {return world_x;} - inline int tile_y() const {return world_y;} + inline int tile_x() const { return world_x; } + inline int tile_y() const { return world_y; } - inline const level_map *get_map() {return container;} - inline const map_tile *get_map_tile() {return tile;} - inline int get_scanline_count() { return scanline_count;} - inline int get_tile_step() {return (static_cast(direction) - 1) * (1 - container->get_width());} + inline const level_map* get_map() { return container; } + inline const map_tile* get_map_tile() { return tile; } + inline int get_scanline_count() { return scanline_count; } + inline int get_tile_step() { + return (static_cast(direction) - 1) * (1 - container->get_width()); + } - //! Returns true iff the next tile will be on a different scanline - /*! - To visit a scanline in right-to-left order, or to revisit a scanline, - wait until this method returns true, then use a THMapScanlineIterator. - */ - inline bool is_last_on_scanline() const; + //! Returns true iff the next tile will be on a different scanline + /*! + To visit a scanline in right-to-left order, or to revisit a scanline, + wait until this method returns true, then use a THMapScanlineIterator. + */ + inline bool is_last_on_scanline() const; -private: - // Maximum extents of the visible parts of a tile (pixel distances relative - // to the top-most corner of an isometric cell) - // If set too low, things will disappear when near the screen edge - // If set too high, rendering will slow down - static const int margin_top = 150; - static const int margin_left = 110; - static const int margin_right = 110; - static const int margin_bottom = 150; + private: + // Maximum extents of the visible parts of a tile (pixel distances relative + // to the top-most corner of an isometric cell) + // If set too low, things will disappear when near the screen edge + // If set too high, rendering will slow down + static const int margin_top = 150; + static const int margin_left = 110; + static const int margin_right = 110; + static const int margin_bottom = 150; - friend class map_scanline_iterator; + friend class map_scanline_iterator; - const map_tile* tile; - const level_map* container; + const map_tile* tile; + const level_map* container; - // TODO: Consider removing these, they are trivial to calculate - int x_relative_to_screen; - int y_relative_to_screen; + // TODO: Consider removing these, they are trivial to calculate + int x_relative_to_screen; + int y_relative_to_screen; - const int screen_offset_x; - const int screen_offset_y; - const int screen_width; - const int screen_height; - int base_x; - int base_y; - int world_x; - int world_y; - int scanline_count; - map_scanline_iterator_direction direction; + const int screen_offset_x; + const int screen_offset_y; + const int screen_width; + const int screen_height; + int base_x; + int base_y; + int world_x; + int world_y; + int scanline_count; + map_scanline_iterator_direction direction; - void advance_until_visible(); + void advance_until_visible(); }; //! Utility class for re-iterating a scanline visited by a map_tile_iterator -class map_scanline_iterator -{ -public: - map_scanline_iterator(); +class map_scanline_iterator { + public: + map_scanline_iterator(); - /*! - @arg itrNodes A tile iterator which has reached the end of a scanline - @arg eDirection The direction in which to iterate the scanline; - forward for left-to-right, backward for right-to-left. - @arg iXOffset If given, values returned by x() will be offset by this. - @arg iYOffset If given, values returned by y() will be offset by this. - */ - map_scanline_iterator(const map_tile_iterator& itrNodes, - map_scanline_iterator_direction eDirection, - int iXOffset = 0, int iYOffset = 0); + /*! + @arg itrNodes A tile iterator which has reached the end of a scanline + @arg eDirection The direction in which to iterate the scanline; + forward for left-to-right, backward for right-to-left. + @arg iXOffset If given, values returned by x() will be offset by this. + @arg iYOffset If given, values returned by y() will be offset by this. + */ + map_scanline_iterator(const map_tile_iterator& itrNodes, + map_scanline_iterator_direction eDirection, + int iXOffset = 0, int iYOffset = 0); - inline operator bool () const {return tile != end_tile;} - inline map_scanline_iterator& operator ++ (); + inline operator bool() const { return tile != end_tile; } + inline map_scanline_iterator& operator++(); - inline const map_tile* operator -> () const {return tile;} - inline int x() const {return x_relative_to_screen;} - inline int y() const {return y_relative_to_screen;} - inline const map_tile* get_next_tile() {return tile + tile_step;} - inline const map_tile* get_previous_tile() { return tile - tile_step;} - map_scanline_iterator operator= (const map_scanline_iterator &iterator); - inline const map_tile* get_tile() {return tile;} + inline const map_tile* operator->() const { return tile; } + inline int x() const { return x_relative_to_screen; } + inline int y() const { return y_relative_to_screen; } + inline const map_tile* get_next_tile() { return tile + tile_step; } + inline const map_tile* get_previous_tile() { return tile - tile_step; } + map_scanline_iterator operator=(const map_scanline_iterator& iterator); + inline const map_tile* get_tile() { return tile; } -private: - const map_tile* tile; - const map_tile* first_tile; - const map_tile* end_tile; - int tile_step; - int x_step; - int x_relative_to_screen; - int y_relative_to_screen; - int steps_taken; + private: + const map_tile* tile; + const map_tile* first_tile; + const map_tile* end_tile; + int tile_step; + int x_step; + int x_relative_to_screen; + int y_relative_to_screen; + int steps_taken; }; -#endif // CORSIX_TH_TH_MAP_H_ +#endif // CORSIX_TH_TH_MAP_H_ diff --git a/CorsixTH/Src/th_map_overlays.cpp b/CorsixTH/Src/th_map_overlays.cpp index 1aef0d31..ec617015 100644 --- a/CorsixTH/Src/th_map_overlays.cpp +++ b/CorsixTH/Src/th_map_overlays.cpp @@ -21,210 +21,193 @@ SOFTWARE. */ #include "th_map_overlays.h" -#include "th_gfx.h" -#include "th_map.h" -#include #include +#include +#include "th_gfx.h" +#include "th_gfx_font.h" +#include "th_map.h" -map_overlay_pair::map_overlay_pair() -{ - first = nullptr; - second = nullptr; - owns_first = false; - owns_second = false; +map_overlay_pair::map_overlay_pair() { + first = nullptr; + second = nullptr; + owns_first = false; + owns_second = false; } -map_overlay_pair::~map_overlay_pair() -{ - set_first(nullptr, false); - set_second(nullptr, false); +map_overlay_pair::~map_overlay_pair() { + set_first(nullptr, false); + set_second(nullptr, false); } -void map_overlay_pair::set_first(map_overlay* pOverlay, bool bTakeOwnership) -{ - if(first && owns_first) - delete first; - first = pOverlay; - owns_first = bTakeOwnership; +void map_overlay_pair::set_first(map_overlay* pOverlay, bool bTakeOwnership) { + if (first && owns_first) { + delete first; + } + first = pOverlay; + owns_first = bTakeOwnership; } -void map_overlay_pair::set_second(map_overlay* pOverlay, bool bTakeOwnership) -{ - if(second && owns_second) - delete second; - second = pOverlay; - owns_second = bTakeOwnership; +void map_overlay_pair::set_second(map_overlay* pOverlay, bool bTakeOwnership) { + if (second && owns_second) { + delete second; + } + second = pOverlay; + owns_second = bTakeOwnership; } void map_overlay_pair::draw_cell(render_target* pCanvas, int iCanvasX, - int iCanvasY, const level_map* pMap, int iNodeX, - int iNodeY) -{ - if(first) - first->draw_cell(pCanvas, iCanvasX, iCanvasY, pMap, iNodeX, iNodeY); - if(second) - second->draw_cell(pCanvas, iCanvasX, iCanvasY, pMap, iNodeX, iNodeY); + int iCanvasY, const level_map* pMap, + int iNodeX, int iNodeY) { + if (first) { + first->draw_cell(pCanvas, iCanvasX, iCanvasY, pMap, iNodeX, iNodeY); + } + if (second) { + second->draw_cell(pCanvas, iCanvasX, iCanvasY, pMap, iNodeX, iNodeY); + } } -map_text_overlay::map_text_overlay() -{ - background_sprite = 0; -} +map_text_overlay::map_text_overlay() { background_sprite = 0; } -void map_text_overlay::set_background_sprite(size_t iSprite) -{ - background_sprite = iSprite; +void map_text_overlay::set_background_sprite(size_t iSprite) { + background_sprite = iSprite; } void map_text_overlay::draw_cell(render_target* pCanvas, int iCanvasX, - int iCanvasY, const level_map* pMap, int iNodeX, - int iNodeY) -{ - if(sprites && background_sprite) - { - sprites->draw_sprite(pCanvas, background_sprite, iCanvasX, - iCanvasY, 0); - } - if(font) - { - draw_text(pCanvas, iCanvasX, iCanvasY, get_text(pMap, iNodeX, iNodeY)); - } + int iCanvasY, const level_map* pMap, + int iNodeX, int iNodeY) { + if (sprites && background_sprite) { + sprites->draw_sprite(pCanvas, background_sprite, iCanvasX, iCanvasY, 0); + } + if (font) { + draw_text(pCanvas, iCanvasX, iCanvasY, get_text(pMap, iNodeX, iNodeY)); + } } -const std::string map_positions_overlay::get_text(const level_map* pMap, int iNodeX, int iNodeY) -{ - std::ostringstream str; - str << iNodeX + 1 << ',' << iNodeY + 1; - return str.str(); +const std::string map_positions_overlay::get_text(const level_map* pMap, + int iNodeX, int iNodeY) { + std::ostringstream str; + str << iNodeX + 1 << ',' << iNodeY + 1; + return str.str(); } -map_typical_overlay::map_typical_overlay() -{ - sprites = nullptr; - font = nullptr; - owns_sprites = false; - owns_font = false; +map_typical_overlay::map_typical_overlay() { + sprites = nullptr; + font = nullptr; + owns_sprites = false; + owns_font = false; } -map_typical_overlay::~map_typical_overlay() -{ - set_sprites(nullptr, false); - set_font(nullptr, false); +map_typical_overlay::~map_typical_overlay() { + set_sprites(nullptr, false); + set_font(nullptr, false); } void map_flags_overlay::draw_cell(render_target* pCanvas, int iCanvasX, - int iCanvasY, const level_map* pMap, int iNodeX, - int iNodeY) -{ - const map_tile *pNode = pMap->get_tile(iNodeX, iNodeY); - if(!pNode) - return; - if(sprites) - { - if(pNode->flags.passable) - sprites->draw_sprite(pCanvas, 3, iCanvasX, iCanvasY, 0); - if(pNode->flags.hospital) - sprites->draw_sprite(pCanvas, 8, iCanvasX, iCanvasY, 0); - if(pNode->flags.buildable) - sprites->draw_sprite(pCanvas, 9, iCanvasX, iCanvasY, 0); - if(pNode->flags.can_travel_n && pMap->get_tile(iNodeX, iNodeY - 1)->flags.passable) - { - sprites->draw_sprite(pCanvas, 4, iCanvasX, iCanvasY, 0); - } - if(pNode->flags.can_travel_e && pMap->get_tile(iNodeX + 1, iNodeY)->flags.passable) - { - sprites->draw_sprite(pCanvas, 5, iCanvasX, iCanvasY, 0); - } - if(pNode->flags.can_travel_s && pMap->get_tile(iNodeX, iNodeY + 1)->flags.passable) - { - sprites->draw_sprite(pCanvas, 6, iCanvasX, iCanvasY, 0); - } - if(pNode->flags.can_travel_w && pMap->get_tile(iNodeX - 1, iNodeY)->flags.passable) - { - sprites->draw_sprite(pCanvas, 7, iCanvasX, iCanvasY, 0); - } + int iCanvasY, const level_map* pMap, + int iNodeX, int iNodeY) { + const map_tile* pNode = pMap->get_tile(iNodeX, iNodeY); + if (!pNode) { + return; + } + if (sprites) { + if (pNode->flags.passable) { + sprites->draw_sprite(pCanvas, 3, iCanvasX, iCanvasY, 0); } - if(font) - { - if(!pNode->objects.empty()) - { - std::ostringstream str; - str << 'T' << static_cast(pNode->objects.front()); - draw_text(pCanvas, iCanvasX, iCanvasY - 8, str.str()); - } - if(pNode->iRoomId) - { - std::ostringstream str; - str << 'R' << static_cast(pNode->iRoomId); - draw_text(pCanvas, iCanvasX, iCanvasY + 8, str.str()); - } + if (pNode->flags.hospital) { + sprites->draw_sprite(pCanvas, 8, iCanvasX, iCanvasY, 0); } + if (pNode->flags.buildable) { + sprites->draw_sprite(pCanvas, 9, iCanvasX, iCanvasY, 0); + } + if (pNode->flags.can_travel_n && + pMap->get_tile(iNodeX, iNodeY - 1)->flags.passable) { + sprites->draw_sprite(pCanvas, 4, iCanvasX, iCanvasY, 0); + } + if (pNode->flags.can_travel_e && + pMap->get_tile(iNodeX + 1, iNodeY)->flags.passable) { + sprites->draw_sprite(pCanvas, 5, iCanvasX, iCanvasY, 0); + } + if (pNode->flags.can_travel_s && + pMap->get_tile(iNodeX, iNodeY + 1)->flags.passable) { + sprites->draw_sprite(pCanvas, 6, iCanvasX, iCanvasY, 0); + } + if (pNode->flags.can_travel_w && + pMap->get_tile(iNodeX - 1, iNodeY)->flags.passable) { + sprites->draw_sprite(pCanvas, 7, iCanvasX, iCanvasY, 0); + } + } + if (font) { + if (!pNode->objects.empty()) { + std::ostringstream str; + str << 'T' << static_cast(pNode->objects.front()); + draw_text(pCanvas, iCanvasX, iCanvasY - 8, str.str()); + } + if (pNode->iRoomId) { + std::ostringstream str; + str << 'R' << static_cast(pNode->iRoomId); + draw_text(pCanvas, iCanvasX, iCanvasY + 8, str.str()); + } + } } namespace { // The sprite to include if the tile in the direction indicated by dx,dy is // not from the same parcel. -struct parcel_edge_sprite -{ - int dx; - int dy; - size_t sprite; +struct parcel_edge_sprite { + int dx; + int dy; + size_t sprite; }; // Parcel edge sprites for all four directions -constexpr std::array parcel_edges {{ - {0, -1, 18}, - {1, 0, 19}, - {0, 1, 20}, - {-1, 0, 21} -}}; +constexpr std::array parcel_edges{ + {{0, -1, 18}, {1, 0, 19}, {0, 1, 20}, {-1, 0, 21}}}; -} +} // namespace void map_parcels_overlay::draw_cell(render_target* pCanvas, int iCanvasX, - int iCanvasY, const level_map* pMap, int iNodeX, - int iNodeY) -{ - const map_tile *pNode = pMap->get_tile(iNodeX, iNodeY); - if(!pNode) - return; - if(font) - draw_text(pCanvas, iCanvasX, iCanvasY, std::to_string((int)pNode->iParcelId)); - if(sprites) - { - for(const parcel_edge_sprite& dir_sprite : parcel_edges) - { - const map_tile* dir_node = pMap->get_tile(iNodeX + dir_sprite.dx, iNodeY + dir_sprite.dy); - if (!dir_node || dir_node->iParcelId == pNode->iParcelId) - { - sprites->draw_sprite(pCanvas, dir_sprite.sprite, iCanvasX, iCanvasY, 0); - } - } + int iCanvasY, const level_map* pMap, + int iNodeX, int iNodeY) { + const map_tile* pNode = pMap->get_tile(iNodeX, iNodeY); + if (!pNode) { + return; + } + if (font) { + draw_text(pCanvas, iCanvasX, iCanvasY, + std::to_string((int)pNode->iParcelId)); + } + if (sprites) { + for (const parcel_edge_sprite& dir_sprite : parcel_edges) { + const map_tile* dir_node = + pMap->get_tile(iNodeX + dir_sprite.dx, iNodeY + dir_sprite.dy); + if (!dir_node || dir_node->iParcelId == pNode->iParcelId) { + sprites->draw_sprite(pCanvas, dir_sprite.sprite, iCanvasX, iCanvasY, 0); + } } + } } - void map_typical_overlay::draw_text(render_target* pCanvas, int iX, int iY, - std::string str) -{ - text_layout oArea = font->get_text_dimensions(str.c_str(), str.length()); - font->draw_text(pCanvas, str.c_str(), str.length(), iX + (64 - oArea.end_x) / 2, - iY + (32 - oArea.end_y) / 2); + std::string str) { + text_layout oArea = font->get_text_dimensions(str.c_str(), str.length()); + font->draw_text(pCanvas, str.c_str(), str.length(), + iX + (64 - oArea.end_x) / 2, iY + (32 - oArea.end_y) / 2); } -void map_typical_overlay::set_sprites(sprite_sheet* pSheet, bool bTakeOwnership) -{ - if(sprites && owns_sprites) - delete sprites; - sprites = pSheet; - owns_sprites = bTakeOwnership; +void map_typical_overlay::set_sprites(sprite_sheet* pSheet, + bool bTakeOwnership) { + if (sprites && owns_sprites) { + delete sprites; + } + sprites = pSheet; + owns_sprites = bTakeOwnership; } -void map_typical_overlay::set_font(::font* font, bool take_ownership) -{ - if(this->font && owns_font) - delete this->font; - this->font = font; - owns_font = take_ownership; +void map_typical_overlay::set_font(::font* font, bool take_ownership) { + if (this->font && owns_font) { + delete this->font; + } + this->font = font; + owns_font = take_ownership; } diff --git a/CorsixTH/Src/th_map_overlays.h b/CorsixTH/Src/th_map_overlays.h index 2aca7740..2e483b0d 100644 --- a/CorsixTH/Src/th_map_overlays.h +++ b/CorsixTH/Src/th_map_overlays.h @@ -31,86 +31,81 @@ class level_map; class render_target; class sprite_sheet; -class map_overlay -{ -public: - virtual ~map_overlay() = default; +class map_overlay { + public: + virtual ~map_overlay() = default; - virtual void draw_cell(render_target* pCanvas, int iCanvasX, int iCanvasY, - const level_map* pMap, int iNodeX, int iNodeY) = 0; + virtual void draw_cell(render_target* pCanvas, int iCanvasX, int iCanvasY, + const level_map* pMap, int iNodeX, int iNodeY) = 0; }; -class map_overlay_pair : public map_overlay -{ -public: - map_overlay_pair(); - virtual ~map_overlay_pair(); +class map_overlay_pair : public map_overlay { + public: + map_overlay_pair(); + virtual ~map_overlay_pair(); - void set_first(map_overlay* pOverlay, bool bTakeOwnership); - void set_second(map_overlay* pOverlay, bool bTakeOwnership); + void set_first(map_overlay* pOverlay, bool bTakeOwnership); + void set_second(map_overlay* pOverlay, bool bTakeOwnership); - void draw_cell(render_target* pCanvas, int iCanvasX, int iCanvasY, - const level_map* pMap, int iNodeX, int iNodeY) override; + void draw_cell(render_target* pCanvas, int iCanvasX, int iCanvasY, + const level_map* pMap, int iNodeX, int iNodeY) override; -private: - map_overlay *first, *second; - bool owns_first, owns_second; + private: + map_overlay *first, *second; + bool owns_first, owns_second; }; -class map_typical_overlay : public map_overlay -{ -public: - map_typical_overlay(); - virtual ~map_typical_overlay(); +class map_typical_overlay : public map_overlay { + public: + map_typical_overlay(); + virtual ~map_typical_overlay(); - void set_sprites(sprite_sheet* pSheet, bool bTakeOwnership); - void set_font(::font* font, bool take_ownership); + void set_sprites(sprite_sheet* pSheet, bool bTakeOwnership); + void set_font(::font* font, bool take_ownership); -protected: - void draw_text(render_target* pCanvas, int iX, int iY, std::string str); + protected: + void draw_text(render_target* pCanvas, int iX, int iY, std::string str); - sprite_sheet* sprites; - ::font* font; + sprite_sheet* sprites; + ::font* font; -private: - bool owns_sprites; - bool owns_font; + private: + bool owns_sprites; + bool owns_font; }; -class map_text_overlay : public map_typical_overlay -{ -public: - map_text_overlay(); - virtual ~map_text_overlay() = default; +class map_text_overlay : public map_typical_overlay { + public: + map_text_overlay(); + virtual ~map_text_overlay() = default; - virtual void draw_cell(render_target* pCanvas, int iCanvasX, int iCanvasY, - const level_map* pMap, int iNodeX, int iNodeY); + virtual void draw_cell(render_target* pCanvas, int iCanvasX, int iCanvasY, + const level_map* pMap, int iNodeX, int iNodeY); - void set_background_sprite(size_t iSprite); - virtual const std::string get_text(const level_map* pMap, int iNodeX, int iNodeY) = 0; + void set_background_sprite(size_t iSprite); + virtual const std::string get_text(const level_map* pMap, int iNodeX, + int iNodeY) = 0; -private: - size_t background_sprite; + private: + size_t background_sprite; }; -class map_positions_overlay final : public map_text_overlay -{ -public: - const std::string get_text(const level_map* pMap, int iNodeX, int iNodeY) override; +class map_positions_overlay final : public map_text_overlay { + public: + const std::string get_text(const level_map* pMap, int iNodeX, + int iNodeY) override; }; -class map_flags_overlay final : public map_typical_overlay -{ -public: - void draw_cell(render_target* pCanvas, int iCanvasX, int iCanvasY, - const level_map* pMap, int iNodeX, int iNodeY) override; +class map_flags_overlay final : public map_typical_overlay { + public: + void draw_cell(render_target* pCanvas, int iCanvasX, int iCanvasY, + const level_map* pMap, int iNodeX, int iNodeY) override; }; -class map_parcels_overlay final : public map_typical_overlay -{ -public: - void draw_cell(render_target* pCanvas, int iCanvasX, int iCanvasY, - const level_map* pMap, int iNodeX, int iNodeY) override; +class map_parcels_overlay final : public map_typical_overlay { + public: + void draw_cell(render_target* pCanvas, int iCanvasX, int iCanvasY, + const level_map* pMap, int iNodeX, int iNodeY) override; }; #endif diff --git a/CorsixTH/Src/th_movie.cpp b/CorsixTH/Src/th_movie.cpp index 76de1647..f4e3ff70 100644 --- a/CorsixTH/Src/th_movie.cpp +++ b/CorsixTH/Src/th_movie.cpp @@ -23,19 +23,21 @@ SOFTWARE. #include "th_movie.h" #include "config.h" #include "lua_sdl.h" -#if (defined(CORSIX_TH_USE_FFMPEG) || defined(CORSIX_TH_USE_LIBAV)) && defined(CORSIX_TH_USE_SDL_MIXER) +#if (defined(CORSIX_TH_USE_FFMPEG) || defined(CORSIX_TH_USE_LIBAV)) && \ + defined(CORSIX_TH_USE_SDL_MIXER) #include "th_gfx.h" -extern "C" -{ - #include - #include - #include - #include - #include -#if (defined(CORSIX_TH_USE_LIBAV) && LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(54, 6, 0)) || \ - (defined(CORSIX_TH_USE_FFMPEG) && LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(51, 63, 100)) - #include +extern "C" { +#include +#include +#include +#include +#include +#if (defined(CORSIX_TH_USE_LIBAV) && \ + LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(54, 6, 0)) || \ + (defined(CORSIX_TH_USE_FFMPEG) && \ + LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(51, 63, 100)) +#include #endif } #include @@ -43,1113 +45,999 @@ extern "C" #include #include -#if (defined(CORSIX_TH_USE_LIBAV) && LIBAVCODEC_VERSION_INT < AV_VERSION_INT(57, 7, 0)) || \ - (defined(CORSIX_TH_USE_FFMPEG) && LIBAVCODEC_VERSION_INT < AV_VERSION_INT(57, 12, 100)) +#if (defined(CORSIX_TH_USE_LIBAV) && \ + LIBAVCODEC_VERSION_INT < AV_VERSION_INT(57, 7, 0)) || \ + (defined(CORSIX_TH_USE_FFMPEG) && \ + LIBAVCODEC_VERSION_INT < AV_VERSION_INT(57, 12, 100)) #define av_packet_unref av_free_packet #endif -#if (defined(CORSIX_TH_USE_LIBAV) && LIBAVCODEC_VERSION_INT < AV_VERSION_INT(55, 28, 1)) || \ - (defined(CORSIX_TH_USE_FFMPEG) && LIBAVCODEC_VERSION_INT < AV_VERSION_INT(55, 45, 101)) +#if (defined(CORSIX_TH_USE_LIBAV) && \ + LIBAVCODEC_VERSION_INT < AV_VERSION_INT(55, 28, 1)) || \ + (defined(CORSIX_TH_USE_FFMPEG) && \ + LIBAVCODEC_VERSION_INT < AV_VERSION_INT(55, 45, 101)) #define av_frame_alloc avcodec_alloc_frame #define av_frame_unref avcodec_get_frame_defaults #define av_frame_free avcodec_free_frame #endif -#if (defined(CORSIX_TH_USE_LIBAV) && LIBAVCODEC_VERSION_INT < AV_VERSION_INT(55, 52, 0)) || \ - (defined(CORSIX_TH_USE_FFMPEG) && LIBAVCODEC_VERSION_INT < AV_VERSION_INT(55, 63, 100)) +#if (defined(CORSIX_TH_USE_LIBAV) && \ + LIBAVCODEC_VERSION_INT < AV_VERSION_INT(55, 52, 0)) || \ + (defined(CORSIX_TH_USE_FFMPEG) && \ + LIBAVCODEC_VERSION_INT < AV_VERSION_INT(55, 63, 100)) void avcodec_free_context(AVCodecContext** ctx) { - avcodec_close(*ctx); - av_free(*ctx); + avcodec_close(*ctx); + av_free(*ctx); } #endif namespace { -void th_movie_audio_callback(int iChannel, void *pStream, int iStreamSize, void *pUserData) -{ - movie_player *pMovie = (movie_player *)pUserData; - pMovie->copy_audio_to_stream((uint8_t*)pStream, iStreamSize); +void th_movie_audio_callback(int iChannel, void* pStream, int iStreamSize, + void* pUserData) { + movie_player* pMovie = (movie_player*)pUserData; + pMovie->copy_audio_to_stream((uint8_t*)pStream, iStreamSize); } -} // namespace +} // namespace -movie_picture::movie_picture(): - buffer(nullptr), - pixel_format(AV_PIX_FMT_RGB24), - mutex{} -{} +movie_picture::movie_picture() + : buffer(nullptr), pixel_format(AV_PIX_FMT_RGB24), mutex{} {} -movie_picture::~movie_picture() -{ - av_freep(&buffer); -} +movie_picture::~movie_picture() { av_freep(&buffer); } -void movie_picture::allocate(int iWidth, int iHeight) -{ - width = iWidth; - height = iHeight; - av_freep(&buffer); -#if (defined(CORSIX_TH_USE_LIBAV) && LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(54, 6, 0)) || \ - (defined(CORSIX_TH_USE_FFMPEG) && LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(51, 63, 100)) - int numBytes = av_image_get_buffer_size(pixel_format, width, height, 1); +void movie_picture::allocate(int iWidth, int iHeight) { + width = iWidth; + height = iHeight; + av_freep(&buffer); +#if (defined(CORSIX_TH_USE_LIBAV) && \ + LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(54, 6, 0)) || \ + (defined(CORSIX_TH_USE_FFMPEG) && \ + LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(51, 63, 100)) + int numBytes = av_image_get_buffer_size(pixel_format, width, height, 1); #else - int numBytes = avpicture_get_size(pixel_format, width, height); + int numBytes = avpicture_get_size(pixel_format, width, height); #endif - buffer = static_cast(av_mallocz(numBytes)); + buffer = static_cast(av_mallocz(numBytes)); } -void movie_picture::deallocate() -{ - av_freep(&buffer); +void movie_picture::deallocate() { av_freep(&buffer); } + +movie_picture_buffer::movie_picture_buffer() + : aborting(false), + allocated(false), + picture_count(0), + read_index(0), + write_index(0), + sws_context(nullptr), + texture(nullptr), + mutex{}, + cond{} {} + +movie_picture_buffer::~movie_picture_buffer() { + sws_freeContext(sws_context); + if (texture) { + SDL_DestroyTexture(texture); + texture = nullptr; + } } -movie_picture_buffer::movie_picture_buffer(): - aborting(false), - allocated(false), - picture_count(0), - read_index(0), - write_index(0), - sws_context(nullptr), - texture(nullptr), - mutex{}, - cond{} -{ +void movie_picture_buffer::abort() { + aborting = true; + std::lock_guard lock(mutex); + cond.notify_all(); } -movie_picture_buffer::~movie_picture_buffer() -{ - sws_freeContext(sws_context); - if (texture) - { - SDL_DestroyTexture(texture); - texture = nullptr; - } +void movie_picture_buffer::reset() { aborting = false; } + +void movie_picture_buffer::allocate(SDL_Renderer* pRenderer, int iWidth, + int iHeight) { + if (texture) { + SDL_DestroyTexture(texture); + std::cerr << "movie_player overlay should be deallocated before being " + "allocated!\n"; + } + texture = SDL_CreateTexture(pRenderer, SDL_PIXELFORMAT_RGB24, + SDL_TEXTUREACCESS_STREAMING, iWidth, iHeight); + if (texture == nullptr) { + std::cerr << "Problem creating overlay: " << SDL_GetError() << "\n"; + return; + } + for (int i = 0; i < picture_buffer_size; i++) { + picture_queue[i].allocate(iWidth, iHeight); + } + // Do not change write_index, it's used by the other thread. + // read_index is only used in this thread. + read_index = write_index; + + std::lock_guard lock(mutex); + picture_count = 0; + allocated = true; + cond.notify_one(); } -void movie_picture_buffer::abort() -{ - aborting = true; +void movie_picture_buffer::deallocate() { + { std::lock_guard lock(mutex); - cond.notify_all(); + allocated = false; + } + + for (int i = 0; i < picture_buffer_size; i++) { + std::lock_guard pictureLock(picture_queue[i].mutex); + picture_queue[i].deallocate(); + } + + if (texture) { + SDL_DestroyTexture(texture); + texture = nullptr; + } } -void movie_picture_buffer::reset() -{ - aborting = false; +bool movie_picture_buffer::advance() { + if (empty()) { + return false; + } + + read_index++; + if (read_index == picture_buffer_size) { + read_index = 0; + } + + std::lock_guard lock(mutex); + picture_count--; + cond.notify_one(); + + return true; } -void movie_picture_buffer::allocate(SDL_Renderer *pRenderer, int iWidth, int iHeight) -{ - if (texture) - { - SDL_DestroyTexture(texture); - std::cerr << "movie_player overlay should be deallocated before being allocated!\n"; - } - texture = SDL_CreateTexture(pRenderer, SDL_PIXELFORMAT_RGB24, SDL_TEXTUREACCESS_STREAMING, iWidth, iHeight); - if (texture == nullptr) - { - std::cerr << "Problem creating overlay: " << SDL_GetError() << "\n"; - return; - } - for(int i = 0; i < picture_buffer_size; i++) - { - picture_queue[i].allocate(iWidth, iHeight); - } - // Do not change write_index, it's used by the other thread. - // read_index is only used in this thread. - read_index = write_index; +void movie_picture_buffer::draw(SDL_Renderer* pRenderer, + const SDL_Rect& dstrect) { + if (!empty()) { + auto cur_pic = &(picture_queue[read_index]); - std::lock_guard lock(mutex); - picture_count = 0; - allocated = true; - cond.notify_one(); + std::lock_guard pictureLock(cur_pic->mutex); + if (cur_pic->buffer) { + SDL_UpdateTexture(texture, nullptr, cur_pic->buffer, cur_pic->width * 3); + int iError = SDL_RenderCopy(pRenderer, texture, nullptr, &dstrect); + if (iError < 0) { + std::cerr << "Error displaying movie frame: " << SDL_GetError() << "\n"; + } + } + } } -void movie_picture_buffer::deallocate() -{ - { - std::lock_guard lock(mutex); - allocated = false; - } - - for(int i = 0; i < picture_buffer_size; i++) - { - std::lock_guard pictureLock(picture_queue[i].mutex); - picture_queue[i].deallocate(); - } - - if (texture) - { - SDL_DestroyTexture(texture); - texture = nullptr; - } +double movie_picture_buffer::get_next_pts() { + double nextPts; + std::lock_guard lock(mutex); + if (!allocated || picture_count < 2) { + nextPts = 0; + } else { + nextPts = picture_queue[(read_index + 1) % picture_buffer_size].pts; + } + return nextPts; } -bool movie_picture_buffer::advance() -{ - if(empty()) { return false; } +bool movie_picture_buffer::empty() { + std::lock_guard lock(mutex); + return (!allocated || picture_count == 0); +} - read_index++; - if(read_index == picture_buffer_size) - { - read_index = 0; +bool movie_picture_buffer::full() { + std::lock_guard lock(mutex); + return unsafe_full(); +} + +bool movie_picture_buffer::unsafe_full() { + return (!allocated || picture_count == picture_buffer_size); +} + +int movie_picture_buffer::write(AVFrame* pFrame, double dPts) { + movie_picture* pMoviePicture = nullptr; + std::unique_lock picBufLock(mutex); + while (unsafe_full() && !aborting) { + cond.wait(picBufLock); + } + picBufLock.unlock(); + + if (aborting) { + return -1; + } + + pMoviePicture = &picture_queue[write_index]; + std::unique_lock pictureLock(pMoviePicture->mutex); + + if (pMoviePicture->buffer) { + sws_context = sws_getCachedContext( + sws_context, pFrame->width, pFrame->height, + (AVPixelFormat)pFrame->format, pMoviePicture->width, + pMoviePicture->height, pMoviePicture->pixel_format, SWS_BICUBIC, + nullptr, nullptr, nullptr); + if (sws_context == nullptr) { + std::cerr << "Failed to initialize SwsContext\n"; + return 1; } - std::lock_guard lock(mutex); - picture_count--; - cond.notify_one(); + /* Allocate a new frame and buffer for the destination RGB24 data. */ + AVFrame* pFrameRGB = av_frame_alloc(); +#if (defined(CORSIX_TH_USE_LIBAV) && \ + LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(54, 6, 0)) || \ + (defined(CORSIX_TH_USE_FFMPEG) && \ + LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(51, 63, 100)) + av_image_fill_arrays(pFrameRGB->data, pFrameRGB->linesize, + pMoviePicture->buffer, pMoviePicture->pixel_format, + pMoviePicture->width, pMoviePicture->height, 1); +#else + avpicture_fill((AVPicture*)pFrameRGB, pMoviePicture->buffer, + pMoviePicture->pixel_format, pMoviePicture->width, + pMoviePicture->height); +#endif - return true; -} + /* Rescale the frame data and convert it to RGB24. */ + sws_scale(sws_context, pFrame->data, pFrame->linesize, 0, pFrame->height, + pFrameRGB->data, pFrameRGB->linesize); -void movie_picture_buffer::draw(SDL_Renderer *pRenderer, const SDL_Rect &dstrect) -{ - if(!empty()) - { - auto cur_pic = &(picture_queue[read_index]); + av_frame_free(&pFrameRGB); - std::lock_guard pictureLock(cur_pic->mutex); - if (cur_pic->buffer) - { - SDL_UpdateTexture(texture, nullptr, cur_pic->buffer, cur_pic->width * 3); - int iError = SDL_RenderCopy(pRenderer, texture, nullptr, &dstrect); - if (iError < 0) - { - std::cerr << "Error displaying movie frame: " << SDL_GetError() << "\n"; - } - } - } -} - -double movie_picture_buffer::get_next_pts() -{ - double nextPts; - std::lock_guard lock(mutex); - if(!allocated || picture_count < 2) - { - nextPts = 0; - } - else - { - nextPts = picture_queue[(read_index + 1) % picture_buffer_size].pts; - } - return nextPts; -} - -bool movie_picture_buffer::empty() -{ - std::lock_guard lock(mutex); - return (!allocated || picture_count == 0); -} - -bool movie_picture_buffer::full() -{ - std::lock_guard lock(mutex); - return unsafe_full(); -} - -bool movie_picture_buffer::unsafe_full() -{ - return (!allocated || picture_count == picture_buffer_size); -} - -int movie_picture_buffer::write(AVFrame* pFrame, double dPts) -{ - movie_picture* pMoviePicture = nullptr; - std::unique_lock picBufLock(mutex); - while(unsafe_full() && !aborting) - { - cond.wait(picBufLock); + pMoviePicture->pts = dPts; + + pictureLock.unlock(); + write_index++; + if (write_index == picture_buffer_size) { + write_index = 0; } + picBufLock.lock(); + picture_count++; picBufLock.unlock(); + } - if(aborting) { return -1; } + return 0; +} - pMoviePicture = &picture_queue[write_index]; - std::unique_lock pictureLock(pMoviePicture->mutex); +av_packet_queue::av_packet_queue() + : first_packet(nullptr), last_packet(nullptr), count(0), mutex{}, cond{} {} - if(pMoviePicture->buffer) - { - sws_context = sws_getCachedContext(sws_context, pFrame->width, pFrame->height, (AVPixelFormat)pFrame->format, pMoviePicture->width, pMoviePicture->height, pMoviePicture->pixel_format, SWS_BICUBIC, nullptr, nullptr, nullptr); - if(sws_context == nullptr) - { - std::cerr << "Failed to initialize SwsContext\n"; - return 1; - } +av_packet_queue::~av_packet_queue() {} - /* Allocate a new frame and buffer for the destination RGB24 data. */ - AVFrame *pFrameRGB = av_frame_alloc(); -#if (defined(CORSIX_TH_USE_LIBAV) && LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(54, 6, 0)) || \ - (defined(CORSIX_TH_USE_FFMPEG) && LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(51, 63, 100)) - av_image_fill_arrays(pFrameRGB->data, pFrameRGB->linesize, pMoviePicture->buffer, pMoviePicture->pixel_format, pMoviePicture->width, pMoviePicture->height, 1); +int av_packet_queue::get_count() const { return count; } + +void av_packet_queue::push(AVPacket* pPacket) { +#if (defined(CORSIX_TH_USE_LIBAV) && \ + LIBAVCODEC_VERSION_INT < AV_VERSION_INT(57, 8, 0)) || \ + (defined(CORSIX_TH_USE_FFMPEG) && \ + LIBAVCODEC_VERSION_INT < AV_VERSION_INT(57, 12, 100)) + if (av_dup_packet(pPacket) < 0) { + throw - 1; + } +#endif + + AVPacketList* pNode = (AVPacketList*)av_malloc(sizeof(AVPacketList)); + pNode->pkt = *pPacket; + pNode->next = nullptr; + + std::lock_guard lock(mutex); + + if (last_packet == nullptr) { + first_packet = pNode; + } else { + last_packet->next = pNode; + } + last_packet = pNode; + count++; + + cond.notify_one(); +} + +AVPacket* av_packet_queue::pull(bool fBlock) { + std::unique_lock lock(mutex); + + AVPacketList* pNode = first_packet; + if (pNode == nullptr && fBlock) { + cond.wait(lock); + pNode = first_packet; + } + + AVPacket* pPacket; + if (pNode == nullptr) { + pPacket = nullptr; + } else { + first_packet = pNode->next; + if (first_packet == nullptr) { + last_packet = nullptr; + } + count--; + pPacket = (AVPacket*)av_malloc(sizeof(AVPacket)); + *pPacket = pNode->pkt; + av_free(pNode); + } + + return pPacket; +} + +void av_packet_queue::release() { + std::lock_guard lock(mutex); + cond.notify_all(); +} + +movie_player::movie_player() + : renderer(nullptr), + last_error(), + decoding_audio_mutex{}, + format_context(nullptr), + video_codec_context(nullptr), + audio_codec_context(nullptr), + video_queue(nullptr), + audio_queue(nullptr), + movie_picture_buffer(new ::movie_picture_buffer()), + audio_resample_context(nullptr), + audio_buffer_size(0), + audio_buffer_max_size(0), + audio_packet(nullptr), + audio_frame(nullptr), + empty_audio_chunk(nullptr), + audio_channel(-1), + stream_thread{}, + video_thread{} { +#if defined(CORSIX_TH_USE_LIBAV) || \ + (defined(CORSIX_TH_USE_FFMPEG) && \ + LIBAVCODEC_VERSION_INT < AV_VERSION_INT(58, 9, 100)) + av_register_all(); +#endif + + flush_packet = (AVPacket*)av_malloc(sizeof(AVPacket)); + av_init_packet(flush_packet); + flush_packet->data = (uint8_t*)"FLUSH"; + flush_packet->size = 5; + + audio_chunk_buffer = + (uint8_t*)std::calloc(audio_chunk_buffer_capacity, sizeof(uint8_t)); +} + +movie_player::~movie_player() { + unload(); + + av_packet_unref(flush_packet); + av_free(flush_packet); + free(audio_chunk_buffer); + delete movie_picture_buffer; +} + +void movie_player::set_renderer(SDL_Renderer* pRenderer) { + renderer = pRenderer; +} + +bool movie_player::movies_enabled() const { return true; } + +bool movie_player::load(const char* szFilepath) { + int iError = 0; + AVCodec* m_pVideoCodec; + AVCodec* m_pAudioCodec; + + unload(); // Unload any currently loaded video to free memory + aborting = false; + + iError = avformat_open_input(&format_context, szFilepath, nullptr, nullptr); + if (iError < 0) { + av_strerror(iError, error_buffer, movie_error_buffer_capacity); + last_error = std::string(error_buffer); + return false; + } + + iError = avformat_find_stream_info(format_context, nullptr); + if (iError < 0) { + av_strerror(iError, error_buffer, movie_error_buffer_capacity); + last_error = std::string(error_buffer); + return false; + } + + video_stream_index = av_find_best_stream(format_context, AVMEDIA_TYPE_VIDEO, + -1, -1, &m_pVideoCodec, 0); + if (video_stream_index < 0) { + av_strerror(video_stream_index, error_buffer, movie_error_buffer_capacity); + last_error = std::string(error_buffer); + return false; + } + video_codec_context = get_codec_context_for_stream( + m_pVideoCodec, format_context->streams[video_stream_index]); + avcodec_open2(video_codec_context, m_pVideoCodec, nullptr); + + audio_stream_index = av_find_best_stream(format_context, AVMEDIA_TYPE_AUDIO, + -1, -1, &m_pAudioCodec, 0); + if (audio_stream_index >= 0) { + audio_codec_context = get_codec_context_for_stream( + m_pAudioCodec, format_context->streams[audio_stream_index]); + avcodec_open2(audio_codec_context, m_pAudioCodec, nullptr); + } + + return true; +} + +AVCodecContext* movie_player::get_codec_context_for_stream( + AVCodec* codec, AVStream* stream) const { +#if (defined(CORSIX_TH_USE_LIBAV) && \ + LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 14, 0)) || \ + (defined(CORSIX_TH_USE_FFMPEG) && \ + LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 33, 100)) + AVCodecContext* ctx = avcodec_alloc_context3(codec); + avcodec_parameters_to_context(ctx, stream->codecpar); + return ctx; #else - avpicture_fill((AVPicture *)pFrameRGB, pMoviePicture->buffer, pMoviePicture->pixel_format, pMoviePicture->width, pMoviePicture->height); -#endif - - /* Rescale the frame data and convert it to RGB24. */ - sws_scale(sws_context, pFrame->data, pFrame->linesize, 0, pFrame->height, pFrameRGB->data, pFrameRGB->linesize); - - av_frame_free(&pFrameRGB); - - pMoviePicture->pts = dPts; - - pictureLock.unlock(); - write_index++; - if(write_index == picture_buffer_size) - { - write_index = 0; - } - picBufLock.lock(); - picture_count++; - picBufLock.unlock(); - } - - return 0; -} - -av_packet_queue::av_packet_queue(): - first_packet(nullptr), - last_packet(nullptr), - count(0), - mutex{}, - cond{} -{ -} - -av_packet_queue::~av_packet_queue() -{ -} - -int av_packet_queue::get_count() const -{ - return count; -} - -void av_packet_queue::push(AVPacket *pPacket) -{ -#if (defined(CORSIX_TH_USE_LIBAV) && LIBAVCODEC_VERSION_INT < AV_VERSION_INT(57, 8, 0)) || \ - (defined(CORSIX_TH_USE_FFMPEG) && LIBAVCODEC_VERSION_INT < AV_VERSION_INT(57, 12, 100)) - if(av_dup_packet(pPacket) < 0) { throw -1; } -#endif - - AVPacketList* pNode = (AVPacketList*)av_malloc(sizeof(AVPacketList)); - pNode->pkt = *pPacket; - pNode->next = nullptr; - - std::lock_guard lock(mutex); - - if(last_packet == nullptr) - { - first_packet = pNode; - } - else - { - last_packet->next = pNode; - } - last_packet = pNode; - count++; - - cond.notify_one(); -} - -AVPacket* av_packet_queue::pull(bool fBlock) -{ - std::unique_lock lock(mutex); - - AVPacketList* pNode = first_packet; - if(pNode == nullptr && fBlock) - { - cond.wait(lock); - pNode = first_packet; - } - - AVPacket *pPacket; - if(pNode == nullptr) - { - pPacket = nullptr; - } - else - { - first_packet = pNode->next; - if(first_packet == nullptr) { last_packet = nullptr; } - count--; - pPacket = (AVPacket*)av_malloc(sizeof(AVPacket)); - *pPacket = pNode->pkt; - av_free(pNode); - } - - return pPacket; -} - -void av_packet_queue::release() -{ - std::lock_guard lock(mutex); - cond.notify_all(); -} - -movie_player::movie_player(): - renderer(nullptr), - last_error(), - format_context(nullptr), - video_codec_context(nullptr), - audio_codec_context(nullptr), - video_queue(nullptr), - audio_queue(nullptr), - movie_picture_buffer(new ::movie_picture_buffer()), - audio_resample_context(nullptr), - audio_buffer_size(0), - audio_buffer_max_size(0), - audio_packet(nullptr), - audio_frame(nullptr), - empty_audio_chunk(nullptr), - audio_channel(-1), - stream_thread{}, - video_thread{}, - decoding_audio_mutex{} -{ -#if defined(CORSIX_TH_USE_LIBAV) || \ - (defined(CORSIX_TH_USE_FFMPEG) && LIBAVCODEC_VERSION_INT < AV_VERSION_INT(58, 9, 100)) - av_register_all(); -#endif - - flush_packet = (AVPacket*)av_malloc(sizeof(AVPacket)); - av_init_packet(flush_packet); - flush_packet->data = (uint8_t *)"FLUSH"; - flush_packet->size = 5; - - audio_chunk_buffer = (uint8_t*)std::calloc(audio_chunk_buffer_capacity, sizeof(uint8_t)); -} - -movie_player::~movie_player() -{ - unload(); - - av_packet_unref(flush_packet); - av_free(flush_packet); - free(audio_chunk_buffer); - delete movie_picture_buffer; -} - -void movie_player::set_renderer(SDL_Renderer *pRenderer) -{ - renderer = pRenderer; -} - -bool movie_player::movies_enabled() const -{ - return true; -} - -bool movie_player::load(const char* szFilepath) -{ - int iError = 0; - AVCodec* m_pVideoCodec; - AVCodec* m_pAudioCodec; - - unload(); //Unload any currently loaded video to free memory - aborting = false; - - iError = avformat_open_input(&format_context, szFilepath, nullptr, nullptr); - if(iError < 0) - { - av_strerror(iError, error_buffer, movie_error_buffer_capacity); - last_error = std::string(error_buffer); - return false; - } - - iError = avformat_find_stream_info(format_context, nullptr); - if(iError < 0) - { - av_strerror(iError, error_buffer, movie_error_buffer_capacity); - last_error = std::string(error_buffer); - return false; - } - - video_stream_index = av_find_best_stream(format_context, AVMEDIA_TYPE_VIDEO, -1, -1, &m_pVideoCodec, 0); - if (video_stream_index < 0) { - av_strerror(video_stream_index, error_buffer, movie_error_buffer_capacity); - last_error = std::string(error_buffer); - return false; - } - video_codec_context = get_codec_context_for_stream(m_pVideoCodec, format_context->streams[video_stream_index]); - avcodec_open2(video_codec_context, m_pVideoCodec, nullptr); - - audio_stream_index = av_find_best_stream(format_context, AVMEDIA_TYPE_AUDIO, -1, -1, &m_pAudioCodec, 0); - if(audio_stream_index >= 0) - { - audio_codec_context = get_codec_context_for_stream(m_pAudioCodec, format_context->streams[audio_stream_index]); - avcodec_open2(audio_codec_context, m_pAudioCodec, nullptr); - } - - return true; -} - -AVCodecContext* movie_player::get_codec_context_for_stream(AVCodec* codec, AVStream* stream) const -{ -#if (defined(CORSIX_TH_USE_LIBAV) && LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 14, 0)) || \ - (defined(CORSIX_TH_USE_FFMPEG) && LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 33, 100)) - AVCodecContext* ctx = avcodec_alloc_context3(codec); - avcodec_parameters_to_context(ctx, stream->codecpar); - return ctx; -#else - return stream->codec; + return stream->codec; #endif } -void movie_player::unload() -{ - aborting = true; +void movie_player::unload() { + aborting = true; - if(audio_queue) - { - audio_queue->release(); - } - if(video_queue) - { - video_queue->release(); - } - movie_picture_buffer->abort(); + if (audio_queue) { + audio_queue->release(); + } + if (video_queue) { + video_queue->release(); + } + movie_picture_buffer->abort(); - if(stream_thread.joinable()) - { - stream_thread.join(); - } - if(video_thread.joinable()) - { - video_thread.join(); - } + if (stream_thread.joinable()) { + stream_thread.join(); + } + if (video_thread.joinable()) { + video_thread.join(); + } - //wait until after other threads are closed to clear the packet queues - //so we don't free something being used. - if(audio_queue) - { - while(audio_queue->get_count() > 0) - { - AVPacket* p = audio_queue->pull(false); - av_packet_unref(p); - av_free(p); - } - delete audio_queue; - audio_queue = nullptr; + // wait until after other threads are closed to clear the packet queues + // so we don't free something being used. + if (audio_queue) { + while (audio_queue->get_count() > 0) { + AVPacket* p = audio_queue->pull(false); + av_packet_unref(p); + av_free(p); } - if(video_queue) - { - while(video_queue->get_count() > 0) - { - AVPacket* p = video_queue->pull(false); - av_packet_unref(p); - av_free(p); - } - delete video_queue; - video_queue = nullptr; - } - movie_picture_buffer->deallocate(); -#if (defined(CORSIX_TH_USE_LIBAV) && LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 14, 0)) || \ - (defined(CORSIX_TH_USE_FFMPEG) && LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 33, 100)) - if(video_codec_context) - { - avcodec_free_context(&video_codec_context); - video_codec_context = nullptr; + delete audio_queue; + audio_queue = nullptr; + } + if (video_queue) { + while (video_queue->get_count() > 0) { + AVPacket* p = video_queue->pull(false); + av_packet_unref(p); + av_free(p); } + delete video_queue; + video_queue = nullptr; + } + movie_picture_buffer->deallocate(); +#if (defined(CORSIX_TH_USE_LIBAV) && \ + LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 14, 0)) || \ + (defined(CORSIX_TH_USE_FFMPEG) && \ + LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 33, 100)) + if (video_codec_context) { + avcodec_free_context(&video_codec_context); + video_codec_context = nullptr; + } #endif - if(audio_channel >= 0) - { - Mix_UnregisterAllEffects(audio_channel); - Mix_HaltChannel(audio_channel); - Mix_FreeChunk(empty_audio_chunk); - audio_channel = -1; - } + if (audio_channel >= 0) { + Mix_UnregisterAllEffects(audio_channel); + Mix_HaltChannel(audio_channel); + Mix_FreeChunk(empty_audio_chunk); + audio_channel = -1; + } - std::lock_guard audioLock(decoding_audio_mutex); + std::lock_guard audioLock(decoding_audio_mutex); - if(audio_buffer_max_size > 0) - { - av_free(audio_buffer); - audio_buffer_max_size = 0; - } -#if (defined(CORSIX_TH_USE_LIBAV) && LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 14, 0)) || \ - (defined(CORSIX_TH_USE_FFMPEG) && LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 33, 100)) - if(audio_codec_context) - { - avcodec_free_context(&audio_codec_context); - audio_codec_context = nullptr; - } + if (audio_buffer_max_size > 0) { + av_free(audio_buffer); + audio_buffer_max_size = 0; + } +#if (defined(CORSIX_TH_USE_LIBAV) && \ + LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 14, 0)) || \ + (defined(CORSIX_TH_USE_FFMPEG) && \ + LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 33, 100)) + if (audio_codec_context) { + avcodec_free_context(&audio_codec_context); + audio_codec_context = nullptr; + } #endif - av_frame_free(&audio_frame); + av_frame_free(&audio_frame); #ifdef CORSIX_TH_USE_FFMPEG - swr_free(&audio_resample_context); + swr_free(&audio_resample_context); #elif defined(CORSIX_TH_USE_LIBAV) - // avresample_free doesn't skip nullptr on it's own. - if (audio_resample_context != nullptr) - { - avresample_free(&audio_resample_context); - audio_resample_context = nullptr; + // avresample_free doesn't skip nullptr on it's own. + if (audio_resample_context != nullptr) { + avresample_free(&audio_resample_context); + audio_resample_context = nullptr; + } +#endif + + if (audio_packet) { + audio_packet->data = audio_packet_data; + audio_packet->size = audio_packet_size; + av_packet_unref(audio_packet); + av_free(audio_packet); + audio_packet = nullptr; + audio_packet_data = nullptr; + audio_packet_size = 0; + } + + if (format_context) { + avformat_close_input(&format_context); + } +} + +void movie_player::play(int iChannel) { + if (!renderer) { + last_error = std::string("Cannot play before setting the renderer"); + return; + } + + video_queue = new av_packet_queue(); + movie_picture_buffer->reset(); + movie_picture_buffer->allocate(renderer, video_codec_context->width, + video_codec_context->height); + + audio_packet = nullptr; + audio_packet_size = 0; + audio_packet_data = nullptr; + + audio_buffer_size = 0; + audio_buffer_index = 0; + audio_buffer_max_size = 0; + + audio_queue = new av_packet_queue(); + current_sync_pts = 0; + current_sync_pts_system_time = SDL_GetTicks(); + + if (audio_stream_index >= 0) { + Mix_QuerySpec(&mixer_frequency, nullptr, &mixer_channels); +#ifdef CORSIX_TH_USE_FFMPEG + audio_resample_context = swr_alloc_set_opts( + audio_resample_context, + mixer_channels == 1 ? AV_CH_LAYOUT_MONO : AV_CH_LAYOUT_STEREO, + AV_SAMPLE_FMT_S16, mixer_frequency, audio_codec_context->channel_layout, + audio_codec_context->sample_fmt, audio_codec_context->sample_rate, 0, + nullptr); + swr_init(audio_resample_context); +#elif defined(CORSIX_TH_USE_LIBAV) + audio_resample_context = avresample_alloc_context(); + av_opt_set_int(audio_resample_context, "in_channel_layout", + audio_codec_context->channel_layout, 0); + av_opt_set_int( + audio_resample_context, "out_channel_layout", + mixer_channels == 1 ? AV_CH_LAYOUT_MONO : AV_CH_LAYOUT_STEREO, 0); + av_opt_set_int(audio_resample_context, "in_sample_rate", + audio_codec_context->sample_rate, 0); + av_opt_set_int(audio_resample_context, "out_sample_rate", mixer_frequency, + 0); + av_opt_set_int(audio_resample_context, "in_sample_fmt", + audio_codec_context->sample_fmt, 0); + av_opt_set_int(audio_resample_context, "out_sample_fmt", AV_SAMPLE_FMT_S16, + 0); + avresample_open(audio_resample_context); +#endif + empty_audio_chunk = + Mix_QuickLoad_RAW(audio_chunk_buffer, audio_chunk_buffer_capacity); + + audio_channel = Mix_PlayChannel(iChannel, empty_audio_chunk, -1); + if (audio_channel < 0) { + audio_channel = -1; + last_error = std::string(Mix_GetError()); + Mix_FreeChunk(empty_audio_chunk); + } else { + Mix_RegisterEffect(audio_channel, th_movie_audio_callback, nullptr, this); + } + } + + stream_thread = std::thread(&movie_player::read_streams, this); + video_thread = std::thread(&movie_player::run_video, this); +} + +void movie_player::stop() { aborting = true; } + +int movie_player::get_native_height() const { + int iHeight = 0; + + if (video_codec_context) { + iHeight = video_codec_context->height; + } + return iHeight; +} + +int movie_player::get_native_width() const { + int iWidth = 0; + + if (video_codec_context) { + iWidth = video_codec_context->width; + } + return iWidth; +} + +bool movie_player::has_audio_track() const { return (audio_stream_index >= 0); } + +const char* movie_player::get_last_error() const { return last_error.c_str(); } + +void movie_player::clear_last_error() { last_error.clear(); } + +void movie_player::refresh(const SDL_Rect& destination_rect) { + SDL_Rect dest_rect; + + dest_rect = SDL_Rect{destination_rect.x, destination_rect.y, + destination_rect.w, destination_rect.h}; + + if (!movie_picture_buffer->empty()) { + double dCurTime = SDL_GetTicks() - current_sync_pts_system_time + + current_sync_pts * 1000.0; + double dNextPts = movie_picture_buffer->get_next_pts(); + + if (dNextPts > 0 && dNextPts * 1000.0 <= dCurTime) { + movie_picture_buffer->advance(); + } + + movie_picture_buffer->draw(renderer, dest_rect); + } +} + +void movie_player::allocate_picture_buffer() { + if (!video_codec_context) { + return; + } + movie_picture_buffer->allocate(renderer, get_native_width(), + get_native_height()); +} + +void movie_player::deallocate_picture_buffer() { + movie_picture_buffer->deallocate(); +} + +void movie_player::read_streams() { + AVPacket packet; + int iError; + + while (!aborting) { + iError = av_read_frame(format_context, &packet); + if (iError < 0) { + if (iError == AVERROR_EOF || format_context->pb->error || + format_context->pb->eof_reached) { + break; + } + } else { + if (packet.stream_index == video_stream_index) { + video_queue->push(&packet); + } else if (packet.stream_index == audio_stream_index) { + audio_queue->push(&packet); + } else { + av_packet_unref(&packet); + } + } + } + + while (!aborting) { + if (video_queue->get_count() == 0 && audio_queue->get_count() == 0 && + movie_picture_buffer->get_next_pts() == 0) { + break; + } + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + } + + SDL_Event endEvent; + endEvent.type = SDL_USEREVENT_MOVIE_OVER; + SDL_PushEvent(&endEvent); + aborting = true; +} + +void movie_player::run_video() { + AVFrame* pFrame = av_frame_alloc(); + double dClockPts; + int iError; + + while (!aborting) { + av_frame_unref(pFrame); + +#ifdef CORSIX_TH_MOVIE_USE_SEND_PACKET_API + iError = get_frame(video_stream_index, pFrame); + + if (iError == AVERROR_EOF) { + break; + } else if (iError < 0) { + std::cerr << "Unexpected error " << iError + << " while decoding video packet" << std::endl; + break; + } +#else + iError = get_video_frame(pFrame); + if (iError < 0) { + break; + } else if (iError == 0) { + continue; } #endif - if(audio_packet) - { + dClockPts = get_presentation_time_for_frame(pFrame, video_stream_index); + iError = movie_picture_buffer->write(pFrame, dClockPts); + + if (iError < 0) { + break; + } + } + + avcodec_flush_buffers(video_codec_context); + av_frame_free(&pFrame); +} + +double movie_player::get_presentation_time_for_frame(AVFrame* frame, + int streamIndex) const { + int64_t pts; +#ifdef CORSIX_TH_USE_LIBAV + pts = frame->pts; + if (pts == AV_NOPTS_VALUE) { + pts = frame->pkt_dts; + } +#else +#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(54, 18, 100) + pts = *(int64_t*)av_opt_ptr(avcodec_get_frame_class(), frame, + "best_effort_timestamp"); +#elif LIBAVCODEC_VERSION_INT < AV_VERSION_INT(58, 18, 100) + pts = av_frame_get_best_effort_timestamp(frame); +#else + pts = frame->best_effort_timestamp; +#endif // LIBAVCODEC_VERSION_INT +#endif // CORSIX_T_USE_LIBAV + + if (pts == AV_NOPTS_VALUE) { + pts = 0; + } + + return pts * av_q2d(format_context->streams[streamIndex]->time_base); +} + +#ifdef CORSIX_TH_MOVIE_USE_SEND_PACKET_API +int movie_player::get_frame(int stream, AVFrame* pFrame) { + int iError = AVERROR(EAGAIN); + AVCodecContext* ctx; + av_packet_queue* pq; + + if (stream == video_stream_index) { + ctx = video_codec_context; + pq = video_queue; + } else if (stream == audio_stream_index) { + ctx = audio_codec_context; + pq = audio_queue; + } else { + throw std::invalid_argument("Invalid value provided for stream"); + } + + while (iError == AVERROR(EAGAIN)) { + iError = avcodec_receive_frame(ctx, pFrame); + + if (iError == AVERROR(EAGAIN)) { + AVPacket* pkt = pq->pull(true); + int res = avcodec_send_packet(ctx, pkt); + if (pkt != nullptr) { + av_packet_unref(pkt); + av_free(pkt); + } + + if (res == AVERROR(EAGAIN)) { + throw std::runtime_error( + "avcodec_receive_frame and avcodec_send_packet should " + "not return EAGAIN at the same time"); + } + } + } + + return iError; +} + +#else +int movie_player::get_video_frame(AVFrame* pFrame) { + int iGotPicture = 0; + int iError; + + AVPacket* pPacket = video_queue->pull(true); + if (pPacket == nullptr) { + return -1; + } + + if (pPacket->data == flush_packet->data) { + // TODO: Flush + + return 0; + } + + iError = + avcodec_decode_video2(video_codec_context, pFrame, &iGotPicture, pPacket); + av_packet_unref(pPacket); + av_free(pPacket); + + if (iError < 0) { + return 0; + } + + if (iGotPicture) { + iError = 1; + return iError; + } + + return 0; +} +#endif + +void movie_player::copy_audio_to_stream(uint8_t* pbStream, int iStreamSize) { + std::lock_guard audioLock(decoding_audio_mutex); + + bool fFirst = true; + while (iStreamSize > 0 && !aborting) { + if (audio_buffer_index >= audio_buffer_size) { + int iAudioSize = decode_audio_frame(fFirst); + fFirst = false; + + if (iAudioSize <= 0) { + std::memset(audio_buffer, 0, audio_buffer_size); + } else { + audio_buffer_size = iAudioSize; + } + audio_buffer_index = 0; + } + + int iCopyLength = audio_buffer_size - audio_buffer_index; + if (iCopyLength > iStreamSize) { + iCopyLength = iStreamSize; + } + std::memcpy(pbStream, (uint8_t*)audio_buffer + audio_buffer_index, + iCopyLength); + iStreamSize -= iCopyLength; + pbStream += iCopyLength; + audio_buffer_index += iCopyLength; + } +} + +int movie_player::decode_audio_frame(bool fFirst) { +#ifdef CORSIX_TH_MOVIE_USE_SEND_PACKET_API + if (!audio_frame) { + audio_frame = av_frame_alloc(); + } else { + av_frame_unref(audio_frame); + } + + int iError = get_frame(audio_stream_index, audio_frame); + + if (iError == AVERROR_EOF) { + return 0; + } else if (iError < 0) { + std::cerr << "Unexpected error " << iError << " while decoding audio packet" + << std::endl; + return 0; + } + + double dClockPts = + get_presentation_time_for_frame(audio_frame, audio_stream_index); + current_sync_pts = dClockPts; + current_sync_pts_system_time = SDL_GetTicks(); +#else + int iGotFrame = 0; + bool fNewPacket = false; + bool fFlushComplete = false; + + while (!iGotFrame && !aborting) { + if (!audio_packet || audio_packet->size == 0) { + if (audio_packet) { audio_packet->data = audio_packet_data; audio_packet->size = audio_packet_size; av_packet_unref(audio_packet); av_free(audio_packet); audio_packet = nullptr; - audio_packet_data = nullptr; - audio_packet_size = 0; - } + } + audio_packet = audio_queue->pull(true); + if (aborting) { + break; + } - if(format_context) - { - avformat_close_input(&format_context); - } -} + audio_packet_data = audio_packet->data; + audio_packet_size = audio_packet->size; -void movie_player::play(int iChannel) -{ - if(!renderer) - { - last_error = std::string("Cannot play before setting the renderer"); - return; - } - - video_queue = new av_packet_queue(); - movie_picture_buffer->reset(); - movie_picture_buffer->allocate(renderer, video_codec_context->width, video_codec_context->height); - - audio_packet = nullptr; - audio_packet_size = 0; - audio_packet_data = nullptr; - - audio_buffer_size = 0; - audio_buffer_index = 0; - audio_buffer_max_size = 0; - - audio_queue = new av_packet_queue(); - current_sync_pts = 0; - current_sync_pts_system_time = SDL_GetTicks(); - - if(audio_stream_index >= 0) - { - Mix_QuerySpec(&mixer_frequency, nullptr, &mixer_channels); -#ifdef CORSIX_TH_USE_FFMPEG - audio_resample_context = swr_alloc_set_opts( - audio_resample_context, - mixer_channels==1?AV_CH_LAYOUT_MONO:AV_CH_LAYOUT_STEREO, - AV_SAMPLE_FMT_S16, - mixer_frequency, - audio_codec_context->channel_layout, - audio_codec_context->sample_fmt, - audio_codec_context->sample_rate, - 0, - nullptr); - swr_init(audio_resample_context); -#elif defined(CORSIX_TH_USE_LIBAV) - audio_resample_context = avresample_alloc_context(); - av_opt_set_int(audio_resample_context, "in_channel_layout", audio_codec_context->channel_layout, 0); - av_opt_set_int(audio_resample_context, "out_channel_layout", mixer_channels == 1 ? AV_CH_LAYOUT_MONO : AV_CH_LAYOUT_STEREO, 0); - av_opt_set_int(audio_resample_context, "in_sample_rate", audio_codec_context->sample_rate, 0); - av_opt_set_int(audio_resample_context, "out_sample_rate", mixer_frequency, 0); - av_opt_set_int(audio_resample_context, "in_sample_fmt", audio_codec_context->sample_fmt, 0); - av_opt_set_int(audio_resample_context, "out_sample_fmt", AV_SAMPLE_FMT_S16, 0); - avresample_open(audio_resample_context); -#endif - empty_audio_chunk = Mix_QuickLoad_RAW(audio_chunk_buffer, audio_chunk_buffer_capacity); - - audio_channel = Mix_PlayChannel(iChannel, empty_audio_chunk, -1); - if(audio_channel < 0) - { - audio_channel = -1; - last_error = std::string(Mix_GetError()); - Mix_FreeChunk(empty_audio_chunk); - } - else - { - Mix_RegisterEffect(audio_channel, th_movie_audio_callback, nullptr, this); - } - } - - stream_thread = std::thread(&movie_player::read_streams, this); - video_thread = std::thread(&movie_player::run_video, this); -} - -void movie_player::stop() -{ - aborting = true; -} - -int movie_player::get_native_height() const -{ - int iHeight = 0; - - if(video_codec_context) - { - iHeight = video_codec_context->height; - } - return iHeight; -} - -int movie_player::get_native_width() const -{ - int iWidth = 0; - - if(video_codec_context) - { - iWidth = video_codec_context->width; - } - return iWidth; -} - -bool movie_player::has_audio_track() const -{ - return (audio_stream_index >= 0); -} - -const char* movie_player::get_last_error() const -{ - return last_error.c_str(); -} - -void movie_player::clear_last_error() -{ - last_error.clear(); -} - -void movie_player::refresh(const SDL_Rect &destination_rect) -{ - SDL_Rect dest_rect; - - dest_rect = SDL_Rect{ destination_rect.x, destination_rect.y, destination_rect.w, destination_rect.h }; - - if(!movie_picture_buffer->empty()) - { - double dCurTime = SDL_GetTicks() - current_sync_pts_system_time + current_sync_pts * 1000.0; - double dNextPts = movie_picture_buffer->get_next_pts(); - - if(dNextPts > 0 && dNextPts * 1000.0 <= dCurTime) - { - movie_picture_buffer->advance(); - } - - movie_picture_buffer->draw(renderer, dest_rect); - } -} - -void movie_player::allocate_picture_buffer() -{ - if(!video_codec_context) - { - return; - } - movie_picture_buffer->allocate(renderer, get_native_width(), get_native_height()); -} - -void movie_player::deallocate_picture_buffer() -{ - movie_picture_buffer->deallocate(); -} - -void movie_player::read_streams() -{ - AVPacket packet; - int iError; - - while(!aborting) - { - iError = av_read_frame(format_context, &packet); - if(iError < 0) - { - if(iError == AVERROR_EOF || format_context->pb->error || format_context->pb->eof_reached) - { - break; - } - } - else - { - if(packet.stream_index == video_stream_index) - { - video_queue->push(&packet); - } - else if (packet.stream_index == audio_stream_index) - { - audio_queue->push(&packet); - } - else - { - av_packet_unref(&packet); - } - } - } - - while(!aborting) - { - if(video_queue->get_count() == 0 && audio_queue->get_count() == 0 && movie_picture_buffer->get_next_pts() == 0) - { - break; - } - std::this_thread::sleep_for(std::chrono::milliseconds(10)); - } - - SDL_Event endEvent; - endEvent.type = SDL_USEREVENT_MOVIE_OVER; - SDL_PushEvent(&endEvent); - aborting = true; -} - -void movie_player::run_video() -{ - AVFrame *pFrame = av_frame_alloc(); - double dClockPts; - int iError; - - while(!aborting) - { - av_frame_unref(pFrame); - -#ifdef CORSIX_TH_MOVIE_USE_SEND_PACKET_API - iError = get_frame(video_stream_index, pFrame); - - if (iError == AVERROR_EOF) - { - break; - } - else if (iError < 0) - { - std::cerr << "Unexpected error " << iError << " while decoding video packet" << std::endl; - break; - } -#else - iError = get_video_frame(pFrame); - if(iError < 0) - { - break; - } - else if(iError == 0) - { - continue; - } -#endif - - dClockPts = get_presentation_time_for_frame(pFrame, video_stream_index); - iError = movie_picture_buffer->write(pFrame, dClockPts); - - if(iError < 0) - { - break; - } - } - - avcodec_flush_buffers(video_codec_context); - av_frame_free(&pFrame); -} - -double movie_player::get_presentation_time_for_frame(AVFrame* frame, int streamIndex) const -{ - int64_t pts; -#ifdef CORSIX_TH_USE_LIBAV - pts = frame->pts; - if (pts == AV_NOPTS_VALUE) - { - pts = frame->pkt_dts; - } -#else -#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(54, 18, 100) - pts = *(int64_t*)av_opt_ptr(avcodec_get_frame_class(), frame, "best_effort_timestamp"); -#elif LIBAVCODEC_VERSION_INT < AV_VERSION_INT(58, 18, 100) - pts = av_frame_get_best_effort_timestamp(frame); -#else - pts = frame->best_effort_timestamp; -#endif //LIBAVCODEC_VERSION_INT -#endif //CORSIX_T_USE_LIBAV - - if (pts == AV_NOPTS_VALUE) - { - pts = 0; - } - - return pts * av_q2d(format_context->streams[streamIndex]->time_base); -} - -#ifdef CORSIX_TH_MOVIE_USE_SEND_PACKET_API -int movie_player::get_frame(int stream, AVFrame* pFrame) -{ - int iError = AVERROR(EAGAIN); - AVCodecContext* ctx; - av_packet_queue* pq; - - if (stream == video_stream_index) - { - ctx = video_codec_context; - pq = video_queue; - } - else if (stream == audio_stream_index) - { - ctx = audio_codec_context; - pq = audio_queue; - } - else - { - throw std::invalid_argument("Invalid value provided for stream"); - } - - while (iError == AVERROR(EAGAIN)) - { - iError = avcodec_receive_frame(ctx, pFrame); - - if (iError == AVERROR(EAGAIN)) - { - AVPacket* pkt = pq->pull(true); - int res = avcodec_send_packet(ctx, pkt); - if (pkt != nullptr) { - av_packet_unref(pkt); - av_free(pkt); - } - - if (res == AVERROR(EAGAIN)) - { - throw std::runtime_error("avcodec_receive_frame and avcodec_send_packet should not return EAGAIN at the same time"); - } - } - } - - return iError; -} - -#else -int movie_player::get_video_frame(AVFrame *pFrame) -{ - int iGotPicture = 0; - int iError; - - AVPacket *pPacket = video_queue->pull(true); - if(pPacket == nullptr) - { + if (audio_packet == nullptr) { + fNewPacket = false; return -1; + } + fNewPacket = true; + + if (audio_packet->data == flush_packet->data) { + avcodec_flush_buffers(audio_codec_context); + fFlushComplete = false; + } } - if(pPacket->data == flush_packet->data) - { - //TODO: Flush - - return 0; + if (fFirst) { + int64_t iStreamPts = audio_packet->pts; + if (iStreamPts != AV_NOPTS_VALUE) { + // There is a time_base in audio_codec_context too, but that one + // is wrong. + double dClockPts = + iStreamPts * + av_q2d(format_context->streams[audio_stream_index]->time_base); + current_sync_pts = dClockPts; + current_sync_pts_system_time = SDL_GetTicks(); + } + fFirst = false; } - iError = avcodec_decode_video2(video_codec_context, pFrame, &iGotPicture, pPacket); - av_packet_unref(pPacket); - av_free(pPacket); - - if(iError < 0) - { - return 0; - } - - if(iGotPicture) - { - iError = 1; - return iError; - } - - return 0; -} -#endif - -void movie_player::copy_audio_to_stream(uint8_t *pbStream, int iStreamSize) -{ - std::lock_guard audioLock(decoding_audio_mutex); - - bool fFirst = true; - while(iStreamSize > 0 && !aborting) - { - if(audio_buffer_index >= audio_buffer_size) - { - int iAudioSize = decode_audio_frame(fFirst); - fFirst = false; - - if(iAudioSize <= 0) - { - std::memset(audio_buffer, 0, audio_buffer_size); - } - else - { - audio_buffer_size = iAudioSize; - } - audio_buffer_index = 0; - } - - int iCopyLength = audio_buffer_size - audio_buffer_index; - if(iCopyLength > iStreamSize) { iCopyLength = iStreamSize; } - std::memcpy(pbStream, (uint8_t *)audio_buffer + audio_buffer_index, iCopyLength); - iStreamSize -= iCopyLength; - pbStream += iCopyLength; - audio_buffer_index += iCopyLength; - } -} - -int movie_player::decode_audio_frame(bool fFirst) -{ -#ifdef CORSIX_TH_MOVIE_USE_SEND_PACKET_API - if (!audio_frame) - { + while (audio_packet->size > 0 || (!audio_packet->data && fNewPacket)) { + if (!audio_frame) { audio_frame = av_frame_alloc(); - } - else - { + } else { av_frame_unref(audio_frame); - } + } - int iError = get_frame(audio_stream_index, audio_frame); + if (fFlushComplete) { + break; + } - if (iError == AVERROR_EOF) - { - return 0; - } - else if (iError < 0) - { - std::cerr << "Unexpected error " << iError << " while decoding audio packet" << std::endl; - return 0; - } + fNewPacket = false; - double dClockPts = get_presentation_time_for_frame(audio_frame, audio_stream_index); - current_sync_pts = dClockPts; - current_sync_pts_system_time = SDL_GetTicks(); -#else - int iGotFrame = 0; - bool fNewPacket = false; - bool fFlushComplete = false; + int iBytesConsumed = avcodec_decode_audio4( + audio_codec_context, audio_frame, &iGotFrame, audio_packet); - while(!iGotFrame && !aborting) - { - if(!audio_packet || audio_packet->size == 0) - { - if(audio_packet) - { - audio_packet->data = audio_packet_data; - audio_packet->size = audio_packet_size; - av_packet_unref(audio_packet); - av_free(audio_packet); - audio_packet = nullptr; - } - audio_packet = audio_queue->pull(true); - if(aborting) - { - break; - } + if (iBytesConsumed < 0) { + audio_packet->size = 0; + break; + } + audio_packet->data += iBytesConsumed; + audio_packet->size -= iBytesConsumed; - audio_packet_data = audio_packet->data; - audio_packet_size = audio_packet->size; - - if(audio_packet == nullptr) - { - fNewPacket = false; - return -1; - } - fNewPacket = true; - - if(audio_packet->data == flush_packet->data) - { - avcodec_flush_buffers(audio_codec_context); - fFlushComplete = false; - } - } - - if(fFirst) - { - int64_t iStreamPts = audio_packet->pts; - if(iStreamPts != AV_NOPTS_VALUE) - { - //There is a time_base in audio_codec_context too, but that one is wrong. - double dClockPts = iStreamPts * av_q2d(format_context->streams[audio_stream_index]->time_base); - current_sync_pts = dClockPts; - current_sync_pts_system_time = SDL_GetTicks(); - } - fFirst = false; - } - - while(audio_packet->size > 0 || (!audio_packet->data && fNewPacket)) - { - if(!audio_frame) - { - audio_frame = av_frame_alloc(); - } - else - { - av_frame_unref(audio_frame); - } - - if(fFlushComplete) - { - break; - } - - fNewPacket = false; - - int iBytesConsumed = avcodec_decode_audio4(audio_codec_context, audio_frame, &iGotFrame, audio_packet); - - if(iBytesConsumed < 0) - { - audio_packet->size = 0; - break; - } - audio_packet->data += iBytesConsumed; - audio_packet->size -= iBytesConsumed; - - if(!iGotFrame) - { - if(audio_packet->data && (audio_codec_context->codec->capabilities & CODEC_CAP_DELAY)) - { - fFlushComplete = true; - } - } + if (!iGotFrame) { + if (audio_packet->data && + (audio_codec_context->codec->capabilities & CODEC_CAP_DELAY)) { + fFlushComplete = true; } + } } + } #endif - //over-estimate output samples - int iOutSamples = (int)av_rescale_rnd(audio_frame->nb_samples, mixer_frequency, audio_codec_context->sample_rate, AV_ROUND_UP); - int iSampleSize = av_get_bytes_per_sample(AV_SAMPLE_FMT_S16) * iOutSamples * mixer_channels; + // over-estimate output samples + int iOutSamples = + (int)av_rescale_rnd(audio_frame->nb_samples, mixer_frequency, + audio_codec_context->sample_rate, AV_ROUND_UP); + int iSampleSize = + av_get_bytes_per_sample(AV_SAMPLE_FMT_S16) * iOutSamples * mixer_channels; - if(iSampleSize > audio_buffer_max_size) - { - if(audio_buffer_max_size > 0) - { - av_free(audio_buffer); - } - audio_buffer = (uint8_t*)av_malloc(iSampleSize); - audio_buffer_max_size = iSampleSize; + if (iSampleSize > audio_buffer_max_size) { + if (audio_buffer_max_size > 0) { + av_free(audio_buffer); } + audio_buffer = (uint8_t*)av_malloc(iSampleSize); + audio_buffer_max_size = iSampleSize; + } #ifdef CORSIX_TH_USE_FFMPEG - swr_convert(audio_resample_context, &audio_buffer, iOutSamples, (const uint8_t**)&audio_frame->data[0], audio_frame->nb_samples); + swr_convert(audio_resample_context, &audio_buffer, iOutSamples, + (const uint8_t**)&audio_frame->data[0], audio_frame->nb_samples); #elif defined(CORSIX_TH_USE_LIBAV) - avresample_convert(audio_resample_context, &audio_buffer, 0, iOutSamples, (uint8_t**)&audio_frame->data[0], 0, audio_frame->nb_samples); + avresample_convert(audio_resample_context, &audio_buffer, 0, iOutSamples, + (uint8_t**)&audio_frame->data[0], 0, + audio_frame->nb_samples); #endif - return iSampleSize; + return iSampleSize; } -#else //CORSIX_TH_USE_FFMPEG || CORSIX_TH_USE_LIBAV +#else // CORSIX_TH_USE_FFMPEG || CORSIX_TH_USE_LIBAV movie_player::movie_player() {} movie_player::~movie_player() {} -void movie_player::set_renderer(SDL_Renderer *renderer) {} +void movie_player::set_renderer(SDL_Renderer* renderer) {} bool movie_player::movies_enabled() const { return false; } bool movie_player::load(const char* file_path) { return true; } void movie_player::unload() {} -void movie_player::play(int iChannel) -{ - SDL_Event endEvent; - endEvent.type = SDL_USEREVENT_MOVIE_OVER; - SDL_PushEvent(&endEvent); +void movie_player::play(int iChannel) { + SDL_Event endEvent; + endEvent.type = SDL_USEREVENT_MOVIE_OVER; + SDL_PushEvent(&endEvent); } void movie_player::stop() {} int movie_player::get_native_height() const { return 0; } -int movie_player::get_native_width() const { return 0; } -bool movie_player::has_audio_track() const { return false; } +int movie_player::get_native_width() const { return 0; } +bool movie_player::has_audio_track() const { return false; } const char* movie_player::get_last_error() const { return nullptr; } void movie_player::clear_last_error() {} -void movie_player::refresh(const SDL_Rect &destination_rect) {} +void movie_player::refresh(const SDL_Rect& destination_rect) {} void movie_player::allocate_picture_buffer() {} void movie_player::deallocate_picture_buffer() {} void movie_player::read_streams() {} void movie_player::run_video() {} -void movie_player::copy_audio_to_stream(uint8_t *stream, int length) {} -#endif //CORSIX_TH_USE_FFMPEG - - +void movie_player::copy_audio_to_stream(uint8_t* stream, int length) {} +#endif // CORSIX_TH_USE_FFMPEG diff --git a/CorsixTH/Src/th_movie.h b/CorsixTH/Src/th_movie.h index de4db412..d15980fb 100644 --- a/CorsixTH/Src/th_movie.h +++ b/CorsixTH/Src/th_movie.h @@ -23,23 +23,23 @@ SOFTWARE. #ifndef TH_VIDEO_H #define TH_VIDEO_H -#include -#include -#include -#include -#include -#include -#include "SDL.h" #include "config.h" +#include +#include +#include +#include +#include +#include +#include "SDL.h" -#if (defined(CORSIX_TH_USE_FFMPEG) || defined(CORSIX_TH_USE_LIBAV)) && defined(CORSIX_TH_USE_SDL_MIXER) +#if (defined(CORSIX_TH_USE_FFMPEG) || defined(CORSIX_TH_USE_LIBAV)) && \ + defined(CORSIX_TH_USE_SDL_MIXER) #include "SDL_mixer.h" -extern "C" -{ +extern "C" { #ifndef INT64_C -#define INT64_C(c) (c ## LL) -#define UINT64_C(c) (c ## ULL) +#define INT64_C(c) (c##LL) +#define UINT64_C(c) (c##ULL) #endif #include #include @@ -51,342 +51,379 @@ extern "C" #endif } -#if (defined(CORSIX_TH_USE_FFMEPG) && LIBAVUTIL_VERSION_INT < AV_VERSION_INT(51, 74, 100)) || \ - (defined(CORSIX_TH_USE_LIBAV) && LIBAVUTIL_VERSION_INT < AV_VERSION_INT(51, 42, 0)) +#if (defined(CORSIX_TH_USE_FFMEPG) && \ + LIBAVUTIL_VERSION_INT < AV_VERSION_INT(51, 74, 100)) || \ + (defined(CORSIX_TH_USE_LIBAV) && \ + LIBAVUTIL_VERSION_INT < AV_VERSION_INT(51, 42, 0)) #define AVPixelFormat PixelFormat #define AV_PIX_FMT_RBG24 PIX_FMT_RGB24 #endif -#if (defined(CORSIX_TH_USE_LIBAV) && LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 16, 0)) || \ - (defined(CORSIX_TH_USE_FFMPEG) && LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 37, 100)) +#if (defined(CORSIX_TH_USE_LIBAV) && \ + LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 16, 0)) || \ + (defined(CORSIX_TH_USE_FFMPEG) && \ + LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 37, 100)) #define CORSIX_TH_MOVIE_USE_SEND_PACKET_API #endif - //! \brief A picture in movie_picture_buffer //! //! Stores the picture from a frame in the movie from the time that it is //! processed until it should be drawn. -class movie_picture -{ -public: - movie_picture(); - ~movie_picture(); +class movie_picture { + public: + movie_picture(); + ~movie_picture(); - //! Allocate the buffer to hold a picture of the given size - void allocate(int iWidth, int iHeight); + //! Allocate the buffer to hold a picture of the given size + void allocate(int iWidth, int iHeight); - //! Delete the buffer - void deallocate(); + //! Delete the buffer + void deallocate(); - uint8_t* buffer; ///< Pixel data in #m_pixelFormat - const AVPixelFormat pixel_format; ///< The format of pixels to output - int width; ///< Picture width - int height; ///< Picture height - double pts; ///< Presentation time stamp - std::mutex mutex; ///< Mutex protecting this picture + uint8_t* buffer; ///< Pixel data in #m_pixelFormat + const AVPixelFormat pixel_format; ///< The format of pixels to output + int width; ///< Picture width + int height; ///< Picture height + double pts; ///< Presentation time stamp + std::mutex mutex; ///< Mutex protecting this picture }; //! A buffer for holding movie pictures and drawing them to the renderer -class movie_picture_buffer -{ -public: - movie_picture_buffer(); - ~movie_picture_buffer(); +class movie_picture_buffer { + public: + movie_picture_buffer(); + ~movie_picture_buffer(); - //NB: The following functions are called by the main program thread + // NB: The following functions are called by the main program thread - //! Indicate that processing should stop and the movie aborted - void abort(); + //! Indicate that processing should stop and the movie aborted + void abort(); - //! Resume after having aborted - void reset(); + //! Resume after having aborted + void reset(); - //! Ready the picture buffer for a new renderer or new picture dimensions - //! by allocating each movie_picture in the queue, resetting the read - //! index and allocating a new texture. - //! - //! \remark Must be run on the program's graphics thread - void allocate(SDL_Renderer *pRenderer, int iWidth, int iHeight); + //! Ready the picture buffer for a new renderer or new picture dimensions + //! by allocating each movie_picture in the queue, resetting the read + //! index and allocating a new texture. + //! + //! \remark Must be run on the program's graphics thread + void allocate(SDL_Renderer* pRenderer, int iWidth, int iHeight); - //! Destroy the associated texture and deallocate each of the - //! movie_pictures in the queue so that the program can release - //! the renderer - //! - //! \remark Must be run on the program's graphics thread - void deallocate(); + //! Destroy the associated texture and deallocate each of the + //! movie_pictures in the queue so that the program can release + //! the renderer + //! + //! \remark Must be run on the program's graphics thread + void deallocate(); - //! Advance the read index - bool advance(); + //! Advance the read index + bool advance(); - //! Draw the movie_picture at the current read index - //! - //! \param pRenderer The renderer to draw the picture to - //! \param dstrect The rectangle on the renderer to draw to - //! - //! \remark Must be run on the program's graphics thread - void draw(SDL_Renderer *pRenderer, const SDL_Rect &dstrect); + //! Draw the movie_picture at the current read index + //! + //! \param pRenderer The renderer to draw the picture to + //! \param dstrect The rectangle on the renderer to draw to + //! + //! \remark Must be run on the program's graphics thread + void draw(SDL_Renderer* pRenderer, const SDL_Rect& dstrect); - //! Get the next presentation time stamp - double get_next_pts(); + //! Get the next presentation time stamp + double get_next_pts(); - //! Return whether there are any pictures left to draw in the picture queue - //! - //! \remark If the movie_picture_buffer is not allocated it cannot be read from - //! or written to. Consequently it is both full and empty. - bool empty(); + //! Return whether there are any pictures left to draw in the picture queue + //! + //! \remark If the movie_picture_buffer is not allocated it cannot be read + //! from or written to. Consequently it is both full and empty. + bool empty(); - //NB: These functions are called by a second thread + // NB: These functions are called by a second thread - //! Return whether there is space to add any more frame data to the queue - //! - //! \remark If the movie_picture_buffer is not allocated it cannot be read from - //! or written to. Consequently it is both full and empty. - bool full(); + //! Return whether there is space to add any more frame data to the queue + //! + //! \remark If the movie_picture_buffer is not allocated it cannot be read + //! from or written to. Consequently it is both full and empty. + bool full(); - //! Write the given frame (and presentation time stamp) to the picture - //! queue - //! - //! \retval 0 Success - //! \retval -1 Abort is in progress - //! \retval 1 An error writing the frame - int write(AVFrame* pFrame, double dPts); -private: - //! Return whether there is space to add any more frame data to the queue - //! - //! \remark Requires external locking - bool unsafe_full(); + //! Write the given frame (and presentation time stamp) to the picture + //! queue + //! + //! \retval 0 Success + //! \retval -1 Abort is in progress + //! \retval 1 An error writing the frame + int write(AVFrame* pFrame, double dPts); - static constexpr size_t picture_buffer_size = 4; ///< The number of elements to allocate in the picture queue - std::atomic aborting; ///< Whether we are in the process of aborting - bool allocated; ///< Whether the picture buffer has been allocated (and hasn't since been deallocated) - int picture_count; ///< The number of elements currently written to the picture queue - int read_index; ///< The position in the picture queue to be read next - int write_index; ///< The position in the picture queue to be written to next - SwsContext* sws_context; ///< The context for software scaling and pixel conversion when writing to the picture queue - SDL_Texture *texture; ///< The (potentially hardware) texture to draw the picture to. In OpenGL this should only be accessed on the main thread - std::mutex mutex; ///< A mutex for restricting access to the picture buffer to a single thread - std::condition_variable cond; ///< A condition for indicating access to the picture buffer - movie_picture picture_queue[picture_buffer_size]; ///< The picture queue, a looping FIFO queue of movie_pictures + private: + //! Return whether there is space to add any more frame data to the queue + //! + //! \remark Requires external locking + bool unsafe_full(); + + static constexpr size_t picture_buffer_size = + 4; ///< The number of elements to allocate in the picture queue + std::atomic aborting; ///< Whether we are in the process of aborting + bool allocated; ///< Whether the picture buffer has been allocated (and + ///< hasn't since been deallocated) + int picture_count; ///< The number of elements currently written to the + ///< picture queue + int read_index; ///< The position in the picture queue to be read next + int write_index; ///< The position in the picture queue to be written to + ///< next + SwsContext* sws_context; ///< The context for software scaling and pixel + ///< conversion when writing to the picture queue + SDL_Texture* texture; ///< The (potentially hardware) texture to draw the + ///< picture to. In OpenGL this should only be + ///< accessed on the main thread + std::mutex mutex; ///< A mutex for restricting access to the picture buffer + ///< to a single thread + std::condition_variable + cond; ///< A condition for indicating access to the picture buffer + movie_picture picture_queue[picture_buffer_size]; ///< The picture queue, a + ///< looping FIFO queue + ///< of movie_pictures }; //! The AVPacketQueue is a thread safe queue of movie packets -class av_packet_queue -{ -public: - //! Construct a new empty packet queue - av_packet_queue(); +class av_packet_queue { + public: + //! Construct a new empty packet queue + av_packet_queue(); - //! Destroy the packet queue. - //! - //! \remarks Does not free the included packets. The packet queue should be - //! flushed before it is destroyed. - ~av_packet_queue(); + //! Destroy the packet queue. + //! + //! \remarks Does not free the included packets. The packet queue should be + //! flushed before it is destroyed. + ~av_packet_queue(); - //! Push a new packet on the back of the queue - void push(AVPacket *packet); + //! Push a new packet on the back of the queue + void push(AVPacket* packet); - //! Pull the packet from the front of the queue - //! - //! \param block Whether to block if the queue is empty or immediately - //! return a nullptr - AVPacket* pull(bool block); + //! Pull the packet from the front of the queue + //! + //! \param block Whether to block if the queue is empty or immediately + //! return a nullptr + AVPacket* pull(bool block); - //! Return the number of packets in the queue - int get_count() const; + //! Return the number of packets in the queue + int get_count() const; - //! Release a blocking pull without writing a new packet to the queue. - void release(); -private: - AVPacketList *first_packet; ///< The packet at the front of the queue - AVPacketList *last_packet; ///< The packet at the end of the queue - int count; ///< The number of packets in the queue - std::mutex mutex; ///< A mutex restricting access to the packet queue to a single thread - std::condition_variable cond; ///< A condition to wait on for signaling the packet queue + //! Release a blocking pull without writing a new packet to the queue. + void release(); + + private: + AVPacketList* first_packet; ///< The packet at the front of the queue + AVPacketList* last_packet; ///< The packet at the end of the queue + int count; ///< The number of packets in the queue + std::mutex mutex; ///< A mutex restricting access to the packet queue to a + ///< single thread + std::condition_variable + cond; ///< A condition to wait on for signaling the packet queue }; -#endif //CORSIX_TH_USE_FFMPEG || CORSIX_TH_USE_LIBAV +#endif // CORSIX_TH_USE_FFMPEG || CORSIX_TH_USE_LIBAV //! Movie player for CorsixTH //! //! The movie player is designed to be preinitialized and used for multiple //! movies. After initializing the movie player, call movie_player::set_renderer -//! to assign the current SDL renderer to the movie player. Then movie_player::load -//! the desired movie and finally movie_player::play it. -class movie_player -{ -public: - //! Construct a new movie_player - movie_player(); +//! to assign the current SDL renderer to the movie player. Then +//! movie_player::load the desired movie and finally movie_player::play it. +class movie_player { + public: + //! Construct a new movie_player + movie_player(); - //! Destroy the movie_player - ~movie_player(); + //! Destroy the movie_player + ~movie_player(); - //! Assign the renderer on which to draw the movie - void set_renderer(SDL_Renderer *pRenderer); + //! Assign the renderer on which to draw the movie + void set_renderer(SDL_Renderer* pRenderer); - //! Return whether movies were compiled into CorsixTH - bool movies_enabled() const; + //! Return whether movies were compiled into CorsixTH + bool movies_enabled() const; - //! Load the movie with the given file name - bool load(const char* szFilepath); + //! Load the movie with the given file name + bool load(const char* szFilepath); - //! Unload and free the currently loaded movie. - //! - //! \remark This is called by load before loading a new movie so it is - //! unnecessary to explicitly call this method. There is no harm either. - void unload(); + //! Unload and free the currently loaded movie. + //! + //! \remark This is called by load before loading a new movie so it is + //! unnecessary to explicitly call this method. There is no harm either. + void unload(); - //! Play the currently loaded movie - //! - //! \param iChannel The audio channel to use - void play(int iChannel); + //! Play the currently loaded movie + //! + //! \param iChannel The audio channel to use + void play(int iChannel); - //! Stop the currently playing movie - void stop(); + //! Stop the currently playing movie + void stop(); - //! Return the original height of the movie - int get_native_height() const; + //! Return the original height of the movie + int get_native_height() const; - //! Return the original width of the movie - int get_native_width() const; + //! Return the original width of the movie + int get_native_width() const; - //! Return whether the movie has an audio stream - bool has_audio_track() const; + //! Return whether the movie has an audio stream + bool has_audio_track() const; - //! Return a text description of the last error encountered - const char* get_last_error() const; + //! Return a text description of the last error encountered + const char* get_last_error() const; - //! Clear the last error so that if there is no more errors before the next - //! call to movie_player::get_last_error() it will return an empty string. - void clear_last_error(); + //! Clear the last error so that if there is no more errors before the next + //! call to movie_player::get_last_error() it will return an empty string. + void clear_last_error(); - //! Draw the next frame if it is time to do so - //! - //! \param destination_rect The location and dimensions in the renderer on - //! which to draw the movie - void refresh(const SDL_Rect &destination_rect); + //! Draw the next frame if it is time to do so + //! + //! \param destination_rect The location and dimensions in the renderer on + //! which to draw the movie + void refresh(const SDL_Rect& destination_rect); - //! Deallocate the picture buffer and free any resources associated with it. - //! - //! \remark This destroys the textures and other resources that may lock - //! the renderer from being deleted. If the target changes you would call - //! this, then free and switch renderers in the outside program, then call - //! movie_player::set_renderer and finally movie_player::allocate_picture_buffer. - //! \remark Up to the size of the picture buffer frames may be lost during - //! this process. - void deallocate_picture_buffer(); + //! Deallocate the picture buffer and free any resources associated with it. + //! + //! \remark This destroys the textures and other resources that may lock + //! the renderer from being deleted. If the target changes you would call + //! this, then free and switch renderers in the outside program, then call + //! movie_player::set_renderer and finally + //! movie_player::allocate_picture_buffer. \remark Up to the size of the + //! picture buffer frames may be lost during this process. + void deallocate_picture_buffer(); - //! Allocate the picture buffer for the current renderer - void allocate_picture_buffer(); + //! Allocate the picture buffer for the current renderer + void allocate_picture_buffer(); - //! Read packets from the movie and allocate them to the appropriate stream - //! packet queues. Signal if we have reached the end of the movie. - //! - //! \remark This should not be called externally. It is public as it is the - //! entry point of a thread. - void read_streams(); + //! Read packets from the movie and allocate them to the appropriate stream + //! packet queues. Signal if we have reached the end of the movie. + //! + //! \remark This should not be called externally. It is public as it is the + //! entry point of a thread. + void read_streams(); - //! Read video frames from the video packet queue and write them to the - //! picture queue. - //! - //! \remark This should not be called externally. It is public as it is the - //! entry point of a thread. - void run_video(); + //! Read video frames from the video packet queue and write them to the + //! picture queue. + //! + //! \remark This should not be called externally. It is public as it is the + //! entry point of a thread. + void run_video(); - //! Read audio from the audio packet queue, and copy it into the audio - //! buffer for playback - void copy_audio_to_stream(uint8_t *pbStream, int iStreamSize); + //! Read audio from the audio packet queue, and copy it into the audio + //! buffer for playback + void copy_audio_to_stream(uint8_t* pbStream, int iStreamSize); -private: -#if (defined(CORSIX_TH_USE_FFMPEG) || defined(CORSIX_TH_USE_LIBAV)) && defined(CORSIX_TH_USE_SDL_MIXER) - static constexpr size_t movie_error_buffer_capacity = 128; ///< Buffer to hold last error description - static constexpr size_t audio_chunk_buffer_capacity = 1024; ///< Buffer for audio playback + private: +#if (defined(CORSIX_TH_USE_FFMPEG) || defined(CORSIX_TH_USE_LIBAV)) && \ + defined(CORSIX_TH_USE_SDL_MIXER) + static constexpr size_t movie_error_buffer_capacity = + 128; ///< Buffer to hold last error description + static constexpr size_t audio_chunk_buffer_capacity = + 1024; ///< Buffer for audio playback - //! Get the AVCodecContext associated with a given stream - AVCodecContext* get_codec_context_for_stream(AVCodec* codec, AVStream* stream) const; + //! Get the AVCodecContext associated with a given stream + AVCodecContext* get_codec_context_for_stream(AVCodec* codec, + AVStream* stream) const; - //! Get the time the given frame should be played (from the start of the stream) - //! - //! \param frame The video or audio frame - //! \param streamIndex The position of the stream in m_pFormatContexts streams array - double get_presentation_time_for_frame(AVFrame* frame, int streamIndex) const; + //! Get the time the given frame should be played (from the start of the + //! stream) + //! + //! \param frame The video or audio frame + //! \param streamIndex The position of the stream in m_pFormatContexts + //! streams array + double get_presentation_time_for_frame(AVFrame* frame, int streamIndex) const; - //! Decode audio from the movie into a format suitable for playback - int decode_audio_frame(bool fFirst); + //! Decode audio from the movie into a format suitable for playback + int decode_audio_frame(bool fFirst); #ifdef CORSIX_TH_MOVIE_USE_SEND_PACKET_API - //! Convert packet data into frames - //! - //! \param stream The index of the stream to get the frame for - //! \param pFrame An empty frame which gets populated by the data in the - //! packet queue. - //! \returns FFMPEG result of avcodec_recieve_frame - int get_frame(int stream, AVFrame* pFrame); + //! Convert packet data into frames + //! + //! \param stream The index of the stream to get the frame for + //! \param pFrame An empty frame which gets populated by the data in the + //! packet queue. + //! \returns FFMPEG result of avcodec_recieve_frame + int get_frame(int stream, AVFrame* pFrame); #else - //! Convert video packet data into a frame. - //! - //! \param pFrame An empty frame which gets populated by the data in the - //! video packet queue. - //! \returns 1 if the frame was received, 0 if it was not, and < 0 on error - int get_video_frame(AVFrame *pFrame); + //! Convert video packet data into a frame. + //! + //! \param pFrame An empty frame which gets populated by the data in the + //! video packet queue. + //! \returns 1 if the frame was received, 0 if it was not, and < 0 on error + int get_video_frame(AVFrame* pFrame); #endif - SDL_Renderer *renderer; ///< The renderer to draw to + SDL_Renderer* renderer; ///< The renderer to draw to - //! A description of the last error - std::string last_error; + //! A description of the last error + std::string last_error; - //! A buffer for passing to ffmpeg to get error details - char error_buffer[movie_error_buffer_capacity]; + //! A buffer for passing to ffmpeg to get error details + char error_buffer[movie_error_buffer_capacity]; - // TODO: Should be atomic - bool aborting; ///< Indicate that we are in process of aborting playback + // TODO: Should be atomic + bool aborting; ///< Indicate that we are in process of aborting playback - std::mutex decoding_audio_mutex; ///< Synchronize access to #m_pAudioBuffer + std::mutex decoding_audio_mutex; ///< Synchronize access to #m_pAudioBuffer - AVFormatContext* format_context; ///< Information related to the loaded movie and all of its streams - int video_stream_index; ///< The index of the video stream - int audio_stream_index; ///< The index of the audio stream - AVCodecContext *video_codec_context; ///< The video codec and information related to video - AVCodecContext *audio_codec_context; ///< The audio codec and information related to audio + AVFormatContext* format_context; ///< Information related to the loaded + ///< movie and all of its streams + int video_stream_index; ///< The index of the video stream + int audio_stream_index; ///< The index of the audio stream + AVCodecContext* video_codec_context; ///< The video codec and information + ///< related to video + AVCodecContext* audio_codec_context; ///< The audio codec and information + ///< related to audio - //queues for transferring data between threads - av_packet_queue *video_queue; ///< Packets from the video stream - av_packet_queue *audio_queue; ///< Packets from the audio stream - ::movie_picture_buffer *movie_picture_buffer; ///< Buffer of processed video + // queues for transferring data between threads + av_packet_queue* video_queue; ///< Packets from the video stream + av_packet_queue* audio_queue; ///< Packets from the audio stream + ::movie_picture_buffer* movie_picture_buffer; ///< Buffer of processed video - //clock sync parameters - int current_sync_pts_system_time; ///< System time matching #m_iCurSyncPts - double current_sync_pts; ///< The current presentation time stamp (from the audio stream) + // clock sync parameters + int current_sync_pts_system_time; ///< System time matching #m_iCurSyncPts + double current_sync_pts; ///< The current presentation time stamp (from the + ///< audio stream) #ifdef CORSIX_TH_USE_FFMPEG - SwrContext* audio_resample_context; ///< Context for resampling audio for playback with ffmpeg + SwrContext* audio_resample_context; ///< Context for resampling audio for + ///< playback with ffmpeg #elif defined(CORSIX_TH_USE_LIBAV) - AVAudioResampleContext* audio_resample_context; ///< Context for resampling audio for playback with libav + AVAudioResampleContext* + audio_resample_context; ///< Context for resampling audio for + ///< playback with libav #endif - int audio_buffer_size; ///< The current size of audio data in #m_pbAudioBuffer - int audio_buffer_index; ///< The current position for writing in #m_pbAudioBuffer - int audio_buffer_max_size; ///< The capacity of #m_pbAudioBuffer (allocated size) - uint8_t* audio_buffer; ///< An audio buffer for playback + int audio_buffer_size; ///< The current size of audio data in + ///< #m_pbAudioBuffer + int audio_buffer_index; ///< The current position for writing in + ///< #m_pbAudioBuffer + int audio_buffer_max_size; ///< The capacity of #m_pbAudioBuffer (allocated + ///< size) + uint8_t* audio_buffer; ///< An audio buffer for playback - AVPacket* audio_packet; ///< The current audio packet being decoded (audio frames don't necessarily line up with packets) - int audio_packet_size; ///< The size of #m_pbAudioPacketData - uint8_t *audio_packet_data; ///< Original data for #m_pAudioPacket, kept so that it can be freed after the packet is processed - AVFrame* audio_frame; ///< The frame we are decoding audio into + AVPacket* audio_packet; ///< The current audio packet being decoded (audio + ///< frames don't necessarily line up with packets) + int audio_packet_size; ///< The size of #m_pbAudioPacketData + uint8_t* audio_packet_data; ///< Original data for #m_pAudioPacket, kept so + ///< that it can be freed after the packet is + ///< processed + AVFrame* audio_frame; ///< The frame we are decoding audio into - Mix_Chunk* empty_audio_chunk; ///< Empty chunk needed for SDL_mixer - uint8_t* audio_chunk_buffer; ///< 0'd out buffer for the SDL_Mixer chunk + Mix_Chunk* empty_audio_chunk; ///< Empty chunk needed for SDL_mixer + uint8_t* audio_chunk_buffer; ///< 0'd out buffer for the SDL_Mixer chunk - int audio_channel; ///< The channel to play audio on, -1 for none - int mixer_channels; ///< How many channels to play on (1 - mono, 2 - stereo) - int mixer_frequency; ///< The frequency of audio expected by SDL_Mixer + int audio_channel; ///< The channel to play audio on, -1 for none + int mixer_channels; ///< How many channels to play on (1 - mono, 2 - + ///< stereo) + int mixer_frequency; ///< The frequency of audio expected by SDL_Mixer - AVPacket* flush_packet; ///< A representative packet indicating a flush is required. + AVPacket* flush_packet; ///< A representative packet indicating a flush is + ///< required. - std::thread stream_thread; ///< The thread responsible for reading the movie streams - std::thread video_thread; ///< The thread responsible for decoding the video stream -#endif //CORSIX_TH_USE_FFMPEG || CORSIX_TH_USE_LIBAV + std::thread stream_thread; ///< The thread responsible for reading the + ///< movie streams + std::thread video_thread; ///< The thread responsible for decoding the + ///< video stream +#endif // CORSIX_TH_USE_FFMPEG || CORSIX_TH_USE_LIBAV }; -#endif // TH_VIDEO_H +#endif // TH_VIDEO_H diff --git a/CorsixTH/Src/th_pathfind.cpp b/CorsixTH/Src/th_pathfind.cpp index 62222f2d..ad96d33e 100644 --- a/CorsixTH/Src/th_pathfind.cpp +++ b/CorsixTH/Src/th_pathfind.cpp @@ -20,621 +20,625 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -#include "config.h" #include "th_pathfind.h" -#include "persist_lua.h" -#include "lua.hpp" +#include "config.h" +#include #include #include -#include #include +#include "lua.hpp" +#include "persist_lua.h" -abstract_pathfinder::abstract_pathfinder(pathfinder *pf) : parent(pf) -{ } +abstract_pathfinder::abstract_pathfinder(pathfinder* pf) : parent(pf) {} -path_node *abstract_pathfinder::init(const level_map *pMap, int iStartX, int iStartY) -{ - int iWidth = pMap->get_width(); - parent->destination = nullptr; - parent->allocate_node_cache(iWidth, pMap->get_height()); - path_node *pNode = parent->nodes + iStartY * iWidth + iStartX; - pNode->prev = nullptr; - pNode->distance = 0; - pNode->guess = guess_distance(pNode); - parent->dirty_node_list[0] = pNode; - parent->dirty_node_count = 1; - parent->open_heap.clear(); - return pNode; +path_node* abstract_pathfinder::init(const level_map* pMap, int iStartX, + int iStartY) { + int iWidth = pMap->get_width(); + parent->destination = nullptr; + parent->allocate_node_cache(iWidth, pMap->get_height()); + path_node* pNode = parent->nodes + iStartY * iWidth + iStartX; + pNode->prev = nullptr; + pNode->distance = 0; + pNode->guess = guess_distance(pNode); + parent->dirty_node_list[0] = pNode; + parent->dirty_node_count = 1; + parent->open_heap.clear(); + return pNode; } /*! No need to check for the node being on the map edge, as the N/E/S/W flags are set as to prevent travelling off the map (as well as to prevent walking through walls). */ -bool abstract_pathfinder::search_neighbours(path_node *pNode, map_tile_flags flags, int iWidth) -{ - if(flags.can_travel_w) - if(try_node(pNode, flags, pNode - 1, travel_direction::west)) return true; - - if(flags.can_travel_e) - if (try_node(pNode, flags, pNode + 1, travel_direction::east)) return true; - - if(flags.can_travel_n) - if (try_node(pNode, flags, pNode - iWidth, travel_direction::north)) return true; - - if(flags.can_travel_s) - if (try_node(pNode, flags, pNode + iWidth, travel_direction::south)) return true; - - return false; -} - -void abstract_pathfinder::record_neighbour_if_passable(path_node *pNode, map_tile_flags neighbour_flags, bool passable, path_node *pNeighbour) -{ - if(neighbour_flags.passable || !passable) - { - if(pNeighbour->prev == pNeighbour) - { - pNeighbour->prev = pNode; - pNeighbour->distance = pNode->distance + 1; - pNeighbour->guess = guess_distance(pNeighbour); - parent->dirty_node_list[parent->dirty_node_count++] = pNeighbour; - parent->push_to_open_heap(pNeighbour); - } - else if(pNode->distance + 1 < pNeighbour->distance) - { - pNeighbour->prev = pNode; - pNeighbour->distance = pNode->distance + 1; - /* guess doesn't change, and already in the dirty list */ - parent->open_heap_promote(pNeighbour); - } +bool abstract_pathfinder::search_neighbours(path_node* pNode, + map_tile_flags flags, int iWidth) { + if (flags.can_travel_w) { + if (try_node(pNode, flags, pNode - 1, travel_direction::west)) { + return true; } + } + + if (flags.can_travel_e) { + if (try_node(pNode, flags, pNode + 1, travel_direction::east)) { + return true; + } + } + + if (flags.can_travel_n) { + if (try_node(pNode, flags, pNode - iWidth, travel_direction::north)) { + return true; + } + } + + if (flags.can_travel_s) { + if (try_node(pNode, flags, pNode + iWidth, travel_direction::south)) { + return true; + } + } + + return false; } -int basic_pathfinder::guess_distance(path_node *pNode) -{ - // As diagonal movement is not allowed, the minimum distance between two - // points is the sum of the distance in X and the distance in Y. - return abs(pNode->x - destination_x) + abs(pNode->y - destination_y); +void abstract_pathfinder::record_neighbour_if_passable( + path_node* pNode, map_tile_flags neighbour_flags, bool passable, + path_node* pNeighbour) { + if (neighbour_flags.passable || !passable) { + if (pNeighbour->prev == pNeighbour) { + pNeighbour->prev = pNode; + pNeighbour->distance = pNode->distance + 1; + pNeighbour->guess = guess_distance(pNeighbour); + parent->dirty_node_list[parent->dirty_node_count++] = pNeighbour; + parent->push_to_open_heap(pNeighbour); + } else if (pNode->distance + 1 < pNeighbour->distance) { + pNeighbour->prev = pNode; + pNeighbour->distance = pNode->distance + 1; + /* guess doesn't change, and already in the dirty list */ + parent->open_heap_promote(pNeighbour); + } + } } -bool basic_pathfinder::try_node(path_node *pNode, map_tile_flags flags, - path_node *pNeighbour, travel_direction direction) -{ - map_tile_flags neighbour_flags = map->get_tile_unchecked(pNeighbour->x, pNeighbour->y)->flags; - record_neighbour_if_passable(pNode, neighbour_flags, flags.passable, pNeighbour); +int basic_pathfinder::guess_distance(path_node* pNode) { + // As diagonal movement is not allowed, the minimum distance between two + // points is the sum of the distance in X and the distance in Y. + return abs(pNode->x - destination_x) + abs(pNode->y - destination_y); +} + +bool basic_pathfinder::try_node(path_node* pNode, map_tile_flags flags, + path_node* pNeighbour, + travel_direction direction) { + map_tile_flags neighbour_flags = + map->get_tile_unchecked(pNeighbour->x, pNeighbour->y)->flags; + record_neighbour_if_passable(pNode, neighbour_flags, flags.passable, + pNeighbour); + return false; +} + +bool basic_pathfinder::find_path(const level_map* pMap, int iStartX, + int iStartY, int iEndX, int iEndY) { + if (pMap == nullptr) { + pMap = parent->default_map; + } + if (pMap == nullptr || pMap->get_tile(iEndX, iEndY) == nullptr || + !pMap->get_tile_unchecked(iEndX, iEndY)->flags.passable) { + parent->destination = nullptr; return false; -} + } -bool basic_pathfinder::find_path(const level_map *pMap, int iStartX, int iStartY, int iEndX, int iEndY) -{ - if(pMap == nullptr) - pMap = parent->default_map; - if(pMap == nullptr || pMap->get_tile(iEndX, iEndY) == nullptr - || !pMap->get_tile_unchecked(iEndX, iEndY)->flags.passable) - { - parent->destination = nullptr; - return false; + map = pMap; + destination_x = iEndX; + destination_y = iEndY; + + path_node* pNode = init(pMap, iStartX, iStartY); + int iWidth = pMap->get_width(); + path_node* pTarget = parent->nodes + iEndY * iWidth + iEndX; + + while (true) { + if (pNode == pTarget) { + parent->destination = pTarget; + return true; } - map = pMap; - destination_x = iEndX; - destination_y = iEndY; - - path_node *pNode = init(pMap, iStartX, iStartY); - int iWidth = pMap->get_width(); - path_node *pTarget = parent->nodes + iEndY * iWidth + iEndX; - - while(true) - { - if(pNode == pTarget) - { - parent->destination = pTarget; - return true; - } - - map_tile_flags flags = pMap->get_tile_unchecked(pNode->x, pNode->y)->flags; - if (search_neighbours(pNode, flags, iWidth)) return true; - - if (parent->open_heap.empty()) { - parent->destination = nullptr; - break; - } else { - pNode = parent->pop_from_open_heap(); - } - } - return false; -} - -int hospital_finder::guess_distance(path_node *pNode) -{ - return 0; -} - -bool hospital_finder::try_node(path_node *pNode, map_tile_flags flags, - path_node *pNeighbour, travel_direction direction) -{ - map_tile_flags neighbour_flags = map->get_tile_unchecked(pNeighbour->x, pNeighbour->y)->flags; - record_neighbour_if_passable(pNode, neighbour_flags, flags.passable, pNeighbour); - return false; -} - -bool hospital_finder::find_path_to_hospital(const level_map *pMap, int iStartX, int iStartY) -{ - if(pMap == nullptr) - pMap = parent->default_map; - if(pMap == nullptr || pMap->get_tile(iStartX, iStartY) == nullptr - || !pMap->get_tile_unchecked(iStartX, iStartY)->flags.passable) - { - parent->destination = nullptr; - return false; + map_tile_flags flags = pMap->get_tile_unchecked(pNode->x, pNode->y)->flags; + if (search_neighbours(pNode, flags, iWidth)) { + return true; } - map = pMap; - - path_node *pNode = init(pMap, iStartX, iStartY); - int iWidth = pMap->get_width(); - - while(true) - { - map_tile_flags flags = pMap->get_tile_unchecked(pNode->x, pNode->y)->flags; - - if(flags.hospital) - { - parent->destination = pNode; - return true; - } - - if (search_neighbours(pNode, flags, iWidth)) return true; - - if (parent->open_heap.empty()) { - parent->destination = nullptr; - break; - } else { - pNode = parent->pop_from_open_heap(); - } + if (parent->open_heap.empty()) { + parent->destination = nullptr; + break; + } else { + pNode = parent->pop_from_open_heap(); } + } + return false; +} + +int hospital_finder::guess_distance(path_node* pNode) { return 0; } + +bool hospital_finder::try_node(path_node* pNode, map_tile_flags flags, + path_node* pNeighbour, + travel_direction direction) { + map_tile_flags neighbour_flags = + map->get_tile_unchecked(pNeighbour->x, pNeighbour->y)->flags; + record_neighbour_if_passable(pNode, neighbour_flags, flags.passable, + pNeighbour); + return false; +} + +bool hospital_finder::find_path_to_hospital(const level_map* pMap, int iStartX, + int iStartY) { + if (pMap == nullptr) { + pMap = parent->default_map; + } + if (pMap == nullptr || pMap->get_tile(iStartX, iStartY) == nullptr || + !pMap->get_tile_unchecked(iStartX, iStartY)->flags.passable) { + parent->destination = nullptr; return false; + } + + map = pMap; + + path_node* pNode = init(pMap, iStartX, iStartY); + int iWidth = pMap->get_width(); + + while (true) { + map_tile_flags flags = pMap->get_tile_unchecked(pNode->x, pNode->y)->flags; + + if (flags.hospital) { + parent->destination = pNode; + return true; + } + + if (search_neighbours(pNode, flags, iWidth)) { + return true; + } + + if (parent->open_heap.empty()) { + parent->destination = nullptr; + break; + } else { + pNode = parent->pop_from_open_heap(); + } + } + return false; } -int idle_tile_finder::guess_distance(path_node *pNode) -{ - return 0; -} +int idle_tile_finder::guess_distance(path_node* pNode) { return 0; } -bool idle_tile_finder::try_node(path_node *pNode, map_tile_flags flags, - path_node *pNeighbour, travel_direction direction) -{ - map_tile_flags neighbour_flags = map->get_tile_unchecked(pNeighbour->x, pNeighbour->y)->flags; - /* When finding an idle tile, do not navigate through doors */ - switch(direction) - { +bool idle_tile_finder::try_node(path_node* pNode, map_tile_flags flags, + path_node* pNeighbour, + travel_direction direction) { + map_tile_flags neighbour_flags = + map->get_tile_unchecked(pNeighbour->x, pNeighbour->y)->flags; + /* When finding an idle tile, do not navigate through doors */ + switch (direction) { case travel_direction::north: - if(!flags.door_north) - record_neighbour_if_passable(pNode, neighbour_flags, flags.passable, pNeighbour); - break; + if (!flags.door_north) { + record_neighbour_if_passable(pNode, neighbour_flags, flags.passable, + pNeighbour); + } + break; case travel_direction::east: - if(!neighbour_flags.door_west) - record_neighbour_if_passable(pNode, neighbour_flags, flags.passable, pNeighbour); - break; + if (!neighbour_flags.door_west) { + record_neighbour_if_passable(pNode, neighbour_flags, flags.passable, + pNeighbour); + } + break; case travel_direction::south: - if(!neighbour_flags.door_north) - record_neighbour_if_passable(pNode, neighbour_flags, flags.passable, pNeighbour); - break; + if (!neighbour_flags.door_north) { + record_neighbour_if_passable(pNode, neighbour_flags, flags.passable, + pNeighbour); + } + break; case travel_direction::west: - if(!flags.door_west) - record_neighbour_if_passable(pNode, neighbour_flags, flags.passable, pNeighbour); + if (!flags.door_west) { + record_neighbour_if_passable(pNode, neighbour_flags, flags.passable, + pNeighbour); + } + break; + } + + /* Identify the neighbour in the open list nearest to the start */ + if (pNeighbour->prev != pNeighbour && pNeighbour->open_idx != -1) { + int iDX = pNeighbour->x - start_x; + int iDY = pNeighbour->y - start_y; + double fDistance = sqrt((double)(iDX * iDX + iDY * iDY)); + if (best_next_node == nullptr || fDistance < best_distance) { + best_next_node = pNeighbour; + best_distance = fDistance; + } + } + return false; +} + +bool idle_tile_finder::find_idle_tile(const level_map* pMap, int iStartX, + int iStartY, int iN) { + if (pMap == nullptr) { + pMap = parent->default_map; + } + if (pMap == nullptr) { + parent->destination = nullptr; + return false; + } + + start_x = iStartX; + start_y = iStartY; + map = pMap; + + path_node* pNode = init(pMap, iStartX, iStartY); + int iWidth = pMap->get_width(); + path_node* pPossibleResult = nullptr; + + while (true) { + pNode->open_idx = -1; + map_tile_flags flags = pMap->get_tile_unchecked(pNode->x, pNode->y)->flags; + + if (!flags.do_not_idle && flags.passable && flags.hospital) { + if (iN == 0) { + parent->destination = pNode; + return true; + } else { + pPossibleResult = pNode; + --iN; + } + } + + best_next_node = nullptr; + best_distance = 0.0; + + if (search_neighbours(pNode, flags, iWidth)) { + return true; + } + + if (parent->open_heap.empty()) { + parent->destination = nullptr; + break; + } + + if (best_next_node) { + // Promote the best neighbour to the front of the open list + // This causes sequential iN to give neighbouring results for most + // iN + best_next_node->guess = -best_next_node->distance; + parent->open_heap_promote(best_next_node); + } + pNode = parent->pop_from_open_heap(); + } + if (pPossibleResult) { + parent->destination = pPossibleResult; + return true; + } + return false; +} + +int object_visitor::guess_distance(path_node* pNode) { return 0; } + +bool object_visitor::try_node(path_node* pNode, map_tile_flags flags, + path_node* pNeighbour, + travel_direction direction) { + int iObjectNumber = 0; + const map_tile* pMapNode = + map->get_tile_unchecked(pNeighbour->x, pNeighbour->y); + map_tile_flags neighbour_flags = + map->get_tile_unchecked(pNeighbour->x, pNeighbour->y)->flags; + for (auto thob : pMapNode->objects) { + if (thob == target) { + iObjectNumber++; + } + } + if (target_any_object_type) { + iObjectNumber = 1; + } + bool bSucces = false; + for (int i = 0; i < iObjectNumber; i++) { + /* call the given Lua function, passing four arguments: */ + /* The x and y position of the object (Lua tile co-ords) */ + /* The direction which was last travelled in to reach (x,y); */ + /* 0 (north), 1 (east), 2 (south), 3 (west) */ + /* The distance to the object from the search starting point */ + lua_pushvalue(L, visit_function_index); + lua_pushinteger(L, pNeighbour->x + 1); + lua_pushinteger(L, pNeighbour->y + 1); + lua_pushinteger(L, static_cast(direction)); + lua_pushinteger(L, pNode->distance); + lua_call(L, 4, 1); + if (lua_toboolean(L, -1) != 0) { + bSucces = true; + } + lua_pop(L, 1); + } + if (bSucces) { + return true; + } + + if (pNode->distance < max_distance) { + switch (direction) { + case travel_direction::north: + if (!flags.door_north) { + record_neighbour_if_passable(pNode, neighbour_flags, flags.passable, + pNeighbour); + } + break; + + case travel_direction::east: + if (!neighbour_flags.door_west) { + record_neighbour_if_passable(pNode, neighbour_flags, flags.passable, + pNeighbour); + } + break; + + case travel_direction::south: + if (!neighbour_flags.door_north) { + record_neighbour_if_passable(pNode, neighbour_flags, flags.passable, + pNeighbour); + } + break; + + case travel_direction::west: + if (!flags.door_west) { + record_neighbour_if_passable(pNode, neighbour_flags, flags.passable, + pNeighbour); + } break; } + } + return false; +} - /* Identify the neighbour in the open list nearest to the start */ - if(pNeighbour->prev != pNeighbour && pNeighbour->open_idx != -1) - { - int iDX = pNeighbour->x - start_x; - int iDY = pNeighbour->y - start_y; - double fDistance = sqrt((double)(iDX * iDX + iDY * iDY)); - if(best_next_node == nullptr || fDistance < best_distance) - { - best_next_node = pNeighbour; best_distance = fDistance; - } - } +bool object_visitor::visit_objects(const level_map* pMap, int iStartX, + int iStartY, object_type eTHOB, + int iMaxDistance, lua_State* L, + int iVisitFunction, bool anyObjectType) { + if (pMap == nullptr) { + pMap = parent->default_map; + } + if (pMap == nullptr) { + parent->destination = nullptr; return false; + } + + this->L = L; + visit_function_index = iVisitFunction; + max_distance = iMaxDistance; + target_any_object_type = anyObjectType; + target = eTHOB; + map = pMap; + + path_node* pNode = init(pMap, iStartX, iStartY); + int iWidth = pMap->get_width(); + + while (true) { + map_tile_flags flags = pMap->get_tile_unchecked(pNode->x, pNode->y)->flags; + if (search_neighbours(pNode, flags, iWidth)) { + return true; + } + + if (parent->open_heap.empty()) { + parent->destination = nullptr; + break; + } else { + pNode = parent->pop_from_open_heap(); + } + } + return false; } -bool idle_tile_finder::find_idle_tile(const level_map *pMap, int iStartX, int iStartY, int iN) -{ - if(pMap == nullptr) - pMap = parent->default_map; - if(pMap == nullptr) - { - parent->destination = nullptr; - return false; - } - - start_x = iStartX; - start_y = iStartY; - map = pMap; - - path_node *pNode = init(pMap, iStartX, iStartY); - int iWidth = pMap->get_width(); - path_node *pPossibleResult = nullptr; - - while(true) - { - pNode->open_idx = -1; - map_tile_flags flags = pMap->get_tile_unchecked(pNode->x, pNode->y)->flags; - - if(!flags.do_not_idle && flags.passable && flags.hospital) - { - if(iN == 0) - { - parent->destination = pNode; - return true; - } - else - { - pPossibleResult = pNode; - --iN; - } - } - - best_next_node = nullptr; - best_distance = 0.0; - - if (search_neighbours(pNode, flags, iWidth)) return true; - - if (parent->open_heap.empty()) { - parent->destination = nullptr; - break; - } - - if(best_next_node) - { - // Promote the best neighbour to the front of the open list - // This causes sequential iN to give neighbouring results for most iN - best_next_node->guess = -best_next_node->distance; - parent->open_heap_promote(best_next_node); - } - pNode = parent->pop_from_open_heap(); - } - if(pPossibleResult) - { - parent->destination = pPossibleResult; - return true; - } - return false; +pathfinder::pathfinder() + : basic_pathfinder(this), + hospital_finder(this), + idle_tile_finder(this), + object_visitor(this), + open_heap() { + nodes = nullptr; + dirty_node_list = nullptr; + destination = nullptr; + default_map = nullptr; + node_cache_width = 0; + node_cache_height = 0; + dirty_node_count = 0; } -int object_visitor::guess_distance(path_node *pNode) -{ - return 0; +pathfinder::~pathfinder() { + delete[] nodes; + delete[] dirty_node_list; } -bool object_visitor::try_node(path_node *pNode, map_tile_flags flags, path_node *pNeighbour, travel_direction direction) -{ - int iObjectNumber = 0; - const map_tile *pMapNode = map->get_tile_unchecked(pNeighbour->x, pNeighbour->y); - map_tile_flags neighbour_flags = map->get_tile_unchecked(pNeighbour->x, pNeighbour->y)->flags; - for(auto thob : pMapNode->objects) - { - if(thob == target) - iObjectNumber++; - } - if(target_any_object_type) - iObjectNumber = 1; - bool bSucces = false; - for(int i = 0; i < iObjectNumber; i++) - { - /* call the given Lua function, passing four arguments: */ - /* The x and y position of the object (Lua tile co-ords) */ - /* The direction which was last travelled in to reach (x,y); */ - /* 0 (north), 1 (east), 2 (south), 3 (west) */ - /* The distance to the object from the search starting point */ - lua_pushvalue(L, visit_function_index); - lua_pushinteger(L, pNeighbour->x + 1); - lua_pushinteger(L, pNeighbour->y + 1); - lua_pushinteger(L, static_cast(direction)); - lua_pushinteger(L, pNode->distance); - lua_call(L, 4, 1); - if(lua_toboolean(L, -1) != 0) - { - bSucces = true; - } - lua_pop(L, 1); - } - if(bSucces) - return true; +void pathfinder::set_default_map(const level_map* pMap) { default_map = pMap; } - if(pNode->distance < max_distance) - { - switch(direction) - { - case travel_direction::north: - if(!flags.door_north) - record_neighbour_if_passable(pNode, neighbour_flags, flags.passable, pNeighbour); - break; - - case travel_direction::east: - if(!neighbour_flags.door_west) - record_neighbour_if_passable(pNode, neighbour_flags, flags.passable, pNeighbour); - break; - - case travel_direction::south: - if(!neighbour_flags.door_north) - record_neighbour_if_passable(pNode, neighbour_flags, flags.passable, pNeighbour); - break; - - case travel_direction::west: - if(!flags.door_west) - record_neighbour_if_passable(pNode, neighbour_flags, flags.passable, pNeighbour); - break; - } - } - return false; -} - -bool object_visitor::visit_objects(const level_map *pMap, int iStartX, int iStartY, - object_type eTHOB, int iMaxDistance, - lua_State *L, int iVisitFunction, bool anyObjectType) -{ - if(pMap == nullptr) - pMap = parent->default_map; - if(pMap == nullptr) - { - parent->destination = nullptr; - return false; - } - - this->L = L; - visit_function_index = iVisitFunction; - max_distance = iMaxDistance; - target_any_object_type = anyObjectType; - target = eTHOB; - map = pMap; - - path_node *pNode = init(pMap, iStartX, iStartY); - int iWidth = pMap->get_width(); - - while(true) - { - map_tile_flags flags = pMap->get_tile_unchecked(pNode->x, pNode->y)->flags; - if (search_neighbours(pNode, flags, iWidth)) return true; - - if (parent->open_heap.empty()) { - parent->destination = nullptr; - break; - } else { - pNode = parent->pop_from_open_heap(); - } - } - return false; -} - -pathfinder::pathfinder() : basic_pathfinder(this), hospital_finder(this), - idle_tile_finder(this), object_visitor(this), - open_heap() -{ - nodes = nullptr; - dirty_node_list = nullptr; - destination = nullptr; - default_map = nullptr; - node_cache_width = 0; - node_cache_height = 0; - dirty_node_count = 0; -} - -pathfinder::~pathfinder() -{ +void pathfinder::allocate_node_cache(int iWidth, int iHeight) { + if (node_cache_width != iWidth || node_cache_height != iHeight) { delete[] nodes; + nodes = new path_node[iWidth * iHeight]; + path_node* pNode = nodes; + for (int iY = 0; iY < iHeight; ++iY) { + for (int iX = 0; iX < iWidth; ++iX, ++pNode) { + pNode->prev = pNode; + pNode->x = iX; + pNode->y = iY; + // Other fields are undefined as the node is not part of a + // path, and thus can be left uninitialised. + } + } delete[] dirty_node_list; + dirty_node_list = new path_node*[iWidth * iHeight]; + node_cache_width = iWidth; + node_cache_height = iHeight; + } else { + for (int i = 0; i < dirty_node_count; ++i) { + dirty_node_list[i]->prev = dirty_node_list[i]; + // Other fields are undefined as the node is not part of a path, + // and thus can keep their old values. + } + } + dirty_node_count = 0; } -void pathfinder::set_default_map(const level_map *pMap) -{ - default_map = pMap; +int pathfinder::get_path_length() const { + if (destination != nullptr) { + return destination->distance; + } else { + return -1; + } } - - -void pathfinder::allocate_node_cache(int iWidth, int iHeight) -{ - if(node_cache_width != iWidth || node_cache_height != iHeight) - { - delete[] nodes; - nodes = new path_node[iWidth * iHeight]; - path_node *pNode = nodes; - for(int iY = 0; iY < iHeight; ++iY) - { - for(int iX = 0; iX < iWidth; ++iX, ++pNode) - { - pNode->prev = pNode; - pNode->x = iX; - pNode->y = iY; - // Other fields are undefined as the node is not part of a - // path, and thus can be left uninitialised. - } - } - delete[] dirty_node_list; - dirty_node_list = new path_node*[iWidth * iHeight]; - node_cache_width = iWidth; - node_cache_height = iHeight; +bool pathfinder::get_path_end(int* pX, int* pY) const { + if (destination == nullptr) { + if (pX) { + *pX = -1; } - else - { - for(int i = 0; i < dirty_node_count; ++i) - { - dirty_node_list[i]->prev = dirty_node_list[i]; - // Other fields are undefined as the node is not part of a path, - // and thus can keep their old values. - } + if (pY) { + *pY = -1; } - dirty_node_count = 0; + return false; + } + if (pX) { + *pX = destination->x; + } + if (pY) { + *pY = destination->y; + } + return true; } -int pathfinder::get_path_length() const -{ - if(destination != nullptr) - return destination->distance; - else - return -1; +void pathfinder::push_result(lua_State* L) const { + lua_checkstack(L, 3); + + if (destination == nullptr) { + lua_pushnil(L); + lua_pushliteral(L, "no path"); + return; + } + + int iLength = destination->distance; + lua_createtable(L, iLength + 1, 0); + lua_createtable(L, iLength + 1, 0); + + for (const path_node* pNode = destination; pNode; pNode = pNode->prev) { + lua_pushinteger(L, pNode->x + 1); + lua_rawseti(L, -3, pNode->distance + 1); + lua_pushinteger(L, pNode->y + 1); + lua_rawseti(L, -2, pNode->distance + 1); + } } -bool pathfinder::get_path_end(int* pX, int* pY) const -{ - if(destination == nullptr) - { - if(pX) - *pX = -1; - if(pY) - *pY = -1; - return false; - } - if(pX) - *pX = destination->x; - if(pY) - *pY = destination->y; - return true; +void pathfinder::push_to_open_heap(path_node* pNode) { + pNode->open_idx = open_heap.size(); + open_heap.push_back(pNode); + open_heap_promote(pNode); } -void pathfinder::push_result(lua_State *L) const -{ - lua_checkstack(L, 3); - - if(destination == nullptr) - { - lua_pushnil(L); - lua_pushliteral(L, "no path"); - return; - } - - int iLength = destination->distance; - lua_createtable(L, iLength + 1, 0); - lua_createtable(L, iLength + 1, 0); - - for(const path_node* pNode = destination; pNode; pNode = pNode->prev) - { - lua_pushinteger(L, pNode->x + 1); - lua_rawseti(L, -3, pNode->distance + 1); - lua_pushinteger(L, pNode->y + 1); - lua_rawseti(L, -2, pNode->distance + 1); +void pathfinder::open_heap_promote(path_node* pNode) { + int i = pNode->open_idx; + while (i > 0) { + int parent = (i - 1) / 2; + path_node* pParent = open_heap[parent]; + if (pParent->value() <= pNode->value()) { + break; } + pParent->open_idx = i; + open_heap[i] = pParent; + open_heap[parent] = pNode; + i = parent; + } + pNode->open_idx = i; } -void pathfinder::push_to_open_heap(path_node* pNode) -{ - pNode->open_idx = open_heap.size(); - open_heap.push_back(pNode); - open_heap_promote(pNode); -} +path_node* pathfinder::pop_from_open_heap() { + path_node* pResult = open_heap[0]; + path_node* pNode = open_heap.back(); + open_heap.pop_back(); -void pathfinder::open_heap_promote(path_node* pNode) -{ - int i = pNode->open_idx; - while(i > 0) - { - int parent = (i - 1) / 2; - path_node *pParent = open_heap[parent]; - if(pParent->value() <= pNode->value()) - break; - pParent->open_idx = i; - open_heap[i] = pParent; - open_heap[parent] = pNode; - i = parent; - } - pNode->open_idx = i; -} - -path_node* pathfinder::pop_from_open_heap() -{ - path_node *pResult = open_heap[0]; - path_node *pNode = open_heap.back(); - open_heap.pop_back(); - - if (open_heap.empty()) { - return pResult; - } - - open_heap[0] = pNode; - int i = 0; - int min = 0; - int left = i * 2 + 1; - const int value = pNode->value(); - while(left < open_heap.size()) - { - min = i; - const int right = (i + 1) * 2; - int minvalue = value; - path_node *pSwap = nullptr; - path_node *pTest = open_heap[left]; - if(pTest->value() < minvalue) - min = left, minvalue = pTest->value(), pSwap = pTest; - if(right < open_heap.size()) - { - pTest = open_heap[right]; - if(pTest->value() < minvalue) - min = right, pSwap = pTest; - } - if(min == i) - break; - - pSwap->open_idx = i; - open_heap[i] = pSwap; - open_heap[min] = pNode; - i = min; - left = i * 2 + 1; - } - pNode->open_idx = min; + if (open_heap.empty()) { return pResult; + } + + open_heap[0] = pNode; + int i = 0; + int min = 0; + int left = i * 2 + 1; + const int value = pNode->value(); + while (left < open_heap.size()) { + min = i; + const int right = (i + 1) * 2; + int minvalue = value; + path_node* pSwap = nullptr; + path_node* pTest = open_heap[left]; + if (pTest->value() < minvalue) { + min = left; + minvalue = pTest->value(); + pSwap = pTest; + } + if (right < open_heap.size()) { + pTest = open_heap[right]; + if (pTest->value() < minvalue) { + min = right; + pSwap = pTest; + } + } + if (min == i) { + break; + } + + pSwap->open_idx = i; + open_heap[i] = pSwap; + open_heap[min] = pNode; + i = min; + left = i * 2 + 1; + } + pNode->open_idx = min; + return pResult; } -void pathfinder::persist(lua_persist_writer *pWriter) const -{ - if(destination == nullptr) - { - pWriter->write_uint(0); - return; - } - pWriter->write_uint(get_path_length() + 1); - pWriter->write_uint(node_cache_width); - pWriter->write_uint(node_cache_height); - for(const path_node* pNode = destination; pNode; pNode = pNode->prev) - { - pWriter->write_uint(pNode->x); - pWriter->write_uint(pNode->y); - } +void pathfinder::persist(lua_persist_writer* pWriter) const { + if (destination == nullptr) { + pWriter->write_uint(0); + return; + } + pWriter->write_uint(get_path_length() + 1); + pWriter->write_uint(node_cache_width); + pWriter->write_uint(node_cache_height); + for (const path_node* pNode = destination; pNode; pNode = pNode->prev) { + pWriter->write_uint(pNode->x); + pWriter->write_uint(pNode->y); + } } -void pathfinder::depersist(lua_persist_reader *pReader) -{ - new (this) pathfinder; // Call constructor +void pathfinder::depersist(lua_persist_reader* pReader) { + new (this) pathfinder; // Call constructor - int iLength; - if(!pReader->read_uint(iLength)) - return; - if(iLength == 0) - return; - int iWidth, iHeight; - if(!pReader->read_uint(iWidth) || !pReader->read_uint(iHeight)) - return; - allocate_node_cache(iWidth, iHeight); - int iX, iY; - if(!pReader->read_uint(iX) || !pReader->read_uint(iY)) - return; - path_node *pNode = nodes + iY * iWidth + iX; - destination = pNode; - for(int i = 0; i <= iLength - 2; ++i) - { - if(!pReader->read_uint(iX) || !pReader->read_uint(iY)) - return; - path_node *pPrevNode = nodes + iY * iWidth + iX; - pNode->distance = iLength - 1 - i; - pNode->prev = pPrevNode; - dirty_node_list[dirty_node_count++] = pNode; - pNode = pPrevNode; + int iLength; + if (!pReader->read_uint(iLength)) { + return; + } + if (iLength == 0) { + return; + } + int iWidth, iHeight; + if (!pReader->read_uint(iWidth) || !pReader->read_uint(iHeight)) { + return; + } + allocate_node_cache(iWidth, iHeight); + int iX, iY; + if (!pReader->read_uint(iX) || !pReader->read_uint(iY)) { + return; + } + path_node* pNode = nodes + iY * iWidth + iX; + destination = pNode; + for (int i = 0; i <= iLength - 2; ++i) { + if (!pReader->read_uint(iX) || !pReader->read_uint(iY)) { + return; } - pNode->distance = 0; - pNode->prev = nullptr; + path_node* pPrevNode = nodes + iY * iWidth + iX; + pNode->distance = iLength - 1 - i; + pNode->prev = pPrevNode; dirty_node_list[dirty_node_count++] = pNode; + pNode = pPrevNode; + } + pNode->distance = 0; + pNode->prev = nullptr; + dirty_node_list[dirty_node_count++] = pNode; } diff --git a/CorsixTH/Src/th_pathfind.h b/CorsixTH/Src/th_pathfind.h index 1e98327d..33cde91e 100644 --- a/CorsixTH/Src/th_pathfind.h +++ b/CorsixTH/Src/th_pathfind.h @@ -30,167 +30,164 @@ class pathfinder; /** Directions of movement. */ enum class travel_direction { - north = 0, ///< Move to the north. - east = 1, ///< Move to the east. - south = 2, ///< Move to the south. - west = 3 ///< Move to the west. + north = 0, ///< Move to the north. + east = 1, ///< Move to the east. + south = 2, ///< Move to the south. + west = 3 ///< Move to the west. }; /** Node in the path finder routines. */ -struct path_node -{ - //! Pointer to the previous node in the path to this cell. - /*! - Points to nullptr if this is the first cell in the path, or points to - itself if it is not part of a path. - */ - const path_node* prev; +struct path_node { + //! Pointer to the previous node in the path to this cell. + /*! + Points to nullptr if this is the first cell in the path, or points to + itself if it is not part of a path. + */ + const path_node* prev; - //! X-position of this cell (constant) - int x; + //! X-position of this cell (constant) + int x; - //! Y-position of this cell (constant) - int y; + //! Y-position of this cell (constant) + int y; - //! Current shortest distance to this cell - /*! - Defined as prev->distance + 1 (or 0 if prev == nullptr). - Value is undefined if not part of a path. - */ - int distance; + //! Current shortest distance to this cell + /*! + Defined as prev->distance + 1 (or 0 if prev == nullptr). + Value is undefined if not part of a path. + */ + int distance; - //! Minimum distance from this cell to the goal - /*! - Value is only dependant upon the cell position and the goal - position, and is undefined if not part of a path. - */ - int guess; + //! Minimum distance from this cell to the goal + /*! + Value is only dependant upon the cell position and the goal + position, and is undefined if not part of a path. + */ + int guess; - //! Index of this cell in the open heap - /*! - If the cell is not in the open heap, then this value is undefined. - */ - int open_idx; + //! Index of this cell in the open heap + /*! + If the cell is not in the open heap, then this value is undefined. + */ + int open_idx; - //! Total cost of this node. - /*! - @return Total cost of the node, traveled distance and guess to the destination. - */ - inline int value() const { return distance + guess; } + //! Total cost of this node. + /*! + @return Total cost of the node, traveled distance and guess to the + destination. + */ + inline int value() const { return distance + guess; } }; /** Base class of the path finders. */ -class abstract_pathfinder -{ -public: - abstract_pathfinder(pathfinder *pf); - virtual ~abstract_pathfinder() = default; +class abstract_pathfinder { + public: + abstract_pathfinder(pathfinder* pf); + virtual ~abstract_pathfinder() = default; - //! Initialize the path finder. - /*! - @param pMap Map to search on. - @param iStartX X coordinate of the start position. - @param iStarty Y coordinate of the start position. - @return The initial node to expand. - */ - path_node *init(const level_map *pMap, int iStartX, int iStarty); + //! Initialize the path finder. + /*! + @param pMap Map to search on. + @param iStartX X coordinate of the start position. + @param iStarty Y coordinate of the start position. + @return The initial node to expand. + */ + path_node* init(const level_map* pMap, int iStartX, int iStarty); - //! Expand the \a pNode to its neighbours. - /*! - @param pNode Node to expand. - @param flags Flags of the node. - @param iWidth Width of the map. - @return Whether the search is done. - */ - bool search_neighbours(path_node *pNode, map_tile_flags flags, int iWidth); + //! Expand the \a pNode to its neighbours. + /*! + @param pNode Node to expand. + @param flags Flags of the node. + @param iWidth Width of the map. + @return Whether the search is done. + */ + bool search_neighbours(path_node* pNode, map_tile_flags flags, int iWidth); - void record_neighbour_if_passable(path_node *pNode, map_tile_flags neighbour_flags, - bool passable, path_node *pNeighbour); + void record_neighbour_if_passable(path_node* pNode, + map_tile_flags neighbour_flags, + bool passable, path_node* pNeighbour); - //! Guess distance to the destination for \a pNode. - /*! - @param pNode Node to fill. - */ - virtual int guess_distance(path_node *pNode) = 0; + //! Guess distance to the destination for \a pNode. + /*! + @param pNode Node to fill. + */ + virtual int guess_distance(path_node* pNode) = 0; - //! Try the \a pNeighbour node. - /*! - @param pNode Source node. - @param flags Flags of the node. - @param pNeighbour Neighbour of \a pNode to try. - @param direction Direction of travel. - @return Whether the search is done. - */ - virtual bool try_node(path_node *pNode, map_tile_flags flags, - path_node *pNeighbour, travel_direction direction) = 0; + //! Try the \a pNeighbour node. + /*! + @param pNode Source node. + @param flags Flags of the node. + @param pNeighbour Neighbour of \a pNode to try. + @param direction Direction of travel. + @return Whether the search is done. + */ + virtual bool try_node(path_node* pNode, map_tile_flags flags, + path_node* pNeighbour, travel_direction direction) = 0; -protected: - pathfinder *parent; ///< Path finder parent object, containing shared data. - const level_map *map; ///< Map being searched. + protected: + pathfinder* parent; ///< Path finder parent object, containing shared data. + const level_map* map; ///< Map being searched. }; -class basic_pathfinder : public abstract_pathfinder -{ -public: - basic_pathfinder(pathfinder *pf) : abstract_pathfinder(pf) { } +class basic_pathfinder : public abstract_pathfinder { + public: + basic_pathfinder(pathfinder* pf) : abstract_pathfinder(pf) {} - int guess_distance(path_node *pNode) override; - bool try_node(path_node *pNode, map_tile_flags flags, - path_node *pNeighbour, travel_direction direction) override; + int guess_distance(path_node* pNode) override; + bool try_node(path_node* pNode, map_tile_flags flags, path_node* pNeighbour, + travel_direction direction) override; - bool find_path(const level_map *pMap, int iStartX, int iStartY, int iEndX, int iEndY); + bool find_path(const level_map* pMap, int iStartX, int iStartY, int iEndX, + int iEndY); - int destination_x; ///< X coordinate of the destination of the path. - int destination_y; ///< Y coordinate of the destination of the path. + int destination_x; ///< X coordinate of the destination of the path. + int destination_y; ///< Y coordinate of the destination of the path. }; -class hospital_finder : public abstract_pathfinder -{ -public: - hospital_finder(pathfinder *pf) : abstract_pathfinder(pf) { } +class hospital_finder : public abstract_pathfinder { + public: + hospital_finder(pathfinder* pf) : abstract_pathfinder(pf) {} - int guess_distance(path_node *pNode) override; - bool try_node(path_node *pNode, map_tile_flags flags, - path_node *pNeighbour, travel_direction direction) override; + int guess_distance(path_node* pNode) override; + bool try_node(path_node* pNode, map_tile_flags flags, path_node* pNeighbour, + travel_direction direction) override; - bool find_path_to_hospital(const level_map *pMap, int iStartX, int iStartY); + bool find_path_to_hospital(const level_map* pMap, int iStartX, int iStartY); }; -class idle_tile_finder : public abstract_pathfinder -{ -public: - idle_tile_finder(pathfinder *pf) : abstract_pathfinder(pf) { } +class idle_tile_finder : public abstract_pathfinder { + public: + idle_tile_finder(pathfinder* pf) : abstract_pathfinder(pf) {} - int guess_distance(path_node *pNode) override; - bool try_node(path_node *pNode, map_tile_flags flags, - path_node *pNeighbour, travel_direction direction) override; + int guess_distance(path_node* pNode) override; + bool try_node(path_node* pNode, map_tile_flags flags, path_node* pNeighbour, + travel_direction direction) override; - bool find_idle_tile(const level_map *pMap, int iStartX, int iStartY, int iN); + bool find_idle_tile(const level_map* pMap, int iStartX, int iStartY, int iN); - path_node *best_next_node; - double best_distance; - int start_x; ///< X coordinate of the start position. - int start_y; ///< Y coordinate of the start position. + path_node* best_next_node; + double best_distance; + int start_x; ///< X coordinate of the start position. + int start_y; ///< Y coordinate of the start position. }; -class object_visitor : public abstract_pathfinder -{ -public: - object_visitor(pathfinder *pf) : abstract_pathfinder(pf) { } +class object_visitor : public abstract_pathfinder { + public: + object_visitor(pathfinder* pf) : abstract_pathfinder(pf) {} - int guess_distance(path_node *pNode) override; - bool try_node(path_node *pNode, map_tile_flags flags, - path_node *pNeighbour, travel_direction direction) override; + int guess_distance(path_node* pNode) override; + bool try_node(path_node* pNode, map_tile_flags flags, path_node* pNeighbour, + travel_direction direction) override; - bool visit_objects(const level_map *pMap, int iStartX, int iStartY, - object_type eTHOB, int iMaxDistance, - lua_State *L, int iVisitFunction, bool anyObjectType); + bool visit_objects(const level_map* pMap, int iStartX, int iStartY, + object_type eTHOB, int iMaxDistance, lua_State* L, + int iVisitFunction, bool anyObjectType); - lua_State *L; - int visit_function_index; - int max_distance; - bool target_any_object_type; - object_type target; + lua_State* L; + int visit_function_index; + int max_distance; + bool target_any_object_type; + object_type target; }; //! Finds paths through maps @@ -208,88 +205,85 @@ public: the current search. The algorithm is implemented in such a way that most path find operations do not need to allocate (or free) any memory. */ -class pathfinder -{ -public: - pathfinder(); - ~pathfinder(); +class pathfinder { + public: + pathfinder(); + ~pathfinder(); - void set_default_map(const level_map *pMap); + void set_default_map(const level_map* pMap); - inline bool find_path(const level_map *pMap, int iStartX, int iStartY, int iEndX, - int iEndY) - { - return basic_pathfinder.find_path(pMap, iStartX, iStartY, iEndX, iEndY); - } + inline bool find_path(const level_map* pMap, int iStartX, int iStartY, + int iEndX, int iEndY) { + return basic_pathfinder.find_path(pMap, iStartX, iStartY, iEndX, iEndY); + } - inline bool find_idle_tile(const level_map *pMap, int iStartX, int iStartY, int iN) - { - return idle_tile_finder.find_idle_tile(pMap, iStartX, iStartY, iN); - } + inline bool find_idle_tile(const level_map* pMap, int iStartX, int iStartY, + int iN) { + return idle_tile_finder.find_idle_tile(pMap, iStartX, iStartY, iN); + } - inline bool find_path_to_hospital(const level_map *pMap, int iStartX, int iStartY) - { - return hospital_finder.find_path_to_hospital(pMap, iStartX, iStartY); - } + inline bool find_path_to_hospital(const level_map* pMap, int iStartX, + int iStartY) { + return hospital_finder.find_path_to_hospital(pMap, iStartX, iStartY); + } - inline bool visit_objects(const level_map *pMap, int iStartX, int iStartY, - object_type eTHOB, int iMaxDistance, lua_State *L, - int iVisitFunction, bool anyObjectType) - { - return object_visitor.visit_objects( - pMap, iStartX, iStartY, eTHOB, iMaxDistance, - L, iVisitFunction, anyObjectType); - } + inline bool visit_objects(const level_map* pMap, int iStartX, int iStartY, + object_type eTHOB, int iMaxDistance, lua_State* L, + int iVisitFunction, bool anyObjectType) { + return object_visitor.visit_objects(pMap, iStartX, iStartY, eTHOB, + iMaxDistance, L, iVisitFunction, + anyObjectType); + } - int get_path_length() const; - bool get_path_end(int* pX, int* pY) const; - void push_result(lua_State *L) const; + int get_path_length() const; + bool get_path_end(int* pX, int* pY) const; + void push_result(lua_State* L) const; - void persist(lua_persist_writer *pWriter) const; - void depersist(lua_persist_reader *pReader); + void persist(lua_persist_writer* pWriter) const; + void depersist(lua_persist_reader* pReader); - //! Allocate node cache for all tiles of the map. - /*! - @param iWidth Width of the map. - @param iHeight Height of the map. - */ - void allocate_node_cache(int iWidth, int iHeight); + //! Allocate node cache for all tiles of the map. + /*! + @param iWidth Width of the map. + @param iHeight Height of the map. + */ + void allocate_node_cache(int iWidth, int iHeight); - path_node* pop_from_open_heap(); - void push_to_open_heap(path_node* pNode); - void open_heap_promote(path_node* pNode); + path_node* pop_from_open_heap(); + void push_to_open_heap(path_node* pNode); + void open_heap_promote(path_node* pNode); - const level_map *default_map; + const level_map* default_map; - //! 2D array of nodes, one for each map cell - path_node *nodes; + //! 2D array of nodes, one for each map cell + path_node* nodes; - //! Array of "dirty" nodes which need to be reset before the next path find - /*! - This array is always large enough to hold every single node, and - #dirty_node_count holds the number of items currently in the array. - */ - path_node **dirty_node_list; + //! Array of "dirty" nodes which need to be reset before the next path find + /*! + This array is always large enough to hold every single node, and + #dirty_node_count holds the number of items currently in the array. + */ + path_node** dirty_node_list; - //! Heap of not yet evaluated nodes as a 0-based array - /*! - This array conforms to the conditions: - value(i) <= value(i * 2 + 1) - value(i) <= value(i * 2 + 2) - This causes the array to be a minimum binary heap. - */ - std::vector open_heap; + //! Heap of not yet evaluated nodes as a 0-based array + /*! + This array conforms to the conditions: + value(i) <= value(i * 2 + 1) + value(i) <= value(i * 2 + 2) + This causes the array to be a minimum binary heap. + */ + std::vector open_heap; - path_node *destination; - int node_cache_width; - int node_cache_height; - int dirty_node_count; + path_node* destination; + int node_cache_width; + int node_cache_height; + int dirty_node_count; -private: - ::basic_pathfinder basic_pathfinder; - ::hospital_finder hospital_finder; - ::idle_tile_finder idle_tile_finder; - ::object_visitor object_visitor; + private: + ::basic_pathfinder basic_pathfinder; + ::hospital_finder hospital_finder; + ::idle_tile_finder idle_tile_finder; + ::object_visitor object_visitor; }; -#endif // CORSIX_TH_TH_PATHFIND_H_ +#endif // CORSIX_TH_TH_PATHFIND_H_ diff --git a/CorsixTH/Src/th_sound.cpp b/CorsixTH/Src/th_sound.cpp index c8e86bd6..92012e18 100644 --- a/CorsixTH/Src/th_sound.cpp +++ b/CorsixTH/Src/th_sound.cpp @@ -20,154 +20,157 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -#include "config.h" #include "th_sound.h" -#include "th.h" +#include "config.h" #include -#include #include +#include +#include "th.h" -sound_archive::sound_archive() -{ - sound_files = nullptr; - data = nullptr; +sound_archive::sound_archive() { + sound_files = nullptr; + data = nullptr; } -sound_archive::~sound_archive() -{ - delete[] data; +sound_archive::~sound_archive() { delete[] data; } + +bool sound_archive::load_from_th_file(const uint8_t* pData, + size_t iDataLength) { + if (iDataLength < sizeof(uint32_t) + sizeof(sound_dat_file_header)) { + return false; + } + + uint32_t iHeaderPosition = + bytes_to_uint32_le(pData + iDataLength - sizeof(uint32_t)); + + if (static_cast(iHeaderPosition) >= + iDataLength - sizeof(sound_dat_file_header)) { + return false; + } + + header = + *reinterpret_cast(pData + iHeaderPosition); + + delete[] data; + data = new (std::nothrow) uint8_t[iDataLength]; + if (data == nullptr) { + return false; + } + std::memcpy(data, pData, iDataLength); + + sound_files = + reinterpret_cast(data + header.table_position); + sound_file_count = header.table_length / sizeof(sound_dat_sound_info); + return true; } -bool sound_archive::load_from_th_file(const uint8_t* pData, size_t iDataLength) -{ - if(iDataLength < sizeof(uint32_t) + sizeof(sound_dat_file_header)) - return false; +size_t sound_archive::get_number_of_sounds() const { return sound_file_count; } - uint32_t iHeaderPosition = bytes_to_uint32_le(pData + iDataLength - sizeof(uint32_t)); - - if(static_cast(iHeaderPosition) >= iDataLength - sizeof(sound_dat_file_header)) - return false; - - header = *reinterpret_cast(pData + iHeaderPosition); - - delete[] data; - data = new (std::nothrow) uint8_t[iDataLength]; - if(data == nullptr) - return false; - std::memcpy(data, pData, iDataLength); - - sound_files = reinterpret_cast(data + header.table_position); - sound_file_count = header.table_length / sizeof(sound_dat_sound_info); - return true; +const char* sound_archive::get_sound_name(size_t iIndex) const { + if (iIndex >= sound_file_count) return nullptr; + return sound_files[iIndex].sound_name; } -size_t sound_archive::get_number_of_sounds() const -{ - return sound_file_count; -} - -const char* sound_archive::get_sound_name(size_t iIndex) const -{ - if(iIndex >= sound_file_count) - return nullptr; - return sound_files[iIndex].sound_name; -} - -constexpr uint32_t fourcc(const char c1, const char c2, const char c3, const char c4) -{ - return ( - (static_cast(static_cast(c1)) << 0) - | (static_cast(static_cast(c2)) << 8) - | (static_cast(static_cast(c3)) << 16) - | (static_cast(static_cast(c4)) << 24)); +constexpr uint32_t fourcc(const char c1, const char c2, const char c3, + const char c4) { + return ((static_cast(static_cast(c1)) << 0) | + (static_cast(static_cast(c2)) << 8) | + (static_cast(static_cast(c3)) << 16) | + (static_cast(static_cast(c4)) << 24)); } namespace { template inline uint64_t mul64(A a, B b) { - return static_cast(a) * static_cast(b); + return static_cast(a) * static_cast(b); } -} // namespace +} // namespace -size_t sound_archive::get_sound_duration(size_t iIndex) -{ - SDL_RWops *pFile = load_sound(iIndex); - if(!pFile) - return 0; +size_t sound_archive::get_sound_duration(size_t iIndex) { + SDL_RWops* pFile = load_sound(iIndex); + if (!pFile) { + return 0; + } - uint16_t iWaveAudioFormat = 0; - uint16_t iWaveChannelCount = 0; - uint32_t iWaveSampleRate = 0; - uint32_t iWaveByteRate = 0; - uint16_t iWaveBlockAlign = 0; - uint16_t iWaveBitsPerSample = 0; - uint32_t iWaveDataLength = 0; + uint16_t iWaveAudioFormat = 0; + uint16_t iWaveChannelCount = 0; + uint32_t iWaveSampleRate = 0; + uint32_t iWaveByteRate = 0; + uint16_t iWaveBlockAlign = 0; + uint16_t iWaveBitsPerSample = 0; + uint32_t iWaveDataLength = 0; - // This is a very crude RIFF parser, but it does the job. - uint32_t iFourCC; - uint32_t iChunkLength; - for(;;) - { - if(SDL_RWread(pFile, &iFourCC, 4, 1) != 1) - break; - if(SDL_RWread(pFile, &iChunkLength, 4, 1) != 1) - break; - if(iFourCC == fourcc('R','I','F','F') || iFourCC == fourcc('L','I','S','T')) - { - if(iChunkLength >= 4) - { - if(SDL_RWread(pFile, &iFourCC, 4, 1) != 1) - break; - else - continue; - } - } - if(iFourCC == fourcc('f','m','t',' ') && iChunkLength >= 16) - { - if(SDL_RWread(pFile, &iWaveAudioFormat, 2, 1) != 1) - break; - if(SDL_RWread(pFile, &iWaveChannelCount, 2, 1) != 1) - break; - if(SDL_RWread(pFile, &iWaveSampleRate, 4, 1) != 1) - break; - if(SDL_RWread(pFile, &iWaveByteRate, 4, 1) != 1) - break; - if(SDL_RWread(pFile, &iWaveBlockAlign, 2, 1) != 1) - break; - if(SDL_RWread(pFile, &iWaveBitsPerSample, 2, 1) != 1) - break; - iChunkLength -= 16; - } - //Finally: - if(iFourCC == fourcc('d','a','t','a')) - { - iWaveDataLength = iChunkLength; - break; - } - if(SDL_RWseek(pFile, iChunkLength + (iChunkLength & 1), RW_SEEK_CUR) == -1) { - break; - } + // This is a very crude RIFF parser, but it does the job. + uint32_t iFourCC; + uint32_t iChunkLength; + for (;;) { + if (SDL_RWread(pFile, &iFourCC, 4, 1) != 1) { + break; } - SDL_RWclose(pFile); - if(iWaveAudioFormat != 1 || iWaveChannelCount == 0 || iWaveSampleRate == 0 - || iWaveDataLength == 0 || iWaveBitsPerSample == 0) - { - return 0; + if (SDL_RWread(pFile, &iChunkLength, 4, 1) != 1) { + break; } + if (iFourCC == fourcc('R', 'I', 'F', 'F') || + iFourCC == fourcc('L', 'I', 'S', 'T')) { + if (iChunkLength >= 4) { + if (SDL_RWread(pFile, &iFourCC, 4, 1) != 1) { + break; + } else { + continue; + } + } + } + if (iFourCC == fourcc('f', 'm', 't', ' ') && iChunkLength >= 16) { + if (SDL_RWread(pFile, &iWaveAudioFormat, 2, 1) != 1) { + break; + } + if (SDL_RWread(pFile, &iWaveChannelCount, 2, 1) != 1) { + break; + } + if (SDL_RWread(pFile, &iWaveSampleRate, 4, 1) != 1) { + break; + } + if (SDL_RWread(pFile, &iWaveByteRate, 4, 1) != 1) { + break; + } + if (SDL_RWread(pFile, &iWaveBlockAlign, 2, 1) != 1) { + break; + } + if (SDL_RWread(pFile, &iWaveBitsPerSample, 2, 1) != 1) { + break; + } + iChunkLength -= 16; + } + // Finally: + if (iFourCC == fourcc('d', 'a', 't', 'a')) { + iWaveDataLength = iChunkLength; + break; + } + if (SDL_RWseek(pFile, iChunkLength + (iChunkLength & 1), RW_SEEK_CUR) == + -1) { + break; + } + } + SDL_RWclose(pFile); + if (iWaveAudioFormat != 1 || iWaveChannelCount == 0 || iWaveSampleRate == 0 || + iWaveDataLength == 0 || iWaveBitsPerSample == 0) { + return 0; + } - return static_cast(mul64(iWaveDataLength, 8000) / - mul64(mul64(iWaveBitsPerSample, iWaveChannelCount), iWaveSampleRate)); + return static_cast( + mul64(iWaveDataLength, 8000) / + mul64(mul64(iWaveBitsPerSample, iWaveChannelCount), iWaveSampleRate)); } -SDL_RWops* sound_archive::load_sound(size_t iIndex) -{ - if(iIndex >= sound_file_count) - return nullptr; +SDL_RWops* sound_archive::load_sound(size_t iIndex) { + if (iIndex >= sound_file_count) { + return nullptr; + } - sound_dat_sound_info *pFile = sound_files + iIndex; - return SDL_RWFromConstMem(data + pFile->position, pFile->length); + sound_dat_sound_info* pFile = sound_files + iIndex; + return SDL_RWFromConstMem(data + pFile->position, pFile->length); } #ifdef CORSIX_TH_USE_SDL_MIXER @@ -176,152 +179,138 @@ constexpr int number_of_channels = 32; sound_player* sound_player::singleton = nullptr; -sound_player::sound_player() -{ - sounds = nullptr; - sound_count = 0; - singleton = this; - camera_x = 0; - camera_y = 0; - camera_radius = 1.0; - master_volume = 1.0; - sound_effect_volume = 0.5; - positionless_volume = MIX_MAX_VOLUME; - sound_effects_enabled = true; +sound_player::sound_player() { + sounds = nullptr; + sound_count = 0; + singleton = this; + camera_x = 0; + camera_y = 0; + camera_radius = 1.0; + master_volume = 1.0; + sound_effect_volume = 0.5; + positionless_volume = MIX_MAX_VOLUME; + sound_effects_enabled = true; - available_channels_bitmap = ~0; - Mix_AllocateChannels(number_of_channels); + available_channels_bitmap = ~0; + Mix_AllocateChannels(number_of_channels); - Mix_ChannelFinished(on_channel_finished); + Mix_ChannelFinished(on_channel_finished); } -sound_player::~sound_player() -{ - populate_from(nullptr); - if(singleton == this) - singleton = nullptr; +sound_player::~sound_player() { + populate_from(nullptr); + if (singleton == this) { + singleton = nullptr; + } } -void sound_player::on_channel_finished(int iChannel) -{ - sound_player *pThis = get_singleton(); - if(pThis == nullptr) - return; +void sound_player::on_channel_finished(int iChannel) { + sound_player* pThis = get_singleton(); + if (pThis == nullptr) return; - pThis->release_channel(iChannel); + pThis->release_channel(iChannel); } -sound_player* sound_player::get_singleton() -{ - return singleton; -} +sound_player* sound_player::get_singleton() { return singleton; } -void sound_player::populate_from(sound_archive *pArchive) -{ - for(size_t i = 0; i < sound_count; ++i) - { - Mix_FreeChunk(sounds[i]); - } - delete[] sounds; - sounds = nullptr; - sound_count = 0; - - if(pArchive == nullptr) - return; - - sounds = new Mix_Chunk*[pArchive->get_number_of_sounds()]; - for(; sound_count < pArchive->get_number_of_sounds(); ++sound_count) - { - sounds[sound_count] = nullptr; - SDL_RWops *pRwop = pArchive->load_sound(sound_count); - if(pRwop) - { - sounds[sound_count] = Mix_LoadWAV_RW(pRwop, 1); - if(sounds[sound_count]) - Mix_VolumeChunk(sounds[sound_count], MIX_MAX_VOLUME); - } +void sound_player::populate_from(sound_archive* pArchive) { + for (size_t i = 0; i < sound_count; ++i) { + Mix_FreeChunk(sounds[i]); + } + delete[] sounds; + sounds = nullptr; + sound_count = 0; + + if (pArchive == nullptr) return; + + sounds = new Mix_Chunk*[pArchive->get_number_of_sounds()]; + for (; sound_count < pArchive->get_number_of_sounds(); ++sound_count) { + sounds[sound_count] = nullptr; + SDL_RWops* pRwop = pArchive->load_sound(sound_count); + if (pRwop) { + sounds[sound_count] = Mix_LoadWAV_RW(pRwop, 1); + if (sounds[sound_count]) { + Mix_VolumeChunk(sounds[sound_count], MIX_MAX_VOLUME); + } } + } } -void sound_player::play(size_t iIndex, double dVolume) -{ - if(available_channels_bitmap == 0 || iIndex >= sound_count || !sounds[iIndex]) - return; +void sound_player::play(size_t iIndex, double dVolume) { + if (available_channels_bitmap == 0 || iIndex >= sound_count || + !sounds[iIndex]) { + return; + } - play_raw(iIndex, (int)(positionless_volume * dVolume)); + play_raw(iIndex, (int)(positionless_volume * dVolume)); } -void sound_player::play_at(size_t iIndex, int iX, int iY) -{ - if(sound_effects_enabled) - play_at(iIndex, sound_effect_volume, iX, iY); +void sound_player::play_at(size_t iIndex, int iX, int iY) { + if (sound_effects_enabled) { + play_at(iIndex, sound_effect_volume, iX, iY); + } } -void sound_player::play_at(size_t iIndex, double dVolume, int iX, int iY) -{ - if(available_channels_bitmap == 0 || iIndex >= sound_count || !sounds[iIndex]) - return; +void sound_player::play_at(size_t iIndex, double dVolume, int iX, int iY) { + if (available_channels_bitmap == 0 || iIndex >= sound_count || + !sounds[iIndex]) { + return; + } - double fDX = (double)(iX - camera_x); - double fDY = (double)(iY - camera_y); - double fDistance = sqrt(fDX * fDX + fDY * fDY); - if(fDistance > camera_radius) - return; - fDistance = fDistance / camera_radius; + double fDX = (double)(iX - camera_x); + double fDY = (double)(iY - camera_y); + double fDistance = sqrt(fDX * fDX + fDY * fDY); + if (fDistance > camera_radius) return; + fDistance = fDistance / camera_radius; - double fVolume = master_volume * (1.0 - fDistance * 0.8) * (double)MIX_MAX_VOLUME * dVolume; + double fVolume = master_volume * (1.0 - fDistance * 0.8) * + (double)MIX_MAX_VOLUME * dVolume; - play_raw(iIndex, (int)(fVolume + 0.5)); + play_raw(iIndex, (int)(fVolume + 0.5)); } -void sound_player::set_sound_effect_volume(double dVolume) -{ - sound_effect_volume = dVolume; +void sound_player::set_sound_effect_volume(double dVolume) { + sound_effect_volume = dVolume; } -void sound_player::set_sound_effects_enabled(bool bOn) -{ - sound_effects_enabled = bOn; +void sound_player::set_sound_effects_enabled(bool bOn) { + sound_effects_enabled = bOn; } -int sound_player::reserve_channel() -{ - // NB: Callers ensure that m_iChannelStatus != 0 - int iChannel = 0; - for(; (available_channels_bitmap & (1 << iChannel)) == 0; ++iChannel) {} - available_channels_bitmap &=~ (1 << iChannel); +int sound_player::reserve_channel() { + // NB: Callers ensure that m_iChannelStatus != 0 + int iChannel = 0; + for (; (available_channels_bitmap & (1 << iChannel)) == 0; ++iChannel) { + } + available_channels_bitmap &= ~(1 << iChannel); - return iChannel; + return iChannel; } -void sound_player::release_channel(int iChannel) -{ - available_channels_bitmap |= (1 << iChannel); +void sound_player::release_channel(int iChannel) { + available_channels_bitmap |= (1 << iChannel); } -void sound_player::play_raw(size_t iIndex, int iVolume) -{ - int iChannel = reserve_channel(); +void sound_player::play_raw(size_t iIndex, int iVolume) { + int iChannel = reserve_channel(); - Mix_Volume(iChannel, iVolume); - Mix_PlayChannelTimed(iChannel, sounds[iIndex], 0, -1); + Mix_Volume(iChannel, iVolume); + Mix_PlayChannelTimed(iChannel, sounds[iIndex], 0, -1); } -void sound_player::set_camera(int iX, int iY, int iRadius) -{ - camera_x = iX; - camera_y = iY; - camera_radius = (double)iRadius; - if(camera_radius < 0.001) - camera_radius = 0.001; +void sound_player::set_camera(int iX, int iY, int iRadius) { + camera_x = iX; + camera_y = iY; + camera_radius = (double)iRadius; + if (camera_radius < 0.001) camera_radius = 0.001; } -#else // CORSIX_TH_USE_SDL_MIXER +#else // CORSIX_TH_USE_SDL_MIXER sound_player::sound_player() {} sound_player::~sound_player() {} -sound_player* sound_player::get_singleton() {return nullptr;} -void sound_player::populate_from(sound_archive *pArchive) {} +sound_player* sound_player::get_singleton() { return nullptr; } +void sound_player::populate_from(sound_archive* pArchive) {} void sound_player::play(size_t iIndex, double dVolume) {} void sound_player::play_at(size_t iIndex, int iX, int iY) {} void sound_player::play_at(size_t iIndex, double dVolume, int iX, int iY) {} @@ -331,4 +320,4 @@ void sound_player::set_camera(int iX, int iY, int iRadius) {} void sound_player::set_sound_effect_volume(double dVolume) {} void sound_player::set_sound_effects_enabled(bool iOn) {} -#endif // CORSIX_TH_USE_SDL_MIXER +#endif // CORSIX_TH_USE_SDL_MIXER diff --git a/CorsixTH/Src/th_sound.h b/CorsixTH/Src/th_sound.h index 93fd6573..2e61c5d7 100644 --- a/CorsixTH/Src/th_sound.h +++ b/CorsixTH/Src/th_sound.h @@ -22,109 +22,109 @@ SOFTWARE. #ifndef CORSIX_TH_TH_SOUND_H_ #define CORSIX_TH_TH_SOUND_H_ +#include "config.h" #include #ifdef CORSIX_TH_USE_SDL_MIXER #include #endif //! Utility class for accessing Theme Hospital's SOUND-0.DAT -class sound_archive -{ -public: - sound_archive(); - ~sound_archive(); +class sound_archive { + public: + sound_archive(); + ~sound_archive(); - bool load_from_th_file(const uint8_t* pData, size_t iDataLength); + bool load_from_th_file(const uint8_t* pData, size_t iDataLength); - //! Returns the number of sounds present in the archive - size_t get_number_of_sounds() const; + //! Returns the number of sounds present in the archive + size_t get_number_of_sounds() const; - //! Gets the name of the sound at a given index - const char *get_sound_name(size_t iIndex) const; + //! Gets the name of the sound at a given index + const char* get_sound_name(size_t iIndex) const; - //! Gets the duration (in miliseconds) of the sound at a given index - size_t get_sound_duration(size_t iIndex); + //! Gets the duration (in miliseconds) of the sound at a given index + size_t get_sound_duration(size_t iIndex); - //! Opens the sound at a given index into an SDL_RWops structure - /*! - The caller is responsible for closing/freeing the result. - */ - SDL_RWops* load_sound(size_t iIndex); + //! Opens the sound at a given index into an SDL_RWops structure + /*! + The caller is responsible for closing/freeing the result. + */ + SDL_RWops* load_sound(size_t iIndex); -private: + private: #if CORSIX_TH_USE_PACK_PRAGMAS #pragma pack(push) #pragma pack(1) #endif - struct sound_dat_file_header - { - uint8_t unknown1[50]; - uint32_t table_position; - uint32_t unknown2; - uint32_t table_length; - uint32_t table_position2; - uint8_t unknown3[112]; - uint32_t table_position3; - uint32_t table_length2; - uint8_t unknown4[48]; - } CORSIX_TH_PACKED_FLAGS; + struct sound_dat_file_header { + uint8_t unknown1[50]; + uint32_t table_position; + uint32_t unknown2; + uint32_t table_length; + uint32_t table_position2; + uint8_t unknown3[112]; + uint32_t table_position3; + uint32_t table_length2; + uint8_t unknown4[48]; + } CORSIX_TH_PACKED_FLAGS; - struct sound_dat_sound_info - { - char sound_name[18]; - uint32_t position; - uint32_t unknown1; - uint32_t length; - uint16_t unknown2; - } CORSIX_TH_PACKED_FLAGS; + struct sound_dat_sound_info { + char sound_name[18]; + uint32_t position; + uint32_t unknown1; + uint32_t length; + uint16_t unknown2; + } CORSIX_TH_PACKED_FLAGS; #if CORSIX_TH_USE_PACK_PRAGMAS #pragma pack(pop) #endif - // TODO: header is only used in one function, should not be class variable. - sound_dat_file_header header; - sound_dat_sound_info* sound_files; - uint8_t* data; - size_t sound_file_count; + // TODO: header is only used in one function, should not be class variable. + sound_dat_file_header header; + sound_dat_sound_info* sound_files; + uint8_t* data; + size_t sound_file_count; }; -class sound_player -{ -public: - sound_player(); - ~sound_player(); +class sound_player { + public: + sound_player(); + ~sound_player(); - static sound_player* get_singleton(); + static sound_player* get_singleton(); - void populate_from(sound_archive *pArchive); + void populate_from(sound_archive* pArchive); - void play(size_t iIndex, double dVolume); - void play_at(size_t iIndex, int iX, int iY); - void play_at(size_t iIndex, double dVolume, int iX, int iY); - void set_sound_effect_volume(double dVolume); - void set_sound_effects_enabled(bool bOn); - void set_camera(int iX, int iY, int iRadius); - int reserve_channel(); - void release_channel(int iChannel); + void play(size_t iIndex, double dVolume); + void play_at(size_t iIndex, int iX, int iY); + void play_at(size_t iIndex, double dVolume, int iX, int iY); + void set_sound_effect_volume(double dVolume); + void set_sound_effects_enabled(bool bOn); + void set_camera(int iX, int iY, int iRadius); + int reserve_channel(); + void release_channel(int iChannel); -private: + private: #ifdef CORSIX_TH_USE_SDL_MIXER - static sound_player* singleton; - static void on_channel_finished(int iChannel); + static sound_player* singleton; + static void on_channel_finished(int iChannel); - inline void play_raw(size_t iIndex, int iVolume); + inline void play_raw(size_t iIndex, int iVolume); - Mix_Chunk **sounds; - size_t sound_count; - uint32_t available_channels_bitmap; ///< The bit index corresponding to a channel is 1 if the channel is available and 0 if it is reserved or in use. - int camera_x; - int camera_y; - double camera_radius; - double master_volume; - double sound_effect_volume; - int positionless_volume; - bool sound_effects_enabled; -#endif // CORSIX_TH_USE_SDL_MIXER + Mix_Chunk** sounds; + size_t sound_count; + uint32_t available_channels_bitmap; ///< The bit index corresponding to a + ///< channel is 1 if the channel is + ///< available and 0 if it is reserved + ///< or in use. + int camera_x; + int camera_y; + double camera_radius; + double master_volume; + double sound_effect_volume; + int positionless_volume; + bool sound_effects_enabled; +#endif // CORSIX_TH_USE_SDL_MIXER }; -#endif // CORSIX_TH_TH_SOUND_H_ +#endif // CORSIX_TH_TH_SOUND_H_ diff --git a/CorsixTH/Src/xmi2mid.cpp b/CorsixTH/Src/xmi2mid.cpp index e762e6ce..095b87cc 100644 --- a/CorsixTH/Src/xmi2mid.cpp +++ b/CorsixTH/Src/xmi2mid.cpp @@ -22,8 +22,8 @@ SOFTWARE. #include "config.h" #ifdef CORSIX_TH_USE_SDL_MIXER -#include #include +#include #include #include #include @@ -31,387 +31,307 @@ SOFTWARE. /*! Utility class for reading or writing to memory as if it were a file. */ -class memory_buffer -{ -public: - memory_buffer() - : data(nullptr), pointer(nullptr), data_end(nullptr), buffer_end(nullptr) - { +class memory_buffer { + public: + memory_buffer() + : data(nullptr), + pointer(nullptr), + data_end(nullptr), + buffer_end(nullptr) {} + + memory_buffer(const uint8_t* pData, size_t iLength) { + data = pointer = (char*)pData; + data_end = data + iLength; + buffer_end = nullptr; + } + + ~memory_buffer() { + if (buffer_end != nullptr) delete[] data; + } + + uint8_t* take_data(size_t* pLength) { + if (pLength) *pLength = data_end - data; + uint8_t* pResult = (unsigned char*)data; + data = pointer = data_end = buffer_end = nullptr; + return pResult; + } + + size_t tell() const { return pointer - data; } + + bool seek(size_t position) { + if (data + position > data_end) { + if (!resize_buffer(position)) return false; + } + pointer = data + position; + return true; + } + + bool skip(int distance) { + if (distance < 0) { + if (pointer + distance < data) return false; + } + return seek(pointer - data + distance); + } + + bool scan_to(const void* pData, size_t iLength) { + for (; pointer + iLength <= data_end; ++pointer) { + if (std::memcmp(pointer, pData, iLength) == 0) return true; + } + return false; + } + + const char* get_pointer() const { return pointer; } + + template + bool read(T& value) { + return read(&value, 1); + } + + template + bool read(T* values, size_t count) { + if (pointer + sizeof(T) * count > data_end) return false; + std::memcpy(values, pointer, sizeof(T) * count); + pointer += sizeof(T) * count; + return true; + } + + unsigned int read_big_endian_uint24() { + uint8_t iByte0, iByte1, iByte2; + if (read(iByte0) && read(iByte1) && read(iByte2)) + return (((iByte0 << 8) | iByte1) << 8) | iByte2; + else + return 0; + } + + unsigned int read_variable_length_uint() { + unsigned int iValue = 0; + uint8_t iByte; + for (int i = 0; i < 4; ++i) { + if (!read(iByte)) return false; + iValue = (iValue << 7) | static_cast(iByte & 0x7F); + if ((iByte & 0x80) == 0) break; + } + return iValue; + } + + template + bool write(const T& value) { + return write(&value, 1); + } + + template + bool write(const T* values, size_t count) { + if (!skip(static_cast(sizeof(T) * count))) return false; + std::memcpy(pointer - sizeof(T) * count, values, sizeof(T) * count); + return true; + } + + bool write_big_endian_uint16(uint16_t iValue) { + return write(byte_swap(iValue)); + } + + bool write_big_endian_uint32(uint32_t iValue) { + return write(byte_swap(iValue)); + } + + bool write_variable_length_uint(unsigned int iValue) { + int iByteCount = 1; + unsigned int iBuffer = iValue & 0x7F; + for (; iValue >>= 7; ++iByteCount) { + iBuffer = (iBuffer << 8) | 0x80 | (iValue & 0x7F); + } + for (int i = 0; i < iByteCount; ++i) { + uint8_t iByte = iBuffer & 0xFF; + if (!write(iByte)) return false; + iBuffer >>= 8; + } + return true; + } + + bool is_end_of_buffer() const { return pointer == data_end; } + + private: + template + static T byte_swap(T value) { + T swapped = 0; + for (int i = 0; i < static_cast(sizeof(T)) * 8; i += 8) { + swapped = static_cast(swapped | ((value >> i) & 0xFF) + << (sizeof(T) * 8 - 8 - i)); + } + return swapped; + } + + bool resize_buffer(size_t size) { + if (data + size <= buffer_end) { + data_end = data + size; + return true; } - memory_buffer(const uint8_t* pData, size_t iLength) - { - data = pointer = (char*)pData; - data_end = data + iLength; - buffer_end = nullptr; + char* pNewData = new (std::nothrow) char[size * 2]; + if (pNewData == nullptr) return false; + size_t iOldLength = data_end - data; + if (iOldLength > 0) { + std::memcpy(pNewData, data, size > iOldLength ? iOldLength : size); } + pointer = pointer - data + pNewData; + if (buffer_end != nullptr) delete[] data; + data = pNewData; + data_end = pNewData + size; + buffer_end = pNewData + size * 2; + return true; + } - ~memory_buffer() - { - if(buffer_end != nullptr) - delete[] data; - } - - uint8_t* take_data(size_t *pLength) - { - if(pLength) - *pLength = data_end - data; - uint8_t* pResult = (unsigned char*)data; - data = pointer = data_end = buffer_end = nullptr; - return pResult; - } - - size_t tell() const - { - return pointer - data; - } - - bool seek(size_t position) - { - if(data + position > data_end) - { - if(!resize_buffer(position)) - return false; - } - pointer = data + position; - return true; - } - - bool skip(int distance) - { - if(distance < 0) - { - if(pointer + distance < data) - return false; - } - return seek(pointer - data + distance); - } - - bool scan_to(const void* pData, size_t iLength) - { - for(; pointer + iLength <= data_end; ++pointer) - { - if(std::memcmp(pointer, pData, iLength) == 0) - return true; - } - return false; - } - - const char* get_pointer() const - { - return pointer; - } - - template - bool read(T& value) - { - return read(&value, 1); - } - - template - bool read(T* values, size_t count) - { - if(pointer + sizeof(T) * count > data_end) - return false; - std::memcpy(values, pointer, sizeof(T) * count); - pointer += sizeof(T) * count; - return true; - } - - unsigned int read_big_endian_uint24() - { - uint8_t iByte0, iByte1, iByte2; - if(read(iByte0) && read(iByte1) && read(iByte2)) - return (((iByte0 << 8) | iByte1) << 8) | iByte2; - else - return 0; - } - - unsigned int read_variable_length_uint() - { - unsigned int iValue = 0; - uint8_t iByte; - for(int i = 0; i < 4; ++i) - { - if(!read(iByte)) - return false; - iValue = (iValue << 7) | static_cast(iByte & 0x7F); - if((iByte & 0x80) == 0) - break; - } - return iValue; - } - - template - bool write(const T& value) - { - return write(&value, 1); - } - - template - bool write(const T* values, size_t count) - { - if(!skip(static_cast(sizeof(T) * count))) - return false; - std::memcpy(pointer - sizeof(T) * count, values, sizeof(T) * count); - return true; - } - - bool write_big_endian_uint16(uint16_t iValue) - { - return write(byte_swap(iValue)); - } - - bool write_big_endian_uint32(uint32_t iValue) - { - return write(byte_swap(iValue)); - } - - bool write_variable_length_uint(unsigned int iValue) - { - int iByteCount = 1; - unsigned int iBuffer = iValue & 0x7F; - for(; iValue >>= 7; ++iByteCount) - { - iBuffer = (iBuffer << 8) | 0x80 | (iValue & 0x7F); - } - for(int i = 0; i < iByteCount; ++i) - { - uint8_t iByte = iBuffer & 0xFF; - if(!write(iByte)) - return false; - iBuffer >>= 8; - } - return true; - } - - bool is_end_of_buffer() const - { - return pointer == data_end; - } - -private: - template - static T byte_swap(T value) - { - T swapped = 0; - for(int i = 0; i < static_cast(sizeof(T)) * 8; i += 8) - { - swapped = static_cast(swapped | ((value >> i) & 0xFF) << (sizeof(T) * 8 - 8 - i)); - } - return swapped; - } - - bool resize_buffer(size_t size) - { - if(data + size <= buffer_end) - { - data_end = data + size; - return true; - } - - char *pNewData = new (std::nothrow) char[size * 2]; - if(pNewData == nullptr) - return false; - size_t iOldLength = data_end - data; - if (iOldLength > 0) { - std::memcpy(pNewData, data, size > iOldLength ? iOldLength : size); - } - pointer = pointer - data + pNewData; - if(buffer_end != nullptr) - delete[] data; - data = pNewData; - data_end = pNewData + size; - buffer_end = pNewData + size * 2; - return true; - } - - char *data, *pointer, *data_end, *buffer_end; + char *data, *pointer, *data_end, *buffer_end; }; -struct midi_token -{ - int time; - unsigned int buffer_length; - const char *buffer; - uint8_t type; - uint8_t data; +struct midi_token { + int time; + unsigned int buffer_length; + const char* buffer; + uint8_t type; + uint8_t data; }; -bool operator < (const midi_token& oLeft, const midi_token& oRight) -{ - return oLeft.time < oRight.time; +bool operator<(const midi_token& oLeft, const midi_token& oRight) { + return oLeft.time < oRight.time; } -struct midi_token_list : std::vector -{ - midi_token* append(int iTime, uint8_t iType) - { - push_back(midi_token()); - midi_token* pToken = &back(); - pToken->time = iTime; - pToken->type = iType; - return pToken; - } +struct midi_token_list : std::vector { + midi_token* append(int iTime, uint8_t iType) { + push_back(midi_token()); + midi_token* pToken = &back(); + pToken->time = iTime; + pToken->type = iType; + return pToken; + } }; -uint8_t* transcode_xmi_to_midi(const unsigned char* xmi_data, - size_t xmi_length, size_t* midi_length) -{ - if (xmi_data == nullptr) { return nullptr; } +uint8_t* transcode_xmi_to_midi(const unsigned char* xmi_data, size_t xmi_length, + size_t* midi_length) { + if (xmi_data == nullptr) { + return nullptr; + } - memory_buffer bufInput(xmi_data, xmi_length); + memory_buffer bufInput(xmi_data, xmi_length); - if(!bufInput.scan_to("EVNT", 4) || !bufInput.skip(8)) - return nullptr; + if (!bufInput.scan_to("EVNT", 4) || !bufInput.skip(8)) return nullptr; - midi_token_list lstTokens; - midi_token* pToken; - int iTokenTime = 0; - int iTempo = 500000; - bool bTempoSet = false; - bool bEnd = false; - uint8_t iTokenType, iExtendedType; + midi_token_list lstTokens; + midi_token* pToken; + int iTokenTime = 0; + int iTempo = 500000; + bool bTempoSet = false; + bool bEnd = false; + uint8_t iTokenType, iExtendedType; - while(!bufInput.is_end_of_buffer() && !bEnd) - { - while(true) - { - if(!bufInput.read(iTokenType)) - return nullptr; + while (!bufInput.is_end_of_buffer() && !bEnd) { + while (true) { + if (!bufInput.read(iTokenType)) return nullptr; - if(iTokenType & 0x80) - break; - else - iTokenTime += static_cast(iTokenType) * 3; - } - pToken = lstTokens.append(iTokenTime, iTokenType); - pToken->buffer = bufInput.get_pointer() + 1; - switch(iTokenType & 0xF0) - { - case 0xC0: - case 0xD0: - if(!bufInput.read(pToken->data)) - return nullptr; - pToken->buffer = nullptr; - break; - case 0x80: - case 0xA0: - case 0xB0: - case 0xE0: - if(!bufInput.read(pToken->data)) - return nullptr; - if(!bufInput.skip(1)) - return nullptr; - break; - case 0x90: - if(!bufInput.read(iExtendedType)) - return nullptr; - pToken->data = iExtendedType; - if(!bufInput.skip(1)) - return nullptr; - pToken = lstTokens.append(iTokenTime + bufInput.read_variable_length_uint() * 3, - iTokenType); - pToken->data = iExtendedType; - pToken->buffer = "\0"; - break; - case 0xF0: - iExtendedType = 0; - if(iTokenType == 0xFF) - { - if(!bufInput.read(iExtendedType)) - return nullptr; - - if(iExtendedType == 0x2F) - bEnd = true; - else if(iExtendedType == 0x51) - { - if(!bTempoSet) - { - bufInput.skip(1); - iTempo = bufInput.read_big_endian_uint24() * 3; - bTempoSet = true; - bufInput.skip(-4); - } - else - { - lstTokens.pop_back(); - if(!bufInput.skip(bufInput.read_variable_length_uint())) - return nullptr; - break; - } - } - } - pToken->data = iExtendedType; - pToken->buffer_length = bufInput.read_variable_length_uint(); - pToken->buffer = bufInput.get_pointer(); - if(!bufInput.skip(pToken->buffer_length)) - return nullptr; - break; - } + if (iTokenType & 0x80) + break; + else + iTokenTime += static_cast(iTokenType) * 3; } + pToken = lstTokens.append(iTokenTime, iTokenType); + pToken->buffer = bufInput.get_pointer() + 1; + switch (iTokenType & 0xF0) { + case 0xC0: + case 0xD0: + if (!bufInput.read(pToken->data)) return nullptr; + pToken->buffer = nullptr; + break; + case 0x80: + case 0xA0: + case 0xB0: + case 0xE0: + if (!bufInput.read(pToken->data)) return nullptr; + if (!bufInput.skip(1)) return nullptr; + break; + case 0x90: + if (!bufInput.read(iExtendedType)) return nullptr; + pToken->data = iExtendedType; + if (!bufInput.skip(1)) return nullptr; + pToken = lstTokens.append( + iTokenTime + bufInput.read_variable_length_uint() * 3, iTokenType); + pToken->data = iExtendedType; + pToken->buffer = "\0"; + break; + case 0xF0: + iExtendedType = 0; + if (iTokenType == 0xFF) { + if (!bufInput.read(iExtendedType)) return nullptr; - if(lstTokens.empty()) - return nullptr; - - memory_buffer bufOutput; - if(!bufOutput.write("MThd\0\0\0\x06\0\0\0\x01", 12)) - return nullptr; - if(!bufOutput.write_big_endian_uint16(static_cast((iTempo * 3) / 25000))) - return nullptr; - if(!bufOutput.write("MTrk\xBA\xAD\xF0\x0D", 8)) - return nullptr; - - std::sort(lstTokens.begin(), lstTokens.end()); - - iTokenTime = 0; - iTokenType = 0; - bEnd = false; - - for(midi_token_list::iterator itr = lstTokens.begin(), - itrEnd = lstTokens.end(); itr != itrEnd && !bEnd; ++itr) - { - if(!bufOutput.write_variable_length_uint(itr->time - iTokenTime)) - return nullptr; - iTokenTime = itr->time; - if(itr->type >= 0xF0) - { - if(!bufOutput.write(iTokenType = itr->type)) + if (iExtendedType == 0x2F) + bEnd = true; + else if (iExtendedType == 0x51) { + if (!bTempoSet) { + bufInput.skip(1); + iTempo = bufInput.read_big_endian_uint24() * 3; + bTempoSet = true; + bufInput.skip(-4); + } else { + lstTokens.pop_back(); + if (!bufInput.skip(bufInput.read_variable_length_uint())) return nullptr; - if(iTokenType == 0xFF) - { - if(!bufOutput.write(itr->data)) - return nullptr; - if(itr->data == 0x2F) - bEnd = true; - } - if(!bufOutput.write_variable_length_uint(itr->buffer_length)) - return nullptr; - if(!bufOutput.write(itr->buffer, itr->buffer_length)) - return nullptr; - } - else - { - if(itr->type != iTokenType) - { - if(!bufOutput.write(iTokenType = itr->type)) - return nullptr; - } - if(!bufOutput.write(itr->data)) - return nullptr; - if(itr->buffer) - { - if(!bufOutput.write(itr->buffer, 1)) - return nullptr; + break; } + } } + pToken->data = iExtendedType; + pToken->buffer_length = bufInput.read_variable_length_uint(); + pToken->buffer = bufInput.get_pointer(); + if (!bufInput.skip(pToken->buffer_length)) return nullptr; + break; } + } - uint32_t iLength = static_cast(bufOutput.tell() - 22); - bufOutput.seek(18); - bufOutput.write_big_endian_uint32(iLength); + if (lstTokens.empty()) return nullptr; - return bufOutput.take_data(midi_length); + memory_buffer bufOutput; + if (!bufOutput.write("MThd\0\0\0\x06\0\0\0\x01", 12)) return nullptr; + if (!bufOutput.write_big_endian_uint16( + static_cast((iTempo * 3) / 25000))) + return nullptr; + if (!bufOutput.write("MTrk\xBA\xAD\xF0\x0D", 8)) return nullptr; + + std::sort(lstTokens.begin(), lstTokens.end()); + + iTokenTime = 0; + iTokenType = 0; + bEnd = false; + + for (midi_token_list::iterator itr = lstTokens.begin(), + itrEnd = lstTokens.end(); + itr != itrEnd && !bEnd; ++itr) { + if (!bufOutput.write_variable_length_uint(itr->time - iTokenTime)) + return nullptr; + iTokenTime = itr->time; + if (itr->type >= 0xF0) { + if (!bufOutput.write(iTokenType = itr->type)) return nullptr; + if (iTokenType == 0xFF) { + if (!bufOutput.write(itr->data)) return nullptr; + if (itr->data == 0x2F) bEnd = true; + } + if (!bufOutput.write_variable_length_uint(itr->buffer_length)) + return nullptr; + if (!bufOutput.write(itr->buffer, itr->buffer_length)) return nullptr; + } else { + if (itr->type != iTokenType) { + if (!bufOutput.write(iTokenType = itr->type)) return nullptr; + } + if (!bufOutput.write(itr->data)) return nullptr; + if (itr->buffer) { + if (!bufOutput.write(itr->buffer, 1)) return nullptr; + } + } + } + + uint32_t iLength = static_cast(bufOutput.tell() - 22); + bufOutput.seek(18); + bufOutput.write_big_endian_uint32(iLength); + + return bufOutput.take_data(midi_length); } #endif diff --git a/CorsixTH/Src/xmi2mid.h b/CorsixTH/Src/xmi2mid.h index 32ca6277..ac07eb85 100644 --- a/CorsixTH/Src/xmi2mid.h +++ b/CorsixTH/Src/xmi2mid.h @@ -25,18 +25,17 @@ SOFTWARE. #include "config.h" #ifdef CORSIX_TH_USE_SDL_MIXER -uint8_t* transcode_xmi_to_midi(const unsigned char* xmi_data, - size_t xmi_length, size_t* midi_length); +uint8_t* transcode_xmi_to_midi(const unsigned char* xmi_data, size_t xmi_length, + size_t* midi_length); -#else // CORSIX_TH_USE_SDL_MIXER +#else // CORSIX_TH_USE_SDL_MIXER inline uint8_t* transcode_xmi_to_midi(const unsigned char* xmi_data, - size_t xmi_length, size_t* midi_length) -{ - // When SDL_mixer isn't being used, there is no need to transocde XMI to - // MIDI, so the function always fails. - return nullptr; + size_t xmi_length, size_t* midi_length) { + // When SDL_mixer isn't being used, there is no need to transocde XMI to + // MIDI, so the function always fails. + return nullptr; } -#endif // CORSIX_TH_USE_SDL_MIXER -#endif // CORSIX_TH_XMI2MID_H_ +#endif // CORSIX_TH_USE_SDL_MIXER +#endif // CORSIX_TH_XMI2MID_H_ diff --git a/CorsixTH/SrcUnshared/main.cpp b/CorsixTH/SrcUnshared/main.cpp index 88f95982..cbd4cf4d 100644 --- a/CorsixTH/SrcUnshared/main.cpp +++ b/CorsixTH/SrcUnshared/main.cpp @@ -20,36 +20,38 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -#include "config.h" #include "../Src/main.h" -#include "../Src/bootstrap.h" -#include +#include "config.h" #include +#include +#include "../Src/bootstrap.h" #ifdef CORSIX_TH_USE_SDL_MIXER #include #endif // Template magic for checking type equality template -struct types_equal{ enum{ +struct types_equal { + enum { result = -1, -}; }; + }; +}; template -struct types_equal{ enum{ +struct types_equal { + enum { result = 1, -}; }; + }; +}; -static void cleanup(lua_State* L) -{ +static void cleanup(lua_State* L) { #ifdef CORSIX_TH_USE_SDL_MIXER - while(Mix_QuerySpec(nullptr, nullptr, nullptr)) - { - Mix_CloseAudio(); - } + while (Mix_QuerySpec(nullptr, nullptr, nullptr)) { + Mix_CloseAudio(); + } #endif - SDL_Quit(); + SDL_Quit(); - lua_close(L); + lua_close(L); } //! Program entry point @@ -59,73 +61,64 @@ static void cleanup(lua_State* L) sooner, hence this function does as little as possible and leaves the rest for lua_main(). */ -int main(int argc, char** argv) -{ - struct compile_time_lua_check - { - // Lua 5.1, not 5.0, is required - int lua_5_point_1_required[LUA_VERSION_NUM >= 501 ? 1 : -1]; +int main(int argc, char** argv) { + struct compile_time_lua_check { + // Lua 5.1, not 5.0, is required + int lua_5_point_1_required[LUA_VERSION_NUM >= 501 ? 1 : -1]; - // Lua numbers must be doubles so that the mantissa has at least - // 32 bits (floats only have 24 bits) - int number_is_double[types_equal::result]; - }; + // Lua numbers must be doubles so that the mantissa has at least + // 32 bits (floats only have 24 bits) + int number_is_double[types_equal::result]; + }; - bool bRun = true; + bool bRun = true; - while(bRun) - { - lua_State *L = NULL; + while (bRun) { + lua_State* L = NULL; - L = luaL_newstate(); - if(L == NULL) - { - fprintf(stderr, "Fatal error starting CorsixTH: " - "Cannot open Lua state.\n"); - return 0; - } - lua_atpanic(L, lua_panic); - luaL_openlibs(L); - lua_settop(L, 0); - lua_pushcfunction(L, lua_stacktrace); - lua_pushcfunction(L, lua_main); - - // Move command line parameters onto the Lua stack - lua_checkstack(L, argc); - for(int i = 0; i < argc; ++i) - { - lua_pushstring(L, argv[i]); - } - - if(lua_pcall(L, argc, 0, 1) != 0) - { - const char* err = lua_tostring(L, -1); - if(err != NULL) - { - fprintf(stderr, "%s\n", err); - } - else - { - fprintf(stderr, "An error has occurred in CorsixTH:\n" - "Uncaught non-string Lua error\n"); - } - lua_pushcfunction(L, bootstrap_lua_error_report); - lua_insert(L, -2); - if(lua_pcall(L, 1, 0, 0) != 0) - { - fprintf(stderr, "%s\n", lua_tostring(L, -1)); - } - } - - lua_getfield(L, LUA_REGISTRYINDEX, "_RESTART"); - bRun = lua_toboolean(L, -1) != 0; - - cleanup(L); - - if(bRun) - { - printf("\n\nRestarting...\n\n\n"); - } + L = luaL_newstate(); + if (L == NULL) { + fprintf(stderr, + "Fatal error starting CorsixTH: " + "Cannot open Lua state.\n"); + return 0; } - return 0; + lua_atpanic(L, lua_panic); + luaL_openlibs(L); + lua_settop(L, 0); + lua_pushcfunction(L, lua_stacktrace); + lua_pushcfunction(L, lua_main); + + // Move command line parameters onto the Lua stack + lua_checkstack(L, argc); + for (int i = 0; i < argc; ++i) { + lua_pushstring(L, argv[i]); + } + + if (lua_pcall(L, argc, 0, 1) != 0) { + const char* err = lua_tostring(L, -1); + if (err != NULL) { + fprintf(stderr, "%s\n", err); + } else { + fprintf(stderr, + "An error has occurred in CorsixTH:\n" + "Uncaught non-string Lua error\n"); + } + lua_pushcfunction(L, bootstrap_lua_error_report); + lua_insert(L, -2); + if (lua_pcall(L, 1, 0, 0) != 0) { + fprintf(stderr, "%s\n", lua_tostring(L, -1)); + } + } + + lua_getfield(L, LUA_REGISTRYINDEX, "_RESTART"); + bRun = lua_toboolean(L, -1) != 0; + + cleanup(L); + + if (bRun) { + printf("\n\nRestarting...\n\n\n"); + } + } + return 0; } diff --git a/common/rnc.cpp b/common/rnc.cpp index f460b73e..9d28c068 100644 --- a/common/rnc.cpp +++ b/common/rnc.cpp @@ -35,12 +35,13 @@ SOFTWARE. */ #include "rnc.h" -#include -#include #include +#include +#include static const std::uint32_t rnc_signature = 0x524E4301; /*!< "RNC\001" */ +// clang-format off static const std::uint16_t rnc_crc_table[256] = { 0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241, 0xC601, 0x06C0, 0x0780, 0xC741, 0x0500, 0xC5C1, 0xC481, 0x0440, @@ -75,24 +76,22 @@ static const std::uint16_t rnc_crc_table[256] = { 0x4400, 0x84C1, 0x8581, 0x4540, 0x8701, 0x47C0, 0x4680, 0x8641, 0x8201, 0x42C0, 0x4380, 0x8341, 0x4100, 0x81C1, 0x8081, 0x4040, }; +// clang-format on -struct bit_stream -{ - std::uint32_t bitbuf; ///< holds between 16 and 32 bits. - int bitcount; ///< how many bits does bitbuf hold? - const std::uint8_t* endpos; ///< pointer past the readable data - const std::uint8_t* p; ///< pointer in data that stream is reading. +struct bit_stream { + std::uint32_t bitbuf; ///< holds between 16 and 32 bits. + int bitcount; ///< how many bits does bitbuf hold? + const std::uint8_t* endpos; ///< pointer past the readable data + const std::uint8_t* p; ///< pointer in data that stream is reading. }; -struct huf_table -{ - int num; ///< number of nodes in the tree. - struct - { - std::uint32_t code; - int codelen; - int value; - } table[32]; +struct huf_table { + int num; ///< number of nodes in the tree. + struct { + std::uint32_t code; + int codelen; + int value; + } table[32]; }; //! Calculate a CRC, the RNC way. @@ -100,56 +99,50 @@ struct huf_table @param data data for which to calculate the CRC @param len length of the data in bytes */ -static std::uint16_t rnc_crc(const std::uint8_t* data, std::size_t len) -{ - std::uint16_t val = 0; +static std::uint16_t rnc_crc(const std::uint8_t* data, std::size_t len) { + std::uint16_t val = 0; - while(len--) - { - val = static_cast(val ^ *data++); - val = static_cast((val >> 8) ^ rnc_crc_table[val & 0xFF]); - } + while (len--) { + val = static_cast(val ^ *data++); + val = static_cast((val >> 8) ^ rnc_crc_table[val & 0xFF]); + } - return val; + return val; } - //! Return the big-endian 32 bit word at p. /*! @param p Pointer to data containing the word */ -static std::uint32_t blong (const std::uint8_t *p) -{ - std::uint32_t n; - n = p[0]; - n = (n << 8) + p[1]; - n = (n << 8) + p[2]; - n = (n << 8) + p[3]; - return n; +static std::uint32_t blong(const std::uint8_t* p) { + std::uint32_t n; + n = p[0]; + n = (n << 8) + p[1]; + n = (n << 8) + p[2]; + n = (n << 8) + p[3]; + return n; } //! Return the big-endian 16 bit word at p. /*! @param p Pointer to data containing the word */ -static std::uint32_t bword (const std::uint8_t *p) -{ - std::uint32_t n; - n = p[0]; - n = (n << 8) + p[1]; - return n; +static std::uint32_t bword(const std::uint8_t* p) { + std::uint32_t n; + n = p[0]; + n = (n << 8) + p[1]; + return n; } //! Return the little-endian 16 bit word at p. /*! @param p Pointer to data containing the word */ -static std::uint32_t lword (const std::uint8_t *p) -{ - std::uint32_t n; - n = p[1]; - n = (n << 8) + p[0]; - return n; +static std::uint32_t lword(const std::uint8_t* p) { + std::uint32_t n; + n = p[1]; + n = (n << 8) + p[0]; + return n; } //! Mirror the bottom n bits of x. @@ -157,24 +150,20 @@ static std::uint32_t lword (const std::uint8_t *p) @param x @param n */ -static std::uint32_t mirror (std::uint32_t x, int n) -{ - std::uint32_t top = 1 << (n-1), bottom = 1; - while (top > bottom) - { - std::uint32_t mask = top | bottom; - std::uint32_t masked = x & mask; - if (masked != 0 && masked != mask) - { - x ^= mask; - } - top >>= 1; - bottom <<= 1; +static std::uint32_t mirror(std::uint32_t x, int n) { + std::uint32_t top = 1 << (n - 1), bottom = 1; + while (top > bottom) { + std::uint32_t mask = top | bottom; + std::uint32_t masked = x & mask; + if (masked != 0 && masked != mask) { + x ^= mask; } - return x; + top >>= 1; + bottom <<= 1; + } + return x; } - //! Initialises a bit stream with the first two bytes of the packed //! data. /*! @@ -184,12 +173,12 @@ static std::uint32_t mirror (std::uint32_t x, int n) @param endpos Pointer to byte after the last memory block the bitstream is to traverse */ -static void bitread_init (bit_stream *bs, const std::uint8_t *p, const std::uint8_t* endpos) -{ - bs->bitbuf = lword(p); - bs->bitcount = 16; - bs->p = p; - bs->endpos = endpos; +static void bitread_init(bit_stream* bs, const std::uint8_t* p, + const std::uint8_t* endpos) { + bs->bitbuf = lword(p); + bs->bitcount = 16; + bs->p = p; + bs->endpos = endpos; } //! Fixes up a bit stream after literals have been read out of the @@ -197,22 +186,20 @@ static void bitread_init (bit_stream *bs, const std::uint8_t *p, const std::uint /*! @param bs Bit stream to correct */ -static void bitread_fix (bit_stream *bs) -{ - // Remove the top 16 bits - bs->bitcount -= 16; - bs->bitbuf &= (1<bitcount)-1; +static void bitread_fix(bit_stream* bs) { + // Remove the top 16 bits + bs->bitcount -= 16; + bs->bitbuf &= (1 << bs->bitcount) - 1; - // Replace with what is in the new current location - // in the bit stream - if(bs->p < bs->endpos - 1) - { - bs->bitbuf |= (lword(bs->p)<bitcount); - bs->bitcount += 16; - } else if (bs->p == bs->endpos - 1) { - bs->bitbuf |= (*(bs->p)<bitcount); - bs->bitcount += 16; - } + // Replace with what is in the new current location + // in the bit stream + if (bs->p < bs->endpos - 1) { + bs->bitbuf |= (lword(bs->p) << bs->bitcount); + bs->bitcount += 16; + } else if (bs->p == bs->endpos - 1) { + bs->bitbuf |= (*(bs->p) << bs->bitcount); + bs->bitcount += 16; + } } //! Return a word consisting of the specified bits without advancing @@ -221,9 +208,8 @@ static void bitread_fix (bit_stream *bs) @param bs Bit stream from which to peek @param mask A 32 bit bit mask specifying which bits to peek */ -static std::uint32_t bit_peek (bit_stream *bs, const std::uint32_t mask) -{ - return bs->bitbuf & mask; +static std::uint32_t bit_peek(bit_stream* bs, const std::uint32_t mask) { + return bs->bitbuf & mask; } //! Advances the bit stream. @@ -232,28 +218,25 @@ static std::uint32_t bit_peek (bit_stream *bs, const std::uint32_t mask) @param n Number of bits to advance the stream. Must be between 0 and 16 */ -static void bit_advance (bit_stream *bs, int n) -{ - bs->bitbuf >>= n; - bs->bitcount -= n; +static void bit_advance(bit_stream* bs, int n) { + bs->bitbuf >>= n; + bs->bitcount -= n; - if (bs->bitcount < 16) - { - // At this point it is possible for bs->p to advance past - // the end of the data. In that case we simply do not read - // anything more into the buffer. If we are on the last - // byte the lword matches what is in that byte. - bs->p += 2; + if (bs->bitcount < 16) { + // At this point it is possible for bs->p to advance past + // the end of the data. In that case we simply do not read + // anything more into the buffer. If we are on the last + // byte the lword matches what is in that byte. + bs->p += 2; - if (bs->p < (bs->endpos - 1)) - { - bs->bitbuf |= (lword(bs->p)<bitcount); - bs->bitcount += 16; - } else if (bs->p < bs->endpos) { - bs->bitbuf |= (*(bs->p)<bitcount); - bs->bitcount += 16; - } + if (bs->p < (bs->endpos - 1)) { + bs->bitbuf |= (lword(bs->p) << bs->bitcount); + bs->bitcount += 16; + } else if (bs->p < bs->endpos) { + bs->bitbuf |= (*(bs->p) << bs->bitcount); + bs->bitcount += 16; } + } } //! Returns bits from the bit stream matching the mask and advances it @@ -264,11 +247,10 @@ static void bit_advance (bit_stream *bs, int n) @param n Number of bits to advance the stream. Must be between 0 and 16 */ -static std::uint32_t bit_read (bit_stream *bs, std::uint32_t mask, int n) -{ - std::uint32_t result = bit_peek(bs, mask); - bit_advance(bs, n); - return result; +static std::uint32_t bit_read(bit_stream* bs, std::uint32_t mask, int n) { + std::uint32_t result = bit_peek(bs, mask); + bit_advance(bs, n); + return result; } //! Read a Huffman table out of the bit stream given. @@ -277,48 +259,41 @@ static std::uint32_t bit_read (bit_stream *bs, std::uint32_t mask, int n) @param bs Bit stream pointing to the start of the Huffman table description */ -static void read_huftable(huf_table *h, bit_stream *bs) -{ - int i, j, k, num; - int leaflen[32]; - int leafmax; - std::uint32_t codeb; /* big-endian form of code. */ +static void read_huftable(huf_table* h, bit_stream* bs) { + int i, j, k, num; + int leaflen[32]; + int leafmax; + std::uint32_t codeb; /* big-endian form of code. */ - num = bit_read(bs, 0x1F, 5); + num = bit_read(bs, 0x1F, 5); - if(num == 0) - { - return; + if (num == 0) { + return; + } + + leafmax = 1; + for (i = 0; i < num; i++) { + leaflen[i] = bit_read(bs, 0x0F, 4); + if (leafmax < leaflen[i]) { + leafmax = leaflen[i]; } + } - leafmax = 1; - for(i = 0; i < num; i++) - { - leaflen[i] = bit_read(bs, 0x0F, 4); - if (leafmax < leaflen[i]) - { - leafmax = leaflen[i]; - } + codeb = 0L; + k = 0; + for (i = 1; i <= leafmax; i++) { + for (j = 0; j < num; j++) { + if (leaflen[j] == i) { + h->table[k].code = mirror(codeb, i); + h->table[k].codelen = i; + h->table[k].value = j; + codeb++; + k++; + } } - - codeb = 0L; - k = 0; - for(i = 1; i <= leafmax; i++) - { - for(j = 0; j < num; j++) - { - if(leaflen[j] == i) - { - h->table[k].code = mirror(codeb, i); - h->table[k].codelen = i; - h->table[k].value = j; - codeb++; - k++; - } - } - codeb <<= 1; - } - h->num = k; + codeb <<= 1; + } + h->num = k; } //! Read a value out of the bit stream using the given Huffman table. @@ -326,49 +301,44 @@ static void read_huftable(huf_table *h, bit_stream *bs) @param h Huffman table to transcribe from @param bs bit stream @param p input data - @return The value from the table with the matching bits, or -1 if none found. + @return The value from the table with the matching bits, or -1 if none + found. */ -static std::uint32_t huf_read(huf_table *h, bit_stream *bs, const std::uint8_t **p) -{ - int i; - std::uint32_t val; - std::uint32_t mask; +static std::uint32_t huf_read(huf_table* h, bit_stream* bs, + const std::uint8_t** p) { + int i; + std::uint32_t val; + std::uint32_t mask; - // Find the current bits in the table - for (i = 0; i < h->num; i++) - { - mask = (1 << h->table[i].codelen) - 1; - if(bit_peek(bs, mask) == h->table[i].code) - { - break; - } + // Find the current bits in the table + for (i = 0; i < h->num; i++) { + mask = (1 << h->table[i].codelen) - 1; + if (bit_peek(bs, mask) == h->table[i].code) { + break; } + } - // No match found in table (error) - if(i == h->num) - { - return -1; - } + // No match found in table (error) + if (i == h->num) { + return -1; + } - bit_advance(bs, h->table[i].codelen); + bit_advance(bs, h->table[i].codelen); - val = h->table[i].value; - if (val >= 2) - { - val = 1 << (val-1); - val |= bit_read(bs, val-1, h->table[i].value - 1); - } - return val; + val = h->table[i].value; + if (val >= 2) { + val = 1 << (val - 1); + val |= bit_read(bs, val - 1, h->table[i].value - 1); + } + return val; } -std::size_t rnc_output_size(const std::uint8_t* input) -{ - return static_cast(blong(input + 4)); +std::size_t rnc_output_size(const std::uint8_t* input) { + return static_cast(blong(input + 4)); } -std::size_t rnc_input_size(const std::uint8_t* input) -{ - return static_cast(blong(input + 8) + rnc_header_size); +std::size_t rnc_input_size(const std::uint8_t* input) { + return static_cast(blong(input + 8) + rnc_header_size); } //! Decompresses RNC data @@ -379,107 +349,93 @@ std::size_t rnc_input_size(const std::uint8_t* input) 4 byte segment of the input header starting at the 4th byte in Big-endian. */ -rnc_status rnc_unpack(const std::uint8_t* input, std::uint8_t* output) -{ - const std::uint8_t *inputend; - std::uint8_t *outputend; - bit_stream input_bs; - huf_table raw = {0}, dist = {0}, len = {0}; - std::uint32_t ch_count; - std::uint32_t ret_len; - std::uint32_t out_crc; - if(blong(input) != rnc_signature) - { - return rnc_status::file_is_not_rnc; - } - ret_len = blong(input + 4); - outputend = output + ret_len; - inputend = input + 18 + blong(input + 8); +rnc_status rnc_unpack(const std::uint8_t* input, std::uint8_t* output) { + const std::uint8_t* inputend; + std::uint8_t* outputend; + bit_stream input_bs; + huf_table raw = {0}, dist = {0}, len = {0}; + std::uint32_t ch_count; + std::uint32_t ret_len; + std::uint32_t out_crc; + if (blong(input) != rnc_signature) { + return rnc_status::file_is_not_rnc; + } + ret_len = blong(input + 4); + outputend = output + ret_len; + inputend = input + 18 + blong(input + 8); - //skip header - input += 18; + // skip header + input += 18; - // Check the packed-data CRC. Also save the unpacked-data CRC - // for later. - if (rnc_crc(input, inputend - input) != bword(input - 4)) - { - return rnc_status::packed_crc_error; - } - out_crc = bword(input - 6); + // Check the packed-data CRC. Also save the unpacked-data CRC + // for later. + if (rnc_crc(input, inputend - input) != bword(input - 4)) { + return rnc_status::packed_crc_error; + } + out_crc = bword(input - 6); - //initialize the bitstream to the input and advance past the - //first two bits as they don't have any understood use. - bitread_init(&input_bs, input, inputend); - bit_advance(&input_bs, 2); + // initialize the bitstream to the input and advance past the + // first two bits as they don't have any understood use. + bitread_init(&input_bs, input, inputend); + bit_advance(&input_bs, 2); - //process chunks - while (output < outputend) - { - read_huftable(&raw, &input_bs); //raw byte length table - read_huftable(&dist, &input_bs); //distance prior to copy table - read_huftable(&len, &input_bs); //length bytes to copy table - ch_count = bit_read(&input_bs, 0xFFFF, 16); + // process chunks + while (output < outputend) { + read_huftable(&raw, &input_bs); // raw byte length table + read_huftable(&dist, &input_bs); // distance prior to copy table + read_huftable(&len, &input_bs); // length bytes to copy table + ch_count = bit_read(&input_bs, 0xFFFF, 16); - while(true) - { - long length, posn; + while (true) { + long length, posn; - // Copy bit pattern to output based on lookup - // of bytes from input. - length = huf_read(&raw, &input_bs, &input); - if(length == -1) - { - return rnc_status::huf_decode_error; - } - if(length) - { - while(length--) - { - *output++ = *(input_bs.p++); - } - bitread_fix(&input_bs); - } - if(--ch_count <= 0) - { - break; - } - - // Read position to copy output to - posn = huf_read(&dist, &input_bs, &input); - if(posn == -1) - { - return rnc_status::huf_decode_error; - } - posn += 1; - - // Read length of output to copy back - length = huf_read(&len, &input_bs, &input); - if(length == -1) - { - return rnc_status::huf_decode_error; - } - length += 2; - - // Copy length bytes from output back posn - while (length > 0) - { - length--; - *output = output[-posn]; - output++; - } + // Copy bit pattern to output based on lookup + // of bytes from input. + length = huf_read(&raw, &input_bs, &input); + if (length == -1) { + return rnc_status::huf_decode_error; + } + if (length) { + while (length--) { + *output++ = *(input_bs.p++); } - } + bitread_fix(&input_bs); + } + if (--ch_count <= 0) { + break; + } - if(outputend != output) - { - return rnc_status::file_size_mismatch; - } + // Read position to copy output to + posn = huf_read(&dist, &input_bs, &input); + if (posn == -1) { + return rnc_status::huf_decode_error; + } + posn += 1; - // Check the unpacked-data CRC. - if (rnc_crc(outputend - ret_len, ret_len) != out_crc) - { - return rnc_status::unpacked_crc_error; - } + // Read length of output to copy back + length = huf_read(&len, &input_bs, &input); + if (length == -1) { + return rnc_status::huf_decode_error; + } + length += 2; - return rnc_status::ok; + // Copy length bytes from output back posn + while (length > 0) { + length--; + *output = output[-posn]; + output++; + } + } + } + + if (outputend != output) { + return rnc_status::file_size_mismatch; + } + + // Check the unpacked-data CRC. + if (rnc_crc(outputend - ret_len, ret_len) != out_crc) { + return rnc_status::unpacked_crc_error; + } + + return rnc_status::ok; } diff --git a/common/rnc.h b/common/rnc.h index 44464ff0..ecb515bf 100644 --- a/common/rnc.h +++ b/common/rnc.h @@ -27,14 +27,13 @@ SOFTWARE. #include /*! Result status values from #rnc_inpack. */ -enum class rnc_status -{ - ok, ///< Everything is fine - file_is_not_rnc, ///< The file does not begin with an RNC signature - huf_decode_error, ///< Error decoding the file - file_size_mismatch, ///< The file size does not match the header - packed_crc_error, ///< The compressed file does not match its checksum - unpacked_crc_error ///< The uncompressed file does not match its checksum +enum class rnc_status { + ok, ///< Everything is fine + file_is_not_rnc, ///< The file does not begin with an RNC signature + huf_decode_error, ///< Error decoding the file + file_size_mismatch, ///< The file size does not match the header + packed_crc_error, ///< The compressed file does not match its checksum + unpacked_crc_error ///< The uncompressed file does not match its checksum }; const std::size_t rnc_header_size = 18; @@ -45,4 +44,4 @@ std::size_t rnc_input_size(const std::uint8_t* input); rnc_status rnc_unpack(const std::uint8_t* input, std::uint8_t* output); -#endif // CORSIX_TH_RNC_H_ +#endif // CORSIX_TH_RNC_H_