[RDY] Cpp fmt (#1562)

* Hand formatted changes before automatic formatting

* Break up / shorten some long lines

* Add some missing braces for clarity

* Multiline strings are merged to let clang-format split them
appropriately.

* sdl_core's frame_count changed from a C style array to std::array
which made the length checks simpler.

* Add includes and forward declairs to avoid transitive dependencies

* Remove th_gfx_font.h include from th_gfx.h - circular dependencies

* using to shorten lines in th_map.cpp

* Avoid non-portable reinterpret_cast for parcels in th_map.

* Use more constants in th_map.

* Use class initializer for th_map classes

* Add clang files to ignore list

* Add clang-format file

* Reformat all files with clang-format

Also includes some manual braces.

* Disable clang-format for backdrop.h

* Clang-format AnimView src files

* clang-format common code

* Fix anonymous struct in union warning

Anonymous structs in anonymous unions are not supported by the standard.

* Check clang-format in travis

* Bin pack parameters

* Bin pack arguments too

* Full Google style

2 space indent, no forced break on braces.

* A couple format overrides

Order of usings in config.h.in since 8 is smaller than 16.
Table layout, since 0x80 nicely fits in 8 columns.
This commit is contained in:
Stephen E. Baker
2019-10-06 08:18:25 -04:00
committed by GitHub
parent 326e2fa622
commit 4a1c98a716
68 changed files with 21334 additions and 23075 deletions

22
.clang-format Normal file
View File

@@ -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

3
.gitignore vendored
View File

@@ -77,6 +77,9 @@ CMakeFiles
Makefile Makefile
CMakeScripts/ CMakeScripts/
# Clang tools
compile_commands.json
# This is for the CMake-generated Visual Studio project # This is for the CMake-generated Visual Studio project
*.vcxproj *.vcxproj
*.vcxproj.user *.vcxproj.user

View File

@@ -44,7 +44,6 @@ before_script:
# Don't ask for confirmation when using scp # Don't ask for confirmation when using scp
- echo -e "Host armedpineapple.co.uk\n\tStrictHostKeyChecking no\n" >> ~/.ssh/config - 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 - echo -e "Host server2.armedpineapple.co.uk\n\tStrictHostKeyChecking no\n" >> ~/.ssh/config
- cd fresh
script: script:
# Check if there are trailing whitespaces. # Check if there are trailing whitespaces.
- ${TRAVIS_BUILD_DIR}/scripts/check_trailing_whitespaces.py $TRAVIS_BUILD_DIR - ${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 - ${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. # Check if there are lua classes with invalid/improper declarations.
- ${TRAVIS_BUILD_DIR}/scripts/check_lua_classes.py - ${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 # Build CorsixTH
- cd fresh
- make VERBOSE=1 - make VERBOSE=1
# Validate lua files # Validate lua files
- find $TRAVIS_BUILD_DIR -path $TRAVIS_BUILD_DIR/CorsixTH/Lua/languages -prune -o -name '*.lua' -print0 | xargs -0 luac -p -- - find $TRAVIS_BUILD_DIR -path $TRAVIS_BUILD_DIR/CorsixTH/Lua/languages -prune -o -name '*.lua' -print0 | xargs -0 luac -p --

View File

@@ -20,23 +20,23 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE. SOFTWARE.
*/ */
#include "config.h"
#include "app.h" #include "app.h"
#include "config.h"
#include "frmMain.h" #include "frmMain.h"
#include "frmSprites.h" #include "frmSprites.h"
IMPLEMENT_APP(ThemeHospitalAnimViewApp) IMPLEMENT_APP(ThemeHospitalAnimViewApp)
bool ThemeHospitalAnimViewApp::OnInit() bool ThemeHospitalAnimViewApp::OnInit() {
{ wxTopLevelWindow* pForm;
wxTopLevelWindow *pForm; if (::wxMessageBox(L"Launch animation viewer? (No -> sprite viewer)",
if(::wxMessageBox(L"Launch animation viewer? (No -> sprite viewer)", L"AnimView", wxYES_NO) == wxYES) L"AnimView", wxYES_NO) == wxYES)
pForm = new frmMain; pForm = new frmMain;
else else
pForm = new frmSprites; pForm = new frmSprites;
pForm->Show(true); pForm->Show(true);
SetTopWindow(pForm); SetTopWindow(pForm);
return true; return true;
} }

View File

@@ -27,19 +27,18 @@ SOFTWARE.
#include "wx/wxprec.h" #include "wx/wxprec.h"
#ifdef __BORLANDC__ #ifdef __BORLANDC__
#pragma hdrstop #pragma hdrstop
#endif #endif
// for all others, include the necessary headers (this file is usually all you // for all others, include the necessary headers (this file is usually all you
// need because it includes almost all "standard" wxWidgets headers) // need because it includes almost all "standard" wxWidgets headers)
#ifndef WX_PRECOMP #ifndef WX_PRECOMP
#include "wx/wx.h" #include "wx/wx.h"
#endif #endif
// ---------------------------- // ----------------------------
class ThemeHospitalAnimViewApp : public wxApp class ThemeHospitalAnimViewApp : public wxApp {
{ virtual bool OnInit();
virtual bool OnInit();
}; };
DECLARE_APP(ThemeHospitalAnimViewApp) DECLARE_APP(ThemeHospitalAnimViewApp)

View File

@@ -1,4 +1,5 @@
/* XPM */ /* XPM */
// clang-format off
static const char *const backdrop_xpm[] = { static const char *const backdrop_xpm[] = {
"126 64 12 1", "126 64 12 1",
"f c #695D7D", "f c #695D7D",
@@ -78,3 +79,4 @@ static const char *const backdrop_xpm[] = {
" bfffff bfffff ", " bfffff bfffff ",
" .f .f " " .f .f "
}; };
// clang-format on

File diff suppressed because it is too large Load Diff

View File

@@ -22,112 +22,113 @@ SOFTWARE.
#pragma once #pragma once
#include "config.h" #include "config.h"
#include <wx/frame.h>
#include <wx/button.h> #include <wx/button.h>
#include <wx/spinctrl.h>
#include <wx/checkbox.h> #include <wx/checkbox.h>
#include <wx/textctrl.h>
#include <wx/panel.h>
#include <wx/timer.h>
#include <wx/listbox.h>
#include <wx/dcclient.h> #include <wx/dcclient.h>
#include <wx/frame.h>
#include <wx/listbox.h>
#include <wx/panel.h>
#include <wx/spinctrl.h>
#include <wx/textctrl.h>
#include <wx/timer.h>
#include <wx/txtstrm.h> #include <wx/txtstrm.h>
#include "th.h" #include "th.h"
//#include <vector> //#include <vector>
class frmMain : public wxFrame class frmMain : public wxFrame {
{ public:
public: frmMain();
frmMain(); ~frmMain();
~frmMain();
enum enum {
{ ID_FIRST_ANIM = wxID_HIGHEST + 1,
ID_FIRST_ANIM = wxID_HIGHEST + 1, ID_PREV_ANIM,
ID_PREV_ANIM, ID_ANIM_INDEX,
ID_ANIM_INDEX, ID_NEXT_ANIM,
ID_NEXT_ANIM, ID_LAST_ANIM,
ID_LAST_ANIM, ID_PREV_FRAME,
ID_PREV_FRAME, ID_NEXT_FRAME,
ID_NEXT_FRAME, ID_PLAY_PAUSE,
ID_PLAY_PAUSE, ID_TIMER_ANIMATE,
ID_TIMER_ANIMATE, ID_SEARCH_LAYER_ID,
ID_SEARCH_LAYER_ID, ID_SEARCH_FRAME,
ID_SEARCH_FRAME, ID_SEARCH_SOUND,
ID_SEARCH_SOUND, ID_SEARCH_RESULTS,
ID_SEARCH_RESULTS, ID_GHOST_0,
ID_GHOST_0, ID_GHOST_1,
ID_GHOST_1, ID_GHOST_2,
ID_GHOST_2, ID_GHOST_3,
ID_GHOST_3, ID_LOAD,
ID_LOAD, ID_BROWSE,
ID_BROWSE, ID_EXPORT,
ID_EXPORT, ID_DRAW_MOOD,
ID_DRAW_MOOD, ID_DRAW_COORDINATES,
ID_DRAW_COORDINATES, ID_LAYER_CHECKS, // Must be last ID
ID_LAYER_CHECKS, // Must be last ID };
};
void load(); void load();
void export_png(); void export_png();
void exportSpritesPage(bool bComplex, wxString sPath, wxString sFilename, wxString spPath=L"", wxString sPalette=L"MPALETTE.DAT"); void exportSpritesPage(bool bComplex, wxString sPath, wxString sFilename,
//std::vector<_sprite_t> m_vSprites; wxString spPath = L"",
wxString sPalette = L"MPALETTE.DAT");
// std::vector<_sprite_t> m_vSprites;
protected: protected:
void _onLoad(wxCommandEvent& evt); void _onLoad(wxCommandEvent& evt);
void _onBrowse(wxCommandEvent& evt); void _onBrowse(wxCommandEvent& evt);
void _onExport(wxCommandEvent& evt); void _onExport(wxCommandEvent& evt);
void _onFirstAnim(wxCommandEvent& evt); void _onFirstAnim(wxCommandEvent& evt);
void _onPrevAnim(wxCommandEvent& evt); void _onPrevAnim(wxCommandEvent& evt);
void _onNextAnim(wxCommandEvent& evt); void _onNextAnim(wxCommandEvent& evt);
void _onLastAnim(wxCommandEvent& evt); void _onLastAnim(wxCommandEvent& evt);
void _onPrevFrame(wxCommandEvent& evt); void _onPrevFrame(wxCommandEvent& evt);
void _onNextFrame(wxCommandEvent& evt); void _onNextFrame(wxCommandEvent& evt);
void _onPlayPause(wxCommandEvent& evt); void _onPlayPause(wxCommandEvent& evt);
void _onToggleMask(wxCommandEvent& evt); void _onToggleMask(wxCommandEvent& evt);
void _onToggleDrawMood(wxCommandEvent& evt); void _onToggleDrawMood(wxCommandEvent& evt);
void _onToggleDrawCoordinates(wxCommandEvent& evt); void _onToggleDrawCoordinates(wxCommandEvent& evt);
void _onSearchLayerId(wxCommandEvent& evt); void _onSearchLayerId(wxCommandEvent& evt);
void _onSearchFrame(wxCommandEvent& evt); void _onSearchFrame(wxCommandEvent& evt);
void _onSearchSoundIndex(wxCommandEvent& evt); void _onSearchSoundIndex(wxCommandEvent& evt);
void _onGotoSearchResult(wxCommandEvent& evt); void _onGotoSearchResult(wxCommandEvent& evt);
void _onAnimChar(wxCommandEvent& evt); void _onAnimChar(wxCommandEvent& evt);
void _onGhostFileChange(wxCommandEvent& evt); void _onGhostFileChange(wxCommandEvent& evt);
void _onGhostIndexChange(wxSpinEvent& evt); void _onGhostIndexChange(wxSpinEvent& evt);
void _onPanelPaint(wxPaintEvent& evt); void _onPanelPaint(wxPaintEvent& evt);
void _onPanelClick(wxMouseEvent& evt); void _onPanelClick(wxMouseEvent& evt);
void _onTimer(wxTimerEvent& evt); void _onTimer(wxTimerEvent& evt);
void _onAnimChange(size_t iIndex); void _onAnimChange(size_t iIndex);
void _drawCoordinates(wxPaintDC& DC, int i, int j); void _drawCoordinates(wxPaintDC& DC, int i, int j);
wxString _getCaseSensitivePath(const wxString& sInsensitivePathPart, const wxString& sPath); wxString _getCaseSensitivePath(const wxString& sInsensitivePathPart,
const wxString& sPath);
THAnimations m_oAnims; THAnimations m_oAnims;
THLayerMask m_mskLayers; THLayerMask m_mskLayers;
wxImage m_imgBackground; wxImage m_imgBackground;
wxTimer m_tmrAnimate; wxTimer m_tmrAnimate;
size_t m_iCurrentAnim; size_t m_iCurrentAnim;
size_t m_iCurrentFrame; size_t m_iCurrentFrame;
int m_iGhostFile; int m_iGhostFile;
int m_iGhostIndex; int m_iGhostIndex;
int m_iMoodDrawX; int m_iMoodDrawX;
int m_iMoodDrawY; int m_iMoodDrawY;
bool m_bPlayingAnimation; bool m_bPlayingAnimation;
bool m_bDrawMood; bool m_bDrawMood;
bool m_bDrawCoordinates; bool m_bDrawCoordinates;
wxButton* m_btnPlayPause; wxButton* m_btnPlayPause;
wxButton* m_btnExport; wxButton* m_btnExport;
wxTextCtrl* m_txtTHPath; wxTextCtrl* m_txtTHPath;
wxTextCtrl* m_txtAnimIndex; wxTextCtrl* m_txtAnimIndex;
wxTextCtrl* m_txtAnimCount; wxTextCtrl* m_txtAnimCount;
wxTextCtrl* m_txtFrameIndex; wxTextCtrl* m_txtFrameIndex;
wxTextCtrl* m_txtFrameCount; wxTextCtrl* m_txtFrameCount;
wxTextCtrl* m_txtFrameFlags[2]; wxTextCtrl* m_txtFrameFlags[2];
wxTextCtrl* m_txtMoodPosition[2]; wxTextCtrl* m_txtMoodPosition[2];
wxCheckBox* m_chkFrameFlags[16]; wxCheckBox* m_chkFrameFlags[16];
wxListBox* m_lstSearchResults; wxListBox* m_lstSearchResults;
wxPanel* m_panFrame; wxPanel* m_panFrame;
DECLARE_EVENT_TABLE() DECLARE_EVENT_TABLE()
}; };

View File

@@ -20,15 +20,15 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE. SOFTWARE.
*/ */
#include "config.h"
#include "frmSprites.h" #include "frmSprites.h"
#include <wx/sizer.h> #include "config.h"
#include <wx/stattext.h> #include <wx/dcclient.h>
#include <wx/msgdlg.h>
#include <wx/dirdlg.h> #include <wx/dirdlg.h>
#include <wx/filedlg.h> #include <wx/filedlg.h>
#include <wx/msgdlg.h>
#include <wx/sizer.h>
#include <wx/stattext.h>
#include <wx/vscroll.h> #include <wx/vscroll.h>
#include <wx/dcclient.h>
BEGIN_EVENT_TABLE(frmSprites, wxFrame) BEGIN_EVENT_TABLE(frmSprites, wxFrame)
EVT_BUTTON(ID_LOAD, frmSprites::_onLoad) EVT_BUTTON(ID_LOAD, frmSprites::_onLoad)
@@ -40,171 +40,177 @@ BEGIN_EVENT_TABLE(frmSprites, wxFrame)
END_EVENT_TABLE() END_EVENT_TABLE()
frmSprites::frmSprites() frmSprites::frmSprites()
: wxFrame(NULL, wxID_ANY, L"Theme Hospital Sprite Viewer") : wxFrame(NULL, wxID_ANY, L"Theme Hospital Sprite Viewer") {
{ wxBoxSizer* pMainSizer = new wxBoxSizer(wxVERTICAL);
wxBoxSizer *pMainSizer = new wxBoxSizer(wxVERTICAL);
wxStaticBoxSizer *pFiles = new wxStaticBoxSizer(wxVERTICAL, this, L"Files"); wxStaticBoxSizer* pFiles = new wxStaticBoxSizer(wxVERTICAL, this, L"Files");
wxFlexGridSizer *pFilesGrid = new wxFlexGridSizer(4, 3, 2, 1); wxFlexGridSizer* pFilesGrid = new wxFlexGridSizer(4, 3, 2, 1);
pFilesGrid->AddGrowableCol(1, 1); pFilesGrid->AddGrowableCol(1, 1);
pFilesGrid->Add(new wxStaticText(this, wxID_ANY, L"Table:"), 0, wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT); pFilesGrid->Add(new wxStaticText(this, wxID_ANY, L"Table:"), 0,
pFilesGrid->Add(m_txtTable = new wxTextCtrl(this, wxID_ANY, L"X:\\ThemeHospital\\hospital\\QData\\Font00V.tab"), 1, wxALIGN_CENTER_VERTICAL | wxEXPAND); wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT);
pFilesGrid->Add(new wxButton(this, ID_BROWSE_TABLE, L"Browse..."), 0, wxALIGN_CENTER_VERTICAL); pFilesGrid->Add(
pFilesGrid->Add(new wxStaticText(this, wxID_ANY, L"Data:"), 0, wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT); m_txtTable = new wxTextCtrl(
pFilesGrid->Add(m_txtData = new wxTextCtrl(this, wxID_ANY, L""), 1, wxALIGN_CENTER_VERTICAL | wxEXPAND); this, wxID_ANY, L"X:\\ThemeHospital\\hospital\\QData\\Font00V.tab"),
pFilesGrid->Add(new wxButton(this, ID_BROWSE_DATA, L"Browse..."), 0, wxALIGN_CENTER_VERTICAL); 1, wxALIGN_CENTER_VERTICAL | wxEXPAND);
pFilesGrid->Add(new wxStaticText(this, wxID_ANY, L"Palette:"), 0, wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT); pFilesGrid->Add(new wxButton(this, ID_BROWSE_TABLE, L"Browse..."), 0,
pFilesGrid->Add(m_txtPalette = new wxTextCtrl(this, wxID_ANY, L"X:\\ThemeHospital\\hospital\\Data\\MPalette.dat"), 1, wxALIGN_CENTER_VERTICAL | wxEXPAND); wxALIGN_CENTER_VERTICAL);
pFilesGrid->Add(new wxButton(this, ID_BROWSE_PALETTE, L"Browse..."), 0, wxALIGN_CENTER_VERTICAL); pFilesGrid->Add(new wxStaticText(this, wxID_ANY, L"Data:"), 0,
pFiles->Add(pFilesGrid, 0, wxEXPAND | wxALL, 1); wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT);
wxButton *pTmp; pFilesGrid->Add(m_txtData = new wxTextCtrl(this, wxID_ANY, L""), 1,
pFiles->Add(pTmp = new wxButton(this, ID_LOAD, L"Load Simple"), 0, wxALIGN_CENTER | wxALL, 1); wxALIGN_CENTER_VERTICAL | wxEXPAND);
pFiles->Add(pTmp = new wxButton(this, ID_LOAD_COMPLEX, L"Load Complex"), 0, wxALIGN_CENTER | wxALL, 1); pFilesGrid->Add(new wxButton(this, ID_BROWSE_DATA, L"Browse..."), 0,
pFiles->Add(pTmp = new wxButton(this, ID_NEXT, L"Next"), 0, wxALIGN_CENTER | wxALL, 1); wxALIGN_CENTER_VERTICAL);
SetBackgroundColour(pTmp->GetBackgroundColour()); pFilesGrid->Add(new wxStaticText(this, wxID_ANY, L"Palette:"), 0,
pMainSizer->Add(pFiles, 0, wxEXPAND | wxALL, 2); 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"); wxStaticBoxSizer* pSprites =
pSprites->Add(m_panFrame = new MyVScrolled(this), 1, wxEXPAND); new wxStaticBoxSizer(wxVERTICAL, this, L"Sprites");
pMainSizer->Add(pSprites, 1, wxEXPAND | wxALL, 2); pSprites->Add(m_panFrame = new MyVScrolled(this), 1, wxEXPAND);
m_panFrame->Connect(wxEVT_PAINT, (wxObjectEventFunction)&frmSprites::_onPanelPaint, NULL, this); 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) void frmSprites::_onLoad(wxCommandEvent& evt) { load(false); }
{
load(false);
}
void frmSprites::_onLoadComplex(wxCommandEvent& evt) void frmSprites::_onLoadComplex(wxCommandEvent& evt) { load(true); }
{
load(true);
}
void frmSprites::_onNext(wxCommandEvent& evt) void frmSprites::_onNext(wxCommandEvent& evt) {
{ wxString s = m_txtTable->GetValue();
wxString s = m_txtTable->GetValue(); while (true) {
while(true) const wxChar* sc = s.c_str();
{ for (size_t i = s.Length(); i > 0;) {
const wxChar* sc = s.c_str(); --i;
for(size_t i = s.Length(); i > 0;) if ('0' <= sc[i] && sc[i] <= '9') {
{ s.SetChar(i, sc[i] + 1);
--i; if (sc[i] > '9') {
if('0' <= sc[i] && sc[i] <= '9') s.SetChar(i, '0');
{ if (sc[i - 1] == '9') {
s.SetChar(i, sc[i] + 1); s.SetChar(i - 1, '0');
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; return;
}
s.SetChar(i - 1, sc[i - 1] + 1);
} }
break;
}
} }
if (::wxFileExists(s)) {
m_txtTable->SetValue(s);
return;
}
}
} }
void frmSprites::load(bool bComplex) void frmSprites::load(bool bComplex) {
{ if (!m_oAnims.loadTableFile(m_txtTable->GetValue()) ||
if(!m_oAnims.loadTableFile(m_txtTable->GetValue()) !m_oAnims.loadSpriteFile(m_txtData->GetValue().IsEmpty()
||!m_oAnims.loadSpriteFile(m_txtData->GetValue().IsEmpty() ? m_txtTable->GetValue().BeforeLast('.')+L".DAT" : m_txtData->GetValue()) ? m_txtTable->GetValue().BeforeLast('.') +
||!m_oAnims.loadPaletteFile(m_txtPalette->GetValue())) L".DAT"
{ : m_txtData->GetValue()) ||
::wxMessageBox(L"Cannot load files"); !m_oAnims.loadPaletteFile(m_txtPalette->GetValue())) {
return; ::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(); if (iY + iHeight >= 0 && iY < iAvailableHeight) {
for(size_t i = 0; i < m_oAnims.getSpriteCount(); ++i) dc.DrawText(itr->caption, iX, iY);
{ if (itr->bitmap.IsOk())
_sprite_t oSprite; dc.DrawBitmap(itr->bitmap, iX, iY + szLabel.GetHeight() + 1);
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(); 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) void frmSprites::_onBrowseTable(wxCommandEvent& WXUNUSED(evt)) {
{ m_txtTable->SetValue(::wxFileSelector(
wxPaintDC dc(m_panFrame); L"Select location of Font00V.tab (DATA)", m_txtTable->GetValue(),
L"Font00V.tab", L"tab", L"Tab files (*.tab)|*.[tT][aA][bB]", 0, this));
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::_onBrowseData(wxCommandEvent& WXUNUSED(evt)) {
void frmSprites::_onBrowseTable(wxCommandEvent& WXUNUSED(evt)) m_txtData->SetValue(::wxFileSelector(
{ L"Choose Theme Hospital data file", m_txtData->GetValue(), L"", L"dat",
m_txtTable->SetValue(::wxFileSelector(L"Select location of Font00V.tab (DATA)", L"Dat files (*.dat)|*.[dD][aA][tT]", 0, this));
m_txtTable->GetValue(),L"Font00V.tab",L"tab",L"Tab files (*.tab)|*.[tT][aA][bB]"
,0, this));
} }
void frmSprites::_onBrowseData(wxCommandEvent& WXUNUSED(evt)) void frmSprites::_onBrowsePalette(wxCommandEvent& WXUNUSED(evt)) {
{ m_txtPalette->SetValue(::wxFileSelector(
m_txtData->SetValue(::wxFileSelector(L"Choose Theme Hospital data file", L"Select location of MPalette.dat (QDATA)", m_txtPalette->GetValue(),
m_txtData->GetValue(),L"",L"dat",L"Dat files (*.dat)|*.[dD][aA][tT]", 0, this)); L"MPalette.dat", L"dat",
} L"Dat or Pal files (*.dat, *.pal)|*.[dD][aA][tT];*.[pP][aA][lL]", 0,
void frmSprites::_onBrowsePalette(wxCommandEvent& WXUNUSED(evt)) this));
{
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));
} }

View File

@@ -24,69 +24,69 @@ SOFTWARE.
#include "config.h" #include "config.h"
#include <wx/bitmap.h> #include <wx/bitmap.h>
#include <wx/frame.h>
#include <wx/button.h> #include <wx/button.h>
#include <wx/checkbox.h> #include <wx/checkbox.h>
#include <wx/textctrl.h> #include <wx/frame.h>
#include <wx/panel.h>
#include <wx/timer.h>
#include <wx/listbox.h> #include <wx/listbox.h>
#include <wx/panel.h>
#include <wx/textctrl.h>
#include <wx/timer.h>
#include <wx/vscroll.h> #include <wx/vscroll.h>
#include "th.h"
#include <vector> #include <vector>
#include "th.h"
static const int ROW_COUNT = 1000; static const int ROW_COUNT = 1000;
// Derived class to add scrollbars to the window. // Derived class to add scrollbars to the window.
class MyVScrolled : public wxVScrolledWindow { class MyVScrolled : public wxVScrolledWindow {
public: public:
MyVScrolled(wxWindow *parent) : wxVScrolledWindow(parent, wxID_ANY) { iMyCount = ROW_COUNT; } 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 class frmSprites : public wxFrame {
{ public:
public: frmSprites();
frmSprites(); ~frmSprites();
~frmSprites();
enum enum {
{ ID_LOAD = wxID_HIGHEST + 1,
ID_LOAD = wxID_HIGHEST + 1, ID_BROWSE_TABLE,
ID_BROWSE_TABLE, ID_BROWSE_DATA,
ID_BROWSE_DATA, ID_BROWSE_PALETTE,
ID_BROWSE_PALETTE, ID_LOAD_COMPLEX,
ID_LOAD_COMPLEX, ID_NEXT,
ID_NEXT, };
};
void load(bool bComplex); void load(bool bComplex);
protected:
struct _sprite_t
{
wxBitmap bitmap;
wxString caption;
};
void _onNext(wxCommandEvent& evt); protected:
void _onLoad(wxCommandEvent& evt); struct _sprite_t {
void _onLoadComplex(wxCommandEvent& evt); wxBitmap bitmap;
void _onPanelPaint(wxPaintEvent& evt); wxString caption;
void _onBrowseData(wxCommandEvent& evt); };
void _onBrowsePalette(wxCommandEvent& evt);
void _onBrowseTable(wxCommandEvent& evt);
std::vector<_sprite_t> m_vSprites; void _onNext(wxCommandEvent& evt);
THAnimations m_oAnims; 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; std::vector<_sprite_t> m_vSprites;
wxTextCtrl* m_txtData; THAnimations m_oAnims;
wxTextCtrl* m_txtPalette;
MyVScrolled* m_panFrame; wxTextCtrl* m_txtTable;
DECLARE_EVENT_TABLE() wxTextCtrl* m_txtData;
wxTextCtrl* m_txtPalette;
MyVScrolled* m_panFrame;
DECLARE_EVENT_TABLE()
}; };

File diff suppressed because it is too large Load Diff

View File

@@ -36,214 +36,210 @@ SOFTWARE.
#pragma once #pragma once
#include "config.h" #include "config.h"
#include <wx/string.h> #include <stdint.h>
#include <wx/file.h> #include <wx/file.h>
#include <wx/image.h> #include <wx/image.h>
#include <wx/string.h>
#include <wx/txtstrm.h> #include <wx/txtstrm.h>
#include <array> #include <array>
#include <stdint.h>
#include <vector> #include <vector>
#pragma pack(push) #pragma pack(push)
#pragma pack(1) #pragma pack(1)
struct th_anim_t struct th_anim_t {
{ uint16_t frame;
uint16_t frame; uint16_t unknown;
uint16_t unknown;
}; };
struct th_frame_t struct th_frame_t {
{ uint32_t list_index;
uint32_t list_index; uint8_t width;
uint8_t width; uint8_t height;
uint8_t height; uint16_t flags;
uint16_t flags; uint16_t next;
uint16_t next;
}; };
struct th_element_t struct th_element_t {
{ uint16_t table_position;
uint16_t table_position; uint8_t offx;
uint8_t offx; uint8_t offy;
uint8_t offy; uint8_t flags;
uint8_t flags; uint8_t layerid;
uint8_t layerid;
}; };
struct th_sprite_t struct th_sprite_t {
{ uint32_t offset;
uint32_t offset; uint8_t width;
uint8_t width; uint8_t height;
uint8_t height;
}; };
struct th_colour_t struct th_colour_t {
{ uint8_t r;
uint8_t r; uint8_t g;
uint8_t g; uint8_t b;
uint8_t b;
}; };
#pragma pack(pop) #pragma pack(pop)
class THLayerMask class THLayerMask {
{ public:
public: THLayerMask();
THLayerMask();
inline void set(int iLayer, int iID) inline void set(int iLayer, int iID) {
{ if (0 <= iLayer && iLayer < 13 && 0 <= iID && iID < 32)
if(0 <= iLayer && iLayer < 13 && 0 <= iID && iID < 32) m_iMask[iLayer] |= (1 << iID);
m_iMask[iLayer] |= (1 << iID); }
}
void clear(); void clear();
inline void clear(int iLayer, int iID) inline void clear(int iLayer, int iID) {
{ if (0 <= iLayer && iLayer < 13 && 0 <= iID && iID < 32)
if(0 <= iLayer && iLayer < 13 && 0 <= iID && iID < 32) m_iMask[iLayer] &= ~(1 << iID);
m_iMask[iLayer] &= ~(1 << iID); }
}
inline bool isSet(int iLayer, int iID) const inline bool isSet(int iLayer, int iID) const {
{ if (0 <= iLayer && iLayer < 13 && 0 <= iID && iID < 32)
if(0 <= iLayer && iLayer < 13 && 0 <= iID && iID < 32) return (m_iMask[iLayer] & (1 << iID)) != 0;
return (m_iMask[iLayer] & (1 << iID)) != 0; else
else return false;
return false; }
} inline bool isSet(int iLayer) const {
inline bool isSet(int iLayer) const if (0 <= iLayer && iLayer < 13)
{ for (int iId = 0; iId < 32; ++iId) {
if(0 <= iLayer && iLayer < 13) if ((m_iMask[iLayer] & (static_cast<std::uint32_t>(1) << iId)) != 0)
for(int iId = 0; iId < 32; ++iId) return true;
{ }
if((m_iMask[iLayer] & (static_cast<std::uint32_t>(1) << iId)) != 0) return false;
return true; }
}
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 <class T>
bool loadVector(std::vector<T>& 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; return false;
}
} }
protected: for (int offset = 0; offset < iLen; offset += sizeof(T)) {
uint32_t m_iMask[13]; vector.push_back(*(reinterpret_cast<T*>(pBuffer + offset)));
}; }
class Bitmap return true;
{ }
public:
Bitmap(); th_element_t* _getElement(uint32_t iListIndex);
~Bitmap();
std::vector<th_anim_t> anims;
void create(int iWidth, int iHeight); std::vector<th_frame_t> frames;
void create(int iWidth, int iHeight, const uint8_t* pData); std::vector<uint16_t> elementList;
std::vector<th_element_t> elements;
inline uint8_t pixel(int iX, int iY) const {return m_pData[iY * m_iWidth + iX];} std::vector<th_sprite_t> sprites;
inline uint8_t& pixel(int iX, int iY) {return m_pData[iY * m_iWidth + iX];} std::vector<Bitmap> spriteBitmaps;
std::vector<uint8_t> chunks;
int getWidth() const {return m_iWidth;} std::vector<th_colour_t> colours;
int getHeight() const {return m_iHeight;} std::array<unsigned char, 256 * 256 * 4> ghostMaps;
size_t m_iGhostMapOffset;
void blit(Bitmap& bmpCanvas, int iX, int iY, int iFlags = 0) const; wxString m_sSpritePath;
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 <class T>
bool loadVector(std::vector<T>& 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<T*>(pBuffer + offset)));
}
return true;
}
th_element_t* _getElement(uint32_t iListIndex);
std::vector<th_anim_t> anims;
std::vector<th_frame_t> frames;
std::vector<uint16_t> elementList;
std::vector<th_element_t> elements;
std::vector<th_sprite_t> sprites;
std::vector<Bitmap> spriteBitmaps;
std::vector<uint8_t> chunks;
std::vector<th_colour_t> colours;
std::array<unsigned char, 256 * 256 * 4> ghostMaps;
size_t m_iGhostMapOffset;
wxString m_sSpritePath;
}; };

View File

@@ -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 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE. SOFTWARE.
*/ */
#include "config.h"
#include <array>
#include <cstring>
#include "lua.hpp" #include "lua.hpp"
#include "th_lua.h" #include "th_lua.h"
#include "config.h"
#include <cstring>
#include <array>
/* Often, an error occurs during the CorsixTH startup process. Examples of /* Often, an error occurs during the CorsixTH startup process. Examples of
such errors include: such errors include:
@@ -42,281 +42,281 @@ homemade bitmap font, as we cannot rely on TH fonts being present).
namespace { namespace {
// clang-format off
constexpr int first_bootstrap_code_line_number = __LINE__ + 2; constexpr int first_bootstrap_code_line_number = __LINE__ + 2;
constexpr std::array<const char*, 45> bootstrap_code {{ constexpr std::array<const char*, 45> bootstrap_code{
"local lines, dat, tab, pal, err = {}, ...", {"local lines, dat, tab, pal, err = {}, ...",
"local function t(s) return s:gsub('\\t', ' ') end", "local function t(s) return s:gsub('\\t', ' ') end",
"for s in tostring(err):gmatch'[^\\r\\n]+' do lines[#lines+1] = t(s) 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", "local TH, SDL, rnc = require'TH', require'sdl', require'rnc'.decompress",
"if not SDL.init('video') then error'Unable to initialise video' end", "if not SDL.init('video') then error'Unable to initialise video' end",
"local w, h = 640, 480", "local w, h = 640, 480",
"local function dernc(x) return x:match'^RNC' and assert(rnc(x)) or x end", "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)", "local video = TheApp and TheApp.video or TH.surface(w, h)",
"video:setCaption('CorsixTH - Error during startup')", "video:setCaption('CorsixTH - Error during startup')",
"local palette, sheet, font = TH.palette(), TH.sheet(), TH.bitmap_font()", "local palette, sheet, font = TH.palette(), TH.sheet(), TH.bitmap_font()",
"if not palette:load(dernc(pal)) then error'Unable to load palette' end", "if not palette:load(dernc(pal)) then error'Unable to load palette' end",
"sheet:setPalette(palette)", "sheet:setPalette(palette)",
"if not sheet:load(dernc(tab), dernc(dat), true, video) then error'Unable to load sheet' end", "if not sheet:load(dernc(tab), dernc(dat), true, video) then error'Unable to load sheet' end",
"font:setSheet(sheet):setSeparation(1, 0)", "font:setSheet(sheet):setSeparation(1, 0)",
"local bx, by, bw, bh = 20, 0, 100, 16", // Print message and draw button: "local bx, by, bw, bh = 20, 0, 100, 16", // Print message and draw button:
"local function draw()", "local function draw()",
" video:startFrame()", " video:startFrame()",
" video:fillBlack()", " video:fillBlack()",
" local y = 20", " local y = 20",
" for _, s in ipairs(lines) do", " for _, s in ipairs(lines) do",
" y = font:drawWrapped(video, s, 20, y, w - 40)", " y = font:drawWrapped(video, s, 20, y, w - 40)",
" end", " end",
" by = y + 20", " by = y + 20",
" video:drawRect(video:mapRGB(151, 23, 23), bx, by, bw, bh)", " 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)", " video:drawRect(video:mapRGB(171, 53, 53), bx + 1, by + 1, bw - 2, bh - 2)",
" font:draw(video, 'Exit', bx, by + 1, bw, bh)", " font:draw(video, 'Exit', bx, by + 1, bw, bh)",
" video:endFrame()", " video:endFrame()",
"end", "end",
"SDL.wm.showCursor(true)", "SDL.wm.showCursor(true)",
"draw()", "draw()",
"local running = true", // Minimal event handler: "local running = true", // Minimal event handler:
"repeat", "repeat",
" local e, where = SDL.mainloop(coroutine.create(function()", " local e, where = SDL.mainloop(coroutine.create(function()",
" while running do", " while running do",
" local e, _, x, y = coroutine.yield(true)", " local e, _, x, y = coroutine.yield(true)",
" if e == 'frame' then draw()", " if e == 'frame' then draw()",
" elseif e == 'buttonup' then", " elseif e == 'buttonup' then",
" x, y = x - bx, y - by", " x, y = x - bx, y - by",
" running = x < 0 or bw <= x or y < 0 or bh <= y", " running = x < 0 or bw <= x or y < 0 or bh <= y",
" end", " end",
" end", " end",
" end))", " end))",
" if running then print(e) end", " if running then print(e) end",
"until where ~= 'callback'", "until where ~= 'callback'",
nullptr nullptr}};
}}; // clang-format on
/* Start autogenerated content */ /* Start autogenerated content */
/* Data from bootstrap_font.tab inserted by mkbootstrap.lua: */ /* Data from bootstrap_font.tab inserted by mkbootstrap.lua: */
constexpr std::array<uint8_t, 499> bootstrap_font_tab { constexpr std::array<uint8_t, 499> bootstrap_font_tab{
0x52, 0x4E, 0x43, 0x01, 0x00, 0x00, 0x05, 0x46, 0x00, 0x00, 0x01, 0xE1, 0xFB, 0x52, 0x4E, 0x43, 0x01, 0x00, 0x00, 0x05, 0x46, 0x00, 0x00, 0x01, 0xE1,
0xF2, 0x66, 0x51, 0xBE, 0xEF, 0x0C, 0x09, 0x59, 0x60, 0x10, 0x34, 0x43, 0x54, 0xFB, 0xF2, 0x66, 0x51, 0xBE, 0xEF, 0x0C, 0x09, 0x59, 0x60, 0x10, 0x34,
0xA6, 0x46, 0xA2, 0x08, 0x00, 0xA0, 0xC4, 0x01, 0xF6, 0x3D, 0x00, 0x00, 0xAF, 0x43, 0x54, 0xA6, 0x46, 0xA2, 0x08, 0x00, 0xA0, 0xC4, 0x01, 0xF6, 0x3D,
0xAE, 0x05, 0x01, 0x03, 0x01, 0x0C, 0x0B, 0xAE, 0x9E, 0x03, 0x0C, 0x16, 0x08, 0x00, 0x00, 0xAF, 0xAE, 0x05, 0x01, 0x03, 0x01, 0x0C, 0x0B, 0xAE, 0x9E,
0x0C, 0x47, 0x63, 0xEA, 0x0C, 0x74, 0x07, 0x0C, 0x9A, 0xEA, 0xEA, 0x06, 0x0C, 0x03, 0x0C, 0x16, 0x08, 0x0C, 0x47, 0x63, 0xEA, 0x0C, 0x74, 0x07, 0x0C,
0xC8, 0x02, 0x0C, 0xD1, 0x1D, 0x92, 0xEE, 0x4B, 0x43, 0x09, 0x01, 0x3B, 0xD5, 0x9A, 0xEA, 0xEA, 0x06, 0x0C, 0xC8, 0x02, 0x0C, 0xD1, 0x1D, 0x92, 0xEE,
0x34, 0x4F, 0xAA, 0xF4, 0xCC, 0xA4, 0x56, 0x60, 0xDA, 0x66, 0x54, 0x1D, 0x66, 0x4B, 0x43, 0x09, 0x01, 0x3B, 0xD5, 0x34, 0x4F, 0xAA, 0xF4, 0xCC, 0xA4,
0xA3, 0x3E, 0x7C, 0x04, 0x0C, 0x9B, 0xD3, 0x4C, 0xB5, 0xD5, 0x52, 0x93, 0xF2, 0x56, 0x60, 0xDA, 0x66, 0x54, 0x1D, 0x66, 0xA3, 0x3E, 0x7C, 0x04, 0x0C,
0x13, 0x24, 0x12, 0x02, 0x31, 0x52, 0x49, 0x92, 0x71, 0x97, 0x54, 0x7B, 0xB9, 0x9B, 0xD3, 0x4C, 0xB5, 0xD5, 0x52, 0x93, 0xF2, 0x13, 0x24, 0x12, 0x02,
0x8C, 0x6A, 0xC3, 0xB3, 0x51, 0xCD, 0xB5, 0x4E, 0xE5, 0x4E, 0x33, 0xF6, 0x1D, 0x31, 0x52, 0x49, 0x92, 0x71, 0x97, 0x54, 0x7B, 0xB9, 0x8C, 0x6A, 0xC3,
0xAD, 0x0F, 0x34, 0xAA, 0x29, 0xDB, 0x8C, 0x4E, 0xAA, 0xBB, 0x12, 0x9A, 0x91, 0xB3, 0x51, 0xCD, 0xB5, 0x4E, 0xE5, 0x4E, 0x33, 0xF6, 0x1D, 0xAD, 0x0F,
0x49, 0x4A, 0xB1, 0xD7, 0x48, 0x2E, 0xF5, 0x10, 0x04, 0x01, 0x49, 0x34, 0x58, 0x34, 0xAA, 0x29, 0xDB, 0x8C, 0x4E, 0xAA, 0xBB, 0x12, 0x9A, 0x91, 0x49,
0x92, 0x54, 0x76, 0x97, 0xE9, 0x91, 0xB9, 0x12, 0x4E, 0xDB, 0x03, 0x05, 0x50, 0x4A, 0xB1, 0xD7, 0x48, 0x2E, 0xF5, 0x10, 0x04, 0x01, 0x49, 0x34, 0x58,
0x6D, 0x2B, 0x36, 0xAA, 0x50, 0xDC, 0xA8, 0x6E, 0x2E, 0x3D, 0x9C, 0x33, 0xA5, 0x92, 0x54, 0x76, 0x97, 0xE9, 0x91, 0xB9, 0x12, 0x4E, 0xDB, 0x03, 0x05,
0xBD, 0x25, 0xC9, 0xDD, 0xF8, 0x09, 0x92, 0x1F, 0x06, 0x45, 0x6D, 0xAA, 0x69, 0x50, 0x6D, 0x2B, 0x36, 0xAA, 0x50, 0xDC, 0xA8, 0x6E, 0x2E, 0x3D, 0x9C,
0x66, 0x92, 0x97, 0xB7, 0x54, 0xB7, 0xDB, 0x1F, 0x39, 0xF8, 0x12, 0x07, 0x41, 0x33, 0xA5, 0xBD, 0x25, 0xC9, 0xDD, 0xF8, 0x09, 0x92, 0x1F, 0x06, 0x45,
0x95, 0x2F, 0x1B, 0x49, 0x41, 0x4B, 0x75, 0xDD, 0x99, 0x49, 0x55, 0x70, 0x75, 0x6D, 0xAA, 0x69, 0x66, 0x92, 0x97, 0xB7, 0x54, 0xB7, 0xDB, 0x1F, 0x39,
0xBF, 0x92, 0x24, 0xA7, 0xC4, 0x49, 0x92, 0xD9, 0xF5, 0x6B, 0x82, 0x14, 0x08, 0xF8, 0x12, 0x07, 0x41, 0x95, 0x2F, 0x1B, 0x49, 0x41, 0x4B, 0x75, 0xDD,
0x38, 0xEA, 0x65, 0x8C, 0x6A, 0x43, 0x9B, 0x51, 0x5D, 0xE5, 0x46, 0x7F, 0xB5, 0x99, 0x49, 0x55, 0x70, 0x75, 0xBF, 0x92, 0x24, 0xA7, 0xC4, 0x49, 0x92,
0xC7, 0xA4, 0x95, 0x91, 0xAE, 0xD0, 0x4C, 0xC6, 0xE2, 0x33, 0xC9, 0xFF, 0x09, 0xD9, 0xF5, 0x6B, 0x82, 0x14, 0x08, 0x38, 0xEA, 0x65, 0x8C, 0x6A, 0x43,
0x92, 0x1F, 0x09, 0x34, 0x47, 0x24, 0xA9, 0x65, 0x7D, 0x9A, 0x46, 0x99, 0x92, 0x9B, 0x51, 0x5D, 0xE5, 0x46, 0x7F, 0xB5, 0xC7, 0xA4, 0x95, 0x91, 0xAE,
0x72, 0xB6, 0xD4, 0x9A, 0xB9, 0xF4, 0x10, 0x0A, 0x19, 0x54, 0x32, 0x1B, 0xCF, 0xD0, 0x4C, 0xC6, 0xE2, 0x33, 0xC9, 0xFF, 0x09, 0x92, 0x1F, 0x09, 0x34,
0x3E, 0x4C, 0xAA, 0x5E, 0x97, 0x31, 0x6D, 0xAA, 0xF4, 0x48, 0x39, 0x87, 0xB0, 0x47, 0x24, 0xA9, 0x65, 0x7D, 0x9A, 0x46, 0x99, 0x92, 0x72, 0xB6, 0xD4,
0x49, 0x09, 0xCF, 0xE9, 0xFB, 0x99, 0x90, 0x46, 0x2C, 0x92, 0x54, 0x4B, 0x6D, 0x9A, 0xB9, 0xF4, 0x10, 0x0A, 0x19, 0x54, 0x32, 0x1B, 0xCF, 0x3E, 0x4C,
0xE9, 0x91, 0x8A, 0x24, 0x49, 0xA6, 0xC0, 0xD9, 0x75, 0xC5, 0x91, 0xBD, 0xF0, 0xAA, 0x5E, 0x97, 0x31, 0x6D, 0xAA, 0xF4, 0x48, 0x39, 0x87, 0xB0, 0x49,
0x9C, 0x75, 0x45, 0xA8, 0x19, 0xFA, 0x46, 0x3A, 0x52, 0x75, 0x5A, 0x8C, 0x2A, 0x09, 0xCF, 0xE9, 0xFB, 0x99, 0x90, 0x46, 0x2C, 0x92, 0x54, 0x4B, 0x6D,
0x75, 0x3B, 0xAA, 0x95, 0xBD, 0x27, 0xBF, 0x27, 0x39, 0xE3, 0x04, 0x0D, 0x41, 0xE9, 0x91, 0x8A, 0x24, 0x49, 0xA6, 0xC0, 0xD9, 0x75, 0xC5, 0x91, 0xBD,
0xD5, 0x24, 0x31, 0x92, 0x42, 0x5E, 0xA4, 0x6A, 0x85, 0x9A, 0xB4, 0xAA, 0x12, 0xF0, 0x9C, 0x75, 0x45, 0xA8, 0x19, 0xFA, 0x46, 0x3A, 0x52, 0x75, 0x5A,
0x9A, 0xD0, 0xF3, 0x39, 0x41, 0x15, 0x0E, 0x37, 0x92, 0x24, 0x5F, 0x84, 0xA5, 0x8C, 0x2A, 0x75, 0x3B, 0xAA, 0x95, 0xBD, 0x27, 0xBF, 0x27, 0x39, 0xE3,
0xD5, 0xDE, 0x33, 0x93, 0xB6, 0xD9, 0xAA, 0x6F, 0xE4, 0x2E, 0xF6, 0x14, 0x0F, 0x04, 0x0D, 0x41, 0xD5, 0x24, 0x31, 0x92, 0x42, 0x5E, 0xA4, 0x6A, 0x85,
0x61, 0x92, 0x52, 0xA4, 0xCA, 0x6C, 0xCD, 0x4C, 0x86, 0x94, 0x92, 0xEA, 0xA5, 0x9A, 0xB4, 0xAA, 0x12, 0x9A, 0xD0, 0xF3, 0x39, 0x41, 0x15, 0x0E, 0x37,
0x7F, 0x39, 0xD9, 0x72, 0xAF, 0x11, 0x10, 0x04, 0xAA, 0x1C, 0xED, 0x47, 0x3D, 0x92, 0x24, 0x5F, 0x84, 0xA5, 0xD5, 0xDE, 0x33, 0x93, 0xB6, 0xD9, 0xAA,
0xF6, 0x97, 0x3D, 0xF4, 0x7D, 0x0A, 0x5E, 0xAF, 0x44, 0x80, 0xAA, 0x57, 0x35, 0x6F, 0xE4, 0x2E, 0xF6, 0x14, 0x0F, 0x61, 0x92, 0x52, 0xA4, 0xCA, 0x6C,
0xAA, 0xA1, 0xF7, 0x34, 0xBA, 0x92, 0xA4, 0xD6, 0xF7, 0x7A, 0xF7, 0x5B, 0x1A, 0xCD, 0x4C, 0x86, 0x94, 0x92, 0xEA, 0xA5, 0x7F, 0x39, 0xD9, 0x72, 0xAF,
0x18, 0x11, 0x35, 0xAA, 0xC4, 0x48, 0xAA, 0x4C, 0x6E, 0xD6, 0x91, 0x93, 0x12, 0x11, 0x10, 0x04, 0xAA, 0x1C, 0xED, 0x47, 0x3D, 0xF6, 0x97, 0x3D, 0xF4,
0x52, 0xBA, 0xE0, 0x35, 0x8D, 0xFF, 0x5C, 0x1F, 0x27, 0x12, 0x54, 0xE9, 0x44, 0x7D, 0x0A, 0x5E, 0xAF, 0x44, 0x80, 0xAA, 0x57, 0x35, 0xAA, 0xA1, 0xF7,
0x51, 0xA5, 0x67, 0x47, 0x92, 0x7F, 0x99, 0x64, 0x37, 0xBA, 0x38, 0x49, 0xE2, 0x34, 0xBA, 0x92, 0xA4, 0xD6, 0xF7, 0x7A, 0xF7, 0x5B, 0x1A, 0x18, 0x11,
0xA8, 0xCF, 0xF2, 0x75, 0x53, 0x0D, 0x13, 0xFA, 0x24, 0x1E, 0x51, 0xB9, 0xC9, 0x35, 0xAA, 0xC4, 0x48, 0xAA, 0x4C, 0x6E, 0xD6, 0x91, 0x93, 0x12, 0x52,
0x29, 0x40, 0x52, 0x7D, 0x0F 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: */ /* Data from bootstrap_font.dat inserted by mkbootstrap.lua: */
constexpr std::array<uint8_t, 1534> bootstrap_font_dat { constexpr std::array<uint8_t, 1534> bootstrap_font_dat{
0x52, 0x4E, 0x43, 0x01, 0x00, 0x00, 0x13, 0x68, 0x00, 0x00, 0x05, 0xEC, 0xD3, 0x52, 0x4E, 0x43, 0x01, 0x00, 0x00, 0x13, 0x68, 0x00, 0x00, 0x05, 0xEC,
0x6C, 0x7E, 0xAB, 0xBE, 0xEF, 0x94, 0x90, 0x81, 0x61, 0x50, 0x34, 0x44, 0x33, 0xD3, 0x6C, 0x7E, 0xAB, 0xBE, 0xEF, 0x94, 0x90, 0x81, 0x61, 0x50, 0x34,
0x33, 0x53, 0x66, 0x64, 0x64, 0x26, 0x20, 0x60, 0x47, 0x1E, 0x01, 0xFF, 0x84, 0x44, 0x33, 0x33, 0x53, 0x66, 0x64, 0x64, 0x26, 0x20, 0x60, 0x47, 0x1E,
0x41, 0x01, 0x02, 0xFF, 0x01, 0x84, 0x09, 0xFF, 0xFF, 0x14, 0x2F, 0x06, 0xCD, 0x01, 0xFF, 0x84, 0x41, 0x01, 0x02, 0xFF, 0x01, 0x84, 0x09, 0xFF, 0xFF,
0x9B, 0xCC, 0x8C, 0x89, 0x04, 0x1B, 0x42, 0x06, 0x65, 0x64, 0x71, 0x3B, 0x44, 0x14, 0x2F, 0x06, 0xCD, 0x9B, 0xCC, 0x8C, 0x89, 0x04, 0x1B, 0x42, 0x06,
0x01, 0x06, 0x78, 0xE0, 0xE9, 0xD9, 0x9A, 0x2B, 0x36, 0x72, 0xB0, 0x43, 0x84, 0x65, 0x64, 0x71, 0x3B, 0x44, 0x01, 0x06, 0x78, 0xE0, 0xE9, 0xD9, 0x9A,
0x59, 0x1B, 0x4D, 0x0A, 0xB6, 0x75, 0x87, 0x91, 0x32, 0xAB, 0x86, 0x02, 0xCC, 0x2B, 0x36, 0x72, 0xB0, 0x43, 0x84, 0x59, 0x1B, 0x4D, 0x0A, 0xB6, 0x75,
0xDE, 0x84, 0x07, 0x82, 0x31, 0x85, 0xCC, 0x45, 0x90, 0x4A, 0x84, 0xE7, 0xC4, 0x87, 0x91, 0x32, 0xAB, 0x86, 0x02, 0xCC, 0xDE, 0x84, 0x07, 0x82, 0x31,
0x9C, 0x42, 0xFC, 0x29, 0x66, 0x0C, 0xA4, 0x94, 0x9D, 0xD9, 0x38, 0x75, 0x35, 0x85, 0xCC, 0x45, 0x90, 0x4A, 0x84, 0xE7, 0xC4, 0x9C, 0x42, 0xFC, 0x29,
0x42, 0x98, 0x07, 0x6D, 0x70, 0x91, 0x1B, 0x03, 0x8E, 0x54, 0x40, 0x9B, 0xED, 0x66, 0x0C, 0xA4, 0x94, 0x9D, 0xD9, 0x38, 0x75, 0x35, 0x42, 0x98, 0x07,
0x89, 0x19, 0x44, 0x87, 0x02, 0x9D, 0x8B, 0x5C, 0x3A, 0x15, 0x19, 0xF8, 0xB7, 0x6D, 0x70, 0x91, 0x1B, 0x03, 0x8E, 0x54, 0x40, 0x9B, 0xED, 0x89, 0x19,
0xAC, 0x43, 0x01, 0x15, 0x93, 0x0B, 0x5D, 0x03, 0x83, 0x40, 0x1A, 0x56, 0xA5, 0x44, 0x87, 0x02, 0x9D, 0x8B, 0x5C, 0x3A, 0x15, 0x19, 0xF8, 0xB7, 0xAC,
0x1D, 0x03, 0xF2, 0x93, 0xA2, 0x50, 0x03, 0xB6, 0x11, 0x1C, 0xF2, 0x8E, 0x8E, 0x43, 0x01, 0x15, 0x93, 0x0B, 0x5D, 0x03, 0x83, 0x40, 0x1A, 0x56, 0xA5,
0x60, 0x87, 0x42, 0x89, 0x93, 0x40, 0x93, 0x64, 0xA2, 0x81, 0x04, 0x29, 0x23, 0x1D, 0x03, 0xF2, 0x93, 0xA2, 0x50, 0x03, 0xB6, 0x11, 0x1C, 0xF2, 0x8E,
0x11, 0x4F, 0x36, 0xA0, 0xBA, 0x8E, 0xA2, 0xA4, 0x1A, 0x08, 0x75, 0x65, 0x6E, 0x8E, 0x60, 0x87, 0x42, 0x89, 0x93, 0x40, 0x93, 0x64, 0xA2, 0x81, 0x04,
0x9C, 0xAC, 0x14, 0xC3, 0x18, 0x67, 0x33, 0x16, 0x4E, 0x10, 0x31, 0x8C, 0x11, 0x29, 0x23, 0x11, 0x4F, 0x36, 0xA0, 0xBA, 0x8E, 0xA2, 0xA4, 0x1A, 0x08,
0x9B, 0x17, 0x7C, 0xCC, 0x44, 0x57, 0x8F, 0xF7, 0x74, 0x0E, 0x3F, 0x71, 0x80, 0x75, 0x65, 0x6E, 0x9C, 0xAC, 0x14, 0xC3, 0x18, 0x67, 0x33, 0x16, 0x4E,
0x64, 0x08, 0x39, 0x90, 0xBC, 0x28, 0x87, 0x3A, 0x15, 0x8B, 0x70, 0x30, 0x9D, 0x10, 0x31, 0x8C, 0x11, 0x9B, 0x17, 0x7C, 0xCC, 0x44, 0x57, 0x8F, 0xF7,
0x04, 0x14, 0x0B, 0x89, 0x1C, 0x95, 0x21, 0x8F, 0x50, 0xB5, 0x2D, 0xD9, 0x85, 0x74, 0x0E, 0x3F, 0x71, 0x80, 0x64, 0x08, 0x39, 0x90, 0xBC, 0x28, 0x87,
0x1B, 0x80, 0x72, 0xEC, 0x0A, 0xCE, 0x6B, 0xA8, 0xDB, 0x86, 0x05, 0x35, 0x93, 0x3A, 0x15, 0x8B, 0x70, 0x30, 0x9D, 0x04, 0x14, 0x0B, 0x89, 0x1C, 0x95,
0x84, 0x12, 0x8B, 0xE6, 0xEC, 0x84, 0xBF, 0x79, 0x5F, 0xDA, 0xA4, 0xC8, 0x97, 0x21, 0x8F, 0x50, 0xB5, 0x2D, 0xD9, 0x85, 0x1B, 0x80, 0x72, 0xEC, 0x0A,
0xF8, 0x84, 0x42, 0x9B, 0x19, 0xB4, 0x96, 0xCB, 0xD4, 0x85, 0x21, 0x84, 0x08, 0xCE, 0x6B, 0xA8, 0xDB, 0x86, 0x05, 0x35, 0x93, 0x84, 0x12, 0x8B, 0xE6,
0xB3, 0x5B, 0x1F, 0x88, 0x60, 0xCE, 0x85, 0x0F, 0x56, 0x92, 0x92, 0xA4, 0x05, 0xEC, 0x84, 0xBF, 0x79, 0x5F, 0xDA, 0xA4, 0xC8, 0x97, 0xF8, 0x84, 0x42,
0xA6, 0x43, 0x46, 0x9E, 0x94, 0x08, 0x8B, 0x58, 0x17, 0x3B, 0x87, 0xC9, 0xAA, 0x9B, 0x19, 0xB4, 0x96, 0xCB, 0xD4, 0x85, 0x21, 0x84, 0x08, 0xB3, 0x5B,
0x86, 0x48, 0x83, 0x23, 0x12, 0x83, 0x52, 0x90, 0x89, 0x38, 0x4A, 0x50, 0x89, 0x1F, 0x88, 0x60, 0xCE, 0x85, 0x0F, 0x56, 0x92, 0x92, 0xA4, 0x05, 0xA6,
0x52, 0x32, 0x98, 0x45, 0xB9, 0x84, 0x48, 0x6D, 0x98, 0x17, 0x9E, 0xA2, 0x37, 0x43, 0x46, 0x9E, 0x94, 0x08, 0x8B, 0x58, 0x17, 0x3B, 0x87, 0xC9, 0xAA,
0x66, 0xC2, 0xA2, 0xB4, 0x5A, 0x2C, 0x54, 0xE5, 0xC5, 0x15, 0x74, 0x44, 0x67, 0x86, 0x48, 0x83, 0x23, 0x12, 0x83, 0x52, 0x90, 0x89, 0x38, 0x4A, 0x50,
0x06, 0x50, 0x6E, 0xE1, 0x06, 0x2F, 0x2D, 0xC8, 0x99, 0x0D, 0xB6, 0x4D, 0x79, 0x89, 0x52, 0x32, 0x98, 0x45, 0xB9, 0x84, 0x48, 0x6D, 0x98, 0x17, 0x9E,
0x24, 0x0E, 0x07, 0x1D, 0xE8, 0x91, 0x94, 0x0F, 0xDE, 0x62, 0x3F, 0x60, 0x0F, 0xA2, 0x37, 0x66, 0xC2, 0xA2, 0xB4, 0x5A, 0x2C, 0x54, 0xE5, 0xC5, 0x15,
0x27, 0x63, 0xE5, 0x40, 0x78, 0x85, 0xB8, 0x15, 0x09, 0xCA, 0x22, 0x2B, 0x19, 0x74, 0x44, 0x67, 0x06, 0x50, 0x6E, 0xE1, 0x06, 0x2F, 0x2D, 0xC8, 0x99,
0x09, 0x53, 0xF9, 0x84, 0x69, 0x6D, 0x32, 0x6B, 0xAE, 0x45, 0xCA, 0x90, 0xE6, 0x0D, 0xB6, 0x4D, 0x79, 0x24, 0x0E, 0x07, 0x1D, 0xE8, 0x91, 0x94, 0x0F,
0x12, 0x6A, 0xB4, 0xA2, 0xD8, 0x4C, 0xFA, 0xA1, 0x5D, 0xEF, 0xC2, 0xE4, 0x02, 0xDE, 0x62, 0x3F, 0x60, 0x0F, 0x27, 0x63, 0xE5, 0x40, 0x78, 0x85, 0xB8,
0x00, 0xF9, 0x5B, 0x05, 0x13, 0x6B, 0xC2, 0xB9, 0xAA, 0x17, 0xD8, 0xBA, 0xB1, 0x15, 0x09, 0xCA, 0x22, 0x2B, 0x19, 0x09, 0x53, 0xF9, 0x84, 0x69, 0x6D,
0xF9, 0xA0, 0x05, 0x4C, 0x2F, 0x9A, 0x61, 0xBA, 0x52, 0x94, 0x63, 0xB9, 0x16, 0x32, 0x6B, 0xAE, 0x45, 0xCA, 0x90, 0xE6, 0x12, 0x6A, 0xB4, 0xA2, 0xD8,
0x9D, 0xA3, 0x65, 0x84, 0x3D, 0x4A, 0x64, 0x9C, 0x96, 0x20, 0xF6, 0x30, 0x85, 0x4C, 0xFA, 0xA1, 0x5D, 0xEF, 0xC2, 0xE4, 0x02, 0x00, 0xF9, 0x5B, 0x05,
0x68, 0x5A, 0x50, 0x95, 0xE5, 0x30, 0xFD, 0x90, 0x1C, 0xE2, 0xE6, 0x43, 0x0B, 0x13, 0x6B, 0xC2, 0xB9, 0xAA, 0x17, 0xD8, 0xBA, 0xB1, 0xF9, 0xA0, 0x05,
0x6A, 0x9F, 0x23, 0x12, 0x27, 0xC2, 0x81, 0x9D, 0x55, 0x85, 0xC7, 0x01, 0xA5, 0x4C, 0x2F, 0x9A, 0x61, 0xBA, 0x52, 0x94, 0x63, 0xB9, 0x16, 0x9D, 0xA3,
0x58, 0x01, 0xC7, 0xD3, 0xF3, 0xFD, 0x87, 0x0B, 0x58, 0x7C, 0x52, 0xCA, 0x6C, 0x65, 0x84, 0x3D, 0x4A, 0x64, 0x9C, 0x96, 0x20, 0xF6, 0x30, 0x85, 0x68,
0x47, 0xF1, 0x39, 0x19, 0x8A, 0x9A, 0xF7, 0x21, 0x0A, 0x67, 0x06, 0xBE, 0xD3, 0x5A, 0x50, 0x95, 0xE5, 0x30, 0xFD, 0x90, 0x1C, 0xE2, 0xE6, 0x43, 0x0B,
0x88, 0xCF, 0x47, 0x34, 0x94, 0x09, 0x04, 0x40, 0x64, 0x09, 0xE6, 0x4F, 0xEA, 0x6A, 0x9F, 0x23, 0x12, 0x27, 0xC2, 0x81, 0x9D, 0x55, 0x85, 0xC7, 0x01,
0xA5, 0x55, 0x4E, 0xA5, 0x95, 0xE5, 0x50, 0x0D, 0x06, 0x46, 0x61, 0x85, 0x23, 0xA5, 0x58, 0x01, 0xC7, 0xD3, 0xF3, 0xFD, 0x87, 0x0B, 0x58, 0x7C, 0x52,
0x08, 0xAB, 0xF9, 0x48, 0x09, 0x33, 0x44, 0x50, 0x07, 0x21, 0x3C, 0xA8, 0x61, 0xCA, 0x6C, 0x47, 0xF1, 0x39, 0x19, 0x8A, 0x9A, 0xF7, 0x21, 0x0A, 0x67,
0x9D, 0x34, 0x84, 0x22, 0x5C, 0x44, 0xFD, 0xA0, 0x6D, 0xE2, 0x6E, 0x56, 0x3A, 0x06, 0xBE, 0xD3, 0x88, 0xCF, 0x47, 0x34, 0x94, 0x09, 0x04, 0x40, 0x64,
0xA3, 0x19, 0x62, 0x4A, 0x82, 0x0C, 0x98, 0x25, 0xCF, 0x24, 0xC8, 0x35, 0x35, 0x09, 0xE6, 0x4F, 0xEA, 0xA5, 0x55, 0x4E, 0xA5, 0x95, 0xE5, 0x50, 0x0D,
0x1A, 0x12, 0x59, 0x5D, 0x98, 0xD6, 0x2E, 0x76, 0x7F, 0x2B, 0xF4, 0x30, 0x24, 0x06, 0x46, 0x61, 0x85, 0x23, 0x08, 0xAB, 0xF9, 0x48, 0x09, 0x33, 0x44,
0xAF, 0x4C, 0x5C, 0x28, 0x2E, 0x28, 0xAA, 0x24, 0xE5, 0x17, 0xFF, 0x55, 0x42, 0x50, 0x07, 0x21, 0x3C, 0xA8, 0x61, 0x9D, 0x34, 0x84, 0x22, 0x5C, 0x44,
0xC9, 0x54, 0x1B, 0x75, 0xDD, 0xBE, 0x47, 0x9B, 0x11, 0x22, 0x0E, 0xCC, 0xAA, 0xFD, 0xA0, 0x6D, 0xE2, 0x6E, 0x56, 0x3A, 0xA3, 0x19, 0x62, 0x4A, 0x82,
0x88, 0x88, 0x66, 0x64, 0xCA, 0xD0, 0x88, 0x08, 0x41, 0x06, 0xF0, 0x17, 0x45, 0x0C, 0x98, 0x25, 0xCF, 0x24, 0xC8, 0x35, 0x35, 0x1A, 0x12, 0x59, 0x5D,
0x82, 0x07, 0xAE, 0x80, 0x9F, 0xB2, 0x23, 0x23, 0xAB, 0x8C, 0xE9, 0xFA, 0xC3, 0x98, 0xD6, 0x2E, 0x76, 0x7F, 0x2B, 0xF4, 0x30, 0x24, 0xAF, 0x4C, 0x5C,
0x53, 0xD0, 0x06, 0x88, 0x67, 0x59, 0xB3, 0x92, 0xD2, 0xE9, 0xA8, 0x82, 0x0A, 0x28, 0x2E, 0x28, 0xAA, 0x24, 0xE5, 0x17, 0xFF, 0x55, 0x42, 0xC9, 0x54,
0xA7, 0xC9, 0x44, 0x8E, 0x47, 0x15, 0x82, 0x37, 0x80, 0x9B, 0x88, 0xF9, 0x08, 0x1B, 0x75, 0xDD, 0xBE, 0x47, 0x9B, 0x11, 0x22, 0x0E, 0xCC, 0xAA, 0x88,
0x27, 0xDC, 0x05, 0xC2, 0xD3, 0x94, 0x59, 0x55, 0x41, 0x7F, 0x91, 0xFF, 0xDB, 0x88, 0x66, 0x64, 0xCA, 0xD0, 0x88, 0x08, 0x41, 0x06, 0xF0, 0x17, 0x45,
0x16, 0x8C, 0x0C, 0x27, 0x76, 0x6E, 0x26, 0x81, 0xAA, 0xA1, 0x8F, 0xC1, 0x20, 0x82, 0x07, 0xAE, 0x80, 0x9F, 0xB2, 0x23, 0x23, 0xAB, 0x8C, 0xE9, 0xFA,
0x63, 0x0B, 0xC3, 0x89, 0x49, 0x31, 0x42, 0x93, 0x8C, 0x69, 0x07, 0x7B, 0x3F, 0xC3, 0x53, 0xD0, 0x06, 0x88, 0x67, 0x59, 0xB3, 0x92, 0xD2, 0xE9, 0xA8,
0x9E, 0x0E, 0x45, 0x56, 0x85, 0x91, 0xE9, 0x78, 0xCC, 0xF9, 0x08, 0xA4, 0xD5, 0x82, 0x0A, 0xA7, 0xC9, 0x44, 0x8E, 0x47, 0x15, 0x82, 0x37, 0x80, 0x9B,
0x92, 0x70, 0x89, 0x16, 0xC3, 0x0C, 0x92, 0x18, 0xA7, 0x59, 0xF2, 0x08, 0xCD, 0x88, 0xF9, 0x08, 0x27, 0xDC, 0x05, 0xC2, 0xD3, 0x94, 0x59, 0x55, 0x41,
0xE0, 0xF2, 0xA8, 0x97, 0x4C, 0xF3, 0x83, 0xA7, 0x1C, 0x68, 0x54, 0x17, 0x03, 0x7F, 0x91, 0xFF, 0xDB, 0x16, 0x8C, 0x0C, 0x27, 0x76, 0x6E, 0x26, 0x81,
0x8A, 0x20, 0x57, 0x50, 0x58, 0x31, 0x11, 0x72, 0x87, 0x71, 0x25, 0x2E, 0xE7, 0xAA, 0xA1, 0x8F, 0xC1, 0x20, 0x63, 0x0B, 0xC3, 0x89, 0x49, 0x31, 0x42,
0xE4, 0x79, 0x90, 0x10, 0x50, 0x1D, 0x54, 0x88, 0xE1, 0xB7, 0x8F, 0x18, 0x02, 0x93, 0x8C, 0x69, 0x07, 0x7B, 0x3F, 0x9E, 0x0E, 0x45, 0x56, 0x85, 0x91,
0x77, 0xA8, 0x0C, 0x64, 0x11, 0x8C, 0xC9, 0x21, 0xE6, 0x07, 0xCB, 0x5E, 0x8F, 0xE9, 0x78, 0xCC, 0xF9, 0x08, 0xA4, 0xD5, 0x92, 0x70, 0x89, 0x16, 0xC3,
0xB2, 0x25, 0x0B, 0x7B, 0x0A, 0x25, 0x8B, 0x95, 0xD0, 0x82, 0xF4, 0xE2, 0x34, 0x0C, 0x92, 0x18, 0xA7, 0x59, 0xF2, 0x08, 0xCD, 0xE0, 0xF2, 0xA8, 0x97,
0x20, 0xEA, 0x5F, 0x36, 0x8C, 0xA1, 0x94, 0x50, 0xFD, 0x69, 0x1B, 0xCB, 0xB0, 0x4C, 0xF3, 0x83, 0xA7, 0x1C, 0x68, 0x54, 0x17, 0x03, 0x8A, 0x20, 0x57,
0x8B, 0x4F, 0xBF, 0xB5, 0xC7, 0x0B, 0xC2, 0xC5, 0x17, 0xEE, 0x8C, 0x91, 0xFB, 0x50, 0x58, 0x31, 0x11, 0x72, 0x87, 0x71, 0x25, 0x2E, 0xE7, 0xE4, 0x79,
0xD3, 0x0D, 0xD1, 0xE1, 0x98, 0x3A, 0xC9, 0x4D, 0x08, 0xF4, 0xB8, 0x59, 0x11, 0x90, 0x10, 0x50, 0x1D, 0x54, 0x88, 0xE1, 0xB7, 0x8F, 0x18, 0x02, 0x77,
0x78, 0x48, 0x20, 0x8B, 0x25, 0x08, 0xBF, 0xF2, 0x89, 0x59, 0xDB, 0x31, 0xD6, 0xA8, 0x0C, 0x64, 0x11, 0x8C, 0xC9, 0x21, 0xE6, 0x07, 0xCB, 0x5E, 0x8F,
0x17, 0x9E, 0xCA, 0x74, 0x20, 0x8E, 0x0E, 0xD5, 0x07, 0x3B, 0x5D, 0x51, 0x8E, 0xB2, 0x25, 0x0B, 0x7B, 0x0A, 0x25, 0x8B, 0x95, 0xD0, 0x82, 0xF4, 0xE2,
0x86, 0x71, 0xCE, 0xE6, 0x2C, 0x54, 0xB2, 0xC6, 0x8B, 0x02, 0x7D, 0x7F, 0xC2, 0x34, 0x20, 0xEA, 0x5F, 0x36, 0x8C, 0xA1, 0x94, 0x50, 0xFD, 0x69, 0x1B,
0x61, 0xC2, 0x0B, 0xF0, 0x85, 0x84, 0xCC, 0x98, 0x11, 0x4A, 0x8D, 0x66, 0x03, 0xCB, 0xB0, 0x8B, 0x4F, 0xBF, 0xB5, 0xC7, 0x0B, 0xC2, 0xC5, 0x17, 0xEE,
0xF8, 0x54, 0x57, 0xB0, 0xF0, 0x45, 0x8C, 0xFF, 0xD3, 0x61, 0xA8, 0x82, 0x09, 0x8C, 0x91, 0xFB, 0xD3, 0x0D, 0xD1, 0xE1, 0x98, 0x3A, 0xC9, 0x4D, 0x08,
0xFF, 0x00, 0xCA, 0x0B, 0x8C, 0x9B, 0x75, 0x24, 0x09, 0x07, 0x00, 0xF9, 0xAC, 0xF4, 0xB8, 0x59, 0x11, 0x78, 0x48, 0x20, 0x8B, 0x25, 0x08, 0xBF, 0xF2,
0xC1, 0xBB, 0xB7, 0x9E, 0xE8, 0x41, 0xE4, 0x2B, 0xCE, 0xAB, 0x2D, 0x9D, 0xEA, 0x89, 0x59, 0xDB, 0x31, 0xD6, 0x17, 0x9E, 0xCA, 0x74, 0x20, 0x8E, 0x0E,
0xFD, 0x41, 0x00, 0x42, 0x8F, 0xC9, 0xF4, 0x87, 0x54, 0x61, 0x2D, 0x42, 0x51, 0xD5, 0x07, 0x3B, 0x5D, 0x51, 0x8E, 0x86, 0x71, 0xCE, 0xE6, 0x2C, 0x54,
0x3F, 0x52, 0x29, 0xCB, 0x87, 0x0F, 0x1F, 0xE4, 0x11, 0x33, 0x8E, 0xE5, 0xCD, 0xB2, 0xC6, 0x8B, 0x02, 0x7D, 0x7F, 0xC2, 0x61, 0xC2, 0x0B, 0xF0, 0x85,
0x86, 0x6F, 0x96, 0x30, 0x40, 0x06, 0xF5, 0x7F, 0x42, 0xB2, 0x61, 0x93, 0x55, 0x84, 0xCC, 0x98, 0x11, 0x4A, 0x8D, 0x66, 0x03, 0xF8, 0x54, 0x57, 0xB0,
0x87, 0xFE, 0x86, 0xE3, 0x67, 0x49, 0xA2, 0xC3, 0x1F, 0x6F, 0xEF, 0x76, 0x4E, 0xF0, 0x45, 0x8C, 0xFF, 0xD3, 0x61, 0xA8, 0x82, 0x09, 0xFF, 0x00, 0xCA,
0xE5, 0x12, 0x89, 0x3E, 0x10, 0xFF, 0x90, 0x99, 0xBF, 0x40, 0x45, 0xAA, 0xFC, 0x0B, 0x8C, 0x9B, 0x75, 0x24, 0x09, 0x07, 0x00, 0xF9, 0xAC, 0xC1, 0xBB,
0x10, 0xDC, 0xC1, 0xF1, 0x44, 0x13, 0x50, 0x58, 0x29, 0x61, 0x28, 0x95, 0x70, 0xB7, 0x9E, 0xE8, 0x41, 0xE4, 0x2B, 0xCE, 0xAB, 0x2D, 0x9D, 0xEA, 0xFD,
0x05, 0x7E, 0x21, 0x8D, 0xE3, 0x83, 0xB2, 0x8F, 0xB2, 0xA2, 0x10, 0xE0, 0x93, 0x41, 0x00, 0x42, 0x8F, 0xC9, 0xF4, 0x87, 0x54, 0x61, 0x2D, 0x42, 0x51,
0xAC, 0xFD, 0x88, 0x38, 0x38, 0xC4, 0x46, 0x23, 0x4A, 0x41, 0xF8, 0x0F, 0xA8, 0x3F, 0x52, 0x29, 0xCB, 0x87, 0x0F, 0x1F, 0xE4, 0x11, 0x33, 0x8E, 0xE5,
0x12, 0xD5, 0x88, 0x7F, 0x92, 0xA2, 0x21, 0xBE, 0xF1, 0x0A, 0x11, 0x92, 0x11, 0xCD, 0x86, 0x6F, 0x96, 0x30, 0x40, 0x06, 0xF5, 0x7F, 0x42, 0xB2, 0x61,
0x99, 0xAB, 0xEA, 0xC3, 0x85, 0x22, 0x1B, 0xA2, 0xDF, 0xE9, 0x7E, 0x18, 0x95, 0x93, 0x55, 0x87, 0xFE, 0x86, 0xE3, 0x67, 0x49, 0xA2, 0xC3, 0x1F, 0x6F,
0xB6, 0x48, 0xD1, 0x9F, 0x50, 0xCF, 0x11, 0xBE, 0xF9, 0xB9, 0x1E, 0xC7, 0x9B, 0xEF, 0x76, 0x4E, 0xE5, 0x12, 0x89, 0x3E, 0x10, 0xFF, 0x90, 0x99, 0xBF,
0x00, 0xE3, 0x62, 0xAA, 0x98, 0x0E, 0x01, 0x87, 0xC0, 0x27, 0x59, 0x53, 0x83, 0x40, 0x45, 0xAA, 0xFC, 0x10, 0xDC, 0xC1, 0xF1, 0x44, 0x13, 0x50, 0x58,
0x47, 0xBA, 0x96, 0xEA, 0xFF, 0x88, 0x68, 0xEA, 0x08, 0xD3, 0x58, 0xCE, 0x0F, 0x29, 0x61, 0x28, 0x95, 0x70, 0x05, 0x7E, 0x21, 0x8D, 0xE3, 0x83, 0xB2,
0x05, 0x8A, 0xDA, 0x27, 0x6E, 0x15, 0x18, 0xF2, 0xAF, 0x38, 0xEA, 0x54, 0xA0, 0x8F, 0xB2, 0xA2, 0x10, 0xE0, 0x93, 0xAC, 0xFD, 0x88, 0x38, 0x38, 0xC4,
0x89, 0x74, 0xA1, 0x4B, 0x16, 0x9A, 0x14, 0x4D, 0x06, 0x85, 0x1D, 0xEC, 0xEF, 0x46, 0x23, 0x4A, 0x41, 0xF8, 0x0F, 0xA8, 0x12, 0xD5, 0x88, 0x7F, 0x92,
0x52, 0x54, 0x93, 0x2C, 0x09, 0xA3, 0xE9, 0xC6, 0x1C, 0x75, 0xAD, 0xD8, 0x14, 0xA2, 0x21, 0xBE, 0xF1, 0x0A, 0x11, 0x92, 0x11, 0x99, 0xAB, 0xEA, 0xC3,
0x2E, 0x31, 0x58, 0x15, 0xCF, 0xB8, 0x44, 0x04, 0xAC, 0x1C, 0xF0, 0x22, 0xA9, 0x85, 0x22, 0x1B, 0xA2, 0xDF, 0xE9, 0x7E, 0x18, 0x95, 0xB6, 0x48, 0xD1,
0x3B, 0x99, 0xD9, 0x44, 0x9D, 0x8E, 0xF8, 0x05, 0xE1, 0x04, 0x7B, 0x0B, 0x32, 0x9F, 0x50, 0xCF, 0x11, 0xBE, 0xF9, 0xB9, 0x1E, 0xC7, 0x9B, 0x00, 0xE3,
0x13, 0x85, 0x22, 0xDE, 0x87, 0xAC, 0x27, 0xAB, 0x2A, 0x66, 0x75, 0xA1, 0x9B, 0x62, 0xAA, 0x98, 0x0E, 0x01, 0x87, 0xC0, 0x27, 0x59, 0x53, 0x83, 0x47,
0xC0, 0x51, 0xE1, 0x59, 0x43, 0x5B, 0xC2, 0x37, 0xDF, 0x00, 0xD5, 0x98, 0x53, 0xBA, 0x96, 0xEA, 0xFF, 0x88, 0x68, 0xEA, 0x08, 0xD3, 0x58, 0xCE, 0x0F,
0xE3, 0x95, 0xD0, 0xD2, 0x88, 0xF6, 0x8F, 0xFD, 0x28, 0x5D, 0xFE, 0x50, 0x34, 0x05, 0x8A, 0xDA, 0x27, 0x6E, 0x15, 0x18, 0xF2, 0xAF, 0x38, 0xEA, 0x54,
0xF4, 0x85, 0xEA, 0xA1, 0xC9, 0x0F, 0xA6, 0x72, 0xA8, 0x57, 0xF8, 0xEA, 0x50, 0xA0, 0x89, 0x74, 0xA1, 0x4B, 0x16, 0x9A, 0x14, 0x4D, 0x06, 0x85, 0x1D,
0x96, 0xC8, 0x84, 0x1E, 0xF8, 0x1F, 0x2C, 0xEB, 0xA9, 0xA3, 0x64, 0xE1, 0xAC, 0xEC, 0xEF, 0x52, 0x54, 0x93, 0x2C, 0x09, 0xA3, 0xE9, 0xC6, 0x1C, 0x75,
0xE4, 0x2B, 0x50, 0xA4, 0x66, 0x54, 0x87, 0x63, 0xA2, 0x87, 0x00, 0x04, 0x29, 0xAD, 0xD8, 0x14, 0x2E, 0x31, 0x58, 0x15, 0xCF, 0xB8, 0x44, 0x04, 0xAC,
0x2F, 0x1E, 0x89, 0x51, 0xC1, 0xC0, 0xD0, 0xA4, 0xC7, 0x14, 0x52, 0xBA, 0x58, 0x1C, 0xF0, 0x22, 0xA9, 0x3B, 0x99, 0xD9, 0x44, 0x9D, 0x8E, 0xF8, 0x05,
0xF1, 0xC4, 0x41, 0x40, 0x57, 0xD5, 0xF2, 0xA6, 0x80, 0xEC, 0xB0, 0x30, 0xB1, 0xE1, 0x04, 0x7B, 0x0B, 0x32, 0x13, 0x85, 0x22, 0xDE, 0x87, 0xAC, 0x27,
0x71, 0xDB, 0x45, 0xA4, 0x86, 0xAC, 0xA7, 0x98, 0xD4, 0xB3, 0x26, 0x42, 0xF1, 0xAB, 0x2A, 0x66, 0x75, 0xA1, 0x9B, 0xC0, 0x51, 0xE1, 0x59, 0x43, 0x5B,
0x24, 0x31, 0x0C, 0x54, 0x4F, 0x68, 0x03, 0xB3, 0x0E, 0xD8, 0xB7, 0xE5, 0x8D, 0xC2, 0x37, 0xDF, 0x00, 0xD5, 0x98, 0x53, 0xE3, 0x95, 0xD0, 0xD2, 0x88,
0x1C, 0x8B, 0xD5, 0x08, 0x2D, 0x9E, 0xD0, 0x9C, 0x06, 0x62, 0xA1, 0xD5, 0x41, 0xF6, 0x8F, 0xFD, 0x28, 0x5D, 0xFE, 0x50, 0x34, 0xF4, 0x85, 0xEA, 0xA1,
0x3B, 0xA0, 0x5C, 0x7E, 0x92, 0x75, 0x84, 0x57, 0x38, 0x41, 0xA0, 0x25, 0xB3, 0xC9, 0x0F, 0xA6, 0x72, 0xA8, 0x57, 0xF8, 0xEA, 0x50, 0x96, 0xC8, 0x84,
0xA6, 0xA3, 0x89, 0x33, 0xC2, 0xD1, 0x60, 0x72, 0x57, 0xB1, 0xC2, 0xC0, 0x2A, 0x1E, 0xF8, 0x1F, 0x2C, 0xEB, 0xA9, 0xA3, 0x64, 0xE1, 0xAC, 0xE4, 0x2B,
0x79, 0xB0, 0xC2, 0x6C, 0x13, 0x01, 0x51, 0x21, 0xCB, 0x13, 0xA3, 0x32, 0x3E, 0x50, 0xA4, 0x66, 0x54, 0x87, 0x63, 0xA2, 0x87, 0x00, 0x04, 0x29, 0x2F,
0x16, 0x49, 0xEF, 0x6C, 0xF1, 0x30, 0x90, 0x05, 0x5D, 0x85, 0x27, 0x1B, 0xAA, 0x1E, 0x89, 0x51, 0xC1, 0xC0, 0xD0, 0xA4, 0xC7, 0x14, 0x52, 0xBA, 0x58,
0xC0, 0x74, 0x7A, 0x5C, 0x37, 0x01, 0x27, 0x05, 0xEA, 0x3C, 0x4A, 0x4B, 0x9F, 0xF1, 0xC4, 0x41, 0x40, 0x57, 0xD5, 0xF2, 0xA6, 0x80, 0xEC, 0xB0, 0x30,
0x0D, 0xC8, 0x18, 0xA2, 0xD4, 0x20, 0x59, 0xCB, 0x19, 0x7D, 0x9C, 0x5F, 0x6C, 0xB1, 0x71, 0xDB, 0x45, 0xA4, 0x86, 0xAC, 0xA7, 0x98, 0xD4, 0xB3, 0x26,
0x04, 0xCA, 0x28, 0x6E, 0x6B, 0xAC, 0x7B, 0x0C, 0xCE, 0xE5, 0x9E, 0x49, 0x84, 0x42, 0xF1, 0x24, 0x31, 0x0C, 0x54, 0x4F, 0x68, 0x03, 0xB3, 0x0E, 0xD8,
0x18, 0xDD, 0x1D, 0xAA, 0x39, 0x29, 0x9F, 0x30, 0xFA, 0x22, 0x67, 0x7C, 0x93, 0xB7, 0xE5, 0x8D, 0x1C, 0x8B, 0xD5, 0x08, 0x2D, 0x9E, 0xD0, 0x9C, 0x06,
0x92, 0x09, 0x13, 0x52, 0x2F, 0x1E, 0x4E, 0x6E, 0x61, 0x05, 0x18, 0x85, 0x07, 0x62, 0xA1, 0xD5, 0x41, 0x3B, 0xA0, 0x5C, 0x7E, 0x92, 0x75, 0x84, 0x57,
0xA9, 0x33, 0x1E, 0x68, 0xBD, 0x95, 0x31, 0x20, 0x8B, 0x2A, 0xA6, 0x70, 0x96, 0x38, 0x41, 0xA0, 0x25, 0xB3, 0xA6, 0xA3, 0x89, 0x33, 0xC2, 0xD1, 0x60,
0xC4, 0x5E, 0x3D, 0x95, 0x50, 0x79, 0x67, 0x46, 0xCA, 0x50, 0x20, 0xDF, 0x10, 0x72, 0x57, 0xB1, 0xC2, 0xC0, 0x2A, 0x79, 0xB0, 0xC2, 0x6C, 0x13, 0x01,
0x2F, 0x08, 0xA0, 0x03, 0xBD, 0x2D, 0x50, 0xEE, 0x55, 0x2C, 0xEC, 0xC9, 0x5F, 0x51, 0x21, 0xCB, 0x13, 0xA3, 0x32, 0x3E, 0x16, 0x49, 0xEF, 0x6C, 0xF1,
0xB9, 0x11, 0x44, 0xEF, 0x4A, 0x21, 0x74, 0x74, 0x75, 0xF3, 0x86, 0xA2, 0x8A, 0x30, 0x90, 0x05, 0x5D, 0x85, 0x27, 0x1B, 0xAA, 0xC0, 0x74, 0x7A, 0x5C,
0x88, 0xA3, 0x77, 0x21, 0xC6, 0xC3, 0x21, 0x27, 0x8A, 0x42, 0x2C, 0x16, 0x3A, 0x37, 0x01, 0x27, 0x05, 0xEA, 0x3C, 0x4A, 0x4B, 0x9F, 0x0D, 0xC8, 0x18,
0x88, 0x8A, 0x7F, 0x73, 0xEA, 0xD8, 0x79, 0x46, 0x1C, 0x67, 0x9B, 0x79, 0x61, 0xA2, 0xD4, 0x20, 0x59, 0xCB, 0x19, 0x7D, 0x9C, 0x5F, 0x6C, 0x04, 0xCA,
0x95, 0xF3, 0x44, 0x63, 0x65, 0x12, 0x8C, 0xA5, 0xBB, 0x88, 0x12, 0x67, 0x9F, 0x28, 0x6E, 0x6B, 0xAC, 0x7B, 0x0C, 0xCE, 0xE5, 0x9E, 0x49, 0x84, 0x18,
0x90, 0x5D, 0x95, 0x66, 0xA1, 0x9C, 0x57, 0x2A, 0x09, 0x34, 0x07, 0x7B, 0xD6, 0xDD, 0x1D, 0xAA, 0x39, 0x29, 0x9F, 0x30, 0xFA, 0x22, 0x67, 0x7C, 0x93,
0x02, 0xAC, 0x9A, 0x43, 0x10, 0xB5, 0x0A, 0xA0, 0x17, 0x1C, 0xA6, 0x00, 0x9B 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: */ /* Data from bootstrap_font.pal inserted by mkbootstrap.lua: */
constexpr std::array<uint8_t, 47> bootstrap_font_pal { constexpr std::array<uint8_t, 47> bootstrap_font_pal{
0x52, 0x4E, 0x43, 0x01, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x1D, 0xB7, 0x52, 0x4E, 0x43, 0x01, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x1D,
0xFF, 0x76, 0x79, 0xBE, 0xEF, 0x90, 0x10, 0x90, 0x05, 0x01, 0x02, 0x00, 0x00, 0xB7, 0xFF, 0x76, 0x79, 0xBE, 0xEF, 0x90, 0x10, 0x90, 0x05, 0x01, 0x02,
0x20, 0x2B, 0x04, 0x00, 0x00, 0x00, 0x40, 0x08, 0x00, 0xB6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x2B, 0x04, 0x00, 0x00, 0x00, 0x40, 0x08, 0x00, 0xB6,
0x3F, 0x3F, 0x3F, 0x62, 0x79, 0xBE, 0x27, 0x3F 0x00, 0x00, 0x00, 0x3F, 0x3F, 0x3F, 0x62, 0x79, 0xBE, 0x27, 0x3F};
};
/* End autogenerated content */ /* End autogenerated content */
// Lua reader function for loading bootstrap_code // Lua reader function for loading bootstrap_code
const char* read_bootstrap_line(lua_State *L, void *data, size_t *size) const char* read_bootstrap_line(lua_State* L, void* data, size_t* size) {
{ int& iLine = *reinterpret_cast<int*>(data);
int& iLine = *reinterpret_cast<int*>(data); ++iLine;
++iLine; if (iLine < 0 || (iLine & 1)) {
if(iLine < 0 || (iLine & 1)) *size = 1;
{ return "\n";
*size = 1; } else {
return "\n"; const char* s = bootstrap_code[iLine / 2];
} if (s == nullptr) {
else *size = 0;
{ return nullptr;
const char *s = bootstrap_code[iLine / 2]; } else {
if(s == nullptr) *size = std::strlen(s);
{ return s;
*size = 0;
return nullptr;
}
else
{
*size = std::strlen(s);
return s;
}
} }
}
} }
template<typename Array> template <typename Array>
inline void push(lua_State *L, Array data) inline void push(lua_State* L, Array data) {
{ lua_pushlstring(L, reinterpret_cast<const char*>(data.data()), data.size());
lua_pushlstring(L, reinterpret_cast<const char*>(data.data()), data.size());
} }
} // namespace } // namespace
int bootstrap_lua_resources(lua_State *L) int bootstrap_lua_resources(lua_State* L) {
{ push(L, bootstrap_font_dat);
push(L, bootstrap_font_dat); push(L, bootstrap_font_tab);
push(L, bootstrap_font_tab); push(L, bootstrap_font_pal);
push(L, bootstrap_font_pal); return 3;
return 3;
} }
int bootstrap_lua_error_report(lua_State *L) int bootstrap_lua_error_report(lua_State* L) {
{ int iLine = -first_bootstrap_code_line_number;
int iLine = -first_bootstrap_code_line_number; if (luaT_load(L, read_bootstrap_line, &iLine, "@bootstrap.cpp", "t") == 0) {
if(luaT_load(L, read_bootstrap_line, &iLine, "@bootstrap.cpp", "t") == 0) bootstrap_lua_resources(L);
{ lua_pushvalue(L, 1);
bootstrap_lua_resources(L); lua_call(L, 4, 0);
lua_pushvalue(L, 1); return 0;
lua_call(L, 4, 0); } else
return 0; return lua_error(L);
}
else
return lua_error(L);
} }

View File

@@ -25,9 +25,9 @@ SOFTWARE.
#include "lua.hpp" #include "lua.hpp"
//! Push onto the stack the bootstrap font data file, table file and palette. //! 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. //! 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_

View File

@@ -63,7 +63,7 @@ SOFTWARE.
/** Standard includes **/ /** Standard includes **/
#ifndef __STDC_CONSTANT_MACROS #ifndef __STDC_CONSTANT_MACROS
# define __STDC_CONSTANT_MACROS #define __STDC_CONSTANT_MACROS
#endif #endif
#include <cstddef> #include <cstddef>
#include <cstdint> #include <cstdint>
@@ -74,18 +74,20 @@ SOFTWARE.
#endif #endif
// We bring in the most common stddef and stdint types to avoid typing // 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::size_t;
using std::uint8_t; using std::uint8_t;
using std::uint16_t; using std::uint16_t;
using std::uint32_t; using std::uint32_t;
using std::uint64_t; using std::uint64_t;
using std::int8_t; // clang-format on
using std::int16_t;
using std::int32_t;
using std::int64_t;
/** Visual Leak Detector **/ /** Visual Leak Detector **/
// In Visual Studio, Visual Leak Detector can be used to find memory leaks. // In Visual Studio, Visual Leak Detector can be used to find memory leaks.
#cmakedefine CORSIX_TH_USE_VLD #cmakedefine CORSIX_TH_USE_VLD
#endif // CORSIX_TH_CONFIG_H_ #endif // CORSIX_TH_CONFIG_H_

View File

@@ -3,7 +3,8 @@
#include <array> #include <array>
constexpr std::array<uint16_t, 0x80> cp437_to_unicode_table { // clang-format off
constexpr std::array<uint16_t, 0x80> cp437_to_unicode_table{
/* 0x00 through 0x7F need no translation */ /* 0x00 through 0x7F need no translation */
0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7, 0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7,
0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x00EC, 0x00C4, 0x00C5, 0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x00EC, 0x00C4, 0x00C5,
@@ -20,7 +21,6 @@ constexpr std::array<uint16_t, 0x80> cp437_to_unicode_table {
0x03B1, 0x00DF, 0x0393, 0x03C0, 0x03A3, 0x03C3, 0x03BC, 0x03C4, 0x03B1, 0x00DF, 0x0393, 0x03C0, 0x03A3, 0x03C3, 0x03BC, 0x03C4,
0x03A6, 0x0398, 0x03A9, 0x03B4, 0x221E, 0x03C6, 0x03B5, 0x2229, 0x03A6, 0x0398, 0x03A9, 0x03B4, 0x221E, 0x03C6, 0x03B5, 0x2229,
0x2261, 0x00B1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00F7, 0x2248, 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 #endif

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -21,9 +21,9 @@ SOFTWARE.
*/ */
#include "config.h" #include "config.h"
#include "th_lua.h"
#include <cstdio> #include <cstdio>
#include <string> #include <string>
#include "th_lua.h"
//! Layer for reading Theme Hospital files out of an .iso disk image //! 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 searches for the Theme Hospital data files, and can then be used to read
these data files. these data files.
*/ */
class iso_filesystem class iso_filesystem {
{ public:
public: iso_filesystem();
iso_filesystem(); ~iso_filesystem();
~iso_filesystem();
//! Set the character to be used between components in file paths //! Set the character to be used between components in file paths
void set_path_separator(char cSeparator); void set_path_separator(char cSeparator);
//! Load an .iso disk image and search for Theme Hospital data files //! 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 \param fRawFile A file handle of an .iso disk image. This handle must
remain valid for as long as the IsoFilesystem instance exists, and remain valid for as long as the IsoFilesystem instance exists, and
is not automatically closed by the IsoFilesystem instance. is not automatically closed by the IsoFilesystem instance.
\return true on success, false on failure - call getError() for reason \return true on success, false on failure - call getError() for reason
*/ */
bool initialise(std::FILE* fRawFile); bool initialise(std::FILE* fRawFile);
//! Get the reason for the most recent failure //! Get the reason for the most recent failure
/*! /*!
Can be called after initialise() or getFileData() return false. Can be called after initialise() or getFileData() return false.
*/ */
const char* get_error() const; const char* get_error() const;
using file_handle = int; using file_handle = int;
//! Find a file in the loaded .iso disk image //! Find a file in the loaded .iso disk image
/*! /*!
If (and only if) the given file could not be found, then isHandleGood() If (and only if) the given file could not be found, then isHandleGood()
will return false on the returned handle. will return false on the returned handle.
*/ */
file_handle find_file(const char* sPath) const; file_handle find_file(const char* sPath) const;
//! Iterate all files of the .iso disk image within a given directory //! Iterate all files of the .iso disk image within a given directory
/*! /*!
\param sPath The directory to iterate \param sPath The directory to iterate
\param fnCallback The function to be called for each file. The first \param fnCallback The function to be called for each file. The first
parameter to this function is pCallbackData. The second is the name parameter to this function is pCallbackData. The second is the name
of a file which is in sPath. of a file which is in sPath.
\param pCallbackData Opaque value to be called to fnCallback. \param pCallbackData Opaque value to be called to fnCallback.
*/ */
void visit_directory_files(const char* sPath, void visit_directory_files(const char* sPath,
void (*fnCallback)(void*, const char*, const char*), void (*fnCallback)(void*, const char*,
const char*),
void* pCallbackData) const; void* pCallbackData) const;
//! Test if a file handle from findFile() is good or is invalid //! Test if a file handle from findFile() is good or is invalid
static inline bool isHandleGood(file_handle x) {return x != 0;} static inline bool isHandleGood(file_handle x) { return x != 0; }
//! Get the size (in bytes) of a file in the loaded .iso disk image //! Get the size (in bytes) of a file in the loaded .iso disk image
/*! /*!
\param iFile A file handle returned by findFile() \param iFile A file handle returned by findFile()
*/ */
uint32_t get_file_size(file_handle iFile) const; uint32_t get_file_size(file_handle iFile) const;
//! Get the contents of a file in the loaded .iso disk image //! Get the contents of a file in the loaded .iso disk image
/*! /*!
\param iFile A file handle returned by findFile() \param iFile A file handle returned by findFile()
\param pBuffer The buffer to place the resulting data in \param pBuffer The buffer to place the resulting data in
\return true on success, false on failure - call getError() for reason \return true on success, false on failure - call getError() for reason
*/ */
bool get_file_data(file_handle iFile, uint8_t *pBuffer); bool get_file_data(file_handle iFile, uint8_t* pBuffer);
private: private:
struct file_metadata struct file_metadata {
{ std::string path;
std::string path; uint32_t sector;
uint32_t sector; uint32_t size;
uint32_t size; };
};
std::FILE* raw_file; std::FILE* raw_file;
char* error; char* error;
std::vector<file_metadata> files; std::vector<file_metadata> files;
long sector_size; long sector_size;
char path_seperator; char path_seperator;
//! Free any memory in use //! Free any memory in use
void clear(); void clear();
//! Set the last error, printf-style //! Set the last error, printf-style
void set_error(const char* sFormat, ...); void set_error(const char* sFormat, ...);
//! Seek to a logical sector of the disk image //! Seek to a logical sector of the disk image
bool seek_to_sector(uint32_t iSector); bool seek_to_sector(uint32_t iSector);
//! Read data from the disk image //! Read data from the disk image
bool read_data(uint32_t iByteCount, uint8_t *pBuffer); bool read_data(uint32_t iByteCount, uint8_t* pBuffer);
//! Scan the given array of directory entries for a Theme Hospital file //! 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 pDirEnt Pointer to a padded array of ISO 9660 directory entries.
\param iDirEntsSize The number of bytes in the directory entry array. \param iDirEntsSize The number of bytes in the directory entry array.
\param iLevel The recursion level (used to prevent infinite loops upon \param iLevel The recursion level (used to prevent infinite loops upon
maliciously-formed .iso disk images). maliciously-formed .iso disk images).
\return 0 if no Theme Hospital files were found. 1 if the given array \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 contains a Theme Hospital data file. 2 if the given array is the
top-level Theme Hospital data directory. Other values otherwise. top-level Theme Hospital data directory. Other values otherwise.
*/ */
int find_hosp_directory(const uint8_t *pDirEnt, int iDirEntsSize, int iLevel); int find_hosp_directory(const uint8_t* pDirEnt, int iDirEntsSize, int iLevel);
//! Build the list of Theme Hospital data files //! Build the list of Theme Hospital data files
/*! /*!
\param iSector The ordinal of a logical sector containing a padded \param iSector The ordinal of a logical sector containing a padded
arrary of ISO 9660 directory entries. arrary of ISO 9660 directory entries.
\param iDirEntsSize The number of bytes in the directory entry array. \param iDirEntsSize The number of bytes in the directory entry array.
\param prefix The path name to prepend to filenames in the directory. \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); 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. //! std:less like implementation for file_metadata. Based on the path.
static bool file_metadata_less(const file_metadata& lhs, const file_metadata& rhs); static bool file_metadata_less(const file_metadata& lhs,
const file_metadata& rhs);
}; };

View File

@@ -24,8 +24,8 @@ SOFTWARE.
#define CORSIX_TH_LUA_HPP_ #define CORSIX_TH_LUA_HPP_
extern "C" { extern "C" {
#include <lua.h>
#include <lauxlib.h> #include <lauxlib.h>
#include <lua.h>
#include <lualib.h> #include <lualib.h>
} }
@@ -35,22 +35,17 @@ extern "C" {
// with LUA_COMPAT_ALL defined, but we have no control over this. // with LUA_COMPAT_ALL defined, but we have no control over this.
#ifndef lua_objlen #ifndef lua_objlen
inline size_t lua_objlen(lua_State *L, int idx) inline size_t lua_objlen(lua_State* L, int idx) { return lua_rawlen(L, idx); }
{
return lua_rawlen(L, idx);
}
#endif #endif
#ifndef lua_equal #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); return lua_compare(L, idx1, idx2, LUA_OPEQ);
} }
#endif #endif
#ifndef lua_lessthan #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); return lua_compare(L, idx1, idx2, LUA_OPLT);
} }
#endif #endif
@@ -58,20 +53,14 @@ inline int lua_lessthan(lua_State *L, int idx1, int idx2)
// Use our own replacements for lua_[sg]etfenv // Use our own replacements for lua_[sg]etfenv
#ifndef lua_setfenv #ifndef lua_setfenv
int luaT_setfenv52(lua_State*, int); int luaT_setfenv52(lua_State*, int);
inline int lua_setfenv(lua_State *L, int n) inline int lua_setfenv(lua_State* L, int n) { return luaT_setfenv52(L, n); }
{
return luaT_setfenv52(L, n);
}
#endif #endif
#ifndef lua_getfenv #ifndef lua_getfenv
void luaT_getfenv52(lua_State*, int); void luaT_getfenv52(lua_State*, int);
inline void lua_getfenv(lua_State *L, int n) inline void lua_getfenv(lua_State* L, int n) { luaT_getfenv52(L, n); }
{
luaT_getfenv52(L, n);
}
#endif #endif
#endif // LUA_VERSION_NUM >= 502 #endif // LUA_VERSION_NUM >= 502
#endif // CORSIX_TH_LUA_HPP_ #endif // CORSIX_TH_LUA_HPP_

View File

@@ -1,7 +1,7 @@
#include "lua_rnc.h" #include "lua_rnc.h"
#include <array>
#include "../../common/rnc.h" #include "../../common/rnc.h"
#include "th_lua.h" #include "th_lua.h"
#include <array>
//! Provides lua function to decompress RNC data //! Provides lua function to decompress RNC data
/*! /*!
@@ -12,70 +12,65 @@
namespace { namespace {
int l_decompress(lua_State *L) int l_decompress(lua_State* L) {
{ size_t inlen;
size_t inlen; const uint8_t* in =
const uint8_t* in = reinterpret_cast<const uint8_t*>(luaL_checklstring(L, 1, &inlen)); reinterpret_cast<const uint8_t*>(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);
// 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_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: case rnc_status::ok:
lua_pushlstring(L, (const char*)outbuf, outlen); lua_pushlstring(L, (const char*)outbuf, outlen);
return 1; return 1;
case rnc_status::file_is_not_rnc: case rnc_status::file_is_not_rnc:
lua_pushliteral(L, "Input is not RNC compressed data"); lua_pushliteral(L, "Input is not RNC compressed data");
break; break;
case rnc_status::huf_decode_error: case rnc_status::huf_decode_error:
lua_pushliteral(L, "Invalid Huffman coding"); lua_pushliteral(L, "Invalid Huffman coding");
break; break;
case rnc_status::file_size_mismatch: case rnc_status::file_size_mismatch:
lua_pushliteral(L, "Size mismatch"); lua_pushliteral(L, "Size mismatch");
break; break;
case rnc_status::packed_crc_error: case rnc_status::packed_crc_error:
lua_pushliteral(L, "Incorrect packed CRC"); lua_pushliteral(L, "Incorrect packed CRC");
break; break;
case rnc_status::unpacked_crc_error: case rnc_status::unpacked_crc_error:
lua_pushliteral(L, "Incorrect unpacked CRC"); lua_pushliteral(L, "Incorrect unpacked CRC");
break; break;
default: default:
lua_pushliteral(L, "Unknown error decompressing RNC data"); lua_pushliteral(L, "Unknown error decompressing RNC data");
break; break;
} }
return 2; return 2;
} }
constexpr std::array<luaL_Reg, 2> rnclib {{ constexpr std::array<luaL_Reg, 2> rnclib{
{"decompress", l_decompress}, {{"decompress", l_decompress}, {nullptr, nullptr}}};
{nullptr, nullptr}
}};
} // namespace } // namespace
int luaopen_rnc(lua_State *L) int luaopen_rnc(lua_State* L) {
{ luaT_register(L, "rnc", rnclib);
luaT_register(L, "rnc", rnclib); return 1;
return 1;
} }

View File

@@ -3,6 +3,6 @@
#include "th_lua.h" #include "th_lua.h"
int luaopen_rnc(lua_State *L); int luaopen_rnc(lua_State* L);
#endif #endif

View File

@@ -23,8 +23,8 @@ SOFTWARE.
#ifndef CORSIX_TH_LUA_SDL_H_ #ifndef CORSIX_TH_LUA_SDL_H_
#define CORSIX_TH_LUA_SDL_H_ #define CORSIX_TH_LUA_SDL_H_
#include "lua.hpp"
#include <SDL.h> #include <SDL.h>
#include "lua.hpp"
// SDL event codes used for delivering custom events to l_mainloop in // SDL event codes used for delivering custom events to l_mainloop in
// sdl_core.cpp // sdl_core.cpp
@@ -39,8 +39,8 @@ SOFTWARE.
// SDL_USEREVENT_SOUND_OVER - informs script of a played sound finishing. // SDL_USEREVENT_SOUND_OVER - informs script of a played sound finishing.
#define SDL_USEREVENT_SOUND_OVER (SDL_USEREVENT + 4) #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_

View File

@@ -21,18 +21,15 @@ SOFTWARE.
*/ */
#include "config.h" #include "config.h"
#include <cstdio>
#include <cstring>
#include <string>
#include "iso_fs.h"
#include "lua.hpp" #include "lua.hpp"
extern "C" {
int luaopen_random(lua_State *L);
}
#include "th_lua.h"
#include "lua_rnc.h" #include "lua_rnc.h"
#include "lua_sdl.h" #include "lua_sdl.h"
#include "persist_lua.h" #include "persist_lua.h"
#include "iso_fs.h" #include "th_lua.h"
#include <cstring>
#include <cstdio>
#include <string>
// Config file checking // Config file checking
#ifndef CORSIX_TH_USE_PACK_PRAGMAS #ifndef CORSIX_TH_USE_PACK_PRAGMAS
@@ -40,119 +37,120 @@ int luaopen_random(lua_State *L);
#endif #endif
// End of config file checking // End of config file checking
extern "C" {
int luaopen_random(lua_State* L);
}
namespace { namespace {
inline void preload_lua_package(lua_State *L, const char* name, lua_CFunction 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); luaT_execute(
L, std::string("package.preload.").append(name).append(" = ...").c_str(),
fn);
} }
} // namespace } // namespace
int lua_main_no_eval(lua_State *L) int lua_main_no_eval(lua_State* L) {
{ // assert(_VERSION == LUA_VERSION)
// assert(_VERSION == LUA_VERSION) size_t iLength;
size_t iLength; lua_getglobal(L, "_VERSION");
lua_getglobal(L, "_VERSION"); const char* sVersion = lua_tolstring(L, -1, &iLength);
const char* sVersion = lua_tolstring(L, -1, &iLength); if (iLength != std::strlen(LUA_VERSION) ||
if(iLength != std::strlen(LUA_VERSION) || std::strcmp(sVersion, LUA_VERSION) != 0) std::strcmp(sVersion, LUA_VERSION) != 0) {
{ lua_pushliteral(
lua_pushliteral(L, "Linked against a version of Lua different to the " L,
"one used when compiling.\nPlease recompile CorsixTH against the " "Linked against a version of Lua different to the one used "
"same Lua version it is linked against."); "when compiling.\nPlease recompile CorsixTH against the same "
return lua_error(L); "Lua version it is linked against.");
} return lua_error(L);
lua_pop(L, 1); }
lua_pop(L, 1);
// math.random* = Mersenne twister variant // math.random* = Mersenne twister variant
luaT_cpcall(L, luaopen_random, nullptr); luaT_cpcall(L, luaopen_random, nullptr);
// Fill in package.preload table so that calls to require("X") from Lua // Fill in package.preload table so that calls to require("X") from Lua
// will call the appropriate luaopen_X function in C. // will call the appropriate luaopen_X function in C.
preload_lua_package(L, "rnc", luaopen_rnc); preload_lua_package(L, "rnc", luaopen_rnc);
preload_lua_package(L, "TH", luaopen_th); preload_lua_package(L, "TH", luaopen_th);
preload_lua_package(L, "persist", luaopen_persist); preload_lua_package(L, "persist", luaopen_persist);
preload_lua_package(L, "sdl", luaopen_sdl); preload_lua_package(L, "sdl", luaopen_sdl);
// require "debug" (Harmless in Lua 5.1, useful in 5.2 for compatbility) // require "debug" (Harmless in Lua 5.1, useful in 5.2 for compatbility)
luaT_execute(L, "require \"debug\""); luaT_execute(L, "require \"debug\"");
// Check for --interpreter and run that instead of CorsixTH.lua // Check for --interpreter and run that instead of CorsixTH.lua
bool bGotScriptFile = false; bool bGotScriptFile = false;
int iNArgs = lua_gettop(L); int iNArgs = lua_gettop(L);
for(int i = 1; i <= iNArgs; ++i) for (int i = 1; i <= iNArgs; ++i) {
{ if (lua_type(L, i) == LUA_TSTRING) {
if(lua_type(L, i) == LUA_TSTRING) size_t iLen;
{ const char* sCmd = lua_tolstring(L, i, &iLen);
size_t iLen; if (iLen > 14 && std::memcmp(sCmd, "--interpreter=", 14) == 0) {
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)
{
lua_getglobal(L, "assert"); lua_getglobal(L, "assert");
lua_getglobal(L, "loadfile"); lua_getglobal(L, "loadfile");
lua_pushstring(L, CORSIX_TH_INTERPRETER_PATH); lua_pushlstring(L, sCmd + 14, iLen - 14);
bGotScriptFile = true; bGotScriptFile = true;
break;
}
} }
}
lua_call(L, 1, 2); if (!bGotScriptFile) {
lua_call(L, 2, 1); lua_getglobal(L, "assert");
lua_insert(L, 1); lua_getglobal(L, "loadfile");
return lua_gettop(L); 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) int lua_main(lua_State* L) {
{ lua_call(L, lua_main_no_eval(L) - 1, LUA_MULTRET);
lua_call(L, lua_main_no_eval(L) - 1, LUA_MULTRET); return lua_gettop(L);
return lua_gettop(L);
} }
int lua_stacktrace(lua_State *L) int lua_stacktrace(lua_State* L) {
{ // err = tostring(err)
// err = tostring(err) lua_settop(L, 1);
lua_settop(L, 1); lua_getglobal(L, "tostring");
lua_getglobal(L, "tostring"); lua_insert(L, 1);
lua_insert(L, 1); lua_call(L, 1, 1);
lua_call(L, 1, 1);
// err = <description> .. err // err = <description> .. err
lua_pushliteral(L, "An error has occurred in CorsixTH:\n"); lua_pushliteral(L, "An error has occurred in CorsixTH:\n");
lua_insert(L, 1); lua_insert(L, 1);
lua_concat(L, 2); lua_concat(L, 2);
// return debug.traceback(err, 2) // return debug.traceback(err, 2)
lua_getglobal(L, "debug"); lua_getglobal(L, "debug");
lua_getfield(L, -1, "traceback"); lua_getfield(L, -1, "traceback");
lua_pushvalue(L, 1); lua_pushvalue(L, 1);
lua_pushinteger(L, 2); lua_pushinteger(L, 2);
lua_call(L, 2, 1); lua_call(L, 2, 1);
return 1; return 1;
} }
int lua_panic(lua_State *L) int lua_panic(lua_State* L) {
{ std::fprintf(stderr,
std::fprintf(stderr, "A Lua error has occurred in CorsixTH outside of protected " "A Lua error has occurred in CorsixTH outside of protected "
"mode!!\n"); "mode!\n");
std::fflush(stderr); std::fflush(stderr);
if(lua_type(L, -1) == LUA_TSTRING) if (lua_type(L, -1) == LUA_TSTRING)
std::fprintf(stderr, "%s\n", lua_tostring(L, -1)); std::fprintf(stderr, "%s\n", lua_tostring(L, -1));
else else
std::fprintf(stderr, "%p\n", lua_topointer(L, -1)); std::fprintf(stderr, "%p\n", lua_topointer(L, -1));
std::fflush(stderr); 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;
} }

View File

@@ -33,14 +33,14 @@ SOFTWARE.
transfers control to CorsixTH.lua as soon as possible (so that as little as transfers control to CorsixTH.lua as soon as possible (so that as little as
possible behaviour is hardcoded into C rather than Lua). 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 //! Alternative lua mode entry point
/*! /*!
Behaves like CorsixTH_lua_main, except that it doesn't transfer control 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. 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 //! 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 processing the error, the caller receives LUA_ERRERR rather than panicking
while processing it itself. while processing it itself.
*/ */
int lua_stacktrace(lua_State *L); int lua_stacktrace(lua_State* L);
//! Process an uncaught Lua error before aborting //! 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, which can be done when they do, but at least the user should be informed,
and the error message printed. 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_

File diff suppressed because it is too large Load Diff

View File

@@ -23,84 +23,76 @@ SOFTWARE.
#ifndef CORSIX_TH_PERSIST_LUA_H_ #ifndef CORSIX_TH_PERSIST_LUA_H_
#define CORSIX_TH_PERSIST_LUA_H_ #define CORSIX_TH_PERSIST_LUA_H_
#include "config.h" #include "config.h"
#include "th_lua.h"
#include <vector>
#include <cstdlib> #include <cstdlib>
#include <vector>
#include "th_lua.h"
template <class T> struct lua_persist_int {}; template <class T>
template <> struct lua_persist_int<int> {typedef unsigned int T;}; struct lua_persist_int {};
template <>
struct lua_persist_int<int> {
typedef unsigned int T;
};
//! Interface used for persisting Lua objects //! Interface used for persisting Lua objects
/*! /*!
When userdata are persisted, they get an instance of this interface for When userdata are persisted, they get an instance of this interface for
writing binary data and other Lua objects. writing binary data and other Lua objects.
*/ */
class lua_persist_writer class lua_persist_writer {
{ public:
public: virtual ~lua_persist_writer() = default;
virtual ~lua_persist_writer() = default;
virtual lua_State* get_stack() = 0; virtual lua_State* get_stack() = 0;
virtual void write_stack_object(int iIndex) = 0; virtual void write_stack_object(int iIndex) = 0;
virtual void write_byte_stream(const uint8_t *pBytes, size_t iCount) = 0; virtual void write_byte_stream(const uint8_t* pBytes, size_t iCount) = 0;
virtual void set_error(const char *sError) = 0; virtual void set_error(const char* sError) = 0;
// write_stack_object for userdata without growing the Lua call stack // write_stack_object for userdata without growing the Lua call stack
// The given index should be a userdata whose __persist metamethod supports // The given index should be a userdata whose __persist metamethod supports
// fast persistance (being called with extra arguments and the wrong // fast persistance (being called with extra arguments and the wrong
// environment / upvalues). // environment / upvalues).
virtual void fast_write_stack_object(int iIndex) = 0; virtual void fast_write_stack_object(int iIndex) = 0;
// Writes an unsigned integer as a variable number of bytes // Writes an unsigned integer as a variable number of bytes
// Endian independant and underlying type size independant // Endian independant and underlying type size independant
template <class T> template <class T>
void write_uint(T tValue) void write_uint(T tValue) {
{ T tTemp(tValue);
T tTemp(tValue); int iNumBytes;
int iNumBytes; for (iNumBytes = 1; tTemp >= (T)0x80; tTemp /= (T)0x80) ++iNumBytes;
for(iNumBytes = 1; tTemp >= (T)0x80; tTemp /= (T)0x80) if (iNumBytes == 1) {
++iNumBytes; uint8_t iByte = (uint8_t)tValue;
if(iNumBytes == 1) write_byte_stream(&iByte, 1);
{ } else {
uint8_t iByte = (uint8_t)tValue; std::vector<uint8_t> bytes(iNumBytes);
write_byte_stream(&iByte, 1); bytes[iNumBytes - 1] = 0x7F & (uint8_t)(tValue);
} for (int i = 1; i < iNumBytes; ++i) {
else tValue /= (T)0x80;
{ bytes[iNumBytes - 1 - i] = 0x80 | (0x7F & (uint8_t)tValue);
std::vector<uint8_t> bytes(iNumBytes); }
bytes[iNumBytes - 1] = 0x7F & (uint8_t)(tValue); write_byte_stream(bytes.data(), iNumBytes);
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 <class T> template <class T>
void write_int(T tValue) void write_int(T tValue) {
{ typename lua_persist_int<T>::T tValueToWrite;
typename lua_persist_int<T>::T tValueToWrite; if (tValue >= 0) {
if(tValue >= 0) tValueToWrite = (typename lua_persist_int<T>::T)tValue;
{ tValueToWrite <<= 1;
tValueToWrite = (typename lua_persist_int<T>::T)tValue; } else {
tValueToWrite <<= 1; tValueToWrite = (typename lua_persist_int<T>::T)(-(tValue + 1));
} tValueToWrite <<= 1;
else tValueToWrite |= 1;
{
tValueToWrite = (typename lua_persist_int<T>::T)(-(tValue + 1));
tValueToWrite <<= 1;
tValueToWrite |= 1;
}
write_uint(tValueToWrite);
} }
write_uint(tValueToWrite);
}
template <class T> template <class T>
void write_float(T fValue) void write_float(T fValue) {
{ write_byte_stream(reinterpret_cast<uint8_t*>(&fValue), sizeof(T));
write_byte_stream(reinterpret_cast<uint8_t*>(&fValue), sizeof(T)); }
}
}; };
//! Interface used for depersisting Lua objects //! Interface used for depersisting Lua objects
@@ -108,65 +100,55 @@ public:
When userdata are depersisted, they get an instance of this interface for When userdata are depersisted, they get an instance of this interface for
reading binary data and other Lua objects. reading binary data and other Lua objects.
*/ */
class lua_persist_reader class lua_persist_reader {
{ public:
public: virtual ~lua_persist_reader() = default;
virtual ~lua_persist_reader() = default;
virtual lua_State* get_stack() = 0; virtual lua_State* get_stack() = 0;
virtual bool read_stack_object() = 0; virtual bool read_stack_object() = 0;
virtual bool read_byte_stream(uint8_t *pBytes, size_t iCount) = 0; virtual bool read_byte_stream(uint8_t* pBytes, size_t iCount) = 0;
virtual void set_error(const char *sError) = 0; virtual void set_error(const char* sError) = 0;
// Reads an integer previously written by lua_persist_writer::write_uint() // Reads an integer previously written by lua_persist_writer::write_uint()
template <class T> template <class T>
bool read_uint(T& tValue) bool read_uint(T& tValue) {
{ T tTemp(0);
T tTemp(0); uint8_t iByte;
uint8_t iByte;
while(true) while (true) {
{ if (!read_byte_stream(&iByte, 1)) return false;
if(!read_byte_stream(&iByte, 1)) if (iByte & 0x80) {
return false; tTemp = static_cast<T>(tTemp | (iByte & 0x7F));
if(iByte & 0x80) tTemp = static_cast<T>(tTemp << 7);
{ } else {
tTemp = static_cast<T>(tTemp | (iByte & 0x7F)); tTemp = static_cast<T>(tTemp | iByte);
tTemp = static_cast<T>(tTemp << 7); break;
} }
else
{
tTemp = static_cast<T>(tTemp | iByte);
break;
}
}
tValue = tTemp;
return true;
} }
template <class T> tValue = tTemp;
bool read_int(T& tValue) return true;
{ }
typename lua_persist_int<T>::T tWrittenValue;
if(!read_uint(tWrittenValue))
return false;
if(tWrittenValue & 1)
tValue = (-(T)(tWrittenValue >> 1)) - 1;
else
tValue = static_cast<T>(tWrittenValue >> 1);
return true;
}
template <class T> template <class T>
bool read_float(T& fValue) bool read_int(T& tValue) {
{ typename lua_persist_int<T>::T tWrittenValue;
if(!read_byte_stream(reinterpret_cast<uint8_t*>(&fValue), sizeof(T))) if (!read_uint(tWrittenValue)) return false;
return false; if (tWrittenValue & 1)
return true; tValue = (-(T)(tWrittenValue >> 1)) - 1;
} else
tValue = static_cast<T>(tWrittenValue >> 1);
return true;
}
template <class T>
bool read_float(T& fValue) {
if (!read_byte_stream(reinterpret_cast<uint8_t*>(&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_

View File

@@ -41,8 +41,8 @@
email: m-mat @ math.sci.hiroshima-u.ac.jp (remove space) email: m-mat @ math.sci.hiroshima-u.ac.jp (remove space)
*/ */
#include <lua.h>
#include <lauxlib.h> #include <lauxlib.h>
#include <lua.h>
#ifdef _MSC_VER #ifdef _MSC_VER
typedef unsigned __int16 uint16_t; typedef unsigned __int16 uint16_t;
typedef unsigned __int32 uint32_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 UPPER_MASK 0x80000000UL /* most significant w-r bits */
#define LOWER_MASK 0x7fffffffUL /* least significant r bits */ #define LOWER_MASK 0x7fffffffUL /* least significant r bits */
uint32_t mt[N]; /* the array for the state vector */ uint32_t mt[N]; /* the array for the state vector */
uint16_t mti=N+1; /* mti==N+1 means mt[N] is not initialized */ uint16_t mti = N + 1; /* mti==N+1 means mt[N] is not initialized */
/* initializes mt[N] with a seed */ /* initializes mt[N] with a seed */
static void init_genrand(uint32_t s) static void init_genrand(uint32_t s) {
{ mt[0] = s;
mt[0]= s; for (mti = 1; mti < N; mti++) {
for (mti=1; mti<N; mti++) { mt[mti] = (1812433253UL * (mt[mti - 1] ^ (mt[mti - 1] >> 30)) + mti);
mt[mti] =
(1812433253UL * (mt[mti-1] ^ (mt[mti-1] >> 30)) + mti);
/* See Knuth TAOCP Vol2. 3rd Ed. P.106 for multiplier. */ /* See Knuth TAOCP Vol2. 3rd Ed. P.106 for multiplier. */
/* In the previous versions, MSBs of the seed affect */ /* In the previous versions, MSBs of the seed affect */
/* only MSBs of the array mt[]. */ /* 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 */ /* generates a random number on [0,0xffffffff]-interval */
static uint32_t genrand_int32(void) static uint32_t genrand_int32(void) {
{
uint32_t y; 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 */ /* mag01[x] = x * MATRIX_A for x=0,1 */
if (mti >= N) { /* generate N words at one time */ if (mti >= N) { /* generate N words at one time */
int kk; 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 */ init_genrand(5489UL); /* a default initial seed is used */
for (kk=0;kk<N-M;kk++) { for (kk = 0; kk < N - M; kk++) {
y = (mt[kk]&UPPER_MASK)|(mt[kk+1]&LOWER_MASK); y = (mt[kk] & UPPER_MASK) | (mt[kk + 1] & LOWER_MASK);
mt[kk] = mt[kk+M] ^ (y >> 1) ^ mag01[y & 0x1UL]; mt[kk] = mt[kk + M] ^ (y >> 1) ^ mag01[y & 0x1UL];
} }
for (;kk<N-1;kk++) { for (; kk < N - 1; kk++) {
y = (mt[kk]&UPPER_MASK)|(mt[kk+1]&LOWER_MASK); y = (mt[kk] & UPPER_MASK) | (mt[kk + 1] & LOWER_MASK);
mt[kk] = mt[kk+(M-N)] ^ (y >> 1) ^ mag01[y & 0x1UL]; mt[kk] = mt[kk + (M - N)] ^ (y >> 1) ^ mag01[y & 0x1UL];
} }
y = (mt[N-1]&UPPER_MASK)|(mt[0]&LOWER_MASK); y = (mt[N - 1] & UPPER_MASK) | (mt[0] & LOWER_MASK);
mt[N-1] = mt[M-1] ^ (y >> 1) ^ mag01[y & 0x1UL]; mt[N - 1] = mt[M - 1] ^ (y >> 1) ^ mag01[y & 0x1UL];
mti = 0; mti = 0;
} }
@@ -112,10 +109,9 @@ static uint32_t genrand_int32(void)
} }
/* generates a random number on [0,1) with 53-bit resolution*/ /* generates a random number on [0,1) with 53-bit resolution*/
static double genrand_res53(void) static double genrand_res53(void) {
{ uint32_t a = genrand_int32() >> 5, b = genrand_int32() >> 6;
uint32_t a=genrand_int32()>>5, b=genrand_int32()>>6; return (a * 67108864.0 + b) * (1.0 / 9007199254740992.0);
return(a*67108864.0+b)*(1.0/9007199254740992.0);
} }
/* These real versions are due to Isaku Wada, 2002/01/09 added */ /* 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 [1, range_end]. If called with two or more arguments, returns a random
integer in the range [range_start, range_end]. 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; lua_Integer min;
uint32_t max; uint32_t max;
switch(lua_gettop(L)) switch (lua_gettop(L)) {
{ default:
default: case 2:
case 2: min = luaL_checkinteger(L, 1);
min = luaL_checkinteger(L, 1); max = (uint32_t)(luaL_checkinteger(L, 2) - min + 1);
max = (uint32_t)(luaL_checkinteger(L, 2) - min + 1); luaL_argcheck(L, max > 0, 2, "interval is empty");
luaL_argcheck(L, max > 0, 2, "interval is empty"); lua_pushinteger(L, min + (lua_Integer)(genrand_int32() % max));
lua_pushinteger(L, min + (lua_Integer)(genrand_int32() % max)); break;
break; case 1:
case 1: max = (uint32_t)luaL_checkinteger(L, 1);
max = (uint32_t)luaL_checkinteger(L, 1); luaL_argcheck(L, 1 <= max, 1, "interval is empty");
luaL_argcheck(L, 1 <= max, 1, "interval is empty"); lua_pushinteger(L, 1 + (lua_Integer)(genrand_int32() % max));
lua_pushinteger(L, 1 + (lua_Integer)(genrand_int32() % max)); break;
break; case 0:
case 0: lua_pushnumber(L, (lua_Number)genrand_res53());
lua_pushnumber(L, (lua_Number)genrand_res53()); break;
break;
} }
return 1; 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 Returns a string which can later be passed to math.randomseed() to restore
the random number generator to its current state. 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*)mt, N * 4);
lua_pushlstring(L, (const char*)&mti, 2); lua_pushlstring(L, (const char*)&mti, 2);
lua_concat(L, 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 the random number generator state from a string previously generated from
math.randomdump(). math.randomdump().
*/ */
static int l_randomseed(lua_State *L) static int l_randomseed(lua_State* L) {
{ if (lua_type(L, 1) == LUA_TSTRING) {
if(lua_type(L, 1) == LUA_TSTRING)
{
int i; int i;
size_t len; size_t len;
const char *data = lua_tolstring(L, 1, &len); const char* data = lua_tolstring(L, 1, &len);
if(len != N * 4 + 2) if (len != N * 4 + 2) luaL_argerror(L, 1, "Seed string wrong length");
luaL_argerror(L, 1, "Seed string wrong length"); for (i = 0; i < N; ++i) mt[i] = ((uint32_t*)data)[i];
for(i = 0; i < N; ++i)
mt[i] = ((uint32_t*)data)[i];
mti = *(uint16_t*)(data + len - 2); mti = *(uint16_t*)(data + len - 2);
} } else {
else
{
init_genrand((uint32_t)luaL_checkinteger(L, 1)); init_genrand((uint32_t)luaL_checkinteger(L, 1));
} }
return 0; return 0;
} }
int luaopen_random(lua_State *L) int luaopen_random(lua_State* L) {
{
lua_getglobal(L, "math"); lua_getglobal(L, "math");
lua_pushliteral(L, "random"); lua_pushliteral(L, "random");
lua_pushcfunction(L, l_random); lua_pushcfunction(L, l_random);

View File

@@ -21,307 +21,258 @@ SOFTWARE.
*/ */
#include "run_length_encoder.h" #include "run_length_encoder.h"
#include "persist_lua.h"
#include <new>
#include <algorithm> #include <algorithm>
#include <new>
#include "persist_lua.h"
integer_run_length_encoder::integer_run_length_encoder() integer_run_length_encoder::integer_run_length_encoder() {
{ buffer = nullptr;
buffer = nullptr; output = nullptr;
output = nullptr; clean();
clean();
} }
integer_run_length_encoder::~integer_run_length_encoder() integer_run_length_encoder::~integer_run_length_encoder() { clean(); }
{
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() bool integer_run_length_encoder::initialise(size_t iRecordSize) {
{ clean();
delete[] buffer; 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; delete[] output;
buffer = nullptr; output = pNewOutput;
output = nullptr; output_capacity = iNewSize;
record_size = 0; }
buffer_capacity = 0; size_t iHeader = (iObjSize / record_size - 1) + 8 * (iObjCount - 1);
buffer_size = 0; output[output_size++] = static_cast<uint32_t>(iHeader);
buffer_offset = 0; // Move the object from the buffer to the output
output_capacity = 0; for (size_t i = 0; i < iObjSize; ++i) {
output_size = 0; output[output_size++] = buffer[buffer_offset];
object_size = 0; buffer_offset = (buffer_offset + 1) % buffer_capacity;
object_copies = 0; }
buffer_size -= iObjSize;
return true;
} }
bool integer_run_length_encoder::initialise(size_t iRecordSize) uint32_t* integer_run_length_encoder::get_output(size_t* pCount) const {
{ if (pCount) *pCount = output_size;
clean(); return output;
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) void integer_run_length_encoder::pump_output(
{ lua_persist_writer* pWriter) const {
buffer[(buffer_offset + buffer_size) % buffer_capacity] = iValue; pWriter->write_uint(output_size);
if(++buffer_size == buffer_capacity) for (size_t i = 0; i < output_size; ++i) {
flush(false); pWriter->write_uint(output[i]);
}
} }
void integer_run_length_encoder::finish() integer_run_length_decoder::integer_run_length_decoder() {
{ buffer = nullptr;
if(buffer_size != 0) clean();
flush(true);
} }
void integer_run_length_encoder::flush(bool bAll) integer_run_length_decoder::~integer_run_length_decoder() { clean(); }
{
do void integer_run_length_decoder::clean() {
{ delete[] buffer;
if(object_size == 0) buffer = nullptr;
{ reader = nullptr;
// Decide on the size of the next object input = nullptr;
// Want the object size which gives most object repeats, then for input_end = nullptr;
// two sizes with the same repeat count, the smaller size. reads_remaining = 0;
size_t iBestRepeats = 0; object_copies = 0;
size_t iBestSize = 0; record_size = 0;
size_t iBestOffset = 0; object_index = 0;
for(size_t iNumRecords = 1; iNumRecords <= 8; ++iNumRecords) object_size = 0;
{
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, bool integer_run_length_decoder::initialise(size_t iRecordSize,
size_t iOffset, size_t iObjSize) const lua_persist_reader* pReader) {
{ clean();
iObjIdx1 = buffer_offset + iOffset * record_size + iObjIdx1 * iObjSize;
iObjIdx2 = buffer_offset + iOffset * record_size + iObjIdx2 * iObjSize; buffer = new (std::nothrow) uint32_t[9 * iRecordSize];
for(size_t i = 0; i < iObjSize; ++i) if (!buffer) return false;
{ reader = pReader;
if(buffer[(iObjIdx1 + i) % buffer_capacity] record_size = iRecordSize;
!= buffer[(iObjIdx2 + i) % buffer_capacity]) return pReader->read_uint(reads_remaining);
{ }
return false;
} 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) uint32_t iValue = buffer[object_index];
{ if (++object_index == object_size) {
// 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<uint32_t>(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;
object_index = 0; object_index = 0;
object_size = 0; --object_copies;
}
return iValue;
} }
bool integer_run_length_decoder::initialise(size_t iRecordSize, lua_persist_reader *pReader) bool integer_run_length_decoder::is_finished() const {
{ if (reader)
clean(); return reads_remaining == 0 && object_copies == 0;
else
buffer = new (std::nothrow) uint32_t[9 * iRecordSize]; return input == input_end && object_copies == 0;
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;
} }

View File

@@ -39,96 +39,94 @@ class lua_persist_writer;
* Object - One or more records. Each object has an associated repeat * Object - One or more records. Each object has an associated repeat
count. count.
*/ */
class integer_run_length_encoder class integer_run_length_encoder {
{ public:
public: integer_run_length_encoder();
integer_run_length_encoder(); ~integer_run_length_encoder();
~integer_run_length_encoder();
//! (Re-)initialise the encoder //! (Re-)initialise the encoder
/*! /*!
Prepares the encoder for accepting a sequence of records. Prepares the encoder for accepting a sequence of records.
\param iRecordSize The number of integers in a record. \param iRecordSize The number of integers in a record.
*/ */
bool initialise(size_t iRecordSize); bool initialise(size_t iRecordSize);
//! Supply the next integer in the input sequence to the encoder //! Supply the next integer in the input sequence to the encoder
void write(uint32_t iValue); void write(uint32_t iValue);
//! Inform the encoder that the input sequence has finished //! Inform the encoder that the input sequence has finished
/*! /*!
finish() must be called prior to getOutput() or pumpOutput(). finish() must be called prior to getOutput() or pumpOutput().
write() must not be called after finish() has been called. write() must not be called after finish() has been called.
*/ */
void finish(); void finish();
uint32_t* get_output(size_t *pCount) const; uint32_t* get_output(size_t* pCount) const;
void pump_output(lua_persist_writer *pWriter) const; void pump_output(lua_persist_writer* pWriter) const;
private: private:
void clean(); void clean();
//! Reduce the amount of data in the buffer //! Reduce the amount of data in the buffer
/*! /*!
\param bAll If true, will reduce buffer_size to zero. \param bAll If true, will reduce buffer_size to zero.
If false, will reduce buffer_size by some amount. If false, will reduce buffer_size by some amount.
*/ */
void flush(bool bAll); void flush(bool bAll);
bool are_ranges_equal(size_t iObjIdx1, size_t iObjIdx2, size_t iOffset, size_t iObjSize) const; bool are_ranges_equal(size_t iObjIdx1, size_t iObjIdx2, size_t iOffset,
bool move_object_to_output(size_t iObjSize, size_t iObjCount); 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 //! A circular fixed-size buffer holding the most recent input
uint32_t* buffer; uint32_t* buffer;
//! A variable-length array holding the output sequence //! A variable-length array holding the output sequence
uint32_t* output; uint32_t* output;
//! The number of integers in a record //! The number of integers in a record
size_t record_size; size_t record_size;
//! The maximum number of integers stored in the buffer //! The maximum number of integers stored in the buffer
size_t buffer_capacity; size_t buffer_capacity;
//! The current number of integers stored in the buffer //! The current number of integers stored in the buffer
size_t buffer_size; size_t buffer_size;
//! The index into buffer of the 1st integer //! The index into buffer of the 1st integer
size_t buffer_offset; size_t buffer_offset;
//! The maximum number of integers storable in the output (before the //! The maximum number of integers storable in the output (before the
//! output array has to be resized). //! output array has to be resized).
size_t output_capacity; size_t output_capacity;
//! The current number of integers stored in the output //! The current number of integers stored in the output
size_t output_size; size_t output_size;
//! The number of integers in the current object (multiple of record size) //! The number of integers in the current object (multiple of record size)
size_t object_size; size_t object_size;
//! The number of copies of the current object already seen and removed //! The number of copies of the current object already seen and removed
//! from the buffer. //! from the buffer.
size_t object_copies; size_t object_copies;
}; };
class integer_run_length_decoder class integer_run_length_decoder {
{ public:
public: integer_run_length_decoder();
integer_run_length_decoder(); ~integer_run_length_decoder();
~integer_run_length_decoder();
bool initialise(size_t iRecordSize, lua_persist_reader *pReader); bool initialise(size_t iRecordSize, lua_persist_reader* pReader);
bool initialise(size_t iRecordSize, const uint32_t *pInput, size_t iCount); bool initialise(size_t iRecordSize, const uint32_t* pInput, size_t iCount);
uint32_t read(); uint32_t read();
bool is_finished() const; bool is_finished() const;
private: private:
void clean(); void clean();
uint32_t* buffer; uint32_t* buffer;
lua_persist_reader* reader; lua_persist_reader* reader;
const uint32_t* input; const uint32_t* input;
union union {
{ const uint32_t* input_end;
const uint32_t* input_end; size_t reads_remaining;
size_t reads_remaining; };
}; size_t object_copies;
size_t object_copies; size_t record_size;
size_t record_size; size_t object_index;
size_t object_index; size_t object_size;
size_t object_size;
}; };
#endif // CORSIX_TH_RLE_H_ #endif // CORSIX_TH_RLE_H_

View File

@@ -21,325 +21,295 @@ SOFTWARE.
*/ */
#include "config.h" #include "config.h"
#include "lua_sdl.h"
#ifdef CORSIX_TH_USE_SDL_MIXER #ifdef CORSIX_TH_USE_SDL_MIXER
#include <SDL_mixer.h>
#include <array>
#include <cstring>
#include "lua_sdl.h"
#include "th_lua.h" #include "th_lua.h"
#include "xmi2mid.h" #include "xmi2mid.h"
#include <SDL_mixer.h>
#ifdef _MSC_VER #ifdef _MSC_VER
#pragma comment(lib, "SDL2_mixer") #pragma comment(lib, "SDL2_mixer")
#endif #endif
#include <cstring>
#include <array>
class music class music {
{ public:
public: Mix_Music* pMusic;
Mix_Music* pMusic;
music() music() { pMusic = nullptr; }
{
pMusic = nullptr; ~music() {
} if (pMusic) {
Mix_FreeMusic(pMusic);
~music() pMusic = nullptr;
{
if(pMusic)
{
Mix_FreeMusic(pMusic);
pMusic = nullptr;
}
} }
}
}; };
namespace { namespace {
void audio_music_over_callback() void audio_music_over_callback() {
{ SDL_Event e;
SDL_Event e; e.type = SDL_USEREVENT_MUSIC_OVER;
e.type = SDL_USEREVENT_MUSIC_OVER; SDL_PushEvent(&e);
SDL_PushEvent(&e);
} }
int l_init(lua_State *L) int l_init(lua_State* L) {
{ constexpr int chunk_size = 2048;
if(Mix_OpenAudio(static_cast<int>(luaL_optinteger(L, 1, MIX_DEFAULT_FREQUENCY)),
MIX_DEFAULT_FORMAT, int err = Mix_OpenAudio(
static_cast<int>(luaL_optinteger(L, 2, MIX_DEFAULT_CHANNELS)), static_cast<int>(luaL_optinteger(L, 1, MIX_DEFAULT_FREQUENCY)),
static_cast<int>(luaL_optinteger(L, 3, 2048)) /* chunk size */) != 0) MIX_DEFAULT_FORMAT,
{ static_cast<int>(luaL_optinteger(L, 2, MIX_DEFAULT_CHANNELS)),
lua_pushboolean(L, 0); static_cast<int>(luaL_optinteger(L, 3, chunk_size)));
lua_pushstring(L, Mix_GetError()); if (err != 0) {
return 2; lua_pushboolean(L, 0);
} lua_pushstring(L, Mix_GetError());
else return 2;
{ } else {
lua_pushboolean(L, 1); lua_pushboolean(L, 1);
Mix_HookMusicFinished(audio_music_over_callback); Mix_HookMusicFinished(audio_music_over_callback);
return 1; return 1;
} }
} }
struct load_music_async_data struct load_music_async_data {
{ lua_State* L;
lua_State* L; Mix_Music* music;
Mix_Music* music; SDL_RWops* rwop;
SDL_RWops* rwop; char* err;
char* err; SDL_Thread* thread;
SDL_Thread* thread;
}; };
int load_music_async_thread(void* arg) int load_music_async_thread(void* arg) {
{ load_music_async_data* async = (load_music_async_data*)arg;
load_music_async_data *async = (load_music_async_data*)arg;
async->music = Mix_LoadMUS_RW(async->rwop, 1); async->music = Mix_LoadMUS_RW(async->rwop, 1);
async->rwop = nullptr; async->rwop = nullptr;
if(async->music == nullptr) if (async->music == nullptr) {
{ size_t iLen = std::strlen(Mix_GetError()) + 1;
size_t iLen = std::strlen(Mix_GetError()) + 1; async->err = (char*)malloc(iLen);
async->err = (char*)malloc(iLen); std::memcpy(async->err, Mix_GetError(), iLen);
std::memcpy(async->err, Mix_GetError(), iLen); }
} SDL_Event e;
SDL_Event e; e.type = SDL_USEREVENT_MUSIC_LOADED;
e.type = SDL_USEREVENT_MUSIC_LOADED; e.user.data1 = arg;
e.user.data1 = arg; SDL_PushEvent(&e);
SDL_PushEvent(&e); return 0;
return 0;
} }
int l_load_music_async(lua_State *L) int l_load_music_async(lua_State* L) {
{ size_t iLength;
size_t iLength; const uint8_t* pData = luaT_checkfile(L, 1, &iLength);
const uint8_t *pData = luaT_checkfile(L, 1, &iLength); luaL_checktype(L, 2, LUA_TFUNCTION);
luaL_checktype(L, 2, LUA_TFUNCTION); SDL_RWops* rwop = SDL_RWFromConstMem(pData, (int)iLength);
SDL_RWops* rwop = SDL_RWFromConstMem(pData, (int)iLength); lua_settop(L, 2);
lua_settop(L, 2);
load_music_async_data *async = luaT_new<load_music_async_data>(L); load_music_async_data* async = luaT_new<load_music_async_data>(L);
lua_pushlightuserdata(L, async); lua_pushlightuserdata(L, async);
lua_pushvalue(L, -2); lua_pushvalue(L, -2);
lua_settable(L, LUA_REGISTRYINDEX); lua_settable(L, LUA_REGISTRYINDEX);
async->L = L; async->L = L;
async->music = nullptr; async->music = nullptr;
async->rwop = rwop; async->rwop = rwop;
async->err = nullptr; async->err = nullptr;
lua_createtable(L, 2, 0); lua_createtable(L, 2, 0);
lua_pushvalue(L, 2); lua_pushvalue(L, 2);
lua_rawseti(L, -2, 1); lua_rawseti(L, -2, 1);
luaT_stdnew<music>(L, luaT_environindex, true); luaT_stdnew<music>(L, luaT_environindex, true);
lua_pushvalue(L, 1); lua_pushvalue(L, 1);
luaT_setenvfield(L, -2, "data"); luaT_setenvfield(L, -2, "data");
lua_rawseti(L, -2, 2); lua_rawseti(L, -2, 2);
lua_settable(L, LUA_REGISTRYINDEX); lua_settable(L, LUA_REGISTRYINDEX);
/* /*
In registry: In registry:
[light userdata async] -> [full userdata async] [light userdata async] -> [full userdata async]
[full userdata async] -> { [full userdata async] -> {
[1] = callback_function, [1] = callback_function,
[2] = empty music_t userdata, [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<music>(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<music>(L, -1);
if(Mix_PlayMusic(pLMusic->pMusic, static_cast<int>(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<struct luaL_Reg, 3> sdl_audiolib {{
{"init", l_init},
{"transcodeXmiToMid", l_transcode_xmi},
{nullptr, nullptr}
}};
constexpr std::array<struct luaL_Reg, 8> 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 New thread will load music, and inform the main loop, which will then
lua_pushvalue(L, 1); 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_pushnil(L);
lua_settable(L, LUA_REGISTRYINDEX); lua_pushstring(L, Mix_GetError());
return 2;
// Callback }
lua_call(L, nargs, 0); music* pLMusic = luaT_stdnew<music>(L, luaT_environindex, true);
pLMusic->pMusic = pMusic;
return 0; lua_pushvalue(L, 1);
luaT_setenvfield(L, -2, "data");
return 1;
} }
int luaopen_sdl_audio(lua_State *L) int l_music_volume(lua_State* L) {
{ lua_Number fValue = luaL_checknumber(L, 1);
lua_newtable(L); fValue = fValue * (lua_Number)MIX_MAX_VOLUME;
luaT_setfuncs(L, sdl_audiolib.data()); int iVolume = (int)(fValue + 0.5);
lua_pushboolean(L, 1); if (iVolume < 0) {
lua_setfield(L, -2, "loaded"); iVolume = 0;
} else if (iVolume > MIX_MAX_VOLUME) {
lua_createtable(L, 0, 2); iVolume = MIX_MAX_VOLUME;
lua_pushvalue(L, -1); }
lua_replace(L, luaT_environindex); Mix_VolumeMusic(iVolume);
lua_pushvalue(L, luaT_environindex); return 0;
luaT_pushcclosure(L, luaT_stdgc<music, luaT_environindex>, 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 l_play_music(lua_State* L) {
music* pLMusic = luaT_testuserdata<music>(L, -1);
int luaopen_sdl_audio(lua_State *L) int err = Mix_PlayMusic(pLMusic->pMusic,
{ static_cast<int>(luaL_optinteger(L, 2, 1)));
lua_newtable(L); if (err != 0) {
lua_pushboolean(L, 0); lua_pushnil(L);
lua_setfield(L, -2, "loaded"); lua_pushstring(L, Mix_GetError());
return 2;
return 1; }
lua_pushboolean(L, 1);
return 1;
} }
int l_load_music_async_callback(lua_State *L) int l_pause_music(lua_State* L) {
{ Mix_PauseMusic();
return 0; 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<struct luaL_Reg, 3> sdl_audiolib{
{{"init", l_init},
{"transcodeXmiToMid", l_transcode_xmi},
{nullptr, nullptr}}};
constexpr std::array<struct luaL_Reg, 8> 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<music, luaT_environindex>, 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

View File

@@ -21,386 +21,350 @@ SOFTWARE.
*/ */
#include "config.h" #include "config.h"
#include <array>
#include <cstdio>
#include <cstring>
#include "lua_sdl.h" #include "lua_sdl.h"
#include "th_lua.h" #include "th_lua.h"
#include <cstring>
#include <cstdio>
#include <array>
namespace { namespace {
int l_init(lua_State *L) int l_init(lua_State* L) {
{ Uint32 flags = 0;
Uint32 flags = 0; int i;
int i; int argc = lua_gettop(L);
int argc = lua_gettop(L); for (i = 1; i <= argc; ++i) {
for(i = 1; i <= argc; ++i) const char* s = luaL_checkstring(L, i);
{ if (std::strcmp(s, "video") == 0)
const char* s = luaL_checkstring(L, i); flags |= SDL_INIT_VIDEO;
if(std::strcmp(s, "video") == 0) else if (std::strcmp(s, "audio") == 0)
flags |= SDL_INIT_VIDEO; flags |= SDL_INIT_AUDIO;
else if(std::strcmp(s, "audio") == 0) else if (std::strcmp(s, "timer") == 0)
flags |= SDL_INIT_AUDIO; flags |= SDL_INIT_TIMER;
else if(std::strcmp(s, "timer") == 0) else if (std::strcmp(s, "*") == 0)
flags |= SDL_INIT_TIMER; flags |= SDL_INIT_EVERYTHING;
else if(std::strcmp(s, "*") == 0) else
flags |= SDL_INIT_EVERYTHING; luaL_argerror(L, i, "Expected SDL part name");
else }
luaL_argerror(L, i, "Expected SDL part name"); if (SDL_Init(flags) != 0) {
} std::fprintf(stderr, "SDL_Init failed: %s\n", SDL_GetError());
if(SDL_Init(flags) != 0) lua_pushboolean(L, 0);
{
std::fprintf(stderr, "SDL_Init failed: %s\n", SDL_GetError());
lua_pushboolean(L, 0);
return 1;
}
lua_pushboolean(L, 1);
return 1; return 1;
}
lua_pushboolean(L, 1);
return 1;
} }
Uint32 timer_frame_callback(Uint32 interval, void *param) Uint32 timer_frame_callback(Uint32 interval, void* param) {
{ SDL_Event e;
SDL_Event e; e.type = SDL_USEREVENT_TICK;
e.type = SDL_USEREVENT_TICK; SDL_PushEvent(&e);
SDL_PushEvent(&e); return interval;
return interval;
} }
class fps_ctrl class fps_ctrl {
{ public:
public: bool limit_fps;
bool limit_fps; bool track_fps;
bool track_fps;
int q_front; int q_front;
int q_back; int q_back;
int frame_count; int frame_count;
Uint32 frame_time[4096]; std::array<Uint32, 4096> frame_time;
void init() void init() {
{ limit_fps = true;
limit_fps = true; track_fps = true;
track_fps = true; q_front = 0;
q_front = 0; q_back = 0;
q_back = 0; frame_count = 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() if (now < 1000) {
{ now = 0;
Uint32 now = SDL_GetTicks(); } else {
frame_time[q_front] = now; now -= 1000;
q_front = (q_front + 1) % static_cast<int>((sizeof(frame_time) / sizeof(*frame_time)));
if(q_front == q_back)
q_back = (q_back + 1) % static_cast<int>((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<int>((sizeof(frame_time) / sizeof(*frame_time)));
}
} }
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) void l_push_modifiers_table(lua_State* L, Uint16 mod) {
{ lua_newtable(L);
lua_newtable(L); if ((mod & KMOD_SHIFT) != 0) {
if ((mod & KMOD_SHIFT) != 0) luaT_pushtablebool(L, "shift", true);
{ }
luaT_pushtablebool(L, "shift", true); if ((mod & KMOD_ALT) != 0) {
} luaT_pushtablebool(L, "alt", true);
if ((mod & KMOD_ALT) != 0) }
{ if ((mod & KMOD_CTRL) != 0) {
luaT_pushtablebool(L, "alt", true); luaT_pushtablebool(L, "ctrl", true);
} }
if ((mod & KMOD_CTRL) != 0) if ((mod & KMOD_GUI) != 0) {
{ luaT_pushtablebool(L, "gui", true);
luaT_pushtablebool(L, "ctrl", true); }
} if ((mod & KMOD_NUM) != 0) {
if ((mod & KMOD_GUI) != 0) luaT_pushtablebool(L, "numlockactive", true);
{ }
luaT_pushtablebool(L, "gui", true);
}
if ((mod & KMOD_NUM) != 0)
{
luaT_pushtablebool(L, "numlockactive", true);
}
} }
int l_get_key_modifiers(lua_State *L) int l_get_key_modifiers(lua_State* L) {
{ l_push_modifiers_table(L, SDL_GetModState());
l_push_modifiers_table(L, SDL_GetModState()); return 1;
return 1;
} }
int l_mainloop(lua_State *L) int l_mainloop(lua_State* L) {
{ luaL_checktype(L, 1, LUA_TTHREAD);
luaL_checktype(L, 1, LUA_TTHREAD); lua_State* dispatcher = lua_tothread(L, 1);
lua_State *dispatcher = lua_tothread(L, 1);
fps_ctrl *fps_control = (fps_ctrl*)lua_touserdata(L, luaT_upvalueindex(1)); fps_ctrl* fps_control = (fps_ctrl*)lua_touserdata(L, luaT_upvalueindex(1));
SDL_TimerID timer = SDL_AddTimer(30, timer_frame_callback, nullptr); SDL_TimerID timer = SDL_AddTimer(30, timer_frame_callback, nullptr);
SDL_Event e; SDL_Event e;
while(SDL_WaitEvent(&e) != 0) while (SDL_WaitEvent(&e) != 0) {
{ bool do_frame = false;
bool do_frame = false; bool do_timer = false;
bool do_timer = false; do {
do int nargs;
{ switch (e.type) {
int nargs; case SDL_QUIT:
switch(e.type) goto leave_loop;
{ case SDL_KEYDOWN:
case SDL_QUIT: lua_pushliteral(dispatcher, "keydown");
goto leave_loop; lua_pushstring(dispatcher, SDL_GetKeyName(e.key.keysym.sym));
case SDL_KEYDOWN: l_push_modifiers_table(dispatcher, e.key.keysym.mod);
lua_pushliteral(dispatcher, "keydown"); lua_pushboolean(dispatcher, e.key.repeat != 0);
lua_pushstring(dispatcher, SDL_GetKeyName(e.key.keysym.sym)); nargs = 4;
l_push_modifiers_table(dispatcher, e.key.keysym.mod); break;
lua_pushboolean(dispatcher, e.key.repeat != 0); case SDL_KEYUP:
nargs = 4; lua_pushliteral(dispatcher, "keyup");
break; lua_pushstring(dispatcher, SDL_GetKeyName(e.key.keysym.sym));
case SDL_KEYUP: nargs = 2;
lua_pushliteral(dispatcher, "keyup"); break;
lua_pushstring(dispatcher, SDL_GetKeyName(e.key.keysym.sym)); case SDL_TEXTINPUT:
nargs = 2; lua_pushliteral(dispatcher, "textinput");
break; lua_pushstring(dispatcher, e.text.text);
case SDL_TEXTINPUT: nargs = 2;
lua_pushliteral(dispatcher, "textinput"); break;
lua_pushstring(dispatcher, e.text.text); case SDL_TEXTEDITING:
nargs = 2; lua_pushliteral(dispatcher, "textediting");
break; lua_pushstring(dispatcher, e.edit.text);
case SDL_TEXTEDITING: lua_pushinteger(dispatcher, e.edit.start);
lua_pushliteral(dispatcher, "textediting"); lua_pushinteger(dispatcher, e.edit.length);
lua_pushstring(dispatcher, e.edit.text); nargs = 4;
lua_pushinteger(dispatcher, e.edit.start); break;
lua_pushinteger(dispatcher, e.edit.length); case SDL_MOUSEBUTTONDOWN:
nargs = 4; lua_pushliteral(dispatcher, "buttondown");
break; lua_pushinteger(dispatcher, e.button.button);
case SDL_MOUSEBUTTONDOWN: lua_pushinteger(dispatcher, e.button.x);
lua_pushliteral(dispatcher, "buttondown"); lua_pushinteger(dispatcher, e.button.y);
lua_pushinteger(dispatcher, e.button.button); nargs = 4;
lua_pushinteger(dispatcher, e.button.x); break;
lua_pushinteger(dispatcher, e.button.y); case SDL_MOUSEBUTTONUP:
nargs = 4; lua_pushliteral(dispatcher, "buttonup");
break; lua_pushinteger(dispatcher, e.button.button);
case SDL_MOUSEBUTTONUP: lua_pushinteger(dispatcher, e.button.x);
lua_pushliteral(dispatcher, "buttonup"); lua_pushinteger(dispatcher, e.button.y);
lua_pushinteger(dispatcher, e.button.button); nargs = 4;
lua_pushinteger(dispatcher, e.button.x); break;
lua_pushinteger(dispatcher, e.button.y); case SDL_MOUSEWHEEL:
nargs = 4; lua_pushliteral(dispatcher, "mousewheel");
break; lua_pushinteger(dispatcher, e.wheel.x);
case SDL_MOUSEWHEEL: lua_pushinteger(dispatcher, e.wheel.y);
lua_pushliteral(dispatcher, "mousewheel"); nargs = 3;
lua_pushinteger(dispatcher, e.wheel.x); break;
lua_pushinteger(dispatcher, e.wheel.y); case SDL_MOUSEMOTION:
nargs = 3; lua_pushliteral(dispatcher, "motion");
break; lua_pushinteger(dispatcher, e.motion.x);
case SDL_MOUSEMOTION: lua_pushinteger(dispatcher, e.motion.y);
lua_pushliteral(dispatcher, "motion"); lua_pushinteger(dispatcher, e.motion.xrel);
lua_pushinteger(dispatcher, e.motion.x); lua_pushinteger(dispatcher, e.motion.yrel);
lua_pushinteger(dispatcher, e.motion.y); nargs = 5;
lua_pushinteger(dispatcher, e.motion.xrel); break;
lua_pushinteger(dispatcher, e.motion.yrel); case SDL_MULTIGESTURE:
nargs = 5; lua_pushliteral(dispatcher, "multigesture");
break; lua_pushinteger(dispatcher, e.mgesture.numFingers);
case SDL_MULTIGESTURE: lua_pushnumber(dispatcher, e.mgesture.dTheta);
lua_pushliteral(dispatcher, "multigesture"); lua_pushnumber(dispatcher, e.mgesture.dDist);
lua_pushinteger(dispatcher, e.mgesture.numFingers); lua_pushnumber(dispatcher, e.mgesture.x);
lua_pushnumber(dispatcher, e.mgesture.dTheta); lua_pushnumber(dispatcher, e.mgesture.y);
lua_pushnumber(dispatcher, e.mgesture.dDist); nargs = 6;
lua_pushnumber(dispatcher, e.mgesture.x); break;
lua_pushnumber(dispatcher, e.mgesture.y); case SDL_WINDOWEVENT:
nargs = 6; switch (e.window.event) {
break; case SDL_WINDOWEVENT_FOCUS_GAINED:
case SDL_WINDOWEVENT: lua_pushliteral(dispatcher, "active");
switch (e.window.event) { lua_pushinteger(dispatcher, 1);
case SDL_WINDOWEVENT_FOCUS_GAINED: nargs = 2;
lua_pushliteral(dispatcher, "active"); break;
lua_pushinteger(dispatcher, 1); case SDL_WINDOWEVENT_FOCUS_LOST:
nargs = 2; lua_pushliteral(dispatcher, "active");
break; lua_pushinteger(dispatcher, 0);
case SDL_WINDOWEVENT_FOCUS_LOST: nargs = 2;
lua_pushliteral(dispatcher, "active"); break;
lua_pushinteger(dispatcher, 0); case SDL_WINDOWEVENT_SIZE_CHANGED:
nargs = 2; lua_pushliteral(dispatcher, "window_resize");
break; lua_pushinteger(dispatcher, e.window.data1);
case SDL_WINDOWEVENT_SIZE_CHANGED: lua_pushinteger(dispatcher, e.window.data2);
lua_pushliteral(dispatcher, "window_resize"); nargs = 3;
lua_pushinteger(dispatcher, e.window.data1); break;
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<int*>(e.user.data1)));
nargs = 2;
break;
default: default:
nargs = 0; nargs = 0;
break; break;
} }
if(nargs != 0) break;
{ case SDL_USEREVENT_MUSIC_OVER:
if(luaT_resume(dispatcher, dispatcher, nargs) != LUA_YIELD) lua_pushliteral(dispatcher, "music_over");
{ nargs = 1;
goto leave_loop; break;
} case SDL_USEREVENT_MUSIC_LOADED:
do_frame = do_frame || (lua_toboolean(dispatcher, 1) != 0); if (luaT_cpcall(L, (lua_CFunction)l_load_music_async_callback,
lua_settop(dispatcher, 0); e.user.data1)) {
} SDL_RemoveTimer(timer);
} while(SDL_PollEvent(&e) != 0); lua_pushliteral(L, "callback");
if(do_timer) return 2;
{ }
lua_pushliteral(dispatcher, "timer"); nargs = 0;
if(luaT_resume(dispatcher, dispatcher, 1) != LUA_YIELD) break;
{ case SDL_USEREVENT_TICK:
break; do_timer = true;
} nargs = 0;
do_frame = do_frame || (lua_toboolean(dispatcher, 1) != 0); break;
lua_settop(dispatcher, 0); 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<int*>(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_frame = do_frame || (lua_toboolean(dispatcher, 1) != 0);
{ lua_settop(dispatcher, 0);
do }
{ } while (SDL_PollEvent(&e) != 0);
if(fps_control->track_fps) if (do_timer) {
{ lua_pushliteral(dispatcher, "timer");
fps_control->count_frame(); if (luaT_resume(dispatcher, dispatcher, 1) != LUA_YIELD) {
} break;
lua_pushliteral(dispatcher, "frame"); }
if(luaT_resume(dispatcher, dispatcher, 1) != LUA_YIELD) do_frame = do_frame || (lua_toboolean(dispatcher, 1) != 0);
{ lua_settop(dispatcher, 0);
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);
} }
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: leave_loop:
SDL_RemoveTimer(timer); SDL_RemoveTimer(timer);
int n = lua_gettop(dispatcher); int n = lua_gettop(dispatcher);
if(lua_status(dispatcher) >= LUA_ERRRUN) if (lua_status(dispatcher) >= LUA_ERRRUN) {
{ n = 1;
n = 1; }
} lua_checkstack(L, n);
lua_checkstack(L, n); lua_xmove(dispatcher, L, n);
lua_xmove(dispatcher, L, n); return n;
return n;
} }
int l_track_fps(lua_State *L) int l_track_fps(lua_State* L) {
{ fps_ctrl* ctrl = (fps_ctrl*)lua_touserdata(L, luaT_upvalueindex(1));
fps_ctrl *ctrl = (fps_ctrl*)lua_touserdata(L, luaT_upvalueindex(1)); ctrl->track_fps = lua_isnone(L, 1) ? true : (lua_toboolean(L, 1) != 0);
ctrl->track_fps = lua_isnone(L, 1) ? true : (lua_toboolean(L, 1) != 0); return 0;
return 0;
} }
int l_limit_fps(lua_State *L) int l_limit_fps(lua_State* L) {
{ fps_ctrl* ctrl = (fps_ctrl*)lua_touserdata(L, luaT_upvalueindex(1));
fps_ctrl *ctrl = (fps_ctrl*)lua_touserdata(L, luaT_upvalueindex(1)); ctrl->limit_fps = lua_isnone(L, 1) ? true : (lua_toboolean(L, 1) != 0);
ctrl->limit_fps = lua_isnone(L, 1) ? true : (lua_toboolean(L, 1) != 0); return 0;
return 0;
} }
int l_get_fps(lua_State *L) int l_get_fps(lua_State* L) {
{ fps_ctrl* ctrl = (fps_ctrl*)lua_touserdata(L, luaT_upvalueindex(1));
fps_ctrl *ctrl = (fps_ctrl*)lua_touserdata(L, luaT_upvalueindex(1)); if (ctrl->track_fps) {
if(ctrl->track_fps) lua_pushinteger(L, ctrl->frame_count);
{ } else {
lua_pushinteger(L, ctrl->frame_count); lua_pushnil(L);
} }
else return 1;
{
lua_pushnil(L);
}
return 1;
} }
int l_get_ticks(lua_State *L) int l_get_ticks(lua_State* L) {
{ lua_pushinteger(L, (lua_Integer)SDL_GetTicks());
lua_pushinteger(L, (lua_Integer)SDL_GetTicks()); return 1;
return 1;
} }
constexpr std::array<luaL_Reg, 4> sdllib {{ constexpr std::array<luaL_Reg, 4> sdllib{
{"init", l_init}, {{"init", l_init},
{"getTicks", l_get_ticks}, {"getTicks", l_get_ticks},
{"getKeyModifiers", l_get_key_modifiers}, {"getKeyModifiers", l_get_key_modifiers},
{nullptr, nullptr} {nullptr, nullptr}}};
}};
constexpr std::array<luaL_Reg, 5> sdllib_with_upvalue {{ constexpr std::array<luaL_Reg, 5> sdllib_with_upvalue{
{"mainloop", l_mainloop}, {{"mainloop", l_mainloop},
{"getFPS", l_get_fps}, {"getFPS", l_get_fps},
{"trackFPS", l_track_fps}, {"trackFPS", l_track_fps},
{"limitFPS", l_limit_fps}, {"limitFPS", l_limit_fps},
{nullptr, nullptr} {nullptr, nullptr}}};
}};
inline void load_extra(lua_State *L, const char *name, lua_CFunction fn) inline void load_extra(lua_State* L, const char* name, lua_CFunction fn) {
{ luaT_pushcfunction(L, fn);
luaT_pushcfunction(L, fn); lua_call(L, 0, 1);
lua_call(L, 0, 1); lua_setfield(L, -2, name);
lua_setfield(L, -2, name);
} }
} // namespace } // namespace
int luaopen_sdl_audio(lua_State *L); int luaopen_sdl_audio(lua_State* L);
int luaopen_sdl_wm(lua_State *L); int luaopen_sdl_wm(lua_State* L);
int luaopen_sdl(lua_State *L) int luaopen_sdl(lua_State* L) {
{ fps_ctrl* ctrl = (fps_ctrl*)lua_newuserdata(L, sizeof(fps_ctrl));
fps_ctrl* ctrl = (fps_ctrl*)lua_newuserdata(L, sizeof(fps_ctrl)); ctrl->init();
ctrl->init(); luaT_register(L, "sdl", sdllib);
luaT_register(L, "sdl", sdllib); for (auto reg = sdllib_with_upvalue.begin(); reg->name; ++reg) {
for (auto reg = sdllib_with_upvalue.begin(); reg->name; ++reg) lua_pushvalue(L, -2);
{ luaT_pushcclosure(L, reg->func, 1);
lua_pushvalue(L, -2); lua_setfield(L, -2, reg->name);
luaT_pushcclosure(L, reg->func, 1); }
lua_setfield(L, -2, reg->name);
}
load_extra(L, "audio", luaopen_sdl_audio); load_extra(L, "audio", luaopen_sdl_audio);
load_extra(L, "wm", luaopen_sdl_wm); load_extra(L, "wm", luaopen_sdl_wm);
return 1; return 1;
} }

View File

@@ -21,23 +21,22 @@ SOFTWARE.
*/ */
#include "config.h" #include "config.h"
#include "th_lua.h"
#include "lua_sdl.h" #include "lua_sdl.h"
#include "th_lua.h"
#ifdef CORSIX_TH_USE_WIN32_SDK #ifdef CORSIX_TH_USE_WIN32_SDK
#include <windows.h>
#include <SDL_syswm.h> #include <SDL_syswm.h>
#include <windows.h>
#include "../resource.h" #include "../resource.h"
#endif #endif
#include <array> #include <array>
namespace { namespace {
int l_set_icon_win32(lua_State *L) int l_set_icon_win32(lua_State* L) {
{ // Hack to set the window icon from the EXE resource under Windows.
// Hack to set the window icon from the EXE resource under Windows. // Does nothing (and returns false) on other platforms.
// Does nothing (and returns false) on other platforms.
lua_pushboolean(L, 0); lua_pushboolean(L, 0);
#if 0 #if 0
// XXX: Doesn't work any more, since window is inside renderer. Move to renderer. // XXX: Doesn't work any more, since window is inside renderer. Move to renderer.
SDL_SysWMinfo oWindowInfo; SDL_SysWMinfo oWindowInfo;
@@ -53,27 +52,24 @@ int l_set_icon_win32(lua_State *L)
lua_pushboolean(L, 1); lua_pushboolean(L, 1);
} }
#endif #endif
return 1; return 1;
} }
int l_show_cursor(lua_State *L) int l_show_cursor(lua_State* L) {
{ SDL_ShowCursor(lua_toboolean(L, 1));
SDL_ShowCursor(lua_toboolean(L, 1)); return 0;
return 0;
} }
constexpr std::array<struct luaL_Reg, 3> sdl_wmlib {{ constexpr std::array<struct luaL_Reg, 3> sdl_wmlib{
{"setIconWin32", l_set_icon_win32}, {{"setIconWin32", l_set_icon_win32},
{"showCursor", l_show_cursor}, {"showCursor", l_show_cursor},
{nullptr, nullptr} {nullptr, nullptr}}};
}};
} // namespace } // namespace
int luaopen_sdl_wm(lua_State *L) int luaopen_sdl_wm(lua_State* L) {
{ lua_newtable(L);
lua_newtable(L); luaT_setfuncs(L, sdl_wmlib.data());
luaT_setfuncs(L, sdl_wmlib.data());
return 1; return 1;
} }

View File

@@ -20,35 +20,28 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE. SOFTWARE.
*/ */
#include "config.h"
#include "th.h" #include "th.h"
#include "config.h"
#include <cstring> #include <cstring>
#include <stdexcept> #include <stdexcept>
link_list::link_list() link_list::link_list() {
{ drawing_layer = 0;
drawing_layer = 0; prev = nullptr;
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; next = nullptr;
} }
prev = 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;
} }
#include "cp437_table.h" #include "cp437_table.h"
@@ -56,177 +49,141 @@ void link_list::remove_from_list()
namespace { namespace {
void utf8encode(uint8_t*& sOut, uint32_t iCodepoint) void utf8encode(uint8_t*& sOut, uint32_t iCodepoint) {
{ if (iCodepoint <= 0x7F) {
if(iCodepoint <= 0x7F) *sOut = static_cast<char>(iCodepoint);
{ ++sOut;
*sOut = static_cast<char>(iCodepoint); } else if (iCodepoint <= 0x7FF) {
uint8_t cSextet = iCodepoint & 0x3F;
iCodepoint >>= 6;
sOut[0] = static_cast<uint8_t>(0xC0 + iCodepoint);
sOut[1] = static_cast<uint8_t>(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<uint8_t>(0xE0 + iCodepoint);
sOut[1] = static_cast<uint8_t>(0x80 + cSextet1);
sOut[2] = static_cast<uint8_t>(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<uint8_t>(0xF0 + iCodepoint);
sOut[1] = static_cast<uint8_t>(0x80 + cSextet1);
sOut[2] = static_cast<uint8_t>(0x80 + cSextet2);
sOut[3] = static_cast<uint8_t>(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;
*sOut = cChar2;
++sOut;
}
} }
else if(iCodepoint <= 0x7FF) } while (cChar1 != 0);
{
uint8_t cSextet = iCodepoint & 0x3F;
iCodepoint >>= 6;
sOut[0] = static_cast<uint8_t>(0xC0 + iCodepoint);
sOut[1] = static_cast<uint8_t>(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<uint8_t>(0xE0 + iCodepoint);
sOut[1] = static_cast<uint8_t>(0x80 + cSextet1);
sOut[2] = static_cast<uint8_t>(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<uint8_t>(0xF0 + iCodepoint);
sOut[1] = static_cast<uint8_t>(0x80 + cSextet1);
sOut[2] = static_cast<uint8_t>(0x80 + cSextet2);
sOut[3] = static_cast<uint8_t>(0x80 + cSextet3);
sOut += 4;
}
} }
void CopyStringCP437(const uint8_t*& sIn, uint8_t*& sOut) } // namespace
{
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) 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");
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);
}
} // namespace size_t iSectionCount = *reinterpret_cast<const uint16_t*>(data);
size_t iHeaderLength = (iSectionCount + 1) * 2;
th_string_list::th_string_list(const uint8_t* data, size_t length) if (length < iHeaderLength)
{ throw std::invalid_argument("iDataLength must be larger than the header");
if(length < 2)
throw std::invalid_argument("length must be 2 or larger");
size_t iSectionCount = *reinterpret_cast<const uint16_t*>(data); size_t iStringDataLength = length - iHeaderLength;
size_t iHeaderLength = (iSectionCount + 1) * 2; const uint8_t* sStringData = data + iHeaderLength;
const uint8_t* sDataEnd = sStringData + iStringDataLength;
if(length < iHeaderLength) // Determine whether the encoding is CP437 or GB2312 (CP936).
throw std::invalid_argument("iDataLength must be larger than the header"); // 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; // String buffer sized to accept the largest possible reencoding of the
const uint8_t *sStringData = data + iHeaderLength; // characters interpreted as CP936 or CP437 (2 bytes per character).
const uint8_t *sDataEnd = sStringData + iStringDataLength; string_buffer.resize(iStringDataLength * 2 + 2);
// Determine whether the encoding is CP437 or GB2312 (CP936). uint8_t* sDataOut = string_buffer.data();
// The range of bytes 0xB0 through 0xDF are box drawing characters in CP437 sections.resize(iSectionCount);
// which shouldn't occur much (if ever) in TH strings, whereas they are for (size_t i = 0; i < iSectionCount; ++i) {
// commonly used in GB2312 encoding. We use 10% as a threshold. size_t section_size = reinterpret_cast<const uint16_t*>(data)[i + 1];
size_t iBCDCount = 0; sections[i].reserve(section_size);
for(size_t i = 0; i < iStringDataLength; ++i) for (size_t j = 0; j < section_size; ++j) {
{ sections[i].push_back(reinterpret_cast<char*>(sDataOut));
if(0xB0 <= sStringData[i] && sStringData[i] <= 0xDF) if (sStringData != sDataEnd) {
++iBCDCount; fnCopyString(sStringData, sDataOut);
}
} }
void (*fnCopyString)(const uint8_t*&, uint8_t*&); }
if(iBCDCount * 10 >= iStringDataLength) // Terminate final string with nil character
fnCopyString = CopyStringCP936; *sDataOut = 0;
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<const uint16_t*>(data)[i + 1];
sections[i].reserve(section_size);
for(size_t j = 0; j < section_size; ++j)
{
sections[i].push_back(reinterpret_cast<char*>(sDataOut));
if(sStringData != sDataEnd)
{
fnCopyString(sStringData, sDataOut);
}
}
}
// 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() size_t th_string_list::get_section_count() { return sections.size(); }
{
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) const char* th_string_list::get_string(size_t section, size_t index) {
{ if (index < get_section_size(section)) {
return section < sections.size() ? sections[section].size() : 0; 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;
} }

View File

@@ -26,70 +26,69 @@ SOFTWARE.
#include <vector> #include <vector>
//! Generic linked list class (for inheriting from) //! Generic linked list class (for inheriting from)
class link_list class link_list {
{ public:
public: link_list();
link_list() ; ~link_list();
~link_list();
link_list* prev; link_list* prev;
link_list* next; link_list* next;
void remove_from_list(); void remove_from_list();
// TODO: drawing layer doesn't belong in a generic link list. // TODO: drawing layer doesn't belong in a generic link list.
int get_drawing_layer() {return drawing_layer;} int get_drawing_layer() { return drawing_layer; }
void set_drawing_layer(int layer) {drawing_layer = layer;} void set_drawing_layer(int layer) { drawing_layer = layer; }
private: private:
int drawing_layer; int drawing_layer;
}; };
//! \brief Theme Hospital localised string list //! \brief Theme Hospital localised string list
//! //!
//! Presents Theme Hospital strings by section and index. //! Presents Theme Hospital strings by section and index.
class th_string_list class th_string_list {
{ public:
public: //! Construct an instance of string_list from the given data
//! Construct an instance of string_list from the given data //! from a Theme Hosptial string file. The format of the data is
//! from a Theme Hosptial string file. The format of the data is //! described at:
//! described at: //! https://github.com/alexandergitter/theme-hospital-spec/blob/master/format-specification.md#strings
//! https://github.com/alexandergitter/theme-hospital-spec/blob/master/format-specification.md#strings //!
//! //! \param data A pointer to the raw data
//! \param data A pointer to the raw data //! \param length The size of the data
//! \param length The size of the data th_string_list(const uint8_t* data, size_t length);
th_string_list(const uint8_t* data, size_t length);
// Delete default constructors and assignment operators. They // Delete default constructors and assignment operators. They
// can be implemented properly later if they are needed but // can be implemented properly later if they are needed but
// for now they are unneeded so it is safer to remove them. // for now they are unneeded so it is safer to remove them.
th_string_list() = delete; th_string_list() = delete;
th_string_list(const th_string_list &) = delete; th_string_list(const th_string_list&) = delete;
th_string_list(th_string_list &&) = delete; th_string_list(th_string_list&&) = delete;
th_string_list& operator= (const th_string_list &) = delete; th_string_list& operator=(const th_string_list&) = delete;
th_string_list&& operator= (th_string_list &&) = delete; th_string_list&& operator=(th_string_list&&) = delete;
~th_string_list(); ~th_string_list();
//! Get the number of sections in the string list //! Get the number of sections in the string list
size_t get_section_count(); size_t get_section_count();
//! Get the number of strings in a section of the string list //! Get the number of strings in a section of the string list
size_t get_section_size(size_t section); size_t get_section_size(size_t section);
//! Get a string from the string list //! Get a string from the string list
/*! /*!
@param section Section index in range [0, getSectionCount() - 1] @param section Section index in range [0, getSectionCount() - 1]
@param index String index in range [0, getSectionSize(iSection) - 1] @param index String index in range [0, getSectionSize(iSection) - 1]
@return nullptr if the index is invalid, otherwise a UTF-8 encoded string. @return nullptr if the index is invalid, otherwise a UTF-8 encoded
*/ string.
const char* get_string(size_t section, size_t index); */
const char* get_string(size_t section, size_t index);
private: private:
//! Section information //! Section information
std::vector<std::vector<const char *>> sections; std::vector<std::vector<const char*>> sections;
//! Memory block containing all the actual strings utf-8 encoded //! Memory block containing all the actual strings utf-8 encoded
std::vector<uint8_t> string_buffer; std::vector<uint8_t> string_buffer;
}; };
/** /**
@@ -99,17 +98,16 @@ private:
* @param bytes A pointer to the first of 4 sequential bytes in memory making * @param bytes A pointer to the first of 4 sequential bytes in memory making
* up the uint32. * up the uint32.
*/ */
inline uint32_t bytes_to_uint32_le(const uint8_t* bytes) inline uint32_t bytes_to_uint32_le(const uint8_t* bytes) {
{ uint32_t res = bytes[3];
uint32_t res = bytes[3]; res <<= 8;
res <<= 8; res |= bytes[2];
res |= bytes[2]; res <<= 8;
res <<= 8; res |= bytes[1];
res |= bytes[1]; res <<= 8;
res <<= 8; res |= bytes[0];
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 * @param bytes A pointer to the first of 2 sequential bytes in memory making
* up the uint16. * up the uint16.
*/ */
inline uint16_t bytes_to_uint16_le(const uint8_t* bytes) inline uint16_t bytes_to_uint16_le(const uint8_t* bytes) {
{ uint16_t res = bytes[1];
uint16_t res = bytes[1]; res <<= 8;
res <<= 8; res |= bytes[0];
res |= bytes[0];
return res; return res;
} }
#endif // CORSIX_TH_TH_H_ #endif // CORSIX_TH_TH_H_

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -28,129 +28,131 @@ SOFTWARE.
#include FT_FREETYPE_H #include FT_FREETYPE_H
#endif #endif
class render_target;
class sprite_sheet;
enum class text_alignment { enum class text_alignment {
left = 0, left = 0,
center = 1, center = 1,
right = 2, right = 2,
}; };
/** Structure for the bounds of a text string that is rendered to the screen. */ /** Structure for the bounds of a text string that is rendered to the screen. */
struct text_layout struct text_layout {
{ //! Number of rows the rendered text spans
//! Number of rows the rendered text spans int row_count;
int row_count;
//! Left X-coordinate for the start of the text //! Left X-coordinate for the start of the text
int start_x; int start_x;
//! Right X-coordinate for the right part of the last letter rendered //! Right X-coordinate for the right part of the last letter rendered
int end_x; int end_x;
//! Top Y-coordinate for the start of the text //! Top Y-coordinate for the start of the text
int start_y; int start_y;
//! Bottom Y-coordinate for the end of the text //! Bottom Y-coordinate for the end of the text
int end_y; int end_y;
//! Width of the widest line in the text //! Width of the widest line in the text
int width; int width;
}; };
class font class font {
{ public:
public: virtual ~font() = default;
virtual ~font() = default;
//! Get the size of drawn text. //! Get the size of drawn text.
/*! /*!
If iMaxWidth is specified the text will wrap, so that the height can 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. 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 @param sMessage A UTF-8 encoded string containing a single line of text
to measure the width and height of. to measure the width and height of.
@param iMessageLength The length, in bytes (not characters), of the @param iMessageLength The length, in bytes (not characters), of the
string at sMessage. string at sMessage.
@param iMaxWidth The maximum length, in pixels, that the text may @param iMaxWidth The maximum length, in pixels, that the text may
occupy. Default is INT_MAX. occupy. Default is INT_MAX.
*/ */
virtual text_layout get_text_dimensions(const char* sMessage, size_t iMessageLength, virtual text_layout get_text_dimensions(const char* sMessage,
int iMaxWidth = INT_MAX) const = 0; size_t iMessageLength,
int iMaxWidth = INT_MAX) const = 0;
//! Draw a single line of text //! Draw a single line of text
/*! /*!
@param pCanvas The render target to draw onto. @param pCanvas The render target to draw onto.
@param sMessage A UTF-8 encoded string containing a single line of text @param sMessage A UTF-8 encoded string containing a single line of text
to draw. to draw.
@param iMessageLength The length, in bytes (not characters), of the @param iMessageLength The length, in bytes (not characters), of the
string at sMessage. string at sMessage.
@param iX The X coordinate of the top-left corner of the bounding @param iX The X coordinate of the top-left corner of the bounding
rectangle for the drawn text. rectangle for the drawn text.
@param iY The Y coordinate of the top-left corner of the bounding @param iY The Y coordinate of the top-left corner of the bounding
rectangle for the drawn text. rectangle for the drawn text.
*/ */
virtual void draw_text(render_target* pCanvas, const char* sMessage, virtual void draw_text(render_target* pCanvas, const char* sMessage,
size_t iMessageLength, int iX, int iY) const = 0; size_t iMessageLength, int iX, int iY) const = 0;
//! Draw a single line of text, splitting it at word boundaries //! 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 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 breaks like `\r` and `\n` in sMessage are ignored), but inserts line
between words so that no single line is wider than iWidth pixels. 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. 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 @param pCanvas The canvas on which to draw. Can be nullptr, in which
nothing is drawn, but other calculations are still made. case nothing is drawn, but other calculations are still made.
@param sMessage The line of text to draw, encoded in CP437. @param sMessage The line of text to draw, encoded in CP437.
@param iMessageLength The length (in bytes) of sMessage. @param iMessageLength The length (in bytes) of sMessage.
@param iX The X position to start drawing on the canvas. @param iX The X position to start drawing on the canvas.
@param iY The Y 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 iWidth The maximum width of each line of text.
@param iMaxRows The maximum number of rows to draw. Default is INT_MAX. @param iMaxRows The maximum number of rows to draw. Default is INT_MAX.
@param iSkipRows Start rendering text after skipping this many rows. @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 @param eAlign How to align each line of text if the width of the line
of text is smaller than iWidth. of text is smaller than iWidth.
*/ */
virtual text_layout draw_text_wrapped(render_target* pCanvas, const char* sMessage, virtual text_layout draw_text_wrapped(
size_t iMessageLength, int iX, int iY, render_target* pCanvas, const char* sMessage, size_t iMessageLength,
int iWidth, int iMaxRows = INT_MAX, int iSkipRows = 0, int iX, int iY, int iWidth, int iMaxRows = INT_MAX, int iSkipRows = 0,
text_alignment eAlign = text_alignment::left) const = 0; text_alignment eAlign = text_alignment::left) const = 0;
}; };
class bitmap_font final : public font class bitmap_font final : public font {
{ public:
public: bitmap_font();
bitmap_font();
//! Set the character glyph sprite sheet //! Set the character glyph sprite sheet
/*! /*!
The sprite sheet should have the space character (ASCII 0x20) at sprite 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. 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.) '!' (ASCII 0x21) at index 2, 'A' (ASCII 0x41) at index 34, etc.)
*/ */
void set_sprite_sheet(sprite_sheet* pSpriteSheet); 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 //! Set the separation between characters and between lines
/*! /*!
Generally, the sprite sheet glyphs will already include separation, and Generally, the sprite sheet glyphs will already include separation, and
thus no extra separation is required (set iCharSep and iLineSep to 0). thus no extra separation is required (set iCharSep and iLineSep to 0).
*/ */
void set_separation(int iCharSep, int iLineSep); void set_separation(int iCharSep, int iLineSep);
text_layout get_text_dimensions(const char* sMessage, size_t iMessageLength, text_layout get_text_dimensions(const char* sMessage, size_t iMessageLength,
int iMaxWidth = INT_MAX) const override; int iMaxWidth = INT_MAX) const override;
void draw_text(render_target* pCanvas, const char* sMessage, void draw_text(render_target* pCanvas, const char* sMessage,
size_t iMessageLength, int iX, int iY) const override; size_t iMessageLength, int iX, int iY) const override;
text_layout draw_text_wrapped(render_target* pCanvas, const char* sMessage, text_layout draw_text_wrapped(
size_t iMessageLength, int iX, int iY, render_target* pCanvas, const char* sMessage, size_t iMessageLength,
int iWidth, int iMaxRows = INT_MAX, int iSkipRows = 0, int iX, int iY, int iWidth, int iMaxRows = INT_MAX, int iSkipRows = 0,
text_alignment eAlign = text_alignment::left) const override; text_alignment eAlign = text_alignment::left) const override;
private: private:
sprite_sheet* sheet; sprite_sheet* sheet;
int letter_spacing; int letter_spacing;
int line_spacing; int line_spacing;
}; };
#ifdef CORSIX_TH_USE_FREETYPE2 #ifdef CORSIX_TH_USE_FREETYPE2
@@ -167,162 +169,163 @@ private:
THRawBitmap class, but with an alpha channel, and a single colour rather THRawBitmap class, but with an alpha channel, and a single colour rather
than a palette). than a palette).
*/ */
class freetype_font final : public font class freetype_font final : public font {
{ public:
public: freetype_font();
freetype_font(); ~freetype_font();
~freetype_font();
//! Get the copyright notice which should be displayed for FreeType2. //! Get the copyright notice which should be displayed for FreeType2.
/*! /*!
To comply with the FreeType2 license, the string returned by this To comply with the FreeType2 license, the string returned by this
function needs to be displayed at some point. function needs to be displayed at some point.
@return A null-terminated UTF-8 encoded string. @return A null-terminated UTF-8 encoded string.
*/ */
static const char* get_copyright_notice(); static const char* get_copyright_notice();
//! Initialise the FreeType2 library. //! Initialise the FreeType2 library.
/*! /*!
This will be called automatically by setFace() as required. This will be called automatically by setFace() as required.
*/ */
FT_Error initialise(); FT_Error initialise();
//! Remove all cached strings, as our graphics context has changed //! Remove all cached strings, as our graphics context has changed
void clear_cache(); void clear_cache();
//! Set the font face to be used. //! Set the font face to be used.
/*! /*!
@param pData Pointer to the start of a font file loaded into memory. @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 This block of memory must remain valid for at least the lifetime
of the THFreeTypeFont objcect. of the THFreeTypeFont objcect.
@param iLength The size, in bytes, of the font file at pData. @param iLength The size, in bytes, of the font file at pData.
*/ */
FT_Error set_face(const uint8_t* pData, size_t iLength); FT_Error set_face(const uint8_t* pData, size_t iLength);
//! Set the font size and colour to match that of a bitmap font. //! 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 Note that the matching is done on a best-effort basis, and will likely
not be perfect. This must be called after setFace(). not be perfect. This must be called after setFace().
@param pBitmapFontSpriteSheet The sprite sheet of the bitmap font. @param pBitmapFontSpriteSheet The sprite sheet of the bitmap font.
*/ */
FT_Error match_bitmap_font(sprite_sheet* pBitmapFontSpriteSheet); FT_Error match_bitmap_font(sprite_sheet* pBitmapFontSpriteSheet);
//! Set the ideal character size using pixel values. //! Set the ideal character size using pixel values.
/*! /*!
Note that the given size might be changed a small amount if doing so 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 would result in a much nicer rendered font. This must be called after
setFace(). setFace().
*/ */
FT_Error set_ideal_character_size(int iWidth, int iHeight); FT_Error set_ideal_character_size(int iWidth, int iHeight);
text_layout get_text_dimensions(const char* sMessage, size_t iMessageLength, text_layout get_text_dimensions(const char* sMessage, size_t iMessageLength,
int iMaxWidth = INT_MAX) const override; int iMaxWidth = INT_MAX) const override;
void draw_text(render_target* pCanvas, const char* sMessage, void draw_text(render_target* pCanvas, const char* sMessage,
size_t iMessageLength, int iX, int iY) const override; size_t iMessageLength, int iX, int iY) const override;
text_layout draw_text_wrapped(render_target* pCanvas, const char* sMessage, text_layout draw_text_wrapped(
size_t iMessageLength, int iX, int iY, render_target* pCanvas, const char* sMessage, size_t iMessageLength,
int iWidth, int iMaxRows = INT_MAX, int iSkipRows = 0, int iX, int iY, int iWidth, int iMaxRows = INT_MAX, int iSkipRows = 0,
text_alignment eAlign = text_alignment::left) const override; text_alignment eAlign = text_alignment::left) const override;
private: private:
struct cached_text struct cached_text {
{ //! The text being converted to pixels
//! The text being converted to pixels char* message;
char* message;
//! Raw pixel data in row major 8-bit greyscale //! Raw pixel data in row major 8-bit greyscale
uint8_t* data; uint8_t* data;
//! Generated texture ready to be rendered //! Generated texture ready to be rendered
SDL_Texture* texture; SDL_Texture* texture;
//! The length of sMessage //! The length of sMessage
size_t message_length; size_t message_length;
//! The size of the buffer allocated to store sMessage //! The size of the buffer allocated to store sMessage
size_t message_buffer_length; size_t message_buffer_length;
//! Width of the image to draw //! Width of the image to draw
int width; int width;
//! Height of the image to draw //! Height of the image to draw
int height; int height;
//! The width of the longest line of text in in the textbox in pixels //! The width of the longest line of text in in the textbox in pixels
int widest_line_width; int widest_line_width;
//! X Coordinate trailing the last character in canvas coordinates //! X Coordinate trailing the last character in canvas coordinates
int last_x; int last_x;
//! Number of rows required //! Number of rows required
int row_count; int row_count;
//! Alignment of the message in the box //! Alignment of the message in the box
text_alignment alignment; text_alignment alignment;
//! True when the data reflects the message given the size constraints //! True when the data reflects the message given the size constraints
bool is_valid; bool is_valid;
}; };
//! Render a FreeType2 monochrome bitmap to a cache canvas. //! 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; 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. //! 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; void render_gray(cached_text* pCacheEntry, FT_Bitmap* pBitmap, FT_Pos x,
FT_Pos y) const;
static FT_Library freetype_library; static FT_Library freetype_library;
static int freetype_init_count; static int freetype_init_count;
static const int cache_size_log2 = 7; static const int cache_size_log2 = 7;
FT_Face font_face; FT_Face font_face;
argb_colour colour; argb_colour colour;
bool is_done_freetype_init; bool is_done_freetype_init;
mutable cached_text cache[1 << cache_size_log2]; 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. //! 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 @return true if 1-bit monochrome rendering should be used, false if
8-bit grayscale rendering should be used (though in the latter 8-bit grayscale rendering should be used (though in the latter
case, 1-bit rendering might still get used). case, 1-bit rendering might still get used).
*/ */
bool is_monochrome() const; bool is_monochrome() const;
//! Convert a cache canvas containing rendered text into a texture. //! Convert a cache canvas containing rendered text into a texture.
/*! /*!
@param pEventualCanvas A pointer to the rendertarget we'll be using to @param pEventualCanvas A pointer to the rendertarget we'll be using to
draw this. draw this.
@param pCacheEntry A cache entry whose pData field points to a pixmap @param pCacheEntry A cache entry whose pData field points to a pixmap
of size iWidth by iHeight. This method will convert said pixmap to 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 an object which can be used by the rendering engine, and store the
result in the pTexture or iTexture field. result in the pTexture or iTexture field.
*/ */
void make_texture(render_target *pEventualCanvas, cached_text* pCacheEntry) const; void make_texture(render_target* pEventualCanvas,
cached_text* pCacheEntry) const;
//! Free a previously-made texture of a cache entry. //! Free a previously-made texture of a cache entry.
/*! /*!
This call should free all the resources previously allocated by a call This call should free all the resources previously allocated by a call
to _makeTexture() and set the texture field to indicate no texture. to _makeTexture() and set the texture field to indicate no texture.
@param pCacheEntry A cache entry previously passed to _makeTexture(). @param pCacheEntry A cache entry previously passed to _makeTexture().
*/ */
void free_texture(cached_text* pCacheEntry) const; void free_texture(cached_text* pCacheEntry) const;
//! Render a previously-made texture of a cache entry. //! Render a previously-made texture of a cache entry.
/*! /*!
@param pCanvas The canvas on which to draw. @param pCanvas The canvas on which to draw.
@param pCacheEntry A cache entry containing the texture to draw, which @param pCacheEntry A cache entry containing the texture to draw, which
will have been stored in the pTexture or iTexture field by a prior will have been stored in the pTexture or iTexture field by a prior
call to _makeTexture(). call to _makeTexture().
@param iX The X position at which to draw the texture on the canvas. @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. @param iY The Y position at which to draw the texture on the canvas.
*/ */
void draw_texture(render_target* pCanvas, cached_text* pCacheEntry, void draw_texture(render_target* pCanvas, cached_text* pCacheEntry, int iX,
int iX, int iY) const; 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_

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -20,405 +20,357 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE. SOFTWARE.
*/ */
#include <cstdio>
#include <cstring>
#include <stdexcept>
#include "bootstrap.h"
#include "th.h" #include "th.h"
#include "th_lua_internal.h" #include "th_lua_internal.h"
#include "bootstrap.h"
#include <cstring>
#include <cstdio>
#include <stdexcept>
void lua_register_anims(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_gfx(const lua_register_state* pState);
void lua_register_map(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_sound(const lua_register_state* pState);
void lua_register_movie(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_strings(const lua_register_state* pState);
void lua_register_ui(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_lfs_ext(const lua_register_state* pState);
void lua_register_iso_fs(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 //! Set a field on the environment table of an object
void luaT_setenvfield(lua_State *L, int index, const char *k) void luaT_setenvfield(lua_State* L, int index, const char* k) {
{ lua_getfenv(L, index);
lua_getfenv(L, index); lua_pushstring(L, k);
lua_pushstring(L, k); lua_pushvalue(L, -3);
lua_pushvalue(L, -3); lua_settable(L, -3);
lua_settable(L, -3); lua_pop(L, 2);
lua_pop(L, 2);
} }
//! Get a field from the environment table of an object //! Get a field from the environment table of an object
void luaT_getenvfield(lua_State *L, int index, const char *k) void luaT_getenvfield(lua_State* L, int index, const char* k) {
{ lua_getfenv(L, index);
lua_getfenv(L, index); lua_getfield(L, -1, k);
lua_getfield(L, -1, k); lua_replace(L, -2);
lua_replace(L, -2);
} }
#if LUA_VERSION_NUM >= 502 #if LUA_VERSION_NUM >= 502
void luaT_getfenv52(lua_State *L, int iIndex) void luaT_getfenv52(lua_State* L, int iIndex) {
{ int iType = lua_type(L, iIndex);
int iType = lua_type(L, iIndex); switch (iType) {
switch(iType)
{
case LUA_TUSERDATA: case LUA_TUSERDATA:
lua_getuservalue(L, iIndex); lua_getuservalue(L, iIndex);
break; break;
case LUA_TFUNCTION: case LUA_TFUNCTION:
if(lua_iscfunction(L, iIndex)) if (lua_iscfunction(L, iIndex)) {
{ // Our convention: upvalue at #1 is environment
// Our convention: upvalue at #1 is environment if (lua_getupvalue(L, iIndex, 1) == nullptr) lua_pushglobaltable(L);
if(lua_getupvalue(L, iIndex, 1) == nullptr) } else {
lua_pushglobaltable(L); // 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 lua_pushglobaltable(L);
{ }
// Language convention: upvalue called _ENV is environment break;
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;
default: default:
luaL_error(L, "Unable to get environment of a %s in 5.2", lua_typename(L, iType)); luaL_error(L, "Unable to get environment of a %s in 5.2",
break; lua_typename(L, iType));
} break;
}
} }
int luaT_setfenv52(lua_State *L, int iIndex) int luaT_setfenv52(lua_State* L, int iIndex) {
{ int iType = lua_type(L, iIndex);
int iType = lua_type(L, iIndex); switch (iType) {
switch(iType)
{
case LUA_TUSERDATA: case LUA_TUSERDATA:
lua_setuservalue(L, iIndex); lua_setuservalue(L, iIndex);
return 1; return 1;
case LUA_TFUNCTION: case LUA_TFUNCTION:
if(lua_iscfunction(L, iIndex)) if (lua_iscfunction(L, iIndex)) {
{ // Our convention: upvalue at #1 is environment
// Our convention: upvalue at #1 is environment if (lua_setupvalue(L, iIndex, 1) == nullptr) {
if(lua_setupvalue(L, iIndex, 1) == nullptr) lua_pop(L, 1);
{ return 0;
lua_pop(L, 1);
return 0;
}
return 1;
} }
else return 1;
{ } else {
// Language convention: upvalue called _ENV is environment, which // Language convention: upvalue called _ENV is environment,
// might be shared with other functions. // which might be shared with other functions.
const char* sUpName = nullptr; const char* sUpName = nullptr;
for(int i = 1; (sUpName = lua_getupvalue(L, iIndex, i)) ; ++i) for (int i = 1; (sUpName = lua_getupvalue(L, iIndex, i)); ++i) {
{ lua_pop(L, 1); // lua_getupvalue puts the value on the
lua_pop(L, 1); // lua_getupvalue puts the value on the stack, but we just want to replace it // stack, but we just want to replace it
if(std::strcmp(sUpName, "_ENV") == 0) if (std::strcmp(sUpName, "_ENV") == 0) {
{ luaL_loadstring(L,
luaL_loadstring(L, "local upv = ... return function() return upv end"); "local upv = ... return function() return upv "
lua_insert(L, -2); "end");
lua_call(L, 1, 1); lua_insert(L, -2);
lua_upvaluejoin(L, iIndex, i, -1, 1); lua_call(L, 1, 1);
lua_pop(L, 1); lua_upvaluejoin(L, iIndex, i, -1, 1);
return 1;
}
}
lua_pop(L, 1); lua_pop(L, 1);
return 0; return 1;
}
} }
default: lua_pop(L, 1);
return 0; return 0;
} }
default:
return 0;
}
} }
#endif #endif
//! Push a C closure as a callable table //! 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) {
{ luaT_pushcclosure(L, fn, n); // .. fn <top
luaT_pushcclosure(L, fn, n); // .. fn <top lua_createtable(L, 0, 1); // .. fn mt <top
lua_createtable(L, 0, 1); // .. fn mt <top lua_pushliteral(L, "__call"); // .. fn mt __call <top
lua_pushliteral(L, "__call"); // .. fn mt __call <top lua_pushvalue(L, -3); // .. fn mt __call fn <top
lua_pushvalue(L, -3); // .. fn mt __call fn <top lua_settable(L, -3); // .. fn mt <top
lua_settable(L, -3); // .. fn mt <top lua_newtable(L); // .. fn mt t <top
lua_newtable(L); // .. fn mt t <top lua_replace(L, -3); // .. t mt <top
lua_replace(L, -3); // .. t mt <top lua_setmetatable(L, -2); // .. t <top
lua_setmetatable(L, -2); // .. t <top
} }
//! Check for a string or userdata //! Check for a string or userdata
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) {
{ const uint8_t* pData;
const uint8_t *pData; size_t iLength;
size_t iLength; if (lua_type(L, idx) == LUA_TUSERDATA) {
if(lua_type(L, idx) == LUA_TUSERDATA) pData = reinterpret_cast<const uint8_t*>(lua_touserdata(L, idx));
{ iLength = lua_objlen(L, idx);
pData = reinterpret_cast<const uint8_t*>(lua_touserdata(L, idx)); } else {
iLength = lua_objlen(L, idx); pData =
} reinterpret_cast<const uint8_t*>(luaL_checklstring(L, idx, &iLength));
else }
{ if (pDataLen != 0) *pDataLen = iLength;
pData = reinterpret_cast<const uint8_t*>(luaL_checklstring(L, idx, &iLength)); return pData;
}
if(pDataLen != 0)
*pDataLen = iLength;
return pData;
} }
namespace { namespace {
int l_load_strings(lua_State *L) int l_load_strings(lua_State* L) {
{ size_t iDataLength;
size_t iDataLength; const uint8_t* pData = luaT_checkfile(L, 1, &iDataLength);
const uint8_t* pData = luaT_checkfile(L, 1, &iDataLength);
try try {
{ th_string_list oStrings(pData, iDataLength);
th_string_list oStrings(pData, iDataLength); lua_settop(L, 0);
lua_settop(L, 0); lua_createtable(L, static_cast<int>(oStrings.get_section_count()), 0);
lua_createtable(L, static_cast<int>(oStrings.get_section_count()), 0); for (size_t iSec = 0; iSec < oStrings.get_section_count(); ++iSec) {
for(size_t iSec = 0; iSec < oStrings.get_section_count(); ++iSec) size_t iCount = oStrings.get_section_size(iSec);
{ lua_createtable(L, static_cast<int>(iCount), 0);
size_t iCount = oStrings.get_section_size(iSec); for (size_t iStr = 0; iStr < iCount; ++iStr) {
lua_createtable(L, static_cast<int>(iCount), 0); lua_pushstring(L, oStrings.get_string(iSec, iStr));
for(size_t iStr = 0; iStr < iCount; ++iStr) lua_rawseti(L, 2, static_cast<int>(iStr + 1));
{ }
lua_pushstring(L, oStrings.get_string(iSec, iStr)); lua_rawseti(L, 1, static_cast<int>(iSec + 1));
lua_rawseti(L, 2, static_cast<int>(iStr + 1));
}
lua_rawseti(L, 1, static_cast<int>(iSec + 1));
}
} }
catch(std::invalid_argument) } catch (std::invalid_argument) {
{ lua_pushboolean(L, 0);
lua_pushboolean(L, 0); }
} return 1;
return 1;
} }
int get_api_version() int get_api_version() {
{
#include "../Lua/api_version.lua" #include "../Lua/api_version.lua"
} }
int l_get_compile_options(lua_State *L) int l_get_compile_options(lua_State* L) {
{ lua_settop(L, 0);
lua_settop(L, 0); lua_newtable(L);
lua_newtable(L);
#ifdef CORSIX_TH_64BIT #ifdef CORSIX_TH_64BIT
lua_pushboolean(L, 1); lua_pushboolean(L, 1);
#else #else
lua_pushboolean(L, 0); lua_pushboolean(L, 0);
#endif #endif
lua_setfield(L, -2, "arch_64"); lua_setfield(L, -2, "arch_64");
lua_pushliteral(L, "SDL"); lua_pushliteral(L, "SDL");
lua_setfield(L, -2, "renderer"); lua_setfield(L, -2, "renderer");
#ifdef CORSIX_TH_USE_SDL_MIXER #ifdef CORSIX_TH_USE_SDL_MIXER
lua_pushboolean(L, 1); lua_pushboolean(L, 1);
#else #else
lua_pushboolean(L, 0); lua_pushboolean(L, 0);
#endif #endif
lua_setfield(L, -2, "audio"); lua_setfield(L, -2, "audio");
lua_getfield(L, LUA_REGISTRYINDEX, "_LOADED"); lua_getfield(L, LUA_REGISTRYINDEX, "_LOADED");
lua_getfield(L, -1, "jit"); lua_getfield(L, -1, "jit");
if(lua_type(L, -1) == LUA_TNIL) 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<int>(lua_metatable::count));
lua_register_state oState;
const lua_register_state *pState = &oState;
oState.L = L;
for(int i = 0; i < static_cast<int>(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_replace(L, -2); 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) } // namespace
{
luaT_execute_loadstring(L, sLuaString); void luaT_setclosure(const lua_register_state* pState, lua_CFunction fn,
lua_call(L, 0, LUA_MULTRET); size_t iUps) {
luaT_pushcclosure(pState->L, fn, iUps);
} }
void luaT_push(lua_State *L, lua_CFunction f) int luaopen_th(lua_State* L) {
{ lua_settop(L, 0);
luaT_pushcfunction(L, f); lua_checkstack(L, 16 + static_cast<int>(lua_metatable::count));
lua_register_state oState;
const lua_register_state* pState = &oState;
oState.L = L;
for (int i = 0; i < static_cast<int>(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) void luaT_execute_loadstring(lua_State* L, const char* sLuaString) {
{ static const int iRegistryCacheIndex = 7;
lua_pushinteger(L, (lua_Integer)i); lua_rawgeti(L, LUA_REGISTRYINDEX, iRegistryCacheIndex);
} if (lua_isnil(L, -1)) {
// Cache not yet created - create it.
void luaT_push(lua_State *L, const char* s) lua_pop(L, 1);
{ lua_getglobal(L, "setmetatable");
lua_pushstring(L, s); if (lua_isnil(L, -1)) {
} // Base library not yet loaded - fallback to simple
// uncached loadstring
void luaT_pushtablebool(lua_State *L, const char *k, bool v) lua_pop(L, 1);
{ if (luaL_loadstring(L, sLuaString)) lua_error(L);
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 */
} }
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) void luaT_execute(lua_State* L, const char* sLuaString) {
{ luaT_execute_loadstring(L, sLuaString);
int i; lua_call(L, 0, LUA_MULTRET);
int len = static_cast<int>(lua_objlen(L, idx)); }
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++) void luaT_push(lua_State* L, int i) { lua_pushinteger(L, (lua_Integer)i); }
{
lua_rawgeti(L, idx, i); void luaT_push(lua_State* L, const char* s) { lua_pushstring(L, s); }
int t = lua_type(L, -1);
switch (t) { void luaT_pushtablebool(lua_State* L, const char* k, bool v) {
case LUA_TSTRING: /* strings */ lua_pushstring(L, k);
std::printf("string: '%s'\n", lua_tostring(L, -1)); lua_pushboolean(L, v);
break; lua_settable(L, -3);
case LUA_TBOOLEAN: /* booleans */ }
std::printf("boolean %s\n", lua_toboolean(L, -1) ? "true" : "false");
break; void luaT_printstack(lua_State* L) {
case LUA_TNUMBER: /* numbers */ int i;
std::printf("number: %g\n", lua_tonumber(L, -1)); int top = lua_gettop(L);
break;
default: /* other values */ std::printf("total items in stack %d\n", top);
std::printf("%s\n", lua_typename(L, t));
break; for (i = 1; i <= top; i++) { /* repeat for each level */
} int t = lua_type(L, i);
std::printf(" "); /* put a separator */ switch (t) {
lua_pop(L, 1); 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<int>(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 */
} }

View File

@@ -23,12 +23,12 @@ SOFTWARE.
#ifndef CORSIX_TH_TH_LUA_H_ #ifndef CORSIX_TH_TH_LUA_H_
#define CORSIX_TH_TH_LUA_H_ #define CORSIX_TH_TH_LUA_H_
#include "config.h" #include "config.h"
#include "lua.hpp"
#include <cstdio> #include <cstdio>
#include <new> #include <new>
#include <vector> #include <vector>
#include "lua.hpp"
int luaopen_th(lua_State *L); int luaopen_th(lua_State* L);
// Compatibility layer for removal of environments in 5.2 // Compatibility layer for removal of environments in 5.2
#if LUA_VERSION_NUM >= 502 #if LUA_VERSION_NUM >= 502
@@ -37,85 +37,78 @@ const int luaT_environindex = lua_upvalueindex(1);
const int luaT_environindex = LUA_ENVIRONINDEX; const int luaT_environindex = LUA_ENVIRONINDEX;
#endif #endif
inline int luaT_upvalueindex(int i) inline int luaT_upvalueindex(int i) {
{
#if LUA_VERSION_NUM >= 502 #if LUA_VERSION_NUM >= 502
return lua_upvalueindex(i + 1); return lua_upvalueindex(i + 1);
#else #else
return lua_upvalueindex(i); return lua_upvalueindex(i);
#endif #endif
} }
template<class Collection> template <class Collection>
inline void luaT_register(lua_State *L, const char *n, Collection &l) inline void luaT_register(lua_State* L, const char* n, Collection& l) {
{
#if LUA_VERSION_NUM >= 502 #if LUA_VERSION_NUM >= 502
lua_createtable(L, 0, static_cast<int>(l.size())); lua_createtable(L, 0, static_cast<int>(l.size()));
lua_pushvalue(L, luaT_environindex); lua_pushvalue(L, luaT_environindex);
luaL_setfuncs(L, l.data(), 1); luaL_setfuncs(L, l.data(), 1);
lua_pushvalue(L, -1); lua_pushvalue(L, -1);
lua_setglobal(L, n); lua_setglobal(L, n);
#else #else
luaL_register(L, n, l.data()); luaL_register(L, n, l.data());
#endif #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 #if LUA_VERSION_NUM >= 502
lua_pushvalue(L, luaT_environindex); lua_pushvalue(L, luaT_environindex);
luaL_setfuncs(L, R, 1); luaL_setfuncs(L, R, 1);
#else #else
luaL_register(L, nullptr, R); luaL_register(L, nullptr, R);
#endif #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 #if LUA_VERSION_NUM >= 502
++nups; ++nups;
lua_pushvalue(L, luaT_environindex); lua_pushvalue(L, luaT_environindex);
lua_insert(L, -nups); lua_insert(L, -nups);
lua_pushcclosure(L, f, nups); lua_pushcclosure(L, f, nups);
#else #else
lua_pushcclosure(L, f, nups); lua_pushcclosure(L, f, nups);
#endif #endif
} }
inline void luaT_pushcfunction(lua_State *L, lua_CFunction f) inline void luaT_pushcfunction(lua_State* L, lua_CFunction f) {
{ luaT_pushcclosure(L, f, 0);
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 #if LUA_VERSION_NUM >= 502
lua_checkstack(L, 2); lua_checkstack(L, 2);
lua_pushcfunction(L, f); lua_pushcfunction(L, f);
lua_pushlightuserdata(L, u); lua_pushlightuserdata(L, u);
return lua_pcall(L, 1, 0, 0); return lua_pcall(L, 1, 0, 0);
#else #else
return lua_cpcall(L, f, u); return lua_cpcall(L, f, u);
#endif #endif
} }
// Compatibility for missing mode argument on lua_load in 5.1 // 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 #if LUA_VERSION_NUM >= 502
return lua_load(L, r, d, s, m); return lua_load(L, r, d, s, m);
#else #else
return lua_load(L, r, d, s); return lua_load(L, r, d, s);
#endif #endif
} }
// Compatibility for missing from argument on lua_resume in 5.1 // 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 #if LUA_VERSION_NUM >= 502
return lua_resume(L, f, n); return lua_resume(L, f, n);
#else #else
return lua_resume(L, n); return lua_resume(L, n);
#endif #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 See also luaT_stdnew() which allocates, and also sets up the environment
table and metatable for the userdata. table and metatable for the userdata.
*/ */
template<typename T, typename ...Ts> template <typename T, typename... Ts>
T* luaT_new(lua_State* L, Ts ...args) T* luaT_new(lua_State* L, Ts... args) {
{ return new (lua_newuserdata(L, sizeof(T))) T(args...);
return new (lua_newuserdata(L, sizeof(T))) T(args...);
} }
//! Check that a Lua argument is a binary data blob //! 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 pointer to the start of it, and the length of it. Otherwise, throws a
Lua error. 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 //! 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 //! 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 //! Set a field on the environment table of a value
/*! /*!
Performs: env(stack[index])[k] = top; pop() 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 //! Get a field from the environment table of a value
/*! /*!
Performs: push(env(stack[index])[k]) 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 <class T> template <class T>
inline T* luaT_stdnew(lua_State *L, int mt_idx = luaT_environindex, bool env = false) inline T* luaT_stdnew(lua_State* L, int mt_idx = luaT_environindex,
{ bool env = false) {
T* p = luaT_new<T>(L); T* p = luaT_new<T>(L);
lua_pushvalue(L, mt_idx); lua_pushvalue(L, mt_idx);
lua_setmetatable(L, -2); lua_setmetatable(L, -2);
if(env) if (env) {
{ lua_newtable(L);
lua_newtable(L); lua_setfenv(L, -2);
lua_setfenv(L, -2); }
} return p;
return p;
} }
template <typename T> struct luaT_classinfo {}; template <typename T>
struct luaT_classinfo {};
class render_target; class render_target;
template <> struct luaT_classinfo<render_target> { template <>
static inline const char* name() {return "Surface";} struct luaT_classinfo<render_target> {
static inline const char* name() { return "Surface"; }
}; };
class level_map; class level_map;
template <> struct luaT_classinfo<level_map> { template <>
static inline const char* name() {return "Map";} struct luaT_classinfo<level_map> {
static inline const char* name() { return "Map"; }
}; };
class sprite_sheet; class sprite_sheet;
template <> struct luaT_classinfo<sprite_sheet> { template <>
static inline const char* name() {return "SpriteSheet";} struct luaT_classinfo<sprite_sheet> {
static inline const char* name() { return "SpriteSheet"; }
}; };
class animation; class animation;
template <> struct luaT_classinfo<animation> { template <>
static inline const char* name() {return "Animation";} struct luaT_classinfo<animation> {
static inline const char* name() { return "Animation"; }
}; };
class animation_manager; class animation_manager;
template <> struct luaT_classinfo<animation_manager> { template <>
static inline const char* name() {return "Animator";} struct luaT_classinfo<animation_manager> {
static inline const char* name() { return "Animator"; }
}; };
class palette; class palette;
template <> struct luaT_classinfo<palette> { template <>
static inline const char* name() {return "Palette";} struct luaT_classinfo<palette> {
static inline const char* name() { return "Palette"; }
}; };
class raw_bitmap; class raw_bitmap;
template <> struct luaT_classinfo<raw_bitmap> { template <>
static inline const char* name() {return "RawBitmap";} struct luaT_classinfo<raw_bitmap> {
static inline const char* name() { return "RawBitmap"; }
}; };
class font; class font;
template <> struct luaT_classinfo<font> { template <>
static inline const char* name() {return "Font";} struct luaT_classinfo<font> {
static inline const char* name() { return "Font"; }
}; };
class bitmap_font; class bitmap_font;
template <> struct luaT_classinfo<bitmap_font> { template <>
static inline const char* name() {return "BitmapFont";} struct luaT_classinfo<bitmap_font> {
static inline const char* name() { return "BitmapFont"; }
}; };
#ifdef CORSIX_TH_USE_FREETYPE2 #ifdef CORSIX_TH_USE_FREETYPE2
class freetype_font; class freetype_font;
template <> struct luaT_classinfo<freetype_font> { template <>
static inline const char* name() {return "FreeTypeFont";} struct luaT_classinfo<freetype_font> {
static inline const char* name() { return "FreeTypeFont"; }
}; };
#endif #endif
struct layers; struct layers;
template <> struct luaT_classinfo<layers> { template <>
static inline const char* name() {return "Layers";} struct luaT_classinfo<layers> {
static inline const char* name() { return "Layers"; }
}; };
class pathfinder; class pathfinder;
template <> struct luaT_classinfo<pathfinder> { template <>
static inline const char* name() {return "Pathfinder";} struct luaT_classinfo<pathfinder> {
static inline const char* name() { return "Pathfinder"; }
}; };
class cursor; class cursor;
template <> struct luaT_classinfo<cursor> { template <>
static inline const char* name() {return "Cursor";} struct luaT_classinfo<cursor> {
static inline const char* name() { return "Cursor"; }
}; };
class line; class line;
template <> struct luaT_classinfo<line> { template <>
static inline const char* name() {return "Line";} struct luaT_classinfo<line> {
static inline const char* name() { return "Line"; }
}; };
class music; class music;
template <> struct luaT_classinfo<music> { template <>
static inline const char* name() {return "Music";} struct luaT_classinfo<music> {
static inline const char* name() { return "Music"; }
}; };
class sound_archive; class sound_archive;
template <> struct luaT_classinfo<sound_archive> { template <>
static inline const char* name() {return "SoundArchive";} struct luaT_classinfo<sound_archive> {
static inline const char* name() { return "SoundArchive"; }
}; };
class sound_player; class sound_player;
template <> struct luaT_classinfo<sound_player> { template <>
static inline const char* name() {return "SoundEffects";} struct luaT_classinfo<sound_player> {
static inline const char* name() { return "SoundEffects"; }
}; };
class movie_player; class movie_player;
template <> struct luaT_classinfo<movie_player> { template <>
static inline const char* name() {return "Movie";} struct luaT_classinfo<movie_player> {
static inline const char* name() { return "Movie"; }
}; };
class abstract_window; class abstract_window;
template <> struct luaT_classinfo<abstract_window> { template <>
static inline const char* name() {return "WindowBase";} struct luaT_classinfo<abstract_window> {
static inline const char* name() { return "WindowBase"; }
}; };
class sprite_render_list; class sprite_render_list;
template <> struct luaT_classinfo<sprite_render_list> { template <>
static inline const char* name() {return "SpriteRenderList";} struct luaT_classinfo<sprite_render_list> {
static inline const char* name() { return "SpriteRenderList"; }
}; };
class string_proxy; class string_proxy;
template <> struct luaT_classinfo<string_proxy> { template <>
static inline const char* name() {return "StringProxy";} struct luaT_classinfo<string_proxy> {
static inline const char* name() { return "StringProxy"; }
}; };
class lfs_ext; class lfs_ext;
template <> struct luaT_classinfo <lfs_ext> { template <>
static inline const char* name() {return "LfsExt";} struct luaT_classinfo<lfs_ext> {
static inline const char* name() { return "LfsExt"; }
}; };
class iso_filesystem; class iso_filesystem;
template <> struct luaT_classinfo<iso_filesystem> { template <>
static inline const char* name() {return "ISO Filesystem";} struct luaT_classinfo<iso_filesystem> {
static inline const char* name() { return "ISO Filesystem"; }
}; };
template <> struct luaT_classinfo<std::FILE*> { template <>
static inline const char* name() {return "file";} struct luaT_classinfo<std::FILE*> {
static inline const char* name() { return "file"; }
}; };
template <class T> template <class T>
T* luaT_testuserdata(lua_State *L, int idx, int mt_idx, bool required = true) 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.
// Turn mt_idx into an absolute index, as the stack size changes. if (mt_idx > LUA_REGISTRYINDEX && mt_idx < 0)
if(mt_idx > LUA_REGISTRYINDEX && mt_idx < 0) mt_idx = lua_gettop(L) + mt_idx + 1;
mt_idx = lua_gettop(L) + mt_idx + 1;
void *ud = lua_touserdata(L, idx); void* ud = lua_touserdata(L, idx);
if(ud != nullptr && lua_getmetatable(L, idx) != 0) if (ud != nullptr && lua_getmetatable(L, idx) != 0) {
{ while (true) {
while(true) if (lua_equal(L, mt_idx, -1) != 0) {
{
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); 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) if (required) {
{ const char* msg =
const char *msg = lua_pushfstring(L, "%s expected, got %s", luaT_classinfo<T>::name(), luaL_typename(L, idx)); lua_pushfstring(L, "%s expected, got %s", luaT_classinfo<T>::name(),
luaL_argerror(L, idx, msg); luaL_typename(L, idx));
} luaL_argerror(L, idx, msg);
return nullptr; }
return nullptr;
} }
template <class T> template <class T>
T* luaT_testuserdata(lua_State *L, int idx = 1) T* luaT_testuserdata(lua_State* L, int idx = 1) {
{ int iMetaIndex = luaT_environindex;
int iMetaIndex = luaT_environindex; if (idx > 1) iMetaIndex = luaT_upvalueindex(idx - 1);
if(idx > 1) return luaT_testuserdata<T>(L, idx, iMetaIndex);
iMetaIndex = luaT_upvalueindex(idx - 1);
return luaT_testuserdata<T>(L, idx, iMetaIndex);
} }
template <class T, int mt> template <class T, int mt>
int luaT_stdgc(lua_State *L) int luaT_stdgc(lua_State* L) {
{ T* p = luaT_testuserdata<T>(L, 1, mt, false);
T* p = luaT_testuserdata<T>(L, 1, mt, false); if (p != nullptr) {
if(p != nullptr) p->~T();
{ }
p->~T(); return 0;
}
return 0;
} }
void luaT_execute(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_execute_loadstring(lua_State* L, const char* sLuaString);
void luaT_push(lua_State *L, lua_CFunction f); void luaT_push(lua_State* L, lua_CFunction f);
void luaT_push(lua_State *L, int i); void luaT_push(lua_State* L, int i);
void luaT_push(lua_State *L, const char* s); void luaT_push(lua_State* L, const char* s);
template <class T> template <class T>
void luaT_execute(lua_State *L, const char* sLuaString, T arg) void luaT_execute(lua_State* L, const char* sLuaString, T arg) {
{ luaT_execute_loadstring(L, sLuaString);
luaT_execute_loadstring(L, sLuaString); luaT_push(L, arg);
luaT_push(L, arg); lua_call(L, 1, LUA_MULTRET);
lua_call(L, 1, LUA_MULTRET);
} }
template <class T1, class T2> template <class T1, class T2>
void luaT_execute(lua_State *L, const char* sLuaString, void luaT_execute(lua_State* L, const char* sLuaString, T1 arg1, T2 arg2) {
T1 arg1, T2 arg2) luaT_execute_loadstring(L, sLuaString);
{ luaT_push(L, arg1);
luaT_execute_loadstring(L, sLuaString); luaT_push(L, arg2);
luaT_push(L, arg1); lua_call(L, 2, LUA_MULTRET);
luaT_push(L, arg2);
lua_call(L, 2, LUA_MULTRET);
} }
template <class T1, class T2, class T3> template <class T1, class T2, class T3>
void luaT_execute(lua_State *L, const char* sLuaString, void luaT_execute(lua_State* L, const char* sLuaString, T1 arg1, T2 arg2,
T1 arg1, T2 arg2, T3 arg3) T3 arg3) {
{ luaT_execute_loadstring(L, sLuaString);
luaT_execute_loadstring(L, sLuaString); luaT_push(L, arg1);
luaT_push(L, arg1); luaT_push(L, arg2);
luaT_push(L, arg2); luaT_push(L, arg3);
luaT_push(L, arg3); lua_call(L, 3, LUA_MULTRET);
lua_call(L, 3, LUA_MULTRET);
} }
template <class T1, class T2, class T3, class T4> template <class T1, class T2, class T3, class T4>
void luaT_execute(lua_State *L, const char* sLuaString, void luaT_execute(lua_State* L, const char* sLuaString, T1 arg1, T2 arg2,
T1 arg1, T2 arg2, T3 arg3, T4 arg4) T3 arg3, T4 arg4) {
{ luaT_execute_loadstring(L, sLuaString);
luaT_execute_loadstring(L, sLuaString); luaT_push(L, arg1);
luaT_push(L, arg1); luaT_push(L, arg2);
luaT_push(L, arg2); luaT_push(L, arg3);
luaT_push(L, arg3); luaT_push(L, arg4);
luaT_push(L, arg4); lua_call(L, 4, LUA_MULTRET);
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_

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -28,57 +28,58 @@ SOFTWARE.
#include <string> #include <string>
enum class lua_metatable { enum class lua_metatable {
map, map,
palette, palette,
sheet, sheet,
font, font,
bitmap_font, bitmap_font,
#ifdef CORSIX_TH_USE_FREETYPE2 #ifdef CORSIX_TH_USE_FREETYPE2
freetype_font, freetype_font,
#endif #endif
layers, layers,
anims, anims,
anim, anim,
pathfinder, pathfinder,
surface, surface,
bitmap, bitmap,
cursor, cursor,
lfs_ext, lfs_ext,
sound_archive, sound_archive,
sound_fx, sound_fx,
movie, movie,
string, string,
window_base, window_base,
sprite_list, sprite_list,
string_proxy, string_proxy,
line, line,
iso_fs, iso_fs,
count count
}; };
struct lua_register_state struct lua_register_state {
{ lua_State* L;
lua_State *L; int metatables[static_cast<size_t>(lua_metatable::count)];
int metatables[static_cast<size_t>(lua_metatable::count)]; int main_table;
int main_table; int top;
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<typename... Args> template <typename... Args>
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,
lua_metatable eMetatable1, Args... args) { size_t iUps, lua_metatable eMetatable1, Args... args) {
lua_pushvalue(pState->L, pState->metatables[static_cast<size_t>(eMetatable1)]); lua_pushvalue(pState->L,
luaT_setclosure(pState, fn, iUps + 1, args...); pState->metatables[static_cast<size_t>(eMetatable1)]);
luaT_setclosure(pState, fn, iUps + 1, args...);
} }
template<typename... Args> template <typename... Args>
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,
const char* str, Args... args) { size_t iUps, const char* str, Args... args) {
lua_pushstring(pState->L, str); lua_pushstring(pState->L, str);
luaT_setclosure(pState, fn, iUps + 1, args...); 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 name The name to use for the function in lua
* @param args The upvalues to associate with the function in lua * @param args The upvalues to associate with the function in lua
*/ */
template<typename... Args> template <typename... Args>
void add_lua_function(const lua_register_state* pState, lua_CFunction fn, const char* name, Args... args) void add_lua_function(const lua_register_state* pState, lua_CFunction fn,
{ const char* name, Args... args) {
luaT_setclosure(pState, fn, 0, args...); luaT_setclosure(pState, fn, 0, args...);
lua_setfield(pState->L, -2, name); 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 * This class should be immediately destructed after adding all of it's
* functions, metamethods and constants to complete the creation of the bind. * functions, metamethods and constants to complete the creation of the bind.
*/ */
template<typename T> template <typename T>
class lua_class_binding final class lua_class_binding final {
{ public:
public: lua_class_binding() = delete;
lua_class_binding()=delete; lua_class_binding(const lua_class_binding&) = delete;
lua_class_binding(const lua_class_binding&)=delete; lua_class_binding(lua_class_binding&&) = delete;
lua_class_binding(lua_class_binding&&)=delete; lua_class_binding& operator=(const lua_class_binding&) = delete;
lua_class_binding& operator= (const lua_class_binding&)=delete; lua_class_binding operator=(lua_class_binding&&) = delete;
lua_class_binding operator= (lua_class_binding&&)=delete;
/** /**
* Inititate class bindings for lua. * Inititate class bindings for lua.
* *
* @param pState The lua environment to bind to. * @param pState The lua environment to bind to.
* @param name The name to give this lua 'class'. * @param name The name to give this lua 'class'.
* @param new_fn The fuction to call when a new class is created. * @param new_fn The fuction to call when a new class is created.
* @param mt The metatable id for the class * @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) : lua_class_binding(const lua_register_state* pState, const char* name,
pState(pState), lua_CFunction new_fn, lua_metatable mt)
: pState(pState),
class_name(name), class_name(name),
class_metatable(pState->metatables[static_cast<size_t>(mt)]) class_metatable(pState->metatables[static_cast<size_t>(mt)]) {
{ lua_settop(pState->L, pState->top);
lua_settop(pState->L, pState->top); /* Make metatable the environment for registered functions */
/* Make metatable the environment for registered functions */ \ lua_pushvalue(pState->L, class_metatable);
lua_pushvalue(pState->L, class_metatable); lua_replace(pState->L, luaT_environindex);
lua_replace(pState->L, luaT_environindex); /* Set the __gc metamethod to C++ destructor */
/* Set the __gc metamethod to C++ destructor */ luaT_pushcclosure(pState->L, luaT_stdgc<T, luaT_environindex>, 0);
luaT_pushcclosure(pState->L, luaT_stdgc<T, luaT_environindex>, 0); lua_setfield(pState->L, class_metatable, "__gc");
lua_setfield(pState->L, class_metatable, "__gc"); /* Set the depersist size */
/* Set the depersist size */ lua_pushinteger(pState->L, sizeof(T));
lua_pushinteger(pState->L, sizeof(T)); lua_setfield(pState->L, class_metatable, "__depersist_size");
lua_setfield(pState->L, class_metatable, "__depersist_size"); /* Create the methods table; call it -> new instance */
/* Create the methods table; call it -> new instance */ luaT_pushcclosuretable(pState->L, new_fn, 0);
luaT_pushcclosuretable(pState->L, new_fn, 0); /* Set __class_name on the methods metatable */
/* Set __class_name on the methods metatable */ lua_getmetatable(pState->L, -1);
lua_getmetatable(pState->L, -1); lua_pushstring(pState->L, class_name);
lua_pushstring(pState->L, class_name); lua_setfield(pState->L, -2, "__class_name");
lua_setfield(pState->L, -2, "__class_name"); lua_pop(pState->L, 1);
lua_pop(pState->L, 1); /* Set __index to the methods table */
/* Set __index to the methods table */ lua_pushvalue(pState->L, -1);
lua_pushvalue(pState->L, -1); lua_setfield(pState->L, class_metatable, "__index");
lua_setfield(pState->L, class_metatable, "__index"); }
}
/** /**
* Set another class as the superclass of this class. * Set another class as the superclass of this class.
* *
* @param super_mt The metatable id of the super class. * @param super_mt The metatable id of the super class.
*/ */
void set_superclass(lua_metatable super_mt) void set_superclass(lua_metatable super_mt) {
{ lua_getmetatable(pState->L, -1);
lua_getmetatable(pState->L, -1); lua_getfield(pState->L, pState->metatables[static_cast<size_t>(super_mt)],
lua_getfield(pState->L, pState->metatables[static_cast<size_t>(super_mt)], "__index"); "__index");
lua_setfield(pState->L, -2, "__index"); lua_setfield(pState->L, -2, "__index");
lua_pop(pState->L, 1); lua_pop(pState->L, 1);
/* Set metatable[1] to super_mt */ /* Set metatable[1] to super_mt */
lua_pushvalue(pState->L, pState->metatables[static_cast<size_t>(super_mt)]); lua_pushvalue(pState->L, pState->metatables[static_cast<size_t>(super_mt)]);
lua_rawseti(pState->L, class_metatable, 1); lua_rawseti(pState->L, class_metatable, 1);
} }
/** /**
* Add a named constant to the lua interface. * Add a named constant to the lua interface.
* *
* @param name (string literal) Name of the constant. * @param name (string literal) Name of the constant.
* @param value (tested with int) Value of the constant. * @param value (tested with int) Value of the constant.
*/ */
template<typename V> template <typename V>
void add_constant(const char* name, V value) void add_constant(const char* name, V value) {
{ luaT_push(pState->L, value);
luaT_push(pState->L, value); lua_setfield(pState->L, -2, name);
lua_setfield(pState->L, -2, name); }
}
/** /**
* Add a C++ metamethod to the lua class. * Add a C++ metamethod to the lua class.
* *
* @param fn The C++ function to call. * @param fn The C++ function to call.
* @param name The name of the metamethod (without the __ prefix). * @param name The name of the metamethod (without the __ prefix).
* @param args The upvalues for the function. * @param args The upvalues for the function.
*/ */
template<typename... Args> template <typename... Args>
void add_metamethod(lua_CFunction fn, const char* name, Args... args) void add_metamethod(lua_CFunction fn, const char* name, Args... args) {
{ luaT_setclosure(pState, fn, 0, args...);
luaT_setclosure(pState, fn, 0, args...); lua_setfield(pState->L, class_metatable,
lua_setfield(pState->L, class_metatable, std::string("__").append(name).c_str()); std::string("__").append(name).c_str());
} }
/** /**
* Add a C++ function to the lua class. * Add a C++ function to the lua class.
* *
* @param fn The C++ function. * @param fn The C++ function.
* @param name The name of the function in lua. * @param name The name of the function in lua.
* @param args The upvalues for the function * @param args The upvalues for the function
*/ */
template<typename... Args> template <typename... Args>
void add_function(lua_CFunction fn, const char* name, Args... args) void add_function(lua_CFunction fn, const char* name, Args... args) {
{ add_lua_function(pState, fn, name, args...);
add_lua_function(pState, fn, name, args...); }
}
/** /**
* Destructor which finalizes the lua binding * Destructor which finalizes the lua binding
*/ */
~lua_class_binding() ~lua_class_binding() {
{ lua_setfield(pState->L, pState->main_table, class_name);
lua_setfield(pState->L, pState->main_table, class_name); }
}
private: private:
const lua_register_state* pState; const lua_register_state* pState;
const char* class_name; const char* class_name;
int class_metatable; int class_metatable;
}; };
#endif #endif

View File

@@ -20,124 +20,110 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE. SOFTWARE.
*/ */
#include "th_lua_internal.h"
#include "iso_fs.h"
#include <cstdio> #include <cstdio>
#include "iso_fs.h"
#include "th_lua_internal.h"
namespace { namespace {
int l_isofs_new(lua_State *L) int l_isofs_new(lua_State* L) {
{ luaT_stdnew<iso_filesystem>(L, luaT_environindex, true);
luaT_stdnew<iso_filesystem>(L, luaT_environindex, true); return 1;
return 1;
} }
int l_isofs_set_path_separator(lua_State *L) int l_isofs_set_path_separator(lua_State* L) {
{ iso_filesystem* pSelf = luaT_testuserdata<iso_filesystem>(L);
iso_filesystem *pSelf = luaT_testuserdata<iso_filesystem>(L); pSelf->set_path_separator(luaL_checkstring(L, 2)[0]);
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<iso_filesystem>(L);
std::FILE* fIso = *luaT_testuserdata<std::FILE*>(L, 2, false);
if (pSelf->initialise(fIso)) {
lua_pushvalue(L, 2);
luaT_setenvfield(L, 1, "file");
lua_settop(L, 1); lua_settop(L, 1);
return 1; return 1;
} else {
lua_pushnil(L);
lua_pushstring(L, pSelf->get_error());
return 2;
}
} }
int l_isofs_set_root(lua_State *L) int l_isofs_file_exists(lua_State* L) {
{ iso_filesystem* pSelf = luaT_testuserdata<iso_filesystem>(L);
iso_filesystem *pSelf = luaT_testuserdata<iso_filesystem>(L); const char* sFilename = luaL_checkstring(L, 2);
std::FILE *fIso = *luaT_testuserdata<std::FILE*>(L, 2, false); iso_filesystem::file_handle iFile = pSelf->find_file(sFilename);
if(pSelf->initialise(fIso)) if (!iso_filesystem::isHandleGood(iFile)) {
{ lua_pushnil(L);
lua_pushvalue(L, 2); lua_pushfstring(L, "Could not find \'%s\' in .iso image", sFilename);
luaT_setenvfield(L, 1, "file"); return 2;
lua_settop(L, 1); }
return 1; lua_pushboolean(L, true);
} return 1;
else
{
lua_pushnil(L);
lua_pushstring(L, pSelf->get_error());
return 2;
}
} }
int l_isofs_file_exists(lua_State *L) int l_isofs_file_size(lua_State* L) {
{ iso_filesystem* pSelf = luaT_testuserdata<iso_filesystem>(L);
iso_filesystem *pSelf = luaT_testuserdata<iso_filesystem>(L); const char* sFilename = luaL_checkstring(L, 2);
const char* sFilename = luaL_checkstring(L, 2); iso_filesystem::file_handle iFile = pSelf->find_file(sFilename);
iso_filesystem::file_handle iFile = pSelf->find_file(sFilename); if (!iso_filesystem::isHandleGood(iFile)) {
if(!iso_filesystem::isHandleGood(iFile)) lua_pushnil(L);
{ lua_pushfstring(L, "Could not find \'%s\' in .iso image", sFilename);
lua_pushnil(L); return 2;
lua_pushfstring(L, "Could not find \'%s\' in .iso image", sFilename); }
return 2; lua_pushinteger(L, pSelf->get_file_size(iFile));
} return 1;
lua_pushboolean(L, true);
return 1;
} }
int l_isofs_file_size(lua_State *L) int l_isofs_read_contents(lua_State* L) {
{ iso_filesystem* pSelf = luaT_testuserdata<iso_filesystem>(L);
iso_filesystem *pSelf = luaT_testuserdata<iso_filesystem>(L); const char* sFilename = luaL_checkstring(L, 2);
const char* sFilename = luaL_checkstring(L, 2); iso_filesystem::file_handle iFile = pSelf->find_file(sFilename);
iso_filesystem::file_handle iFile = pSelf->find_file(sFilename); if (!iso_filesystem::isHandleGood(iFile)) {
if(!iso_filesystem::isHandleGood(iFile)) lua_pushnil(L);
{ lua_pushfstring(L, "Could not find \'%s\' in .iso image", sFilename);
lua_pushnil(L); return 2;
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<uint8_t*>(pBuffer))) {
lua_pushinteger(L, pSelf->get_file_size(iFile)); lua_pushnil(L);
return 1; lua_pushstring(L, pSelf->get_error());
return 2;
}
lua_pushlstring(L, reinterpret_cast<char*>(pBuffer),
pSelf->get_file_size(iFile));
return 1;
} }
int l_isofs_read_contents(lua_State *L) void l_isofs_list_files_callback(void* p, const char* name, const char* path) {
{ lua_State* L = reinterpret_cast<lua_State*>(p);
iso_filesystem *pSelf = luaT_testuserdata<iso_filesystem>(L); lua_pushstring(L, name);
const char* sFilename = luaL_checkstring(L, 2); lua_pushstring(L, path);
iso_filesystem::file_handle iFile = pSelf->find_file(sFilename); lua_settable(L, 3);
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<uint8_t*>(pBuffer)))
{
lua_pushnil(L);
lua_pushstring(L, pSelf->get_error());
return 2;
}
lua_pushlstring(L, reinterpret_cast<char*>(pBuffer), pSelf->get_file_size(iFile));
return 1;
} }
void l_isofs_list_files_callback(void *p, const char* name, const char* path) int l_isofs_list_files(lua_State* L) {
{ iso_filesystem* pSelf = luaT_testuserdata<iso_filesystem>(L);
lua_State *L = reinterpret_cast<lua_State*>(p); const char* sPath = luaL_checkstring(L, 2);
lua_pushstring(L, name); lua_settop(L, 2);
lua_pushstring(L, path); lua_newtable(L);
lua_settable(L, 3); pSelf->visit_directory_files(sPath, l_isofs_list_files_callback, L);
return 1;
} }
int l_isofs_list_files(lua_State *L) } // namespace
{
iso_filesystem *pSelf = luaT_testuserdata<iso_filesystem>(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 void lua_register_iso_fs(const lua_register_state* pState) {
lua_class_binding<iso_filesystem> lcb(pState, "iso_fs", l_isofs_new,
void lua_register_iso_fs(const lua_register_state* pState) lua_metatable::iso_fs);
{ lcb.add_function(l_isofs_set_path_separator, "setPathSeparator");
lua_class_binding<iso_filesystem> lcb(pState, "iso_fs", l_isofs_new, lua_metatable::iso_fs); lcb.add_function(l_isofs_set_root, "setRoot");
lcb.add_function(l_isofs_set_path_separator, "setPathSeparator"); lcb.add_function(l_isofs_file_exists, "fileExists");
lcb.add_function(l_isofs_set_root, "setRoot"); lcb.add_function(l_isofs_file_size, "fileSize");
lcb.add_function(l_isofs_file_exists, "fileExists"); lcb.add_function(l_isofs_read_contents, "readContents");
lcb.add_function(l_isofs_file_size, "fileSize"); lcb.add_function(l_isofs_list_files, "listFiles");
lcb.add_function(l_isofs_read_contents, "readContents");
lcb.add_function(l_isofs_list_files, "listFiles");
} }

View File

@@ -21,8 +21,8 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE. SOFTWARE.
*/ */
#include "th_lua_internal.h"
#include "config.h" #include "config.h"
#include "th_lua_internal.h"
#ifdef CORSIX_TH_USE_WIN32_SDK #ifdef CORSIX_TH_USE_WIN32_SDK
#include <windows.h> #include <windows.h>
#endif #endif
@@ -31,77 +31,68 @@ class lfs_ext {};
namespace { namespace {
int l_lfs_ext_new(lua_State *L) int l_lfs_ext_new(lua_State* L) {
{ luaT_stdnew<lfs_ext>(L, luaT_environindex, true);
luaT_stdnew<lfs_ext>(L, luaT_environindex, true); return 1;
return 1;
} }
#ifdef _WIN32 #ifdef _WIN32
#ifdef CORSIX_TH_USE_WIN32_SDK #ifdef CORSIX_TH_USE_WIN32_SDK
int l_volume_list(lua_State *L) int l_volume_list(lua_State* L) {
{ /* Windows, using the Win32 API. */
/* Windows, using the Win32 API. */ DWORD iDriveMask = GetLogicalDrives();
DWORD iDriveMask = GetLogicalDrives(); int iNDrives = 0;
int iNDrives = 0; char cDrive;
char cDrive; lua_settop(L, 0);
lua_settop(L, 0); lua_newtable(L);
lua_newtable(L); for (cDrive = 'A'; cDrive <= 'Z'; ++cDrive) {
for (cDrive = 'A'; cDrive <= 'Z'; ++cDrive) if (iDriveMask & (1 << (cDrive - 'A'))) {
{ char sName[4] = {cDrive, ':', '\\', 0};
if (iDriveMask & (1 << (cDrive - 'A'))) if (GetDriveTypeA(sName) > DRIVE_NO_ROOT_DIR) {
{ lua_pushlstring(L, sName, 2);
char sName[4] = { cDrive, ':', '\\', 0 }; lua_rawseti(L, 1, ++iNDrives);
if (GetDriveTypeA(sName) > DRIVE_NO_ROOT_DIR) }
{
lua_pushlstring(L, sName, 2);
lua_rawseti(L, 1, ++iNDrives);
}
}
} }
return 1; }
return 1;
} }
#else #else
int l_volume_list(lua_State *L) int l_volume_list(lua_State* L) {
{ /* Windows, without the Win32 API. */
/* Windows, without the Win32 API. */ int iNDrives = 0;
int iNDrives = 0; char cDrive;
char cDrive; lua_settop(L, 0);
lua_settop(L, 0); lua_newtable(L);
lua_newtable(L); lua_getfield(L, luaT_upvalueindex(1), "attributes");
lua_getfield(L, luaT_upvalueindex(1), "attributes"); for (cDrive = 'A'; cDrive <= 'Z'; ++cDrive) {
for (cDrive = 'A'; cDrive <= 'Z'; ++cDrive) lua_pushvalue(L, 2);
{ lua_pushfstring(L, "%c:\\", cDrive);
lua_pushvalue(L, 2); lua_pushliteral(L, "mode");
lua_pushfstring(L, "%c:\\", cDrive); lua_call(L, 2, 1);
lua_pushliteral(L, "mode"); if (lua_toboolean(L, 3) != 0) {
lua_call(L, 2, 1); lua_pushfstring(L, "%c:", cDrive);
if (lua_toboolean(L, 3) != 0) lua_rawseti(L, 1, ++iNDrives);
{
lua_pushfstring(L, "%c:", cDrive);
lua_rawseti(L, 1, ++iNDrives);
}
lua_pop(L, 1);
} }
return 1; lua_pop(L, 1);
}
return 1;
} }
#endif #endif
#else #else
int l_volume_list(lua_State *L) int l_volume_list(lua_State* L) {
{ /* Non-Windows systems. Assume that / is the root of the filesystem. */
/* Non-Windows systems. Assume that / is the root of the filesystem. */ lua_settop(L, 0);
lua_settop(L, 0); lua_newtable(L);
lua_newtable(L); lua_pushliteral(L, "/");
lua_pushliteral(L, "/"); lua_rawseti(L, 1, 1);
lua_rawseti(L, 1, 1); return 1;
return 1;
} }
#endif #endif
} // namespace } // namespace
void lua_register_lfs_ext(const lua_register_state *pState) void lua_register_lfs_ext(const lua_register_state* pState) {
{ lua_class_binding<lfs_ext> lcb(pState, "lfsExt", l_lfs_ext_new,
lua_class_binding<lfs_ext> lcb(pState, "lfsExt", l_lfs_ext_new, lua_metatable::lfs_ext); lua_metatable::lfs_ext);
lcb.add_function(l_volume_list, "volumes"); lcb.add_function(l_volume_list, "volumes");
} }

File diff suppressed because it is too large Load Diff

View File

@@ -20,134 +20,120 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE. SOFTWARE.
*/ */
#include "th_gfx.h"
#include "th_lua_internal.h" #include "th_lua_internal.h"
#include "th_movie.h" #include "th_movie.h"
#include "th_gfx.h"
namespace { namespace {
int l_movie_new(lua_State *L) int l_movie_new(lua_State* L) {
{ luaT_stdnew<movie_player>(L, luaT_environindex, true);
luaT_stdnew<movie_player>(L, luaT_environindex, true); return 1;
return 1;
} }
int l_movie_set_renderer(lua_State *L) int l_movie_set_renderer(lua_State* L) {
{ movie_player* pMovie = luaT_testuserdata<movie_player>(L);
movie_player *pMovie = luaT_testuserdata<movie_player>(L); render_target* pRenderTarget = luaT_testuserdata<render_target>(L, 2);
render_target *pRenderTarget = luaT_testuserdata<render_target>(L, 2); pMovie->set_renderer(pRenderTarget->get_renderer());
pMovie->set_renderer(pRenderTarget->get_renderer()); return 0;
return 0;
} }
int l_movie_enabled(lua_State *L) int l_movie_enabled(lua_State* L) {
{ movie_player* pMovie = luaT_testuserdata<movie_player>(L);
movie_player *pMovie = luaT_testuserdata<movie_player>(L); lua_pushboolean(L, pMovie->movies_enabled());
lua_pushboolean(L, pMovie->movies_enabled()); return 1;
return 1;
} }
int l_movie_load(lua_State *L) int l_movie_load(lua_State* L) {
{ bool loaded;
bool loaded; const char* warning;
const char* warning; movie_player* pMovie = luaT_testuserdata<movie_player>(L);
movie_player *pMovie = luaT_testuserdata<movie_player>(L); const char* filepath = lua_tolstring(L, 2, nullptr);
const char* filepath = lua_tolstring(L, 2, nullptr); pMovie->clear_last_error();
pMovie->clear_last_error(); loaded = pMovie->load(filepath);
loaded = pMovie->load(filepath); warning = pMovie->get_last_error();
warning = pMovie->get_last_error(); lua_pushboolean(L, loaded);
lua_pushboolean(L, loaded); lua_pushstring(L, warning);
lua_pushstring(L, warning); return 2;
return 2;
} }
int l_movie_unload(lua_State *L) int l_movie_unload(lua_State* L) {
{ movie_player* pMovie = luaT_testuserdata<movie_player>(L);
movie_player *pMovie = luaT_testuserdata<movie_player>(L); pMovie->unload();
pMovie->unload(); return 0;
return 0;
} }
int l_movie_play(lua_State *L) int l_movie_play(lua_State* L) {
{ const char* warning;
const char* warning; movie_player* pMovie = luaT_testuserdata<movie_player>(L);
movie_player *pMovie = luaT_testuserdata<movie_player>(L); pMovie->clear_last_error();
pMovie->clear_last_error(); pMovie->play(static_cast<int>(luaL_checkinteger(L, 2)));
pMovie->play( warning = pMovie->get_last_error();
static_cast<int>(luaL_checkinteger(L, 2))); lua_pushstring(L, warning);
warning = pMovie->get_last_error(); return 1;
lua_pushstring(L, warning);
return 1;
} }
int l_movie_stop(lua_State *L) int l_movie_stop(lua_State* L) {
{ movie_player* pVideo = luaT_testuserdata<movie_player>(L);
movie_player *pVideo = luaT_testuserdata<movie_player>(L); pVideo->stop();
pVideo->stop(); return 0;
return 0;
} }
int l_movie_get_native_height(lua_State *L) int l_movie_get_native_height(lua_State* L) {
{ movie_player* pMovie = luaT_testuserdata<movie_player>(L);
movie_player *pMovie = luaT_testuserdata<movie_player>(L); lua_pushinteger(L, pMovie->get_native_height());
lua_pushinteger(L, pMovie->get_native_height()); return 1;
return 1;
} }
int l_movie_get_native_width(lua_State *L) int l_movie_get_native_width(lua_State* L) {
{ movie_player* pMovie = luaT_testuserdata<movie_player>(L);
movie_player *pMovie = luaT_testuserdata<movie_player>(L); lua_pushinteger(L, pMovie->get_native_width());
lua_pushinteger(L, pMovie->get_native_width()); return 1;
return 1;
} }
int l_movie_has_audio_track(lua_State *L) int l_movie_has_audio_track(lua_State* L) {
{ movie_player* pMovie = luaT_testuserdata<movie_player>(L);
movie_player *pMovie = luaT_testuserdata<movie_player>(L); lua_pushboolean(L, pMovie->has_audio_track());
lua_pushboolean(L, pMovie->has_audio_track()); return 1;
return 1;
} }
int l_movie_refresh(lua_State *L) int l_movie_refresh(lua_State* L) {
{ movie_player* pMovie = luaT_testuserdata<movie_player>(L);
movie_player *pMovie = luaT_testuserdata<movie_player>(L); pMovie->refresh(SDL_Rect{static_cast<int>(luaL_checkinteger(L, 2)),
pMovie->refresh(SDL_Rect{ static_cast<int>(luaL_checkinteger(L, 3)),
static_cast<int>(luaL_checkinteger(L, 2)), static_cast<int>(luaL_checkinteger(L, 4)),
static_cast<int>(luaL_checkinteger(L, 3)), static_cast<int>(luaL_checkinteger(L, 5))});
static_cast<int>(luaL_checkinteger(L, 4)), return 0;
static_cast<int>(luaL_checkinteger(L, 5)) });
return 0;
} }
int l_movie_allocate_picture_buffer(lua_State *L) int l_movie_allocate_picture_buffer(lua_State* L) {
{ movie_player* pMovie = luaT_testuserdata<movie_player>(L);
movie_player *pMovie = luaT_testuserdata<movie_player>(L); pMovie->allocate_picture_buffer();
pMovie->allocate_picture_buffer(); return 0;
return 0;
} }
int l_movie_deallocate_picture_buffer(lua_State *L) int l_movie_deallocate_picture_buffer(lua_State* L) {
{ movie_player* pMovie = luaT_testuserdata<movie_player>(L);
movie_player *pMovie = luaT_testuserdata<movie_player>(L); pMovie->deallocate_picture_buffer();
pMovie->deallocate_picture_buffer(); return 0;
return 0;
} }
} // namespace } // namespace
void lua_register_movie(const lua_register_state *pState) void lua_register_movie(const lua_register_state* pState) {
{ lua_class_binding<movie_player> lcb(pState, "moviePlayer", l_movie_new,
lua_class_binding<movie_player> lcb(pState, "moviePlayer", l_movie_new, lua_metatable::movie); lua_metatable::movie);
lcb.add_function(l_movie_set_renderer, "setRenderer", lua_metatable::surface); lcb.add_function(l_movie_set_renderer, "setRenderer", lua_metatable::surface);
lcb.add_function(l_movie_enabled, "getEnabled"); lcb.add_function(l_movie_enabled, "getEnabled");
lcb.add_function(l_movie_load, "load"); lcb.add_function(l_movie_load, "load");
lcb.add_function(l_movie_unload, "unload"); lcb.add_function(l_movie_unload, "unload");
lcb.add_function(l_movie_play, "play"); lcb.add_function(l_movie_play, "play");
lcb.add_function(l_movie_stop, "stop"); lcb.add_function(l_movie_stop, "stop");
lcb.add_function(l_movie_get_native_height, "getNativeHeight"); lcb.add_function(l_movie_get_native_height, "getNativeHeight");
lcb.add_function(l_movie_get_native_width, "getNativeWidth"); lcb.add_function(l_movie_get_native_width, "getNativeWidth");
lcb.add_function(l_movie_has_audio_track, "hasAudioTrack"); lcb.add_function(l_movie_has_audio_track, "hasAudioTrack");
lcb.add_function(l_movie_refresh, "refresh"); lcb.add_function(l_movie_refresh, "refresh");
lcb.add_function(l_movie_allocate_picture_buffer, "allocatePictureBuffer"); lcb.add_function(l_movie_allocate_picture_buffer, "allocatePictureBuffer");
lcb.add_function(l_movie_deallocate_picture_buffer, "deallocatePictureBuffer"); lcb.add_function(l_movie_deallocate_picture_buffer,
"deallocatePictureBuffer");
} }

View File

@@ -20,44 +20,41 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE. SOFTWARE.
*/ */
#include "th_lua_internal.h" #include <cctype>
#include "th_sound.h"
#include "th_lua.h"
#include "lua_sdl.h"
#include <cstring> #include <cstring>
#include <map> #include <map>
#include <cctype> #include "lua_sdl.h"
#include "th_lua.h"
#include "th_lua_internal.h"
#include "th_sound.h"
namespace { namespace {
int played_sound_callback_ids[1000]; int played_sound_callback_ids[1000];
int played_sound_callback_index = 0; int played_sound_callback_index = 0;
std::map<int,SDL_TimerID> map_sound_timers; std::map<int, SDL_TimerID> map_sound_timers;
int l_soundarc_new(lua_State *L) int l_soundarc_new(lua_State* L) {
{ luaT_stdnew<sound_archive>(L, luaT_environindex, true);
luaT_stdnew<sound_archive>(L, luaT_environindex, true); return 1;
return 1;
} }
int l_soundarc_load(lua_State *L) int l_soundarc_load(lua_State* L) {
{ sound_archive* pArchive = luaT_testuserdata<sound_archive>(L);
sound_archive* pArchive = luaT_testuserdata<sound_archive>(L); size_t iDataLen;
size_t iDataLen; const uint8_t* pData = luaT_checkfile(L, 2, &iDataLen);
const uint8_t* pData = luaT_checkfile(L, 2, &iDataLen);
if(pArchive->load_from_th_file(pData, iDataLen)) if (pArchive->load_from_th_file(pData, iDataLen))
lua_pushboolean(L, 1); lua_pushboolean(L, 1);
else else
lua_pushboolean(L, 0); lua_pushboolean(L, 0);
return 1; return 1;
} }
int l_soundarc_count(lua_State *L) int l_soundarc_count(lua_State* L) {
{ sound_archive* pArchive = luaT_testuserdata<sound_archive>(L);
sound_archive* pArchive = luaT_testuserdata<sound_archive>(L); lua_pushnumber(L, (lua_Number)pArchive->get_number_of_sounds());
lua_pushnumber(L, (lua_Number)pArchive->get_number_of_sounds()); return 1;
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 * @return Negative number when \a s1 should be before \a s2, zero if both
* string are equal, else a positive number. * string are equal, else a positive number.
*/ */
int ignorecase_cmp(const char *s1, const char *s2) int ignorecase_cmp(const char* s1, const char* s2) {
{ while (*s1 && *s2) {
while(*s1 && *s2) if (std::tolower(*s1) != std::tolower(*s2)) break;
{
if (std::tolower(*s1) != std::tolower(*s2))
break;
s1++; s1++;
s2++; s2++;
} }
return std::tolower(*s1) - std::tolower(*s2); return std::tolower(*s1) - std::tolower(*s2);
} }
size_t l_soundarc_checkidx(lua_State *L, int iArg, sound_archive* pArchive) size_t l_soundarc_checkidx(lua_State* L, int iArg, sound_archive* pArchive) {
{ if (lua_isnumber(L, iArg)) {
if(lua_isnumber(L, iArg)) size_t iIndex = (size_t)lua_tonumber(L, iArg);
{ if (iIndex >= pArchive->get_number_of_sounds()) {
size_t iIndex = (size_t)lua_tonumber(L, iArg); lua_pushnil(L);
if(iIndex >= pArchive->get_number_of_sounds()) lua_pushfstring(L,
{ "Sound index out of "
lua_pushnil(L); "bounds (%f is not in range [0, %d])",
lua_pushfstring(L, "Sound index out of " lua_tonumber(L, iArg),
"bounds (%f is not in range [0, %d])", lua_tonumber(L, iArg), static_cast<int>(pArchive->get_number_of_sounds()) - 1);
static_cast<int>(pArchive->get_number_of_sounds()) - 1); return pArchive->get_number_of_sounds();
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;
} }
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); lua_pop(L, 2);
size_t iCount = pArchive->get_number_of_sounds(); return iIndex;
for(size_t i = 0; i < iCount; ++i) }
{ lua_pop(L, 2);
if(ignorecase_cmp(sName, pArchive->get_sound_name(i)) == 0) size_t iCount = pArchive->get_number_of_sounds();
{ for (size_t i = 0; i < iCount; ++i) {
lua_getfenv(L, 1); if (ignorecase_cmp(sName, pArchive->get_sound_name(i)) == 0) {
lua_pushvalue(L, iArg); lua_getfenv(L, 1);
lua_pushlightuserdata(L, (void*)i); lua_pushvalue(L, iArg);
lua_settable(L, -3); lua_pushlightuserdata(L, (void*)i);
lua_pop(L, 1); lua_settable(L, -3);
return i; lua_pop(L, 1);
} return i;
} }
lua_pushnil(L); }
lua_pushliteral(L, "File not found in sound archive: "); lua_pushnil(L);
lua_pushvalue(L, iArg); lua_pushliteral(L, "File not found in sound archive: ");
lua_concat(L, 2); lua_pushvalue(L, iArg);
return pArchive->get_number_of_sounds(); lua_concat(L, 2);
return pArchive->get_number_of_sounds();
} }
int l_soundarc_sound_name(lua_State *L) int l_soundarc_sound_name(lua_State* L) {
{ sound_archive* pArchive = luaT_testuserdata<sound_archive>(L);
sound_archive* pArchive = luaT_testuserdata<sound_archive>(L); size_t iIndex = l_soundarc_checkidx(L, 2, pArchive);
size_t iIndex = l_soundarc_checkidx(L, 2, pArchive); if (iIndex == pArchive->get_number_of_sounds()) return 2;
if(iIndex == pArchive->get_number_of_sounds()) lua_pushstring(L, pArchive->get_sound_name(iIndex));
return 2; return 1;
lua_pushstring(L, pArchive->get_sound_name(iIndex));
return 1;
} }
int l_soundarc_duration(lua_State *L) int l_soundarc_duration(lua_State* L) {
{ sound_archive* pArchive = luaT_testuserdata<sound_archive>(L);
sound_archive* pArchive = luaT_testuserdata<sound_archive>(L); size_t iIndex = l_soundarc_checkidx(L, 2, pArchive);
size_t iIndex = l_soundarc_checkidx(L, 2, pArchive); if (iIndex == pArchive->get_number_of_sounds()) return 2;
if(iIndex == pArchive->get_number_of_sounds()) size_t iDuration = pArchive->get_sound_duration(iIndex);
return 2; lua_pushnumber(
size_t iDuration = pArchive->get_sound_duration(iIndex); L, static_cast<lua_Number>(iDuration) / static_cast<lua_Number>(1000));
lua_pushnumber(L, static_cast<lua_Number>(iDuration) / static_cast<lua_Number>(1000)); return 1;
return 1;
} }
int l_soundarc_data(lua_State *L) int l_soundarc_data(lua_State* L) {
{ sound_archive* pArchive = luaT_testuserdata<sound_archive>(L);
sound_archive* pArchive = luaT_testuserdata<sound_archive>(L); size_t iIndex = l_soundarc_checkidx(L, 2, pArchive);
size_t iIndex = l_soundarc_checkidx(L, 2, pArchive); if (iIndex == pArchive->get_number_of_sounds()) return 2;
if(iIndex == pArchive->get_number_of_sounds()) SDL_RWops* pRWops = pArchive->load_sound(iIndex);
return 2; if (!pRWops) return 0;
SDL_RWops *pRWops = pArchive->load_sound(iIndex); size_t iLength = SDL_RWseek(pRWops, 0, SEEK_END);
if(!pRWops) SDL_RWseek(pRWops, 0, SEEK_SET);
return 0; // There is a potential leak of pRWops if either of these Lua calls cause
size_t iLength = SDL_RWseek(pRWops, 0, SEEK_END); // a memory error, but it isn't very likely, and this a debugging function
SDL_RWseek(pRWops, 0, SEEK_SET); // anyway, so it isn't very important.
// There is a potential leak of pRWops if either of these Lua calls cause void* pBuffer = lua_newuserdata(L, iLength);
// a memory error, but it isn't very likely, and this a debugging function lua_pushlstring(L, (const char*)pBuffer,
// anyway, so it isn't very important. SDL_RWread(pRWops, pBuffer, 1, iLength));
void *pBuffer = lua_newuserdata(L, iLength); SDL_RWclose(pRWops);
lua_pushlstring(L, (const char*)pBuffer, return 1;
SDL_RWread(pRWops, pBuffer, 1, iLength));
SDL_RWclose(pRWops);
return 1;
} }
int l_soundarc_sound_exists(lua_State *L) int l_soundarc_sound_exists(lua_State* L) {
{ sound_archive* pArchive = luaT_testuserdata<sound_archive>(L);
sound_archive* pArchive = luaT_testuserdata<sound_archive>(L); size_t iIndex = l_soundarc_checkidx(L, 2, pArchive);
size_t iIndex = l_soundarc_checkidx(L, 2, pArchive); if (iIndex == pArchive->get_number_of_sounds())
if(iIndex == pArchive->get_number_of_sounds()) lua_pushboolean(L, 0);
lua_pushboolean(L, 0); else
else
lua_pushboolean(L, 1);
return 1;
}
int l_soundfx_new(lua_State *L)
{
luaT_stdnew<sound_player>(L, luaT_environindex, true);
return 1;
}
int l_soundfx_set_archive(lua_State *L)
{
sound_player *pEffects = luaT_testuserdata<sound_player>(L);
sound_archive *pArchive = luaT_testuserdata<sound_archive>(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<sound_player>(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<sound_player>(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<int*>(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<sound_player>(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<int>(luaL_checkinteger(L, 4)), static_cast<int>(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<int>(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<int>(luaL_checkinteger(L, 6));
size_t interval = pArchive->get_sound_duration(iIndex) + iPlayedCallbackDelay;
SDL_TimerID timersID = SDL_AddTimer(static_cast<Uint32>(interval),
played_sound_callback,
&(played_sound_callback_ids[played_sound_callback_index]));
map_sound_timers.insert(std::pair<int, SDL_TimerID>(played_sound_callback_ids[played_sound_callback_index], timersID));
played_sound_callback_index++;
}
lua_pushboolean(L, 1); lua_pushboolean(L, 1);
return 1; return 1;
} }
int l_soundfx_set_camera(lua_State *L) int l_soundfx_new(lua_State* L) {
{ luaT_stdnew<sound_player>(L, luaT_environindex, true);
sound_player *pEffects = luaT_testuserdata<sound_player>(L); return 1;
pEffects->set_camera(static_cast<int>(luaL_checkinteger(L, 2)), static_cast<int>(luaL_checkinteger(L, 3)), static_cast<int>(luaL_checkinteger(L, 4))); }
int l_soundfx_set_archive(lua_State* L) {
sound_player* pEffects = luaT_testuserdata<sound_player>(L);
sound_archive* pArchive = luaT_testuserdata<sound_archive>(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<sound_player>(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<sound_player>(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<int*>(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<sound_player>(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; 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<int>(luaL_checkinteger(L, 4)),
static_cast<int>(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<int>(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<int>(luaL_checkinteger(L, 6));
size_t interval =
pArchive->get_sound_duration(iIndex) + iPlayedCallbackDelay;
SDL_TimerID timersID =
SDL_AddTimer(static_cast<Uint32>(interval), played_sound_callback,
&(played_sound_callback_ids[played_sound_callback_index]));
map_sound_timers.insert(std::pair<int, SDL_TimerID>(
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 l_soundfx_set_camera(lua_State* L) {
{ sound_player* pEffects = luaT_testuserdata<sound_player>(L);
int iChannel; pEffects->set_camera(static_cast<int>(luaL_checkinteger(L, 2)),
sound_player *pEffects = luaT_testuserdata<sound_player>(L); static_cast<int>(luaL_checkinteger(L, 3)),
iChannel = pEffects->reserve_channel(); static_cast<int>(luaL_checkinteger(L, 4)));
lua_pushinteger(L, iChannel); return 0;
return 1;
} }
int l_soundfx_release_channel(lua_State *L) int l_soundfx_reserve_channel(lua_State* L) {
{ int iChannel;
sound_player *pEffects = luaT_testuserdata<sound_player>(L); sound_player* pEffects = luaT_testuserdata<sound_player>(L);
pEffects->release_channel(static_cast<int>(luaL_checkinteger(L, 2))); iChannel = pEffects->reserve_channel();
return 1; lua_pushinteger(L, iChannel);
return 1;
} }
} // namespace int l_soundfx_release_channel(lua_State* L) {
sound_player* pEffects = luaT_testuserdata<sound_player>(L);
void lua_register_sound(const lua_register_state *pState) pEffects->release_channel(static_cast<int>(luaL_checkinteger(L, 2)));
{ return 1;
// Sound Archive }
{
lua_class_binding<sound_archive> lcb(pState, "soundArchive", l_soundarc_new, lua_metatable::sound_archive); } // namespace
lcb.add_metamethod(l_soundarc_count, "len");
lcb.add_function(l_soundarc_load, "load"); void lua_register_sound(const lua_register_state* pState) {
lcb.add_function(l_soundarc_sound_name, "getFilename"); // Bad name, doesn't represent a file // Sound Archive
lcb.add_function(l_soundarc_duration, "getDuration"); {
lcb.add_function(l_soundarc_data, "getFileData"); // Bad name, doesn't represent a file lua_class_binding<sound_archive> lcb(pState, "soundArchive", l_soundarc_new,
lcb.add_function(l_soundarc_sound_exists, "soundExists"); lua_metatable::sound_archive);
} lcb.add_metamethod(l_soundarc_count, "len");
lcb.add_function(l_soundarc_load, "load");
// Sound Effects lcb.add_function(l_soundarc_sound_name,
{ "getFilename"); // Bad name, doesn't represent a file
lua_class_binding<sound_player> lcb(pState, "soundEffects", l_soundfx_new, lua_metatable::sound_fx); lcb.add_function(l_soundarc_duration, "getDuration");
lcb.add_function(l_soundfx_set_archive, "setSoundArchive", lua_metatable::sound_archive); lcb.add_function(l_soundarc_data,
lcb.add_function(l_soundfx_play, "play"); "getFileData"); // Bad name, doesn't represent a file
lcb.add_function(l_soundfx_set_sound_volume, "setSoundVolume"); lcb.add_function(l_soundarc_sound_exists, "soundExists");
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"); // Sound Effects
lcb.add_function(l_soundfx_release_channel, "releaseChannel"); {
} lua_class_binding<sound_player> 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");
}
} }

File diff suppressed because it is too large Load Diff

View File

@@ -20,156 +20,152 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE. SOFTWARE.
*/ */
#include "th_lua_internal.h"
#include "th_gfx.h"
#include "th_map.h"
#include <algorithm> #include <algorithm>
#include "th_gfx.h"
#include "th_lua_internal.h"
#include "th_map.h"
class abstract_window {}; class abstract_window {};
namespace { namespace {
int l_abstract_window_new(lua_State *L) int l_abstract_window_new(lua_State* L) {
{ return luaL_error(L,
return luaL_error(L, "windowBase can only be used a base class - " "windowBase can only be used a base class - "
" do not create a windowBase directly."); " 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) uint8_t range_scale(uint16_t low, uint16_t high, uint16_t val, uint16_t start,
{ uint16_t end) {
return static_cast<uint8_t>(std::max(start + (end - start) * (val - low) / (high - low), 0xFF)); return static_cast<uint8_t>(
std::max(start + (end - start) * (val - low) / (high - low), 0xFF));
} }
inline bool is_wall(uint16_t blk) inline bool is_wall(uint16_t blk) {
{ return ((82 <= ((blk)&0xFF)) && (((blk)&0xFF) <= 164));
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) 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]); 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) int l_town_map_draw(lua_State* L) {
{ luaL_checktype(L, 1, LUA_TTABLE);
luaL_checktype(L, 1, LUA_TTABLE); level_map* pMap = luaT_testuserdata<level_map>(L, 2);
level_map* pMap = luaT_testuserdata<level_map>(L, 2); render_target* pCanvas = luaT_testuserdata<render_target>(L, 3);
render_target *pCanvas = luaT_testuserdata<render_target>(L, 3); int iCanvasXBase = static_cast<int>(luaL_checkinteger(L, 4));
int iCanvasXBase = static_cast<int>(luaL_checkinteger(L, 4)); int iCanvasYBase = static_cast<int>(luaL_checkinteger(L, 5));
int iCanvasYBase = static_cast<int>(luaL_checkinteger(L, 5)); bool bShowHeat = lua_toboolean(L, 6) != 0;
bool bShowHeat = lua_toboolean(L, 6) != 0;
uint32_t iColourMyHosp = render_target::map_colour(0, 0, 70); uint32_t iColourMyHosp = render_target::map_colour(0, 0, 70);
uint32_t iColourWall = render_target::map_colour(255, 255, 255); uint32_t iColourWall = render_target::map_colour(255, 255, 255);
uint32_t iColourDoor = render_target::map_colour(200, 200, 200); uint32_t iColourDoor = render_target::map_colour(200, 200, 200);
uint32_t iColourPurchasable = render_target::map_colour(255, 0, 0); uint32_t iColourPurchasable = render_target::map_colour(255, 0, 0);
const map_tile *pNode = pMap->get_tile_unchecked(0, 0); const map_tile* pNode = pMap->get_tile_unchecked(0, 0);
const map_tile *pOriginalNode = pMap->get_original_tile_unchecked(0, 0); const map_tile* pOriginalNode = pMap->get_original_tile_unchecked(0, 0);
int iCanvasY = iCanvasYBase + 3; int iCanvasY = iCanvasYBase + 3;
int iMapWidth = pMap->get_width(); int iMapWidth = pMap->get_width();
for(int iY = 0; iY < pMap->get_height(); ++iY, iCanvasY += 3) for (int iY = 0; iY < pMap->get_height(); ++iY, iCanvasY += 3) {
{ int iCanvasX = iCanvasXBase;
int iCanvasX = iCanvasXBase; for (int iX = 0; iX < iMapWidth;
for(int iX = 0; iX < iMapWidth; ++iX, ++pNode, ++pOriginalNode, iCanvasX += 3) ++iX, ++pNode, ++pOriginalNode, iCanvasX += 3) {
{ if (pOriginalNode->flags.hospital) {
if(pOriginalNode->flags.hospital) uint32_t iColour = iColourMyHosp;
{ if (!(pNode->flags.hospital)) {
uint32_t iColour = iColourMyHosp; // TODO: Replace 1 with player number
if(!(pNode->flags.hospital)) if (pMap->is_parcel_purchasable(pNode->iParcelId, 1))
{ iColour = iColourPurchasable;
// TODO: Replace 1 with player number else
if(pMap->is_parcel_purchasable(pNode->iParcelId, 1)) goto dont_paint_tile;
iColour = iColourPurchasable; } else if (bShowHeat) {
else uint16_t iTemp = pMap->get_tile_temperature(pNode);
goto dont_paint_tile; if (iTemp < 5200) // Less than 4 degrees
} iTemp = 0;
else if(bShowHeat) else if (iTemp > 32767) // More than 25 degrees
{ iTemp = 255;
uint16_t iTemp = pMap->get_tile_temperature(pNode); else // NB: 108 == (32767 - 5200) / 255
if(iTemp < 5200) // Less than 4 degrees iTemp = static_cast<uint16_t>((iTemp - 5200) / 108);
iTemp = 0;
else if(iTemp > 32767) // More than 25 degrees
iTemp = 255;
else // NB: 108 == (32767 - 5200) / 255
iTemp = static_cast<uint16_t>((iTemp - 5200) / 108);
const uint16_t minOkTemp = 140; const uint16_t minOkTemp = 140;
const uint16_t maxOkTemp = 180; const uint16_t maxOkTemp = 180;
uint8_t iR = 0; uint8_t iR = 0;
uint8_t iG = 0; uint8_t iG = 0;
uint8_t iB = 0; uint8_t iB = 0;
switch(pMap->get_temperature_display()) switch (pMap->get_temperature_display()) {
{ case temperature_theme::multi_colour:
case temperature_theme::multi_colour: iB = 70;
iB = 70; if (iTemp < minOkTemp) {
if(iTemp < minOkTemp) { iB = range_scale(0, minOkTemp - 1, iTemp, 200, 60);
iB = range_scale(0, minOkTemp - 1, iTemp, 200, 60); } else if (iTemp < maxOkTemp) {
} else if(iTemp < maxOkTemp) { iG = range_scale(minOkTemp, maxOkTemp - 1, iTemp, 140, 224);
iG = range_scale(minOkTemp, maxOkTemp - 1, iTemp, 140, 224); } else {
} else { iR = range_scale(maxOkTemp, 255, iTemp, 224, 255);
iR = range_scale(maxOkTemp, 255, iTemp, 224, 255); }
} break;
break; case temperature_theme::yellow_red:
case temperature_theme::yellow_red: if (iTemp < minOkTemp) { // Below 11 degrees
if(iTemp < minOkTemp) { // Below 11 degrees iR = range_scale(0, minOkTemp - 1, iTemp, 100, 213);
iR = range_scale(0, minOkTemp - 1, iTemp, 100, 213); iG = range_scale(0, minOkTemp - 1, iTemp, 80, 180);
iG = range_scale(0, minOkTemp - 1, iTemp, 80, 180); } else {
} else { iR = range_scale(minOkTemp, 255, iTemp, 223, 235);
iR = range_scale(minOkTemp, 255, iTemp, 223, 235); iG = range_scale(minOkTemp, 255, iTemp, 184, 104);
iG = range_scale(minOkTemp, 255, iTemp, 184, 104); iB = range_scale(minOkTemp, 255, iTemp, 0, 53);
iB = range_scale(minOkTemp, 255, iTemp, 0, 53); }
} break;
break; case temperature_theme::red:
case temperature_theme::red: iR = static_cast<uint8_t>(iTemp);
iR = static_cast<uint8_t>(iTemp); iB = 70;
iB = 70; break;
break; }
}
iColour = render_target::map_colour(iR, iG, iB); 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);
}
}
}
} }
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) void lua_register_ui(const lua_register_state* pState) {
{ // WindowBase
// WindowBase lua_class_binding<abstract_window> lcb(pState, "windowHelpers",
lua_class_binding<abstract_window> lcb(pState, "windowHelpers", l_abstract_window_new, lua_metatable::window_base); l_abstract_window_new,
lcb.add_function(l_town_map_draw, "townMapDraw", lua_metatable::map, lua_metatable::surface); lua_metatable::window_base);
lcb.add_function(l_town_map_draw, "townMapDraw", lua_metatable::map,
lua_metatable::surface);
} }

File diff suppressed because it is too large Load Diff

View File

@@ -22,197 +22,194 @@ SOFTWARE.
#ifndef CORSIX_TH_TH_MAP_H_ #ifndef CORSIX_TH_TH_MAP_H_
#define CORSIX_TH_TH_MAP_H_ #define CORSIX_TH_TH_MAP_H_
#include "th_gfx.h"
#include <list> #include <list>
#include <string> #include <string>
#include "th_gfx.h"
/* /*
Object type enumeration uses same values as original TH does. Object type enumeration uses same values as original TH does.
See game string table section 39 for proof. Section 1 also has See game string table section 39 for proof. Section 1 also has
names in this order. names in this order.
*/ */
enum class object_type : uint8_t enum class object_type : uint8_t {
{ no_object = 0,
no_object = 0, desk = 1,
desk = 1, cabinet = 2,
cabinet = 2, door = 3,
door = 3, bench = 4,
bench = 4, table = 5, // Not in game
table = 5, // Not in game chair = 6,
chair = 6, drinks_machine = 7,
drinks_machine = 7, bed = 8,
bed = 8, inflator = 9,
inflator = 9, pool_table = 10,
pool_table = 10, reception_desk = 11,
reception_desk = 11, b_table = 12, // Not in game?
b_table = 12, // Not in game? cardio = 13,
cardio = 13, scanner = 14,
scanner = 14, scanner_console = 15,
scanner_console = 15, screen = 16,
screen = 16, litter_bomb = 17,
litter_bomb = 17, couch = 18,
couch = 18, sofa = 19,
sofa = 19, crash = 20, // The trolley in general diagnosis
crash = 20, // The trolley in general diagnosis tv = 21,
tv = 21, ultrascan = 22,
ultrascan = 22, dna_fixer = 23,
dna_fixer = 23, cast_remover = 24,
cast_remover = 24, hair_restorer = 25,
hair_restorer = 25, slicer = 26,
slicer = 26, xray = 27,
xray = 27, radiation_shield = 28,
radiation_shield = 28, xray_viewer = 29,
xray_viewer = 29, op_table = 30,
op_table = 30, lamp = 31, // Not in game?
lamp = 31, // Not in game? sink = 32,
sink = 32, op_sink1 = 33,
op_sink1 = 33, op_sink2 = 34,
op_sink2 = 34, surgeon_screen = 35,
surgeon_screen = 35, lecture_chair = 36,
lecture_chair = 36, projector = 37,
projector = 37, // 38 is unused
// 38 is unused pharmacy = 39,
pharmacy = 39, computer = 40,
computer = 40, chemical_mixer = 41,
chemical_mixer = 41, blood_machine = 42,
blood_machine = 42, extinguisher = 43,
extinguisher = 43, radiator = 44,
radiator = 44, plant = 45,
plant = 45, electro = 46,
electro = 46, jelly_vat = 47,
jelly_vat = 47, hell = 48,
hell = 48, // 49 is unused
// 49 is unused bin = 50,
bin = 50, loo = 51,
loo = 51, double_door1 = 52,
double_door1 = 52, double_door2 = 53,
double_door2 = 53, decon_shower = 54,
decon_shower = 54, autopsy = 55,
autopsy = 55, bookcase = 56,
bookcase = 56, video_game = 57,
video_game = 57, entrance_left_door = 58,
entrance_left_door = 58, entrance_right_door = 59,
entrance_right_door = 59, skeleton = 60,
skeleton = 60, comfy_chair = 61,
comfy_chair = 61, litter = 62,
litter = 62, helicopter = 63,
helicopter = 63, rathole = 64,
rathole = 64, // 65 through 255 are unused
// 65 through 255 are unused
}; };
//! Map flags and object type //! Map flags and object type
//! The point of storing the object type here is to allow pathfinding code //! The point of storing the object type here is to allow pathfinding code
//! to use object types as pathfinding goals. //! to use object types as pathfinding goals.
struct map_tile_flags struct map_tile_flags {
{ enum class key : uint32_t {
enum class key : uint32_t { passable_mask = 1 << 0,
passable_mask = 1 << 0, can_travel_n_mask = 1 << 1,
can_travel_n_mask = 1 << 1, can_travel_e_mask = 1 << 2,
can_travel_e_mask = 1 << 2, can_travel_s_mask = 1 << 3,
can_travel_s_mask = 1 << 3, can_travel_w_mask = 1 << 4,
can_travel_w_mask = 1 << 4, hospital_mask = 1 << 5,
hospital_mask = 1 << 5, buildable_mask = 1 << 6,
buildable_mask = 1 << 6, passable_if_not_for_blueprint_mask = 1 << 7,
passable_if_not_for_blueprint_mask = 1 << 7, room_mask = 1 << 8,
room_mask = 1 << 8, shadow_half_mask = 1 << 9,
shadow_half_mask = 1 << 9, shadow_full_mask = 1 << 10,
shadow_full_mask = 1 << 10, shadow_wall_mask = 1 << 11,
shadow_wall_mask = 1 << 11, door_north_mask = 1 << 12,
door_north_mask = 1 << 12, door_west_mask = 1 << 13,
door_west_mask = 1 << 13, do_not_idle_mask = 1 << 14,
do_not_idle_mask = 1 << 14, tall_north_mask = 1 << 15,
tall_north_mask = 1 << 15, tall_west_mask = 1 << 16,
tall_west_mask = 1 << 16, buildable_n_mask = 1 << 17,
buildable_n_mask = 1 << 17, buildable_e_mask = 1 << 18,
buildable_e_mask = 1 << 18, buildable_s_mask = 1 << 19,
buildable_s_mask = 1 << 19, buildable_w_mask = 1 << 20,
buildable_w_mask = 1 << 20, };
};
bool passable; //!< Pathfinding: Can walk on this tile bool passable; //!< Pathfinding: Can walk on this tile
bool can_travel_n; //!< Pathfinding: Can walk to the north bool can_travel_n; //!< Pathfinding: Can walk to the north
bool can_travel_e; //!< Pathfinding: Can walk to the east bool can_travel_e; //!< Pathfinding: Can walk to the east
bool can_travel_s; //!< Pathfinding: Can walk to the south bool can_travel_s; //!< Pathfinding: Can walk to the south
bool can_travel_w; //!< Pathfinding: Can walk to the west bool can_travel_w; //!< Pathfinding: Can walk to the west
bool hospital; //!< World: Tile is inside a hospital building bool hospital; //!< World: Tile is inside a hospital building
bool buildable; //!< Player: Can build on this tile bool buildable; //!< Player: Can build on this tile
bool passable_if_not_for_blueprint; bool passable_if_not_for_blueprint;
bool room; //!< World: Tile is inside a room bool room; //!< World: Tile is inside a room
bool shadow_half; //!< Rendering: Put block 75 over floor bool shadow_half; //!< Rendering: Put block 75 over floor
bool shadow_full; //!< Rendering: Put block 74 over floor bool shadow_full; //!< Rendering: Put block 74 over floor
bool shadow_wall; //!< Rendering: Put block 156 over east wall bool shadow_wall; //!< Rendering: Put block 156 over east wall
bool door_north; //!< World: Door on north wall of tile bool door_north; //!< World: Door on north wall of tile
bool door_west; //!< World: Door on west 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 do_not_idle; //!< World: Humanoids should not idle on tile
bool tall_north; //!< Shadows: Wall-like object on north wall bool tall_north; //!< Shadows: Wall-like object on north wall
bool tall_west; //!< Shadows: Wall-like object on west 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_n; //!< Can build on the north side of the tile
bool buildable_e; //!< Can build on the east 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_s; //!< Can build on the south side of the tile
bool buildable_w; //!< Can build on the west 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 //! Convert the given uint32_t reprentation of the map_tile flags
//! to a map_tile_flags instance. //! to a map_tile_flags instance.
map_tile_flags& operator =(uint32_t raw); map_tile_flags& operator=(uint32_t raw);
//! Get/set the flag with the given key //! Get/set the flag with the given key
bool& operator[] (map_tile_flags::key key); bool& operator[](map_tile_flags::key key);
//! Get the flag with the given key //! Get the flag with the given key
const bool& operator[](map_tile_flags::key key) const; const bool& operator[](map_tile_flags::key key) const;
//! Convert map_tile_flags into it's uint32_t representation //! Convert map_tile_flags into it's uint32_t representation
operator uint32_t() const; operator uint32_t() const;
}; };
enum class temperature_theme { enum class temperature_theme {
red, //!< Default warmth colouring (red gradients) red, //!< Default warmth colouring (red gradients)
multi_colour, //!< Different colours (blue, green, red) multi_colour, //!< Different colours (blue, green, red)
yellow_red //!< Gradients of yellow, orange, and red yellow_red //!< Gradients of yellow, orange, and red
}; };
struct map_tile : public link_list struct map_tile : public link_list {
{ map_tile();
map_tile(); ~map_tile();
~map_tile();
// Linked list for entities rendered at this tile // Linked list for entities rendered at this tile
// THLinkList::pPrev (will always be nullptr) // THLinkList::pPrev (will always be nullptr)
// THLinkList::pNext // THLinkList::pNext
//! Linked list for entities rendered in an early (right-to-left) pass //! Linked list for entities rendered in an early (right-to-left) pass
link_list oEarlyEntities; link_list oEarlyEntities;
//! Block tiles for rendering //! Block tiles for rendering
//! For each tile, the lower byte is the index in the sprite sheet, and the //! For each tile, the lower byte is the index in the sprite sheet, and the
//! upper byte is for the drawing flags. //! upper byte is for the drawing flags.
//! Layer 0 is for the floor //! Layer 0 is for the floor
//! Layer 1 is for the north wall //! Layer 1 is for the north wall
//! Layer 2 is for the west wall //! Layer 2 is for the west wall
//! Layer 3 is for the UI //! Layer 3 is for the UI
//! NB: In Lua, layers are numbered 1 - 4 rather than 0 - 3 //! NB: In Lua, layers are numbered 1 - 4 rather than 0 - 3
uint16_t iBlock[4]; uint16_t iBlock[4];
//! Parcels (plots) of land have an ID, with each tile in the plot having //! Parcels (plots) of land have an ID, with each tile in the plot having
//! that ID. Parcel 0 is the outside. //! that ID. Parcel 0 is the outside.
uint16_t iParcelId; uint16_t iParcelId;
//! Rooms have an ID, with room #0 being the corridor (and the outside). //! Rooms have an ID, with room #0 being the corridor (and the outside).
uint16_t iRoomId; uint16_t iRoomId;
//! A value between 0 (extreme cold) and 65535 (extreme heat) representing //! A value between 0 (extreme cold) and 65535 (extreme heat) representing
//! the temperature of the tile. To allow efficient calculation of a tile's //! 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 //! heat based on the previous tick's heat of the surrounding tiles, the
//! previous temperature is also stored, with the array indices switching //! previous temperature is also stored, with the array indices switching
//! every tick. //! every tick.
uint16_t aiTemperature[2]; uint16_t aiTemperature[2];
//! Flags for information and object type //! Flags for information and object type
map_tile_flags flags; map_tile_flags flags;
//! objects in this tile //! objects in this tile
std::list<object_type> objects; std::list<object_type> objects;
}; };
class sprite_sheet; class sprite_sheet;
@@ -226,218 +223,223 @@ class sprite_sheet;
* The object flags present in the map data. The meaning of this * The object flags present in the map data. The meaning of this
value is left unspecified. 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 map_overlay;
class level_map class level_map {
{ public:
public: level_map();
level_map(); ~level_map();
~level_map();
bool set_size(int iWidth, int iHeight); bool set_size(int iWidth, int iHeight);
bool load_blank(); bool load_blank();
bool load_from_th_file(const uint8_t* pData, size_t iDataLength, bool load_from_th_file(const uint8_t* pData, size_t iDataLength,
map_load_object_callback_fn fnObjectCallback, map_load_object_callback_fn fnObjectCallback,
void* pCallbackToken); void* pCallbackToken);
void save(std::string filename); void save(std::string filename);
//! Set the sprite sheet to be used for drawing the map //! Set the sprite sheet to be used for drawing the map
/*! /*!
The sprites for map floor tiles, wall tiles, and map decorators The sprites for map floor tiles, wall tiles, and map decorators
all come from the given sheet. all come from the given sheet.
*/ */
void set_block_sheet(sprite_sheet* pSheet); void set_block_sheet(sprite_sheet* pSheet);
//! Set the draw flags on all wall blocks //! Set the draw flags on all wall blocks
/*! /*!
This is typically called with THDF_Alpha50 to draw walls transparently, This is typically called with THDF_Alpha50 to draw walls transparently,
or with 0 to draw them opaque again. or with 0 to draw them opaque again.
*/ */
void set_all_wall_draw_flags(uint8_t iFlags); void set_all_wall_draw_flags(uint8_t iFlags);
void update_pathfinding(); void update_pathfinding();
void update_shadows(); void update_shadows();
void set_temperature_display(temperature_theme eTempDisplay); void set_temperature_display(temperature_theme eTempDisplay);
inline temperature_theme get_temperature_display() const {return current_temperature_theme;} inline temperature_theme get_temperature_display() const {
void update_temperatures(uint16_t iAirTemperature, return current_temperature_theme;
uint16_t iRadiatorTemperature); }
void update_temperatures(uint16_t iAirTemperature,
uint16_t iRadiatorTemperature);
//! Get the map width (in tiles) //! Get the map width (in tiles)
inline int get_width() const {return width;} inline int get_width() const { return width; }
//! Get the map height (in tiles) //! Get the map height (in tiles)
inline int get_height() const {return height;} inline int get_height() const { return height; }
//! Get the number of plots of land in this map //! Get the number of plots of land in this map
inline int get_parcel_count() const {return parcel_count - 1;} 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_camera_tile(int iPlayer, int* pX, int* pY) const;
bool get_player_heliport_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_camera_tile(int iPlayer, int iX, int iY);
void set_player_heliport_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 //! Get the number of tiles inside a given parcel
int get_parcel_tile_count(int iParcelId) const; int get_parcel_tile_count(int iParcelId) const;
//! Change the owner of a particular parcel //! Change the owner of a particular parcel
/*! /*!
\param iParcelId The parcel of land to change ownership of. Should be \param iParcelId The parcel of land to change ownership of. Should be
an integer between 1 and getParcelCount() inclusive (parcel 0 is an integer between 1 and getParcelCount() inclusive (parcel 0 is
the outside, and should never have its ownership changed). the outside, and should never have its ownership changed).
\param iOwner The number of the player who should own the parcel, or \param iOwner The number of the player who should own the parcel, or
zero if no player should own the parcel. zero if no player should own the parcel.
\return vSplitTiles A vector that contains tile coordinates where \return vSplitTiles A vector that contains tile coordinates where
iParcelId is adjacent to another part of the hospital. iParcelId is adjacent to another part of the hospital.
*/ */
std::vector<std::pair<int, int>> set_parcel_owner(int iParcelId, int iOwner); std::vector<std::pair<int, int>> set_parcel_owner(int iParcelId, int iOwner);
//! Get the owner of a particular parcel of land //! Get the owner of a particular parcel of land
/*! /*!
\param iParcelId An integer between 0 and getParcelCount() inclusive. \param iParcelId An integer between 0 and getParcelCount() inclusive.
\return 0 if the parcel is unowned, otherwise the number of the owning \return 0 if the parcel is unowned, otherwise the number of the owning
player. player.
*/ */
int get_parcel_owner(int iParcelId) const; int get_parcel_owner(int iParcelId) const;
//! Query if two parcels are directly connected //! Query if two parcels are directly connected
/*! /*!
\param iParcel1 An integer between 0 and getParcelCount() inclusive. \param iParcel1 An integer between 0 and getParcelCount() inclusive.
\param iParcel2 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 \return true if there is a path between the two parcels which does not
go into any other parcels. false otherwise. go into any other parcels. false otherwise.
*/ */
bool are_parcels_adjacent(int iParcel1, int iParcel2); bool are_parcels_adjacent(int iParcel1, int iParcel2);
//! Query if a given player is in a position to purchase a given parcel //! 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 \param iParcelId The parcel of land to query. Should be an integer
between 1 and getParcelCount() inclusive. between 1 and getParcelCount() inclusive.
\param iPlayer The number of the player to perform the query on behalf \param iPlayer The number of the player to perform the query on behalf
of. Should be a strictly positive integer. of. Should be a strictly positive integer.
\return true if the parcel has a door to the outside, or is directly \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 connected to a parcel already owned by the given player. false
otherwise. otherwise.
*/ */
bool is_parcel_purchasable(int iParcelId, int iPlayer); bool is_parcel_purchasable(int iParcelId, int iPlayer);
//! Draw the map (and any attached animations) //! Draw the map (and any attached animations)
/*! /*!
Draws the world pixel rectangle (iScreenX, iScreenY, iWidth, iHeight) Draws the world pixel rectangle (iScreenX, iScreenY, iWidth, iHeight)
to the rectangle (iCanvasX, iCanvasY, iWidth, iHeight) on pCanvas. Note to the rectangle (iCanvasX, iCanvasY, iWidth, iHeight) on pCanvas. Note
that world pixel co-ordinates are also known as absolute screen that world pixel co-ordinates are also known as absolute screen
co-ordinates - they are not world (tile) co-ordinates, nor (relative) co-ordinates - they are not world (tile) co-ordinates, nor (relative)
screen co-ordinates. screen co-ordinates.
*/ */
void draw(render_target* pCanvas, int iScreenX, int iScreenY, int iWidth, void draw(render_target* pCanvas, int iScreenX, int iScreenY, int iWidth,
int iHeight, int iCanvasX, int iCanvasY) const; int iHeight, int iCanvasX, int iCanvasY) const;
//! Perform a hit-test against the animations attached to the map //! Perform a hit-test against the animations attached to the map
/*! /*!
If there is an animation at world pixel co-ordinates (iTestX, iTestY), If there is an animation at world pixel co-ordinates (iTestX, iTestY),
then it is returned. Otherwise nullptr is returned. then it is returned. Otherwise nullptr is returned.
To perform a hit-test using world (tile) co-ordinates, get the tile 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 itself and query the top 8 bits of map_tile::flags, or traverse the
tile's animation lists. tile's animation lists.
*/ */
drawable* hit_test(int iTestX, int iTestY) const; drawable* hit_test(int iTestX, int iTestY) const;
// When using the unchecked versions, the map co-ordinates MUST be valid. // When using the unchecked versions, the map co-ordinates MUST be valid.
// When using the normal versions, nullptr is returned for invalid co-ords. // When using the normal versions, nullptr is returned for invalid co-ords.
map_tile* get_tile(int iX, int iY); map_tile* get_tile(int iX, int iY);
const map_tile* get_tile(int iX, int iY) const; const map_tile* get_tile(int iX, int iY) const;
const map_tile* get_original_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); map_tile* get_tile_unchecked(int iX, int iY);
const map_tile* get_tile_unchecked(int iX, int iY) const; const map_tile* get_tile_unchecked(int iX, int iY) const;
const map_tile* get_original_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; uint16_t get_tile_temperature(const map_tile* pNode) const;
int get_tile_owner(const map_tile* pNode) const; int get_tile_owner(const map_tile* pNode) const;
//! Convert world (tile) co-ordinates to absolute screen co-ordinates //! Convert world (tile) co-ordinates to absolute screen co-ordinates
template <typename T> template <typename T>
static inline void world_to_screen(T& x, T& y) static inline void world_to_screen(T& x, T& y) {
{ T x_(x);
T x_(x); x = (T)32 * (x_ - y);
x = (T)32 * (x_ - y); y = (T)16 * (x_ + y);
y = (T)16 * (x_ + y); }
}
//! Convert absolute screen co-ordinates to world (tile) co-ordinates //! Convert absolute screen co-ordinates to world (tile) co-ordinates
template <typename T> template <typename T>
static inline void screen_to_world(T& x, T& y) static inline void screen_to_world(T& x, T& y) {
{ T x_(x);
T x_(x); x = y / (T)32 + x_ / (T)64;
x = y / (T)32 + x_ / (T)64; y = y / (T)32 - x_ / (T)64;
y = y / (T)32 - x_ / (T)64; }
}
void persist(lua_persist_writer *pWriter) const; void persist(lua_persist_writer* pWriter) const;
void depersist(lua_persist_reader *pReader); void depersist(lua_persist_reader* pReader);
void set_overlay(map_overlay *pOverlay, bool bTakeOwnership); void set_overlay(map_overlay* pOverlay, bool bTakeOwnership);
private: private:
drawable* hit_test_drawables(link_list* pListStart, int iXs, int iYs, drawable* hit_test_drawables(link_list* pListStart, int iXs, int iYs,
int iTestX, int iTestY) const; int iTestX, int iTestY) const;
void read_tile_index(const uint8_t* pData, int& iX, int &iY) 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; 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. //! Calculate a weighted impact of a neighbour tile on the temperature of
//! \param iNeighbourSum Incremented by the temperature of the tile multiplied by the weight of the connection. //! the current tile. \param iNeighbourSum Incremented by the temperature of
//! \param canTravel A tile flag indicating whether travel between this tile and it's neighbour is allowed. //! the tile multiplied by the weight of the connection. \param canTravel A
//! \param relative_idx The index of the neighbour tile, relative to this tile into cells. //! tile flag indicating whether travel between this tile and it's neighbour
//! \param pNode A pointer to the current tile being tested. //! is allowed. \param relative_idx The index of the neighbour tile,
//! \param prevTemp The array index into map_tile::temperature that currently stores the temperature of the tile (prior to this calculation). //! relative to this tile into cells. \param pNode A pointer to the current
//! \return The weight of the connection, 0 if there is no neighbour, 1 through walls, and 4 through air. //! tile being tested. \param prevTemp The array index into
uint32_t thermal_neighbour(uint32_t &iNeighbourSum, bool canTravel, std::ptrdiff_t relative_idx, map_tile* pNode, int prevTemp) const; //! 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 //! Create the adjacency matrix if it doesn't already exist
void make_adjacency_matrix(); void make_adjacency_matrix();
//! Create the purchasability matrix if it doesn't already exist //! Create the purchasability matrix if it doesn't already exist
void make_purchase_matrix(); void make_purchase_matrix();
//! If it exists, update the purchasability matrix. //! If it exists, update the purchasability matrix.
void update_purchase_matrix(); void update_purchase_matrix();
int count_parcel_tiles(int iParcelId) const; int count_parcel_tiles(int iParcelId) const;
map_tile* cells; map_tile* cells;
map_tile* original_cells; // Cells at map load time, before any changes map_tile* original_cells; // Cells at map load time, before any changes
sprite_sheet* blocks; sprite_sheet* blocks;
map_overlay* overlay; map_overlay* overlay;
bool owns_overlay; bool owns_overlay;
int* plot_owner; // 0 for unowned, 1 for player 1, etc. int* plot_owner; // 0 for unowned, 1 for player 1, etc.
int width; int width;
int height; int height;
int player_count; int player_count;
int initial_camera_x[4]; int initial_camera_x[4];
int initial_camera_y[4]; int initial_camera_y[4];
int heliport_x[4]; int heliport_x[4];
int heliport_y[4]; int heliport_y[4];
int parcel_count; int parcel_count;
int current_temperature_index; int current_temperature_index;
temperature_theme current_temperature_theme; temperature_theme current_temperature_theme;
int* parcel_tile_counts; int* parcel_tile_counts;
// 2D symmetric array giving true if there is a path between two parcels // 2D symmetric array giving true if there is a path between two parcels
// which doesn't go into any other parcels. // which doesn't go into any other parcels.
bool* parcel_adjacency_matrix; bool* parcel_adjacency_matrix;
// 4 by N matrix giving true if player can purchase parcel. // 4 by N matrix giving true if player can purchase parcel.
bool* purchasable_matrix; bool* purchasable_matrix;
}; };
enum class map_scanline_iterator_direction { enum class map_scanline_iterator_direction {
forward = 2, forward = 2,
backward = 0, backward = 0,
}; };
//! Utility class for iterating over map tiles within a screen rectangle //! 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 tiles right-to-left, wait until isLastOnScanline() returns true, then use
an instance of THMapScanlineIterator. an instance of THMapScanlineIterator.
*/ */
class map_tile_iterator class map_tile_iterator {
{ public:
public: map_tile_iterator();
map_tile_iterator();
/*! /*!
@arg pMap The map whose tiles should be iterated @arg pMap The map whose tiles should be iterated
@arg iScreenX The X co-ordinate of the top-left corner of the @arg iScreenX The X co-ordinate of the top-left corner of the
screen-space rectangle to iterate. screen-space rectangle to iterate.
@arg iScreenY The Y co-ordinate of the top-left corner of the @arg iScreenY The Y co-ordinate of the top-left corner of the
screen-space rectangle to iterate. screen-space rectangle to iterate.
@arg iWidth The width 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 iHeight The width of the screen-space rectangle to iterate.
@arg eScanlineDirection The direction in which to iterate scanlines; @arg eScanlineDirection The direction in which to iterate scanlines;
forward for top-to-bottom, backward for bottom-to-top. forward for top-to-bottom, backward for bottom-to-top.
*/ */
map_tile_iterator(const level_map*pMap, int iScreenX, int iScreenY, map_tile_iterator(const level_map* pMap, int iScreenX, int iScreenY,
int iWidth, int iHeight, int iWidth, int iHeight,
map_scanline_iterator_direction eScanlineDirection = map_scanline_iterator_direction::forward); map_scanline_iterator_direction eScanlineDirection =
map_scanline_iterator_direction::forward);
//! Returns false iff the iterator has exhausted its tiles //! Returns false iff the iterator has exhausted its tiles
inline operator bool () const {return tile != nullptr;} inline operator bool() const { return tile != nullptr; }
//! Advances the iterator to the next tile //! Advances the iterator to the next tile
inline map_tile_iterator& operator ++ (); inline map_tile_iterator& operator++();
//! Accessor for the current tile //! Accessor for the current tile
inline const map_tile* operator -> () const {return 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 //! Get the X position of the tile relative to the top-left corner of the
inline int tile_x_position_on_screen() const {return x_relative_to_screen;} //! 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 //! Get the Y position of the tile relative to the top-left corner of the
inline int tile_y_position_on_screen() const {return y_relative_to_screen;} //! 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_x() const { return world_x; }
inline int tile_y() const {return world_y;} inline int tile_y() const { return world_y; }
inline const level_map *get_map() {return container;} inline const level_map* get_map() { return container; }
inline const map_tile *get_map_tile() {return tile;} inline const map_tile* get_map_tile() { return tile; }
inline int get_scanline_count() { return scanline_count;} inline int get_scanline_count() { return scanline_count; }
inline int get_tile_step() {return (static_cast<int>(direction) - 1) * (1 - container->get_width());} inline int get_tile_step() {
return (static_cast<int>(direction) - 1) * (1 - container->get_width());
}
//! Returns true iff the next tile will be on a different scanline //! 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, To visit a scanline in right-to-left order, or to revisit a scanline,
wait until this method returns true, then use a THMapScanlineIterator. wait until this method returns true, then use a THMapScanlineIterator.
*/ */
inline bool is_last_on_scanline() const; inline bool is_last_on_scanline() const;
private: private:
// Maximum extents of the visible parts of a tile (pixel distances relative // Maximum extents of the visible parts of a tile (pixel distances relative
// to the top-most corner of an isometric cell) // to the top-most corner of an isometric cell)
// If set too low, things will disappear when near the screen edge // If set too low, things will disappear when near the screen edge
// If set too high, rendering will slow down // If set too high, rendering will slow down
static const int margin_top = 150; static const int margin_top = 150;
static const int margin_left = 110; static const int margin_left = 110;
static const int margin_right = 110; static const int margin_right = 110;
static const int margin_bottom = 150; static const int margin_bottom = 150;
friend class map_scanline_iterator; friend class map_scanline_iterator;
const map_tile* tile; const map_tile* tile;
const level_map* container; const level_map* container;
// TODO: Consider removing these, they are trivial to calculate // TODO: Consider removing these, they are trivial to calculate
int x_relative_to_screen; int x_relative_to_screen;
int y_relative_to_screen; int y_relative_to_screen;
const int screen_offset_x; const int screen_offset_x;
const int screen_offset_y; const int screen_offset_y;
const int screen_width; const int screen_width;
const int screen_height; const int screen_height;
int base_x; int base_x;
int base_y; int base_y;
int world_x; int world_x;
int world_y; int world_y;
int scanline_count; int scanline_count;
map_scanline_iterator_direction direction; 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 //! Utility class for re-iterating a scanline visited by a map_tile_iterator
class map_scanline_iterator class map_scanline_iterator {
{ public:
public: map_scanline_iterator();
map_scanline_iterator();
/*! /*!
@arg itrNodes A tile iterator which has reached the end of a scanline @arg itrNodes A tile iterator which has reached the end of a scanline
@arg eDirection The direction in which to iterate the scanline; @arg eDirection The direction in which to iterate the scanline;
forward for left-to-right, backward for right-to-left. forward for left-to-right, backward for right-to-left.
@arg iXOffset If given, values returned by x() will be offset by this. @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. @arg iYOffset If given, values returned by y() will be offset by this.
*/ */
map_scanline_iterator(const map_tile_iterator& itrNodes, map_scanline_iterator(const map_tile_iterator& itrNodes,
map_scanline_iterator_direction eDirection, map_scanline_iterator_direction eDirection,
int iXOffset = 0, int iYOffset = 0); int iXOffset = 0, int iYOffset = 0);
inline operator bool () const {return tile != end_tile;} inline operator bool() const { return tile != end_tile; }
inline map_scanline_iterator& operator ++ (); inline map_scanline_iterator& operator++();
inline const map_tile* operator -> () const {return tile;} inline const map_tile* operator->() const { return tile; }
inline int x() const {return x_relative_to_screen;} inline int x() const { return x_relative_to_screen; }
inline int y() const {return y_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_next_tile() { return tile + tile_step; }
inline const map_tile* get_previous_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); map_scanline_iterator operator=(const map_scanline_iterator& iterator);
inline const map_tile* get_tile() {return tile;} inline const map_tile* get_tile() { return tile; }
private: private:
const map_tile* tile; const map_tile* tile;
const map_tile* first_tile; const map_tile* first_tile;
const map_tile* end_tile; const map_tile* end_tile;
int tile_step; int tile_step;
int x_step; int x_step;
int x_relative_to_screen; int x_relative_to_screen;
int y_relative_to_screen; int y_relative_to_screen;
int steps_taken; int steps_taken;
}; };
#endif // CORSIX_TH_TH_MAP_H_ #endif // CORSIX_TH_TH_MAP_H_

View File

@@ -21,210 +21,193 @@ SOFTWARE.
*/ */
#include "th_map_overlays.h" #include "th_map_overlays.h"
#include "th_gfx.h"
#include "th_map.h"
#include <sstream>
#include <array> #include <array>
#include <sstream>
#include "th_gfx.h"
#include "th_gfx_font.h"
#include "th_map.h"
map_overlay_pair::map_overlay_pair() map_overlay_pair::map_overlay_pair() {
{ first = nullptr;
first = nullptr; second = nullptr;
second = nullptr; owns_first = false;
owns_first = false; owns_second = false;
owns_second = false;
} }
map_overlay_pair::~map_overlay_pair() map_overlay_pair::~map_overlay_pair() {
{ set_first(nullptr, false);
set_first(nullptr, false); set_second(nullptr, false);
set_second(nullptr, false);
} }
void map_overlay_pair::set_first(map_overlay* pOverlay, bool bTakeOwnership) void map_overlay_pair::set_first(map_overlay* pOverlay, bool bTakeOwnership) {
{ if (first && owns_first) {
if(first && owns_first) delete first;
delete first; }
first = pOverlay; first = pOverlay;
owns_first = bTakeOwnership; owns_first = bTakeOwnership;
} }
void map_overlay_pair::set_second(map_overlay* pOverlay, bool bTakeOwnership) void map_overlay_pair::set_second(map_overlay* pOverlay, bool bTakeOwnership) {
{ if (second && owns_second) {
if(second && owns_second) delete second;
delete second; }
second = pOverlay; second = pOverlay;
owns_second = bTakeOwnership; owns_second = bTakeOwnership;
} }
void map_overlay_pair::draw_cell(render_target* pCanvas, int iCanvasX, void map_overlay_pair::draw_cell(render_target* pCanvas, int iCanvasX,
int iCanvasY, const level_map* pMap, int iNodeX, int iCanvasY, const level_map* pMap,
int iNodeY) int iNodeX, int iNodeY) {
{ if (first) {
if(first) first->draw_cell(pCanvas, iCanvasX, iCanvasY, pMap, iNodeX, iNodeY);
first->draw_cell(pCanvas, iCanvasX, iCanvasY, pMap, iNodeX, iNodeY); }
if(second) if (second) {
second->draw_cell(pCanvas, iCanvasX, iCanvasY, pMap, iNodeX, iNodeY); second->draw_cell(pCanvas, iCanvasX, iCanvasY, pMap, iNodeX, iNodeY);
}
} }
map_text_overlay::map_text_overlay() map_text_overlay::map_text_overlay() { background_sprite = 0; }
{
background_sprite = 0;
}
void map_text_overlay::set_background_sprite(size_t iSprite) void map_text_overlay::set_background_sprite(size_t iSprite) {
{ background_sprite = iSprite;
background_sprite = iSprite;
} }
void map_text_overlay::draw_cell(render_target* pCanvas, int iCanvasX, void map_text_overlay::draw_cell(render_target* pCanvas, int iCanvasX,
int iCanvasY, const level_map* pMap, int iNodeX, int iCanvasY, const level_map* pMap,
int iNodeY) int iNodeX, int iNodeY) {
{ if (sprites && background_sprite) {
if(sprites && background_sprite) sprites->draw_sprite(pCanvas, background_sprite, iCanvasX, iCanvasY, 0);
{ }
sprites->draw_sprite(pCanvas, background_sprite, iCanvasX, if (font) {
iCanvasY, 0); draw_text(pCanvas, iCanvasX, iCanvasY, get_text(pMap, iNodeX, iNodeY));
} }
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) const std::string map_positions_overlay::get_text(const level_map* pMap,
{ int iNodeX, int iNodeY) {
std::ostringstream str; std::ostringstream str;
str << iNodeX + 1 << ',' << iNodeY + 1; str << iNodeX + 1 << ',' << iNodeY + 1;
return str.str(); return str.str();
} }
map_typical_overlay::map_typical_overlay() map_typical_overlay::map_typical_overlay() {
{ sprites = nullptr;
sprites = nullptr; font = nullptr;
font = nullptr; owns_sprites = false;
owns_sprites = false; owns_font = false;
owns_font = false;
} }
map_typical_overlay::~map_typical_overlay() map_typical_overlay::~map_typical_overlay() {
{ set_sprites(nullptr, false);
set_sprites(nullptr, false); set_font(nullptr, false);
set_font(nullptr, false);
} }
void map_flags_overlay::draw_cell(render_target* pCanvas, int iCanvasX, void map_flags_overlay::draw_cell(render_target* pCanvas, int iCanvasX,
int iCanvasY, const level_map* pMap, int iNodeX, int iCanvasY, const level_map* pMap,
int iNodeY) int iNodeX, int iNodeY) {
{ const map_tile* pNode = pMap->get_tile(iNodeX, iNodeY);
const map_tile *pNode = pMap->get_tile(iNodeX, iNodeY); if (!pNode) {
if(!pNode) return;
return; }
if(sprites) if (sprites) {
{ if (pNode->flags.passable) {
if(pNode->flags.passable) sprites->draw_sprite(pCanvas, 3, iCanvasX, iCanvasY, 0);
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);
}
} }
if(font) if (pNode->flags.hospital) {
{ sprites->draw_sprite(pCanvas, 8, iCanvasX, iCanvasY, 0);
if(!pNode->objects.empty())
{
std::ostringstream str;
str << 'T' << static_cast<int>(pNode->objects.front());
draw_text(pCanvas, iCanvasX, iCanvasY - 8, str.str());
}
if(pNode->iRoomId)
{
std::ostringstream str;
str << 'R' << static_cast<int>(pNode->iRoomId);
draw_text(pCanvas, iCanvasX, iCanvasY + 8, str.str());
}
} }
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<int>(pNode->objects.front());
draw_text(pCanvas, iCanvasX, iCanvasY - 8, str.str());
}
if (pNode->iRoomId) {
std::ostringstream str;
str << 'R' << static_cast<int>(pNode->iRoomId);
draw_text(pCanvas, iCanvasX, iCanvasY + 8, str.str());
}
}
} }
namespace { namespace {
// The sprite to include if the tile in the direction indicated by dx,dy is // The sprite to include if the tile in the direction indicated by dx,dy is
// not from the same parcel. // not from the same parcel.
struct parcel_edge_sprite struct parcel_edge_sprite {
{ int dx;
int dx; int dy;
int dy; size_t sprite;
size_t sprite;
}; };
// Parcel edge sprites for all four directions // Parcel edge sprites for all four directions
constexpr std::array<parcel_edge_sprite, 4> parcel_edges {{ constexpr std::array<parcel_edge_sprite, 4> parcel_edges{
{0, -1, 18}, {{0, -1, 18}, {1, 0, 19}, {0, 1, 20}, {-1, 0, 21}}};
{1, 0, 19},
{0, 1, 20},
{-1, 0, 21}
}};
} } // namespace
void map_parcels_overlay::draw_cell(render_target* pCanvas, int iCanvasX, void map_parcels_overlay::draw_cell(render_target* pCanvas, int iCanvasX,
int iCanvasY, const level_map* pMap, int iNodeX, int iCanvasY, const level_map* pMap,
int iNodeY) int iNodeX, int iNodeY) {
{ const map_tile* pNode = pMap->get_tile(iNodeX, iNodeY);
const map_tile *pNode = pMap->get_tile(iNodeX, iNodeY); if (!pNode) {
if(!pNode) return;
return; }
if(font) if (font) {
draw_text(pCanvas, iCanvasX, iCanvasY, std::to_string((int)pNode->iParcelId)); draw_text(pCanvas, iCanvasX, iCanvasY,
if(sprites) std::to_string((int)pNode->iParcelId));
{ }
for(const parcel_edge_sprite& dir_sprite : parcel_edges) 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); const map_tile* dir_node =
if (!dir_node || dir_node->iParcelId == pNode->iParcelId) 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); sprites->draw_sprite(pCanvas, dir_sprite.sprite, iCanvasX, iCanvasY, 0);
} }
}
} }
}
} }
void map_typical_overlay::draw_text(render_target* pCanvas, int iX, int iY, void map_typical_overlay::draw_text(render_target* pCanvas, int iX, int iY,
std::string str) std::string str) {
{ text_layout oArea = font->get_text_dimensions(str.c_str(), str.length());
text_layout oArea = font->get_text_dimensions(str.c_str(), str.length()); font->draw_text(pCanvas, str.c_str(), str.length(),
font->draw_text(pCanvas, str.c_str(), str.length(), iX + (64 - oArea.end_x) / 2, iX + (64 - oArea.end_x) / 2, iY + (32 - oArea.end_y) / 2);
iY + (32 - oArea.end_y) / 2);
} }
void map_typical_overlay::set_sprites(sprite_sheet* pSheet, bool bTakeOwnership) void map_typical_overlay::set_sprites(sprite_sheet* pSheet,
{ bool bTakeOwnership) {
if(sprites && owns_sprites) if (sprites && owns_sprites) {
delete sprites; delete sprites;
sprites = pSheet; }
owns_sprites = bTakeOwnership; sprites = pSheet;
owns_sprites = bTakeOwnership;
} }
void map_typical_overlay::set_font(::font* font, bool take_ownership) void map_typical_overlay::set_font(::font* font, bool take_ownership) {
{ if (this->font && owns_font) {
if(this->font && owns_font) delete this->font;
delete this->font; }
this->font = font; this->font = font;
owns_font = take_ownership; owns_font = take_ownership;
} }

View File

@@ -31,86 +31,81 @@ class level_map;
class render_target; class render_target;
class sprite_sheet; class sprite_sheet;
class map_overlay class map_overlay {
{ public:
public: virtual ~map_overlay() = default;
virtual ~map_overlay() = default;
virtual void draw_cell(render_target* pCanvas, int iCanvasX, int iCanvasY, virtual void draw_cell(render_target* pCanvas, int iCanvasX, int iCanvasY,
const level_map* pMap, int iNodeX, int iNodeY) = 0; const level_map* pMap, int iNodeX, int iNodeY) = 0;
}; };
class map_overlay_pair : public map_overlay class map_overlay_pair : public map_overlay {
{ public:
public: map_overlay_pair();
map_overlay_pair(); virtual ~map_overlay_pair();
virtual ~map_overlay_pair();
void set_first(map_overlay* pOverlay, bool bTakeOwnership); void set_first(map_overlay* pOverlay, bool bTakeOwnership);
void set_second(map_overlay* pOverlay, bool bTakeOwnership); void set_second(map_overlay* pOverlay, bool bTakeOwnership);
void draw_cell(render_target* pCanvas, int iCanvasX, int iCanvasY, void draw_cell(render_target* pCanvas, int iCanvasX, int iCanvasY,
const level_map* pMap, int iNodeX, int iNodeY) override; const level_map* pMap, int iNodeX, int iNodeY) override;
private: private:
map_overlay *first, *second; map_overlay *first, *second;
bool owns_first, owns_second; bool owns_first, owns_second;
}; };
class map_typical_overlay : public map_overlay class map_typical_overlay : public map_overlay {
{ public:
public: map_typical_overlay();
map_typical_overlay(); virtual ~map_typical_overlay();
virtual ~map_typical_overlay();
void set_sprites(sprite_sheet* pSheet, bool bTakeOwnership); void set_sprites(sprite_sheet* pSheet, bool bTakeOwnership);
void set_font(::font* font, bool take_ownership); void set_font(::font* font, bool take_ownership);
protected: protected:
void draw_text(render_target* pCanvas, int iX, int iY, std::string str); void draw_text(render_target* pCanvas, int iX, int iY, std::string str);
sprite_sheet* sprites; sprite_sheet* sprites;
::font* font; ::font* font;
private: private:
bool owns_sprites; bool owns_sprites;
bool owns_font; bool owns_font;
}; };
class map_text_overlay : public map_typical_overlay class map_text_overlay : public map_typical_overlay {
{ public:
public: map_text_overlay();
map_text_overlay(); virtual ~map_text_overlay() = default;
virtual ~map_text_overlay() = default;
virtual void draw_cell(render_target* pCanvas, int iCanvasX, int iCanvasY, virtual void draw_cell(render_target* pCanvas, int iCanvasX, int iCanvasY,
const level_map* pMap, int iNodeX, int iNodeY); const level_map* pMap, int iNodeX, int iNodeY);
void set_background_sprite(size_t iSprite); void set_background_sprite(size_t iSprite);
virtual const std::string get_text(const level_map* pMap, int iNodeX, int iNodeY) = 0; virtual const std::string get_text(const level_map* pMap, int iNodeX,
int iNodeY) = 0;
private: private:
size_t background_sprite; size_t background_sprite;
}; };
class map_positions_overlay final : public map_text_overlay class map_positions_overlay final : public map_text_overlay {
{ public:
public: const std::string get_text(const level_map* pMap, int iNodeX,
const std::string get_text(const level_map* pMap, int iNodeX, int iNodeY) override; int iNodeY) override;
}; };
class map_flags_overlay final : public map_typical_overlay class map_flags_overlay final : public map_typical_overlay {
{ public:
public: void draw_cell(render_target* pCanvas, int iCanvasX, int iCanvasY,
void draw_cell(render_target* pCanvas, int iCanvasX, int iCanvasY, const level_map* pMap, int iNodeX, int iNodeY) override;
const level_map* pMap, int iNodeX, int iNodeY) override;
}; };
class map_parcels_overlay final : public map_typical_overlay class map_parcels_overlay final : public map_typical_overlay {
{ public:
public: void draw_cell(render_target* pCanvas, int iCanvasX, int iCanvasY,
void draw_cell(render_target* pCanvas, int iCanvasX, int iCanvasY, const level_map* pMap, int iNodeX, int iNodeY) override;
const level_map* pMap, int iNodeX, int iNodeY) override;
}; };
#endif #endif

File diff suppressed because it is too large Load Diff

View File

@@ -23,23 +23,23 @@ SOFTWARE.
#ifndef TH_VIDEO_H #ifndef TH_VIDEO_H
#define TH_VIDEO_H #define TH_VIDEO_H
#include <string>
#include <queue>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <atomic>
#include "SDL.h"
#include "config.h" #include "config.h"
#include <atomic>
#include <condition_variable>
#include <mutex>
#include <queue>
#include <string>
#include <thread>
#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" #include "SDL_mixer.h"
extern "C" extern "C" {
{
#ifndef INT64_C #ifndef INT64_C
#define INT64_C(c) (c ## LL) #define INT64_C(c) (c##LL)
#define UINT64_C(c) (c ## ULL) #define UINT64_C(c) (c##ULL)
#endif #endif
#include <libavformat/avformat.h> #include <libavformat/avformat.h>
#include <libavutil/avutil.h> #include <libavutil/avutil.h>
@@ -51,342 +51,379 @@ extern "C"
#endif #endif
} }
#if (defined(CORSIX_TH_USE_FFMEPG) && LIBAVUTIL_VERSION_INT < AV_VERSION_INT(51, 74, 100)) || \ #if (defined(CORSIX_TH_USE_FFMEPG) && \
(defined(CORSIX_TH_USE_LIBAV) && LIBAVUTIL_VERSION_INT < AV_VERSION_INT(51, 42, 0)) 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 AVPixelFormat PixelFormat
#define AV_PIX_FMT_RBG24 PIX_FMT_RGB24 #define AV_PIX_FMT_RBG24 PIX_FMT_RGB24
#endif #endif
#if (defined(CORSIX_TH_USE_LIBAV) && LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 16, 0)) || \ #if (defined(CORSIX_TH_USE_LIBAV) && \
(defined(CORSIX_TH_USE_FFMPEG) && LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 37, 100)) 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 #define CORSIX_TH_MOVIE_USE_SEND_PACKET_API
#endif #endif
//! \brief A picture in movie_picture_buffer //! \brief A picture in movie_picture_buffer
//! //!
//! Stores the picture from a frame in the movie from the time that it is //! Stores the picture from a frame in the movie from the time that it is
//! processed until it should be drawn. //! processed until it should be drawn.
class movie_picture class movie_picture {
{ public:
public: movie_picture();
movie_picture(); ~movie_picture();
~movie_picture();
//! Allocate the buffer to hold a picture of the given size //! Allocate the buffer to hold a picture of the given size
void allocate(int iWidth, int iHeight); void allocate(int iWidth, int iHeight);
//! Delete the buffer //! Delete the buffer
void deallocate(); void deallocate();
uint8_t* buffer; ///< Pixel data in #m_pixelFormat uint8_t* buffer; ///< Pixel data in #m_pixelFormat
const AVPixelFormat pixel_format; ///< The format of pixels to output const AVPixelFormat pixel_format; ///< The format of pixels to output
int width; ///< Picture width int width; ///< Picture width
int height; ///< Picture height int height; ///< Picture height
double pts; ///< Presentation time stamp double pts; ///< Presentation time stamp
std::mutex mutex; ///< Mutex protecting this picture std::mutex mutex; ///< Mutex protecting this picture
}; };
//! A buffer for holding movie pictures and drawing them to the renderer //! A buffer for holding movie pictures and drawing them to the renderer
class movie_picture_buffer class movie_picture_buffer {
{ public:
public: movie_picture_buffer();
movie_picture_buffer(); ~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 //! Indicate that processing should stop and the movie aborted
void abort(); void abort();
//! Resume after having aborted //! Resume after having aborted
void reset(); void reset();
//! Ready the picture buffer for a new renderer or new picture dimensions //! Ready the picture buffer for a new renderer or new picture dimensions
//! by allocating each movie_picture in the queue, resetting the read //! by allocating each movie_picture in the queue, resetting the read
//! index and allocating a new texture. //! index and allocating a new texture.
//! //!
//! \remark Must be run on the program's graphics thread //! \remark Must be run on the program's graphics thread
void allocate(SDL_Renderer *pRenderer, int iWidth, int iHeight); void allocate(SDL_Renderer* pRenderer, int iWidth, int iHeight);
//! Destroy the associated texture and deallocate each of the //! Destroy the associated texture and deallocate each of the
//! movie_pictures in the queue so that the program can release //! movie_pictures in the queue so that the program can release
//! the renderer //! the renderer
//! //!
//! \remark Must be run on the program's graphics thread //! \remark Must be run on the program's graphics thread
void deallocate(); void deallocate();
//! Advance the read index //! Advance the read index
bool advance(); bool advance();
//! Draw the movie_picture at the current read index //! Draw the movie_picture at the current read index
//! //!
//! \param pRenderer The renderer to draw the picture to //! \param pRenderer The renderer to draw the picture to
//! \param dstrect The rectangle on the renderer to draw to //! \param dstrect The rectangle on the renderer to draw to
//! //!
//! \remark Must be run on the program's graphics thread //! \remark Must be run on the program's graphics thread
void draw(SDL_Renderer *pRenderer, const SDL_Rect &dstrect); void draw(SDL_Renderer* pRenderer, const SDL_Rect& dstrect);
//! Get the next presentation time stamp //! Get the next presentation time stamp
double get_next_pts(); double get_next_pts();
//! Return whether there are any pictures left to draw in the picture queue //! 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 //! \remark If the movie_picture_buffer is not allocated it cannot be read
//! or written to. Consequently it is both full and empty. //! from or written to. Consequently it is both full and empty.
bool 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 //! 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 //! \remark If the movie_picture_buffer is not allocated it cannot be read
//! or written to. Consequently it is both full and empty. //! from or written to. Consequently it is both full and empty.
bool full(); bool full();
//! Write the given frame (and presentation time stamp) to the picture //! Write the given frame (and presentation time stamp) to the picture
//! queue //! queue
//! //!
//! \retval 0 Success //! \retval 0 Success
//! \retval -1 Abort is in progress //! \retval -1 Abort is in progress
//! \retval 1 An error writing the frame //! \retval 1 An error writing the frame
int write(AVFrame* pFrame, double dPts); 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();
static constexpr size_t picture_buffer_size = 4; ///< The number of elements to allocate in the picture queue private:
std::atomic<bool> aborting; ///< Whether we are in the process of aborting //! Return whether there is space to add any more frame data to the queue
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 //! \remark Requires external locking
int read_index; ///< The position in the picture queue to be read next bool unsafe_full();
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 static constexpr size_t picture_buffer_size =
SDL_Texture *texture; ///< The (potentially hardware) texture to draw the picture to. In OpenGL this should only be accessed on the main thread 4; ///< The number of elements to allocate in the picture queue
std::mutex mutex; ///< A mutex for restricting access to the picture buffer to a single thread std::atomic<bool> aborting; ///< Whether we are in the process of aborting
std::condition_variable cond; ///< A condition for indicating access to the picture buffer bool allocated; ///< Whether the picture buffer has been allocated (and
movie_picture picture_queue[picture_buffer_size]; ///< The picture queue, a looping FIFO queue of movie_pictures ///< 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 //! The AVPacketQueue is a thread safe queue of movie packets
class av_packet_queue class av_packet_queue {
{ public:
public: //! Construct a new empty packet queue
//! Construct a new empty packet queue av_packet_queue();
av_packet_queue();
//! Destroy the packet queue. //! Destroy the packet queue.
//! //!
//! \remarks Does not free the included packets. The packet queue should be //! \remarks Does not free the included packets. The packet queue should be
//! flushed before it is destroyed. //! flushed before it is destroyed.
~av_packet_queue(); ~av_packet_queue();
//! Push a new packet on the back of the queue //! Push a new packet on the back of the queue
void push(AVPacket *packet); void push(AVPacket* packet);
//! Pull the packet from the front of the queue //! Pull the packet from the front of the queue
//! //!
//! \param block Whether to block if the queue is empty or immediately //! \param block Whether to block if the queue is empty or immediately
//! return a nullptr //! return a nullptr
AVPacket* pull(bool block); AVPacket* pull(bool block);
//! Return the number of packets in the queue //! Return the number of packets in the queue
int get_count() const; int get_count() const;
//! Release a blocking pull without writing a new packet to the queue. //! Release a blocking pull without writing a new packet to the queue.
void release(); void release();
private:
AVPacketList *first_packet; ///< The packet at the front of the queue private:
AVPacketList *last_packet; ///< The packet at the end of the queue AVPacketList* first_packet; ///< The packet at the front of the queue
int count; ///< The number of packets in the queue AVPacketList* last_packet; ///< The packet at the end of the queue
std::mutex mutex; ///< A mutex restricting access to the packet queue to a single thread int count; ///< The number of packets in the queue
std::condition_variable cond; ///< A condition to wait on for signaling the packet 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 //! Movie player for CorsixTH
//! //!
//! The movie player is designed to be preinitialized and used for multiple //! The movie player is designed to be preinitialized and used for multiple
//! movies. After initializing the movie player, call movie_player::set_renderer //! 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 //! to assign the current SDL renderer to the movie player. Then
//! the desired movie and finally movie_player::play it. //! movie_player::load the desired movie and finally movie_player::play it.
class movie_player class movie_player {
{ public:
public: //! Construct a new movie_player
//! Construct a new movie_player movie_player();
movie_player();
//! Destroy the movie_player //! Destroy the movie_player
~movie_player(); ~movie_player();
//! Assign the renderer on which to draw the movie //! Assign the renderer on which to draw the movie
void set_renderer(SDL_Renderer *pRenderer); void set_renderer(SDL_Renderer* pRenderer);
//! Return whether movies were compiled into CorsixTH //! Return whether movies were compiled into CorsixTH
bool movies_enabled() const; bool movies_enabled() const;
//! Load the movie with the given file name //! Load the movie with the given file name
bool load(const char* szFilepath); bool load(const char* szFilepath);
//! Unload and free the currently loaded movie. //! Unload and free the currently loaded movie.
//! //!
//! \remark This is called by load before loading a new movie so it is //! \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. //! unnecessary to explicitly call this method. There is no harm either.
void unload(); void unload();
//! Play the currently loaded movie //! Play the currently loaded movie
//! //!
//! \param iChannel The audio channel to use //! \param iChannel The audio channel to use
void play(int iChannel); void play(int iChannel);
//! Stop the currently playing movie //! Stop the currently playing movie
void stop(); void stop();
//! Return the original height of the movie //! Return the original height of the movie
int get_native_height() const; int get_native_height() const;
//! Return the original width of the movie //! Return the original width of the movie
int get_native_width() const; int get_native_width() const;
//! Return whether the movie has an audio stream //! Return whether the movie has an audio stream
bool has_audio_track() const; bool has_audio_track() const;
//! Return a text description of the last error encountered //! Return a text description of the last error encountered
const char* get_last_error() const; const char* get_last_error() const;
//! Clear the last error so that if there is no more errors before the next //! 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. //! call to movie_player::get_last_error() it will return an empty string.
void clear_last_error(); void clear_last_error();
//! Draw the next frame if it is time to do so //! Draw the next frame if it is time to do so
//! //!
//! \param destination_rect The location and dimensions in the renderer on //! \param destination_rect The location and dimensions in the renderer on
//! which to draw the movie //! which to draw the movie
void refresh(const SDL_Rect &destination_rect); void refresh(const SDL_Rect& destination_rect);
//! Deallocate the picture buffer and free any resources associated with it. //! Deallocate the picture buffer and free any resources associated with it.
//! //!
//! \remark This destroys the textures and other resources that may lock //! \remark This destroys the textures and other resources that may lock
//! the renderer from being deleted. If the target changes you would call //! the renderer from being deleted. If the target changes you would call
//! this, then free and switch renderers in the outside program, then call //! this, then free and switch renderers in the outside program, then call
//! movie_player::set_renderer and finally movie_player::allocate_picture_buffer. //! movie_player::set_renderer and finally
//! \remark Up to the size of the picture buffer frames may be lost during //! movie_player::allocate_picture_buffer. \remark Up to the size of the
//! this process. //! picture buffer frames may be lost during this process.
void deallocate_picture_buffer(); void deallocate_picture_buffer();
//! Allocate the picture buffer for the current renderer //! Allocate the picture buffer for the current renderer
void allocate_picture_buffer(); void allocate_picture_buffer();
//! Read packets from the movie and allocate them to the appropriate stream //! Read packets from the movie and allocate them to the appropriate stream
//! packet queues. Signal if we have reached the end of the movie. //! 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 //! \remark This should not be called externally. It is public as it is the
//! entry point of a thread. //! entry point of a thread.
void read_streams(); void read_streams();
//! Read video frames from the video packet queue and write them to the //! Read video frames from the video packet queue and write them to the
//! picture queue. //! picture queue.
//! //!
//! \remark This should not be called externally. It is public as it is the //! \remark This should not be called externally. It is public as it is the
//! entry point of a thread. //! entry point of a thread.
void run_video(); void run_video();
//! Read audio from the audio packet queue, and copy it into the audio //! Read audio from the audio packet queue, and copy it into the audio
//! buffer for playback //! buffer for playback
void copy_audio_to_stream(uint8_t *pbStream, int iStreamSize); void copy_audio_to_stream(uint8_t* pbStream, int iStreamSize);
private: private:
#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)) && \
static constexpr size_t movie_error_buffer_capacity = 128; ///< Buffer to hold last error description defined(CORSIX_TH_USE_SDL_MIXER)
static constexpr size_t audio_chunk_buffer_capacity = 1024; ///< Buffer for audio playback 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 //! Get the AVCodecContext associated with a given stream
AVCodecContext* get_codec_context_for_stream(AVCodec* codec, AVStream* stream) const; 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) //! 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 //! \param frame The video or audio frame
double get_presentation_time_for_frame(AVFrame* frame, int streamIndex) const; //! \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 //! Decode audio from the movie into a format suitable for playback
int decode_audio_frame(bool fFirst); int decode_audio_frame(bool fFirst);
#ifdef CORSIX_TH_MOVIE_USE_SEND_PACKET_API #ifdef CORSIX_TH_MOVIE_USE_SEND_PACKET_API
//! Convert packet data into frames //! Convert packet data into frames
//! //!
//! \param stream The index of the stream to get the frame for //! \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 //! \param pFrame An empty frame which gets populated by the data in the
//! packet queue. //! packet queue.
//! \returns FFMPEG result of avcodec_recieve_frame //! \returns FFMPEG result of avcodec_recieve_frame
int get_frame(int stream, AVFrame* pFrame); int get_frame(int stream, AVFrame* pFrame);
#else #else
//! Convert video packet data into a frame. //! Convert video packet data into a frame.
//! //!
//! \param pFrame An empty frame which gets populated by the data in the //! \param pFrame An empty frame which gets populated by the data in the
//! video packet queue. //! video packet queue.
//! \returns 1 if the frame was received, 0 if it was not, and < 0 on error //! \returns 1 if the frame was received, 0 if it was not, and < 0 on error
int get_video_frame(AVFrame *pFrame); int get_video_frame(AVFrame* pFrame);
#endif #endif
SDL_Renderer *renderer; ///< The renderer to draw to SDL_Renderer* renderer; ///< The renderer to draw to
//! A description of the last error //! A description of the last error
std::string last_error; std::string last_error;
//! A buffer for passing to ffmpeg to get error details //! A buffer for passing to ffmpeg to get error details
char error_buffer[movie_error_buffer_capacity]; char error_buffer[movie_error_buffer_capacity];
// TODO: Should be atomic // TODO: Should be atomic
bool aborting; ///< Indicate that we are in process of aborting playback 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 AVFormatContext* format_context; ///< Information related to the loaded
int video_stream_index; ///< The index of the video stream ///< movie and all of its streams
int audio_stream_index; ///< The index of the audio stream int video_stream_index; ///< The index of the video stream
AVCodecContext *video_codec_context; ///< The video codec and information related to video int audio_stream_index; ///< The index of the audio stream
AVCodecContext *audio_codec_context; ///< The audio codec and information related to audio 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 // queues for transferring data between threads
av_packet_queue *video_queue; ///< Packets from the video stream av_packet_queue* video_queue; ///< Packets from the video stream
av_packet_queue *audio_queue; ///< Packets from the audio stream av_packet_queue* audio_queue; ///< Packets from the audio stream
::movie_picture_buffer *movie_picture_buffer; ///< Buffer of processed video ::movie_picture_buffer* movie_picture_buffer; ///< Buffer of processed video
//clock sync parameters // clock sync parameters
int current_sync_pts_system_time; ///< System time matching #m_iCurSyncPts int current_sync_pts_system_time; ///< System time matching #m_iCurSyncPts
double current_sync_pts; ///< The current presentation time stamp (from the audio stream) double current_sync_pts; ///< The current presentation time stamp (from the
///< audio stream)
#ifdef CORSIX_TH_USE_FFMPEG #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) #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 #endif
int audio_buffer_size; ///< The current size of audio data in #m_pbAudioBuffer int audio_buffer_size; ///< The current size of audio data in
int audio_buffer_index; ///< The current position for writing in #m_pbAudioBuffer ///< #m_pbAudioBuffer
int audio_buffer_max_size; ///< The capacity of #m_pbAudioBuffer (allocated size) int audio_buffer_index; ///< The current position for writing in
uint8_t* audio_buffer; ///< An audio buffer for playback ///< #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) AVPacket* audio_packet; ///< The current audio packet being decoded (audio
int audio_packet_size; ///< The size of #m_pbAudioPacketData ///< frames don't necessarily line up with packets)
uint8_t *audio_packet_data; ///< Original data for #m_pAudioPacket, kept so that it can be freed after the packet is processed int audio_packet_size; ///< The size of #m_pbAudioPacketData
AVFrame* audio_frame; ///< The frame we are decoding audio into 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 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 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 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_channels; ///< How many channels to play on (1 - mono, 2 -
int mixer_frequency; ///< The frequency of audio expected by SDL_Mixer ///< 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 stream_thread; ///< The thread responsible for reading the
std::thread video_thread; ///< The thread responsible for decoding the video stream ///< movie streams
#endif //CORSIX_TH_USE_FFMPEG || CORSIX_TH_USE_LIBAV 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

File diff suppressed because it is too large Load Diff

View File

@@ -30,167 +30,164 @@ class pathfinder;
/** Directions of movement. */ /** Directions of movement. */
enum class travel_direction { enum class travel_direction {
north = 0, ///< Move to the north. north = 0, ///< Move to the north.
east = 1, ///< Move to the east. east = 1, ///< Move to the east.
south = 2, ///< Move to the south. south = 2, ///< Move to the south.
west = 3 ///< Move to the west. west = 3 ///< Move to the west.
}; };
/** Node in the path finder routines. */ /** Node in the path finder routines. */
struct path_node struct path_node {
{ //! Pointer to the previous node in the path to this cell.
//! 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
Points to nullptr if this is the first cell in the path, or points to itself if it is not part of a path.
itself if it is not part of a path. */
*/ const path_node* prev;
const path_node* prev;
//! X-position of this cell (constant) //! X-position of this cell (constant)
int x; int x;
//! Y-position of this cell (constant) //! Y-position of this cell (constant)
int y; int y;
//! Current shortest distance to this cell //! Current shortest distance to this cell
/*! /*!
Defined as prev->distance + 1 (or 0 if prev == nullptr). Defined as prev->distance + 1 (or 0 if prev == nullptr).
Value is undefined if not part of a path. Value is undefined if not part of a path.
*/ */
int distance; int distance;
//! Minimum distance from this cell to the goal //! Minimum distance from this cell to the goal
/*! /*!
Value is only dependant upon the cell position and the goal Value is only dependant upon the cell position and the goal
position, and is undefined if not part of a path. position, and is undefined if not part of a path.
*/ */
int guess; int guess;
//! Index of this cell in the open heap //! Index of this cell in the open heap
/*! /*!
If the cell is not in the open heap, then this value is undefined. If the cell is not in the open heap, then this value is undefined.
*/ */
int open_idx; int open_idx;
//! Total cost of this node. //! Total cost of this node.
/*! /*!
@return Total cost of the node, traveled distance and guess to the destination. @return Total cost of the node, traveled distance and guess to the
*/ destination.
inline int value() const { return distance + guess; } */
inline int value() const { return distance + guess; }
}; };
/** Base class of the path finders. */ /** Base class of the path finders. */
class abstract_pathfinder class abstract_pathfinder {
{ public:
public: abstract_pathfinder(pathfinder* pf);
abstract_pathfinder(pathfinder *pf); virtual ~abstract_pathfinder() = default;
virtual ~abstract_pathfinder() = default;
//! Initialize the path finder. //! Initialize the path finder.
/*! /*!
@param pMap Map to search on. @param pMap Map to search on.
@param iStartX X coordinate of the start position. @param iStartX X coordinate of the start position.
@param iStarty Y coordinate of the start position. @param iStarty Y coordinate of the start position.
@return The initial node to expand. @return The initial node to expand.
*/ */
path_node *init(const level_map *pMap, int iStartX, int iStarty); path_node* init(const level_map* pMap, int iStartX, int iStarty);
//! Expand the \a pNode to its neighbours. //! Expand the \a pNode to its neighbours.
/*! /*!
@param pNode Node to expand. @param pNode Node to expand.
@param flags Flags of the node. @param flags Flags of the node.
@param iWidth Width of the map. @param iWidth Width of the map.
@return Whether the search is done. @return Whether the search is done.
*/ */
bool search_neighbours(path_node *pNode, map_tile_flags flags, int iWidth); 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, void record_neighbour_if_passable(path_node* pNode,
bool passable, path_node *pNeighbour); map_tile_flags neighbour_flags,
bool passable, path_node* pNeighbour);
//! Guess distance to the destination for \a pNode. //! Guess distance to the destination for \a pNode.
/*! /*!
@param pNode Node to fill. @param pNode Node to fill.
*/ */
virtual int guess_distance(path_node *pNode) = 0; virtual int guess_distance(path_node* pNode) = 0;
//! Try the \a pNeighbour node. //! Try the \a pNeighbour node.
/*! /*!
@param pNode Source node. @param pNode Source node.
@param flags Flags of the node. @param flags Flags of the node.
@param pNeighbour Neighbour of \a pNode to try. @param pNeighbour Neighbour of \a pNode to try.
@param direction Direction of travel. @param direction Direction of travel.
@return Whether the search is done. @return Whether the search is done.
*/ */
virtual bool try_node(path_node *pNode, map_tile_flags flags, virtual bool try_node(path_node* pNode, map_tile_flags flags,
path_node *pNeighbour, travel_direction direction) = 0; path_node* pNeighbour, travel_direction direction) = 0;
protected: protected:
pathfinder *parent; ///< Path finder parent object, containing shared data. pathfinder* parent; ///< Path finder parent object, containing shared data.
const level_map *map; ///< Map being searched. const level_map* map; ///< Map being searched.
}; };
class basic_pathfinder : public abstract_pathfinder class basic_pathfinder : public abstract_pathfinder {
{ public:
public: basic_pathfinder(pathfinder* pf) : abstract_pathfinder(pf) {}
basic_pathfinder(pathfinder *pf) : abstract_pathfinder(pf) { }
int guess_distance(path_node *pNode) override; int guess_distance(path_node* pNode) override;
bool try_node(path_node *pNode, map_tile_flags flags, bool try_node(path_node* pNode, map_tile_flags flags, path_node* pNeighbour,
path_node *pNeighbour, travel_direction direction) override; 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_x; ///< X coordinate of the destination of the path.
int destination_y; ///< Y coordinate of the destination of the path. int destination_y; ///< Y coordinate of the destination of the path.
}; };
class hospital_finder : public abstract_pathfinder class hospital_finder : public abstract_pathfinder {
{ public:
public: hospital_finder(pathfinder* pf) : abstract_pathfinder(pf) {}
hospital_finder(pathfinder *pf) : abstract_pathfinder(pf) { }
int guess_distance(path_node *pNode) override; int guess_distance(path_node* pNode) override;
bool try_node(path_node *pNode, map_tile_flags flags, bool try_node(path_node* pNode, map_tile_flags flags, path_node* pNeighbour,
path_node *pNeighbour, travel_direction direction) override; 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 class idle_tile_finder : public abstract_pathfinder {
{ public:
public: idle_tile_finder(pathfinder* pf) : abstract_pathfinder(pf) {}
idle_tile_finder(pathfinder *pf) : abstract_pathfinder(pf) { }
int guess_distance(path_node *pNode) override; int guess_distance(path_node* pNode) override;
bool try_node(path_node *pNode, map_tile_flags flags, bool try_node(path_node* pNode, map_tile_flags flags, path_node* pNeighbour,
path_node *pNeighbour, travel_direction direction) override; 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; path_node* best_next_node;
double best_distance; double best_distance;
int start_x; ///< X coordinate of the start position. int start_x; ///< X coordinate of the start position.
int start_y; ///< Y coordinate of the start position. int start_y; ///< Y coordinate of the start position.
}; };
class object_visitor : public abstract_pathfinder class object_visitor : public abstract_pathfinder {
{ public:
public: object_visitor(pathfinder* pf) : abstract_pathfinder(pf) {}
object_visitor(pathfinder *pf) : abstract_pathfinder(pf) { }
int guess_distance(path_node *pNode) override; int guess_distance(path_node* pNode) override;
bool try_node(path_node *pNode, map_tile_flags flags, bool try_node(path_node* pNode, map_tile_flags flags, path_node* pNeighbour,
path_node *pNeighbour, travel_direction direction) override; travel_direction direction) override;
bool visit_objects(const level_map *pMap, int iStartX, int iStartY, bool visit_objects(const level_map* pMap, int iStartX, int iStartY,
object_type eTHOB, int iMaxDistance, object_type eTHOB, int iMaxDistance, lua_State* L,
lua_State *L, int iVisitFunction, bool anyObjectType); int iVisitFunction, bool anyObjectType);
lua_State *L; lua_State* L;
int visit_function_index; int visit_function_index;
int max_distance; int max_distance;
bool target_any_object_type; bool target_any_object_type;
object_type target; object_type target;
}; };
//! Finds paths through maps //! Finds paths through maps
@@ -208,88 +205,85 @@ public:
the current search. The algorithm is implemented in such a way that most 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. path find operations do not need to allocate (or free) any memory.
*/ */
class pathfinder class pathfinder {
{ public:
public: pathfinder();
pathfinder(); ~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, inline bool find_path(const level_map* pMap, int iStartX, int iStartY,
int iEndY) int iEndX, int iEndY) {
{ return basic_pathfinder.find_path(pMap, iStartX, iStartY, iEndX, 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) 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); 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) inline bool find_path_to_hospital(const level_map* pMap, int iStartX,
{ int iStartY) {
return hospital_finder.find_path_to_hospital(pMap, iStartX, iStartY); return hospital_finder.find_path_to_hospital(pMap, iStartX, iStartY);
} }
inline bool visit_objects(const level_map *pMap, int iStartX, int iStartY, inline bool visit_objects(const level_map* pMap, int iStartX, int iStartY,
object_type eTHOB, int iMaxDistance, lua_State *L, object_type eTHOB, int iMaxDistance, lua_State* L,
int iVisitFunction, bool anyObjectType) int iVisitFunction, bool anyObjectType) {
{ return object_visitor.visit_objects(pMap, iStartX, iStartY, eTHOB,
return object_visitor.visit_objects( iMaxDistance, L, iVisitFunction,
pMap, iStartX, iStartY, eTHOB, iMaxDistance, anyObjectType);
L, iVisitFunction, anyObjectType); }
}
int get_path_length() const; int get_path_length() const;
bool get_path_end(int* pX, int* pY) const; bool get_path_end(int* pX, int* pY) const;
void push_result(lua_State *L) const; void push_result(lua_State* L) const;
void persist(lua_persist_writer *pWriter) const; void persist(lua_persist_writer* pWriter) const;
void depersist(lua_persist_reader *pReader); void depersist(lua_persist_reader* pReader);
//! Allocate node cache for all tiles of the map. //! Allocate node cache for all tiles of the map.
/*! /*!
@param iWidth Width of the map. @param iWidth Width of the map.
@param iHeight Height of the map. @param iHeight Height of the map.
*/ */
void allocate_node_cache(int iWidth, int iHeight); void allocate_node_cache(int iWidth, int iHeight);
path_node* pop_from_open_heap(); path_node* pop_from_open_heap();
void push_to_open_heap(path_node* pNode); void push_to_open_heap(path_node* pNode);
void open_heap_promote(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 //! 2D array of nodes, one for each map cell
path_node *nodes; path_node* nodes;
//! Array of "dirty" nodes which need to be reset before the next path find //! 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 This array is always large enough to hold every single node, and
#dirty_node_count holds the number of items currently in the array. #dirty_node_count holds the number of items currently in the array.
*/ */
path_node **dirty_node_list; path_node** dirty_node_list;
//! Heap of not yet evaluated nodes as a 0-based array //! Heap of not yet evaluated nodes as a 0-based array
/*! /*!
This array conforms to the conditions: This array conforms to the conditions:
value(i) <= value(i * 2 + 1) value(i) <= value(i * 2 + 1)
value(i) <= value(i * 2 + 2) value(i) <= value(i * 2 + 2)
This causes the array to be a minimum binary heap. This causes the array to be a minimum binary heap.
*/ */
std::vector<path_node*> open_heap; std::vector<path_node*> open_heap;
path_node *destination; path_node* destination;
int node_cache_width; int node_cache_width;
int node_cache_height; int node_cache_height;
int dirty_node_count; int dirty_node_count;
private: private:
::basic_pathfinder basic_pathfinder; ::basic_pathfinder basic_pathfinder;
::hospital_finder hospital_finder; ::hospital_finder hospital_finder;
::idle_tile_finder idle_tile_finder; ::idle_tile_finder idle_tile_finder;
::object_visitor object_visitor; ::object_visitor object_visitor;
}; };
#endif // CORSIX_TH_TH_PATHFIND_H_ #endif // CORSIX_TH_TH_PATHFIND_H_

View File

@@ -20,154 +20,157 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE. SOFTWARE.
*/ */
#include "config.h"
#include "th_sound.h" #include "th_sound.h"
#include "th.h" #include "config.h"
#include <cmath> #include <cmath>
#include <new>
#include <cstring> #include <cstring>
#include <new>
#include "th.h"
sound_archive::sound_archive() sound_archive::sound_archive() {
{ sound_files = nullptr;
sound_files = nullptr; data = nullptr;
data = nullptr;
} }
sound_archive::~sound_archive() sound_archive::~sound_archive() { delete[] data; }
{
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<size_t>(iHeaderPosition) >=
iDataLength - sizeof(sound_dat_file_header)) {
return false;
}
header =
*reinterpret_cast<const sound_dat_file_header*>(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<sound_dat_sound_info*>(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) size_t sound_archive::get_number_of_sounds() const { return sound_file_count; }
{
if(iDataLength < sizeof(uint32_t) + sizeof(sound_dat_file_header))
return false;
uint32_t iHeaderPosition = bytes_to_uint32_le(pData + iDataLength - sizeof(uint32_t)); const char* sound_archive::get_sound_name(size_t iIndex) const {
if (iIndex >= sound_file_count) return nullptr;
if(static_cast<size_t>(iHeaderPosition) >= iDataLength - sizeof(sound_dat_file_header)) return sound_files[iIndex].sound_name;
return false;
header = *reinterpret_cast<const sound_dat_file_header*>(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<sound_dat_sound_info*>(data + header.table_position);
sound_file_count = header.table_length / sizeof(sound_dat_sound_info);
return true;
} }
size_t sound_archive::get_number_of_sounds() const constexpr uint32_t fourcc(const char c1, const char c2, const char c3,
{ const char c4) {
return sound_file_count; return ((static_cast<uint32_t>(static_cast<uint8_t>(c1)) << 0) |
} (static_cast<uint32_t>(static_cast<uint8_t>(c2)) << 8) |
(static_cast<uint32_t>(static_cast<uint8_t>(c3)) << 16) |
const char* sound_archive::get_sound_name(size_t iIndex) const (static_cast<uint32_t>(static_cast<uint8_t>(c4)) << 24));
{
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<uint32_t>(static_cast<uint8_t>(c1)) << 0)
| (static_cast<uint32_t>(static_cast<uint8_t>(c2)) << 8)
| (static_cast<uint32_t>(static_cast<uint8_t>(c3)) << 16)
| (static_cast<uint32_t>(static_cast<uint8_t>(c4)) << 24));
} }
namespace { namespace {
template <typename A, typename B> template <typename A, typename B>
inline uint64_t mul64(A a, B b) { inline uint64_t mul64(A a, B b) {
return static_cast<uint64_t>(a) * static_cast<uint64_t>(b); return static_cast<uint64_t>(a) * static_cast<uint64_t>(b);
} }
} // namespace } // namespace
size_t sound_archive::get_sound_duration(size_t iIndex) size_t sound_archive::get_sound_duration(size_t iIndex) {
{ SDL_RWops* pFile = load_sound(iIndex);
SDL_RWops *pFile = load_sound(iIndex); if (!pFile) {
if(!pFile) return 0;
return 0; }
uint16_t iWaveAudioFormat = 0; uint16_t iWaveAudioFormat = 0;
uint16_t iWaveChannelCount = 0; uint16_t iWaveChannelCount = 0;
uint32_t iWaveSampleRate = 0; uint32_t iWaveSampleRate = 0;
uint32_t iWaveByteRate = 0; uint32_t iWaveByteRate = 0;
uint16_t iWaveBlockAlign = 0; uint16_t iWaveBlockAlign = 0;
uint16_t iWaveBitsPerSample = 0; uint16_t iWaveBitsPerSample = 0;
uint32_t iWaveDataLength = 0; uint32_t iWaveDataLength = 0;
// This is a very crude RIFF parser, but it does the job. // This is a very crude RIFF parser, but it does the job.
uint32_t iFourCC; uint32_t iFourCC;
uint32_t iChunkLength; uint32_t iChunkLength;
for(;;) for (;;) {
{ if (SDL_RWread(pFile, &iFourCC, 4, 1) != 1) {
if(SDL_RWread(pFile, &iFourCC, 4, 1) != 1) break;
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;
}
} }
SDL_RWclose(pFile); if (SDL_RWread(pFile, &iChunkLength, 4, 1) != 1) {
if(iWaveAudioFormat != 1 || iWaveChannelCount == 0 || iWaveSampleRate == 0 break;
|| iWaveDataLength == 0 || iWaveBitsPerSample == 0)
{
return 0;
} }
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<size_t>(mul64(iWaveDataLength, 8000) / return static_cast<size_t>(
mul64(mul64(iWaveBitsPerSample, iWaveChannelCount), iWaveSampleRate)); mul64(iWaveDataLength, 8000) /
mul64(mul64(iWaveBitsPerSample, iWaveChannelCount), iWaveSampleRate));
} }
SDL_RWops* sound_archive::load_sound(size_t iIndex) SDL_RWops* sound_archive::load_sound(size_t iIndex) {
{ if (iIndex >= sound_file_count) {
if(iIndex >= sound_file_count) return nullptr;
return nullptr; }
sound_dat_sound_info *pFile = sound_files + iIndex; sound_dat_sound_info* pFile = sound_files + iIndex;
return SDL_RWFromConstMem(data + pFile->position, pFile->length); return SDL_RWFromConstMem(data + pFile->position, pFile->length);
} }
#ifdef CORSIX_TH_USE_SDL_MIXER #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::singleton = nullptr;
sound_player::sound_player() sound_player::sound_player() {
{ sounds = nullptr;
sounds = nullptr; sound_count = 0;
sound_count = 0; singleton = this;
singleton = this; camera_x = 0;
camera_x = 0; camera_y = 0;
camera_y = 0; camera_radius = 1.0;
camera_radius = 1.0; master_volume = 1.0;
master_volume = 1.0; sound_effect_volume = 0.5;
sound_effect_volume = 0.5; positionless_volume = MIX_MAX_VOLUME;
positionless_volume = MIX_MAX_VOLUME; sound_effects_enabled = true;
sound_effects_enabled = true;
available_channels_bitmap = ~0; available_channels_bitmap = ~0;
Mix_AllocateChannels(number_of_channels); Mix_AllocateChannels(number_of_channels);
Mix_ChannelFinished(on_channel_finished); Mix_ChannelFinished(on_channel_finished);
} }
sound_player::~sound_player() sound_player::~sound_player() {
{ populate_from(nullptr);
populate_from(nullptr); if (singleton == this) {
if(singleton == this) singleton = nullptr;
singleton = nullptr; }
} }
void sound_player::on_channel_finished(int iChannel) void sound_player::on_channel_finished(int iChannel) {
{ sound_player* pThis = get_singleton();
sound_player *pThis = get_singleton(); if (pThis == nullptr) return;
if(pThis == nullptr)
return;
pThis->release_channel(iChannel); pThis->release_channel(iChannel);
} }
sound_player* sound_player::get_singleton() sound_player* sound_player::get_singleton() { return singleton; }
{
return singleton;
}
void sound_player::populate_from(sound_archive *pArchive) void sound_player::populate_from(sound_archive* pArchive) {
{ for (size_t i = 0; i < sound_count; ++i) {
for(size_t i = 0; i < sound_count; ++i) Mix_FreeChunk(sounds[i]);
{ }
Mix_FreeChunk(sounds[i]); delete[] sounds;
} sounds = nullptr;
delete[] sounds; sound_count = 0;
sounds = nullptr;
sound_count = 0; if (pArchive == nullptr) return;
if(pArchive == nullptr) sounds = new Mix_Chunk*[pArchive->get_number_of_sounds()];
return; for (; sound_count < pArchive->get_number_of_sounds(); ++sound_count) {
sounds[sound_count] = nullptr;
sounds = new Mix_Chunk*[pArchive->get_number_of_sounds()]; SDL_RWops* pRwop = pArchive->load_sound(sound_count);
for(; sound_count < pArchive->get_number_of_sounds(); ++sound_count) if (pRwop) {
{ sounds[sound_count] = Mix_LoadWAV_RW(pRwop, 1);
sounds[sound_count] = nullptr; if (sounds[sound_count]) {
SDL_RWops *pRwop = pArchive->load_sound(sound_count); Mix_VolumeChunk(sounds[sound_count], MIX_MAX_VOLUME);
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) void sound_player::play(size_t iIndex, double dVolume) {
{ if (available_channels_bitmap == 0 || iIndex >= sound_count ||
if(available_channels_bitmap == 0 || iIndex >= sound_count || !sounds[iIndex]) !sounds[iIndex]) {
return; 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) void sound_player::play_at(size_t iIndex, int iX, int iY) {
{ if (sound_effects_enabled) {
if(sound_effects_enabled) play_at(iIndex, sound_effect_volume, iX, iY);
play_at(iIndex, sound_effect_volume, iX, iY); }
} }
void sound_player::play_at(size_t iIndex, double dVolume, int iX, int iY) void sound_player::play_at(size_t iIndex, double dVolume, int iX, int iY) {
{ if (available_channels_bitmap == 0 || iIndex >= sound_count ||
if(available_channels_bitmap == 0 || iIndex >= sound_count || !sounds[iIndex]) !sounds[iIndex]) {
return; return;
}
double fDX = (double)(iX - camera_x); double fDX = (double)(iX - camera_x);
double fDY = (double)(iY - camera_y); double fDY = (double)(iY - camera_y);
double fDistance = sqrt(fDX * fDX + fDY * fDY); double fDistance = sqrt(fDX * fDX + fDY * fDY);
if(fDistance > camera_radius) if (fDistance > camera_radius) return;
return; fDistance = fDistance / camera_radius;
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) void sound_player::set_sound_effect_volume(double dVolume) {
{ sound_effect_volume = dVolume;
sound_effect_volume = dVolume;
} }
void sound_player::set_sound_effects_enabled(bool bOn) void sound_player::set_sound_effects_enabled(bool bOn) {
{ sound_effects_enabled = bOn;
sound_effects_enabled = bOn;
} }
int sound_player::reserve_channel() int sound_player::reserve_channel() {
{ // NB: Callers ensure that m_iChannelStatus != 0
// NB: Callers ensure that m_iChannelStatus != 0 int iChannel = 0;
int iChannel = 0; for (; (available_channels_bitmap & (1 << iChannel)) == 0; ++iChannel) {
for(; (available_channels_bitmap & (1 << iChannel)) == 0; ++iChannel) {} }
available_channels_bitmap &=~ (1 << iChannel); available_channels_bitmap &= ~(1 << iChannel);
return iChannel; return iChannel;
} }
void sound_player::release_channel(int iChannel) void sound_player::release_channel(int iChannel) {
{ available_channels_bitmap |= (1 << iChannel);
available_channels_bitmap |= (1 << iChannel);
} }
void sound_player::play_raw(size_t iIndex, int iVolume) void sound_player::play_raw(size_t iIndex, int iVolume) {
{ int iChannel = reserve_channel();
int iChannel = reserve_channel();
Mix_Volume(iChannel, iVolume); Mix_Volume(iChannel, iVolume);
Mix_PlayChannelTimed(iChannel, sounds[iIndex], 0, -1); Mix_PlayChannelTimed(iChannel, sounds[iIndex], 0, -1);
} }
void sound_player::set_camera(int iX, int iY, int iRadius) void sound_player::set_camera(int iX, int iY, int iRadius) {
{ camera_x = iX;
camera_x = iX; camera_y = iY;
camera_y = iY; camera_radius = (double)iRadius;
camera_radius = (double)iRadius; if (camera_radius < 0.001) camera_radius = 0.001;
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() {} sound_player::~sound_player() {}
sound_player* sound_player::get_singleton() {return nullptr;} sound_player* sound_player::get_singleton() { return nullptr; }
void sound_player::populate_from(sound_archive *pArchive) {} void sound_player::populate_from(sound_archive* pArchive) {}
void sound_player::play(size_t iIndex, double dVolume) {} 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, int iX, int iY) {}
void sound_player::play_at(size_t iIndex, double dVolume, 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_effect_volume(double dVolume) {}
void sound_player::set_sound_effects_enabled(bool iOn) {} void sound_player::set_sound_effects_enabled(bool iOn) {}
#endif // CORSIX_TH_USE_SDL_MIXER #endif // CORSIX_TH_USE_SDL_MIXER

View File

@@ -22,109 +22,109 @@ SOFTWARE.
#ifndef CORSIX_TH_TH_SOUND_H_ #ifndef CORSIX_TH_TH_SOUND_H_
#define CORSIX_TH_TH_SOUND_H_ #define CORSIX_TH_TH_SOUND_H_
#include "config.h"
#include <SDL.h> #include <SDL.h>
#ifdef CORSIX_TH_USE_SDL_MIXER #ifdef CORSIX_TH_USE_SDL_MIXER
#include <SDL_mixer.h> #include <SDL_mixer.h>
#endif #endif
//! Utility class for accessing Theme Hospital's SOUND-0.DAT //! Utility class for accessing Theme Hospital's SOUND-0.DAT
class sound_archive class sound_archive {
{ public:
public: sound_archive();
sound_archive(); ~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 //! Returns the number of sounds present in the archive
size_t get_number_of_sounds() const; size_t get_number_of_sounds() const;
//! Gets the name of the sound at a given index //! Gets the name of the sound at a given index
const char *get_sound_name(size_t iIndex) const; const char* get_sound_name(size_t iIndex) const;
//! Gets the duration (in miliseconds) of the sound at a given index //! Gets the duration (in miliseconds) of the sound at a given index
size_t get_sound_duration(size_t iIndex); size_t get_sound_duration(size_t iIndex);
//! Opens the sound at a given index into an SDL_RWops structure //! Opens the sound at a given index into an SDL_RWops structure
/*! /*!
The caller is responsible for closing/freeing the result. The caller is responsible for closing/freeing the result.
*/ */
SDL_RWops* load_sound(size_t iIndex); SDL_RWops* load_sound(size_t iIndex);
private: private:
#if CORSIX_TH_USE_PACK_PRAGMAS #if CORSIX_TH_USE_PACK_PRAGMAS
#pragma pack(push) #pragma pack(push)
#pragma pack(1) #pragma pack(1)
#endif #endif
struct sound_dat_file_header struct sound_dat_file_header {
{ uint8_t unknown1[50];
uint8_t unknown1[50]; uint32_t table_position;
uint32_t table_position; uint32_t unknown2;
uint32_t unknown2; uint32_t table_length;
uint32_t table_length; uint32_t table_position2;
uint32_t table_position2; uint8_t unknown3[112];
uint8_t unknown3[112]; uint32_t table_position3;
uint32_t table_position3; uint32_t table_length2;
uint32_t table_length2; uint8_t unknown4[48];
uint8_t unknown4[48]; } CORSIX_TH_PACKED_FLAGS;
} CORSIX_TH_PACKED_FLAGS;
struct sound_dat_sound_info struct sound_dat_sound_info {
{ char sound_name[18];
char sound_name[18]; uint32_t position;
uint32_t position; uint32_t unknown1;
uint32_t unknown1; uint32_t length;
uint32_t length; uint16_t unknown2;
uint16_t unknown2; } CORSIX_TH_PACKED_FLAGS;
} CORSIX_TH_PACKED_FLAGS;
#if CORSIX_TH_USE_PACK_PRAGMAS #if CORSIX_TH_USE_PACK_PRAGMAS
#pragma pack(pop) #pragma pack(pop)
#endif #endif
// TODO: header is only used in one function, should not be class variable. // TODO: header is only used in one function, should not be class variable.
sound_dat_file_header header; sound_dat_file_header header;
sound_dat_sound_info* sound_files; sound_dat_sound_info* sound_files;
uint8_t* data; uint8_t* data;
size_t sound_file_count; size_t sound_file_count;
}; };
class sound_player class sound_player {
{ public:
public: sound_player();
sound_player(); ~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(size_t iIndex, double dVolume);
void play_at(size_t iIndex, int iX, int iY); void play_at(size_t iIndex, int iX, int iY);
void play_at(size_t iIndex, double dVolume, 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_effect_volume(double dVolume);
void set_sound_effects_enabled(bool bOn); void set_sound_effects_enabled(bool bOn);
void set_camera(int iX, int iY, int iRadius); void set_camera(int iX, int iY, int iRadius);
int reserve_channel(); int reserve_channel();
void release_channel(int iChannel); void release_channel(int iChannel);
private: private:
#ifdef CORSIX_TH_USE_SDL_MIXER #ifdef CORSIX_TH_USE_SDL_MIXER
static sound_player* singleton; static sound_player* singleton;
static void on_channel_finished(int iChannel); 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; Mix_Chunk** sounds;
size_t sound_count; 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. uint32_t available_channels_bitmap; ///< The bit index corresponding to a
int camera_x; ///< channel is 1 if the channel is
int camera_y; ///< available and 0 if it is reserved
double camera_radius; ///< or in use.
double master_volume; int camera_x;
double sound_effect_volume; int camera_y;
int positionless_volume; double camera_radius;
bool sound_effects_enabled; double master_volume;
#endif // CORSIX_TH_USE_SDL_MIXER 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_

View File

@@ -22,8 +22,8 @@ SOFTWARE.
#include "config.h" #include "config.h"
#ifdef CORSIX_TH_USE_SDL_MIXER #ifdef CORSIX_TH_USE_SDL_MIXER
#include <cstring>
#include <algorithm> #include <algorithm>
#include <cstring>
#include <iterator> #include <iterator>
#include <new> #include <new>
#include <vector> #include <vector>
@@ -31,387 +31,307 @@ SOFTWARE.
/*! /*!
Utility class for reading or writing to memory as if it were a file. Utility class for reading or writing to memory as if it were a file.
*/ */
class memory_buffer class memory_buffer {
{ public:
public: memory_buffer()
memory_buffer() : data(nullptr),
: data(nullptr), pointer(nullptr), data_end(nullptr), buffer_end(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 <class T>
bool read(T& value) {
return read(&value, 1);
}
template <class T>
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<unsigned int>(iByte & 0x7F);
if ((iByte & 0x80) == 0) break;
}
return iValue;
}
template <class T>
bool write(const T& value) {
return write(&value, 1);
}
template <class T>
bool write(const T* values, size_t count) {
if (!skip(static_cast<int>(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 <class T>
static T byte_swap(T value) {
T swapped = 0;
for (int i = 0; i < static_cast<int>(sizeof(T)) * 8; i += 8) {
swapped = static_cast<T>(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) char* pNewData = new (std::nothrow) char[size * 2];
{ if (pNewData == nullptr) return false;
data = pointer = (char*)pData; size_t iOldLength = data_end - data;
data_end = data + iLength; if (iOldLength > 0) {
buffer_end = nullptr; 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() char *data, *pointer, *data_end, *buffer_end;
{
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 <class T>
bool read(T& value)
{
return read(&value, 1);
}
template <class T>
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<unsigned int>(iByte & 0x7F);
if((iByte & 0x80) == 0)
break;
}
return iValue;
}
template <class T>
bool write(const T& value)
{
return write(&value, 1);
}
template <class T>
bool write(const T* values, size_t count)
{
if(!skip(static_cast<int>(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 <class T>
static T byte_swap(T value)
{
T swapped = 0;
for(int i = 0; i < static_cast<int>(sizeof(T)) * 8; i += 8)
{
swapped = static_cast<T>(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;
}; };
struct midi_token struct midi_token {
{ int time;
int time; unsigned int buffer_length;
unsigned int buffer_length; const char* buffer;
const char *buffer; uint8_t type;
uint8_t type; uint8_t data;
uint8_t data;
}; };
bool operator < (const midi_token& oLeft, const midi_token& oRight) bool operator<(const midi_token& oLeft, const midi_token& oRight) {
{ return oLeft.time < oRight.time;
return oLeft.time < oRight.time;
} }
struct midi_token_list : std::vector<midi_token> struct midi_token_list : std::vector<midi_token> {
{ midi_token* append(int iTime, uint8_t iType) {
midi_token* append(int iTime, uint8_t iType) push_back(midi_token());
{ midi_token* pToken = &back();
push_back(midi_token()); pToken->time = iTime;
midi_token* pToken = &back(); pToken->type = iType;
pToken->time = iTime; return pToken;
pToken->type = iType; }
return pToken;
}
}; };
uint8_t* transcode_xmi_to_midi(const unsigned char* xmi_data, uint8_t* transcode_xmi_to_midi(const unsigned char* xmi_data, size_t xmi_length,
size_t xmi_length, size_t* midi_length) size_t* midi_length) {
{ if (xmi_data == nullptr) {
if (xmi_data == nullptr) { return 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)) if (!bufInput.scan_to("EVNT", 4) || !bufInput.skip(8)) return nullptr;
return nullptr;
midi_token_list lstTokens; midi_token_list lstTokens;
midi_token* pToken; midi_token* pToken;
int iTokenTime = 0; int iTokenTime = 0;
int iTempo = 500000; int iTempo = 500000;
bool bTempoSet = false; bool bTempoSet = false;
bool bEnd = false; bool bEnd = false;
uint8_t iTokenType, iExtendedType; uint8_t iTokenType, iExtendedType;
while(!bufInput.is_end_of_buffer() && !bEnd) while (!bufInput.is_end_of_buffer() && !bEnd) {
{ while (true) {
while(true) if (!bufInput.read(iTokenType)) return nullptr;
{
if(!bufInput.read(iTokenType))
return nullptr;
if(iTokenType & 0x80) if (iTokenType & 0x80)
break; break;
else else
iTokenTime += static_cast<int>(iTokenType) * 3; iTokenTime += static_cast<int>(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;
}
} }
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()) if (iExtendedType == 0x2F)
return nullptr; bEnd = true;
else if (iExtendedType == 0x51) {
memory_buffer bufOutput; if (!bTempoSet) {
if(!bufOutput.write("MThd\0\0\0\x06\0\0\0\x01", 12)) bufInput.skip(1);
return nullptr; iTempo = bufInput.read_big_endian_uint24() * 3;
if(!bufOutput.write_big_endian_uint16(static_cast<uint16_t>((iTempo * 3) / 25000))) bTempoSet = true;
return nullptr; bufInput.skip(-4);
if(!bufOutput.write("MTrk\xBA\xAD\xF0\x0D", 8)) } else {
return nullptr; lstTokens.pop_back();
if (!bufInput.skip(bufInput.read_variable_length_uint()))
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; return nullptr;
if(iTokenType == 0xFF) break;
{
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;
} }
}
} }
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<uint32_t>(bufOutput.tell() - 22); if (lstTokens.empty()) return nullptr;
bufOutput.seek(18);
bufOutput.write_big_endian_uint32(iLength);
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<uint16_t>((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<uint32_t>(bufOutput.tell() - 22);
bufOutput.seek(18);
bufOutput.write_big_endian_uint32(iLength);
return bufOutput.take_data(midi_length);
} }
#endif #endif

View File

@@ -25,18 +25,17 @@ SOFTWARE.
#include "config.h" #include "config.h"
#ifdef CORSIX_TH_USE_SDL_MIXER #ifdef CORSIX_TH_USE_SDL_MIXER
uint8_t* transcode_xmi_to_midi(const unsigned char* xmi_data, uint8_t* transcode_xmi_to_midi(const unsigned char* xmi_data, size_t xmi_length,
size_t xmi_length, size_t* midi_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, inline uint8_t* transcode_xmi_to_midi(const unsigned char* xmi_data,
size_t xmi_length, size_t* midi_length) size_t xmi_length, size_t* midi_length) {
{ // When SDL_mixer isn't being used, there is no need to transocde XMI to
// When SDL_mixer isn't being used, there is no need to transocde XMI to // MIDI, so the function always fails.
// MIDI, so the function always fails. return nullptr;
return nullptr;
} }
#endif // CORSIX_TH_USE_SDL_MIXER #endif // CORSIX_TH_USE_SDL_MIXER
#endif // CORSIX_TH_XMI2MID_H_ #endif // CORSIX_TH_XMI2MID_H_

View File

@@ -20,36 +20,38 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE. SOFTWARE.
*/ */
#include "config.h"
#include "../Src/main.h" #include "../Src/main.h"
#include "../Src/bootstrap.h" #include "config.h"
#include <stack>
#include <SDL.h> #include <SDL.h>
#include <stack>
#include "../Src/bootstrap.h"
#ifdef CORSIX_TH_USE_SDL_MIXER #ifdef CORSIX_TH_USE_SDL_MIXER
#include <SDL_mixer.h> #include <SDL_mixer.h>
#endif #endif
// Template magic for checking type equality // Template magic for checking type equality
template <typename T1, typename T2> template <typename T1, typename T2>
struct types_equal{ enum{ struct types_equal {
enum {
result = -1, result = -1,
}; }; };
};
template <typename T1> template <typename T1>
struct types_equal<T1, T1>{ enum{ struct types_equal<T1, T1> {
enum {
result = 1, result = 1,
}; }; };
};
static void cleanup(lua_State* L) static void cleanup(lua_State* L) {
{
#ifdef CORSIX_TH_USE_SDL_MIXER #ifdef CORSIX_TH_USE_SDL_MIXER
while(Mix_QuerySpec(nullptr, nullptr, nullptr)) while (Mix_QuerySpec(nullptr, nullptr, nullptr)) {
{ Mix_CloseAudio();
Mix_CloseAudio(); }
}
#endif #endif
SDL_Quit(); SDL_Quit();
lua_close(L); lua_close(L);
} }
//! Program entry point //! 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 sooner, hence this function does as little as possible and leaves the rest
for lua_main(). for lua_main().
*/ */
int main(int argc, char** argv) int main(int argc, char** argv) {
{ struct compile_time_lua_check {
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 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 // Lua numbers must be doubles so that the mantissa has at least
// 32 bits (floats only have 24 bits) // 32 bits (floats only have 24 bits)
int number_is_double[types_equal<lua_Number, double>::result]; int number_is_double[types_equal<lua_Number, double>::result];
}; };
bool bRun = true; bool bRun = true;
while(bRun) while (bRun) {
{ lua_State* L = NULL;
lua_State *L = NULL;
L = luaL_newstate(); L = luaL_newstate();
if(L == NULL) if (L == NULL) {
{ fprintf(stderr,
fprintf(stderr, "Fatal error starting CorsixTH: " "Fatal error starting CorsixTH: "
"Cannot open Lua state.\n"); "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; 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;
} }

View File

@@ -35,12 +35,13 @@ SOFTWARE.
*/ */
#include "rnc.h" #include "rnc.h"
#include <vector>
#include <cstdint>
#include <cstddef> #include <cstddef>
#include <cstdint>
#include <vector>
static const std::uint32_t rnc_signature = 0x524E4301; /*!< "RNC\001" */ static const std::uint32_t rnc_signature = 0x524E4301; /*!< "RNC\001" */
// clang-format off
static const std::uint16_t rnc_crc_table[256] = { static const std::uint16_t rnc_crc_table[256] = {
0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241, 0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241,
0xC601, 0x06C0, 0x0780, 0xC741, 0x0500, 0xC5C1, 0xC481, 0x0440, 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, 0x4400, 0x84C1, 0x8581, 0x4540, 0x8701, 0x47C0, 0x4680, 0x8641,
0x8201, 0x42C0, 0x4380, 0x8341, 0x4100, 0x81C1, 0x8081, 0x4040, 0x8201, 0x42C0, 0x4380, 0x8341, 0x4100, 0x81C1, 0x8081, 0x4040,
}; };
// clang-format on
struct bit_stream struct bit_stream {
{ std::uint32_t bitbuf; ///< holds between 16 and 32 bits.
std::uint32_t bitbuf; ///< holds between 16 and 32 bits. int bitcount; ///< how many bits does bitbuf hold?
int bitcount; ///< how many bits does bitbuf hold? const std::uint8_t* endpos; ///< pointer past the readable data
const std::uint8_t* endpos; ///< pointer past the readable data const std::uint8_t* p; ///< pointer in data that stream is reading.
const std::uint8_t* p; ///< pointer in data that stream is reading.
}; };
struct huf_table struct huf_table {
{ int num; ///< number of nodes in the tree.
int num; ///< number of nodes in the tree. struct {
struct std::uint32_t code;
{ int codelen;
std::uint32_t code; int value;
int codelen; } table[32];
int value;
} table[32];
}; };
//! Calculate a CRC, the RNC way. //! Calculate a CRC, the RNC way.
@@ -100,56 +99,50 @@ struct huf_table
@param data data for which to calculate the CRC @param data data for which to calculate the CRC
@param len length of the data in bytes @param len length of the data in bytes
*/ */
static std::uint16_t rnc_crc(const std::uint8_t* data, std::size_t len) static std::uint16_t rnc_crc(const std::uint8_t* data, std::size_t len) {
{ std::uint16_t val = 0;
std::uint16_t val = 0;
while(len--) while (len--) {
{ val = static_cast<std::uint16_t>(val ^ *data++);
val = static_cast<std::uint16_t>(val ^ *data++); val = static_cast<std::uint16_t>((val >> 8) ^ rnc_crc_table[val & 0xFF]);
val = static_cast<std::uint16_t>((val >> 8) ^ rnc_crc_table[val & 0xFF]); }
}
return val; return val;
} }
//! Return the big-endian 32 bit word at p. //! Return the big-endian 32 bit word at p.
/*! /*!
@param p Pointer to data containing the word @param p Pointer to data containing the word
*/ */
static std::uint32_t blong (const std::uint8_t *p) static std::uint32_t blong(const std::uint8_t* p) {
{ std::uint32_t n;
std::uint32_t n; n = p[0];
n = p[0]; n = (n << 8) + p[1];
n = (n << 8) + p[1]; n = (n << 8) + p[2];
n = (n << 8) + p[2]; n = (n << 8) + p[3];
n = (n << 8) + p[3]; return n;
return n;
} }
//! Return the big-endian 16 bit word at p. //! Return the big-endian 16 bit word at p.
/*! /*!
@param p Pointer to data containing the word @param p Pointer to data containing the word
*/ */
static std::uint32_t bword (const std::uint8_t *p) static std::uint32_t bword(const std::uint8_t* p) {
{ std::uint32_t n;
std::uint32_t n; n = p[0];
n = p[0]; n = (n << 8) + p[1];
n = (n << 8) + p[1]; return n;
return n;
} }
//! Return the little-endian 16 bit word at p. //! Return the little-endian 16 bit word at p.
/*! /*!
@param p Pointer to data containing the word @param p Pointer to data containing the word
*/ */
static std::uint32_t lword (const std::uint8_t *p) static std::uint32_t lword(const std::uint8_t* p) {
{ std::uint32_t n;
std::uint32_t n; n = p[1];
n = p[1]; n = (n << 8) + p[0];
n = (n << 8) + p[0]; return n;
return n;
} }
//! Mirror the bottom n bits of x. //! Mirror the bottom n bits of x.
@@ -157,24 +150,20 @@ static std::uint32_t lword (const std::uint8_t *p)
@param x @param x
@param n @param n
*/ */
static std::uint32_t mirror (std::uint32_t x, int n) static std::uint32_t mirror(std::uint32_t x, int n) {
{ std::uint32_t top = 1 << (n - 1), bottom = 1;
std::uint32_t top = 1 << (n-1), bottom = 1; while (top > bottom) {
while (top > bottom) std::uint32_t mask = top | bottom;
{ std::uint32_t masked = x & mask;
std::uint32_t mask = top | bottom; if (masked != 0 && masked != mask) {
std::uint32_t masked = x & mask; x ^= mask;
if (masked != 0 && masked != mask)
{
x ^= mask;
}
top >>= 1;
bottom <<= 1;
} }
return x; top >>= 1;
bottom <<= 1;
}
return x;
} }
//! Initialises a bit stream with the first two bytes of the packed //! Initialises a bit stream with the first two bytes of the packed
//! data. //! 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 @param endpos Pointer to byte after the last memory block the bitstream is
to traverse to traverse
*/ */
static void bitread_init (bit_stream *bs, const std::uint8_t *p, const std::uint8_t* endpos) static void bitread_init(bit_stream* bs, const std::uint8_t* p,
{ const std::uint8_t* endpos) {
bs->bitbuf = lword(p); bs->bitbuf = lword(p);
bs->bitcount = 16; bs->bitcount = 16;
bs->p = p; bs->p = p;
bs->endpos = endpos; bs->endpos = endpos;
} }
//! Fixes up a bit stream after literals have been read out of the //! 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 @param bs Bit stream to correct
*/ */
static void bitread_fix (bit_stream *bs) static void bitread_fix(bit_stream* bs) {
{ // Remove the top 16 bits
// Remove the top 16 bits bs->bitcount -= 16;
bs->bitcount -= 16; bs->bitbuf &= (1 << bs->bitcount) - 1;
bs->bitbuf &= (1<<bs->bitcount)-1;
// Replace with what is in the new current location // Replace with what is in the new current location
// in the bit stream // in the bit stream
if(bs->p < bs->endpos - 1) if (bs->p < bs->endpos - 1) {
{ bs->bitbuf |= (lword(bs->p) << bs->bitcount);
bs->bitbuf |= (lword(bs->p)<<bs->bitcount); bs->bitcount += 16;
bs->bitcount += 16; } else if (bs->p == bs->endpos - 1) {
} else if (bs->p == bs->endpos - 1) { bs->bitbuf |= (*(bs->p) << bs->bitcount);
bs->bitbuf |= (*(bs->p)<<bs->bitcount); bs->bitcount += 16;
bs->bitcount += 16; }
}
} }
//! Return a word consisting of the specified bits without advancing //! 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 bs Bit stream from which to peek
@param mask A 32 bit bit mask specifying which bits 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) static std::uint32_t bit_peek(bit_stream* bs, const std::uint32_t mask) {
{ return bs->bitbuf & mask;
return bs->bitbuf & mask;
} }
//! Advances the bit stream. //! 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 @param n Number of bits to advance the stream. Must be
between 0 and 16 between 0 and 16
*/ */
static void bit_advance (bit_stream *bs, int n) static void bit_advance(bit_stream* bs, int n) {
{ bs->bitbuf >>= n;
bs->bitbuf >>= n; bs->bitcount -= n;
bs->bitcount -= n;
if (bs->bitcount < 16) if (bs->bitcount < 16) {
{ // At this point it is possible for bs->p to advance past
// 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
// the end of the data. In that case we simply do not read // anything more into the buffer. If we are on the last
// anything more into the buffer. If we are on the last // byte the lword matches what is in that byte.
// byte the lword matches what is in that byte. bs->p += 2;
bs->p += 2;
if (bs->p < (bs->endpos - 1)) if (bs->p < (bs->endpos - 1)) {
{ bs->bitbuf |= (lword(bs->p) << bs->bitcount);
bs->bitbuf |= (lword(bs->p)<<bs->bitcount); bs->bitcount += 16;
bs->bitcount += 16; } else if (bs->p < bs->endpos) {
} else if (bs->p < bs->endpos) { bs->bitbuf |= (*(bs->p) << bs->bitcount);
bs->bitbuf |= (*(bs->p)<<bs->bitcount); bs->bitcount += 16;
bs->bitcount += 16;
}
} }
}
} }
//! Returns bits from the bit stream matching the mask and advances it //! 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 @param n Number of bits to advance the stream. Must be
between 0 and 16 between 0 and 16
*/ */
static std::uint32_t bit_read (bit_stream *bs, std::uint32_t mask, int n) static std::uint32_t bit_read(bit_stream* bs, std::uint32_t mask, int n) {
{ std::uint32_t result = bit_peek(bs, mask);
std::uint32_t result = bit_peek(bs, mask); bit_advance(bs, n);
bit_advance(bs, n); return result;
return result;
} }
//! Read a Huffman table out of the bit stream given. //! 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 @param bs Bit stream pointing to the start of the Huffman table
description description
*/ */
static void read_huftable(huf_table *h, bit_stream *bs) static void read_huftable(huf_table* h, bit_stream* bs) {
{ int i, j, k, num;
int i, j, k, num; int leaflen[32];
int leaflen[32]; int leafmax;
int leafmax; std::uint32_t codeb; /* big-endian form of code. */
std::uint32_t codeb; /* big-endian form of code. */
num = bit_read(bs, 0x1F, 5); num = bit_read(bs, 0x1F, 5);
if(num == 0) if (num == 0) {
{ return;
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; codeb = 0L;
for(i = 0; i < num; i++) k = 0;
{ for (i = 1; i <= leafmax; i++) {
leaflen[i] = bit_read(bs, 0x0F, 4); for (j = 0; j < num; j++) {
if (leafmax < leaflen[i]) if (leaflen[j] == i) {
{ h->table[k].code = mirror(codeb, i);
leafmax = leaflen[i]; h->table[k].codelen = i;
} h->table[k].value = j;
codeb++;
k++;
}
} }
codeb <<= 1;
codeb = 0L; }
k = 0; h->num = k;
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;
} }
//! Read a value out of the bit stream using the given Huffman table. //! 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 h Huffman table to transcribe from
@param bs bit stream @param bs bit stream
@param p input data @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) static std::uint32_t huf_read(huf_table* h, bit_stream* bs,
{ const std::uint8_t** p) {
int i; int i;
std::uint32_t val; std::uint32_t val;
std::uint32_t mask; std::uint32_t mask;
// Find the current bits in the table // Find the current bits in the table
for (i = 0; i < h->num; i++) for (i = 0; i < h->num; i++) {
{ mask = (1 << h->table[i].codelen) - 1;
mask = (1 << h->table[i].codelen) - 1; if (bit_peek(bs, mask) == h->table[i].code) {
if(bit_peek(bs, mask) == h->table[i].code) break;
{
break;
}
} }
}
// No match found in table (error) // No match found in table (error)
if(i == h->num) if (i == h->num) {
{ return -1;
return -1; }
}
bit_advance(bs, h->table[i].codelen); bit_advance(bs, h->table[i].codelen);
val = h->table[i].value; val = h->table[i].value;
if (val >= 2) if (val >= 2) {
{ val = 1 << (val - 1);
val = 1 << (val-1); val |= bit_read(bs, val - 1, h->table[i].value - 1);
val |= bit_read(bs, val-1, h->table[i].value - 1); }
} return val;
return val;
} }
std::size_t rnc_output_size(const std::uint8_t* input) std::size_t rnc_output_size(const std::uint8_t* input) {
{ return static_cast<std::size_t>(blong(input + 4));
return static_cast<std::size_t>(blong(input + 4));
} }
std::size_t rnc_input_size(const std::uint8_t* input) std::size_t rnc_input_size(const std::uint8_t* input) {
{ return static_cast<std::size_t>(blong(input + 8) + rnc_header_size);
return static_cast<std::size_t>(blong(input + 8) + rnc_header_size);
} }
//! Decompresses RNC data //! 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 4 byte segment of the input header starting at the 4th byte
in Big-endian. in Big-endian.
*/ */
rnc_status rnc_unpack(const std::uint8_t* input, std::uint8_t* output) rnc_status rnc_unpack(const std::uint8_t* input, std::uint8_t* output) {
{ const std::uint8_t* inputend;
const std::uint8_t *inputend; std::uint8_t* outputend;
std::uint8_t *outputend; bit_stream input_bs;
bit_stream input_bs; huf_table raw = {0}, dist = {0}, len = {0};
huf_table raw = {0}, dist = {0}, len = {0}; std::uint32_t ch_count;
std::uint32_t ch_count; std::uint32_t ret_len;
std::uint32_t ret_len; std::uint32_t out_crc;
std::uint32_t out_crc; if (blong(input) != rnc_signature) {
if(blong(input) != rnc_signature) return rnc_status::file_is_not_rnc;
{ }
return rnc_status::file_is_not_rnc; ret_len = blong(input + 4);
} outputend = output + ret_len;
ret_len = blong(input + 4); inputend = input + 18 + blong(input + 8);
outputend = output + ret_len;
inputend = input + 18 + blong(input + 8);
//skip header // skip header
input += 18; input += 18;
// Check the packed-data CRC. Also save the unpacked-data CRC // Check the packed-data CRC. Also save the unpacked-data CRC
// for later. // for later.
if (rnc_crc(input, inputend - input) != bword(input - 4)) if (rnc_crc(input, inputend - input) != bword(input - 4)) {
{ return rnc_status::packed_crc_error;
return rnc_status::packed_crc_error; }
} out_crc = bword(input - 6);
out_crc = bword(input - 6);
//initialize the bitstream to the input and advance past the // initialize the bitstream to the input and advance past the
//first two bits as they don't have any understood use. // first two bits as they don't have any understood use.
bitread_init(&input_bs, input, inputend); bitread_init(&input_bs, input, inputend);
bit_advance(&input_bs, 2); bit_advance(&input_bs, 2);
//process chunks // process chunks
while (output < outputend) while (output < outputend) {
{ read_huftable(&raw, &input_bs); // raw byte length table
read_huftable(&raw, &input_bs); //raw byte length table read_huftable(&dist, &input_bs); // distance prior to copy table
read_huftable(&dist, &input_bs); //distance prior to copy table read_huftable(&len, &input_bs); // length bytes to copy table
read_huftable(&len, &input_bs); //length bytes to copy table ch_count = bit_read(&input_bs, 0xFFFF, 16);
ch_count = bit_read(&input_bs, 0xFFFF, 16);
while(true) while (true) {
{ long length, posn;
long length, posn;
// Copy bit pattern to output based on lookup // Copy bit pattern to output based on lookup
// of bytes from input. // of bytes from input.
length = huf_read(&raw, &input_bs, &input); length = huf_read(&raw, &input_bs, &input);
if(length == -1) if (length == -1) {
{ return rnc_status::huf_decode_error;
return rnc_status::huf_decode_error; }
} if (length) {
if(length) while (length--) {
{ *output++ = *(input_bs.p++);
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++;
}
} }
} bitread_fix(&input_bs);
}
if (--ch_count <= 0) {
break;
}
if(outputend != output) // Read position to copy output to
{ posn = huf_read(&dist, &input_bs, &input);
return rnc_status::file_size_mismatch; if (posn == -1) {
} return rnc_status::huf_decode_error;
}
posn += 1;
// Check the unpacked-data CRC. // Read length of output to copy back
if (rnc_crc(outputend - ret_len, ret_len) != out_crc) length = huf_read(&len, &input_bs, &input);
{ if (length == -1) {
return rnc_status::unpacked_crc_error; 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;
} }

View File

@@ -27,14 +27,13 @@ SOFTWARE.
#include <cstdint> #include <cstdint>
/*! Result status values from #rnc_inpack. */ /*! Result status values from #rnc_inpack. */
enum class rnc_status enum class rnc_status {
{ ok, ///< Everything is fine
ok, ///< Everything is fine file_is_not_rnc, ///< The file does not begin with an RNC signature
file_is_not_rnc, ///< The file does not begin with an RNC signature huf_decode_error, ///< Error decoding the file
huf_decode_error, ///< Error decoding the file file_size_mismatch, ///< The file size does not match the header
file_size_mismatch, ///< The file size does not match the header packed_crc_error, ///< The compressed file does not match its checksum
packed_crc_error, ///< The compressed file does not match its checksum unpacked_crc_error ///< The uncompressed 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; 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); rnc_status rnc_unpack(const std::uint8_t* input, std::uint8_t* output);
#endif // CORSIX_TH_RNC_H_ #endif // CORSIX_TH_RNC_H_