TONY: Properly implement game saving and loading.

Saving isn't enabled in the demo, so for testing purposes I'm currently dissbling the ADGF_DEMO flag in the detection tables so saving is enabled.
This commit is contained in:
Paul Gilbert 2012-05-12 21:54:37 +10:00
parent 109e5f8226
commit 073e46503c
7 changed files with 123 additions and 151 deletions

View file

@ -371,6 +371,7 @@ RMOptionScreen::RMOptionScreen(void) {
m_menu = NULL; m_menu = NULL;
m_HideLoadSave = NULL; m_HideLoadSave = NULL;
m_QuitConfirm = NULL; m_QuitConfirm = NULL;
m_bQuitConfirm = false;
Create(RM_SX, RM_SY); Create(RM_SX, RM_SY);
@ -1401,10 +1402,11 @@ void RMOptionScreen::RemoveThis(CORO_PARAM, bool &result) {
} }
bool RMOptionScreen::LoadThumbnailFromSaveState(int nState, byte *lpDestBuf, RMString& name, byte &diff) { bool RMOptionScreen::LoadThumbnailFromSaveState(int nState, byte *lpDestBuf, RMString &name, byte &diff) {
char buf[256]; Common::String buf;
char namebuf[256]; int i; char namebuf[256];
Common::File f; int i;
Common::InSaveFile *f;
char id[4]; char id[4];
// Pulisce la destinazione // Pulisce la destinazione
@ -1412,60 +1414,68 @@ bool RMOptionScreen::LoadThumbnailFromSaveState(int nState, byte *lpDestBuf, RMS
name = "No name"; name = "No name";
diff = 10; diff = 10;
// Si fa dare il nome del salvataggio // Get the savegame filename for the given slot
_vm->GetSaveStateFileName(nState, buf); buf = _vm->GetSaveStateFileName(nState);
// Guarda se esiste // Try and open the savegame
if (f.open(buf)) f = g_system->getSavefileManager()->openForLoading(buf);
if (f == NULL)
return false; return false;
// Controlla se è giusto l'header // Check to see if the file has a valid header
f.read(id, 4); f->read(id, 4);
if (id[0] != 'R' || id[1] != 'M' || id[2] != 'S') { if (id[0] != 'R' || id[1] != 'M' || id[2] != 'S') {
f.close(); delete f;
return false; return false;
} }
if (id[3] < 0x3) { if (id[3] < 0x3) {
// Versione vecchia, niente screenshot // Very old version that doesn't have screenshots
f.close(); delete f;
return true; return true;
} }
// legge lo screenshot // Load the screenshot
if (id[3] >= 0x5) { if ((id[3] >= 0x5) && (id[3] < 0x8)) {
// Read it as an LZO compressed data block
byte *cmpbuf; byte *cmpbuf;
uint32 cmpsize, size; uint32 cmpsize, size;
cmpbuf = new byte[160 * 120 * 4]; cmpbuf = new byte[160 * 120 * 4];
// Se la versione >= 5, è compresso! // Se la versione >= 5, è compresso!
cmpsize = f.readUint32LE(); cmpsize = f->readUint32LE();
f.read(cmpbuf, cmpsize); f->read(cmpbuf, cmpsize);
lzo1x_decompress(cmpbuf,cmpsize,lpDestBuf,&size); lzo1x_decompress(cmpbuf,cmpsize,lpDestBuf,&size);
delete[] cmpbuf; delete[] cmpbuf;
} else } else {
f.read(lpDestBuf, 160 * 120 * 2); // Read in the screenshot as an uncompressed data block
if (id[3] >= 8)
// Recent versions use hardcoded 160x120 uncomrpessed data, so size can be skipped
f->skip(4);
f->read(lpDestBuf, 160 * 120 * 2);
}
if (id[3] >= 0x5) { if (id[3] >= 0x5) {
// Legge il livello di difficoltà // Read in the difficulty level
diff = f.readByte(); diff = f->readByte();
} }
if (id[3] < 0x4) { if (id[3] < 0x4) {
// Versione vecchia, niente nome // Savegame version doesn't have a stored name
f.close(); delete f;
return true; return true;
} }
i = f.readByte(); i = f->readByte();
f.read(namebuf, i); f->read(namebuf, i);
namebuf[i] = '\0'; namebuf[i] = '\0';
name = namebuf; name = namebuf;
f.close(); delete f;
return true; return true;
} }

View file

@ -713,66 +713,55 @@ void RestoreMusic(CORO_PARAM);
void SaveMusic(Common::OutSaveFile *f); void SaveMusic(Common::OutSaveFile *f);
void LoadMusic(Common::InSaveFile *f); void LoadMusic(Common::InSaveFile *f);
unsigned char wrkmem[LZO1X_999_MEM_COMPRESS]; #define TONY_SAVEGAME_VERSION 8
void RMGfxEngine::SaveState(const char *fn, byte *curThumb, const char *name, bool bFastCompress) { void RMGfxEngine::SaveState(const char *fn, byte *curThumb, const char *name, bool bFastCompress) {
Common::OutSaveFile *f; Common::OutSaveFile *f;
byte *state, *statecmp; byte *state;
byte *thumbcmp; uint thumbsize;
uint thumbsizecmp, thumbsize; uint size;
uint size, sizecmp;
int i; int i;
char buf[4]; char buf[4];
RMPoint tp = m_tony.Position(); RMPoint tp = m_tony.Position();
// Salvataggio: variabili mpal + locazione corrente + posizione di tony + inventario // Saving: mpal variables, current location, + tony inventory position
// Per ora salviamo solo lo stato MPAL // For now, we only save the MPAL state
size=mpalGetSaveStateSize(); size = mpalGetSaveStateSize();
state = new byte[size]; state = new byte[size];
statecmp = new byte[size*2];
mpalSaveState(state); mpalSaveState(state);
thumbcmp = new byte[160 * 120 * 4];
thumbsize = 160 * 120 * 2; thumbsize = 160 * 120 * 2;
if (bFastCompress) {
lzo1x_1_compress(state,size,statecmp,&sizecmp,wrkmem);
lzo1x_1_compress(curThumb,thumbsize,thumbcmp,&thumbsizecmp,wrkmem);
} else {
lzo1x_999_compress(state,size,statecmp,&sizecmp,wrkmem);
lzo1x_999_compress(curThumb,thumbsize,thumbcmp,&thumbsizecmp,wrkmem);
}
buf[0] = 'R'; buf[0] = 'R';
buf[1] = 'M'; buf[1] = 'M';
buf[2] = 'S'; buf[2] = 'S';
buf[3] = 0x7; buf[3] = TONY_SAVEGAME_VERSION;
f = g_system->getSavefileManager()->openForSaving(fn); f = g_system->getSavefileManager()->openForSaving(fn);
if (f==NULL) return; if (f == NULL)
f->write(buf, 4); return;
f->writeUint32LE(thumbsizecmp);
f->write(thumbcmp, thumbsizecmp);
// Livello di difficoltà f->write(buf, 4);
f->writeUint32LE(thumbsize);
f->write(curThumb, thumbsize);
// Difficulty level
i = mpalQueryGlobalVar("VERSIONEFACILE"); i = mpalQueryGlobalVar("VERSIONEFACILE");
f->writeByte(i); f->writeByte(i);
i=strlen(name); i = strlen(name);
f->writeByte(i); f->writeByte(i);
f->write(name, i); f->write(name, i);
f->writeUint32LE(m_nCurLoc); f->writeUint32LE(m_nCurLoc);
f->writeUint32LE(tp.x); f->writeUint32LE(tp.x);
f->writeUint32LE(tp.y); f->writeUint32LE(tp.y);
f->writeUint32LE(size);
f->writeUint32LE(sizecmp);
f->write(statecmp, sizecmp);
delete[] state;
delete[] statecmp;
delete[] thumbcmp;
// inventario f->writeUint32LE(size);
f->write(state, size);
delete[] state;
// Inventory
size = m_inv.GetSaveStateSize(); size = m_inv.GetSaveStateSize();
state = new byte[size]; state = new byte[size];
m_inv.SaveState(state); m_inv.SaveState(state);
@ -791,16 +780,16 @@ void RMGfxEngine::SaveState(const char *fn, byte *curThumb, const char *name, bo
// New Ver5 // New Ver5
bool bStat; bool bStat;
// Salva lo stato della pastorella e del palesati // Saves the state of the shepherdess and show yourself
bStat = m_tony.GetPastorella(); bStat = m_tony.GetPastorella();
f->writeByte(bStat); f->writeByte(bStat);
bStat = m_inter.GetPalesati(); bStat = m_inter.GetPalesati();
f->writeByte(bStat); f->writeByte(bStat);
// Salva gli mchar // Save the chars
CharsSaveAll(f); CharsSaveAll(f);
// Salva le opzioni // Save the options
f->writeByte(bCfgInvLocked); f->writeByte(bCfgInvLocked);
f->writeByte(bCfgInvNoScroll); f->writeByte(bCfgInvNoScroll);
f->writeByte(bCfgTimerizedText); f->writeByte(bCfgTimerizedText);
@ -819,10 +808,10 @@ void RMGfxEngine::SaveState(const char *fn, byte *curThumb, const char *name, bo
f->writeByte(nCfgMusicVolume); f->writeByte(nCfgMusicVolume);
f->writeByte(nCfgSFXVolume); f->writeByte(nCfgSFXVolume);
// Salva gli hotspot // Save the hotspots
SaveChangedHotspot(f); SaveChangedHotspot(f);
// Salva la musica // Save the music
SaveMusic(f); SaveMusic(f);
f->finalize(); f->finalize();
@ -845,8 +834,10 @@ void RMGfxEngine::LoadState(CORO_PARAM, const char *fn) {
CORO_BEGIN_CODE(_ctx); CORO_BEGIN_CODE(_ctx);
_ctx->f = g_system->getSavefileManager()->openForLoading(fn); _ctx->f = g_system->getSavefileManager()->openForLoading(fn);
if (_ctx->f == NULL) return; if (_ctx->f == NULL)
return;
_ctx->f->read(_ctx->buf, 4); _ctx->f->read(_ctx->buf, 4);
if (_ctx->buf[0] != 'R' || _ctx->buf[1] != 'M' || _ctx->buf[2] != 'S') { if (_ctx->buf[0] != 'R' || _ctx->buf[1] != 'M' || _ctx->buf[2] != 'S') {
delete _ctx->f; delete _ctx->f;
return; return;
@ -854,39 +845,43 @@ void RMGfxEngine::LoadState(CORO_PARAM, const char *fn) {
_ctx->ver = _ctx->buf[3]; _ctx->ver = _ctx->buf[3];
if (_ctx->ver != 0x1 && _ctx->ver != 0x2 && _ctx->ver != 0x3 && _ctx->ver != 0x4 && _ctx->ver != 0x5 && _ctx->ver != 0x6 && _ctx->ver != 0x7) { if (_ctx->ver == 0 || _ctx->ver > TONY_SAVEGAME_VERSION) {
delete _ctx->f; delete _ctx->f;
return; return;
} }
if (_ctx->ver >= 0x3) { if (_ctx->ver >= 0x3) {
// C'è il thumbnail. Se _ctx->ver >= 5, è compresso // There is a thumbnail. If the version is between 5 and 7, it's compressed
if (_ctx->ver >= 0x5) { if ((_ctx->ver >= 0x5) && (_ctx->ver <= 0x7)) {
_ctx->i = 0; _ctx->i = 0;
_ctx->i = _ctx->f->readUint32LE(); _ctx->i = _ctx->f->readUint32LE();
_ctx->f->seek(_ctx->i); _ctx->f->seek(_ctx->i);
} else } else {
if (_ctx->ver >= 8)
// Skip thumbnail size
_ctx->f->skip(4);
_ctx->f->seek(160 * 120 * 2, SEEK_CUR); _ctx->f->seek(160 * 120 * 2, SEEK_CUR);
}
} }
if (_ctx->ver >= 0x5) { if (_ctx->ver >= 0x5) {
// Skip del livello di difficoltà // Skip the difficulty level
_ctx->f->seek(1, SEEK_CUR); _ctx->f->seek(1, SEEK_CUR);
} }
if (_ctx->ver >= 0x4) { // Skippa il nome, che non serve a nessuno if (_ctx->ver >= 0x4) { // Skip the savegame name, which serves no purpose
_ctx->i = _ctx->f->readByte(); _ctx->i = _ctx->f->readByte();
_ctx->f->seek(_ctx->i, SEEK_CUR); _ctx->f->seek(_ctx->i, SEEK_CUR);
} }
_ctx->loc = _ctx->f->readUint32LE();
_ctx->loc = _ctx->f->readUint32LE(); _ctx->loc = _ctx->f->readUint32LE();
_ctx->tp.x = _ctx->f->readUint32LE(); _ctx->tp.x = _ctx->f->readUint32LE();
_ctx->tp.y = _ctx->f->readUint32LE(); _ctx->tp.y = _ctx->f->readUint32LE();
_ctx->size = _ctx->f->readUint32LE(); _ctx->size = _ctx->f->readUint32LE();
if (_ctx->ver >= 0x5) { if ((_ctx->ver >= 0x5) && (_ctx->ver <= 7)) {
// Stato MPAL compresso! // MPAL was packed!
_ctx->sizecmp = _ctx->f->readUint32LE(); _ctx->sizecmp = _ctx->f->readUint32LE();
_ctx->state = new byte[_ctx->size]; _ctx->state = new byte[_ctx->size];
_ctx->statecmp = new byte[_ctx->sizecmp]; _ctx->statecmp = new byte[_ctx->sizecmp];
@ -894,6 +889,7 @@ void RMGfxEngine::LoadState(CORO_PARAM, const char *fn) {
lzo1x_decompress(_ctx->statecmp,_ctx->sizecmp,_ctx->state,&_ctx->size); lzo1x_decompress(_ctx->statecmp,_ctx->sizecmp,_ctx->state,&_ctx->size);
delete[] _ctx->statecmp; delete[] _ctx->statecmp;
} else { } else {
// Read uncompressed MPAL data
_ctx->state = new byte[_ctx->size]; _ctx->state = new byte[_ctx->size];
_ctx->f->read(_ctx->state, _ctx->size); _ctx->f->read(_ctx->state, _ctx->size);
} }
@ -901,8 +897,7 @@ void RMGfxEngine::LoadState(CORO_PARAM, const char *fn) {
mpalLoadState(_ctx->state); mpalLoadState(_ctx->state);
delete[] _ctx->state; delete[] _ctx->state;
// Inventory
// inventario
_ctx->size = _ctx->f->readUint32LE(); _ctx->size = _ctx->f->readUint32LE();
_ctx->state = new byte[_ctx->size]; _ctx->state = new byte[_ctx->size];
_ctx->f->read(_ctx->state, _ctx->size); _ctx->f->read(_ctx->state, _ctx->size);
@ -930,7 +925,7 @@ void RMGfxEngine::LoadState(CORO_PARAM, const char *fn) {
} }
if (_ctx->ver >= 6) { if (_ctx->ver >= 6) {
// Carica le opzioni // Load options
bCfgInvLocked = _ctx->f->readByte(); bCfgInvLocked = _ctx->f->readByte();
bCfgInvNoScroll = _ctx->f->readByte(); bCfgInvNoScroll = _ctx->f->readByte();
bCfgTimerizedText = _ctx->f->readByte(); bCfgTimerizedText = _ctx->f->readByte();
@ -949,7 +944,7 @@ void RMGfxEngine::LoadState(CORO_PARAM, const char *fn) {
nCfgMusicVolume = _ctx->f->readByte(); nCfgMusicVolume = _ctx->f->readByte();
nCfgSFXVolume = _ctx->f->readByte(); nCfgSFXVolume = _ctx->f->readByte();
// Carica gli hotspot // Load hotspots
LoadChangedHotspot(_ctx->f); LoadChangedHotspot(_ctx->f);
} }
@ -964,11 +959,11 @@ void RMGfxEngine::LoadState(CORO_PARAM, const char *fn) {
m_tony.SetPattern(RMTony::PAT_STANDRIGHT); m_tony.SetPattern(RMTony::PAT_STANDRIGHT);
MainUnfreeze(); MainUnfreeze();
// Le versioni vecchie necessitano di On enter // On older versions, need to an enter action
if (_ctx->ver < 5) if (_ctx->ver < 5)
mpalQueryDoAction(0, _ctx->loc, 0); mpalQueryDoAction(0, _ctx->loc, 0);
else { else {
// In quelle nuove, ci basta resettare gli mcode // In the new ones, we just reset the mcode
MCharResetCodes(); MCharResetCodes();
} }

View file

@ -127,4 +127,14 @@ bool RMInput::MouseRight() {
return _rightButton; return _rightButton;
} }
/**
* Return true if a key has been pressed */
bool RMInput::GetAsyncKeyState(Common::KeyCode kc) {
// The act of testing for a particular key automatically clears the state, to prevent
// the same key being registered in multiple different frames
bool result = _keyDown[(int)kc];
_keyDown[(int)kc] = false;
return result;
}
} // End of namespace Tony } // End of namespace Tony

View file

@ -98,7 +98,7 @@ public:
bool MouseBothReleased() { return _leftReleaseMouse && _rightReleaseMouse; } bool MouseBothReleased() { return _leftReleaseMouse && _rightReleaseMouse; }
// Returns true if the given key is pressed // Returns true if the given key is pressed
bool GetAsyncKeyState(Common::KeyCode kc) { return _keyDown[(int)kc]; } bool GetAsyncKeyState(Common::KeyCode kc);
}; };
} // End of namespace Tony } // End of namespace Tony

View file

@ -389,20 +389,13 @@ int TonyEngine::GetMusicVolume(int nChannel) {
} }
void TonyEngine::GetSaveStateFileName(int n, char *buf) { Common::String TonyEngine::GetSaveStateFileName(int n) {
RMString name; return Common::String::format("tony.%03d", n);
if (n > 0)
name.Format("%02d", n);
else
name.Format("autosave");
name += ".sav";
} }
void TonyEngine::AutoSave(CORO_PARAM) { void TonyEngine::AutoSave(CORO_PARAM) {
CORO_BEGIN_CONTEXT; CORO_BEGIN_CONTEXT;
char buf[256]; Common::String buf;
CORO_END_CONTEXT(_ctx); CORO_END_CONTEXT(_ctx);
CORO_BEGIN_CODE(_ctx); CORO_BEGIN_CODE(_ctx);
@ -411,8 +404,8 @@ void TonyEngine::AutoSave(CORO_PARAM) {
CORO_INVOKE_0(MainWaitFrame); CORO_INVOKE_0(MainWaitFrame);
CORO_INVOKE_0(MainWaitFrame); CORO_INVOKE_0(MainWaitFrame);
MainFreeze(); MainFreeze();
GetSaveStateFileName(0, _ctx->buf); _ctx->buf = GetSaveStateFileName(0);
_theEngine.SaveState(_ctx->buf, (byte *)m_curThumbnail, "Autosave", true); _theEngine.SaveState(_ctx->buf.c_str(), (byte *)m_curThumbnail, "Autosave", true);
MainUnfreeze(); MainUnfreeze();
CORO_END_CODE; CORO_END_CODE;
@ -420,22 +413,20 @@ void TonyEngine::AutoSave(CORO_PARAM) {
void TonyEngine::SaveState(int n, const char *name) { void TonyEngine::SaveState(int n, const char *name) {
char buf[256]; Common::String buf = GetSaveStateFileName(n);
_theEngine.SaveState(buf.c_str(), (byte *)m_curThumbnail, name);
GetSaveStateFileName(n, buf);
_theEngine.SaveState(buf,(byte *)m_curThumbnail, name);
} }
void TonyEngine::LoadState(CORO_PARAM, int n) { void TonyEngine::LoadState(CORO_PARAM, int n) {
CORO_BEGIN_CONTEXT; CORO_BEGIN_CONTEXT;
char buf[256]; Common::String buf;
CORO_END_CONTEXT(_ctx); CORO_END_CONTEXT(_ctx);
CORO_BEGIN_CODE(_ctx); CORO_BEGIN_CODE(_ctx);
GetSaveStateFileName(n, _ctx->buf); _ctx->buf = GetSaveStateFileName(n);
CORO_INVOKE_1(_theEngine.LoadState, _ctx->buf); CORO_INVOKE_1(_theEngine.LoadState, _ctx->buf.c_str());
CORO_END_CODE; CORO_END_CODE;
} }
@ -485,8 +476,7 @@ void TonyEngine::CloseVoiceDatabase() {
} }
void TonyEngine::GrabThumbnail(void) { void TonyEngine::GrabThumbnail(void) {
//_window.GrabThumbnail(m_curThumbnail); _window.GrabThumbnail(m_curThumbnail);
warning("TODO: TonyEngine::GrabThumbnail");
} }
void TonyEngine::OptionScreen(void) { void TonyEngine::OptionScreen(void) {

View file

@ -194,7 +194,7 @@ public:
void AutoSave(CORO_PARAM); void AutoSave(CORO_PARAM);
void SaveState(int n, const char *name); void SaveState(int n, const char *name);
void LoadState(CORO_PARAM, int n); void LoadState(CORO_PARAM, int n);
void GetSaveStateFileName(int n, char *buf); Common::String GetSaveStateFileName(int n);
// Prende il thumbnail // Prende il thumbnail
void GrabThumbnail(void); void GrabThumbnail(void);

View file

@ -90,7 +90,8 @@ void RMWindow::Close(void) {
} }
void RMWindow::GrabThumbnail(uint16 *thumbmem) { void RMWindow::GrabThumbnail(uint16 *thumbmem) {
warning("TODO: RMWindow::GrabThumbnail"); m_bGrabThumbnail = true;
m_wThumbBuf = thumbmem;
} }
/** /**
@ -135,6 +136,14 @@ void RMWindow::GetNewFrame(byte *lpBuf, Common::Rect *rcBoundEllipse) {
// Standard screen copy // Standard screen copy
g_system->copyRectToScreen(lpBuf, RM_SX * 2, 0, 0, RM_SX, RM_SY); g_system->copyRectToScreen(lpBuf, RM_SX * 2, 0, 0, RM_SX, RM_SY);
} }
if (m_bGrabThumbnail) {
// Need to generate a thumbnail
RMSnapshot s;
s.GrabScreenshot(lpBuf, 4, m_wThumbBuf);
m_bGrabThumbnail = false;
}
} }
/** /**
@ -270,7 +279,6 @@ bool RMSnapshot::GetFreeSnapName(char *fn) {
} }
void RMSnapshot::GrabScreenshot(byte *lpBuf, int dezoom, uint16 *lpDestBuf) { void RMSnapshot::GrabScreenshot(byte *lpBuf, int dezoom, uint16 *lpDestBuf) {
#ifdef REFACTOR_ME
uint16 *src = (uint16 *)lpBuf; uint16 *src = (uint16 *)lpBuf;
int dimx = RM_SX / dezoom; int dimx = RM_SX / dezoom;
@ -278,15 +286,15 @@ void RMSnapshot::GrabScreenshot(byte *lpBuf, int dezoom, uint16 *lpDestBuf) {
int u, v, curv; int u, v, curv;
uint16 appo; uint16 appo;
uint32 k = 0; uint32 k = 0;
int sommar, sommab, sommag; int sommar, sommab, sommag;
uint16 *cursrc; uint16 *cursrc;
if (lpDestBuf == NULL) if (lpDestBuf == NULL)
src += (RM_SY - 1) * RM_BBX; src += (RM_SY - 1) * RM_BBX;
if (dezoom == 1 && 0) { // @@@ NON E' TESTATA MOLTO BENE!!! if (dezoom == 1 && 0) {
byte *curOut = rgb; byte *curOut = rgb;
for (int y = 0; y < dimy; y++) { for (int y = 0; y < dimy; y++) {
@ -329,9 +337,10 @@ void RMSnapshot::GrabScreenshot(byte *lpBuf, int dezoom, uint16 *lpDestBuf) {
rgb[k + 2] = (byte) (sommar * 8 / (dezoom * dezoom)); rgb[k + 2] = (byte) (sommar * 8 / (dezoom * dezoom));
if (lpDestBuf!=NULL) if (lpDestBuf!=NULL)
lpDestBuf[k/3] = ((int)rgb[k+0]>>3) | (((int)rgb[k+1]>>3)<<5) | (((int)rgb[k+2]>>3)<<10); lpDestBuf[k / 3] = ((int)rgb[k + 0] >> 3) | (((int)rgb[k + 1] >> 3) << 5) |
(((int)rgb[k + 2] >> 3) << 10);
k+=3; k += 3;
} }
if (lpDestBuf == NULL) if (lpDestBuf == NULL)
@ -340,48 +349,6 @@ void RMSnapshot::GrabScreenshot(byte *lpBuf, int dezoom, uint16 *lpDestBuf) {
src += RM_BBX * dezoom; src += RM_BBX * dezoom;
} }
} }
if (lpDestBuf == NULL) {
if (!GetFreeSnapName(filename))
return;
HANDLE hFile = CreateFile(filename,
GENERIC_WRITE,
0,
NULL,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN,
NULL);
BITMAPFILEHEADER bmfHeader;
bmfHeader.bfType = ((uint16) ('M' << 8) | 'B');
bmfHeader.bfSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + dimx * dimy * 3;
bmfHeader.bfReserved1 = 0;
bmfHeader.bfReserved2 = 0;
bmfHeader.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
BITMAPINFOHEADER bmiHeader;
bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bmiHeader.biWidth = dimx;
bmiHeader.biHeight = dimy;
bmiHeader.biPlanes = 1;
bmiHeader.biBitCount = 24;
bmiHeader.biCompression = BI_RGB;
bmiHeader.biSizeImage = dimx * dimy * 3;
bmiHeader.biXPelsPerMeter = 0xB12;
bmiHeader.biYPelsPerMeter = 0xB12;
bmiHeader.biClrUsed = 0;
bmiHeader.biClrImportant = 0;
uint32 dwWritten;
WriteFile(hFile, &bmfHeader, sizeof(BITMAPFILEHEADER), &dwWritten, NULL);
WriteFile(hFile, &bmiHeader, sizeof(BITMAPINFOHEADER), &dwWritten, NULL);
WriteFile(hFile, rgb, dimx * dimy * 3, &dwWritten, NULL);
CloseHandle(hFile);
}
#endif
} }
} // End of namespace Tony } // End of namespace Tony