[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
CMakeScripts/
# Clang tools
compile_commands.json
# This is for the CMake-generated Visual Studio project
*.vcxproj
*.vcxproj.user

View File

@@ -44,7 +44,6 @@ before_script:
# Don't ask for confirmation when using scp
- echo -e "Host armedpineapple.co.uk\n\tStrictHostKeyChecking no\n" >> ~/.ssh/config
- echo -e "Host server2.armedpineapple.co.uk\n\tStrictHostKeyChecking no\n" >> ~/.ssh/config
- cd fresh
script:
# Check if there are trailing whitespaces.
- ${TRAVIS_BUILD_DIR}/scripts/check_trailing_whitespaces.py $TRAVIS_BUILD_DIR
@@ -52,7 +51,12 @@ script:
- ${TRAVIS_BUILD_DIR}/scripts/check_language_files_not_BOM.py $TRAVIS_BUILD_DIR/CorsixTH/Lua/languages
# Check if there are lua classes with invalid/improper declarations.
- ${TRAVIS_BUILD_DIR}/scripts/check_lua_classes.py
# Check cpp format
- cd ${TRAVIS_BUILD_DIR}
- clang-format -i CorsixTH/Src/*{.cpp,.h} AnimView/*{.cpp,.h} common/*{.cpp,.h} CorsixTH/SrcUnshared/main.cpp
- git diff --exit-code
# Build CorsixTH
- cd fresh
- make VERBOSE=1
# Validate lua files
- find $TRAVIS_BUILD_DIR -path $TRAVIS_BUILD_DIR/CorsixTH/Lua/languages -prune -o -name '*.lua' -print0 | xargs -0 luac -p --

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

@@ -20,15 +20,15 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include "config.h"
#include "frmSprites.h"
#include <wx/sizer.h>
#include <wx/stattext.h>
#include <wx/msgdlg.h>
#include "config.h"
#include <wx/dcclient.h>
#include <wx/dirdlg.h>
#include <wx/filedlg.h>
#include <wx/msgdlg.h>
#include <wx/sizer.h>
#include <wx/stattext.h>
#include <wx/vscroll.h>
#include <wx/dcclient.h>
BEGIN_EVENT_TABLE(frmSprites, wxFrame)
EVT_BUTTON(ID_LOAD, frmSprites::_onLoad)
@@ -40,171 +40,177 @@ BEGIN_EVENT_TABLE(frmSprites, wxFrame)
END_EVENT_TABLE()
frmSprites::frmSprites()
: wxFrame(NULL, wxID_ANY, L"Theme Hospital Sprite Viewer")
{
wxBoxSizer *pMainSizer = new wxBoxSizer(wxVERTICAL);
: wxFrame(NULL, wxID_ANY, L"Theme Hospital Sprite Viewer") {
wxBoxSizer* pMainSizer = new wxBoxSizer(wxVERTICAL);
wxStaticBoxSizer *pFiles = new wxStaticBoxSizer(wxVERTICAL, this, L"Files");
wxFlexGridSizer *pFilesGrid = new wxFlexGridSizer(4, 3, 2, 1);
pFilesGrid->AddGrowableCol(1, 1);
pFilesGrid->Add(new wxStaticText(this, wxID_ANY, L"Table:"), 0, wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT);
pFilesGrid->Add(m_txtTable = new wxTextCtrl(this, wxID_ANY, L"X:\\ThemeHospital\\hospital\\QData\\Font00V.tab"), 1, wxALIGN_CENTER_VERTICAL | wxEXPAND);
pFilesGrid->Add(new wxButton(this, ID_BROWSE_TABLE, L"Browse..."), 0, wxALIGN_CENTER_VERTICAL);
pFilesGrid->Add(new wxStaticText(this, wxID_ANY, L"Data:"), 0, wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT);
pFilesGrid->Add(m_txtData = new wxTextCtrl(this, wxID_ANY, L""), 1, wxALIGN_CENTER_VERTICAL | wxEXPAND);
pFilesGrid->Add(new wxButton(this, ID_BROWSE_DATA, L"Browse..."), 0, wxALIGN_CENTER_VERTICAL);
pFilesGrid->Add(new wxStaticText(this, wxID_ANY, L"Palette:"), 0, wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT);
pFilesGrid->Add(m_txtPalette = new wxTextCtrl(this, wxID_ANY, L"X:\\ThemeHospital\\hospital\\Data\\MPalette.dat"), 1, wxALIGN_CENTER_VERTICAL | wxEXPAND);
pFilesGrid->Add(new wxButton(this, ID_BROWSE_PALETTE, L"Browse..."), 0, wxALIGN_CENTER_VERTICAL);
pFiles->Add(pFilesGrid, 0, wxEXPAND | wxALL, 1);
wxButton *pTmp;
pFiles->Add(pTmp = new wxButton(this, ID_LOAD, L"Load Simple"), 0, wxALIGN_CENTER | wxALL, 1);
pFiles->Add(pTmp = new wxButton(this, ID_LOAD_COMPLEX, L"Load Complex"), 0, wxALIGN_CENTER | wxALL, 1);
pFiles->Add(pTmp = new wxButton(this, ID_NEXT, L"Next"), 0, wxALIGN_CENTER | wxALL, 1);
SetBackgroundColour(pTmp->GetBackgroundColour());
pMainSizer->Add(pFiles, 0, wxEXPAND | wxALL, 2);
wxStaticBoxSizer* pFiles = new wxStaticBoxSizer(wxVERTICAL, this, L"Files");
wxFlexGridSizer* pFilesGrid = new wxFlexGridSizer(4, 3, 2, 1);
pFilesGrid->AddGrowableCol(1, 1);
pFilesGrid->Add(new wxStaticText(this, wxID_ANY, L"Table:"), 0,
wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT);
pFilesGrid->Add(
m_txtTable = new wxTextCtrl(
this, wxID_ANY, L"X:\\ThemeHospital\\hospital\\QData\\Font00V.tab"),
1, wxALIGN_CENTER_VERTICAL | wxEXPAND);
pFilesGrid->Add(new wxButton(this, ID_BROWSE_TABLE, L"Browse..."), 0,
wxALIGN_CENTER_VERTICAL);
pFilesGrid->Add(new wxStaticText(this, wxID_ANY, L"Data:"), 0,
wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT);
pFilesGrid->Add(m_txtData = new wxTextCtrl(this, wxID_ANY, L""), 1,
wxALIGN_CENTER_VERTICAL | wxEXPAND);
pFilesGrid->Add(new wxButton(this, ID_BROWSE_DATA, L"Browse..."), 0,
wxALIGN_CENTER_VERTICAL);
pFilesGrid->Add(new wxStaticText(this, wxID_ANY, L"Palette:"), 0,
wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT);
pFilesGrid->Add(
m_txtPalette = new wxTextCtrl(
this, wxID_ANY, L"X:\\ThemeHospital\\hospital\\Data\\MPalette.dat"),
1, wxALIGN_CENTER_VERTICAL | wxEXPAND);
pFilesGrid->Add(new wxButton(this, ID_BROWSE_PALETTE, L"Browse..."), 0,
wxALIGN_CENTER_VERTICAL);
pFiles->Add(pFilesGrid, 0, wxEXPAND | wxALL, 1);
wxButton* pTmp;
pFiles->Add(pTmp = new wxButton(this, ID_LOAD, L"Load Simple"), 0,
wxALIGN_CENTER | wxALL, 1);
pFiles->Add(pTmp = new wxButton(this, ID_LOAD_COMPLEX, L"Load Complex"), 0,
wxALIGN_CENTER | wxALL, 1);
pFiles->Add(pTmp = new wxButton(this, ID_NEXT, L"Next"), 0,
wxALIGN_CENTER | wxALL, 1);
SetBackgroundColour(pTmp->GetBackgroundColour());
pMainSizer->Add(pFiles, 0, wxEXPAND | wxALL, 2);
wxStaticBoxSizer *pSprites = new wxStaticBoxSizer(wxVERTICAL, this, L"Sprites");
pSprites->Add(m_panFrame = new MyVScrolled(this), 1, wxEXPAND);
pMainSizer->Add(pSprites, 1, wxEXPAND | wxALL, 2);
m_panFrame->Connect(wxEVT_PAINT, (wxObjectEventFunction)&frmSprites::_onPanelPaint, NULL, this);
wxStaticBoxSizer* pSprites =
new wxStaticBoxSizer(wxVERTICAL, this, L"Sprites");
pSprites->Add(m_panFrame = new MyVScrolled(this), 1, wxEXPAND);
pMainSizer->Add(pSprites, 1, wxEXPAND | wxALL, 2);
m_panFrame->Connect(wxEVT_PAINT,
(wxObjectEventFunction)&frmSprites::_onPanelPaint, NULL,
this);
SetSizer(pMainSizer);
SetSizer(pMainSizer);
load(true);
load(true);
}
frmSprites::~frmSprites()
{
}
frmSprites::~frmSprites() {}
void frmSprites::_onLoad(wxCommandEvent& evt)
{
load(false);
}
void frmSprites::_onLoad(wxCommandEvent& evt) { load(false); }
void frmSprites::_onLoadComplex(wxCommandEvent& evt)
{
load(true);
}
void frmSprites::_onLoadComplex(wxCommandEvent& evt) { load(true); }
void frmSprites::_onNext(wxCommandEvent& evt)
{
wxString s = m_txtTable->GetValue();
while(true)
{
const wxChar* sc = s.c_str();
for(size_t i = s.Length(); i > 0;)
{
--i;
if('0' <= sc[i] && sc[i] <= '9')
{
s.SetChar(i, sc[i] + 1);
if(sc[i] > '9')
{
s.SetChar(i, '0');
if(sc[i - 1] == '9')
{
s.SetChar(i - 1, '0');
return;
}
s.SetChar(i - 1, sc[i - 1] + 1);
}
break;
}
}
if(::wxFileExists(s))
{
m_txtTable->SetValue(s);
void frmSprites::_onNext(wxCommandEvent& evt) {
wxString s = m_txtTable->GetValue();
while (true) {
const wxChar* sc = s.c_str();
for (size_t i = s.Length(); i > 0;) {
--i;
if ('0' <= sc[i] && sc[i] <= '9') {
s.SetChar(i, sc[i] + 1);
if (sc[i] > '9') {
s.SetChar(i, '0');
if (sc[i - 1] == '9') {
s.SetChar(i - 1, '0');
return;
}
s.SetChar(i - 1, sc[i - 1] + 1);
}
break;
}
}
if (::wxFileExists(s)) {
m_txtTable->SetValue(s);
return;
}
}
}
void frmSprites::load(bool bComplex)
{
if(!m_oAnims.loadTableFile(m_txtTable->GetValue())
||!m_oAnims.loadSpriteFile(m_txtData->GetValue().IsEmpty() ? m_txtTable->GetValue().BeforeLast('.')+L".DAT" : m_txtData->GetValue())
||!m_oAnims.loadPaletteFile(m_txtPalette->GetValue()))
{
::wxMessageBox(L"Cannot load files");
return;
void frmSprites::load(bool bComplex) {
if (!m_oAnims.loadTableFile(m_txtTable->GetValue()) ||
!m_oAnims.loadSpriteFile(m_txtData->GetValue().IsEmpty()
? m_txtTable->GetValue().BeforeLast('.') +
L".DAT"
: m_txtData->GetValue()) ||
!m_oAnims.loadPaletteFile(m_txtPalette->GetValue())) {
::wxMessageBox(L"Cannot load files");
return;
}
m_vSprites.clear();
for (size_t i = 0; i < m_oAnims.getSpriteCount(); ++i) {
_sprite_t oSprite;
Bitmap* pSpriteBitmap = m_oAnims.getSpriteBitmap(i, bComplex);
oSprite.caption =
wxString::Format(L"#%i (%ix%i)", (int)i, pSpriteBitmap->getWidth(),
pSpriteBitmap->getHeight());
if (pSpriteBitmap->getWidth() * pSpriteBitmap->getHeight() > 0) {
wxImage imgSprite(pSpriteBitmap->getWidth(), pSpriteBitmap->getHeight(),
false);
pSpriteBitmap->blit(imgSprite, 0, 0, NULL, m_oAnims.getPalette(), 0x8000);
oSprite.bitmap = wxBitmap(imgSprite);
}
m_vSprites.push_back(oSprite);
}
m_panFrame->Refresh();
}
void frmSprites::_onPanelPaint(wxPaintEvent& evt) {
wxPaintDC dc(m_panFrame);
int iAvailableWidth, iAvailableHeight;
m_panFrame->GetClientSize(&iAvailableWidth, &iAvailableHeight);
int iX = 0;
int iTallest = 0;
int iTotal = 0;
int iY = -m_panFrame->GetVisibleRowsBegin();
for (std::vector<_sprite_t>::iterator itr = m_vSprites.begin(),
itrEnd = m_vSprites.end();
itr != itrEnd; ++itr) {
wxSize szLabel = dc.GetTextExtent(itr->caption);
int iWidth = wxMax(szLabel.GetWidth(),
itr->bitmap.IsOk() ? itr->bitmap.GetWidth() : 0);
int iHeight = (itr->bitmap.IsOk() ? itr->bitmap.GetHeight() : 0) +
szLabel.GetHeight() + 2;
if (iWidth + iX > iAvailableWidth) {
iY += iTallest;
iTotal += iTallest;
iX = iTallest = 0;
}
m_vSprites.clear();
for(size_t i = 0; i < m_oAnims.getSpriteCount(); ++i)
{
_sprite_t oSprite;
Bitmap* pSpriteBitmap = m_oAnims.getSpriteBitmap(i, bComplex);
oSprite.caption = wxString::Format(L"#%i (%ix%i)", (int)i, pSpriteBitmap->getWidth(), pSpriteBitmap->getHeight());
if(pSpriteBitmap->getWidth() * pSpriteBitmap->getHeight() > 0)
{
wxImage imgSprite(pSpriteBitmap->getWidth(), pSpriteBitmap->getHeight(), false);
pSpriteBitmap->blit(imgSprite, 0, 0, NULL, m_oAnims.getPalette(), 0x8000);
oSprite.bitmap = wxBitmap(imgSprite);
}
m_vSprites.push_back(oSprite);
if (iY + iHeight >= 0 && iY < iAvailableHeight) {
dc.DrawText(itr->caption, iX, iY);
if (itr->bitmap.IsOk())
dc.DrawBitmap(itr->bitmap, iX, iY + szLabel.GetHeight() + 1);
}
m_panFrame->Refresh();
iTallest = wxMax(iTallest, iHeight);
iX += iWidth + 2;
}
iTotal += iTallest; // Add last row too.
// Update the row count if it doesn't match.
if (iTotal != m_panFrame->iMyCount) {
m_panFrame->iMyCount = iTotal;
m_panFrame->SetRowCount(iTotal);
}
}
void frmSprites::_onPanelPaint(wxPaintEvent& evt)
{
wxPaintDC dc(m_panFrame);
int iAvailableWidth, iAvailableHeight;
m_panFrame->GetClientSize(&iAvailableWidth, &iAvailableHeight);
int iX = 0;
int iTallest = 0;
int iTotal = 0;
int iY = -m_panFrame->GetVisibleRowsBegin();
for(std::vector<_sprite_t>::iterator itr = m_vSprites.begin(), itrEnd = m_vSprites.end();
itr != itrEnd; ++itr)
{
wxSize szLabel = dc.GetTextExtent(itr->caption);
int iWidth = wxMax(szLabel.GetWidth(), itr->bitmap.IsOk() ? itr->bitmap.GetWidth() : 0);
int iHeight = (itr->bitmap.IsOk() ? itr->bitmap.GetHeight() : 0) + szLabel.GetHeight() + 2;
if(iWidth + iX > iAvailableWidth)
{
iY += iTallest;
iTotal += iTallest;
iX = iTallest = 0;
}
if (iY + iHeight >= 0 && iY < iAvailableHeight) {
dc.DrawText(itr->caption, iX, iY);
if(itr->bitmap.IsOk())
dc.DrawBitmap(itr->bitmap, iX, iY + szLabel.GetHeight() + 1);
}
iTallest = wxMax(iTallest, iHeight);
iX += iWidth + 2;
}
iTotal += iTallest; // Add last row too.
// Update the row count if it doesn't match.
if (iTotal != m_panFrame->iMyCount) {
m_panFrame->iMyCount = iTotal;
m_panFrame->SetRowCount(iTotal);
}
void frmSprites::_onBrowseTable(wxCommandEvent& WXUNUSED(evt)) {
m_txtTable->SetValue(::wxFileSelector(
L"Select location of Font00V.tab (DATA)", m_txtTable->GetValue(),
L"Font00V.tab", L"tab", L"Tab files (*.tab)|*.[tT][aA][bB]", 0, this));
}
void frmSprites::_onBrowseTable(wxCommandEvent& WXUNUSED(evt))
{
m_txtTable->SetValue(::wxFileSelector(L"Select location of Font00V.tab (DATA)",
m_txtTable->GetValue(),L"Font00V.tab",L"tab",L"Tab files (*.tab)|*.[tT][aA][bB]"
,0, this));
void frmSprites::_onBrowseData(wxCommandEvent& WXUNUSED(evt)) {
m_txtData->SetValue(::wxFileSelector(
L"Choose Theme Hospital data file", m_txtData->GetValue(), L"", L"dat",
L"Dat files (*.dat)|*.[dD][aA][tT]", 0, this));
}
void frmSprites::_onBrowseData(wxCommandEvent& WXUNUSED(evt))
{
m_txtData->SetValue(::wxFileSelector(L"Choose Theme Hospital data file",
m_txtData->GetValue(),L"",L"dat",L"Dat files (*.dat)|*.[dD][aA][tT]", 0, this));
}
void frmSprites::_onBrowsePalette(wxCommandEvent& WXUNUSED(evt))
{
m_txtPalette->SetValue(::wxFileSelector(L"Select location of MPalette.dat (QDATA)",
m_txtPalette->GetValue(),L"MPalette.dat",L"dat",L"Dat or Pal files (*.dat, *.pal)|*.[dD][aA][tT];*.[pP][aA][lL]", 0, this));
void frmSprites::_onBrowsePalette(wxCommandEvent& WXUNUSED(evt)) {
m_txtPalette->SetValue(::wxFileSelector(
L"Select location of MPalette.dat (QDATA)", m_txtPalette->GetValue(),
L"MPalette.dat", L"dat",
L"Dat or Pal files (*.dat, *.pal)|*.[dD][aA][tT];*.[pP][aA][lL]", 0,
this));
}

View File

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

File diff suppressed because it is too large Load Diff

View File

@@ -36,214 +36,210 @@ SOFTWARE.
#pragma once
#include "config.h"
#include <wx/string.h>
#include <stdint.h>
#include <wx/file.h>
#include <wx/image.h>
#include <wx/string.h>
#include <wx/txtstrm.h>
#include <array>
#include <stdint.h>
#include <vector>
#pragma pack(push)
#pragma pack(1)
struct th_anim_t
{
uint16_t frame;
uint16_t unknown;
struct th_anim_t {
uint16_t frame;
uint16_t unknown;
};
struct th_frame_t
{
uint32_t list_index;
uint8_t width;
uint8_t height;
uint16_t flags;
uint16_t next;
struct th_frame_t {
uint32_t list_index;
uint8_t width;
uint8_t height;
uint16_t flags;
uint16_t next;
};
struct th_element_t
{
uint16_t table_position;
uint8_t offx;
uint8_t offy;
uint8_t flags;
uint8_t layerid;
struct th_element_t {
uint16_t table_position;
uint8_t offx;
uint8_t offy;
uint8_t flags;
uint8_t layerid;
};
struct th_sprite_t
{
uint32_t offset;
uint8_t width;
uint8_t height;
struct th_sprite_t {
uint32_t offset;
uint8_t width;
uint8_t height;
};
struct th_colour_t
{
uint8_t r;
uint8_t g;
uint8_t b;
struct th_colour_t {
uint8_t r;
uint8_t g;
uint8_t b;
};
#pragma pack(pop)
class THLayerMask
{
public:
THLayerMask();
class THLayerMask {
public:
THLayerMask();
inline void set(int iLayer, int iID)
{
if(0 <= iLayer && iLayer < 13 && 0 <= iID && iID < 32)
m_iMask[iLayer] |= (1 << iID);
}
inline void set(int iLayer, int iID) {
if (0 <= iLayer && iLayer < 13 && 0 <= iID && iID < 32)
m_iMask[iLayer] |= (1 << iID);
}
void clear();
void clear();
inline void clear(int iLayer, int iID)
{
if(0 <= iLayer && iLayer < 13 && 0 <= iID && iID < 32)
m_iMask[iLayer] &= ~(1 << iID);
}
inline void clear(int iLayer, int iID) {
if (0 <= iLayer && iLayer < 13 && 0 <= iID && iID < 32)
m_iMask[iLayer] &= ~(1 << iID);
}
inline bool isSet(int iLayer, int iID) const
{
if(0 <= iLayer && iLayer < 13 && 0 <= iID && iID < 32)
return (m_iMask[iLayer] & (1 << iID)) != 0;
else
return false;
}
inline bool isSet(int iLayer) const
{
if(0 <= iLayer && iLayer < 13)
for(int iId = 0; iId < 32; ++iId)
{
if((m_iMask[iLayer] & (static_cast<std::uint32_t>(1) << iId)) != 0)
return true;
}
inline bool isSet(int iLayer, int iID) const {
if (0 <= iLayer && iLayer < 13 && 0 <= iID && iID < 32)
return (m_iMask[iLayer] & (1 << iID)) != 0;
else
return false;
}
inline bool isSet(int iLayer) const {
if (0 <= iLayer && iLayer < 13)
for (int iId = 0; iId < 32; ++iId) {
if ((m_iMask[iLayer] & (static_cast<std::uint32_t>(1) << iId)) != 0)
return true;
}
return false;
}
protected:
uint32_t m_iMask[13];
};
class Bitmap {
public:
Bitmap();
~Bitmap();
void create(int iWidth, int iHeight);
void create(int iWidth, int iHeight, const uint8_t* pData);
inline uint8_t pixel(int iX, int iY) const {
return m_pData[iY * m_iWidth + iX];
}
inline uint8_t& pixel(int iX, int iY) { return m_pData[iY * m_iWidth + iX]; }
int getWidth() const { return m_iWidth; }
int getHeight() const { return m_iHeight; }
void blit(Bitmap& bmpCanvas, int iX, int iY, int iFlags = 0) const;
void blit(wxImage& imgCanvas, int iX, int iY,
const unsigned char* pColourTranslate, const th_colour_t* pPalette,
int iFlags = 0) const;
bool IsOk() { return m_pData != nullptr; }
protected:
int m_iWidth;
int m_iHeight;
uint8_t* m_pData;
};
class THAnimations {
public:
THAnimations();
~THAnimations();
bool loadAnimationFile(wxString sFilename) {
return loadVector(anims, sFilename);
}
bool loadFrameFile(wxString sFilename);
bool loadListFile(wxString sFilename) {
return loadVector(elementList, sFilename);
}
bool loadElementFile(wxString sFilename) {
return loadVector(elements, sFilename);
}
bool loadTableFile(wxString sFilename);
bool loadSpriteFile(wxString sFilename) {
return loadVector(chunks, sFilename);
}
bool loadPaletteFile(wxString sFilename);
bool loadGhostFile(wxString sFilename, int iIndex);
size_t markDuplicates();
size_t getAnimationCount();
size_t getSpriteCount();
size_t getFrameCount(size_t iAnimation);
uint16_t getUnknownField(size_t iAnimation) {
return anims.at(iAnimation).unknown;
}
uint16_t getFrameField(size_t iAnimation) {
return anims.at(iAnimation).frame;
}
th_frame_t* getFrameStruct(size_t iAnimation, size_t iFrame);
bool isAnimationDuplicate(size_t iAnimation);
bool doesAnimationIncludeFrame(size_t iAnimation, size_t iFrame);
void getAnimationMask(size_t iAnimation, THLayerMask& mskLayers);
void setSpritePath(wxString aPath);
Bitmap* getSpriteBitmap(size_t iSprite, bool bComplex = false);
th_colour_t* getPalette() { return colours.data(); }
void setGhost(int iFile, int iIndex);
void drawFrame(wxImage& imgCanvas, size_t iAnimation, size_t iFrame,
const THLayerMask* pMask, wxSize& size, int iXOffset = 0,
int iYOffset = 0);
void copySpriteToCanvas(wxString spriteFile, int iSpriteIndex,
wxImage& imgCanvas, int iX, int iY, int iFlags = 0);
static unsigned char* Decompress(unsigned char* pData, size_t& iLength);
protected:
template <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;
}
}
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;
}
}
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;
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
SOFTWARE.
*/
#include "config.h"
#include <array>
#include <cstring>
#include "lua.hpp"
#include "th_lua.h"
#include "config.h"
#include <cstring>
#include <array>
/* Often, an error occurs during the CorsixTH startup process. Examples of
such errors include:
@@ -42,281 +42,281 @@ homemade bitmap font, as we cannot rely on TH fonts being present).
namespace {
// clang-format off
constexpr int first_bootstrap_code_line_number = __LINE__ + 2;
constexpr std::array<const char*, 45> bootstrap_code {{
"local lines, dat, tab, pal, err = {}, ...",
"local function t(s) return s:gsub('\\t', ' ') end",
"for s in tostring(err):gmatch'[^\\r\\n]+' do lines[#lines+1] = t(s) end",
"local TH, SDL, rnc = require'TH', require'sdl', require'rnc'.decompress",
"if not SDL.init('video') then error'Unable to initialise video' end",
"local w, h = 640, 480",
"local function dernc(x) return x:match'^RNC' and assert(rnc(x)) or x end",
"local video = TheApp and TheApp.video or TH.surface(w, h)",
"video:setCaption('CorsixTH - Error during startup')",
"local palette, sheet, font = TH.palette(), TH.sheet(), TH.bitmap_font()",
"if not palette:load(dernc(pal)) then error'Unable to load palette' end",
"sheet:setPalette(palette)",
"if not sheet:load(dernc(tab), dernc(dat), true, video) then error'Unable to load sheet' end",
"font:setSheet(sheet):setSeparation(1, 0)",
"local bx, by, bw, bh = 20, 0, 100, 16", // Print message and draw button:
"local function draw()",
" video:startFrame()",
" video:fillBlack()",
" local y = 20",
" for _, s in ipairs(lines) do",
" y = font:drawWrapped(video, s, 20, y, w - 40)",
" end",
" by = y + 20",
" video:drawRect(video:mapRGB(151, 23, 23), bx, by, bw, bh)",
" video:drawRect(video:mapRGB(171, 53, 53), bx + 1, by + 1, bw - 2, bh - 2)",
" font:draw(video, 'Exit', bx, by + 1, bw, bh)",
" video:endFrame()",
"end",
"SDL.wm.showCursor(true)",
"draw()",
"local running = true", // Minimal event handler:
"repeat",
" local e, where = SDL.mainloop(coroutine.create(function()",
" while running do",
" local e, _, x, y = coroutine.yield(true)",
" if e == 'frame' then draw()",
" elseif e == 'buttonup' then",
" x, y = x - bx, y - by",
" running = x < 0 or bw <= x or y < 0 or bh <= y",
" end",
" end",
" end))",
" if running then print(e) end",
"until where ~= 'callback'",
nullptr
}};
constexpr std::array<const char*, 45> bootstrap_code{
{"local lines, dat, tab, pal, err = {}, ...",
"local function t(s) return s:gsub('\\t', ' ') end",
"for s in tostring(err):gmatch'[^\\r\\n]+' do lines[#lines+1] = t(s) end",
"local TH, SDL, rnc = require'TH', require'sdl', require'rnc'.decompress",
"if not SDL.init('video') then error'Unable to initialise video' end",
"local w, h = 640, 480",
"local function dernc(x) return x:match'^RNC' and assert(rnc(x)) or x end",
"local video = TheApp and TheApp.video or TH.surface(w, h)",
"video:setCaption('CorsixTH - Error during startup')",
"local palette, sheet, font = TH.palette(), TH.sheet(), TH.bitmap_font()",
"if not palette:load(dernc(pal)) then error'Unable to load palette' end",
"sheet:setPalette(palette)",
"if not sheet:load(dernc(tab), dernc(dat), true, video) then error'Unable to load sheet' end",
"font:setSheet(sheet):setSeparation(1, 0)",
"local bx, by, bw, bh = 20, 0, 100, 16", // Print message and draw button:
"local function draw()",
" video:startFrame()",
" video:fillBlack()",
" local y = 20",
" for _, s in ipairs(lines) do",
" y = font:drawWrapped(video, s, 20, y, w - 40)",
" end",
" by = y + 20",
" video:drawRect(video:mapRGB(151, 23, 23), bx, by, bw, bh)",
" video:drawRect(video:mapRGB(171, 53, 53), bx + 1, by + 1, bw - 2, bh - 2)",
" font:draw(video, 'Exit', bx, by + 1, bw, bh)",
" video:endFrame()",
"end",
"SDL.wm.showCursor(true)",
"draw()",
"local running = true", // Minimal event handler:
"repeat",
" local e, where = SDL.mainloop(coroutine.create(function()",
" while running do",
" local e, _, x, y = coroutine.yield(true)",
" if e == 'frame' then draw()",
" elseif e == 'buttonup' then",
" x, y = x - bx, y - by",
" running = x < 0 or bw <= x or y < 0 or bh <= y",
" end",
" end",
" end))",
" if running then print(e) end",
"until where ~= 'callback'",
nullptr}};
// clang-format on
/* Start autogenerated content */
/* Data from bootstrap_font.tab inserted by mkbootstrap.lua: */
constexpr std::array<uint8_t, 499> bootstrap_font_tab {
0x52, 0x4E, 0x43, 0x01, 0x00, 0x00, 0x05, 0x46, 0x00, 0x00, 0x01, 0xE1, 0xFB,
0xF2, 0x66, 0x51, 0xBE, 0xEF, 0x0C, 0x09, 0x59, 0x60, 0x10, 0x34, 0x43, 0x54,
0xA6, 0x46, 0xA2, 0x08, 0x00, 0xA0, 0xC4, 0x01, 0xF6, 0x3D, 0x00, 0x00, 0xAF,
0xAE, 0x05, 0x01, 0x03, 0x01, 0x0C, 0x0B, 0xAE, 0x9E, 0x03, 0x0C, 0x16, 0x08,
0x0C, 0x47, 0x63, 0xEA, 0x0C, 0x74, 0x07, 0x0C, 0x9A, 0xEA, 0xEA, 0x06, 0x0C,
0xC8, 0x02, 0x0C, 0xD1, 0x1D, 0x92, 0xEE, 0x4B, 0x43, 0x09, 0x01, 0x3B, 0xD5,
0x34, 0x4F, 0xAA, 0xF4, 0xCC, 0xA4, 0x56, 0x60, 0xDA, 0x66, 0x54, 0x1D, 0x66,
0xA3, 0x3E, 0x7C, 0x04, 0x0C, 0x9B, 0xD3, 0x4C, 0xB5, 0xD5, 0x52, 0x93, 0xF2,
0x13, 0x24, 0x12, 0x02, 0x31, 0x52, 0x49, 0x92, 0x71, 0x97, 0x54, 0x7B, 0xB9,
0x8C, 0x6A, 0xC3, 0xB3, 0x51, 0xCD, 0xB5, 0x4E, 0xE5, 0x4E, 0x33, 0xF6, 0x1D,
0xAD, 0x0F, 0x34, 0xAA, 0x29, 0xDB, 0x8C, 0x4E, 0xAA, 0xBB, 0x12, 0x9A, 0x91,
0x49, 0x4A, 0xB1, 0xD7, 0x48, 0x2E, 0xF5, 0x10, 0x04, 0x01, 0x49, 0x34, 0x58,
0x92, 0x54, 0x76, 0x97, 0xE9, 0x91, 0xB9, 0x12, 0x4E, 0xDB, 0x03, 0x05, 0x50,
0x6D, 0x2B, 0x36, 0xAA, 0x50, 0xDC, 0xA8, 0x6E, 0x2E, 0x3D, 0x9C, 0x33, 0xA5,
0xBD, 0x25, 0xC9, 0xDD, 0xF8, 0x09, 0x92, 0x1F, 0x06, 0x45, 0x6D, 0xAA, 0x69,
0x66, 0x92, 0x97, 0xB7, 0x54, 0xB7, 0xDB, 0x1F, 0x39, 0xF8, 0x12, 0x07, 0x41,
0x95, 0x2F, 0x1B, 0x49, 0x41, 0x4B, 0x75, 0xDD, 0x99, 0x49, 0x55, 0x70, 0x75,
0xBF, 0x92, 0x24, 0xA7, 0xC4, 0x49, 0x92, 0xD9, 0xF5, 0x6B, 0x82, 0x14, 0x08,
0x38, 0xEA, 0x65, 0x8C, 0x6A, 0x43, 0x9B, 0x51, 0x5D, 0xE5, 0x46, 0x7F, 0xB5,
0xC7, 0xA4, 0x95, 0x91, 0xAE, 0xD0, 0x4C, 0xC6, 0xE2, 0x33, 0xC9, 0xFF, 0x09,
0x92, 0x1F, 0x09, 0x34, 0x47, 0x24, 0xA9, 0x65, 0x7D, 0x9A, 0x46, 0x99, 0x92,
0x72, 0xB6, 0xD4, 0x9A, 0xB9, 0xF4, 0x10, 0x0A, 0x19, 0x54, 0x32, 0x1B, 0xCF,
0x3E, 0x4C, 0xAA, 0x5E, 0x97, 0x31, 0x6D, 0xAA, 0xF4, 0x48, 0x39, 0x87, 0xB0,
0x49, 0x09, 0xCF, 0xE9, 0xFB, 0x99, 0x90, 0x46, 0x2C, 0x92, 0x54, 0x4B, 0x6D,
0xE9, 0x91, 0x8A, 0x24, 0x49, 0xA6, 0xC0, 0xD9, 0x75, 0xC5, 0x91, 0xBD, 0xF0,
0x9C, 0x75, 0x45, 0xA8, 0x19, 0xFA, 0x46, 0x3A, 0x52, 0x75, 0x5A, 0x8C, 0x2A,
0x75, 0x3B, 0xAA, 0x95, 0xBD, 0x27, 0xBF, 0x27, 0x39, 0xE3, 0x04, 0x0D, 0x41,
0xD5, 0x24, 0x31, 0x92, 0x42, 0x5E, 0xA4, 0x6A, 0x85, 0x9A, 0xB4, 0xAA, 0x12,
0x9A, 0xD0, 0xF3, 0x39, 0x41, 0x15, 0x0E, 0x37, 0x92, 0x24, 0x5F, 0x84, 0xA5,
0xD5, 0xDE, 0x33, 0x93, 0xB6, 0xD9, 0xAA, 0x6F, 0xE4, 0x2E, 0xF6, 0x14, 0x0F,
0x61, 0x92, 0x52, 0xA4, 0xCA, 0x6C, 0xCD, 0x4C, 0x86, 0x94, 0x92, 0xEA, 0xA5,
0x7F, 0x39, 0xD9, 0x72, 0xAF, 0x11, 0x10, 0x04, 0xAA, 0x1C, 0xED, 0x47, 0x3D,
0xF6, 0x97, 0x3D, 0xF4, 0x7D, 0x0A, 0x5E, 0xAF, 0x44, 0x80, 0xAA, 0x57, 0x35,
0xAA, 0xA1, 0xF7, 0x34, 0xBA, 0x92, 0xA4, 0xD6, 0xF7, 0x7A, 0xF7, 0x5B, 0x1A,
0x18, 0x11, 0x35, 0xAA, 0xC4, 0x48, 0xAA, 0x4C, 0x6E, 0xD6, 0x91, 0x93, 0x12,
0x52, 0xBA, 0xE0, 0x35, 0x8D, 0xFF, 0x5C, 0x1F, 0x27, 0x12, 0x54, 0xE9, 0x44,
0x51, 0xA5, 0x67, 0x47, 0x92, 0x7F, 0x99, 0x64, 0x37, 0xBA, 0x38, 0x49, 0xE2,
0xA8, 0xCF, 0xF2, 0x75, 0x53, 0x0D, 0x13, 0xFA, 0x24, 0x1E, 0x51, 0xB9, 0xC9,
0x29, 0x40, 0x52, 0x7D, 0x0F
};
constexpr std::array<uint8_t, 499> bootstrap_font_tab{
0x52, 0x4E, 0x43, 0x01, 0x00, 0x00, 0x05, 0x46, 0x00, 0x00, 0x01, 0xE1,
0xFB, 0xF2, 0x66, 0x51, 0xBE, 0xEF, 0x0C, 0x09, 0x59, 0x60, 0x10, 0x34,
0x43, 0x54, 0xA6, 0x46, 0xA2, 0x08, 0x00, 0xA0, 0xC4, 0x01, 0xF6, 0x3D,
0x00, 0x00, 0xAF, 0xAE, 0x05, 0x01, 0x03, 0x01, 0x0C, 0x0B, 0xAE, 0x9E,
0x03, 0x0C, 0x16, 0x08, 0x0C, 0x47, 0x63, 0xEA, 0x0C, 0x74, 0x07, 0x0C,
0x9A, 0xEA, 0xEA, 0x06, 0x0C, 0xC8, 0x02, 0x0C, 0xD1, 0x1D, 0x92, 0xEE,
0x4B, 0x43, 0x09, 0x01, 0x3B, 0xD5, 0x34, 0x4F, 0xAA, 0xF4, 0xCC, 0xA4,
0x56, 0x60, 0xDA, 0x66, 0x54, 0x1D, 0x66, 0xA3, 0x3E, 0x7C, 0x04, 0x0C,
0x9B, 0xD3, 0x4C, 0xB5, 0xD5, 0x52, 0x93, 0xF2, 0x13, 0x24, 0x12, 0x02,
0x31, 0x52, 0x49, 0x92, 0x71, 0x97, 0x54, 0x7B, 0xB9, 0x8C, 0x6A, 0xC3,
0xB3, 0x51, 0xCD, 0xB5, 0x4E, 0xE5, 0x4E, 0x33, 0xF6, 0x1D, 0xAD, 0x0F,
0x34, 0xAA, 0x29, 0xDB, 0x8C, 0x4E, 0xAA, 0xBB, 0x12, 0x9A, 0x91, 0x49,
0x4A, 0xB1, 0xD7, 0x48, 0x2E, 0xF5, 0x10, 0x04, 0x01, 0x49, 0x34, 0x58,
0x92, 0x54, 0x76, 0x97, 0xE9, 0x91, 0xB9, 0x12, 0x4E, 0xDB, 0x03, 0x05,
0x50, 0x6D, 0x2B, 0x36, 0xAA, 0x50, 0xDC, 0xA8, 0x6E, 0x2E, 0x3D, 0x9C,
0x33, 0xA5, 0xBD, 0x25, 0xC9, 0xDD, 0xF8, 0x09, 0x92, 0x1F, 0x06, 0x45,
0x6D, 0xAA, 0x69, 0x66, 0x92, 0x97, 0xB7, 0x54, 0xB7, 0xDB, 0x1F, 0x39,
0xF8, 0x12, 0x07, 0x41, 0x95, 0x2F, 0x1B, 0x49, 0x41, 0x4B, 0x75, 0xDD,
0x99, 0x49, 0x55, 0x70, 0x75, 0xBF, 0x92, 0x24, 0xA7, 0xC4, 0x49, 0x92,
0xD9, 0xF5, 0x6B, 0x82, 0x14, 0x08, 0x38, 0xEA, 0x65, 0x8C, 0x6A, 0x43,
0x9B, 0x51, 0x5D, 0xE5, 0x46, 0x7F, 0xB5, 0xC7, 0xA4, 0x95, 0x91, 0xAE,
0xD0, 0x4C, 0xC6, 0xE2, 0x33, 0xC9, 0xFF, 0x09, 0x92, 0x1F, 0x09, 0x34,
0x47, 0x24, 0xA9, 0x65, 0x7D, 0x9A, 0x46, 0x99, 0x92, 0x72, 0xB6, 0xD4,
0x9A, 0xB9, 0xF4, 0x10, 0x0A, 0x19, 0x54, 0x32, 0x1B, 0xCF, 0x3E, 0x4C,
0xAA, 0x5E, 0x97, 0x31, 0x6D, 0xAA, 0xF4, 0x48, 0x39, 0x87, 0xB0, 0x49,
0x09, 0xCF, 0xE9, 0xFB, 0x99, 0x90, 0x46, 0x2C, 0x92, 0x54, 0x4B, 0x6D,
0xE9, 0x91, 0x8A, 0x24, 0x49, 0xA6, 0xC0, 0xD9, 0x75, 0xC5, 0x91, 0xBD,
0xF0, 0x9C, 0x75, 0x45, 0xA8, 0x19, 0xFA, 0x46, 0x3A, 0x52, 0x75, 0x5A,
0x8C, 0x2A, 0x75, 0x3B, 0xAA, 0x95, 0xBD, 0x27, 0xBF, 0x27, 0x39, 0xE3,
0x04, 0x0D, 0x41, 0xD5, 0x24, 0x31, 0x92, 0x42, 0x5E, 0xA4, 0x6A, 0x85,
0x9A, 0xB4, 0xAA, 0x12, 0x9A, 0xD0, 0xF3, 0x39, 0x41, 0x15, 0x0E, 0x37,
0x92, 0x24, 0x5F, 0x84, 0xA5, 0xD5, 0xDE, 0x33, 0x93, 0xB6, 0xD9, 0xAA,
0x6F, 0xE4, 0x2E, 0xF6, 0x14, 0x0F, 0x61, 0x92, 0x52, 0xA4, 0xCA, 0x6C,
0xCD, 0x4C, 0x86, 0x94, 0x92, 0xEA, 0xA5, 0x7F, 0x39, 0xD9, 0x72, 0xAF,
0x11, 0x10, 0x04, 0xAA, 0x1C, 0xED, 0x47, 0x3D, 0xF6, 0x97, 0x3D, 0xF4,
0x7D, 0x0A, 0x5E, 0xAF, 0x44, 0x80, 0xAA, 0x57, 0x35, 0xAA, 0xA1, 0xF7,
0x34, 0xBA, 0x92, 0xA4, 0xD6, 0xF7, 0x7A, 0xF7, 0x5B, 0x1A, 0x18, 0x11,
0x35, 0xAA, 0xC4, 0x48, 0xAA, 0x4C, 0x6E, 0xD6, 0x91, 0x93, 0x12, 0x52,
0xBA, 0xE0, 0x35, 0x8D, 0xFF, 0x5C, 0x1F, 0x27, 0x12, 0x54, 0xE9, 0x44,
0x51, 0xA5, 0x67, 0x47, 0x92, 0x7F, 0x99, 0x64, 0x37, 0xBA, 0x38, 0x49,
0xE2, 0xA8, 0xCF, 0xF2, 0x75, 0x53, 0x0D, 0x13, 0xFA, 0x24, 0x1E, 0x51,
0xB9, 0xC9, 0x29, 0x40, 0x52, 0x7D, 0x0F};
/* Data from bootstrap_font.dat inserted by mkbootstrap.lua: */
constexpr std::array<uint8_t, 1534> bootstrap_font_dat {
0x52, 0x4E, 0x43, 0x01, 0x00, 0x00, 0x13, 0x68, 0x00, 0x00, 0x05, 0xEC, 0xD3,
0x6C, 0x7E, 0xAB, 0xBE, 0xEF, 0x94, 0x90, 0x81, 0x61, 0x50, 0x34, 0x44, 0x33,
0x33, 0x53, 0x66, 0x64, 0x64, 0x26, 0x20, 0x60, 0x47, 0x1E, 0x01, 0xFF, 0x84,
0x41, 0x01, 0x02, 0xFF, 0x01, 0x84, 0x09, 0xFF, 0xFF, 0x14, 0x2F, 0x06, 0xCD,
0x9B, 0xCC, 0x8C, 0x89, 0x04, 0x1B, 0x42, 0x06, 0x65, 0x64, 0x71, 0x3B, 0x44,
0x01, 0x06, 0x78, 0xE0, 0xE9, 0xD9, 0x9A, 0x2B, 0x36, 0x72, 0xB0, 0x43, 0x84,
0x59, 0x1B, 0x4D, 0x0A, 0xB6, 0x75, 0x87, 0x91, 0x32, 0xAB, 0x86, 0x02, 0xCC,
0xDE, 0x84, 0x07, 0x82, 0x31, 0x85, 0xCC, 0x45, 0x90, 0x4A, 0x84, 0xE7, 0xC4,
0x9C, 0x42, 0xFC, 0x29, 0x66, 0x0C, 0xA4, 0x94, 0x9D, 0xD9, 0x38, 0x75, 0x35,
0x42, 0x98, 0x07, 0x6D, 0x70, 0x91, 0x1B, 0x03, 0x8E, 0x54, 0x40, 0x9B, 0xED,
0x89, 0x19, 0x44, 0x87, 0x02, 0x9D, 0x8B, 0x5C, 0x3A, 0x15, 0x19, 0xF8, 0xB7,
0xAC, 0x43, 0x01, 0x15, 0x93, 0x0B, 0x5D, 0x03, 0x83, 0x40, 0x1A, 0x56, 0xA5,
0x1D, 0x03, 0xF2, 0x93, 0xA2, 0x50, 0x03, 0xB6, 0x11, 0x1C, 0xF2, 0x8E, 0x8E,
0x60, 0x87, 0x42, 0x89, 0x93, 0x40, 0x93, 0x64, 0xA2, 0x81, 0x04, 0x29, 0x23,
0x11, 0x4F, 0x36, 0xA0, 0xBA, 0x8E, 0xA2, 0xA4, 0x1A, 0x08, 0x75, 0x65, 0x6E,
0x9C, 0xAC, 0x14, 0xC3, 0x18, 0x67, 0x33, 0x16, 0x4E, 0x10, 0x31, 0x8C, 0x11,
0x9B, 0x17, 0x7C, 0xCC, 0x44, 0x57, 0x8F, 0xF7, 0x74, 0x0E, 0x3F, 0x71, 0x80,
0x64, 0x08, 0x39, 0x90, 0xBC, 0x28, 0x87, 0x3A, 0x15, 0x8B, 0x70, 0x30, 0x9D,
0x04, 0x14, 0x0B, 0x89, 0x1C, 0x95, 0x21, 0x8F, 0x50, 0xB5, 0x2D, 0xD9, 0x85,
0x1B, 0x80, 0x72, 0xEC, 0x0A, 0xCE, 0x6B, 0xA8, 0xDB, 0x86, 0x05, 0x35, 0x93,
0x84, 0x12, 0x8B, 0xE6, 0xEC, 0x84, 0xBF, 0x79, 0x5F, 0xDA, 0xA4, 0xC8, 0x97,
0xF8, 0x84, 0x42, 0x9B, 0x19, 0xB4, 0x96, 0xCB, 0xD4, 0x85, 0x21, 0x84, 0x08,
0xB3, 0x5B, 0x1F, 0x88, 0x60, 0xCE, 0x85, 0x0F, 0x56, 0x92, 0x92, 0xA4, 0x05,
0xA6, 0x43, 0x46, 0x9E, 0x94, 0x08, 0x8B, 0x58, 0x17, 0x3B, 0x87, 0xC9, 0xAA,
0x86, 0x48, 0x83, 0x23, 0x12, 0x83, 0x52, 0x90, 0x89, 0x38, 0x4A, 0x50, 0x89,
0x52, 0x32, 0x98, 0x45, 0xB9, 0x84, 0x48, 0x6D, 0x98, 0x17, 0x9E, 0xA2, 0x37,
0x66, 0xC2, 0xA2, 0xB4, 0x5A, 0x2C, 0x54, 0xE5, 0xC5, 0x15, 0x74, 0x44, 0x67,
0x06, 0x50, 0x6E, 0xE1, 0x06, 0x2F, 0x2D, 0xC8, 0x99, 0x0D, 0xB6, 0x4D, 0x79,
0x24, 0x0E, 0x07, 0x1D, 0xE8, 0x91, 0x94, 0x0F, 0xDE, 0x62, 0x3F, 0x60, 0x0F,
0x27, 0x63, 0xE5, 0x40, 0x78, 0x85, 0xB8, 0x15, 0x09, 0xCA, 0x22, 0x2B, 0x19,
0x09, 0x53, 0xF9, 0x84, 0x69, 0x6D, 0x32, 0x6B, 0xAE, 0x45, 0xCA, 0x90, 0xE6,
0x12, 0x6A, 0xB4, 0xA2, 0xD8, 0x4C, 0xFA, 0xA1, 0x5D, 0xEF, 0xC2, 0xE4, 0x02,
0x00, 0xF9, 0x5B, 0x05, 0x13, 0x6B, 0xC2, 0xB9, 0xAA, 0x17, 0xD8, 0xBA, 0xB1,
0xF9, 0xA0, 0x05, 0x4C, 0x2F, 0x9A, 0x61, 0xBA, 0x52, 0x94, 0x63, 0xB9, 0x16,
0x9D, 0xA3, 0x65, 0x84, 0x3D, 0x4A, 0x64, 0x9C, 0x96, 0x20, 0xF6, 0x30, 0x85,
0x68, 0x5A, 0x50, 0x95, 0xE5, 0x30, 0xFD, 0x90, 0x1C, 0xE2, 0xE6, 0x43, 0x0B,
0x6A, 0x9F, 0x23, 0x12, 0x27, 0xC2, 0x81, 0x9D, 0x55, 0x85, 0xC7, 0x01, 0xA5,
0x58, 0x01, 0xC7, 0xD3, 0xF3, 0xFD, 0x87, 0x0B, 0x58, 0x7C, 0x52, 0xCA, 0x6C,
0x47, 0xF1, 0x39, 0x19, 0x8A, 0x9A, 0xF7, 0x21, 0x0A, 0x67, 0x06, 0xBE, 0xD3,
0x88, 0xCF, 0x47, 0x34, 0x94, 0x09, 0x04, 0x40, 0x64, 0x09, 0xE6, 0x4F, 0xEA,
0xA5, 0x55, 0x4E, 0xA5, 0x95, 0xE5, 0x50, 0x0D, 0x06, 0x46, 0x61, 0x85, 0x23,
0x08, 0xAB, 0xF9, 0x48, 0x09, 0x33, 0x44, 0x50, 0x07, 0x21, 0x3C, 0xA8, 0x61,
0x9D, 0x34, 0x84, 0x22, 0x5C, 0x44, 0xFD, 0xA0, 0x6D, 0xE2, 0x6E, 0x56, 0x3A,
0xA3, 0x19, 0x62, 0x4A, 0x82, 0x0C, 0x98, 0x25, 0xCF, 0x24, 0xC8, 0x35, 0x35,
0x1A, 0x12, 0x59, 0x5D, 0x98, 0xD6, 0x2E, 0x76, 0x7F, 0x2B, 0xF4, 0x30, 0x24,
0xAF, 0x4C, 0x5C, 0x28, 0x2E, 0x28, 0xAA, 0x24, 0xE5, 0x17, 0xFF, 0x55, 0x42,
0xC9, 0x54, 0x1B, 0x75, 0xDD, 0xBE, 0x47, 0x9B, 0x11, 0x22, 0x0E, 0xCC, 0xAA,
0x88, 0x88, 0x66, 0x64, 0xCA, 0xD0, 0x88, 0x08, 0x41, 0x06, 0xF0, 0x17, 0x45,
0x82, 0x07, 0xAE, 0x80, 0x9F, 0xB2, 0x23, 0x23, 0xAB, 0x8C, 0xE9, 0xFA, 0xC3,
0x53, 0xD0, 0x06, 0x88, 0x67, 0x59, 0xB3, 0x92, 0xD2, 0xE9, 0xA8, 0x82, 0x0A,
0xA7, 0xC9, 0x44, 0x8E, 0x47, 0x15, 0x82, 0x37, 0x80, 0x9B, 0x88, 0xF9, 0x08,
0x27, 0xDC, 0x05, 0xC2, 0xD3, 0x94, 0x59, 0x55, 0x41, 0x7F, 0x91, 0xFF, 0xDB,
0x16, 0x8C, 0x0C, 0x27, 0x76, 0x6E, 0x26, 0x81, 0xAA, 0xA1, 0x8F, 0xC1, 0x20,
0x63, 0x0B, 0xC3, 0x89, 0x49, 0x31, 0x42, 0x93, 0x8C, 0x69, 0x07, 0x7B, 0x3F,
0x9E, 0x0E, 0x45, 0x56, 0x85, 0x91, 0xE9, 0x78, 0xCC, 0xF9, 0x08, 0xA4, 0xD5,
0x92, 0x70, 0x89, 0x16, 0xC3, 0x0C, 0x92, 0x18, 0xA7, 0x59, 0xF2, 0x08, 0xCD,
0xE0, 0xF2, 0xA8, 0x97, 0x4C, 0xF3, 0x83, 0xA7, 0x1C, 0x68, 0x54, 0x17, 0x03,
0x8A, 0x20, 0x57, 0x50, 0x58, 0x31, 0x11, 0x72, 0x87, 0x71, 0x25, 0x2E, 0xE7,
0xE4, 0x79, 0x90, 0x10, 0x50, 0x1D, 0x54, 0x88, 0xE1, 0xB7, 0x8F, 0x18, 0x02,
0x77, 0xA8, 0x0C, 0x64, 0x11, 0x8C, 0xC9, 0x21, 0xE6, 0x07, 0xCB, 0x5E, 0x8F,
0xB2, 0x25, 0x0B, 0x7B, 0x0A, 0x25, 0x8B, 0x95, 0xD0, 0x82, 0xF4, 0xE2, 0x34,
0x20, 0xEA, 0x5F, 0x36, 0x8C, 0xA1, 0x94, 0x50, 0xFD, 0x69, 0x1B, 0xCB, 0xB0,
0x8B, 0x4F, 0xBF, 0xB5, 0xC7, 0x0B, 0xC2, 0xC5, 0x17, 0xEE, 0x8C, 0x91, 0xFB,
0xD3, 0x0D, 0xD1, 0xE1, 0x98, 0x3A, 0xC9, 0x4D, 0x08, 0xF4, 0xB8, 0x59, 0x11,
0x78, 0x48, 0x20, 0x8B, 0x25, 0x08, 0xBF, 0xF2, 0x89, 0x59, 0xDB, 0x31, 0xD6,
0x17, 0x9E, 0xCA, 0x74, 0x20, 0x8E, 0x0E, 0xD5, 0x07, 0x3B, 0x5D, 0x51, 0x8E,
0x86, 0x71, 0xCE, 0xE6, 0x2C, 0x54, 0xB2, 0xC6, 0x8B, 0x02, 0x7D, 0x7F, 0xC2,
0x61, 0xC2, 0x0B, 0xF0, 0x85, 0x84, 0xCC, 0x98, 0x11, 0x4A, 0x8D, 0x66, 0x03,
0xF8, 0x54, 0x57, 0xB0, 0xF0, 0x45, 0x8C, 0xFF, 0xD3, 0x61, 0xA8, 0x82, 0x09,
0xFF, 0x00, 0xCA, 0x0B, 0x8C, 0x9B, 0x75, 0x24, 0x09, 0x07, 0x00, 0xF9, 0xAC,
0xC1, 0xBB, 0xB7, 0x9E, 0xE8, 0x41, 0xE4, 0x2B, 0xCE, 0xAB, 0x2D, 0x9D, 0xEA,
0xFD, 0x41, 0x00, 0x42, 0x8F, 0xC9, 0xF4, 0x87, 0x54, 0x61, 0x2D, 0x42, 0x51,
0x3F, 0x52, 0x29, 0xCB, 0x87, 0x0F, 0x1F, 0xE4, 0x11, 0x33, 0x8E, 0xE5, 0xCD,
0x86, 0x6F, 0x96, 0x30, 0x40, 0x06, 0xF5, 0x7F, 0x42, 0xB2, 0x61, 0x93, 0x55,
0x87, 0xFE, 0x86, 0xE3, 0x67, 0x49, 0xA2, 0xC3, 0x1F, 0x6F, 0xEF, 0x76, 0x4E,
0xE5, 0x12, 0x89, 0x3E, 0x10, 0xFF, 0x90, 0x99, 0xBF, 0x40, 0x45, 0xAA, 0xFC,
0x10, 0xDC, 0xC1, 0xF1, 0x44, 0x13, 0x50, 0x58, 0x29, 0x61, 0x28, 0x95, 0x70,
0x05, 0x7E, 0x21, 0x8D, 0xE3, 0x83, 0xB2, 0x8F, 0xB2, 0xA2, 0x10, 0xE0, 0x93,
0xAC, 0xFD, 0x88, 0x38, 0x38, 0xC4, 0x46, 0x23, 0x4A, 0x41, 0xF8, 0x0F, 0xA8,
0x12, 0xD5, 0x88, 0x7F, 0x92, 0xA2, 0x21, 0xBE, 0xF1, 0x0A, 0x11, 0x92, 0x11,
0x99, 0xAB, 0xEA, 0xC3, 0x85, 0x22, 0x1B, 0xA2, 0xDF, 0xE9, 0x7E, 0x18, 0x95,
0xB6, 0x48, 0xD1, 0x9F, 0x50, 0xCF, 0x11, 0xBE, 0xF9, 0xB9, 0x1E, 0xC7, 0x9B,
0x00, 0xE3, 0x62, 0xAA, 0x98, 0x0E, 0x01, 0x87, 0xC0, 0x27, 0x59, 0x53, 0x83,
0x47, 0xBA, 0x96, 0xEA, 0xFF, 0x88, 0x68, 0xEA, 0x08, 0xD3, 0x58, 0xCE, 0x0F,
0x05, 0x8A, 0xDA, 0x27, 0x6E, 0x15, 0x18, 0xF2, 0xAF, 0x38, 0xEA, 0x54, 0xA0,
0x89, 0x74, 0xA1, 0x4B, 0x16, 0x9A, 0x14, 0x4D, 0x06, 0x85, 0x1D, 0xEC, 0xEF,
0x52, 0x54, 0x93, 0x2C, 0x09, 0xA3, 0xE9, 0xC6, 0x1C, 0x75, 0xAD, 0xD8, 0x14,
0x2E, 0x31, 0x58, 0x15, 0xCF, 0xB8, 0x44, 0x04, 0xAC, 0x1C, 0xF0, 0x22, 0xA9,
0x3B, 0x99, 0xD9, 0x44, 0x9D, 0x8E, 0xF8, 0x05, 0xE1, 0x04, 0x7B, 0x0B, 0x32,
0x13, 0x85, 0x22, 0xDE, 0x87, 0xAC, 0x27, 0xAB, 0x2A, 0x66, 0x75, 0xA1, 0x9B,
0xC0, 0x51, 0xE1, 0x59, 0x43, 0x5B, 0xC2, 0x37, 0xDF, 0x00, 0xD5, 0x98, 0x53,
0xE3, 0x95, 0xD0, 0xD2, 0x88, 0xF6, 0x8F, 0xFD, 0x28, 0x5D, 0xFE, 0x50, 0x34,
0xF4, 0x85, 0xEA, 0xA1, 0xC9, 0x0F, 0xA6, 0x72, 0xA8, 0x57, 0xF8, 0xEA, 0x50,
0x96, 0xC8, 0x84, 0x1E, 0xF8, 0x1F, 0x2C, 0xEB, 0xA9, 0xA3, 0x64, 0xE1, 0xAC,
0xE4, 0x2B, 0x50, 0xA4, 0x66, 0x54, 0x87, 0x63, 0xA2, 0x87, 0x00, 0x04, 0x29,
0x2F, 0x1E, 0x89, 0x51, 0xC1, 0xC0, 0xD0, 0xA4, 0xC7, 0x14, 0x52, 0xBA, 0x58,
0xF1, 0xC4, 0x41, 0x40, 0x57, 0xD5, 0xF2, 0xA6, 0x80, 0xEC, 0xB0, 0x30, 0xB1,
0x71, 0xDB, 0x45, 0xA4, 0x86, 0xAC, 0xA7, 0x98, 0xD4, 0xB3, 0x26, 0x42, 0xF1,
0x24, 0x31, 0x0C, 0x54, 0x4F, 0x68, 0x03, 0xB3, 0x0E, 0xD8, 0xB7, 0xE5, 0x8D,
0x1C, 0x8B, 0xD5, 0x08, 0x2D, 0x9E, 0xD0, 0x9C, 0x06, 0x62, 0xA1, 0xD5, 0x41,
0x3B, 0xA0, 0x5C, 0x7E, 0x92, 0x75, 0x84, 0x57, 0x38, 0x41, 0xA0, 0x25, 0xB3,
0xA6, 0xA3, 0x89, 0x33, 0xC2, 0xD1, 0x60, 0x72, 0x57, 0xB1, 0xC2, 0xC0, 0x2A,
0x79, 0xB0, 0xC2, 0x6C, 0x13, 0x01, 0x51, 0x21, 0xCB, 0x13, 0xA3, 0x32, 0x3E,
0x16, 0x49, 0xEF, 0x6C, 0xF1, 0x30, 0x90, 0x05, 0x5D, 0x85, 0x27, 0x1B, 0xAA,
0xC0, 0x74, 0x7A, 0x5C, 0x37, 0x01, 0x27, 0x05, 0xEA, 0x3C, 0x4A, 0x4B, 0x9F,
0x0D, 0xC8, 0x18, 0xA2, 0xD4, 0x20, 0x59, 0xCB, 0x19, 0x7D, 0x9C, 0x5F, 0x6C,
0x04, 0xCA, 0x28, 0x6E, 0x6B, 0xAC, 0x7B, 0x0C, 0xCE, 0xE5, 0x9E, 0x49, 0x84,
0x18, 0xDD, 0x1D, 0xAA, 0x39, 0x29, 0x9F, 0x30, 0xFA, 0x22, 0x67, 0x7C, 0x93,
0x92, 0x09, 0x13, 0x52, 0x2F, 0x1E, 0x4E, 0x6E, 0x61, 0x05, 0x18, 0x85, 0x07,
0xA9, 0x33, 0x1E, 0x68, 0xBD, 0x95, 0x31, 0x20, 0x8B, 0x2A, 0xA6, 0x70, 0x96,
0xC4, 0x5E, 0x3D, 0x95, 0x50, 0x79, 0x67, 0x46, 0xCA, 0x50, 0x20, 0xDF, 0x10,
0x2F, 0x08, 0xA0, 0x03, 0xBD, 0x2D, 0x50, 0xEE, 0x55, 0x2C, 0xEC, 0xC9, 0x5F,
0xB9, 0x11, 0x44, 0xEF, 0x4A, 0x21, 0x74, 0x74, 0x75, 0xF3, 0x86, 0xA2, 0x8A,
0x88, 0xA3, 0x77, 0x21, 0xC6, 0xC3, 0x21, 0x27, 0x8A, 0x42, 0x2C, 0x16, 0x3A,
0x88, 0x8A, 0x7F, 0x73, 0xEA, 0xD8, 0x79, 0x46, 0x1C, 0x67, 0x9B, 0x79, 0x61,
0x95, 0xF3, 0x44, 0x63, 0x65, 0x12, 0x8C, 0xA5, 0xBB, 0x88, 0x12, 0x67, 0x9F,
0x90, 0x5D, 0x95, 0x66, 0xA1, 0x9C, 0x57, 0x2A, 0x09, 0x34, 0x07, 0x7B, 0xD6,
0x02, 0xAC, 0x9A, 0x43, 0x10, 0xB5, 0x0A, 0xA0, 0x17, 0x1C, 0xA6, 0x00, 0x9B
};
constexpr std::array<uint8_t, 1534> bootstrap_font_dat{
0x52, 0x4E, 0x43, 0x01, 0x00, 0x00, 0x13, 0x68, 0x00, 0x00, 0x05, 0xEC,
0xD3, 0x6C, 0x7E, 0xAB, 0xBE, 0xEF, 0x94, 0x90, 0x81, 0x61, 0x50, 0x34,
0x44, 0x33, 0x33, 0x53, 0x66, 0x64, 0x64, 0x26, 0x20, 0x60, 0x47, 0x1E,
0x01, 0xFF, 0x84, 0x41, 0x01, 0x02, 0xFF, 0x01, 0x84, 0x09, 0xFF, 0xFF,
0x14, 0x2F, 0x06, 0xCD, 0x9B, 0xCC, 0x8C, 0x89, 0x04, 0x1B, 0x42, 0x06,
0x65, 0x64, 0x71, 0x3B, 0x44, 0x01, 0x06, 0x78, 0xE0, 0xE9, 0xD9, 0x9A,
0x2B, 0x36, 0x72, 0xB0, 0x43, 0x84, 0x59, 0x1B, 0x4D, 0x0A, 0xB6, 0x75,
0x87, 0x91, 0x32, 0xAB, 0x86, 0x02, 0xCC, 0xDE, 0x84, 0x07, 0x82, 0x31,
0x85, 0xCC, 0x45, 0x90, 0x4A, 0x84, 0xE7, 0xC4, 0x9C, 0x42, 0xFC, 0x29,
0x66, 0x0C, 0xA4, 0x94, 0x9D, 0xD9, 0x38, 0x75, 0x35, 0x42, 0x98, 0x07,
0x6D, 0x70, 0x91, 0x1B, 0x03, 0x8E, 0x54, 0x40, 0x9B, 0xED, 0x89, 0x19,
0x44, 0x87, 0x02, 0x9D, 0x8B, 0x5C, 0x3A, 0x15, 0x19, 0xF8, 0xB7, 0xAC,
0x43, 0x01, 0x15, 0x93, 0x0B, 0x5D, 0x03, 0x83, 0x40, 0x1A, 0x56, 0xA5,
0x1D, 0x03, 0xF2, 0x93, 0xA2, 0x50, 0x03, 0xB6, 0x11, 0x1C, 0xF2, 0x8E,
0x8E, 0x60, 0x87, 0x42, 0x89, 0x93, 0x40, 0x93, 0x64, 0xA2, 0x81, 0x04,
0x29, 0x23, 0x11, 0x4F, 0x36, 0xA0, 0xBA, 0x8E, 0xA2, 0xA4, 0x1A, 0x08,
0x75, 0x65, 0x6E, 0x9C, 0xAC, 0x14, 0xC3, 0x18, 0x67, 0x33, 0x16, 0x4E,
0x10, 0x31, 0x8C, 0x11, 0x9B, 0x17, 0x7C, 0xCC, 0x44, 0x57, 0x8F, 0xF7,
0x74, 0x0E, 0x3F, 0x71, 0x80, 0x64, 0x08, 0x39, 0x90, 0xBC, 0x28, 0x87,
0x3A, 0x15, 0x8B, 0x70, 0x30, 0x9D, 0x04, 0x14, 0x0B, 0x89, 0x1C, 0x95,
0x21, 0x8F, 0x50, 0xB5, 0x2D, 0xD9, 0x85, 0x1B, 0x80, 0x72, 0xEC, 0x0A,
0xCE, 0x6B, 0xA8, 0xDB, 0x86, 0x05, 0x35, 0x93, 0x84, 0x12, 0x8B, 0xE6,
0xEC, 0x84, 0xBF, 0x79, 0x5F, 0xDA, 0xA4, 0xC8, 0x97, 0xF8, 0x84, 0x42,
0x9B, 0x19, 0xB4, 0x96, 0xCB, 0xD4, 0x85, 0x21, 0x84, 0x08, 0xB3, 0x5B,
0x1F, 0x88, 0x60, 0xCE, 0x85, 0x0F, 0x56, 0x92, 0x92, 0xA4, 0x05, 0xA6,
0x43, 0x46, 0x9E, 0x94, 0x08, 0x8B, 0x58, 0x17, 0x3B, 0x87, 0xC9, 0xAA,
0x86, 0x48, 0x83, 0x23, 0x12, 0x83, 0x52, 0x90, 0x89, 0x38, 0x4A, 0x50,
0x89, 0x52, 0x32, 0x98, 0x45, 0xB9, 0x84, 0x48, 0x6D, 0x98, 0x17, 0x9E,
0xA2, 0x37, 0x66, 0xC2, 0xA2, 0xB4, 0x5A, 0x2C, 0x54, 0xE5, 0xC5, 0x15,
0x74, 0x44, 0x67, 0x06, 0x50, 0x6E, 0xE1, 0x06, 0x2F, 0x2D, 0xC8, 0x99,
0x0D, 0xB6, 0x4D, 0x79, 0x24, 0x0E, 0x07, 0x1D, 0xE8, 0x91, 0x94, 0x0F,
0xDE, 0x62, 0x3F, 0x60, 0x0F, 0x27, 0x63, 0xE5, 0x40, 0x78, 0x85, 0xB8,
0x15, 0x09, 0xCA, 0x22, 0x2B, 0x19, 0x09, 0x53, 0xF9, 0x84, 0x69, 0x6D,
0x32, 0x6B, 0xAE, 0x45, 0xCA, 0x90, 0xE6, 0x12, 0x6A, 0xB4, 0xA2, 0xD8,
0x4C, 0xFA, 0xA1, 0x5D, 0xEF, 0xC2, 0xE4, 0x02, 0x00, 0xF9, 0x5B, 0x05,
0x13, 0x6B, 0xC2, 0xB9, 0xAA, 0x17, 0xD8, 0xBA, 0xB1, 0xF9, 0xA0, 0x05,
0x4C, 0x2F, 0x9A, 0x61, 0xBA, 0x52, 0x94, 0x63, 0xB9, 0x16, 0x9D, 0xA3,
0x65, 0x84, 0x3D, 0x4A, 0x64, 0x9C, 0x96, 0x20, 0xF6, 0x30, 0x85, 0x68,
0x5A, 0x50, 0x95, 0xE5, 0x30, 0xFD, 0x90, 0x1C, 0xE2, 0xE6, 0x43, 0x0B,
0x6A, 0x9F, 0x23, 0x12, 0x27, 0xC2, 0x81, 0x9D, 0x55, 0x85, 0xC7, 0x01,
0xA5, 0x58, 0x01, 0xC7, 0xD3, 0xF3, 0xFD, 0x87, 0x0B, 0x58, 0x7C, 0x52,
0xCA, 0x6C, 0x47, 0xF1, 0x39, 0x19, 0x8A, 0x9A, 0xF7, 0x21, 0x0A, 0x67,
0x06, 0xBE, 0xD3, 0x88, 0xCF, 0x47, 0x34, 0x94, 0x09, 0x04, 0x40, 0x64,
0x09, 0xE6, 0x4F, 0xEA, 0xA5, 0x55, 0x4E, 0xA5, 0x95, 0xE5, 0x50, 0x0D,
0x06, 0x46, 0x61, 0x85, 0x23, 0x08, 0xAB, 0xF9, 0x48, 0x09, 0x33, 0x44,
0x50, 0x07, 0x21, 0x3C, 0xA8, 0x61, 0x9D, 0x34, 0x84, 0x22, 0x5C, 0x44,
0xFD, 0xA0, 0x6D, 0xE2, 0x6E, 0x56, 0x3A, 0xA3, 0x19, 0x62, 0x4A, 0x82,
0x0C, 0x98, 0x25, 0xCF, 0x24, 0xC8, 0x35, 0x35, 0x1A, 0x12, 0x59, 0x5D,
0x98, 0xD6, 0x2E, 0x76, 0x7F, 0x2B, 0xF4, 0x30, 0x24, 0xAF, 0x4C, 0x5C,
0x28, 0x2E, 0x28, 0xAA, 0x24, 0xE5, 0x17, 0xFF, 0x55, 0x42, 0xC9, 0x54,
0x1B, 0x75, 0xDD, 0xBE, 0x47, 0x9B, 0x11, 0x22, 0x0E, 0xCC, 0xAA, 0x88,
0x88, 0x66, 0x64, 0xCA, 0xD0, 0x88, 0x08, 0x41, 0x06, 0xF0, 0x17, 0x45,
0x82, 0x07, 0xAE, 0x80, 0x9F, 0xB2, 0x23, 0x23, 0xAB, 0x8C, 0xE9, 0xFA,
0xC3, 0x53, 0xD0, 0x06, 0x88, 0x67, 0x59, 0xB3, 0x92, 0xD2, 0xE9, 0xA8,
0x82, 0x0A, 0xA7, 0xC9, 0x44, 0x8E, 0x47, 0x15, 0x82, 0x37, 0x80, 0x9B,
0x88, 0xF9, 0x08, 0x27, 0xDC, 0x05, 0xC2, 0xD3, 0x94, 0x59, 0x55, 0x41,
0x7F, 0x91, 0xFF, 0xDB, 0x16, 0x8C, 0x0C, 0x27, 0x76, 0x6E, 0x26, 0x81,
0xAA, 0xA1, 0x8F, 0xC1, 0x20, 0x63, 0x0B, 0xC3, 0x89, 0x49, 0x31, 0x42,
0x93, 0x8C, 0x69, 0x07, 0x7B, 0x3F, 0x9E, 0x0E, 0x45, 0x56, 0x85, 0x91,
0xE9, 0x78, 0xCC, 0xF9, 0x08, 0xA4, 0xD5, 0x92, 0x70, 0x89, 0x16, 0xC3,
0x0C, 0x92, 0x18, 0xA7, 0x59, 0xF2, 0x08, 0xCD, 0xE0, 0xF2, 0xA8, 0x97,
0x4C, 0xF3, 0x83, 0xA7, 0x1C, 0x68, 0x54, 0x17, 0x03, 0x8A, 0x20, 0x57,
0x50, 0x58, 0x31, 0x11, 0x72, 0x87, 0x71, 0x25, 0x2E, 0xE7, 0xE4, 0x79,
0x90, 0x10, 0x50, 0x1D, 0x54, 0x88, 0xE1, 0xB7, 0x8F, 0x18, 0x02, 0x77,
0xA8, 0x0C, 0x64, 0x11, 0x8C, 0xC9, 0x21, 0xE6, 0x07, 0xCB, 0x5E, 0x8F,
0xB2, 0x25, 0x0B, 0x7B, 0x0A, 0x25, 0x8B, 0x95, 0xD0, 0x82, 0xF4, 0xE2,
0x34, 0x20, 0xEA, 0x5F, 0x36, 0x8C, 0xA1, 0x94, 0x50, 0xFD, 0x69, 0x1B,
0xCB, 0xB0, 0x8B, 0x4F, 0xBF, 0xB5, 0xC7, 0x0B, 0xC2, 0xC5, 0x17, 0xEE,
0x8C, 0x91, 0xFB, 0xD3, 0x0D, 0xD1, 0xE1, 0x98, 0x3A, 0xC9, 0x4D, 0x08,
0xF4, 0xB8, 0x59, 0x11, 0x78, 0x48, 0x20, 0x8B, 0x25, 0x08, 0xBF, 0xF2,
0x89, 0x59, 0xDB, 0x31, 0xD6, 0x17, 0x9E, 0xCA, 0x74, 0x20, 0x8E, 0x0E,
0xD5, 0x07, 0x3B, 0x5D, 0x51, 0x8E, 0x86, 0x71, 0xCE, 0xE6, 0x2C, 0x54,
0xB2, 0xC6, 0x8B, 0x02, 0x7D, 0x7F, 0xC2, 0x61, 0xC2, 0x0B, 0xF0, 0x85,
0x84, 0xCC, 0x98, 0x11, 0x4A, 0x8D, 0x66, 0x03, 0xF8, 0x54, 0x57, 0xB0,
0xF0, 0x45, 0x8C, 0xFF, 0xD3, 0x61, 0xA8, 0x82, 0x09, 0xFF, 0x00, 0xCA,
0x0B, 0x8C, 0x9B, 0x75, 0x24, 0x09, 0x07, 0x00, 0xF9, 0xAC, 0xC1, 0xBB,
0xB7, 0x9E, 0xE8, 0x41, 0xE4, 0x2B, 0xCE, 0xAB, 0x2D, 0x9D, 0xEA, 0xFD,
0x41, 0x00, 0x42, 0x8F, 0xC9, 0xF4, 0x87, 0x54, 0x61, 0x2D, 0x42, 0x51,
0x3F, 0x52, 0x29, 0xCB, 0x87, 0x0F, 0x1F, 0xE4, 0x11, 0x33, 0x8E, 0xE5,
0xCD, 0x86, 0x6F, 0x96, 0x30, 0x40, 0x06, 0xF5, 0x7F, 0x42, 0xB2, 0x61,
0x93, 0x55, 0x87, 0xFE, 0x86, 0xE3, 0x67, 0x49, 0xA2, 0xC3, 0x1F, 0x6F,
0xEF, 0x76, 0x4E, 0xE5, 0x12, 0x89, 0x3E, 0x10, 0xFF, 0x90, 0x99, 0xBF,
0x40, 0x45, 0xAA, 0xFC, 0x10, 0xDC, 0xC1, 0xF1, 0x44, 0x13, 0x50, 0x58,
0x29, 0x61, 0x28, 0x95, 0x70, 0x05, 0x7E, 0x21, 0x8D, 0xE3, 0x83, 0xB2,
0x8F, 0xB2, 0xA2, 0x10, 0xE0, 0x93, 0xAC, 0xFD, 0x88, 0x38, 0x38, 0xC4,
0x46, 0x23, 0x4A, 0x41, 0xF8, 0x0F, 0xA8, 0x12, 0xD5, 0x88, 0x7F, 0x92,
0xA2, 0x21, 0xBE, 0xF1, 0x0A, 0x11, 0x92, 0x11, 0x99, 0xAB, 0xEA, 0xC3,
0x85, 0x22, 0x1B, 0xA2, 0xDF, 0xE9, 0x7E, 0x18, 0x95, 0xB6, 0x48, 0xD1,
0x9F, 0x50, 0xCF, 0x11, 0xBE, 0xF9, 0xB9, 0x1E, 0xC7, 0x9B, 0x00, 0xE3,
0x62, 0xAA, 0x98, 0x0E, 0x01, 0x87, 0xC0, 0x27, 0x59, 0x53, 0x83, 0x47,
0xBA, 0x96, 0xEA, 0xFF, 0x88, 0x68, 0xEA, 0x08, 0xD3, 0x58, 0xCE, 0x0F,
0x05, 0x8A, 0xDA, 0x27, 0x6E, 0x15, 0x18, 0xF2, 0xAF, 0x38, 0xEA, 0x54,
0xA0, 0x89, 0x74, 0xA1, 0x4B, 0x16, 0x9A, 0x14, 0x4D, 0x06, 0x85, 0x1D,
0xEC, 0xEF, 0x52, 0x54, 0x93, 0x2C, 0x09, 0xA3, 0xE9, 0xC6, 0x1C, 0x75,
0xAD, 0xD8, 0x14, 0x2E, 0x31, 0x58, 0x15, 0xCF, 0xB8, 0x44, 0x04, 0xAC,
0x1C, 0xF0, 0x22, 0xA9, 0x3B, 0x99, 0xD9, 0x44, 0x9D, 0x8E, 0xF8, 0x05,
0xE1, 0x04, 0x7B, 0x0B, 0x32, 0x13, 0x85, 0x22, 0xDE, 0x87, 0xAC, 0x27,
0xAB, 0x2A, 0x66, 0x75, 0xA1, 0x9B, 0xC0, 0x51, 0xE1, 0x59, 0x43, 0x5B,
0xC2, 0x37, 0xDF, 0x00, 0xD5, 0x98, 0x53, 0xE3, 0x95, 0xD0, 0xD2, 0x88,
0xF6, 0x8F, 0xFD, 0x28, 0x5D, 0xFE, 0x50, 0x34, 0xF4, 0x85, 0xEA, 0xA1,
0xC9, 0x0F, 0xA6, 0x72, 0xA8, 0x57, 0xF8, 0xEA, 0x50, 0x96, 0xC8, 0x84,
0x1E, 0xF8, 0x1F, 0x2C, 0xEB, 0xA9, 0xA3, 0x64, 0xE1, 0xAC, 0xE4, 0x2B,
0x50, 0xA4, 0x66, 0x54, 0x87, 0x63, 0xA2, 0x87, 0x00, 0x04, 0x29, 0x2F,
0x1E, 0x89, 0x51, 0xC1, 0xC0, 0xD0, 0xA4, 0xC7, 0x14, 0x52, 0xBA, 0x58,
0xF1, 0xC4, 0x41, 0x40, 0x57, 0xD5, 0xF2, 0xA6, 0x80, 0xEC, 0xB0, 0x30,
0xB1, 0x71, 0xDB, 0x45, 0xA4, 0x86, 0xAC, 0xA7, 0x98, 0xD4, 0xB3, 0x26,
0x42, 0xF1, 0x24, 0x31, 0x0C, 0x54, 0x4F, 0x68, 0x03, 0xB3, 0x0E, 0xD8,
0xB7, 0xE5, 0x8D, 0x1C, 0x8B, 0xD5, 0x08, 0x2D, 0x9E, 0xD0, 0x9C, 0x06,
0x62, 0xA1, 0xD5, 0x41, 0x3B, 0xA0, 0x5C, 0x7E, 0x92, 0x75, 0x84, 0x57,
0x38, 0x41, 0xA0, 0x25, 0xB3, 0xA6, 0xA3, 0x89, 0x33, 0xC2, 0xD1, 0x60,
0x72, 0x57, 0xB1, 0xC2, 0xC0, 0x2A, 0x79, 0xB0, 0xC2, 0x6C, 0x13, 0x01,
0x51, 0x21, 0xCB, 0x13, 0xA3, 0x32, 0x3E, 0x16, 0x49, 0xEF, 0x6C, 0xF1,
0x30, 0x90, 0x05, 0x5D, 0x85, 0x27, 0x1B, 0xAA, 0xC0, 0x74, 0x7A, 0x5C,
0x37, 0x01, 0x27, 0x05, 0xEA, 0x3C, 0x4A, 0x4B, 0x9F, 0x0D, 0xC8, 0x18,
0xA2, 0xD4, 0x20, 0x59, 0xCB, 0x19, 0x7D, 0x9C, 0x5F, 0x6C, 0x04, 0xCA,
0x28, 0x6E, 0x6B, 0xAC, 0x7B, 0x0C, 0xCE, 0xE5, 0x9E, 0x49, 0x84, 0x18,
0xDD, 0x1D, 0xAA, 0x39, 0x29, 0x9F, 0x30, 0xFA, 0x22, 0x67, 0x7C, 0x93,
0x92, 0x09, 0x13, 0x52, 0x2F, 0x1E, 0x4E, 0x6E, 0x61, 0x05, 0x18, 0x85,
0x07, 0xA9, 0x33, 0x1E, 0x68, 0xBD, 0x95, 0x31, 0x20, 0x8B, 0x2A, 0xA6,
0x70, 0x96, 0xC4, 0x5E, 0x3D, 0x95, 0x50, 0x79, 0x67, 0x46, 0xCA, 0x50,
0x20, 0xDF, 0x10, 0x2F, 0x08, 0xA0, 0x03, 0xBD, 0x2D, 0x50, 0xEE, 0x55,
0x2C, 0xEC, 0xC9, 0x5F, 0xB9, 0x11, 0x44, 0xEF, 0x4A, 0x21, 0x74, 0x74,
0x75, 0xF3, 0x86, 0xA2, 0x8A, 0x88, 0xA3, 0x77, 0x21, 0xC6, 0xC3, 0x21,
0x27, 0x8A, 0x42, 0x2C, 0x16, 0x3A, 0x88, 0x8A, 0x7F, 0x73, 0xEA, 0xD8,
0x79, 0x46, 0x1C, 0x67, 0x9B, 0x79, 0x61, 0x95, 0xF3, 0x44, 0x63, 0x65,
0x12, 0x8C, 0xA5, 0xBB, 0x88, 0x12, 0x67, 0x9F, 0x90, 0x5D, 0x95, 0x66,
0xA1, 0x9C, 0x57, 0x2A, 0x09, 0x34, 0x07, 0x7B, 0xD6, 0x02, 0xAC, 0x9A,
0x43, 0x10, 0xB5, 0x0A, 0xA0, 0x17, 0x1C, 0xA6, 0x00, 0x9B};
/* Data from bootstrap_font.pal inserted by mkbootstrap.lua: */
constexpr std::array<uint8_t, 47> bootstrap_font_pal {
0x52, 0x4E, 0x43, 0x01, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x1D, 0xB7,
0xFF, 0x76, 0x79, 0xBE, 0xEF, 0x90, 0x10, 0x90, 0x05, 0x01, 0x02, 0x00, 0x00,
0x20, 0x2B, 0x04, 0x00, 0x00, 0x00, 0x40, 0x08, 0x00, 0xB6, 0x00, 0x00, 0x00,
0x3F, 0x3F, 0x3F, 0x62, 0x79, 0xBE, 0x27, 0x3F
};
constexpr std::array<uint8_t, 47> bootstrap_font_pal{
0x52, 0x4E, 0x43, 0x01, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x1D,
0xB7, 0xFF, 0x76, 0x79, 0xBE, 0xEF, 0x90, 0x10, 0x90, 0x05, 0x01, 0x02,
0x00, 0x00, 0x20, 0x2B, 0x04, 0x00, 0x00, 0x00, 0x40, 0x08, 0x00, 0xB6,
0x00, 0x00, 0x00, 0x3F, 0x3F, 0x3F, 0x62, 0x79, 0xBE, 0x27, 0x3F};
/* End autogenerated content */
// Lua reader function for loading bootstrap_code
const char* read_bootstrap_line(lua_State *L, void *data, size_t *size)
{
int& iLine = *reinterpret_cast<int*>(data);
++iLine;
if(iLine < 0 || (iLine & 1))
{
*size = 1;
return "\n";
}
else
{
const char *s = bootstrap_code[iLine / 2];
if(s == nullptr)
{
*size = 0;
return nullptr;
}
else
{
*size = std::strlen(s);
return s;
}
const char* read_bootstrap_line(lua_State* L, void* data, size_t* size) {
int& iLine = *reinterpret_cast<int*>(data);
++iLine;
if (iLine < 0 || (iLine & 1)) {
*size = 1;
return "\n";
} else {
const char* s = bootstrap_code[iLine / 2];
if (s == nullptr) {
*size = 0;
return nullptr;
} else {
*size = std::strlen(s);
return s;
}
}
}
template<typename Array>
inline void push(lua_State *L, Array data)
{
lua_pushlstring(L, reinterpret_cast<const char*>(data.data()), data.size());
template <typename Array>
inline void push(lua_State* L, Array data) {
lua_pushlstring(L, reinterpret_cast<const char*>(data.data()), data.size());
}
} // namespace
} // namespace
int bootstrap_lua_resources(lua_State *L)
{
push(L, bootstrap_font_dat);
push(L, bootstrap_font_tab);
push(L, bootstrap_font_pal);
return 3;
int bootstrap_lua_resources(lua_State* L) {
push(L, bootstrap_font_dat);
push(L, bootstrap_font_tab);
push(L, bootstrap_font_pal);
return 3;
}
int bootstrap_lua_error_report(lua_State *L)
{
int iLine = -first_bootstrap_code_line_number;
if(luaT_load(L, read_bootstrap_line, &iLine, "@bootstrap.cpp", "t") == 0)
{
bootstrap_lua_resources(L);
lua_pushvalue(L, 1);
lua_call(L, 4, 0);
return 0;
}
else
return lua_error(L);
int bootstrap_lua_error_report(lua_State* L) {
int iLine = -first_bootstrap_code_line_number;
if (luaT_load(L, read_bootstrap_line, &iLine, "@bootstrap.cpp", "t") == 0) {
bootstrap_lua_resources(L);
lua_pushvalue(L, 1);
lua_call(L, 4, 0);
return 0;
} else
return lua_error(L);
}

View File

@@ -25,9 +25,9 @@ SOFTWARE.
#include "lua.hpp"
//! Push onto the stack the bootstrap font data file, table file and palette.
int bootstrap_lua_resources(lua_State *L);
int bootstrap_lua_resources(lua_State* L);
//! Provide an onscreen report of the error message on the top of the stack.
int bootstrap_lua_error_report(lua_State *L);
int bootstrap_lua_error_report(lua_State* L);
#endif // CORSIX_TH_BOOTSTRAP_H_
#endif // CORSIX_TH_BOOTSTRAP_H_

View File

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

View File

@@ -3,7 +3,8 @@
#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 */
0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7,
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,
0x03A6, 0x0398, 0x03A9, 0x03B4, 0x221E, 0x03C6, 0x03B5, 0x2229,
0x2261, 0x00B1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00F7, 0x2248,
0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0
};
0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0};
// clang-format on
#endif

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

View File

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

View File

@@ -1,7 +1,7 @@
#include "lua_rnc.h"
#include <array>
#include "../../common/rnc.h"
#include "th_lua.h"
#include <array>
//! Provides lua function to decompress RNC data
/*!
@@ -12,70 +12,65 @@
namespace {
int l_decompress(lua_State *L)
{
size_t inlen;
const uint8_t* in = reinterpret_cast<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);
int l_decompress(lua_State* L) {
size_t inlen;
const uint8_t* in =
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);
switch(rnc_unpack(in, (uint8_t*)outbuf))
{
lua_pushliteral(L, "Input is not RNC compressed data");
return 2;
}
uint32_t outlen = rnc_output_size(in);
// Allocate scratch area as Lua userdata so that if something horrible
// happens, it'll be cleaned up by Lua's GC. Remember that most Lua API
// calls can throw errors, so they all have to be wrapped with code to
// detect errors and free the buffer if said buffer was not managed by Lua.
void* outbuf = lua_newuserdata(L, outlen);
lua_pushnil(L);
switch (rnc_unpack(in, (uint8_t*)outbuf)) {
case rnc_status::ok:
lua_pushlstring(L, (const char*)outbuf, outlen);
return 1;
lua_pushlstring(L, (const char*)outbuf, outlen);
return 1;
case rnc_status::file_is_not_rnc:
lua_pushliteral(L, "Input is not RNC compressed data");
break;
lua_pushliteral(L, "Input is not RNC compressed data");
break;
case rnc_status::huf_decode_error:
lua_pushliteral(L, "Invalid Huffman coding");
break;
lua_pushliteral(L, "Invalid Huffman coding");
break;
case rnc_status::file_size_mismatch:
lua_pushliteral(L, "Size mismatch");
break;
lua_pushliteral(L, "Size mismatch");
break;
case rnc_status::packed_crc_error:
lua_pushliteral(L, "Incorrect packed CRC");
break;
lua_pushliteral(L, "Incorrect packed CRC");
break;
case rnc_status::unpacked_crc_error:
lua_pushliteral(L, "Incorrect unpacked CRC");
break;
lua_pushliteral(L, "Incorrect unpacked CRC");
break;
default:
lua_pushliteral(L, "Unknown error decompressing RNC data");
break;
}
return 2;
lua_pushliteral(L, "Unknown error decompressing RNC data");
break;
}
return 2;
}
constexpr std::array<luaL_Reg, 2> rnclib {{
{"decompress", l_decompress},
{nullptr, nullptr}
}};
constexpr std::array<luaL_Reg, 2> rnclib{
{{"decompress", l_decompress}, {nullptr, nullptr}}};
} // namespace
} // namespace
int luaopen_rnc(lua_State *L)
{
luaT_register(L, "rnc", rnclib);
return 1;
int luaopen_rnc(lua_State* L) {
luaT_register(L, "rnc", rnclib);
return 1;
}

View File

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

View File

@@ -23,8 +23,8 @@ SOFTWARE.
#ifndef CORSIX_TH_LUA_SDL_H_
#define CORSIX_TH_LUA_SDL_H_
#include "lua.hpp"
#include <SDL.h>
#include "lua.hpp"
// SDL event codes used for delivering custom events to l_mainloop in
// sdl_core.cpp
@@ -39,8 +39,8 @@ SOFTWARE.
// SDL_USEREVENT_SOUND_OVER - informs script of a played sound finishing.
#define SDL_USEREVENT_SOUND_OVER (SDL_USEREVENT + 4)
int luaopen_sdl(lua_State *L);
int luaopen_sdl(lua_State* L);
int l_load_music_async_callback(lua_State *L);
int l_load_music_async_callback(lua_State* L);
#endif // CORSIX_TH_LUA_SDL_H_
#endif // CORSIX_TH_LUA_SDL_H_

View File

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

View File

@@ -33,14 +33,14 @@ SOFTWARE.
transfers control to CorsixTH.lua as soon as possible (so that as little as
possible behaviour is hardcoded into C rather than Lua).
*/
int lua_main(lua_State *L);
int lua_main(lua_State* L);
//! Alternative lua mode entry point
/*!
Behaves like CorsixTH_lua_main, except that it doesn't transfer control
over to Lua scripts - it just prepares everything for them and loads them.
*/
int lua_main_no_eval(lua_State *L);
int lua_main_no_eval(lua_State* L);
//! Process a caught error before returning it to the caller
/*!
@@ -49,7 +49,7 @@ int lua_main_no_eval(lua_State *L);
processing the error, the caller receives LUA_ERRERR rather than panicking
while processing it itself.
*/
int lua_stacktrace(lua_State *L);
int lua_stacktrace(lua_State* L);
//! Process an uncaught Lua error before aborting
/*!
@@ -57,6 +57,6 @@ int lua_stacktrace(lua_State *L);
which can be done when they do, but at least the user should be informed,
and the error message printed.
*/
int lua_panic(lua_State *L);
int lua_panic(lua_State* L);
#endif // CORSIX_TH_MAIN_H_
#endif // CORSIX_TH_MAIN_H_

File diff suppressed because it is too large Load Diff

View File

@@ -23,84 +23,76 @@ SOFTWARE.
#ifndef CORSIX_TH_PERSIST_LUA_H_
#define CORSIX_TH_PERSIST_LUA_H_
#include "config.h"
#include "th_lua.h"
#include <vector>
#include <cstdlib>
#include <vector>
#include "th_lua.h"
template <class T> struct lua_persist_int {};
template <> struct lua_persist_int<int> {typedef unsigned int T;};
template <class T>
struct lua_persist_int {};
template <>
struct lua_persist_int<int> {
typedef unsigned int T;
};
//! Interface used for persisting Lua objects
/*!
When userdata are persisted, they get an instance of this interface for
writing binary data and other Lua objects.
*/
class lua_persist_writer
{
public:
virtual ~lua_persist_writer() = default;
class lua_persist_writer {
public:
virtual ~lua_persist_writer() = default;
virtual lua_State* get_stack() = 0;
virtual void write_stack_object(int iIndex) = 0;
virtual void write_byte_stream(const uint8_t *pBytes, size_t iCount) = 0;
virtual void set_error(const char *sError) = 0;
virtual lua_State* get_stack() = 0;
virtual void write_stack_object(int iIndex) = 0;
virtual void write_byte_stream(const uint8_t* pBytes, size_t iCount) = 0;
virtual void set_error(const char* sError) = 0;
// write_stack_object for userdata without growing the Lua call stack
// The given index should be a userdata whose __persist metamethod supports
// fast persistance (being called with extra arguments and the wrong
// environment / upvalues).
virtual void fast_write_stack_object(int iIndex) = 0;
// write_stack_object for userdata without growing the Lua call stack
// The given index should be a userdata whose __persist metamethod supports
// fast persistance (being called with extra arguments and the wrong
// environment / upvalues).
virtual void fast_write_stack_object(int iIndex) = 0;
// Writes an unsigned integer as a variable number of bytes
// Endian independant and underlying type size independant
template <class T>
void write_uint(T tValue)
{
T tTemp(tValue);
int iNumBytes;
for(iNumBytes = 1; tTemp >= (T)0x80; tTemp /= (T)0x80)
++iNumBytes;
if(iNumBytes == 1)
{
uint8_t iByte = (uint8_t)tValue;
write_byte_stream(&iByte, 1);
}
else
{
std::vector<uint8_t> bytes(iNumBytes);
bytes[iNumBytes - 1] = 0x7F & (uint8_t)(tValue);
for(int i = 1; i < iNumBytes; ++i)
{
tValue /= (T)0x80;
bytes[iNumBytes - 1 - i] = 0x80 | (0x7F & (uint8_t)tValue);
}
write_byte_stream(bytes.data(), iNumBytes);
}
// Writes an unsigned integer as a variable number of bytes
// Endian independant and underlying type size independant
template <class T>
void write_uint(T tValue) {
T tTemp(tValue);
int iNumBytes;
for (iNumBytes = 1; tTemp >= (T)0x80; tTemp /= (T)0x80) ++iNumBytes;
if (iNumBytes == 1) {
uint8_t iByte = (uint8_t)tValue;
write_byte_stream(&iByte, 1);
} else {
std::vector<uint8_t> bytes(iNumBytes);
bytes[iNumBytes - 1] = 0x7F & (uint8_t)(tValue);
for (int i = 1; i < iNumBytes; ++i) {
tValue /= (T)0x80;
bytes[iNumBytes - 1 - i] = 0x80 | (0x7F & (uint8_t)tValue);
}
write_byte_stream(bytes.data(), iNumBytes);
}
}
template <class T>
void write_int(T tValue)
{
typename lua_persist_int<T>::T tValueToWrite;
if(tValue >= 0)
{
tValueToWrite = (typename lua_persist_int<T>::T)tValue;
tValueToWrite <<= 1;
}
else
{
tValueToWrite = (typename lua_persist_int<T>::T)(-(tValue + 1));
tValueToWrite <<= 1;
tValueToWrite |= 1;
}
write_uint(tValueToWrite);
template <class T>
void write_int(T tValue) {
typename lua_persist_int<T>::T tValueToWrite;
if (tValue >= 0) {
tValueToWrite = (typename lua_persist_int<T>::T)tValue;
tValueToWrite <<= 1;
} else {
tValueToWrite = (typename lua_persist_int<T>::T)(-(tValue + 1));
tValueToWrite <<= 1;
tValueToWrite |= 1;
}
write_uint(tValueToWrite);
}
template <class T>
void write_float(T fValue)
{
write_byte_stream(reinterpret_cast<uint8_t*>(&fValue), sizeof(T));
}
template <class T>
void write_float(T fValue) {
write_byte_stream(reinterpret_cast<uint8_t*>(&fValue), sizeof(T));
}
};
//! Interface used for depersisting Lua objects
@@ -108,65 +100,55 @@ public:
When userdata are depersisted, they get an instance of this interface for
reading binary data and other Lua objects.
*/
class lua_persist_reader
{
public:
virtual ~lua_persist_reader() = default;
class lua_persist_reader {
public:
virtual ~lua_persist_reader() = default;
virtual lua_State* get_stack() = 0;
virtual bool read_stack_object() = 0;
virtual bool read_byte_stream(uint8_t *pBytes, size_t iCount) = 0;
virtual void set_error(const char *sError) = 0;
virtual lua_State* get_stack() = 0;
virtual bool read_stack_object() = 0;
virtual bool read_byte_stream(uint8_t* pBytes, size_t iCount) = 0;
virtual void set_error(const char* sError) = 0;
// Reads an integer previously written by lua_persist_writer::write_uint()
template <class T>
bool read_uint(T& tValue)
{
T tTemp(0);
uint8_t iByte;
// Reads an integer previously written by lua_persist_writer::write_uint()
template <class T>
bool read_uint(T& tValue) {
T tTemp(0);
uint8_t iByte;
while(true)
{
if(!read_byte_stream(&iByte, 1))
return false;
if(iByte & 0x80)
{
tTemp = static_cast<T>(tTemp | (iByte & 0x7F));
tTemp = static_cast<T>(tTemp << 7);
}
else
{
tTemp = static_cast<T>(tTemp | iByte);
break;
}
}
tValue = tTemp;
return true;
while (true) {
if (!read_byte_stream(&iByte, 1)) return false;
if (iByte & 0x80) {
tTemp = static_cast<T>(tTemp | (iByte & 0x7F));
tTemp = static_cast<T>(tTemp << 7);
} else {
tTemp = static_cast<T>(tTemp | iByte);
break;
}
}
template <class T>
bool read_int(T& tValue)
{
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;
}
tValue = tTemp;
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;
}
template <class T>
bool read_int(T& tValue) {
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>
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)
*/
#include <lua.h>
#include <lauxlib.h>
#include <lua.h>
#ifdef _MSC_VER
typedef unsigned __int16 uint16_t;
typedef unsigned __int32 uint32_t;
@@ -57,16 +57,14 @@ typedef unsigned __int32 uint32_t;
#define UPPER_MASK 0x80000000UL /* most significant w-r bits */
#define LOWER_MASK 0x7fffffffUL /* least significant r bits */
uint32_t mt[N]; /* the array for the state vector */
uint16_t mti=N+1; /* mti==N+1 means mt[N] is not initialized */
uint32_t mt[N]; /* the array for the state vector */
uint16_t mti = N + 1; /* mti==N+1 means mt[N] is not initialized */
/* initializes mt[N] with a seed */
static void init_genrand(uint32_t s)
{
mt[0]= s;
for (mti=1; mti<N; mti++) {
mt[mti] =
(1812433253UL * (mt[mti-1] ^ (mt[mti-1] >> 30)) + mti);
static void init_genrand(uint32_t s) {
mt[0] = s;
for (mti = 1; mti < N; mti++) {
mt[mti] = (1812433253UL * (mt[mti - 1] ^ (mt[mti - 1] >> 30)) + mti);
/* See Knuth TAOCP Vol2. 3rd Ed. P.106 for multiplier. */
/* In the previous versions, MSBs of the seed affect */
/* only MSBs of the array mt[]. */
@@ -74,28 +72,27 @@ static void init_genrand(uint32_t s)
}
/* generates a random number on [0,0xffffffff]-interval */
static uint32_t genrand_int32(void)
{
static uint32_t genrand_int32(void) {
uint32_t y;
static uint32_t mag01[2]={0x0UL, MATRIX_A};
static uint32_t mag01[2] = {0x0UL, MATRIX_A};
/* mag01[x] = x * MATRIX_A for x=0,1 */
if (mti >= N) { /* generate N words at one time */
int kk;
if (mti == N+1) /* if init_genrand() has not been called, */
if (mti == N + 1) /* if init_genrand() has not been called, */
init_genrand(5489UL); /* a default initial seed is used */
for (kk=0;kk<N-M;kk++) {
y = (mt[kk]&UPPER_MASK)|(mt[kk+1]&LOWER_MASK);
mt[kk] = mt[kk+M] ^ (y >> 1) ^ mag01[y & 0x1UL];
for (kk = 0; kk < N - M; kk++) {
y = (mt[kk] & UPPER_MASK) | (mt[kk + 1] & LOWER_MASK);
mt[kk] = mt[kk + M] ^ (y >> 1) ^ mag01[y & 0x1UL];
}
for (;kk<N-1;kk++) {
y = (mt[kk]&UPPER_MASK)|(mt[kk+1]&LOWER_MASK);
mt[kk] = mt[kk+(M-N)] ^ (y >> 1) ^ mag01[y & 0x1UL];
for (; kk < N - 1; kk++) {
y = (mt[kk] & UPPER_MASK) | (mt[kk + 1] & LOWER_MASK);
mt[kk] = mt[kk + (M - N)] ^ (y >> 1) ^ mag01[y & 0x1UL];
}
y = (mt[N-1]&UPPER_MASK)|(mt[0]&LOWER_MASK);
mt[N-1] = mt[M-1] ^ (y >> 1) ^ mag01[y & 0x1UL];
y = (mt[N - 1] & UPPER_MASK) | (mt[0] & LOWER_MASK);
mt[N - 1] = mt[M - 1] ^ (y >> 1) ^ mag01[y & 0x1UL];
mti = 0;
}
@@ -112,10 +109,9 @@ static uint32_t genrand_int32(void)
}
/* generates a random number on [0,1) with 53-bit resolution*/
static double genrand_res53(void)
{
uint32_t a=genrand_int32()>>5, b=genrand_int32()>>6;
return(a*67108864.0+b)*(1.0/9007199254740992.0);
static double genrand_res53(void) {
uint32_t a = genrand_int32() >> 5, b = genrand_int32() >> 6;
return (a * 67108864.0 + b) * (1.0 / 9007199254740992.0);
}
/* These real versions are due to Isaku Wada, 2002/01/09 added */
@@ -129,27 +125,25 @@ static double genrand_res53(void)
[1, range_end]. If called with two or more arguments, returns a random
integer in the range [range_start, range_end].
*/
static int l_random(lua_State *L)
{
static int l_random(lua_State* L) {
lua_Integer min;
uint32_t max;
switch(lua_gettop(L))
{
default:
case 2:
min = luaL_checkinteger(L, 1);
max = (uint32_t)(luaL_checkinteger(L, 2) - min + 1);
luaL_argcheck(L, max > 0, 2, "interval is empty");
lua_pushinteger(L, min + (lua_Integer)(genrand_int32() % max));
break;
case 1:
max = (uint32_t)luaL_checkinteger(L, 1);
luaL_argcheck(L, 1 <= max, 1, "interval is empty");
lua_pushinteger(L, 1 + (lua_Integer)(genrand_int32() % max));
break;
case 0:
lua_pushnumber(L, (lua_Number)genrand_res53());
break;
switch (lua_gettop(L)) {
default:
case 2:
min = luaL_checkinteger(L, 1);
max = (uint32_t)(luaL_checkinteger(L, 2) - min + 1);
luaL_argcheck(L, max > 0, 2, "interval is empty");
lua_pushinteger(L, min + (lua_Integer)(genrand_int32() % max));
break;
case 1:
max = (uint32_t)luaL_checkinteger(L, 1);
luaL_argcheck(L, 1 <= max, 1, "interval is empty");
lua_pushinteger(L, 1 + (lua_Integer)(genrand_int32() % max));
break;
case 0:
lua_pushnumber(L, (lua_Number)genrand_res53());
break;
}
return 1;
}
@@ -162,8 +156,7 @@ static int l_random(lua_State *L)
Returns a string which can later be passed to math.randomseed() to restore
the random number generator to its current state.
*/
static int l_randomdump(lua_State *L)
{
static int l_randomdump(lua_State* L) {
lua_pushlstring(L, (const char*)mt, N * 4);
lua_pushlstring(L, (const char*)&mti, 2);
lua_concat(L, 2);
@@ -180,28 +173,21 @@ static int l_randomdump(lua_State *L)
the random number generator state from a string previously generated from
math.randomdump().
*/
static int l_randomseed(lua_State *L)
{
if(lua_type(L, 1) == LUA_TSTRING)
{
static int l_randomseed(lua_State* L) {
if (lua_type(L, 1) == LUA_TSTRING) {
int i;
size_t len;
const char *data = lua_tolstring(L, 1, &len);
if(len != N * 4 + 2)
luaL_argerror(L, 1, "Seed string wrong length");
for(i = 0; i < N; ++i)
mt[i] = ((uint32_t*)data)[i];
const char* data = lua_tolstring(L, 1, &len);
if (len != N * 4 + 2) luaL_argerror(L, 1, "Seed string wrong length");
for (i = 0; i < N; ++i) mt[i] = ((uint32_t*)data)[i];
mti = *(uint16_t*)(data + len - 2);
}
else
{
} else {
init_genrand((uint32_t)luaL_checkinteger(L, 1));
}
return 0;
}
int luaopen_random(lua_State *L)
{
int luaopen_random(lua_State* L) {
lua_getglobal(L, "math");
lua_pushliteral(L, "random");
lua_pushcfunction(L, l_random);

View File

@@ -21,307 +21,258 @@ SOFTWARE.
*/
#include "run_length_encoder.h"
#include "persist_lua.h"
#include <new>
#include <algorithm>
#include <new>
#include "persist_lua.h"
integer_run_length_encoder::integer_run_length_encoder()
{
buffer = nullptr;
output = nullptr;
clean();
integer_run_length_encoder::integer_run_length_encoder() {
buffer = nullptr;
output = nullptr;
clean();
}
integer_run_length_encoder::~integer_run_length_encoder()
{
clean();
integer_run_length_encoder::~integer_run_length_encoder() { clean(); }
void integer_run_length_encoder::clean() {
delete[] buffer;
delete[] output;
buffer = nullptr;
output = nullptr;
record_size = 0;
buffer_capacity = 0;
buffer_size = 0;
buffer_offset = 0;
output_capacity = 0;
output_size = 0;
object_size = 0;
object_copies = 0;
}
void integer_run_length_encoder::clean()
{
delete[] buffer;
bool integer_run_length_encoder::initialise(size_t iRecordSize) {
clean();
record_size = iRecordSize;
// Buffer must hold at least 7 + 2 * 8 records, as the maximum object size
// is 8 records, 2 of which are needed to detect a repeat, and 7 for the
// offset at which the objects are found.
buffer_capacity = iRecordSize * 8 * 4;
buffer_size = 0;
buffer_offset = 0;
buffer = new (std::nothrow) uint32_t[buffer_capacity];
if (!buffer) return false;
output_capacity = iRecordSize * 32;
output_size = 0;
output = new (std::nothrow) uint32_t[output_capacity];
if (!output) return false;
object_size = 0;
object_copies = 0;
return true;
}
void integer_run_length_encoder::write(uint32_t iValue) {
buffer[(buffer_offset + buffer_size) % buffer_capacity] = iValue;
if (++buffer_size == buffer_capacity) flush(false);
}
void integer_run_length_encoder::finish() {
if (buffer_size != 0) flush(true);
}
void integer_run_length_encoder::flush(bool bAll) {
do {
if (object_size == 0) {
// Decide on the size of the next object
// Want the object size which gives most object repeats, then for
// two sizes with the same repeat count, the smaller size.
size_t iBestRepeats = 0;
size_t iBestSize = 0;
size_t iBestOffset = 0;
for (size_t iNumRecords = 1; iNumRecords <= 8; ++iNumRecords) {
for (size_t iOffset = 0; iOffset < iNumRecords; ++iOffset) {
size_t iNumRepeats = 0;
size_t iObjSize = iNumRecords * record_size;
while (iObjSize * (iOffset + iNumRepeats + 1) <= buffer_size &&
are_ranges_equal(0, iNumRepeats, iOffset, iObjSize)) {
++iNumRepeats;
}
if (iNumRepeats > iBestRepeats ||
(iNumRepeats == iBestRepeats && iObjSize < iBestSize)) {
iBestRepeats = iNumRepeats;
iBestSize = iObjSize;
iBestOffset = iOffset;
}
}
}
if (iBestRepeats == 1) {
// No repeats were found, so the best we can do is output
// a large non-repeating blob.
move_object_to_output(std::min(buffer_size, 8 * record_size), 1);
} else {
if (iBestOffset != 0)
move_object_to_output(iBestOffset * record_size, 1);
// Mark the object as the current one, and remove all but the
// last instance of it from the buffer. On the next flush, the
// new data might continue the same object, hence why the
// object isn't output just yet.
object_size = iBestSize;
object_copies = iBestRepeats - 1;
buffer_offset =
(buffer_offset + object_size * object_copies) % buffer_capacity;
buffer_size -= object_size * object_copies;
}
} else {
// Try to match more of the current object
while (object_size * 2 <= buffer_size &&
are_ranges_equal(0, 1, 0, object_size)) {
++object_copies;
buffer_offset = (buffer_offset + object_size) % buffer_capacity;
buffer_size -= object_size;
}
// Write data
if (object_size * 2 <= buffer_size || bAll) {
move_object_to_output(object_size, object_copies + 1);
object_size = 0;
object_copies = 0;
}
}
} while (bAll && buffer_size != 0);
}
bool integer_run_length_encoder::are_ranges_equal(size_t iObjIdx1,
size_t iObjIdx2,
size_t iOffset,
size_t iObjSize) const {
iObjIdx1 = buffer_offset + iOffset * record_size + iObjIdx1 * iObjSize;
iObjIdx2 = buffer_offset + iOffset * record_size + iObjIdx2 * iObjSize;
for (size_t i = 0; i < iObjSize; ++i) {
if (buffer[(iObjIdx1 + i) % buffer_capacity] !=
buffer[(iObjIdx2 + i) % buffer_capacity]) {
return false;
}
}
return true;
}
bool integer_run_length_encoder::move_object_to_output(size_t iObjSize,
size_t iObjCount) {
// Grow the output array if needed
if (output_capacity - output_size <= iObjSize) {
size_t iNewSize = (output_capacity + iObjSize) * 2;
uint32_t* pNewOutput = new (std::nothrow) uint32_t[iNewSize];
if (!pNewOutput) return false;
#ifdef _MSC_VER
#pragma warning(disable : 4996)
#endif
std::copy(output, output + output_size, pNewOutput);
#ifdef _MSC_VER
#pragma warning(default : 4996)
#endif
delete[] output;
buffer = nullptr;
output = nullptr;
record_size = 0;
buffer_capacity = 0;
buffer_size = 0;
buffer_offset = 0;
output_capacity = 0;
output_size = 0;
object_size = 0;
object_copies = 0;
output = pNewOutput;
output_capacity = iNewSize;
}
size_t iHeader = (iObjSize / record_size - 1) + 8 * (iObjCount - 1);
output[output_size++] = static_cast<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;
}
bool integer_run_length_encoder::initialise(size_t iRecordSize)
{
clean();
record_size = iRecordSize;
// Buffer must hold at least 7 + 2 * 8 records, as the maximum object size
// is 8 records, 2 of which are needed to detect a repeat, and 7 for the
// offset at which the objects are found.
buffer_capacity = iRecordSize * 8 * 4;
buffer_size = 0;
buffer_offset = 0;
buffer = new (std::nothrow) uint32_t[buffer_capacity];
if(!buffer)
return false;
output_capacity = iRecordSize * 32;
output_size = 0;
output = new (std::nothrow) uint32_t[output_capacity];
if(!output)
return false;
object_size = 0;
object_copies = 0;
return true;
uint32_t* integer_run_length_encoder::get_output(size_t* pCount) const {
if (pCount) *pCount = output_size;
return output;
}
void integer_run_length_encoder::write(uint32_t iValue)
{
buffer[(buffer_offset + buffer_size) % buffer_capacity] = iValue;
if(++buffer_size == buffer_capacity)
flush(false);
void integer_run_length_encoder::pump_output(
lua_persist_writer* pWriter) const {
pWriter->write_uint(output_size);
for (size_t i = 0; i < output_size; ++i) {
pWriter->write_uint(output[i]);
}
}
void integer_run_length_encoder::finish()
{
if(buffer_size != 0)
flush(true);
integer_run_length_decoder::integer_run_length_decoder() {
buffer = nullptr;
clean();
}
void integer_run_length_encoder::flush(bool bAll)
{
do
{
if(object_size == 0)
{
// Decide on the size of the next object
// Want the object size which gives most object repeats, then for
// two sizes with the same repeat count, the smaller size.
size_t iBestRepeats = 0;
size_t iBestSize = 0;
size_t iBestOffset = 0;
for(size_t iNumRecords = 1; iNumRecords <= 8; ++iNumRecords)
{
for(size_t iOffset = 0; iOffset < iNumRecords; ++iOffset)
{
size_t iNumRepeats = 0;
size_t iObjSize = iNumRecords * record_size;
while(iObjSize * (iOffset + iNumRepeats + 1) <= buffer_size
&& are_ranges_equal(0, iNumRepeats, iOffset, iObjSize))
{
++iNumRepeats;
}
if(iNumRepeats > iBestRepeats
||(iNumRepeats == iBestRepeats && iObjSize < iBestSize))
{
iBestRepeats = iNumRepeats;
iBestSize = iObjSize;
iBestOffset = iOffset;
}
}
}
if(iBestRepeats == 1)
{
// No repeats were found, so the best we can do is output
// a large non-repeating blob.
move_object_to_output(std::min(buffer_size, 8 * record_size), 1);
}
else
{
if(iBestOffset != 0)
move_object_to_output(iBestOffset * record_size, 1);
// Mark the object as the current one, and remove all but the
// last instance of it from the buffer. On the next flush, the
// new data might continue the same object, hence why the
// object isn't output just yet.
object_size = iBestSize;
object_copies = iBestRepeats - 1;
buffer_offset = (buffer_offset + object_size * object_copies) % buffer_capacity;
buffer_size -= object_size * object_copies;
}
}
else
{
// Try to match more of the current object
while(object_size * 2 <= buffer_size &&
are_ranges_equal(0, 1, 0, object_size))
{
++object_copies;
buffer_offset = (buffer_offset + object_size) % buffer_capacity;
buffer_size -= object_size;
}
// Write data
if(object_size * 2 <= buffer_size || bAll)
{
move_object_to_output(object_size, object_copies + 1);
object_size = 0;
object_copies = 0;
}
}
} while(bAll && buffer_size != 0);
integer_run_length_decoder::~integer_run_length_decoder() { clean(); }
void integer_run_length_decoder::clean() {
delete[] buffer;
buffer = nullptr;
reader = nullptr;
input = nullptr;
input_end = nullptr;
reads_remaining = 0;
object_copies = 0;
record_size = 0;
object_index = 0;
object_size = 0;
}
bool integer_run_length_encoder::are_ranges_equal(size_t iObjIdx1, size_t iObjIdx2,
size_t iOffset, size_t iObjSize) const
{
iObjIdx1 = buffer_offset + iOffset * record_size + iObjIdx1 * iObjSize;
iObjIdx2 = buffer_offset + iOffset * record_size + iObjIdx2 * iObjSize;
for(size_t i = 0; i < iObjSize; ++i)
{
if(buffer[(iObjIdx1 + i) % buffer_capacity]
!= buffer[(iObjIdx2 + i) % buffer_capacity])
{
return false;
}
bool integer_run_length_decoder::initialise(size_t iRecordSize,
lua_persist_reader* pReader) {
clean();
buffer = new (std::nothrow) uint32_t[9 * iRecordSize];
if (!buffer) return false;
reader = pReader;
record_size = iRecordSize;
return pReader->read_uint(reads_remaining);
}
bool integer_run_length_decoder::initialise(size_t iRecordSize,
const uint32_t* pInput,
size_t iCount) {
clean();
buffer = new (std::nothrow) uint32_t[9 * iRecordSize];
if (!buffer) return false;
input = pInput;
input_end = pInput + iCount;
record_size = iRecordSize;
return true;
}
uint32_t integer_run_length_decoder::read() {
if (object_copies == 0) {
uint32_t iHeader = 0;
if (reader) {
reader->read_uint(iHeader);
--reads_remaining;
} else
iHeader = *(input++);
object_size = record_size * (1 + (iHeader & 7));
object_copies = (iHeader / 8) + 1;
if (reader) {
for (size_t i = 0; i < object_size; ++i) {
reader->read_uint(buffer[i]);
--reads_remaining;
}
} else {
for (size_t i = 0; i < object_size; ++i) buffer[i] = *(input++);
}
return true;
}
}
bool integer_run_length_encoder::move_object_to_output(size_t iObjSize, size_t iObjCount)
{
// Grow the output array if needed
if(output_capacity - output_size <= iObjSize)
{
size_t iNewSize = (output_capacity + iObjSize) * 2;
uint32_t *pNewOutput = new (std::nothrow) uint32_t[iNewSize];
if(!pNewOutput)
return false;
#ifdef _MSC_VER
#pragma warning(disable: 4996)
#endif
std::copy(output, output + output_size, pNewOutput);
#ifdef _MSC_VER
#pragma warning(default: 4996)
#endif
delete[] output;
output = pNewOutput;
output_capacity = iNewSize;
}
size_t iHeader = (iObjSize / record_size - 1) + 8 * (iObjCount - 1);
output[output_size++] = static_cast<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;
uint32_t iValue = buffer[object_index];
if (++object_index == object_size) {
object_index = 0;
object_size = 0;
--object_copies;
}
return iValue;
}
bool integer_run_length_decoder::initialise(size_t iRecordSize, lua_persist_reader *pReader)
{
clean();
buffer = new (std::nothrow) uint32_t[9 * iRecordSize];
if(!buffer)
return false;
reader = pReader;
record_size = iRecordSize;
return pReader->read_uint(reads_remaining);
}
bool integer_run_length_decoder::initialise(size_t iRecordSize, const uint32_t *pInput, size_t iCount)
{
clean();
buffer = new (std::nothrow) uint32_t[9 * iRecordSize];
if(!buffer)
return false;
input = pInput;
input_end = pInput + iCount;
record_size = iRecordSize;
return true;
}
uint32_t integer_run_length_decoder::read()
{
if(object_copies == 0)
{
uint32_t iHeader = 0;
if(reader)
{
reader->read_uint(iHeader);
--reads_remaining;
}
else
iHeader = *(input++);
object_size = record_size * (1 + (iHeader & 7));
object_copies = (iHeader / 8) + 1;
if(reader)
{
for(size_t i = 0; i < object_size; ++i)
{
reader->read_uint(buffer[i]);
--reads_remaining;
}
}
else
{
for(size_t i = 0; i < object_size; ++i)
buffer[i] = *(input++);
}
}
uint32_t iValue = buffer[object_index];
if(++object_index == object_size)
{
object_index = 0;
--object_copies;
}
return iValue;
}
bool integer_run_length_decoder::is_finished() const
{
if(reader)
return reads_remaining == 0 && object_copies == 0;
else
return input == input_end && object_copies == 0;
bool integer_run_length_decoder::is_finished() const {
if (reader)
return reads_remaining == 0 && object_copies == 0;
else
return input == input_end && object_copies == 0;
}

View File

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

View File

@@ -21,325 +21,295 @@ SOFTWARE.
*/
#include "config.h"
#include "lua_sdl.h"
#ifdef CORSIX_TH_USE_SDL_MIXER
#include <SDL_mixer.h>
#include <array>
#include <cstring>
#include "lua_sdl.h"
#include "th_lua.h"
#include "xmi2mid.h"
#include <SDL_mixer.h>
#ifdef _MSC_VER
#pragma comment(lib, "SDL2_mixer")
#endif
#include <cstring>
#include <array>
class music
{
public:
Mix_Music* pMusic;
class music {
public:
Mix_Music* pMusic;
music()
{
pMusic = nullptr;
}
~music()
{
if(pMusic)
{
Mix_FreeMusic(pMusic);
pMusic = nullptr;
}
music() { pMusic = nullptr; }
~music() {
if (pMusic) {
Mix_FreeMusic(pMusic);
pMusic = nullptr;
}
}
};
namespace {
void audio_music_over_callback()
{
SDL_Event e;
e.type = SDL_USEREVENT_MUSIC_OVER;
SDL_PushEvent(&e);
void audio_music_over_callback() {
SDL_Event e;
e.type = SDL_USEREVENT_MUSIC_OVER;
SDL_PushEvent(&e);
}
int l_init(lua_State *L)
{
if(Mix_OpenAudio(static_cast<int>(luaL_optinteger(L, 1, MIX_DEFAULT_FREQUENCY)),
MIX_DEFAULT_FORMAT,
static_cast<int>(luaL_optinteger(L, 2, MIX_DEFAULT_CHANNELS)),
static_cast<int>(luaL_optinteger(L, 3, 2048)) /* chunk size */) != 0)
{
lua_pushboolean(L, 0);
lua_pushstring(L, Mix_GetError());
return 2;
}
else
{
lua_pushboolean(L, 1);
Mix_HookMusicFinished(audio_music_over_callback);
return 1;
}
int l_init(lua_State* L) {
constexpr int chunk_size = 2048;
int err = Mix_OpenAudio(
static_cast<int>(luaL_optinteger(L, 1, MIX_DEFAULT_FREQUENCY)),
MIX_DEFAULT_FORMAT,
static_cast<int>(luaL_optinteger(L, 2, MIX_DEFAULT_CHANNELS)),
static_cast<int>(luaL_optinteger(L, 3, chunk_size)));
if (err != 0) {
lua_pushboolean(L, 0);
lua_pushstring(L, Mix_GetError());
return 2;
} else {
lua_pushboolean(L, 1);
Mix_HookMusicFinished(audio_music_over_callback);
return 1;
}
}
struct load_music_async_data
{
lua_State* L;
Mix_Music* music;
SDL_RWops* rwop;
char* err;
SDL_Thread* thread;
struct load_music_async_data {
lua_State* L;
Mix_Music* music;
SDL_RWops* rwop;
char* err;
SDL_Thread* thread;
};
int load_music_async_thread(void* arg)
{
load_music_async_data *async = (load_music_async_data*)arg;
int load_music_async_thread(void* arg) {
load_music_async_data* async = (load_music_async_data*)arg;
async->music = Mix_LoadMUS_RW(async->rwop, 1);
async->rwop = nullptr;
if(async->music == nullptr)
{
size_t iLen = std::strlen(Mix_GetError()) + 1;
async->err = (char*)malloc(iLen);
std::memcpy(async->err, Mix_GetError(), iLen);
}
SDL_Event e;
e.type = SDL_USEREVENT_MUSIC_LOADED;
e.user.data1 = arg;
SDL_PushEvent(&e);
return 0;
async->music = Mix_LoadMUS_RW(async->rwop, 1);
async->rwop = nullptr;
if (async->music == nullptr) {
size_t iLen = std::strlen(Mix_GetError()) + 1;
async->err = (char*)malloc(iLen);
std::memcpy(async->err, Mix_GetError(), iLen);
}
SDL_Event e;
e.type = SDL_USEREVENT_MUSIC_LOADED;
e.user.data1 = arg;
SDL_PushEvent(&e);
return 0;
}
int l_load_music_async(lua_State *L)
{
size_t iLength;
const uint8_t *pData = luaT_checkfile(L, 1, &iLength);
luaL_checktype(L, 2, LUA_TFUNCTION);
SDL_RWops* rwop = SDL_RWFromConstMem(pData, (int)iLength);
lua_settop(L, 2);
int l_load_music_async(lua_State* L) {
size_t iLength;
const uint8_t* pData = luaT_checkfile(L, 1, &iLength);
luaL_checktype(L, 2, LUA_TFUNCTION);
SDL_RWops* rwop = SDL_RWFromConstMem(pData, (int)iLength);
lua_settop(L, 2);
load_music_async_data *async = luaT_new<load_music_async_data>(L);
lua_pushlightuserdata(L, async);
lua_pushvalue(L, -2);
lua_settable(L, LUA_REGISTRYINDEX);
async->L = L;
async->music = nullptr;
async->rwop = rwop;
async->err = nullptr;
lua_createtable(L, 2, 0);
lua_pushvalue(L, 2);
lua_rawseti(L, -2, 1);
luaT_stdnew<music>(L, luaT_environindex, true);
lua_pushvalue(L, 1);
luaT_setenvfield(L, -2, "data");
lua_rawseti(L, -2, 2);
lua_settable(L, LUA_REGISTRYINDEX);
load_music_async_data* async = luaT_new<load_music_async_data>(L);
lua_pushlightuserdata(L, async);
lua_pushvalue(L, -2);
lua_settable(L, LUA_REGISTRYINDEX);
async->L = L;
async->music = nullptr;
async->rwop = rwop;
async->err = nullptr;
lua_createtable(L, 2, 0);
lua_pushvalue(L, 2);
lua_rawseti(L, -2, 1);
luaT_stdnew<music>(L, luaT_environindex, true);
lua_pushvalue(L, 1);
luaT_setenvfield(L, -2, "data");
lua_rawseti(L, -2, 2);
lua_settable(L, LUA_REGISTRYINDEX);
/*
In registry:
[light userdata async] -> [full userdata async]
[full userdata async] -> {
[1] = callback_function,
[2] = empty music_t userdata,
}
New thread will load music, and inform the main loop, which will then
call the callback and remove the new entries from the registry.
*/
async->thread = SDL_CreateThread(load_music_async_thread, "music_thread", async);
return 0;
}
int l_load_music(lua_State *L)
{
size_t iLength;
const uint8_t *pData = luaT_checkfile(L, 1, &iLength);
SDL_RWops* rwop = SDL_RWFromConstMem(pData, (int)iLength);
Mix_Music* pMusic = Mix_LoadMUS_RW(rwop, 1);
if(pMusic == nullptr)
{
lua_pushnil(L);
lua_pushstring(L, Mix_GetError());
return 2;
}
music* pLMusic = luaT_stdnew<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);
/*
In registry:
[light userdata async] -> [full userdata async]
[full userdata async] -> {
[1] = callback_function,
[2] = empty music_t userdata,
}
}
else
{
lua_rawgeti(L, 2, 2);
music* pLMusic = (music*)lua_touserdata(L, -1);
pLMusic->pMusic = async->music;
async->music = nullptr;
}
// Finish cleanup
lua_pushvalue(L, 1);
New thread will load music, and inform the main loop, which will then
call the callback and remove the new entries from the registry.
*/
async->thread =
SDL_CreateThread(load_music_async_thread, "music_thread", async);
return 0;
}
int l_load_music(lua_State* L) {
size_t iLength;
const uint8_t* pData = luaT_checkfile(L, 1, &iLength);
SDL_RWops* rwop = SDL_RWFromConstMem(pData, (int)iLength);
Mix_Music* pMusic = Mix_LoadMUS_RW(rwop, 1);
if (pMusic == nullptr) {
lua_pushnil(L);
lua_settable(L, LUA_REGISTRYINDEX);
// Callback
lua_call(L, nargs, 0);
return 0;
lua_pushstring(L, Mix_GetError());
return 2;
}
music* pLMusic = luaT_stdnew<music>(L, luaT_environindex, true);
pLMusic->pMusic = pMusic;
lua_pushvalue(L, 1);
luaT_setenvfield(L, -2, "data");
return 1;
}
int luaopen_sdl_audio(lua_State *L)
{
lua_newtable(L);
luaT_setfuncs(L, sdl_audiolib.data());
lua_pushboolean(L, 1);
lua_setfield(L, -2, "loaded");
lua_createtable(L, 0, 2);
lua_pushvalue(L, -1);
lua_replace(L, luaT_environindex);
lua_pushvalue(L, luaT_environindex);
luaT_pushcclosure(L, luaT_stdgc<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;
int l_music_volume(lua_State* L) {
lua_Number fValue = luaL_checknumber(L, 1);
fValue = fValue * (lua_Number)MIX_MAX_VOLUME;
int iVolume = (int)(fValue + 0.5);
if (iVolume < 0) {
iVolume = 0;
} else if (iVolume > MIX_MAX_VOLUME) {
iVolume = MIX_MAX_VOLUME;
}
Mix_VolumeMusic(iVolume);
return 0;
}
#else // CORSIX_TH_USE_SDL_MIXER
int luaopen_sdl_audio(lua_State *L)
{
lua_newtable(L);
lua_pushboolean(L, 0);
lua_setfield(L, -2, "loaded");
return 1;
int l_play_music(lua_State* L) {
music* pLMusic = luaT_testuserdata<music>(L, -1);
int err = Mix_PlayMusic(pLMusic->pMusic,
static_cast<int>(luaL_optinteger(L, 2, 1)));
if (err != 0) {
lua_pushnil(L);
lua_pushstring(L, Mix_GetError());
return 2;
}
lua_pushboolean(L, 1);
return 1;
}
int l_load_music_async_callback(lua_State *L)
{
return 0;
int l_pause_music(lua_State* L) {
Mix_PauseMusic();
lua_pushboolean(L, Mix_PausedMusic() != 0 ? 1 : 0);
return 1;
}
#endif // CORSIX_TH_USE_SDL_MIXER
int l_resume_music(lua_State* L) {
Mix_ResumeMusic();
lua_pushboolean(L, Mix_PausedMusic() == 0 ? 1 : 0);
return 1;
}
int l_stop_music(lua_State* L) {
Mix_HaltMusic();
return 0;
}
int l_transcode_xmi(lua_State* L) {
size_t iLength, iMidLength;
const uint8_t* pData = luaT_checkfile(L, 1, &iLength);
uint8_t* pMidData = transcode_xmi_to_midi(pData, iLength, &iMidLength);
if (pMidData == nullptr) {
lua_pushnil(L);
lua_pushliteral(L, "Unable to transcode XMI to MIDI");
return 2;
}
lua_pushlstring(L, (const char*)pMidData, iMidLength);
delete[] pMidData;
return 1;
}
constexpr std::array<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 <array>
#include <cstdio>
#include <cstring>
#include "lua_sdl.h"
#include "th_lua.h"
#include <cstring>
#include <cstdio>
#include <array>
namespace {
int l_init(lua_State *L)
{
Uint32 flags = 0;
int i;
int argc = lua_gettop(L);
for(i = 1; i <= argc; ++i)
{
const char* s = luaL_checkstring(L, i);
if(std::strcmp(s, "video") == 0)
flags |= SDL_INIT_VIDEO;
else if(std::strcmp(s, "audio") == 0)
flags |= SDL_INIT_AUDIO;
else if(std::strcmp(s, "timer") == 0)
flags |= SDL_INIT_TIMER;
else if(std::strcmp(s, "*") == 0)
flags |= SDL_INIT_EVERYTHING;
else
luaL_argerror(L, i, "Expected SDL part name");
}
if(SDL_Init(flags) != 0)
{
std::fprintf(stderr, "SDL_Init failed: %s\n", SDL_GetError());
lua_pushboolean(L, 0);
return 1;
}
lua_pushboolean(L, 1);
int l_init(lua_State* L) {
Uint32 flags = 0;
int i;
int argc = lua_gettop(L);
for (i = 1; i <= argc; ++i) {
const char* s = luaL_checkstring(L, i);
if (std::strcmp(s, "video") == 0)
flags |= SDL_INIT_VIDEO;
else if (std::strcmp(s, "audio") == 0)
flags |= SDL_INIT_AUDIO;
else if (std::strcmp(s, "timer") == 0)
flags |= SDL_INIT_TIMER;
else if (std::strcmp(s, "*") == 0)
flags |= SDL_INIT_EVERYTHING;
else
luaL_argerror(L, i, "Expected SDL part name");
}
if (SDL_Init(flags) != 0) {
std::fprintf(stderr, "SDL_Init failed: %s\n", SDL_GetError());
lua_pushboolean(L, 0);
return 1;
}
lua_pushboolean(L, 1);
return 1;
}
Uint32 timer_frame_callback(Uint32 interval, void *param)
{
SDL_Event e;
e.type = SDL_USEREVENT_TICK;
SDL_PushEvent(&e);
return interval;
Uint32 timer_frame_callback(Uint32 interval, void* param) {
SDL_Event e;
e.type = SDL_USEREVENT_TICK;
SDL_PushEvent(&e);
return interval;
}
class fps_ctrl
{
public:
bool limit_fps;
bool track_fps;
class fps_ctrl {
public:
bool limit_fps;
bool track_fps;
int q_front;
int q_back;
int frame_count;
Uint32 frame_time[4096];
int q_front;
int q_back;
int frame_count;
std::array<Uint32, 4096> frame_time;
void init()
{
limit_fps = true;
track_fps = true;
q_front = 0;
q_back = 0;
frame_count = 0;
void init() {
limit_fps = true;
track_fps = true;
q_front = 0;
q_back = 0;
frame_count = 0;
}
void count_frame() {
Uint32 now = SDL_GetTicks();
frame_time[q_front] = now;
q_front = (q_front + 1) % frame_time.size();
if (q_front == q_back) {
q_back = (q_back + 1) % frame_time.size();
} else {
++frame_count;
}
void count_frame()
{
Uint32 now = SDL_GetTicks();
frame_time[q_front] = now;
q_front = (q_front + 1) % static_cast<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)));
}
if (now < 1000) {
now = 0;
} else {
now -= 1000;
}
while (frame_time[q_back] < now) {
--frame_count;
q_back = (q_back + 1) % frame_time.size();
}
}
};
void l_push_modifiers_table(lua_State *L, Uint16 mod)
{
lua_newtable(L);
if ((mod & KMOD_SHIFT) != 0)
{
luaT_pushtablebool(L, "shift", true);
}
if ((mod & KMOD_ALT) != 0)
{
luaT_pushtablebool(L, "alt", true);
}
if ((mod & KMOD_CTRL) != 0)
{
luaT_pushtablebool(L, "ctrl", true);
}
if ((mod & KMOD_GUI) != 0)
{
luaT_pushtablebool(L, "gui", true);
}
if ((mod & KMOD_NUM) != 0)
{
luaT_pushtablebool(L, "numlockactive", true);
}
void l_push_modifiers_table(lua_State* L, Uint16 mod) {
lua_newtable(L);
if ((mod & KMOD_SHIFT) != 0) {
luaT_pushtablebool(L, "shift", true);
}
if ((mod & KMOD_ALT) != 0) {
luaT_pushtablebool(L, "alt", true);
}
if ((mod & KMOD_CTRL) != 0) {
luaT_pushtablebool(L, "ctrl", true);
}
if ((mod & KMOD_GUI) != 0) {
luaT_pushtablebool(L, "gui", true);
}
if ((mod & KMOD_NUM) != 0) {
luaT_pushtablebool(L, "numlockactive", true);
}
}
int l_get_key_modifiers(lua_State *L)
{
l_push_modifiers_table(L, SDL_GetModState());
return 1;
int l_get_key_modifiers(lua_State* L) {
l_push_modifiers_table(L, SDL_GetModState());
return 1;
}
int l_mainloop(lua_State *L)
{
luaL_checktype(L, 1, LUA_TTHREAD);
lua_State *dispatcher = lua_tothread(L, 1);
int l_mainloop(lua_State* L) {
luaL_checktype(L, 1, LUA_TTHREAD);
lua_State* dispatcher = lua_tothread(L, 1);
fps_ctrl *fps_control = (fps_ctrl*)lua_touserdata(L, luaT_upvalueindex(1));
SDL_TimerID timer = SDL_AddTimer(30, timer_frame_callback, nullptr);
SDL_Event e;
fps_ctrl* fps_control = (fps_ctrl*)lua_touserdata(L, luaT_upvalueindex(1));
SDL_TimerID timer = SDL_AddTimer(30, timer_frame_callback, nullptr);
SDL_Event e;
while(SDL_WaitEvent(&e) != 0)
{
bool do_frame = false;
bool do_timer = false;
do
{
int nargs;
switch(e.type)
{
case SDL_QUIT:
goto leave_loop;
case SDL_KEYDOWN:
lua_pushliteral(dispatcher, "keydown");
lua_pushstring(dispatcher, SDL_GetKeyName(e.key.keysym.sym));
l_push_modifiers_table(dispatcher, e.key.keysym.mod);
lua_pushboolean(dispatcher, e.key.repeat != 0);
nargs = 4;
break;
case SDL_KEYUP:
lua_pushliteral(dispatcher, "keyup");
lua_pushstring(dispatcher, SDL_GetKeyName(e.key.keysym.sym));
nargs = 2;
break;
case SDL_TEXTINPUT:
lua_pushliteral(dispatcher, "textinput");
lua_pushstring(dispatcher, e.text.text);
nargs = 2;
break;
case SDL_TEXTEDITING:
lua_pushliteral(dispatcher, "textediting");
lua_pushstring(dispatcher, e.edit.text);
lua_pushinteger(dispatcher, e.edit.start);
lua_pushinteger(dispatcher, e.edit.length);
nargs = 4;
break;
case SDL_MOUSEBUTTONDOWN:
lua_pushliteral(dispatcher, "buttondown");
lua_pushinteger(dispatcher, e.button.button);
lua_pushinteger(dispatcher, e.button.x);
lua_pushinteger(dispatcher, e.button.y);
nargs = 4;
break;
case SDL_MOUSEBUTTONUP:
lua_pushliteral(dispatcher, "buttonup");
lua_pushinteger(dispatcher, e.button.button);
lua_pushinteger(dispatcher, e.button.x);
lua_pushinteger(dispatcher, e.button.y);
nargs = 4;
break;
case SDL_MOUSEWHEEL:
lua_pushliteral(dispatcher, "mousewheel");
lua_pushinteger(dispatcher, e.wheel.x);
lua_pushinteger(dispatcher, e.wheel.y);
nargs = 3;
break;
case SDL_MOUSEMOTION:
lua_pushliteral(dispatcher, "motion");
lua_pushinteger(dispatcher, e.motion.x);
lua_pushinteger(dispatcher, e.motion.y);
lua_pushinteger(dispatcher, e.motion.xrel);
lua_pushinteger(dispatcher, e.motion.yrel);
nargs = 5;
break;
case SDL_MULTIGESTURE:
lua_pushliteral(dispatcher, "multigesture");
lua_pushinteger(dispatcher, e.mgesture.numFingers);
lua_pushnumber(dispatcher, e.mgesture.dTheta);
lua_pushnumber(dispatcher, e.mgesture.dDist);
lua_pushnumber(dispatcher, e.mgesture.x);
lua_pushnumber(dispatcher, e.mgesture.y);
nargs = 6;
break;
case SDL_WINDOWEVENT:
switch (e.window.event) {
case SDL_WINDOWEVENT_FOCUS_GAINED:
lua_pushliteral(dispatcher, "active");
lua_pushinteger(dispatcher, 1);
nargs = 2;
break;
case SDL_WINDOWEVENT_FOCUS_LOST:
lua_pushliteral(dispatcher, "active");
lua_pushinteger(dispatcher, 0);
nargs = 2;
break;
case SDL_WINDOWEVENT_SIZE_CHANGED:
lua_pushliteral(dispatcher, "window_resize");
lua_pushinteger(dispatcher, e.window.data1);
lua_pushinteger(dispatcher, e.window.data2);
nargs = 3;
break;
default:
nargs = 0;
break;
}
break;
case SDL_USEREVENT_MUSIC_OVER:
lua_pushliteral(dispatcher, "music_over");
nargs = 1;
break;
case SDL_USEREVENT_MUSIC_LOADED:
if(luaT_cpcall(L, (lua_CFunction)l_load_music_async_callback, e.user.data1))
{
SDL_RemoveTimer(timer);
lua_pushliteral(L, "callback");
return 2;
}
nargs = 0;
break;
case SDL_USEREVENT_TICK:
do_timer = true;
nargs = 0;
break;
case SDL_USEREVENT_MOVIE_OVER:
lua_pushliteral(dispatcher, "movie_over");
nargs = 1;
break;
case SDL_USEREVENT_SOUND_OVER:
lua_pushliteral(dispatcher, "sound_over");
lua_pushinteger(dispatcher, *(static_cast<int*>(e.user.data1)));
nargs = 2;
break;
while (SDL_WaitEvent(&e) != 0) {
bool do_frame = false;
bool do_timer = false;
do {
int nargs;
switch (e.type) {
case SDL_QUIT:
goto leave_loop;
case SDL_KEYDOWN:
lua_pushliteral(dispatcher, "keydown");
lua_pushstring(dispatcher, SDL_GetKeyName(e.key.keysym.sym));
l_push_modifiers_table(dispatcher, e.key.keysym.mod);
lua_pushboolean(dispatcher, e.key.repeat != 0);
nargs = 4;
break;
case SDL_KEYUP:
lua_pushliteral(dispatcher, "keyup");
lua_pushstring(dispatcher, SDL_GetKeyName(e.key.keysym.sym));
nargs = 2;
break;
case SDL_TEXTINPUT:
lua_pushliteral(dispatcher, "textinput");
lua_pushstring(dispatcher, e.text.text);
nargs = 2;
break;
case SDL_TEXTEDITING:
lua_pushliteral(dispatcher, "textediting");
lua_pushstring(dispatcher, e.edit.text);
lua_pushinteger(dispatcher, e.edit.start);
lua_pushinteger(dispatcher, e.edit.length);
nargs = 4;
break;
case SDL_MOUSEBUTTONDOWN:
lua_pushliteral(dispatcher, "buttondown");
lua_pushinteger(dispatcher, e.button.button);
lua_pushinteger(dispatcher, e.button.x);
lua_pushinteger(dispatcher, e.button.y);
nargs = 4;
break;
case SDL_MOUSEBUTTONUP:
lua_pushliteral(dispatcher, "buttonup");
lua_pushinteger(dispatcher, e.button.button);
lua_pushinteger(dispatcher, e.button.x);
lua_pushinteger(dispatcher, e.button.y);
nargs = 4;
break;
case SDL_MOUSEWHEEL:
lua_pushliteral(dispatcher, "mousewheel");
lua_pushinteger(dispatcher, e.wheel.x);
lua_pushinteger(dispatcher, e.wheel.y);
nargs = 3;
break;
case SDL_MOUSEMOTION:
lua_pushliteral(dispatcher, "motion");
lua_pushinteger(dispatcher, e.motion.x);
lua_pushinteger(dispatcher, e.motion.y);
lua_pushinteger(dispatcher, e.motion.xrel);
lua_pushinteger(dispatcher, e.motion.yrel);
nargs = 5;
break;
case SDL_MULTIGESTURE:
lua_pushliteral(dispatcher, "multigesture");
lua_pushinteger(dispatcher, e.mgesture.numFingers);
lua_pushnumber(dispatcher, e.mgesture.dTheta);
lua_pushnumber(dispatcher, e.mgesture.dDist);
lua_pushnumber(dispatcher, e.mgesture.x);
lua_pushnumber(dispatcher, e.mgesture.y);
nargs = 6;
break;
case SDL_WINDOWEVENT:
switch (e.window.event) {
case SDL_WINDOWEVENT_FOCUS_GAINED:
lua_pushliteral(dispatcher, "active");
lua_pushinteger(dispatcher, 1);
nargs = 2;
break;
case SDL_WINDOWEVENT_FOCUS_LOST:
lua_pushliteral(dispatcher, "active");
lua_pushinteger(dispatcher, 0);
nargs = 2;
break;
case SDL_WINDOWEVENT_SIZE_CHANGED:
lua_pushliteral(dispatcher, "window_resize");
lua_pushinteger(dispatcher, e.window.data1);
lua_pushinteger(dispatcher, e.window.data2);
nargs = 3;
break;
default:
nargs = 0;
break;
}
if(nargs != 0)
{
if(luaT_resume(dispatcher, dispatcher, nargs) != LUA_YIELD)
{
goto leave_loop;
}
do_frame = do_frame || (lua_toboolean(dispatcher, 1) != 0);
lua_settop(dispatcher, 0);
}
} while(SDL_PollEvent(&e) != 0);
if(do_timer)
{
lua_pushliteral(dispatcher, "timer");
if(luaT_resume(dispatcher, dispatcher, 1) != LUA_YIELD)
{
break;
}
do_frame = do_frame || (lua_toboolean(dispatcher, 1) != 0);
lua_settop(dispatcher, 0);
nargs = 0;
break;
}
break;
case SDL_USEREVENT_MUSIC_OVER:
lua_pushliteral(dispatcher, "music_over");
nargs = 1;
break;
case SDL_USEREVENT_MUSIC_LOADED:
if (luaT_cpcall(L, (lua_CFunction)l_load_music_async_callback,
e.user.data1)) {
SDL_RemoveTimer(timer);
lua_pushliteral(L, "callback");
return 2;
}
nargs = 0;
break;
case SDL_USEREVENT_TICK:
do_timer = true;
nargs = 0;
break;
case SDL_USEREVENT_MOVIE_OVER:
lua_pushliteral(dispatcher, "movie_over");
nargs = 1;
break;
case SDL_USEREVENT_SOUND_OVER:
lua_pushliteral(dispatcher, "sound_over");
lua_pushinteger(dispatcher, *(static_cast<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
{
if(fps_control->track_fps)
{
fps_control->count_frame();
}
lua_pushliteral(dispatcher, "frame");
if(luaT_resume(dispatcher, dispatcher, 1) != LUA_YIELD)
{
goto leave_loop;
}
lua_settop(dispatcher, 0);
} while(fps_control->limit_fps == false && SDL_PollEvent(nullptr) == 0);
}
// No events pending - a good time to do a bit of garbage collection
lua_gc(L, LUA_GCSTEP, 2);
do_frame = do_frame || (lua_toboolean(dispatcher, 1) != 0);
lua_settop(dispatcher, 0);
}
} while (SDL_PollEvent(&e) != 0);
if (do_timer) {
lua_pushliteral(dispatcher, "timer");
if (luaT_resume(dispatcher, dispatcher, 1) != LUA_YIELD) {
break;
}
do_frame = do_frame || (lua_toboolean(dispatcher, 1) != 0);
lua_settop(dispatcher, 0);
}
if (do_frame || !fps_control->limit_fps) {
do {
if (fps_control->track_fps) {
fps_control->count_frame();
}
lua_pushliteral(dispatcher, "frame");
if (luaT_resume(dispatcher, dispatcher, 1) != LUA_YIELD) {
goto leave_loop;
}
lua_settop(dispatcher, 0);
} while (fps_control->limit_fps == false && SDL_PollEvent(nullptr) == 0);
}
// No events pending - a good time to do a bit of garbage collection
lua_gc(L, LUA_GCSTEP, 2);
}
leave_loop:
SDL_RemoveTimer(timer);
int n = lua_gettop(dispatcher);
if(lua_status(dispatcher) >= LUA_ERRRUN)
{
n = 1;
}
lua_checkstack(L, n);
lua_xmove(dispatcher, L, n);
return n;
SDL_RemoveTimer(timer);
int n = lua_gettop(dispatcher);
if (lua_status(dispatcher) >= LUA_ERRRUN) {
n = 1;
}
lua_checkstack(L, n);
lua_xmove(dispatcher, L, n);
return n;
}
int l_track_fps(lua_State *L)
{
fps_ctrl *ctrl = (fps_ctrl*)lua_touserdata(L, luaT_upvalueindex(1));
ctrl->track_fps = lua_isnone(L, 1) ? true : (lua_toboolean(L, 1) != 0);
return 0;
int l_track_fps(lua_State* L) {
fps_ctrl* ctrl = (fps_ctrl*)lua_touserdata(L, luaT_upvalueindex(1));
ctrl->track_fps = lua_isnone(L, 1) ? true : (lua_toboolean(L, 1) != 0);
return 0;
}
int l_limit_fps(lua_State *L)
{
fps_ctrl *ctrl = (fps_ctrl*)lua_touserdata(L, luaT_upvalueindex(1));
ctrl->limit_fps = lua_isnone(L, 1) ? true : (lua_toboolean(L, 1) != 0);
return 0;
int l_limit_fps(lua_State* L) {
fps_ctrl* ctrl = (fps_ctrl*)lua_touserdata(L, luaT_upvalueindex(1));
ctrl->limit_fps = lua_isnone(L, 1) ? true : (lua_toboolean(L, 1) != 0);
return 0;
}
int l_get_fps(lua_State *L)
{
fps_ctrl *ctrl = (fps_ctrl*)lua_touserdata(L, luaT_upvalueindex(1));
if(ctrl->track_fps)
{
lua_pushinteger(L, ctrl->frame_count);
}
else
{
lua_pushnil(L);
}
return 1;
int l_get_fps(lua_State* L) {
fps_ctrl* ctrl = (fps_ctrl*)lua_touserdata(L, luaT_upvalueindex(1));
if (ctrl->track_fps) {
lua_pushinteger(L, ctrl->frame_count);
} else {
lua_pushnil(L);
}
return 1;
}
int l_get_ticks(lua_State *L)
{
lua_pushinteger(L, (lua_Integer)SDL_GetTicks());
return 1;
int l_get_ticks(lua_State* L) {
lua_pushinteger(L, (lua_Integer)SDL_GetTicks());
return 1;
}
constexpr std::array<luaL_Reg, 4> sdllib {{
{"init", l_init},
{"getTicks", l_get_ticks},
{"getKeyModifiers", l_get_key_modifiers},
{nullptr, nullptr}
}};
constexpr std::array<luaL_Reg, 4> sdllib{
{{"init", l_init},
{"getTicks", l_get_ticks},
{"getKeyModifiers", l_get_key_modifiers},
{nullptr, nullptr}}};
constexpr std::array<luaL_Reg, 5> sdllib_with_upvalue {{
{"mainloop", l_mainloop},
{"getFPS", l_get_fps},
{"trackFPS", l_track_fps},
{"limitFPS", l_limit_fps},
{nullptr, nullptr}
}};
constexpr std::array<luaL_Reg, 5> sdllib_with_upvalue{
{{"mainloop", l_mainloop},
{"getFPS", l_get_fps},
{"trackFPS", l_track_fps},
{"limitFPS", l_limit_fps},
{nullptr, nullptr}}};
inline void load_extra(lua_State *L, const char *name, lua_CFunction fn)
{
luaT_pushcfunction(L, fn);
lua_call(L, 0, 1);
lua_setfield(L, -2, name);
inline void load_extra(lua_State* L, const char* name, lua_CFunction fn) {
luaT_pushcfunction(L, fn);
lua_call(L, 0, 1);
lua_setfield(L, -2, name);
}
} // namespace
} // namespace
int luaopen_sdl_audio(lua_State *L);
int luaopen_sdl_wm(lua_State *L);
int luaopen_sdl_audio(lua_State* L);
int luaopen_sdl_wm(lua_State* L);
int luaopen_sdl(lua_State *L)
{
fps_ctrl* ctrl = (fps_ctrl*)lua_newuserdata(L, sizeof(fps_ctrl));
ctrl->init();
luaT_register(L, "sdl", sdllib);
for (auto reg = sdllib_with_upvalue.begin(); reg->name; ++reg)
{
lua_pushvalue(L, -2);
luaT_pushcclosure(L, reg->func, 1);
lua_setfield(L, -2, reg->name);
}
int luaopen_sdl(lua_State* L) {
fps_ctrl* ctrl = (fps_ctrl*)lua_newuserdata(L, sizeof(fps_ctrl));
ctrl->init();
luaT_register(L, "sdl", sdllib);
for (auto reg = sdllib_with_upvalue.begin(); reg->name; ++reg) {
lua_pushvalue(L, -2);
luaT_pushcclosure(L, reg->func, 1);
lua_setfield(L, -2, reg->name);
}
load_extra(L, "audio", luaopen_sdl_audio);
load_extra(L, "wm", luaopen_sdl_wm);
load_extra(L, "audio", luaopen_sdl_audio);
load_extra(L, "wm", luaopen_sdl_wm);
return 1;
return 1;
}

View File

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

View File

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

View File

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

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

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.
*/
#include <cstdio>
#include <cstring>
#include <stdexcept>
#include "bootstrap.h"
#include "th.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_gfx(const lua_register_state *pState);
void lua_register_map(const lua_register_state *pState);
void lua_register_sound(const lua_register_state *pState);
void lua_register_movie(const lua_register_state *pState);
void lua_register_strings(const lua_register_state *pState);
void lua_register_ui(const lua_register_state *pState);
void lua_register_lfs_ext(const lua_register_state *pState);
void lua_register_iso_fs(const lua_register_state *pState);
void lua_register_anims(const lua_register_state* pState);
void lua_register_gfx(const lua_register_state* pState);
void lua_register_map(const lua_register_state* pState);
void lua_register_sound(const lua_register_state* pState);
void lua_register_movie(const lua_register_state* pState);
void lua_register_strings(const lua_register_state* pState);
void lua_register_ui(const lua_register_state* pState);
void lua_register_lfs_ext(const lua_register_state* pState);
void lua_register_iso_fs(const lua_register_state* pState);
//! Set a field on the environment table of an object
void luaT_setenvfield(lua_State *L, int index, const char *k)
{
lua_getfenv(L, index);
lua_pushstring(L, k);
lua_pushvalue(L, -3);
lua_settable(L, -3);
lua_pop(L, 2);
void luaT_setenvfield(lua_State* L, int index, const char* k) {
lua_getfenv(L, index);
lua_pushstring(L, k);
lua_pushvalue(L, -3);
lua_settable(L, -3);
lua_pop(L, 2);
}
//! Get a field from the environment table of an object
void luaT_getenvfield(lua_State *L, int index, const char *k)
{
lua_getfenv(L, index);
lua_getfield(L, -1, k);
lua_replace(L, -2);
void luaT_getenvfield(lua_State* L, int index, const char* k) {
lua_getfenv(L, index);
lua_getfield(L, -1, k);
lua_replace(L, -2);
}
#if LUA_VERSION_NUM >= 502
void luaT_getfenv52(lua_State *L, int iIndex)
{
int iType = lua_type(L, iIndex);
switch(iType)
{
void luaT_getfenv52(lua_State* L, int iIndex) {
int iType = lua_type(L, iIndex);
switch (iType) {
case LUA_TUSERDATA:
lua_getuservalue(L, iIndex);
break;
lua_getuservalue(L, iIndex);
break;
case LUA_TFUNCTION:
if(lua_iscfunction(L, iIndex))
{
// Our convention: upvalue at #1 is environment
if(lua_getupvalue(L, iIndex, 1) == nullptr)
lua_pushglobaltable(L);
if (lua_iscfunction(L, iIndex)) {
// Our convention: upvalue at #1 is environment
if (lua_getupvalue(L, iIndex, 1) == nullptr) lua_pushglobaltable(L);
} else {
// Language convention: upvalue called _ENV is environment
const char* sUpName = nullptr;
for (int i = 1; (sUpName = lua_getupvalue(L, iIndex, i)); ++i) {
if (std::strcmp(sUpName, "_ENV") == 0)
return;
else
lua_pop(L, 1);
}
else
{
// Language convention: upvalue called _ENV is environment
const char* sUpName = nullptr;
for(int i = 1; (sUpName = lua_getupvalue(L, iIndex, i)) ; ++i)
{
if(std::strcmp(sUpName, "_ENV") == 0)
return;
else
lua_pop(L, 1);
}
lua_pushglobaltable(L);
}
break;
lua_pushglobaltable(L);
}
break;
default:
luaL_error(L, "Unable to get environment of a %s in 5.2", lua_typename(L, iType));
break;
}
luaL_error(L, "Unable to get environment of a %s in 5.2",
lua_typename(L, iType));
break;
}
}
int luaT_setfenv52(lua_State *L, int iIndex)
{
int iType = lua_type(L, iIndex);
switch(iType)
{
int luaT_setfenv52(lua_State* L, int iIndex) {
int iType = lua_type(L, iIndex);
switch (iType) {
case LUA_TUSERDATA:
lua_setuservalue(L, iIndex);
return 1;
lua_setuservalue(L, iIndex);
return 1;
case LUA_TFUNCTION:
if(lua_iscfunction(L, iIndex))
{
// Our convention: upvalue at #1 is environment
if(lua_setupvalue(L, iIndex, 1) == nullptr)
{
lua_pop(L, 1);
return 0;
}
return 1;
if (lua_iscfunction(L, iIndex)) {
// Our convention: upvalue at #1 is environment
if (lua_setupvalue(L, iIndex, 1) == nullptr) {
lua_pop(L, 1);
return 0;
}
else
{
// Language convention: upvalue called _ENV is environment, which
// might be shared with other functions.
const char* sUpName = nullptr;
for(int i = 1; (sUpName = lua_getupvalue(L, iIndex, i)) ; ++i)
{
lua_pop(L, 1); // lua_getupvalue puts the value on the stack, but we just want to replace it
if(std::strcmp(sUpName, "_ENV") == 0)
{
luaL_loadstring(L, "local upv = ... return function() return upv end");
lua_insert(L, -2);
lua_call(L, 1, 1);
lua_upvaluejoin(L, iIndex, i, -1, 1);
lua_pop(L, 1);
return 1;
}
}
return 1;
} else {
// Language convention: upvalue called _ENV is environment,
// which might be shared with other functions.
const char* sUpName = nullptr;
for (int i = 1; (sUpName = lua_getupvalue(L, iIndex, i)); ++i) {
lua_pop(L, 1); // lua_getupvalue puts the value on the
// stack, but we just want to replace it
if (std::strcmp(sUpName, "_ENV") == 0) {
luaL_loadstring(L,
"local upv = ... return function() return upv "
"end");
lua_insert(L, -2);
lua_call(L, 1, 1);
lua_upvaluejoin(L, iIndex, i, -1, 1);
lua_pop(L, 1);
return 0;
return 1;
}
}
default:
lua_pop(L, 1);
return 0;
}
}
default:
return 0;
}
}
#endif
//! Push a C closure as a callable table
void luaT_pushcclosuretable(lua_State *L, lua_CFunction fn, int n)
{
luaT_pushcclosure(L, fn, n); // .. fn <top
lua_createtable(L, 0, 1); // .. fn mt <top
lua_pushliteral(L, "__call"); // .. fn mt __call <top
lua_pushvalue(L, -3); // .. fn mt __call fn <top
lua_settable(L, -3); // .. fn mt <top
lua_newtable(L); // .. fn mt t <top
lua_replace(L, -3); // .. t mt <top
lua_setmetatable(L, -2); // .. t <top
void luaT_pushcclosuretable(lua_State* L, lua_CFunction fn, int n) {
luaT_pushcclosure(L, fn, n); // .. fn <top
lua_createtable(L, 0, 1); // .. fn mt <top
lua_pushliteral(L, "__call"); // .. fn mt __call <top
lua_pushvalue(L, -3); // .. fn mt __call fn <top
lua_settable(L, -3); // .. fn mt <top
lua_newtable(L); // .. fn mt t <top
lua_replace(L, -3); // .. t mt <top
lua_setmetatable(L, -2); // .. t <top
}
//! Check for a string or userdata
const uint8_t* luaT_checkfile(lua_State *L, int idx, size_t* pDataLen)
{
const uint8_t *pData;
size_t iLength;
if(lua_type(L, idx) == LUA_TUSERDATA)
{
pData = reinterpret_cast<const uint8_t*>(lua_touserdata(L, idx));
iLength = lua_objlen(L, idx);
}
else
{
pData = reinterpret_cast<const uint8_t*>(luaL_checklstring(L, idx, &iLength));
}
if(pDataLen != 0)
*pDataLen = iLength;
return pData;
const uint8_t* luaT_checkfile(lua_State* L, int idx, size_t* pDataLen) {
const uint8_t* pData;
size_t iLength;
if (lua_type(L, idx) == LUA_TUSERDATA) {
pData = reinterpret_cast<const uint8_t*>(lua_touserdata(L, idx));
iLength = lua_objlen(L, idx);
} else {
pData =
reinterpret_cast<const uint8_t*>(luaL_checklstring(L, idx, &iLength));
}
if (pDataLen != 0) *pDataLen = iLength;
return pData;
}
namespace {
int l_load_strings(lua_State *L)
{
size_t iDataLength;
const uint8_t* pData = luaT_checkfile(L, 1, &iDataLength);
int l_load_strings(lua_State* L) {
size_t iDataLength;
const uint8_t* pData = luaT_checkfile(L, 1, &iDataLength);
try
{
th_string_list oStrings(pData, iDataLength);
lua_settop(L, 0);
lua_createtable(L, static_cast<int>(oStrings.get_section_count()), 0);
for(size_t iSec = 0; iSec < oStrings.get_section_count(); ++iSec)
{
size_t iCount = oStrings.get_section_size(iSec);
lua_createtable(L, static_cast<int>(iCount), 0);
for(size_t iStr = 0; iStr < iCount; ++iStr)
{
lua_pushstring(L, oStrings.get_string(iSec, iStr));
lua_rawseti(L, 2, static_cast<int>(iStr + 1));
}
lua_rawseti(L, 1, static_cast<int>(iSec + 1));
}
try {
th_string_list oStrings(pData, iDataLength);
lua_settop(L, 0);
lua_createtable(L, static_cast<int>(oStrings.get_section_count()), 0);
for (size_t iSec = 0; iSec < oStrings.get_section_count(); ++iSec) {
size_t iCount = oStrings.get_section_size(iSec);
lua_createtable(L, static_cast<int>(iCount), 0);
for (size_t iStr = 0; iStr < iCount; ++iStr) {
lua_pushstring(L, oStrings.get_string(iSec, iStr));
lua_rawseti(L, 2, static_cast<int>(iStr + 1));
}
lua_rawseti(L, 1, static_cast<int>(iSec + 1));
}
catch(std::invalid_argument)
{
lua_pushboolean(L, 0);
}
return 1;
} catch (std::invalid_argument) {
lua_pushboolean(L, 0);
}
return 1;
}
int get_api_version()
{
int get_api_version() {
#include "../Lua/api_version.lua"
}
int l_get_compile_options(lua_State *L)
{
lua_settop(L, 0);
lua_newtable(L);
int l_get_compile_options(lua_State* L) {
lua_settop(L, 0);
lua_newtable(L);
#ifdef CORSIX_TH_64BIT
lua_pushboolean(L, 1);
lua_pushboolean(L, 1);
#else
lua_pushboolean(L, 0);
lua_pushboolean(L, 0);
#endif
lua_setfield(L, -2, "arch_64");
lua_setfield(L, -2, "arch_64");
lua_pushliteral(L, "SDL");
lua_setfield(L, -2, "renderer");
lua_pushliteral(L, "SDL");
lua_setfield(L, -2, "renderer");
#ifdef CORSIX_TH_USE_SDL_MIXER
lua_pushboolean(L, 1);
lua_pushboolean(L, 1);
#else
lua_pushboolean(L, 0);
lua_pushboolean(L, 0);
#endif
lua_setfield(L, -2, "audio");
lua_setfield(L, -2, "audio");
lua_getfield(L, LUA_REGISTRYINDEX, "_LOADED");
lua_getfield(L, -1, "jit");
if(lua_type(L, -1) == LUA_TNIL)
{
lua_replace(L, -2);
}
else
{
lua_getfield(L, -1, "version");
lua_replace(L, -3);
lua_pop(L, 1);
}
lua_setfield(L, -2, "jit");
lua_pushinteger(L, get_api_version());
lua_setfield(L, -2, "api_version");
return 1;
}
} // namespace
void luaT_setclosure(const lua_register_state *pState, lua_CFunction fn, size_t iUps) {
luaT_pushcclosure(pState->L, fn, iUps);
}
int luaopen_th(lua_State *L)
{
lua_settop(L, 0);
lua_checkstack(L, 16 + static_cast<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_getfield(L, LUA_REGISTRYINDEX, "_LOADED");
lua_getfield(L, -1, "jit");
if (lua_type(L, -1) == LUA_TNIL) {
lua_replace(L, -2);
} else {
lua_getfield(L, -1, "version");
lua_replace(L, -3);
lua_pop(L, 1);
}
lua_setfield(L, -2, "jit");
lua_pushinteger(L, get_api_version());
lua_setfield(L, -2, "api_version");
return 1;
}
void luaT_execute(lua_State *L, const char* sLuaString)
{
luaT_execute_loadstring(L, sLuaString);
lua_call(L, 0, LUA_MULTRET);
} // namespace
void luaT_setclosure(const lua_register_state* pState, lua_CFunction fn,
size_t iUps) {
luaT_pushcclosure(pState->L, fn, iUps);
}
void luaT_push(lua_State *L, lua_CFunction f)
{
luaT_pushcfunction(L, f);
int luaopen_th(lua_State* L) {
lua_settop(L, 0);
lua_checkstack(L, 16 + static_cast<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)
{
lua_pushinteger(L, (lua_Integer)i);
}
void luaT_push(lua_State *L, const char* s)
{
lua_pushstring(L, s);
}
void luaT_pushtablebool(lua_State *L, const char *k, bool v)
{
lua_pushstring(L, k);
lua_pushboolean(L, v);
lua_settable(L, -3);
}
void luaT_printstack(lua_State* L)
{
int i;
int top = lua_gettop(L);
std::printf("total items in stack %d\n", top);
for (i = 1; i <= top; i++)
{ /* repeat for each level */
int t = lua_type(L, i);
switch (t) {
case LUA_TSTRING: /* strings */
std::printf("string: '%s'\n", lua_tostring(L, i));
break;
case LUA_TBOOLEAN: /* booleans */
std::printf("boolean %s\n", lua_toboolean(L, i) ? "true" : "false");
break;
case LUA_TNUMBER: /* numbers */
std::printf("number: %g\n", lua_tonumber(L, i));
break;
default: /* other values */
std::printf("%s\n", lua_typename(L, t));
break;
}
std::printf(" "); /* put a separator */
void luaT_execute_loadstring(lua_State* L, const char* sLuaString) {
static const int iRegistryCacheIndex = 7;
lua_rawgeti(L, LUA_REGISTRYINDEX, iRegistryCacheIndex);
if (lua_isnil(L, -1)) {
// Cache not yet created - create it.
lua_pop(L, 1);
lua_getglobal(L, "setmetatable");
if (lua_isnil(L, -1)) {
// Base library not yet loaded - fallback to simple
// uncached loadstring
lua_pop(L, 1);
if (luaL_loadstring(L, sLuaString)) lua_error(L);
}
std::printf("\n"); /* end the listing */
lua_pop(L, 1);
#if LUA_VERSION_NUM >= 502
luaL_loadstring(L,
"local assert, load = assert, load\n"
"return setmetatable({}, {__mode = [[v]], \n"
"__index = function(t, k)\n"
"local v = assert(load(k))\n"
"t[k] = v\n"
"return v\n"
"end})");
#else
luaL_loadstring(L,
"local assert, loadstring = assert, loadstring\n"
"return setmetatable({}, {__mode = [[v]], \n"
"__index = function(t, k)\n"
"local v = assert(loadstring(k))\n"
"t[k] = v\n"
"return v\n"
"end})");
#endif
lua_call(L, 0, 1);
lua_pushvalue(L, -1);
lua_rawseti(L, LUA_REGISTRYINDEX, iRegistryCacheIndex);
}
lua_getfield(L, -1, sLuaString);
lua_replace(L, -2);
}
void luaT_printrawtable(lua_State* L, int idx)
{
int i;
int len = static_cast<int>(lua_objlen(L, idx));
void luaT_execute(lua_State* L, const char* sLuaString) {
luaT_execute_loadstring(L, sLuaString);
lua_call(L, 0, LUA_MULTRET);
}
std::printf("total items in table %d\n", len);
void luaT_push(lua_State* L, lua_CFunction f) { luaT_pushcfunction(L, f); }
for (i = 1; i <= len; i++)
{
lua_rawgeti(L, idx, i);
int t = lua_type(L, -1);
switch (t) {
case LUA_TSTRING: /* strings */
std::printf("string: '%s'\n", lua_tostring(L, -1));
break;
case LUA_TBOOLEAN: /* booleans */
std::printf("boolean %s\n", lua_toboolean(L, -1) ? "true" : "false");
break;
case LUA_TNUMBER: /* numbers */
std::printf("number: %g\n", lua_tonumber(L, -1));
break;
default: /* other values */
std::printf("%s\n", lua_typename(L, t));
break;
}
std::printf(" "); /* put a separator */
lua_pop(L, 1);
void luaT_push(lua_State* L, int i) { lua_pushinteger(L, (lua_Integer)i); }
void luaT_push(lua_State* L, const char* s) { lua_pushstring(L, s); }
void luaT_pushtablebool(lua_State* L, const char* k, bool v) {
lua_pushstring(L, k);
lua_pushboolean(L, v);
lua_settable(L, -3);
}
void luaT_printstack(lua_State* L) {
int i;
int top = lua_gettop(L);
std::printf("total items in stack %d\n", top);
for (i = 1; i <= top; i++) { /* repeat for each level */
int t = lua_type(L, i);
switch (t) {
case LUA_TSTRING: /* strings */
std::printf("string: '%s'\n", lua_tostring(L, i));
break;
case LUA_TBOOLEAN: /* booleans */
std::printf("boolean %s\n", lua_toboolean(L, i) ? "true" : "false");
break;
case LUA_TNUMBER: /* numbers */
std::printf("number: %g\n", lua_tonumber(L, i));
break;
default: /* other values */
std::printf("%s\n", lua_typename(L, t));
break;
}
std::printf("\n"); /* end the listing */
std::printf(" "); /* put a separator */
}
std::printf("\n"); /* end the listing */
}
void luaT_printrawtable(lua_State* L, int idx) {
int i;
int len = static_cast<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_
#define CORSIX_TH_TH_LUA_H_
#include "config.h"
#include "lua.hpp"
#include <cstdio>
#include <new>
#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
#if LUA_VERSION_NUM >= 502
@@ -37,85 +37,78 @@ const int luaT_environindex = lua_upvalueindex(1);
const int luaT_environindex = LUA_ENVIRONINDEX;
#endif
inline int luaT_upvalueindex(int i)
{
inline int luaT_upvalueindex(int i) {
#if LUA_VERSION_NUM >= 502
return lua_upvalueindex(i + 1);
return lua_upvalueindex(i + 1);
#else
return lua_upvalueindex(i);
return lua_upvalueindex(i);
#endif
}
template<class Collection>
inline void luaT_register(lua_State *L, const char *n, Collection &l)
{
template <class Collection>
inline void luaT_register(lua_State* L, const char* n, Collection& l) {
#if LUA_VERSION_NUM >= 502
lua_createtable(L, 0, static_cast<int>(l.size()));
lua_pushvalue(L, luaT_environindex);
luaL_setfuncs(L, l.data(), 1);
lua_pushvalue(L, -1);
lua_setglobal(L, n);
lua_createtable(L, 0, static_cast<int>(l.size()));
lua_pushvalue(L, luaT_environindex);
luaL_setfuncs(L, l.data(), 1);
lua_pushvalue(L, -1);
lua_setglobal(L, n);
#else
luaL_register(L, n, l.data());
luaL_register(L, n, l.data());
#endif
}
inline void luaT_setfuncs(lua_State *L, const luaL_Reg *R)
{
inline void luaT_setfuncs(lua_State* L, const luaL_Reg* R) {
#if LUA_VERSION_NUM >= 502
lua_pushvalue(L, luaT_environindex);
luaL_setfuncs(L, R, 1);
lua_pushvalue(L, luaT_environindex);
luaL_setfuncs(L, R, 1);
#else
luaL_register(L, nullptr, R);
luaL_register(L, nullptr, R);
#endif
}
inline void luaT_pushcclosure(lua_State* L, lua_CFunction f, int nups)
{
inline void luaT_pushcclosure(lua_State* L, lua_CFunction f, int nups) {
#if LUA_VERSION_NUM >= 502
++nups;
lua_pushvalue(L, luaT_environindex);
lua_insert(L, -nups);
lua_pushcclosure(L, f, nups);
++nups;
lua_pushvalue(L, luaT_environindex);
lua_insert(L, -nups);
lua_pushcclosure(L, f, nups);
#else
lua_pushcclosure(L, f, nups);
lua_pushcclosure(L, f, nups);
#endif
}
inline void luaT_pushcfunction(lua_State *L, lua_CFunction f)
{
luaT_pushcclosure(L, f, 0);
inline void luaT_pushcfunction(lua_State* L, lua_CFunction f) {
luaT_pushcclosure(L, f, 0);
}
inline int luaT_cpcall(lua_State *L, lua_CFunction f, void *u)
{
inline int luaT_cpcall(lua_State* L, lua_CFunction f, void* u) {
#if LUA_VERSION_NUM >= 502
lua_checkstack(L, 2);
lua_pushcfunction(L, f);
lua_pushlightuserdata(L, u);
return lua_pcall(L, 1, 0, 0);
lua_checkstack(L, 2);
lua_pushcfunction(L, f);
lua_pushlightuserdata(L, u);
return lua_pcall(L, 1, 0, 0);
#else
return lua_cpcall(L, f, u);
return lua_cpcall(L, f, u);
#endif
}
// Compatibility for missing mode argument on lua_load in 5.1
inline int luaT_load(lua_State *L, lua_Reader r, void *d, const char *s, const char *m)
{
inline int luaT_load(lua_State* L, lua_Reader r, void* d, const char* s,
const char* m) {
#if LUA_VERSION_NUM >= 502
return lua_load(L, r, d, s, m);
return lua_load(L, r, d, s, m);
#else
return lua_load(L, r, d, s);
return lua_load(L, r, d, s);
#endif
}
// Compatibility for missing from argument on lua_resume in 5.1
inline int luaT_resume(lua_State *L, lua_State *f, int n)
{
inline int luaT_resume(lua_State* L, lua_State* f, int n) {
#if LUA_VERSION_NUM >= 502
return lua_resume(L, f, n);
return lua_resume(L, f, n);
#else
return lua_resume(L, n);
return lua_resume(L, n);
#endif
}
@@ -128,10 +121,9 @@ inline int luaT_resume(lua_State *L, lua_State *f, int n)
See also luaT_stdnew() which allocates, and also sets up the environment
table and metatable for the userdata.
*/
template<typename T, typename ...Ts>
T* luaT_new(lua_State* L, Ts ...args)
{
return new (lua_newuserdata(L, sizeof(T))) T(args...);
template <typename T, typename... Ts>
T* luaT_new(lua_State* L, Ts... args) {
return new (lua_newuserdata(L, sizeof(T))) T(args...);
}
//! Check that a Lua argument is a binary data blob
@@ -140,269 +132,280 @@ T* luaT_new(lua_State* L, Ts ...args)
pointer to the start of it, and the length of it. Otherwise, throws a
Lua error.
*/
const uint8_t* luaT_checkfile(lua_State *L, int idx, size_t* pDataLen);
const uint8_t* luaT_checkfile(lua_State* L, int idx, size_t* pDataLen);
//! Check that a Lua argument is a string or a proxied string
const char* luaT_checkstring(lua_State *L, int idx, size_t* pLength);
const char* luaT_checkstring(lua_State* L, int idx, size_t* pLength);
//! Push a C closure as a callable table
void luaT_pushcclosuretable(lua_State *L, lua_CFunction fn, int n);
void luaT_pushcclosuretable(lua_State* L, lua_CFunction fn, int n);
//! Set a field on the environment table of a value
/*!
Performs: env(stack[index])[k] = top; pop()
*/
void luaT_setenvfield(lua_State *L, int index, const char *k);
void luaT_setenvfield(lua_State* L, int index, const char* k);
//! Get a field from the environment table of a value
/*!
Performs: push(env(stack[index])[k])
*/
void luaT_getenvfield(lua_State *L, int index, const char *k);
void luaT_getenvfield(lua_State* L, int index, const char* k);
template <class T>
inline T* luaT_stdnew(lua_State *L, int mt_idx = luaT_environindex, bool env = false)
{
T* p = luaT_new<T>(L);
lua_pushvalue(L, mt_idx);
lua_setmetatable(L, -2);
if(env)
{
lua_newtable(L);
lua_setfenv(L, -2);
}
return p;
inline T* luaT_stdnew(lua_State* L, int mt_idx = luaT_environindex,
bool env = false) {
T* p = luaT_new<T>(L);
lua_pushvalue(L, mt_idx);
lua_setmetatable(L, -2);
if (env) {
lua_newtable(L);
lua_setfenv(L, -2);
}
return p;
}
template <typename T> struct luaT_classinfo {};
template <typename T>
struct luaT_classinfo {};
class render_target;
template <> struct luaT_classinfo<render_target> {
static inline const char* name() {return "Surface";}
template <>
struct luaT_classinfo<render_target> {
static inline const char* name() { return "Surface"; }
};
class level_map;
template <> struct luaT_classinfo<level_map> {
static inline const char* name() {return "Map";}
template <>
struct luaT_classinfo<level_map> {
static inline const char* name() { return "Map"; }
};
class sprite_sheet;
template <> struct luaT_classinfo<sprite_sheet> {
static inline const char* name() {return "SpriteSheet";}
template <>
struct luaT_classinfo<sprite_sheet> {
static inline const char* name() { return "SpriteSheet"; }
};
class animation;
template <> struct luaT_classinfo<animation> {
static inline const char* name() {return "Animation";}
template <>
struct luaT_classinfo<animation> {
static inline const char* name() { return "Animation"; }
};
class animation_manager;
template <> struct luaT_classinfo<animation_manager> {
static inline const char* name() {return "Animator";}
template <>
struct luaT_classinfo<animation_manager> {
static inline const char* name() { return "Animator"; }
};
class palette;
template <> struct luaT_classinfo<palette> {
static inline const char* name() {return "Palette";}
template <>
struct luaT_classinfo<palette> {
static inline const char* name() { return "Palette"; }
};
class raw_bitmap;
template <> struct luaT_classinfo<raw_bitmap> {
static inline const char* name() {return "RawBitmap";}
template <>
struct luaT_classinfo<raw_bitmap> {
static inline const char* name() { return "RawBitmap"; }
};
class font;
template <> struct luaT_classinfo<font> {
static inline const char* name() {return "Font";}
template <>
struct luaT_classinfo<font> {
static inline const char* name() { return "Font"; }
};
class bitmap_font;
template <> struct luaT_classinfo<bitmap_font> {
static inline const char* name() {return "BitmapFont";}
template <>
struct luaT_classinfo<bitmap_font> {
static inline const char* name() { return "BitmapFont"; }
};
#ifdef CORSIX_TH_USE_FREETYPE2
class freetype_font;
template <> struct luaT_classinfo<freetype_font> {
static inline const char* name() {return "FreeTypeFont";}
template <>
struct luaT_classinfo<freetype_font> {
static inline const char* name() { return "FreeTypeFont"; }
};
#endif
struct layers;
template <> struct luaT_classinfo<layers> {
static inline const char* name() {return "Layers";}
template <>
struct luaT_classinfo<layers> {
static inline const char* name() { return "Layers"; }
};
class pathfinder;
template <> struct luaT_classinfo<pathfinder> {
static inline const char* name() {return "Pathfinder";}
template <>
struct luaT_classinfo<pathfinder> {
static inline const char* name() { return "Pathfinder"; }
};
class cursor;
template <> struct luaT_classinfo<cursor> {
static inline const char* name() {return "Cursor";}
template <>
struct luaT_classinfo<cursor> {
static inline const char* name() { return "Cursor"; }
};
class line;
template <> struct luaT_classinfo<line> {
static inline const char* name() {return "Line";}
template <>
struct luaT_classinfo<line> {
static inline const char* name() { return "Line"; }
};
class music;
template <> struct luaT_classinfo<music> {
static inline const char* name() {return "Music";}
template <>
struct luaT_classinfo<music> {
static inline const char* name() { return "Music"; }
};
class sound_archive;
template <> struct luaT_classinfo<sound_archive> {
static inline const char* name() {return "SoundArchive";}
template <>
struct luaT_classinfo<sound_archive> {
static inline const char* name() { return "SoundArchive"; }
};
class sound_player;
template <> struct luaT_classinfo<sound_player> {
static inline const char* name() {return "SoundEffects";}
template <>
struct luaT_classinfo<sound_player> {
static inline const char* name() { return "SoundEffects"; }
};
class movie_player;
template <> struct luaT_classinfo<movie_player> {
static inline const char* name() {return "Movie";}
template <>
struct luaT_classinfo<movie_player> {
static inline const char* name() { return "Movie"; }
};
class abstract_window;
template <> struct luaT_classinfo<abstract_window> {
static inline const char* name() {return "WindowBase";}
template <>
struct luaT_classinfo<abstract_window> {
static inline const char* name() { return "WindowBase"; }
};
class sprite_render_list;
template <> struct luaT_classinfo<sprite_render_list> {
static inline const char* name() {return "SpriteRenderList";}
template <>
struct luaT_classinfo<sprite_render_list> {
static inline const char* name() { return "SpriteRenderList"; }
};
class string_proxy;
template <> struct luaT_classinfo<string_proxy> {
static inline const char* name() {return "StringProxy";}
template <>
struct luaT_classinfo<string_proxy> {
static inline const char* name() { return "StringProxy"; }
};
class lfs_ext;
template <> struct luaT_classinfo <lfs_ext> {
static inline const char* name() {return "LfsExt";}
template <>
struct luaT_classinfo<lfs_ext> {
static inline const char* name() { return "LfsExt"; }
};
class iso_filesystem;
template <> struct luaT_classinfo<iso_filesystem> {
static inline const char* name() {return "ISO Filesystem";}
template <>
struct luaT_classinfo<iso_filesystem> {
static inline const char* name() { return "ISO Filesystem"; }
};
template <> struct luaT_classinfo<std::FILE*> {
static inline const char* name() {return "file";}
template <>
struct luaT_classinfo<std::FILE*> {
static inline const char* name() { return "file"; }
};
template <class T>
T* luaT_testuserdata(lua_State *L, int idx, int mt_idx, bool required = true)
{
// Turn mt_idx into an absolute index, as the stack size changes.
if(mt_idx > LUA_REGISTRYINDEX && mt_idx < 0)
mt_idx = lua_gettop(L) + mt_idx + 1;
T* luaT_testuserdata(lua_State* L, int idx, int mt_idx, bool required = true) {
// Turn mt_idx into an absolute index, as the stack size changes.
if (mt_idx > LUA_REGISTRYINDEX && mt_idx < 0)
mt_idx = lua_gettop(L) + mt_idx + 1;
void *ud = lua_touserdata(L, idx);
if(ud != nullptr && lua_getmetatable(L, idx) != 0)
{
while(true)
{
if(lua_equal(L, mt_idx, -1) != 0)
{
lua_pop(L, 1);
return (T*)ud;
}
// Go up one inheritance level, if there is one.
if(lua_type(L, -1) != LUA_TTABLE)
break;
lua_rawgeti(L, -1, 1);
lua_replace(L, -2);
}
void* ud = lua_touserdata(L, idx);
if (ud != nullptr && lua_getmetatable(L, idx) != 0) {
while (true) {
if (lua_equal(L, mt_idx, -1) != 0) {
lua_pop(L, 1);
return (T*)ud;
}
// Go up one inheritance level, if there is one.
if (lua_type(L, -1) != LUA_TTABLE) break;
lua_rawgeti(L, -1, 1);
lua_replace(L, -2);
}
lua_pop(L, 1);
}
if (required)
{
const char *msg = lua_pushfstring(L, "%s expected, got %s", luaT_classinfo<T>::name(), luaL_typename(L, idx));
luaL_argerror(L, idx, msg);
}
return nullptr;
if (required) {
const char* msg =
lua_pushfstring(L, "%s expected, got %s", luaT_classinfo<T>::name(),
luaL_typename(L, idx));
luaL_argerror(L, idx, msg);
}
return nullptr;
}
template <class T>
T* luaT_testuserdata(lua_State *L, int idx = 1)
{
int iMetaIndex = luaT_environindex;
if(idx > 1)
iMetaIndex = luaT_upvalueindex(idx - 1);
return luaT_testuserdata<T>(L, idx, iMetaIndex);
T* luaT_testuserdata(lua_State* L, int idx = 1) {
int iMetaIndex = luaT_environindex;
if (idx > 1) iMetaIndex = luaT_upvalueindex(idx - 1);
return luaT_testuserdata<T>(L, idx, iMetaIndex);
}
template <class T, int mt>
int luaT_stdgc(lua_State *L)
{
T* p = luaT_testuserdata<T>(L, 1, mt, false);
if(p != nullptr)
{
p->~T();
}
return 0;
int luaT_stdgc(lua_State* L) {
T* p = luaT_testuserdata<T>(L, 1, mt, false);
if (p != nullptr) {
p->~T();
}
return 0;
}
void luaT_execute(lua_State *L, const char* sLuaString);
void luaT_execute_loadstring(lua_State *L, const char* sLuaString);
void luaT_execute(lua_State* L, const char* sLuaString);
void luaT_execute_loadstring(lua_State* L, const char* sLuaString);
void luaT_push(lua_State *L, lua_CFunction f);
void luaT_push(lua_State *L, int i);
void luaT_push(lua_State *L, const char* s);
void luaT_push(lua_State* L, lua_CFunction f);
void luaT_push(lua_State* L, int i);
void luaT_push(lua_State* L, const char* s);
template <class T>
void luaT_execute(lua_State *L, const char* sLuaString, T arg)
{
luaT_execute_loadstring(L, sLuaString);
luaT_push(L, arg);
lua_call(L, 1, LUA_MULTRET);
void luaT_execute(lua_State* L, const char* sLuaString, T arg) {
luaT_execute_loadstring(L, sLuaString);
luaT_push(L, arg);
lua_call(L, 1, LUA_MULTRET);
}
template <class T1, class T2>
void luaT_execute(lua_State *L, const char* sLuaString,
T1 arg1, T2 arg2)
{
luaT_execute_loadstring(L, sLuaString);
luaT_push(L, arg1);
luaT_push(L, arg2);
lua_call(L, 2, LUA_MULTRET);
void luaT_execute(lua_State* L, const char* sLuaString, T1 arg1, T2 arg2) {
luaT_execute_loadstring(L, sLuaString);
luaT_push(L, arg1);
luaT_push(L, arg2);
lua_call(L, 2, LUA_MULTRET);
}
template <class T1, class T2, class T3>
void luaT_execute(lua_State *L, const char* sLuaString,
T1 arg1, T2 arg2, T3 arg3)
{
luaT_execute_loadstring(L, sLuaString);
luaT_push(L, arg1);
luaT_push(L, arg2);
luaT_push(L, arg3);
lua_call(L, 3, LUA_MULTRET);
void luaT_execute(lua_State* L, const char* sLuaString, T1 arg1, T2 arg2,
T3 arg3) {
luaT_execute_loadstring(L, sLuaString);
luaT_push(L, arg1);
luaT_push(L, arg2);
luaT_push(L, arg3);
lua_call(L, 3, LUA_MULTRET);
}
template <class T1, class T2, class T3, class T4>
void luaT_execute(lua_State *L, const char* sLuaString,
T1 arg1, T2 arg2, T3 arg3, T4 arg4)
{
luaT_execute_loadstring(L, sLuaString);
luaT_push(L, arg1);
luaT_push(L, arg2);
luaT_push(L, arg3);
luaT_push(L, arg4);
lua_call(L, 4, LUA_MULTRET);
void luaT_execute(lua_State* L, const char* sLuaString, T1 arg1, T2 arg2,
T3 arg3, T4 arg4) {
luaT_execute_loadstring(L, sLuaString);
luaT_push(L, arg1);
luaT_push(L, arg2);
luaT_push(L, arg3);
luaT_push(L, arg4);
lua_call(L, 4, LUA_MULTRET);
}
void luaT_pushtablebool(lua_State *L, const char *k, bool v);
void luaT_pushtablebool(lua_State* L, const char* k, bool v);
void luaT_printstack(lua_State *L);
void luaT_printstack(lua_State* L);
void luaT_printrawtable(lua_State *L, int idx);
void luaT_printrawtable(lua_State* L, int idx);
#endif // CORSIX_TH_TH_LUA_H_
#endif // CORSIX_TH_TH_LUA_H_

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

View File

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

View File

@@ -21,8 +21,8 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include "th_lua_internal.h"
#include "config.h"
#include "th_lua_internal.h"
#ifdef CORSIX_TH_USE_WIN32_SDK
#include <windows.h>
#endif
@@ -31,77 +31,68 @@ class lfs_ext {};
namespace {
int l_lfs_ext_new(lua_State *L)
{
luaT_stdnew<lfs_ext>(L, luaT_environindex, true);
return 1;
int l_lfs_ext_new(lua_State* L) {
luaT_stdnew<lfs_ext>(L, luaT_environindex, true);
return 1;
}
#ifdef _WIN32
#ifdef CORSIX_TH_USE_WIN32_SDK
int l_volume_list(lua_State *L)
{
/* Windows, using the Win32 API. */
DWORD iDriveMask = GetLogicalDrives();
int iNDrives = 0;
char cDrive;
lua_settop(L, 0);
lua_newtable(L);
for (cDrive = 'A'; cDrive <= 'Z'; ++cDrive)
{
if (iDriveMask & (1 << (cDrive - 'A')))
{
char sName[4] = { cDrive, ':', '\\', 0 };
if (GetDriveTypeA(sName) > DRIVE_NO_ROOT_DIR)
{
lua_pushlstring(L, sName, 2);
lua_rawseti(L, 1, ++iNDrives);
}
}
int l_volume_list(lua_State* L) {
/* Windows, using the Win32 API. */
DWORD iDriveMask = GetLogicalDrives();
int iNDrives = 0;
char cDrive;
lua_settop(L, 0);
lua_newtable(L);
for (cDrive = 'A'; cDrive <= 'Z'; ++cDrive) {
if (iDriveMask & (1 << (cDrive - 'A'))) {
char sName[4] = {cDrive, ':', '\\', 0};
if (GetDriveTypeA(sName) > DRIVE_NO_ROOT_DIR) {
lua_pushlstring(L, sName, 2);
lua_rawseti(L, 1, ++iNDrives);
}
}
return 1;
}
return 1;
}
#else
int l_volume_list(lua_State *L)
{
/* Windows, without the Win32 API. */
int iNDrives = 0;
char cDrive;
lua_settop(L, 0);
lua_newtable(L);
lua_getfield(L, luaT_upvalueindex(1), "attributes");
for (cDrive = 'A'; cDrive <= 'Z'; ++cDrive)
{
lua_pushvalue(L, 2);
lua_pushfstring(L, "%c:\\", cDrive);
lua_pushliteral(L, "mode");
lua_call(L, 2, 1);
if (lua_toboolean(L, 3) != 0)
{
lua_pushfstring(L, "%c:", cDrive);
lua_rawseti(L, 1, ++iNDrives);
}
lua_pop(L, 1);
int l_volume_list(lua_State* L) {
/* Windows, without the Win32 API. */
int iNDrives = 0;
char cDrive;
lua_settop(L, 0);
lua_newtable(L);
lua_getfield(L, luaT_upvalueindex(1), "attributes");
for (cDrive = 'A'; cDrive <= 'Z'; ++cDrive) {
lua_pushvalue(L, 2);
lua_pushfstring(L, "%c:\\", cDrive);
lua_pushliteral(L, "mode");
lua_call(L, 2, 1);
if (lua_toboolean(L, 3) != 0) {
lua_pushfstring(L, "%c:", cDrive);
lua_rawseti(L, 1, ++iNDrives);
}
return 1;
lua_pop(L, 1);
}
return 1;
}
#endif
#else
int l_volume_list(lua_State *L)
{
/* Non-Windows systems. Assume that / is the root of the filesystem. */
lua_settop(L, 0);
lua_newtable(L);
lua_pushliteral(L, "/");
lua_rawseti(L, 1, 1);
return 1;
int l_volume_list(lua_State* L) {
/* Non-Windows systems. Assume that / is the root of the filesystem. */
lua_settop(L, 0);
lua_newtable(L);
lua_pushliteral(L, "/");
lua_rawseti(L, 1, 1);
return 1;
}
#endif
} // namespace
} // namespace
void lua_register_lfs_ext(const lua_register_state *pState)
{
lua_class_binding<lfs_ext> lcb(pState, "lfsExt", l_lfs_ext_new, lua_metatable::lfs_ext);
lcb.add_function(l_volume_list, "volumes");
void lua_register_lfs_ext(const lua_register_state* pState) {
lua_class_binding<lfs_ext> lcb(pState, "lfsExt", l_lfs_ext_new,
lua_metatable::lfs_ext);
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.
*/
#include "th_gfx.h"
#include "th_lua_internal.h"
#include "th_movie.h"
#include "th_gfx.h"
namespace {
int l_movie_new(lua_State *L)
{
luaT_stdnew<movie_player>(L, luaT_environindex, true);
return 1;
int l_movie_new(lua_State* L) {
luaT_stdnew<movie_player>(L, luaT_environindex, true);
return 1;
}
int l_movie_set_renderer(lua_State *L)
{
movie_player *pMovie = luaT_testuserdata<movie_player>(L);
render_target *pRenderTarget = luaT_testuserdata<render_target>(L, 2);
pMovie->set_renderer(pRenderTarget->get_renderer());
return 0;
int l_movie_set_renderer(lua_State* L) {
movie_player* pMovie = luaT_testuserdata<movie_player>(L);
render_target* pRenderTarget = luaT_testuserdata<render_target>(L, 2);
pMovie->set_renderer(pRenderTarget->get_renderer());
return 0;
}
int l_movie_enabled(lua_State *L)
{
movie_player *pMovie = luaT_testuserdata<movie_player>(L);
lua_pushboolean(L, pMovie->movies_enabled());
return 1;
int l_movie_enabled(lua_State* L) {
movie_player* pMovie = luaT_testuserdata<movie_player>(L);
lua_pushboolean(L, pMovie->movies_enabled());
return 1;
}
int l_movie_load(lua_State *L)
{
bool loaded;
const char* warning;
movie_player *pMovie = luaT_testuserdata<movie_player>(L);
const char* filepath = lua_tolstring(L, 2, nullptr);
pMovie->clear_last_error();
loaded = pMovie->load(filepath);
warning = pMovie->get_last_error();
lua_pushboolean(L, loaded);
lua_pushstring(L, warning);
return 2;
int l_movie_load(lua_State* L) {
bool loaded;
const char* warning;
movie_player* pMovie = luaT_testuserdata<movie_player>(L);
const char* filepath = lua_tolstring(L, 2, nullptr);
pMovie->clear_last_error();
loaded = pMovie->load(filepath);
warning = pMovie->get_last_error();
lua_pushboolean(L, loaded);
lua_pushstring(L, warning);
return 2;
}
int l_movie_unload(lua_State *L)
{
movie_player *pMovie = luaT_testuserdata<movie_player>(L);
pMovie->unload();
return 0;
int l_movie_unload(lua_State* L) {
movie_player* pMovie = luaT_testuserdata<movie_player>(L);
pMovie->unload();
return 0;
}
int l_movie_play(lua_State *L)
{
const char* warning;
movie_player *pMovie = luaT_testuserdata<movie_player>(L);
pMovie->clear_last_error();
pMovie->play(
static_cast<int>(luaL_checkinteger(L, 2)));
warning = pMovie->get_last_error();
lua_pushstring(L, warning);
return 1;
int l_movie_play(lua_State* L) {
const char* warning;
movie_player* pMovie = luaT_testuserdata<movie_player>(L);
pMovie->clear_last_error();
pMovie->play(static_cast<int>(luaL_checkinteger(L, 2)));
warning = pMovie->get_last_error();
lua_pushstring(L, warning);
return 1;
}
int l_movie_stop(lua_State *L)
{
movie_player *pVideo = luaT_testuserdata<movie_player>(L);
pVideo->stop();
return 0;
int l_movie_stop(lua_State* L) {
movie_player* pVideo = luaT_testuserdata<movie_player>(L);
pVideo->stop();
return 0;
}
int l_movie_get_native_height(lua_State *L)
{
movie_player *pMovie = luaT_testuserdata<movie_player>(L);
lua_pushinteger(L, pMovie->get_native_height());
return 1;
int l_movie_get_native_height(lua_State* L) {
movie_player* pMovie = luaT_testuserdata<movie_player>(L);
lua_pushinteger(L, pMovie->get_native_height());
return 1;
}
int l_movie_get_native_width(lua_State *L)
{
movie_player *pMovie = luaT_testuserdata<movie_player>(L);
lua_pushinteger(L, pMovie->get_native_width());
return 1;
int l_movie_get_native_width(lua_State* L) {
movie_player* pMovie = luaT_testuserdata<movie_player>(L);
lua_pushinteger(L, pMovie->get_native_width());
return 1;
}
int l_movie_has_audio_track(lua_State *L)
{
movie_player *pMovie = luaT_testuserdata<movie_player>(L);
lua_pushboolean(L, pMovie->has_audio_track());
return 1;
int l_movie_has_audio_track(lua_State* L) {
movie_player* pMovie = luaT_testuserdata<movie_player>(L);
lua_pushboolean(L, pMovie->has_audio_track());
return 1;
}
int l_movie_refresh(lua_State *L)
{
movie_player *pMovie = luaT_testuserdata<movie_player>(L);
pMovie->refresh(SDL_Rect{
static_cast<int>(luaL_checkinteger(L, 2)),
static_cast<int>(luaL_checkinteger(L, 3)),
static_cast<int>(luaL_checkinteger(L, 4)),
static_cast<int>(luaL_checkinteger(L, 5)) });
return 0;
int l_movie_refresh(lua_State* L) {
movie_player* pMovie = luaT_testuserdata<movie_player>(L);
pMovie->refresh(SDL_Rect{static_cast<int>(luaL_checkinteger(L, 2)),
static_cast<int>(luaL_checkinteger(L, 3)),
static_cast<int>(luaL_checkinteger(L, 4)),
static_cast<int>(luaL_checkinteger(L, 5))});
return 0;
}
int l_movie_allocate_picture_buffer(lua_State *L)
{
movie_player *pMovie = luaT_testuserdata<movie_player>(L);
pMovie->allocate_picture_buffer();
return 0;
int l_movie_allocate_picture_buffer(lua_State* L) {
movie_player* pMovie = luaT_testuserdata<movie_player>(L);
pMovie->allocate_picture_buffer();
return 0;
}
int l_movie_deallocate_picture_buffer(lua_State *L)
{
movie_player *pMovie = luaT_testuserdata<movie_player>(L);
pMovie->deallocate_picture_buffer();
return 0;
int l_movie_deallocate_picture_buffer(lua_State* L) {
movie_player* pMovie = luaT_testuserdata<movie_player>(L);
pMovie->deallocate_picture_buffer();
return 0;
}
} // namespace
} // namespace
void lua_register_movie(const lua_register_state *pState)
{
lua_class_binding<movie_player> lcb(pState, "moviePlayer", l_movie_new, lua_metatable::movie);
lcb.add_function(l_movie_set_renderer, "setRenderer", lua_metatable::surface);
lcb.add_function(l_movie_enabled, "getEnabled");
lcb.add_function(l_movie_load, "load");
lcb.add_function(l_movie_unload, "unload");
lcb.add_function(l_movie_play, "play");
lcb.add_function(l_movie_stop, "stop");
lcb.add_function(l_movie_get_native_height, "getNativeHeight");
lcb.add_function(l_movie_get_native_width, "getNativeWidth");
lcb.add_function(l_movie_has_audio_track, "hasAudioTrack");
lcb.add_function(l_movie_refresh, "refresh");
lcb.add_function(l_movie_allocate_picture_buffer, "allocatePictureBuffer");
lcb.add_function(l_movie_deallocate_picture_buffer, "deallocatePictureBuffer");
void lua_register_movie(const lua_register_state* pState) {
lua_class_binding<movie_player> lcb(pState, "moviePlayer", l_movie_new,
lua_metatable::movie);
lcb.add_function(l_movie_set_renderer, "setRenderer", lua_metatable::surface);
lcb.add_function(l_movie_enabled, "getEnabled");
lcb.add_function(l_movie_load, "load");
lcb.add_function(l_movie_unload, "unload");
lcb.add_function(l_movie_play, "play");
lcb.add_function(l_movie_stop, "stop");
lcb.add_function(l_movie_get_native_height, "getNativeHeight");
lcb.add_function(l_movie_get_native_width, "getNativeWidth");
lcb.add_function(l_movie_has_audio_track, "hasAudioTrack");
lcb.add_function(l_movie_refresh, "refresh");
lcb.add_function(l_movie_allocate_picture_buffer, "allocatePictureBuffer");
lcb.add_function(l_movie_deallocate_picture_buffer,
"deallocatePictureBuffer");
}

View File

@@ -20,44 +20,41 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include "th_lua_internal.h"
#include "th_sound.h"
#include "th_lua.h"
#include "lua_sdl.h"
#include <cctype>
#include <cstring>
#include <map>
#include <cctype>
#include "lua_sdl.h"
#include "th_lua.h"
#include "th_lua_internal.h"
#include "th_sound.h"
namespace {
int played_sound_callback_ids[1000];
int played_sound_callback_index = 0;
std::map<int,SDL_TimerID> map_sound_timers;
std::map<int, SDL_TimerID> map_sound_timers;
int l_soundarc_new(lua_State *L)
{
luaT_stdnew<sound_archive>(L, luaT_environindex, true);
return 1;
int l_soundarc_new(lua_State* L) {
luaT_stdnew<sound_archive>(L, luaT_environindex, true);
return 1;
}
int l_soundarc_load(lua_State *L)
{
sound_archive* pArchive = luaT_testuserdata<sound_archive>(L);
size_t iDataLen;
const uint8_t* pData = luaT_checkfile(L, 2, &iDataLen);
int l_soundarc_load(lua_State* L) {
sound_archive* pArchive = luaT_testuserdata<sound_archive>(L);
size_t iDataLen;
const uint8_t* pData = luaT_checkfile(L, 2, &iDataLen);
if(pArchive->load_from_th_file(pData, iDataLen))
lua_pushboolean(L, 1);
else
lua_pushboolean(L, 0);
return 1;
if (pArchive->load_from_th_file(pData, iDataLen))
lua_pushboolean(L, 1);
else
lua_pushboolean(L, 0);
return 1;
}
int l_soundarc_count(lua_State *L)
{
sound_archive* pArchive = luaT_testuserdata<sound_archive>(L);
lua_pushnumber(L, (lua_Number)pArchive->get_number_of_sounds());
return 1;
int l_soundarc_count(lua_State* L) {
sound_archive* pArchive = luaT_testuserdata<sound_archive>(L);
lua_pushnumber(L, (lua_Number)pArchive->get_number_of_sounds());
return 1;
}
/**
@@ -67,258 +64,240 @@ int l_soundarc_count(lua_State *L)
* @return Negative number when \a s1 should be before \a s2, zero if both
* string are equal, else a positive number.
*/
int ignorecase_cmp(const char *s1, const char *s2)
{
while(*s1 && *s2)
{
if (std::tolower(*s1) != std::tolower(*s2))
break;
int ignorecase_cmp(const char* s1, const char* s2) {
while (*s1 && *s2) {
if (std::tolower(*s1) != std::tolower(*s2)) break;
s1++;
s2++;
}
return std::tolower(*s1) - std::tolower(*s2);
s1++;
s2++;
}
return std::tolower(*s1) - std::tolower(*s2);
}
size_t l_soundarc_checkidx(lua_State *L, int iArg, sound_archive* pArchive)
{
if(lua_isnumber(L, iArg))
{
size_t iIndex = (size_t)lua_tonumber(L, iArg);
if(iIndex >= pArchive->get_number_of_sounds())
{
lua_pushnil(L);
lua_pushfstring(L, "Sound index out of "
"bounds (%f is not in range [0, %d])", lua_tonumber(L, iArg),
static_cast<int>(pArchive->get_number_of_sounds()) - 1);
return pArchive->get_number_of_sounds();
}
return iIndex;
}
const char* sName = luaL_checkstring(L, iArg);
lua_getfenv(L, 1);
lua_pushvalue(L, iArg);
lua_rawget(L, -2);
if(lua_type(L, -1) == LUA_TLIGHTUSERDATA)
{
size_t iIndex = (size_t)lua_topointer(L, -1);
lua_pop(L, 2);
return iIndex;
size_t l_soundarc_checkidx(lua_State* L, int iArg, sound_archive* pArchive) {
if (lua_isnumber(L, iArg)) {
size_t iIndex = (size_t)lua_tonumber(L, iArg);
if (iIndex >= pArchive->get_number_of_sounds()) {
lua_pushnil(L);
lua_pushfstring(L,
"Sound index out of "
"bounds (%f is not in range [0, %d])",
lua_tonumber(L, iArg),
static_cast<int>(pArchive->get_number_of_sounds()) - 1);
return pArchive->get_number_of_sounds();
}
return iIndex;
}
const char* sName = luaL_checkstring(L, iArg);
lua_getfenv(L, 1);
lua_pushvalue(L, iArg);
lua_rawget(L, -2);
if (lua_type(L, -1) == LUA_TLIGHTUSERDATA) {
size_t iIndex = (size_t)lua_topointer(L, -1);
lua_pop(L, 2);
size_t iCount = pArchive->get_number_of_sounds();
for(size_t i = 0; i < iCount; ++i)
{
if(ignorecase_cmp(sName, pArchive->get_sound_name(i)) == 0)
{
lua_getfenv(L, 1);
lua_pushvalue(L, iArg);
lua_pushlightuserdata(L, (void*)i);
lua_settable(L, -3);
lua_pop(L, 1);
return i;
}
return iIndex;
}
lua_pop(L, 2);
size_t iCount = pArchive->get_number_of_sounds();
for (size_t i = 0; i < iCount; ++i) {
if (ignorecase_cmp(sName, pArchive->get_sound_name(i)) == 0) {
lua_getfenv(L, 1);
lua_pushvalue(L, iArg);
lua_pushlightuserdata(L, (void*)i);
lua_settable(L, -3);
lua_pop(L, 1);
return i;
}
lua_pushnil(L);
lua_pushliteral(L, "File not found in sound archive: ");
lua_pushvalue(L, iArg);
lua_concat(L, 2);
return pArchive->get_number_of_sounds();
}
lua_pushnil(L);
lua_pushliteral(L, "File not found in sound archive: ");
lua_pushvalue(L, iArg);
lua_concat(L, 2);
return pArchive->get_number_of_sounds();
}
int l_soundarc_sound_name(lua_State *L)
{
sound_archive* pArchive = luaT_testuserdata<sound_archive>(L);
size_t iIndex = l_soundarc_checkidx(L, 2, pArchive);
if(iIndex == pArchive->get_number_of_sounds())
return 2;
lua_pushstring(L, pArchive->get_sound_name(iIndex));
return 1;
int l_soundarc_sound_name(lua_State* L) {
sound_archive* pArchive = luaT_testuserdata<sound_archive>(L);
size_t iIndex = l_soundarc_checkidx(L, 2, pArchive);
if (iIndex == pArchive->get_number_of_sounds()) return 2;
lua_pushstring(L, pArchive->get_sound_name(iIndex));
return 1;
}
int l_soundarc_duration(lua_State *L)
{
sound_archive* pArchive = luaT_testuserdata<sound_archive>(L);
size_t iIndex = l_soundarc_checkidx(L, 2, pArchive);
if(iIndex == pArchive->get_number_of_sounds())
return 2;
size_t iDuration = pArchive->get_sound_duration(iIndex);
lua_pushnumber(L, static_cast<lua_Number>(iDuration) / static_cast<lua_Number>(1000));
return 1;
int l_soundarc_duration(lua_State* L) {
sound_archive* pArchive = luaT_testuserdata<sound_archive>(L);
size_t iIndex = l_soundarc_checkidx(L, 2, pArchive);
if (iIndex == pArchive->get_number_of_sounds()) return 2;
size_t iDuration = pArchive->get_sound_duration(iIndex);
lua_pushnumber(
L, static_cast<lua_Number>(iDuration) / static_cast<lua_Number>(1000));
return 1;
}
int l_soundarc_data(lua_State *L)
{
sound_archive* pArchive = luaT_testuserdata<sound_archive>(L);
size_t iIndex = l_soundarc_checkidx(L, 2, pArchive);
if(iIndex == pArchive->get_number_of_sounds())
return 2;
SDL_RWops *pRWops = pArchive->load_sound(iIndex);
if(!pRWops)
return 0;
size_t iLength = SDL_RWseek(pRWops, 0, SEEK_END);
SDL_RWseek(pRWops, 0, SEEK_SET);
// There is a potential leak of pRWops if either of these Lua calls cause
// a memory error, but it isn't very likely, and this a debugging function
// anyway, so it isn't very important.
void *pBuffer = lua_newuserdata(L, iLength);
lua_pushlstring(L, (const char*)pBuffer,
SDL_RWread(pRWops, pBuffer, 1, iLength));
SDL_RWclose(pRWops);
return 1;
int l_soundarc_data(lua_State* L) {
sound_archive* pArchive = luaT_testuserdata<sound_archive>(L);
size_t iIndex = l_soundarc_checkidx(L, 2, pArchive);
if (iIndex == pArchive->get_number_of_sounds()) return 2;
SDL_RWops* pRWops = pArchive->load_sound(iIndex);
if (!pRWops) return 0;
size_t iLength = SDL_RWseek(pRWops, 0, SEEK_END);
SDL_RWseek(pRWops, 0, SEEK_SET);
// There is a potential leak of pRWops if either of these Lua calls cause
// a memory error, but it isn't very likely, and this a debugging function
// anyway, so it isn't very important.
void* pBuffer = lua_newuserdata(L, iLength);
lua_pushlstring(L, (const char*)pBuffer,
SDL_RWread(pRWops, pBuffer, 1, iLength));
SDL_RWclose(pRWops);
return 1;
}
int l_soundarc_sound_exists(lua_State *L)
{
sound_archive* pArchive = luaT_testuserdata<sound_archive>(L);
size_t iIndex = l_soundarc_checkidx(L, 2, pArchive);
if(iIndex == pArchive->get_number_of_sounds())
lua_pushboolean(L, 0);
else
lua_pushboolean(L, 1);
return 1;
}
int l_soundfx_new(lua_State *L)
{
luaT_stdnew<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++;
}
int l_soundarc_sound_exists(lua_State* L) {
sound_archive* pArchive = luaT_testuserdata<sound_archive>(L);
size_t iIndex = l_soundarc_checkidx(L, 2, pArchive);
if (iIndex == pArchive->get_number_of_sounds())
lua_pushboolean(L, 0);
else
lua_pushboolean(L, 1);
return 1;
return 1;
}
int l_soundfx_set_camera(lua_State *L)
{
sound_player *pEffects = luaT_testuserdata<sound_player>(L);
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_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);
return 1;
}
int l_soundfx_reserve_channel(lua_State *L)
{
int iChannel;
sound_player *pEffects = luaT_testuserdata<sound_player>(L);
iChannel = pEffects->reserve_channel();
lua_pushinteger(L, iChannel);
return 1;
int l_soundfx_set_camera(lua_State* L) {
sound_player* pEffects = luaT_testuserdata<sound_player>(L);
pEffects->set_camera(static_cast<int>(luaL_checkinteger(L, 2)),
static_cast<int>(luaL_checkinteger(L, 3)),
static_cast<int>(luaL_checkinteger(L, 4)));
return 0;
}
int l_soundfx_release_channel(lua_State *L)
{
sound_player *pEffects = luaT_testuserdata<sound_player>(L);
pEffects->release_channel(static_cast<int>(luaL_checkinteger(L, 2)));
return 1;
int l_soundfx_reserve_channel(lua_State* L) {
int iChannel;
sound_player* pEffects = luaT_testuserdata<sound_player>(L);
iChannel = pEffects->reserve_channel();
lua_pushinteger(L, iChannel);
return 1;
}
} // namespace
void lua_register_sound(const lua_register_state *pState)
{
// Sound Archive
{
lua_class_binding<sound_archive> lcb(pState, "soundArchive", l_soundarc_new, lua_metatable::sound_archive);
lcb.add_metamethod(l_soundarc_count, "len");
lcb.add_function(l_soundarc_load, "load");
lcb.add_function(l_soundarc_sound_name, "getFilename"); // Bad name, doesn't represent a file
lcb.add_function(l_soundarc_duration, "getDuration");
lcb.add_function(l_soundarc_data, "getFileData"); // Bad name, doesn't represent a file
lcb.add_function(l_soundarc_sound_exists, "soundExists");
}
// Sound Effects
{
lua_class_binding<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");
}
int l_soundfx_release_channel(lua_State* L) {
sound_player* pEffects = luaT_testuserdata<sound_player>(L);
pEffects->release_channel(static_cast<int>(luaL_checkinteger(L, 2)));
return 1;
}
} // namespace
void lua_register_sound(const lua_register_state* pState) {
// Sound Archive
{
lua_class_binding<sound_archive> lcb(pState, "soundArchive", l_soundarc_new,
lua_metatable::sound_archive);
lcb.add_metamethod(l_soundarc_count, "len");
lcb.add_function(l_soundarc_load, "load");
lcb.add_function(l_soundarc_sound_name,
"getFilename"); // Bad name, doesn't represent a file
lcb.add_function(l_soundarc_duration, "getDuration");
lcb.add_function(l_soundarc_data,
"getFileData"); // Bad name, doesn't represent a file
lcb.add_function(l_soundarc_sound_exists, "soundExists");
}
// Sound Effects
{
lua_class_binding<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.
*/
#include "th_lua_internal.h"
#include "th_gfx.h"
#include "th_map.h"
#include <algorithm>
#include "th_gfx.h"
#include "th_lua_internal.h"
#include "th_map.h"
class abstract_window {};
namespace {
int l_abstract_window_new(lua_State *L)
{
return luaL_error(L, "windowBase can only be used a base class - "
" do not create a windowBase directly.");
int l_abstract_window_new(lua_State* L) {
return luaL_error(L,
"windowBase can only be used a base class - "
" do not create a windowBase directly.");
}
uint8_t range_scale(uint16_t low, uint16_t high, uint16_t val, uint16_t start, uint16_t end)
{
return static_cast<uint8_t>(std::max(start + (end - start) * (val - low) / (high - low), 0xFF));
uint8_t range_scale(uint16_t low, uint16_t high, uint16_t val, uint16_t start,
uint16_t end) {
return static_cast<uint8_t>(
std::max(start + (end - start) * (val - low) / (high - low), 0xFF));
}
inline bool is_wall(uint16_t blk)
{
return ((82 <= ((blk) & 0xFF)) && (((blk) & 0xFF) <= 164));
inline bool is_wall(uint16_t blk) {
return ((82 <= ((blk)&0xFF)) && (((blk)&0xFF) <= 164));
}
inline bool is_wall_drawn(const level_map &map, const map_tile &node, const map_tile &original_node, size_t n)
{
return map.get_tile_owner(&node) != 0 ? is_wall(node.iBlock[n]) : is_wall(original_node.iBlock[n]);
inline bool is_wall_drawn(const level_map& map, const map_tile& node,
const map_tile& original_node, size_t n) {
return map.get_tile_owner(&node) != 0 ? is_wall(node.iBlock[n])
: is_wall(original_node.iBlock[n]);
}
int l_town_map_draw(lua_State *L)
{
luaL_checktype(L, 1, LUA_TTABLE);
level_map* pMap = luaT_testuserdata<level_map>(L, 2);
render_target *pCanvas = luaT_testuserdata<render_target>(L, 3);
int iCanvasXBase = static_cast<int>(luaL_checkinteger(L, 4));
int iCanvasYBase = static_cast<int>(luaL_checkinteger(L, 5));
bool bShowHeat = lua_toboolean(L, 6) != 0;
int l_town_map_draw(lua_State* L) {
luaL_checktype(L, 1, LUA_TTABLE);
level_map* pMap = luaT_testuserdata<level_map>(L, 2);
render_target* pCanvas = luaT_testuserdata<render_target>(L, 3);
int iCanvasXBase = static_cast<int>(luaL_checkinteger(L, 4));
int iCanvasYBase = static_cast<int>(luaL_checkinteger(L, 5));
bool bShowHeat = lua_toboolean(L, 6) != 0;
uint32_t iColourMyHosp = render_target::map_colour(0, 0, 70);
uint32_t iColourWall = render_target::map_colour(255, 255, 255);
uint32_t iColourDoor = render_target::map_colour(200, 200, 200);
uint32_t iColourPurchasable = render_target::map_colour(255, 0, 0);
uint32_t iColourMyHosp = render_target::map_colour(0, 0, 70);
uint32_t iColourWall = render_target::map_colour(255, 255, 255);
uint32_t iColourDoor = render_target::map_colour(200, 200, 200);
uint32_t iColourPurchasable = render_target::map_colour(255, 0, 0);
const map_tile *pNode = pMap->get_tile_unchecked(0, 0);
const map_tile *pOriginalNode = pMap->get_original_tile_unchecked(0, 0);
int iCanvasY = iCanvasYBase + 3;
int iMapWidth = pMap->get_width();
for(int iY = 0; iY < pMap->get_height(); ++iY, iCanvasY += 3)
{
int iCanvasX = iCanvasXBase;
for(int iX = 0; iX < iMapWidth; ++iX, ++pNode, ++pOriginalNode, iCanvasX += 3)
{
if(pOriginalNode->flags.hospital)
{
uint32_t iColour = iColourMyHosp;
if(!(pNode->flags.hospital))
{
// TODO: Replace 1 with player number
if(pMap->is_parcel_purchasable(pNode->iParcelId, 1))
iColour = iColourPurchasable;
else
goto dont_paint_tile;
}
else if(bShowHeat)
{
uint16_t iTemp = pMap->get_tile_temperature(pNode);
if(iTemp < 5200) // Less than 4 degrees
iTemp = 0;
else if(iTemp > 32767) // More than 25 degrees
iTemp = 255;
else // NB: 108 == (32767 - 5200) / 255
iTemp = static_cast<uint16_t>((iTemp - 5200) / 108);
const map_tile* pNode = pMap->get_tile_unchecked(0, 0);
const map_tile* pOriginalNode = pMap->get_original_tile_unchecked(0, 0);
int iCanvasY = iCanvasYBase + 3;
int iMapWidth = pMap->get_width();
for (int iY = 0; iY < pMap->get_height(); ++iY, iCanvasY += 3) {
int iCanvasX = iCanvasXBase;
for (int iX = 0; iX < iMapWidth;
++iX, ++pNode, ++pOriginalNode, iCanvasX += 3) {
if (pOriginalNode->flags.hospital) {
uint32_t iColour = iColourMyHosp;
if (!(pNode->flags.hospital)) {
// TODO: Replace 1 with player number
if (pMap->is_parcel_purchasable(pNode->iParcelId, 1))
iColour = iColourPurchasable;
else
goto dont_paint_tile;
} else if (bShowHeat) {
uint16_t iTemp = pMap->get_tile_temperature(pNode);
if (iTemp < 5200) // Less than 4 degrees
iTemp = 0;
else if (iTemp > 32767) // More than 25 degrees
iTemp = 255;
else // NB: 108 == (32767 - 5200) / 255
iTemp = static_cast<uint16_t>((iTemp - 5200) / 108);
const uint16_t minOkTemp = 140;
const uint16_t maxOkTemp = 180;
const uint16_t minOkTemp = 140;
const uint16_t maxOkTemp = 180;
uint8_t iR = 0;
uint8_t iG = 0;
uint8_t iB = 0;
switch(pMap->get_temperature_display())
{
case temperature_theme::multi_colour:
iB = 70;
if(iTemp < minOkTemp) {
iB = range_scale(0, minOkTemp - 1, iTemp, 200, 60);
} else if(iTemp < maxOkTemp) {
iG = range_scale(minOkTemp, maxOkTemp - 1, iTemp, 140, 224);
} else {
iR = range_scale(maxOkTemp, 255, iTemp, 224, 255);
}
break;
case temperature_theme::yellow_red:
if(iTemp < minOkTemp) { // Below 11 degrees
iR = range_scale(0, minOkTemp - 1, iTemp, 100, 213);
iG = range_scale(0, minOkTemp - 1, iTemp, 80, 180);
} else {
iR = range_scale(minOkTemp, 255, iTemp, 223, 235);
iG = range_scale(minOkTemp, 255, iTemp, 184, 104);
iB = range_scale(minOkTemp, 255, iTemp, 0, 53);
}
break;
case temperature_theme::red:
iR = static_cast<uint8_t>(iTemp);
iB = 70;
break;
}
uint8_t iR = 0;
uint8_t iG = 0;
uint8_t iB = 0;
switch (pMap->get_temperature_display()) {
case temperature_theme::multi_colour:
iB = 70;
if (iTemp < minOkTemp) {
iB = range_scale(0, minOkTemp - 1, iTemp, 200, 60);
} else if (iTemp < maxOkTemp) {
iG = range_scale(minOkTemp, maxOkTemp - 1, iTemp, 140, 224);
} else {
iR = range_scale(maxOkTemp, 255, iTemp, 224, 255);
}
break;
case temperature_theme::yellow_red:
if (iTemp < minOkTemp) { // Below 11 degrees
iR = range_scale(0, minOkTemp - 1, iTemp, 100, 213);
iG = range_scale(0, minOkTemp - 1, iTemp, 80, 180);
} else {
iR = range_scale(minOkTemp, 255, iTemp, 223, 235);
iG = range_scale(minOkTemp, 255, iTemp, 184, 104);
iB = range_scale(minOkTemp, 255, iTemp, 0, 53);
}
break;
case temperature_theme::red:
iR = static_cast<uint8_t>(iTemp);
iB = 70;
break;
}
iColour = render_target::map_colour(iR, iG, iB);
}
pCanvas->fill_rect(iColour, iCanvasX, iCanvasY, 3, 3);
}
dont_paint_tile:
if(is_wall_drawn(*pMap, *pNode, *pOriginalNode, 1)) {
pCanvas->fill_rect(iColourWall, iCanvasX, iCanvasY, 3, 1);
// Draw entrance door
auto l = (pNode - 1)->objects;
if(!l.empty() && l.front() == object_type::entrance_right_door) {
if (pNode->flags.hospital) {
pCanvas->fill_rect(iColourDoor, iCanvasX-6, iCanvasY-2, 9, 3);
} else {
pCanvas->fill_rect(iColourDoor, iCanvasX-6, iCanvasY, 9, 3);
}
}
}
if(is_wall_drawn(*pMap, *pNode, *pOriginalNode, 2)) {
pCanvas->fill_rect(iColourWall, iCanvasX, iCanvasY, 1, 3);
// Draw entrance door
auto l = (pNode - iMapWidth)->objects;
if(!l.empty() && l.front() == object_type::entrance_right_door) {
if (pNode->flags.hospital) {
pCanvas->fill_rect(iColourDoor, iCanvasX-2, iCanvasY-6, 3, 9);
} else {
pCanvas->fill_rect(iColourDoor, iCanvasX, iCanvasY-6, 3, 9);
}
}
}
iColour = render_target::map_colour(iR, iG, iB);
}
pCanvas->fill_rect(iColour, iCanvasX, iCanvasY, 3, 3);
}
dont_paint_tile:
if (is_wall_drawn(*pMap, *pNode, *pOriginalNode, 1)) {
pCanvas->fill_rect(iColourWall, iCanvasX, iCanvasY, 3, 1);
// Draw entrance door
auto l = (pNode - 1)->objects;
if (!l.empty() && l.front() == object_type::entrance_right_door) {
if (pNode->flags.hospital) {
pCanvas->fill_rect(iColourDoor, iCanvasX - 6, iCanvasY - 2, 9, 3);
} else {
pCanvas->fill_rect(iColourDoor, iCanvasX - 6, iCanvasY, 9, 3);
}
}
}
if (is_wall_drawn(*pMap, *pNode, *pOriginalNode, 2)) {
pCanvas->fill_rect(iColourWall, iCanvasX, iCanvasY, 1, 3);
// Draw entrance door
auto l = (pNode - iMapWidth)->objects;
if (!l.empty() && l.front() == object_type::entrance_right_door) {
if (pNode->flags.hospital) {
pCanvas->fill_rect(iColourDoor, iCanvasX - 2, iCanvasY - 6, 3, 9);
} else {
pCanvas->fill_rect(iColourDoor, iCanvasX, iCanvasY - 6, 3, 9);
}
}
}
}
}
return 0;
return 0;
}
} // namespace
} // namespace
void lua_register_ui(const lua_register_state *pState)
{
// WindowBase
lua_class_binding<abstract_window> lcb(pState, "windowHelpers", l_abstract_window_new, lua_metatable::window_base);
lcb.add_function(l_town_map_draw, "townMapDraw", lua_metatable::map, lua_metatable::surface);
void lua_register_ui(const lua_register_state* pState) {
// WindowBase
lua_class_binding<abstract_window> lcb(pState, "windowHelpers",
l_abstract_window_new,
lua_metatable::window_base);
lcb.add_function(l_town_map_draw, "townMapDraw", lua_metatable::map,
lua_metatable::surface);
}

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -20,36 +20,38 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include "config.h"
#include "../Src/main.h"
#include "../Src/bootstrap.h"
#include <stack>
#include "config.h"
#include <SDL.h>
#include <stack>
#include "../Src/bootstrap.h"
#ifdef CORSIX_TH_USE_SDL_MIXER
#include <SDL_mixer.h>
#endif
// Template magic for checking type equality
template <typename T1, typename T2>
struct types_equal{ enum{
struct types_equal {
enum {
result = -1,
}; };
};
};
template <typename T1>
struct types_equal<T1, T1>{ enum{
struct types_equal<T1, T1> {
enum {
result = 1,
}; };
};
};
static void cleanup(lua_State* L)
{
static void cleanup(lua_State* L) {
#ifdef CORSIX_TH_USE_SDL_MIXER
while(Mix_QuerySpec(nullptr, nullptr, nullptr))
{
Mix_CloseAudio();
}
while (Mix_QuerySpec(nullptr, nullptr, nullptr)) {
Mix_CloseAudio();
}
#endif
SDL_Quit();
SDL_Quit();
lua_close(L);
lua_close(L);
}
//! Program entry point
@@ -59,73 +61,64 @@ static void cleanup(lua_State* L)
sooner, hence this function does as little as possible and leaves the rest
for lua_main().
*/
int main(int argc, char** argv)
{
struct compile_time_lua_check
{
// Lua 5.1, not 5.0, is required
int lua_5_point_1_required[LUA_VERSION_NUM >= 501 ? 1 : -1];
int main(int argc, char** argv) {
struct compile_time_lua_check {
// Lua 5.1, not 5.0, is required
int lua_5_point_1_required[LUA_VERSION_NUM >= 501 ? 1 : -1];
// Lua numbers must be doubles so that the mantissa has at least
// 32 bits (floats only have 24 bits)
int number_is_double[types_equal<lua_Number, double>::result];
};
// Lua numbers must be doubles so that the mantissa has at least
// 32 bits (floats only have 24 bits)
int number_is_double[types_equal<lua_Number, double>::result];
};
bool bRun = true;
bool bRun = true;
while(bRun)
{
lua_State *L = NULL;
while (bRun) {
lua_State* L = NULL;
L = luaL_newstate();
if(L == NULL)
{
fprintf(stderr, "Fatal error starting CorsixTH: "
"Cannot open Lua state.\n");
return 0;
}
lua_atpanic(L, lua_panic);
luaL_openlibs(L);
lua_settop(L, 0);
lua_pushcfunction(L, lua_stacktrace);
lua_pushcfunction(L, lua_main);
// Move command line parameters onto the Lua stack
lua_checkstack(L, argc);
for(int i = 0; i < argc; ++i)
{
lua_pushstring(L, argv[i]);
}
if(lua_pcall(L, argc, 0, 1) != 0)
{
const char* err = lua_tostring(L, -1);
if(err != NULL)
{
fprintf(stderr, "%s\n", err);
}
else
{
fprintf(stderr, "An error has occurred in CorsixTH:\n"
"Uncaught non-string Lua error\n");
}
lua_pushcfunction(L, bootstrap_lua_error_report);
lua_insert(L, -2);
if(lua_pcall(L, 1, 0, 0) != 0)
{
fprintf(stderr, "%s\n", lua_tostring(L, -1));
}
}
lua_getfield(L, LUA_REGISTRYINDEX, "_RESTART");
bRun = lua_toboolean(L, -1) != 0;
cleanup(L);
if(bRun)
{
printf("\n\nRestarting...\n\n\n");
}
L = luaL_newstate();
if (L == NULL) {
fprintf(stderr,
"Fatal error starting CorsixTH: "
"Cannot open Lua state.\n");
return 0;
}
return 0;
lua_atpanic(L, lua_panic);
luaL_openlibs(L);
lua_settop(L, 0);
lua_pushcfunction(L, lua_stacktrace);
lua_pushcfunction(L, lua_main);
// Move command line parameters onto the Lua stack
lua_checkstack(L, argc);
for (int i = 0; i < argc; ++i) {
lua_pushstring(L, argv[i]);
}
if (lua_pcall(L, argc, 0, 1) != 0) {
const char* err = lua_tostring(L, -1);
if (err != NULL) {
fprintf(stderr, "%s\n", err);
} else {
fprintf(stderr,
"An error has occurred in CorsixTH:\n"
"Uncaught non-string Lua error\n");
}
lua_pushcfunction(L, bootstrap_lua_error_report);
lua_insert(L, -2);
if (lua_pcall(L, 1, 0, 0) != 0) {
fprintf(stderr, "%s\n", lua_tostring(L, -1));
}
}
lua_getfield(L, LUA_REGISTRYINDEX, "_RESTART");
bRun = lua_toboolean(L, -1) != 0;
cleanup(L);
if (bRun) {
printf("\n\nRestarting...\n\n\n");
}
}
return 0;
}

View File

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

View File

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