2017-05-31 18:49:03 +02:00
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers , whose names
* are too numerous to list here . Please refer to the COPYRIGHT
* file distributed with this source distribution .
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation ; either version 2
* of the License , or ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 51 Franklin Street , Fifth Floor , Boston , MA 02110 - 1301 , USA .
*
*/
2017-08-29 01:22:53 +02:00
# include "audio/mods/protracker.h"
2017-05-31 18:49:03 +02:00
# include "common/config-manager.h"
# include "common/debug.h"
# include "common/debug-channels.h"
2017-08-29 01:22:53 +02:00
# include "common/endian.h"
2017-05-31 18:49:03 +02:00
# include "common/error.h"
2017-06-03 22:58:42 +02:00
# include "common/events.h"
2017-05-31 18:49:03 +02:00
# include "common/file.h"
# include "common/fs.h"
2017-08-29 01:22:53 +02:00
# include "common/memstream.h"
# include "common/savefile.h"
2017-06-03 22:58:42 +02:00
# include "common/str.h"
# include "common/system.h"
2017-09-30 23:26:06 +01:00
# include "common/translation.h"
2017-05-31 18:49:03 +02:00
# include "engines/util.h"
2017-06-03 22:58:42 +02:00
# include "graphics/cursorman.h"
# include "graphics/surface.h"
# include "graphics/screen.h"
# include "graphics/palette.h"
2017-08-29 01:22:53 +02:00
# include "graphics/thumbnail.h"
# include "gui/saveload.h"
2017-05-31 18:49:03 +02:00
2018-03-21 12:14:24 +01:00
# include "supernova/sound.h"
2017-05-31 18:49:03 +02:00
# include "supernova/supernova.h"
2017-07-11 05:33:46 +02:00
# include "supernova/state.h"
2017-05-31 18:49:03 +02:00
namespace Supernova {
2017-06-24 13:43:42 +02:00
const AudioInfo audioInfo [ kAudioNumSamples ] = {
2017-09-24 17:53:01 +01:00
{ 44 , 0 , - 1 } ,
{ 45 , 0 , - 1 } ,
{ 46 , 0 , 2510 } ,
{ 46 , 2510 , 4020 } ,
{ 46 , 4020 , - 1 } ,
{ 47 , 0 , 24010 } ,
{ 47 , 24010 , - 1 } ,
{ 48 , 0 , 2510 } ,
{ 48 , 2510 , 10520 } ,
{ 48 , 10520 , 13530 } ,
{ 48 , 13530 , - 1 } ,
{ 50 , 0 , 12786 } ,
{ 50 , 12786 , - 1 } ,
{ 51 , 0 , - 1 } ,
{ 53 , 0 , - 1 } ,
{ 54 , 0 , 8010 } ,
{ 54 , 8010 , 24020 } ,
{ 54 , 24020 , 30030 } ,
{ 54 , 30030 , 31040 } ,
{ 54 , 31040 , - 1 }
2017-06-24 13:43:42 +02:00
} ;
2018-01-01 13:54:43 +01:00
const Object Object : : nullObject ;
2017-06-15 19:55:09 +02:00
ObjectType operator | ( ObjectType a , ObjectType b ) {
return static_cast < ObjectType > ( + a | + b ) ;
}
2017-06-16 10:25:41 +02:00
ObjectType operator & ( ObjectType a , ObjectType b ) {
return static_cast < ObjectType > ( + a & + b ) ;
}
2017-06-15 19:55:09 +02:00
ObjectType operator ^ ( ObjectType a , ObjectType b ) {
return static_cast < ObjectType > ( + a ^ + b ) ;
}
ObjectType & operator | = ( ObjectType & a , ObjectType b ) {
return a = a | b ;
}
2017-06-16 10:25:41 +02:00
ObjectType & operator & = ( ObjectType & a , ObjectType b ) {
return a = a & b ;
}
2017-06-15 19:55:09 +02:00
ObjectType & operator ^ = ( ObjectType & a , ObjectType b ) {
return a = a ^ b ;
}
2017-05-31 18:49:03 +02:00
SupernovaEngine : : SupernovaEngine ( OSystem * syst )
2017-09-24 17:53:01 +01:00
: Engine ( syst )
, _console ( NULL )
, _gm ( NULL )
2017-11-17 23:35:19 +00:00
, _currentImage ( NULL )
2018-01-07 23:40:33 +01:00
, _soundMusicIntro ( NULL )
, _soundMusicOutro ( NULL )
2018-01-08 00:58:18 +01:00
, _rnd ( " supernova " )
2017-09-24 17:53:01 +01:00
, _brightness ( 255 )
, _menuBrightness ( 255 )
, _delay ( 33 )
, _textSpeed ( kTextSpeed [ 2 ] )
, _screenWidth ( 320 )
, _screenHeight ( 200 )
, _messageDisplayed ( false )
, _allowLoadGame ( true )
, _allowSaveGame ( true )
2017-05-31 18:49:03 +02:00
{
2017-06-03 22:58:42 +02:00
// const Common::FSNode gameDataDir(ConfMan.get("path"));
// SearchMan.addSubDirectoryMatching(gameDataDir, "sound");
2017-05-31 18:49:03 +02:00
2017-11-07 23:54:41 +00:00
if ( ConfMan . hasKey ( " textspeed " ) )
_textSpeed = ConfMan . getInt ( " textspeed " ) ;
2017-05-31 18:49:03 +02:00
// setup engine specific debug channels
DebugMan . addDebugChannel ( kDebugGeneral , " general " , " Supernova general debug channel " ) ;
}
SupernovaEngine : : ~ SupernovaEngine ( ) {
DebugMan . clearAllDebugChannels ( ) ;
2017-06-15 22:16:48 +02:00
2017-11-17 23:35:19 +00:00
delete _currentImage ;
2017-06-03 22:58:42 +02:00
delete _console ;
2017-07-23 12:29:11 +02:00
delete _gm ;
2018-03-21 12:14:24 +01:00
delete _sound ;
2018-01-07 23:40:33 +01:00
delete _soundMusicIntro ;
delete _soundMusicOutro ;
2017-05-31 18:49:03 +02:00
}
Common : : Error SupernovaEngine : : run ( ) {
2018-01-11 01:17:51 +01:00
Graphics : : ModeList modes ;
modes . push_back ( Graphics : : Mode ( 320 , 200 ) ) ;
modes . push_back ( Graphics : : Mode ( 640 , 480 ) ) ;
initGraphicsModes ( modes ) ;
2017-06-24 14:45:23 +02:00
initGraphics ( _screenWidth , _screenHeight ) ;
2017-09-30 23:26:06 +01:00
Common : : Error status = loadGameStrings ( ) ;
if ( status . getCode ( ) ! = Common : : kNoError )
return status ;
2017-06-03 22:58:42 +02:00
initData ( ) ;
2017-06-10 00:17:19 +02:00
initPalette ( ) ;
2017-06-03 22:58:42 +02:00
2018-03-21 12:14:24 +01:00
_sound = new Sound ( this , _mixer ) ;
_gm = new GameManager ( this , _sound ) ;
_console = new Console ( this , _gm ) ;
2017-07-11 05:33:46 +02:00
CursorMan . replaceCursor ( _mouseNormal , 16 , 16 , 0 , 0 , kColorCursorTransparent ) ;
2017-06-26 22:08:57 +02:00
CursorMan . replaceCursorPalette ( initVGAPalette , 0 , 16 ) ;
2017-06-15 22:16:48 +02:00
CursorMan . showMouse ( true ) ;
2017-09-05 19:43:18 +01:00
setTotalPlayTime ( 0 ) ;
2017-09-04 23:04:01 +01:00
int saveSlot = ConfMan . getInt ( " save_slot " ) ;
if ( saveSlot > = 0 ) {
if ( loadGameState ( saveSlot ) . getCode ( ) ! = Common : : kNoError )
error ( " Failed to load save game from slot %i " , saveSlot ) ;
}
2017-08-27 17:10:08 +02:00
while ( ! shouldQuit ( ) ) {
2017-07-28 10:54:00 +02:00
uint32 start = _system - > getMillis ( ) ;
2018-03-11 22:07:13 +01:00
_gm - > updateEvents ( ) ;
2017-07-23 12:29:11 +02:00
_gm - > executeRoom ( ) ;
2017-06-23 19:48:26 +02:00
_console - > onFrame ( ) ;
2017-06-03 22:58:42 +02:00
_system - > updateScreen ( ) ;
2017-07-28 10:54:00 +02:00
int end = _delay - ( _system - > getMillis ( ) - start ) ;
if ( end > 0 )
_system - > delayMillis ( end ) ;
2017-06-03 22:58:42 +02:00
}
2017-06-15 22:16:48 +02:00
2018-03-21 12:14:24 +01:00
_mixer - > stopAll ( ) ;
2017-05-31 18:49:03 +02:00
return Common : : kNoError ;
}
2017-06-05 16:27:06 +02:00
2017-08-27 17:10:08 +02:00
bool SupernovaEngine : : hasFeature ( EngineFeature f ) const {
switch ( f ) {
case kSupportsRTL :
return true ;
2017-08-29 01:27:07 +02:00
case kSupportsLoadingDuringRuntime :
return true ;
case kSupportsSavingDuringRuntime :
return true ;
2017-08-27 17:10:08 +02:00
default :
return false ;
}
}
2017-08-29 01:22:53 +02:00
void SupernovaEngine : : pauseEngineIntern ( bool pause ) {
_mixer - > pauseAll ( pause ) ;
2017-11-07 23:51:27 +00:00
_gm - > pauseTimer ( pause ) ;
2017-08-29 01:22:53 +02:00
}
2017-09-30 23:26:06 +01:00
Common : : Error SupernovaEngine : : loadGameStrings ( ) {
Common : : String cur_lang = ConfMan . get ( " language " ) ;
Common : : String string_id ( " TEXT " ) ;
// Note: we don't print any warning or errors here if we cannot find the file
// or the format is not as expected. We will get those warning when reading the
// strings anyway (actually the engine will even refuse to start).
Common : : File f ;
if ( ! f . open ( SUPERNOVA_DAT ) ) {
Common : : String msg = Common : : String : : format ( _ ( " Unable to locate the '%s' engine data file. " ) , SUPERNOVA_DAT ) ;
GUIErrorMessage ( msg ) ;
return Common : : kReadingFailed ;
}
// Validate the data file header
char id [ 5 ] , lang [ 5 ] ;
id [ 4 ] = lang [ 4 ] = ' \0 ' ;
f . read ( id , 3 ) ;
if ( strncmp ( id , " MSN " , 3 ) ! = 0 ) {
Common : : String msg = Common : : String : : format ( _ ( " The '%s' engine data file is corrupt. " ) , SUPERNOVA_DAT ) ;
GUIErrorMessage ( msg ) ;
return Common : : kReadingFailed ;
}
2018-03-09 11:20:47 +01:00
2017-09-30 23:26:06 +01:00
int version = f . readByte ( ) ;
if ( version ! = SUPERNOVA_DAT_VERSION ) {
Common : : String msg = Common : : String : : format (
_ ( " Incorrect version of the '%s' engine data file found. Expected %d but got %d. " ) ,
SUPERNOVA_DAT , SUPERNOVA_DAT_VERSION , version ) ;
GUIErrorMessage ( msg ) ;
return Common : : kReadingFailed ;
}
while ( ! f . eos ( ) ) {
f . read ( id , 4 ) ;
f . read ( lang , 4 ) ;
uint32 size = f . readUint32LE ( ) ;
if ( f . eos ( ) )
break ;
if ( string_id = = id & & cur_lang = = lang ) {
while ( size > 0 ) {
Common : : String s ;
char ch ;
while ( ( ch = ( char ) f . readByte ( ) ) ! = ' \0 ' )
s + = ch ;
_gameStrings . push_back ( s ) ;
size - = s . size ( ) + 1 ;
}
return Common : : kNoError ;
} else
f . skip ( size ) ;
}
Common : : Language l = Common : : parseLanguage ( cur_lang ) ;
Common : : String msg = Common : : String : : format ( _ ( " Unable to locate the text for %s language in '%s' engine data file. " ) , Common : : getLanguageDescription ( l ) , SUPERNOVA_DAT ) ;
GUIErrorMessage ( msg ) ;
return Common : : kReadingFailed ;
}
2017-06-05 16:27:06 +02:00
void SupernovaEngine : : initData ( ) {
2017-06-23 19:48:26 +02:00
// Sound
2017-09-05 21:17:00 +01:00
// Note:
// - samples start with a header of 6 bytes: 01 SS SS 00 AD 00
// where SS SS (LE uint16) is the size of the sound sample + 2
// - samples end with a footer of 4 bytes: 00 00
// Skip those in the buffer
2017-06-24 13:43:42 +02:00
Common : : File file ;
for ( int i = 0 ; i < kAudioNumSamples ; + + i ) {
if ( ! file . open ( Common : : String : : format ( " msn_data.%03d " , audioInfo [ i ] . _filenumber ) ) ) {
error ( " File %s could not be read! " , file . getName ( ) ) ;
}
if ( audioInfo [ i ] . _offsetEnd = = - 1 ) {
file . seek ( 0 , SEEK_END ) ;
2017-09-05 21:17:00 +01:00
_soundSamples [ i ] . _length = file . pos ( ) - audioInfo [ i ] . _offsetStart - 10 ;
2017-06-24 13:43:42 +02:00
} else {
2017-09-05 21:17:00 +01:00
_soundSamples [ i ] . _length = audioInfo [ i ] . _offsetEnd - audioInfo [ i ] . _offsetStart - 10 ;
2017-06-24 13:43:42 +02:00
}
_soundSamples [ i ] . _buffer = new byte [ _soundSamples [ i ] . _length ] ;
2017-09-05 21:17:00 +01:00
file . seek ( audioInfo [ i ] . _offsetStart + 6 ) ;
2017-06-24 13:43:42 +02:00
file . read ( _soundSamples [ i ] . _buffer , _soundSamples [ i ] . _length ) ;
file . close ( ) ;
}
2017-06-26 22:08:57 +02:00
2018-03-21 12:14:24 +01:00
_soundMusicIntro = convertToMod ( " msn_data.052 " ) ;
_soundMusicOutro = convertToMod ( " msn_data.049 " ) ;
2017-06-30 16:00:14 +02:00
2017-06-26 22:08:57 +02:00
// Cursor
const uint16 * bufferNormal = reinterpret_cast < const uint16 * > ( mouseNormal ) ;
const uint16 * bufferWait = reinterpret_cast < const uint16 * > ( mouseWait ) ;
for ( uint i = 0 ; i < sizeof ( mouseNormal ) / 4 ; + + i ) {
for ( uint bit = 0 ; bit < 16 ; + + bit ) {
uint mask = 0x8000 > > bit ;
uint bitIndex = i * 16 + bit ;
2017-06-30 16:35:33 +02:00
_mouseNormal [ bitIndex ] = ( READ_LE_UINT16 ( bufferNormal + i ) & mask ) ? kColorCursorTransparent : kColorBlack ;
if ( READ_LE_UINT16 ( bufferNormal + i + 16 ) & mask )
2017-06-26 22:08:57 +02:00
_mouseNormal [ bitIndex ] = kColorLightRed ;
2017-06-30 16:35:33 +02:00
_mouseWait [ bitIndex ] = ( READ_LE_UINT16 ( bufferWait + i ) & mask ) ? kColorCursorTransparent : kColorBlack ;
if ( READ_LE_UINT16 ( bufferWait + i + 16 ) & mask )
2017-06-26 22:08:57 +02:00
_mouseWait [ bitIndex ] = kColorLightRed ;
}
}
2017-06-05 16:27:06 +02:00
}
void SupernovaEngine : : initPalette ( ) {
2017-06-10 00:17:19 +02:00
_system - > getPaletteManager ( ) - > setPalette ( initVGAPalette , 0 , 256 ) ;
2017-06-03 22:58:42 +02:00
}
2017-06-24 13:43:42 +02:00
void SupernovaEngine : : playSound ( AudioIndex sample ) {
2018-03-21 12:14:24 +01:00
_sound - > play ( sample ) ;
2017-06-03 22:58:42 +02:00
}
2018-03-21 12:14:24 +01:00
void SupernovaEngine : : stopAudio ( ) {
_sound - > stop ( ) ;
2017-06-03 22:58:42 +02:00
}
2018-03-21 12:14:24 +01:00
void SupernovaEngine : : playSound ( MusicIndex index ) {
_sound - > play ( index ) ;
2017-06-05 16:27:06 +02:00
}
2017-11-16 02:34:51 +00:00
void SupernovaEngine : : renderImageSection ( int section ) {
2017-11-05 02:36:45 +00:00
// Note: inverting means we are removing the section. So we should get the rect for that
// section but draw the background (section 0) instead.
bool invert = false ;
if ( section > 128 ) {
2017-07-17 16:28:06 +02:00
section - = 128 ;
2017-11-05 02:36:45 +00:00
invert = true ;
}
2017-11-16 02:34:51 +00:00
if ( ! _currentImage | | section > _currentImage - > _numSections - 1 )
2017-06-28 14:09:13 +02:00
return ;
2017-11-16 02:34:51 +00:00
Common : : Rect sectionRect ( _currentImage - > _section [ section ] . x1 ,
2018-03-09 11:20:47 +01:00
_currentImage - > _section [ section ] . y1 ,
_currentImage - > _section [ section ] . x2 + 1 ,
_currentImage - > _section [ section ] . y2 + 1 ) ;
2017-11-16 02:34:51 +00:00
if ( _currentImage - > _filenumber = = 1 | | _currentImage - > _filenumber = = 2 ) {
2017-06-24 14:45:23 +02:00
sectionRect . setWidth ( 640 ) ;
sectionRect . setHeight ( 480 ) ;
if ( _screenWidth ! = 640 ) {
_screenWidth = 640 ;
_screenHeight = 480 ;
initGraphics ( _screenWidth , _screenHeight ) ;
}
} else {
if ( _screenWidth ! = 320 ) {
_screenWidth = 320 ;
_screenHeight = 200 ;
initGraphics ( _screenWidth , _screenHeight ) ;
}
}
2017-11-05 02:59:22 +00:00
uint offset = 0 ;
2017-11-05 18:49:10 +00:00
int pitch = sectionRect . width ( ) ;
2017-11-05 02:59:22 +00:00
if ( invert ) {
2017-11-16 02:34:51 +00:00
pitch = _currentImage - > _pitch ;
offset = _currentImage - > _section [ section ] . y1 * pitch + _currentImage - > _section [ section ] . x1 ;
2017-07-17 16:28:06 +02:00
section = 0 ;
2017-11-05 02:59:22 +00:00
}
2017-07-17 16:28:06 +02:00
2017-11-16 02:34:51 +00:00
_system - > copyRectToScreen ( static_cast < const byte * > ( _currentImage - > _sectionSurfaces [ section ] - > getPixels ( ) ) + offset ,
2018-03-09 11:20:47 +01:00
pitch ,
sectionRect . left , sectionRect . top ,
sectionRect . width ( ) , sectionRect . height ( ) ) ;
2017-06-18 21:04:23 +02:00
}
2017-11-16 02:34:51 +00:00
void SupernovaEngine : : renderImage ( int section ) {
2017-11-17 23:35:19 +00:00
if ( ! _currentImage )
return ;
2017-11-16 02:34:51 +00:00
bool sectionVisible = true ;
if ( section > 128 ) {
sectionVisible = false ;
section - = 128 ;
}
_gm - > _currentRoom - > setSectionVisible ( section , sectionVisible ) ;
do {
if ( sectionVisible )
renderImageSection ( section ) ;
else
renderImageSection ( section + 128 ) ;
section = _currentImage - > _section [ section ] . next ;
} while ( section ! = 0 ) ;
}
bool SupernovaEngine : : setCurrentImage ( int filenumber ) {
2017-11-17 23:35:19 +00:00
if ( _currentImage & & _currentImage - > _filenumber = = filenumber )
return true ;
delete _currentImage ;
_currentImage = new MSNImageDecoder ( ) ;
if ( ! _currentImage - > init ( filenumber ) ) {
delete _currentImage ;
_currentImage = NULL ;
2017-11-16 02:34:51 +00:00
return false ;
2017-11-16 02:49:03 +00:00
}
2017-11-16 02:34:51 +00:00
_system - > getPaletteManager ( ) - > setPalette ( _currentImage - > getPalette ( ) , 16 , 239 ) ;
paletteBrightness ( ) ;
return true ;
2017-06-23 19:48:26 +02:00
}
2017-06-18 21:04:23 +02:00
void SupernovaEngine : : saveScreen ( int x , int y , int width , int height ) {
_screenBuffer . push ( x , y , width , height ) ;
}
2018-02-23 19:32:21 +01:00
void SupernovaEngine : : saveScreen ( const GuiElement & guiElement ) {
saveScreen ( guiElement . left , guiElement . top , guiElement . width ( ) , guiElement . height ( ) ) ;
}
2017-06-18 21:04:23 +02:00
void SupernovaEngine : : restoreScreen ( ) {
_screenBuffer . restore ( ) ;
}
void SupernovaEngine : : renderRoom ( Room & room ) {
2017-12-14 00:11:01 +00:00
if ( room . getId ( ) = = INTRO )
return ;
2017-11-16 02:34:51 +00:00
if ( setCurrentImage ( room . getFileNumber ( ) ) ) {
2017-07-17 16:39:16 +02:00
for ( int i = 0 ; i < _currentImage - > _numSections ; + + i ) {
2017-08-13 21:34:36 +02:00
int section = i ;
if ( room . isSectionVisible ( section ) ) {
do {
2017-11-16 02:34:51 +00:00
renderImageSection ( section ) ;
2017-08-13 21:34:36 +02:00
section = _currentImage - > _section [ section ] . next ;
} while ( section ! = 0 ) ;
}
2017-07-11 05:33:46 +02:00
}
2017-06-13 20:22:37 +02:00
}
2017-06-03 22:58:42 +02:00
}
2017-08-04 10:42:02 +02:00
int SupernovaEngine : : textWidth ( const uint16 key ) {
2017-09-08 02:56:22 +01:00
char text [ 2 ] ;
text [ 0 ] = key & 0xFF ;
text [ 1 ] = 0 ;
2017-08-04 10:42:02 +02:00
return textWidth ( text ) ;
}
2017-07-18 23:29:12 +02:00
int SupernovaEngine : : textWidth ( const char * text ) {
2017-06-08 12:15:24 +02:00
int charWidth = 0 ;
while ( * text ! = ' \0 ' ) {
byte c = * text + + ;
if ( c < 32 ) {
continue ;
} else if ( c = = 225 ) {
c = 35 ;
}
2017-06-23 19:53:05 +02:00
for ( uint i = 0 ; i < 5 ; + + i ) {
2017-06-08 12:15:24 +02:00
if ( font [ c - 32 ] [ i ] = = 0xff ) {
break ;
}
2017-07-19 00:20:40 +02:00
+ + charWidth ;
2017-06-08 12:15:24 +02:00
}
2017-07-19 00:20:40 +02:00
+ + charWidth ;
2017-06-08 12:15:24 +02:00
}
return charWidth ;
}
2017-06-07 18:34:11 +02:00
2017-06-18 18:28:01 +02:00
void SupernovaEngine : : renderMessage ( const char * text , MessagePosition position ) {
Common : : String t ( text ) ;
2017-06-09 06:47:56 +02:00
char * row [ 20 ] ;
2017-06-18 18:28:01 +02:00
Common : : String : : iterator p = t . begin ( ) ;
2017-06-23 19:53:05 +02:00
uint numRows = 0 ;
2017-06-09 06:47:56 +02:00
int rowWidthMax = 0 ;
int x = 0 ;
int y = 0 ;
byte textColor = 0 ;
2017-06-15 22:16:48 +02:00
2017-06-09 06:47:56 +02:00
while ( * p ! = ' \0 ' ) {
row [ numRows ] = p ;
+ + numRows ;
while ( ( * p ! = ' \0 ' ) & & ( * p ! = ' | ' ) ) {
+ + p ;
}
if ( * p = = ' | ' ) {
* p = ' \0 ' ;
+ + p ;
}
}
2017-06-23 19:53:05 +02:00
for ( uint i = 0 ; i < numRows ; + + i ) {
2017-07-18 23:29:12 +02:00
int rowWidth = textWidth ( row [ i ] ) ;
2017-06-09 06:47:56 +02:00
if ( rowWidth > rowWidthMax )
rowWidthMax = rowWidth ;
}
2017-06-15 22:16:48 +02:00
2017-06-09 06:47:56 +02:00
switch ( position ) {
case kMessageNormal :
2017-07-08 22:01:26 +02:00
x = 160 - rowWidthMax / 2 ;
2017-08-04 11:35:51 +02:00
textColor = kColorWhite99 ;
2017-06-09 06:47:56 +02:00
break ;
case kMessageTop :
2017-07-08 22:01:26 +02:00
x = 160 - rowWidthMax / 2 ;
2017-06-15 22:16:48 +02:00
textColor = kColorLightYellow ;
2017-06-09 06:47:56 +02:00
break ;
case kMessageCenter :
2017-07-08 22:01:26 +02:00
x = 160 - rowWidthMax / 2 ;
2017-06-15 22:16:48 +02:00
textColor = kColorLightRed ;
2017-06-09 06:47:56 +02:00
break ;
case kMessageLeft :
x = 3 ;
2017-06-15 22:16:48 +02:00
textColor = kColorLightYellow ;
2017-06-09 06:47:56 +02:00
break ;
case kMessageRight :
x = 317 - rowWidthMax ;
2017-06-15 22:16:48 +02:00
textColor = kColorLightGreen ;
2017-06-09 06:47:56 +02:00
break ;
}
2017-06-15 22:16:48 +02:00
2017-06-09 06:47:56 +02:00
if ( position = = kMessageNormal ) {
y = 70 - ( ( numRows * 9 ) / 2 ) ;
} else if ( position = = kMessageTop ) {
y = 5 ;
} else {
y = 142 ;
}
2017-06-15 22:16:48 +02:00
2017-06-09 06:47:56 +02:00
int message_columns = x - 3 ;
int message_rows = y - 3 ;
int message_width = rowWidthMax + 6 ;
int message_height = numRows * 9 + 5 ;
2017-07-11 05:33:46 +02:00
saveScreen ( message_columns , message_rows , message_width , message_height ) ;
2017-08-04 11:35:51 +02:00
renderBox ( message_columns , message_rows , message_width , message_height , kColorWhite35 ) ;
2017-06-23 19:53:05 +02:00
for ( uint i = 0 ; i < numRows ; + + i ) {
2017-06-09 06:47:56 +02:00
renderText ( row [ i ] , x , y , textColor ) ;
y + = 9 ;
}
2017-06-15 22:16:48 +02:00
2017-06-18 18:28:01 +02:00
_messageDisplayed = true ;
2018-03-11 23:13:36 +01:00
_gm - > _messageDuration = ( Common : : strnlen ( text , 512 ) + 20 ) * _textSpeed / 10 ;
2017-06-18 18:28:01 +02:00
}
void SupernovaEngine : : removeMessage ( ) {
2017-07-17 16:37:05 +02:00
if ( _messageDisplayed ) {
restoreScreen ( ) ;
_messageDisplayed = false ;
}
2017-06-09 06:47:56 +02:00
}
void SupernovaEngine : : renderText ( const char * text , int x , int y , byte color ) {
Graphics : : Surface * screen = _system - > lockScreen ( ) ;
byte * cursor = static_cast < byte * > ( screen - > getBasePtr ( x , y ) ) ;
2017-06-21 07:45:25 +02:00
const byte * basePtr = cursor ;
2017-06-09 06:47:56 +02:00
byte c ;
while ( ( c = * text + + ) ! = ' \0 ' ) {
if ( c < 32 ) {
continue ;
} else if ( c = = 225 ) {
c = 128 ;
}
2017-06-15 22:16:48 +02:00
2017-06-23 19:53:05 +02:00
for ( uint i = 0 ; i < 5 ; + + i ) {
2017-06-09 06:47:56 +02:00
if ( font [ c - 32 ] [ i ] = = 0xff ) {
break ;
}
byte * ascentLine = cursor ;
for ( byte j = font [ c - 32 ] [ i ] ; j ! = 0 ; j > > = 1 ) {
if ( j & 1 ) {
* cursor = color ;
}
cursor + = kScreenWidth ;
}
cursor = + + ascentLine ;
}
2017-07-19 00:20:40 +02:00
+ + cursor ;
2017-06-09 06:47:56 +02:00
}
_system - > unlockScreen ( ) ;
2017-06-21 07:45:25 +02:00
2017-06-23 19:53:05 +02:00
uint numChars = cursor - basePtr ;
uint absPosition = y * kScreenWidth + x + numChars ;
2017-06-21 07:45:25 +02:00
_textCursorX = absPosition % kScreenWidth ;
_textCursorY = absPosition / kScreenWidth ;
_textColor = color ;
}
2017-08-04 10:42:02 +02:00
void SupernovaEngine : : renderText ( const uint16 character , int x , int y , byte color ) {
2017-09-08 02:56:22 +01:00
char text [ 2 ] ;
text [ 0 ] = character & 0xFF ;
text [ 1 ] = 0 ;
2017-08-04 10:42:02 +02:00
renderText ( text , x , y , color ) ;
}
2017-06-21 07:45:25 +02:00
void SupernovaEngine : : renderText ( const char * text ) {
renderText ( text , _textCursorX , _textCursorY , _textColor ) ;
2017-06-09 06:47:56 +02:00
}
2017-08-04 10:42:02 +02:00
void SupernovaEngine : : renderText ( const uint16 character ) {
2017-09-08 02:56:22 +01:00
char text [ 2 ] ;
text [ 0 ] = character & 0xFF ;
text [ 1 ] = 0 ;
2017-08-04 10:42:02 +02:00
renderText ( text , _textCursorX , _textCursorY , _textColor ) ;
}
2018-02-23 19:32:21 +01:00
void SupernovaEngine : : renderText ( const GuiElement & guiElement ) {
renderText ( guiElement . getText ( ) , guiElement . getTextPos ( ) . x ,
2018-03-09 21:10:57 +01:00
guiElement . getTextPos ( ) . y , guiElement . getTextColor ( ) ) ;
2018-02-23 19:32:21 +01:00
}
2017-08-04 10:42:02 +02:00
2017-06-07 18:34:11 +02:00
void SupernovaEngine : : renderBox ( int x , int y , int width , int height , byte color ) {
Graphics : : Surface * screen = _system - > lockScreen ( ) ;
2017-07-04 01:00:27 +02:00
screen - > fillRect ( Common : : Rect ( x , y , x + width , y + height ) , color ) ;
2017-06-07 18:34:11 +02:00
_system - > unlockScreen ( ) ;
}
2018-02-23 19:32:21 +01:00
void SupernovaEngine : : renderBox ( const GuiElement & guiElement ) {
renderBox ( guiElement . left , guiElement . top , guiElement . width ( ) ,
2018-03-09 21:10:57 +01:00
guiElement . height ( ) , guiElement . getBackgroundColor ( ) ) ;
2018-02-23 19:32:21 +01:00
}
2017-06-10 00:17:19 +02:00
void SupernovaEngine : : paletteBrightness ( ) {
byte palette [ 768 ] ;
2017-06-15 22:16:48 +02:00
2017-06-12 11:11:35 +02:00
_system - > getPaletteManager ( ) - > grabPalette ( palette , 0 , 255 ) ;
2017-06-23 19:53:05 +02:00
for ( uint i = 0 ; i < 48 ; + + i ) {
2017-06-10 00:17:19 +02:00
palette [ i ] = ( initVGAPalette [ i ] * _menuBrightness ) > > 8 ;
}
2017-06-23 19:53:05 +02:00
for ( uint i = 0 ; i < 717 ; + + i ) {
2017-06-10 00:17:19 +02:00
const byte * imagePalette ;
2017-11-17 23:35:19 +00:00
if ( _currentImage & & _currentImage - > getPalette ( ) ) {
2017-06-23 19:48:26 +02:00
imagePalette = _currentImage - > getPalette ( ) ;
2017-06-10 00:17:19 +02:00
} else {
2017-11-07 00:04:39 +00:00
imagePalette = palette + 48 ;
2017-06-10 00:17:19 +02:00
}
2017-06-12 11:11:35 +02:00
palette [ i + 48 ] = ( imagePalette [ i ] * _brightness ) > > 8 ;
2017-06-10 00:17:19 +02:00
}
2017-06-12 11:11:35 +02:00
_system - > getPaletteManager ( ) - > setPalette ( palette , 0 , 255 ) ;
2017-05-31 18:49:03 +02:00
}
2017-06-08 12:15:24 +02:00
2017-06-10 00:17:19 +02:00
void SupernovaEngine : : paletteFadeOut ( ) {
2017-11-18 00:49:23 +00:00
while ( _menuBrightness > 10 ) {
_menuBrightness - = 10 ;
if ( _brightness > _menuBrightness )
_brightness = _menuBrightness ;
2017-06-10 00:17:19 +02:00
paletteBrightness ( ) ;
_system - > updateScreen ( ) ;
2017-06-13 20:22:37 +02:00
_system - > delayMillis ( _delay ) ;
2017-06-10 00:17:19 +02:00
}
_menuBrightness = 0 ;
_brightness = 0 ;
paletteBrightness ( ) ;
_system - > updateScreen ( ) ;
}
void SupernovaEngine : : paletteFadeIn ( ) {
2017-11-18 00:49:23 +00:00
while ( _menuBrightness < 245 ) {
if ( _brightness < _gm - > _roomBrightness )
_brightness + = 10 ;
_menuBrightness + = 10 ;
2017-06-10 00:17:19 +02:00
paletteBrightness ( ) ;
_system - > updateScreen ( ) ;
2017-06-13 20:22:37 +02:00
_system - > delayMillis ( _delay ) ;
2017-06-10 00:17:19 +02:00
}
_menuBrightness = 255 ;
2017-11-18 00:49:23 +00:00
_brightness = _gm - > _roomBrightness ;
2017-06-10 00:17:19 +02:00
paletteBrightness ( ) ;
_system - > updateScreen ( ) ;
}
2017-06-18 21:04:23 +02:00
void SupernovaEngine : : setColor63 ( byte value ) {
byte color [ 3 ] = { value , value , value } ;
_system - > getPaletteManager ( ) - > setPalette ( color , 63 , 1 ) ;
}
2017-11-07 23:54:41 +00:00
void SupernovaEngine : : setTextSpeed ( ) {
const Common : : String & textSpeedString = getGameString ( kStringTextSpeed ) ;
int stringWidth = textWidth ( textSpeedString ) ;
int textX = ( 320 - stringWidth ) / 2 ;
int textY = 100 ;
stringWidth + = 4 ;
int boxX = stringWidth > 110 ? ( 320 - stringWidth ) / 2 : 105 ;
int boxY = 97 ;
int boxWidth = stringWidth > 110 ? stringWidth : 110 ;
int boxHeight = 27 ;
_gm - > animationOff ( ) ;
_gm - > saveTime ( ) ;
saveScreen ( boxX , boxY , boxWidth , boxHeight ) ;
renderBox ( boxX , boxY , boxWidth , boxHeight , kColorBlue ) ;
renderText ( textSpeedString , textX , textY , kColorWhite99 ) ; // Text speed
// Find the closest index in kTextSpeed for the current _textSpeed.
// Important note: values in kTextSpeed decrease with the index.
int speedIndex = 0 ;
while ( speedIndex < 4 & & _textSpeed < ( kTextSpeed [ speedIndex ] + kTextSpeed [ speedIndex + 1 ] ) / 2 )
+ + speedIndex ;
char nbString [ 2 ] ;
nbString [ 1 ] = 0 ;
for ( int i = 0 ; i < 5 ; + + i ) {
byte color = i = = speedIndex ? kColorWhite63 : kColorWhite35 ;
renderBox ( 110 + 21 * i , 111 , 16 , 10 , color ) ;
nbString [ 0 ] = ' 1 ' + i ;
renderText ( nbString , 115 + 21 * i , 112 , kColorWhite99 ) ;
}
do {
_gm - > getInput ( ) ;
int key = _gm - > _keyPressed ? _gm - > _key . keycode : Common : : KEYCODE_INVALID ;
if ( ! _gm - > _keyPressed & & _gm - > _mouseClicked & & _gm - > _mouseY > = 111 & & _gm - > _mouseY < 121 & & ( _gm - > _mouseX + 16 ) % 21 < 16 )
key = Common : : KEYCODE_0 - 5 + ( _gm - > _mouseX + 16 ) / 21 ;
if ( key = = Common : : KEYCODE_ESCAPE )
break ;
else if ( key > = Common : : KEYCODE_1 & & key < = Common : : KEYCODE_5 ) {
speedIndex = key - Common : : KEYCODE_1 ;
_textSpeed = kTextSpeed [ speedIndex ] ;
ConfMan . setInt ( " textspeed " , _textSpeed ) ;
break ;
}
} while ( ! shouldQuit ( ) ) ;
_gm - > resetInputState ( ) ;
restoreScreen ( ) ;
_gm - > loadTime ( ) ;
_gm - > animationOn ( ) ;
}
2018-02-23 18:25:53 +01:00
bool SupernovaEngine : : quitGameDialog ( ) {
bool quit = false ;
GuiElement guiQuitBox ;
guiQuitBox . setColor ( kColorRed , kColorWhite99 , kColorRed , kColorWhite99 ) ;
guiQuitBox . setSize ( 112 , 97 , 112 + 96 , 97 + 27 ) ;
2018-03-01 19:19:38 +01:00
guiQuitBox . setText ( getGameString ( kStringLeaveGame ) . c_str ( ) ) ;
2018-02-23 18:25:53 +01:00
guiQuitBox . setTextPosition ( guiQuitBox . left + 3 , guiQuitBox . top + 3 ) ;
GuiElement guiQuitYes ;
guiQuitYes . setColor ( kColorWhite35 , kColorWhite99 , kColorWhite35 , kColorWhite99 ) ;
guiQuitYes . setSize ( 115 , 111 , 158 , 121 ) ;
2018-03-01 19:19:38 +01:00
guiQuitYes . setText ( getGameString ( kStringYes ) . c_str ( ) ) ;
2018-02-23 18:25:53 +01:00
guiQuitYes . setTextPosition ( 132 , 112 ) ;
GuiElement guiQuitNo ;
guiQuitNo . setColor ( kColorWhite35 , kColorWhite99 , kColorWhite35 , kColorWhite99 ) ;
guiQuitNo . setSize ( 162 , 111 , 205 , 121 ) ;
2018-03-01 19:19:38 +01:00
guiQuitNo . setText ( getGameString ( kStringNo ) . c_str ( ) ) ;
2018-02-23 18:25:53 +01:00
guiQuitNo . setTextPosition ( 173 , 112 ) ;
_gm - > animationOff ( ) ;
_gm - > saveTime ( ) ;
2018-02-23 19:32:21 +01:00
saveScreen ( guiQuitBox ) ;
renderBox ( guiQuitBox ) ;
renderText ( guiQuitBox ) ;
renderBox ( guiQuitYes ) ;
renderText ( guiQuitYes ) ;
renderBox ( guiQuitNo ) ;
renderText ( guiQuitNo ) ;
2018-02-23 18:25:53 +01:00
do {
_gm - > getInput ( ) ;
if ( _gm - > _keyPressed ) {
if ( _gm - > _key . keycode = = Common : : KEYCODE_j ) {
quit = true ;
break ;
} else if ( _gm - > _key . keycode = = Common : : KEYCODE_n ) {
quit = false ;
break ;
}
}
if ( _gm - > _mouseClicked ) {
if ( guiQuitYes . contains ( _gm - > _mouseX , _gm - > _mouseY ) ) {
quit = true ;
break ;
} else if ( guiQuitNo . contains ( _gm - > _mouseX , _gm - > _mouseY ) ) {
quit = false ;
break ;
}
}
} while ( true ) ;
_gm - > resetInputState ( ) ;
restoreScreen ( ) ;
_gm - > loadTime ( ) ;
_gm - > animationOn ( ) ;
return quit ;
}
2017-06-29 11:42:47 +02:00
Common : : MemoryReadStream * SupernovaEngine : : convertToMod ( const char * filename , int version ) {
// MSN format
struct {
uint16 seg ;
uint16 start ;
uint16 end ;
uint16 loopStart ;
uint16 loopEnd ;
char volume ;
char dummy [ 5 ] ;
} instr2 [ 22 ] ;
int nbInstr2 ; // 22 for version1, 15 for version 2
int16 songLength ;
char arrangement [ 128 ] ;
int16 patternNumber ;
int32 note2 [ 28 ] [ 64 ] [ 4 ] ;
nbInstr2 = ( ( version = = 1 ) ? 22 : 15 ) ;
Common : : File msnFile ;
msnFile . open ( filename ) ;
if ( ! msnFile . isOpen ( ) ) {
warning ( " Data file '%s' not found " , msnFile . getName ( ) ) ;
return NULL ;
}
for ( int i = 0 ; i < nbInstr2 ; + + i ) {
instr2 [ i ] . seg = msnFile . readUint16LE ( ) ;
instr2 [ i ] . start = msnFile . readUint16LE ( ) ;
instr2 [ i ] . end = msnFile . readUint16LE ( ) ;
instr2 [ i ] . loopStart = msnFile . readUint16LE ( ) ;
instr2 [ i ] . loopEnd = msnFile . readUint16LE ( ) ;
instr2 [ i ] . volume = msnFile . readByte ( ) ;
msnFile . read ( instr2 [ i ] . dummy , 5 ) ;
}
songLength = msnFile . readSint16LE ( ) ;
msnFile . read ( arrangement , 128 ) ;
patternNumber = msnFile . readSint16LE ( ) ;
for ( int p = 0 ; p < patternNumber ; + + p ) {
for ( int n = 0 ; n < 64 ; + + n ) {
for ( int k = 0 ; k < 4 ; + + k ) {
note2 [ p ] [ n ] [ k ] = msnFile . readSint32LE ( ) ;
}
}
}
/* MOD format */
struct {
char iname [ 22 ] ;
uint16 length ;
char finetune ;
char volume ;
uint16 loopStart ;
uint16 loopLength ;
} instr [ 31 ] ;
int32 note [ 28 ] [ 64 ] [ 4 ] ;
// We can't recover some MOD effects since several of them are mapped to 0.
// Assume the MSN effect of value 0 is Arpeggio (MOD effect of value 0).
const char invConvEff [ 8 ] = { 0 , 1 , 2 , 3 , 10 , 12 , 13 , 15 } ;
// Reminder from convertToMsn
// 31 30 29 28 27 26 25 24 - 23 22 21 20 19 18 17 16 - 15 14 13 12 11 10 09 08 - 07 06 05 04 03 02 01 00
// h h h h g g g g f f f f e e e e d d d d c c c c b b b b a a a a
//
// MSN:
// hhhh (4 bits) Cleared to 0
// dddd c (5 bits) Sample index | after mapping through convInstr
// ccc (3 bits) Effect type | after mapping through convEff
// bbbb aaaa (8 bits) Effect value | unmodified
// gggg ffff eeee (12 bits) Sample period | unmodified
//
// MS2:
// hhhh (4 bits) Cleared to 0
// dddd (4 bits) Sample index | after mapping through convInstr
// cccc (4 bits) Effect type | unmodified
// bbbb aaaa (8 bits) Effect value | unmodified
// gggg ffff eeee (12 bits) Sample period | transformed (0xE000 / p) - 256
//
// MOD:
// hhhh dddd (8 bits) Sample index
// cccc (4 bits) Effect type for this channel/division
// bbbb aaaa (8 bits) Effect value
// gggg ffff eeee (12 bits) Sample period
// Can we recover the instruments mapping? I don't think so as part of the original instrument index is cleared.
// And it doesn't really matter as long as we are consistent.
// However we need to make sure 31 (or 15 in MS2) is mapped to 0 in MOD.
// We just add 1 to all other values, and this means a 1 <-> 1 mapping for the instruments
for ( int p = 0 ; p < patternNumber ; + + p ) {
for ( int n = 0 ; n < 64 ; + + n ) {
for ( int k = 0 ; k < 4 ; + + k ) {
int32 * l = & ( note [ p ] [ n ] [ k ] ) ;
* l = note2 [ p ] [ n ] [ k ] ;
int32 i = 0 ;
if ( nbInstr2 = = 22 ) { // version 1
i = ( ( * l & 0xF800 ) > > 11 ) ;
int32 e = ( ( * l & 0x0700 ) > > 8 ) ;
int32 e1 = invConvEff [ e ] ;
* l & = 0x0FFF00FF ;
* l | = ( e1 < < 8 ) ;
} else { // version 2
int32 h = ( * l > > 16 ) ;
i = ( ( * l & 0xF000 ) > > 12 ) ;
* l & = 0x00000FFF ;
if ( h )
h = 0xE000 / ( h + 256 ) ;
* l | = ( h < < 16 ) ;
if ( i = = 15 )
i = 31 ;
}
// Add back index in note
if ( i ! = 31 ) {
+ + i ;
* l | = ( ( i & 0x0F ) < < 12 ) ;
* l | = ( ( i & 0xF0 ) < < 24 ) ;
}
}
}
}
for ( int i = 0 ; i < 31 ; + + i ) {
// iname is not stored in the mod file. Just set it to 'instrument#'
// finetune is not stored either. Assume 0.
memset ( instr [ i ] . iname , 0 , 22 ) ;
sprintf ( instr [ i ] . iname , " instrument%d " , i + 1 ) ;
instr [ i ] . length = 0 ;
instr [ i ] . finetune = 0 ;
instr [ i ] . volume = 0 ;
instr [ i ] . loopStart = 0 ;
instr [ i ] . loopLength = 0 ;
if ( i < nbInstr2 ) {
instr [ i ] . length = ( ( instr2 [ i ] . end - instr2 [ i ] . start ) > > 1 ) ;
instr [ i ] . loopStart = ( ( instr2 [ i ] . loopStart - instr2 [ i ] . start ) > > 1 ) ;
instr [ i ] . loopLength = ( ( instr2 [ i ] . loopEnd - instr2 [ i ] . loopStart ) > > 1 ) ;
instr [ i ] . volume = instr2 [ i ] . volume ;
}
}
// The ciaaSpeed is kind of useless and not present in the MSN file.
// Traditionally 0x78 in SoundTracker. Was used in NoiseTracker as a restart point.
// ProTracker uses 0x7F. FastTracker uses it as a restart point, whereas ScreamTracker 3 uses 0x7F like ProTracker.
// You can use this to roughly detect which tracker made a MOD, and detection gets more accurate for more obscure MOD types.
char ciaaSpeed = 0x7F ;
// The mark cannot be recovered either. Since we have 4 channels and 31 instrument it can be either ID='M.K.' or ID='4CHN'.
// Assume 'M.K.'
const char mark [ 4 ] = { ' M ' , ' . ' , ' K ' , ' . ' } ;
Common : : MemoryWriteStreamDynamic buffer ( DisposeAfterUse : : NO ) ;
buffer . write ( msnFile . getName ( ) , 19 ) ;
buffer . writeByte ( 0 ) ;
for ( int i = 0 ; i < 31 ; + + i ) {
buffer . write ( instr [ i ] . iname , 22 ) ;
buffer . writeUint16BE ( instr [ i ] . length ) ;
buffer . writeByte ( instr [ i ] . finetune ) ;
buffer . writeByte ( instr [ i ] . volume ) ;
buffer . writeUint16BE ( instr [ i ] . loopStart ) ;
buffer . writeUint16BE ( instr [ i ] . loopLength ) ;
}
buffer . writeByte ( ( char ) songLength ) ;
buffer . writeByte ( ciaaSpeed ) ;
buffer . write ( arrangement , 128 ) ;
buffer . write ( mark , 4 ) ;
for ( int p = 0 ; p < patternNumber ; + + p ) {
for ( int n = 0 ; n < 64 ; + + n ) {
for ( int k = 0 ; k < 4 ; + + k ) {
// buffer.writeUint32BE(*((uint32*)(note[p][n]+k)));
buffer . writeSint32BE ( note [ p ] [ n ] [ k ] ) ;
}
}
}
uint nb ;
char buf [ 4096 ] ;
while ( ( nb = msnFile . read ( buf , 4096 ) ) > 0 )
buffer . write ( buf , nb ) ;
2017-09-24 18:02:21 +01:00
return new Common : : MemoryReadStream ( buffer . getData ( ) , buffer . size ( ) , DisposeAfterUse : : YES ) ;
2017-06-29 11:42:47 +02:00
}
2017-06-18 21:04:23 +02:00
2017-08-29 01:27:07 +02:00
bool SupernovaEngine : : canLoadGameStateCurrently ( ) {
return _allowLoadGame ;
}
Common : : Error SupernovaEngine : : loadGameState ( int slot ) {
2017-08-30 22:37:41 +02:00
return ( loadGame ( slot ) ? Common : : kNoError : Common : : kReadingFailed ) ;
2017-08-29 01:27:07 +02:00
}
bool SupernovaEngine : : canSaveGameStateCurrently ( ) {
2017-11-17 23:13:45 +00:00
// Do not allow saving when either _allowSaveGame, _animationEnabled or _guiEnabled is false
return _allowSaveGame & & _gm - > _animationEnabled & & _gm - > _guiEnabled ;
2017-08-29 01:27:07 +02:00
}
Common : : Error SupernovaEngine : : saveGameState ( int slot , const Common : : String & desc ) {
return ( saveGame ( slot , desc ) ? Common : : kNoError : Common : : kWritingFailed ) ;
}
bool SupernovaEngine : : loadGame ( int slot ) {
2017-08-31 20:53:18 +02:00
if ( slot < 0 )
2017-08-29 01:27:07 +02:00
return false ;
2017-08-31 20:53:18 +02:00
Common : : String filename = Common : : String : : format ( " msn_save.%03d " , slot ) ;
Common : : InSaveFile * savefile = _saveFileMan - > openForLoading ( filename ) ;
2017-08-29 01:27:07 +02:00
if ( ! savefile )
return false ;
2017-09-04 22:48:50 +01:00
uint saveHeader = savefile - > readUint32LE ( ) ;
if ( saveHeader ! = SAVEGAME_HEADER ) {
warning ( " No header found in '%s' " , filename . c_str ( ) ) ;
delete savefile ;
2017-10-14 23:26:32 +02:00
return false ; //Common::kUnknownError
2017-09-04 22:48:50 +01:00
}
2018-03-09 11:20:47 +01:00
2017-09-04 22:48:50 +01:00
byte saveVersion = savefile - > readByte ( ) ;
2017-10-01 21:36:15 +01:00
// Save version 1 was used during development and is no longer supported
if ( saveVersion > SAVEGAME_VERSION | | saveVersion = = 1 ) {
2017-09-04 22:48:50 +01:00
warning ( " Save game version %i not supported " , saveVersion ) ;
delete savefile ;
2017-10-14 23:26:32 +02:00
return false ; //Common::kUnknownError;
2017-09-04 22:48:50 +01:00
}
2017-10-31 22:40:49 +00:00
// Make sure no message is displayed as this would otherwise delay the
// switch to the new location until a mouse click.
removeMessage ( ) ;
2017-08-29 01:27:07 +02:00
int descriptionSize = savefile - > readSint16LE ( ) ;
savefile - > skip ( descriptionSize ) ;
savefile - > skip ( 6 ) ;
2017-09-05 19:43:18 +01:00
setTotalPlayTime ( savefile - > readUint32LE ( ) * 1000 ) ;
2017-08-31 20:53:18 +02:00
Graphics : : skipThumbnail ( * savefile ) ;
2017-11-04 14:54:26 +00:00
_gm - > deserialize ( savefile , saveVersion ) ;
2017-08-29 01:27:07 +02:00
2017-11-17 23:13:45 +00:00
if ( saveVersion > = 5 ) {
_menuBrightness = savefile - > readByte ( ) ;
_brightness = savefile - > readByte ( ) ;
} else {
_menuBrightness = _brightness = 255 ;
}
2017-08-29 01:27:07 +02:00
delete savefile ;
return true ;
}
bool SupernovaEngine : : saveGame ( int slot , const Common : : String & description ) {
2017-08-31 20:53:18 +02:00
if ( slot < 0 )
2017-08-29 01:27:07 +02:00
return false ;
2017-08-31 20:53:18 +02:00
Common : : String filename = Common : : String : : format ( " msn_save.%03d " , slot ) ;
Common : : OutSaveFile * savefile = _saveFileMan - > openForSaving ( filename ) ;
2017-08-29 01:27:07 +02:00
if ( ! savefile )
return false ;
2018-03-09 11:20:47 +01:00
2017-09-04 22:48:50 +01:00
savefile - > writeUint32LE ( SAVEGAME_HEADER ) ;
savefile - > writeByte ( SAVEGAME_VERSION ) ;
2017-08-29 01:27:07 +02:00
TimeDate currentDate ;
_system - > getTimeAndDate ( currentDate ) ;
uint32 saveDate = ( currentDate . tm_mday & 0xFF ) < < 24 | ( ( currentDate . tm_mon + 1 ) & 0xFF ) < < 16 | ( ( currentDate . tm_year + 1900 ) & 0xFFFF ) ;
uint16 saveTime = ( currentDate . tm_hour & 0xFF ) < < 8 | ( ( currentDate . tm_min ) & 0xFF ) ;
2017-08-31 20:53:18 +02:00
savefile - > writeSint16LE ( description . size ( ) + 1 ) ;
savefile - > write ( description . c_str ( ) , description . size ( ) + 1 ) ;
2017-08-29 01:27:07 +02:00
savefile - > writeUint32LE ( saveDate ) ;
savefile - > writeUint16LE ( saveTime ) ;
2017-09-05 19:43:18 +01:00
savefile - > writeUint32LE ( getTotalPlayTime ( ) / 1000 ) ;
2017-08-31 20:53:18 +02:00
Graphics : : saveThumbnail ( * savefile ) ;
2017-08-29 01:27:07 +02:00
_gm - > serialize ( savefile ) ;
2017-11-17 23:13:45 +00:00
savefile - > writeByte ( _menuBrightness ) ;
savefile - > writeByte ( _brightness ) ;
2017-08-29 01:27:07 +02:00
savefile - > finalize ( ) ;
delete savefile ;
return true ;
}
2017-11-17 21:04:22 +00:00
void SupernovaEngine : : errorTempSave ( bool saving ) {
2018-03-09 11:20:47 +01:00
GUIErrorMessage ( saving
2017-11-18 00:06:54 +01:00
? " Failed to save temporary game state. Make sure your save game directory is set in ScummVM and that you can write to it. "
: " Failed to load temporary game state. " ) ;
error ( " Unrecoverable error " ) ;
2017-11-17 21:04:22 +00:00
}
2017-07-11 05:33:46 +02:00
ScreenBufferStack : : ScreenBufferStack ( )
2017-09-24 17:53:01 +01:00
: _last ( _buffer ) {
2017-07-11 05:33:46 +02:00
}
2017-11-05 15:47:33 +00:00
void ScreenBufferStack : : push ( int x , int y , int width , int height ) {
2017-07-11 05:33:46 +02:00
if ( _last = = ARRAYEND ( _buffer ) )
return ;
2017-11-05 15:47:33 +00:00
Graphics : : Surface * screenSurface = g_system - > lockScreen ( ) ;
if ( x < 0 ) {
width + = x ;
x = 0 ;
}
if ( x + width > screenSurface - > w )
width = screenSurface - > w - x ;
if ( y < 0 ) {
height + = y ;
y = 0 ;
}
if ( y + height > screenSurface - > h )
height = screenSurface - > h - y ;
2017-07-11 05:33:46 +02:00
_last - > _pixels = new byte [ width * height ] ;
byte * pixels = _last - > _pixels ;
2017-11-05 15:47:33 +00:00
const byte * screen = static_cast < const byte * > ( screenSurface - > getBasePtr ( x , y ) ) ;
2017-07-11 05:33:46 +02:00
for ( int i = 0 ; i < height ; + + i ) {
Common : : copy ( screen , screen + width , pixels ) ;
2017-11-05 15:47:33 +00:00
screen + = screenSurface - > pitch ;
2017-07-11 05:33:46 +02:00
pixels + = width ;
}
g_system - > unlockScreen ( ) ;
_last - > _x = x ;
_last - > _y = y ;
_last - > _width = width ;
_last - > _height = height ;
+ + _last ;
}
void ScreenBufferStack : : restore ( ) {
if ( _last = = _buffer )
return ;
- - _last ;
g_system - > lockScreen ( ) - > copyRectToSurface (
2018-03-09 11:20:47 +01:00
_last - > _pixels , _last - > _width , _last - > _x , _last - > _y ,
_last - > _width , _last - > _height ) ;
2017-07-11 05:33:46 +02:00
g_system - > unlockScreen ( ) ;
delete [ ] _last - > _pixels ;
}
2017-06-10 00:17:19 +02:00
}