2019-03-05 19:28:12 +01: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 .
*
*/
# include "common/archive.h"
# include "common/config-manager.h"
# include "common/file.h"
# include "common/savefile.h"
# include "common/system.h"
# include "cryomni3d/versailles/engine.h"
namespace CryOmni3D {
namespace Versailles {
2019-06-08 12:22:11 +02:00
Common : : Error CryOmni3DEngine_Versailles : : loadGameState ( int slot ) {
_loadedSave = slot + 1 ;
_abortCommand = kAbortLoadGame ;
return Common : : kNoError ;
}
2020-02-04 22:13:33 -08:00
Common : : Error CryOmni3DEngine_Versailles : : saveGameState ( int slot , const Common : : String & desc , bool isAutosave ) {
2019-06-08 12:22:11 +02:00
saveGame ( _isVisiting , slot + 1 , desc ) ;
return Common : : kNoError ;
}
2019-03-05 19:28:12 +01:00
2019-05-26 18:29:57 +02:00
Common : : String CryOmni3DEngine_Versailles : : getSaveFileName ( bool visit , uint saveNum ) const {
2019-03-05 19:28:12 +01:00
return Common : : String : : format ( " %s%s.%04u " , _targetName . c_str ( ) , visit ? " _visit " : " " , saveNum ) ;
}
bool CryOmni3DEngine_Versailles : : canVisit ( ) const {
2019-10-20 17:40:36 +02:00
return Common : : File : : exists ( " game0001.sav " ) ;
2019-03-05 19:28:12 +01:00
}
void CryOmni3DEngine_Versailles : : getSavesList ( bool visit , Common : : StringArray & saveNames ) {
2019-06-08 12:08:05 +02:00
char saveName [ kSaveDescriptionLen + 1 ] ;
saveName [ kSaveDescriptionLen ] = ' \0 ' ;
2019-03-05 19:28:12 +01:00
Common : : String pattern = Common : : String : : format ( " %s%s.???? " , _targetName . c_str ( ) ,
visit ? " _visit " : " " ) ;
2019-06-08 12:08:05 +02:00
Common : : StringArray filenames = _saveFileMan - > listSavefiles ( pattern ) ;
2019-03-05 19:28:12 +01:00
sort ( filenames . begin ( ) , filenames . end ( ) ) ; // Sort (hopefully ensuring we are sorted numerically..)
saveNames . clear ( ) ;
saveNames . reserve ( 100 ) ;
int num = 1 ;
int slotNum ;
if ( visit ) {
// Add bootstrap visit
2019-10-20 17:40:36 +02:00
if ( Common : : File : : exists ( " game0001.sav " ) ) {
2019-03-05 19:28:12 +01:00
Common : : File visitFile ;
2019-10-20 17:40:36 +02:00
if ( ! visitFile . open ( " game0001.sav " ) ) {
2019-03-05 19:28:12 +01:00
error ( " Can't load visit file " ) ;
}
2019-06-08 12:08:05 +02:00
visitFile . read ( saveName , kSaveDescriptionLen ) ;
2019-03-05 19:28:12 +01:00
saveNames . push_back ( saveName ) ;
} else {
warning ( " visiting mode but no bootstrap " ) ;
// No bootstrap visit, too bad
saveNames . push_back ( _messages [ 55 ] ) ; //Fill with free slot
}
num + + ;
}
for ( Common : : StringArray : : const_iterator file = filenames . begin ( ) ; file ! = filenames . end ( ) ;
+ + file ) {
// Obtain the last 4 digits of the filename, since they correspond to the save slot
slotNum = atoi ( file - > c_str ( ) + file - > size ( ) - 4 ) ;
if ( slotNum > = 1 & & slotNum < = 99 ) {
while ( num < slotNum ) {
saveNames . push_back ( _messages [ 55 ] ) ; //Fill with free slot
num + + ;
}
num + + ;
Common : : InSaveFile * in = _saveFileMan - > openForLoading ( * file ) ;
if ( in ) {
2019-06-08 12:08:05 +02:00
if ( in - > read ( saveName , kSaveDescriptionLen ) = = kSaveDescriptionLen ) {
2019-05-05 12:37:47 +02:00
saveNames . push_back ( saveName ) ;
}
2019-03-05 19:28:12 +01:00
delete in ;
}
}
}
2019-05-26 18:29:57 +02:00
for ( uint i = saveNames . size ( ) ; i < 100 ; i + + ) {
2019-03-05 19:28:12 +01:00
saveNames . push_back ( _messages [ 55 ] ) ;
}
}
2019-05-26 18:29:57 +02:00
void CryOmni3DEngine_Versailles : : saveGame ( bool visit , uint saveNum ,
2019-05-20 20:56:35 +02:00
const Common : : String & saveName ) {
2019-03-05 19:28:12 +01:00
if ( visit & & saveNum = = 1 ) {
error ( " Can't erase bootstrap visit " ) ;
}
Common : : String saveFileName = getSaveFileName ( visit , saveNum ) ;
Common : : OutSaveFile * out ;
2019-06-08 12:08:05 +02:00
if ( ! ( out = _saveFileMan - > openForSaving ( saveFileName ) ) ) {
2019-03-05 19:28:12 +01:00
return ;
}
2019-05-20 20:56:35 +02:00
// Sync countdown to game variable before saving it to file
syncCountdown ( ) ;
2019-03-05 19:28:12 +01:00
// Write save name
2019-06-08 12:08:05 +02:00
char saveNameC [ kSaveDescriptionLen ] ;
2019-03-05 19:28:12 +01:00
memset ( saveNameC , 0 , sizeof ( saveNameC ) ) ;
2019-07-04 08:19:52 +02:00
// Silence -Wstringop-truncation using parentheses, we don't have to have a null-terminated string here
( strncpy ( saveNameC , saveName . c_str ( ) , sizeof ( saveNameC ) ) ) ;
2019-03-05 19:28:12 +01:00
out - > write ( saveNameC , sizeof ( saveNameC ) ) ;
// dummy values
out - > writeUint32LE ( 0 ) ;
out - > writeUint32BE ( 0 ) ;
out - > writeUint32BE ( 0 ) ;
// Dialog variables
assert ( _dialogsMan . size ( ) < 200 ) ;
2019-05-26 18:29:57 +02:00
for ( uint i = 0 ; i < _dialogsMan . size ( ) ; i + + ) {
2019-03-05 19:28:12 +01:00
out - > writeByte ( _dialogsMan [ i ] ) ;
}
2019-05-26 18:29:57 +02:00
for ( uint i = _dialogsMan . size ( ) ; i < 200 ; i + + ) {
2019-03-05 19:28:12 +01:00
out - > writeByte ( 0 ) ;
}
// Inventory
assert ( _inventory . size ( ) = = 50 ) ;
for ( Inventory : : const_iterator it = _inventory . begin ( ) ; it ! = _inventory . end ( ) ; it + + ) {
2019-06-30 16:43:32 +02:00
uint objId = uint ( - 1 ) ;
2019-03-05 19:28:12 +01:00
if ( * it ! = nullptr ) {
// Inventory contains pointers to objects stored in _objects
objId = * it - _objects . begin ( ) ;
}
out - > writeUint32BE ( objId ) ;
}
// Offset of inventory in toolbar
out - > writeUint32BE ( _toolbar . inventoryOffset ( ) ) ;
// Level, place, warp position
out - > writeUint32BE ( _currentLevel ) ;
out - > writeUint32BE ( _currentPlaceId ) ;
out - > writeDoubleBE ( _omni3dMan . getAlpha ( ) ) ;
out - > writeDoubleBE ( _omni3dMan . getBeta ( ) ) ;
// Places states
2019-05-05 12:35:23 +02:00
assert ( _placeStates . size ( ) < = 100 ) ;
2019-03-05 19:28:12 +01:00
Common : : Array < PlaceState > : : const_iterator placeIt = _placeStates . begin ( ) ;
2019-05-26 18:29:57 +02:00
for ( uint i = 0 ; placeIt ! = _placeStates . end ( ) ; placeIt + + , i + + ) {
2019-03-05 19:28:12 +01:00
out - > writeUint32BE ( placeIt - > state ) ;
}
2019-05-26 18:29:57 +02:00
for ( uint i = _placeStates . size ( ) ; i < 100 ; i + + ) {
2019-03-05 19:28:12 +01:00
out - > writeUint32BE ( 0 ) ;
}
// Game variables
assert ( _gameVariables . size ( ) < 100 ) ;
2019-05-26 18:29:57 +02:00
for ( Common : : Array < uint > : : const_iterator it = _gameVariables . begin ( ) ;
2019-03-05 19:28:12 +01:00
it ! = _gameVariables . end ( ) ; it + + ) {
out - > writeUint32BE ( * it ) ;
}
2019-05-26 18:29:57 +02:00
for ( uint i = _gameVariables . size ( ) ; i < 100 ; i + + ) {
2019-03-05 19:28:12 +01:00
out - > writeUint32BE ( 0 ) ;
}
out - > finalize ( ) ;
delete out ;
}
2019-05-26 18:29:57 +02:00
bool CryOmni3DEngine_Versailles : : loadGame ( bool visit , uint saveNum ) {
2019-03-05 19:28:12 +01:00
Common : : SeekableReadStream * in ;
if ( visit & & saveNum = = 1 ) {
// Load bootstrap visit
Common : : File * visitFile = new Common : : File ( ) ;
2019-10-20 17:40:36 +02:00
if ( ! visitFile - > open ( " game0001.sav " ) ) {
2019-03-05 19:28:12 +01:00
delete visitFile ;
error ( " Can't load visit file " ) ;
}
in = visitFile ;
} else {
Common : : String saveFileName = getSaveFileName ( visit , saveNum ) ;
in = _saveFileMan - > openForLoading ( saveFileName ) ;
}
if ( ! in | | in - > size ( ) ! = 1260 ) {
return false ;
}
musicStop ( ) ;
// Load save name but don't use it
2019-06-08 12:08:05 +02:00
char saveNameC [ kSaveDescriptionLen ] ;
2019-03-05 19:28:12 +01:00
in - > read ( saveNameC , sizeof ( saveNameC ) ) ;
// dummy values
2019-05-30 15:44:30 +02:00
( void ) in - > readUint32LE ( ) ;
( void ) in - > readUint32BE ( ) ;
( void ) in - > readUint32BE ( ) ;
2019-03-05 19:28:12 +01:00
// Dialog variables
assert ( _dialogsMan . size ( ) < 200 ) ;
2019-05-26 18:29:57 +02:00
for ( uint i = 0 ; i < _dialogsMan . size ( ) ; i + + ) {
2019-03-05 19:28:12 +01:00
_dialogsMan [ i ] = in - > readByte ( ) ;
}
2019-05-26 18:29:57 +02:00
for ( uint i = _dialogsMan . size ( ) ; i < 200 ; i + + ) {
2019-05-30 15:44:30 +02:00
// Read the remaining bytes but don't use them
( void ) in - > readByte ( ) ;
2019-03-05 19:28:12 +01:00
}
// Inventory
assert ( _inventory . size ( ) = = 50 ) ;
for ( Inventory : : iterator it = _inventory . begin ( ) ; it ! = _inventory . end ( ) ; it + + ) {
2019-05-26 18:29:57 +02:00
uint objId = in - > readUint32BE ( ) ;
2019-03-05 19:28:12 +01:00
if ( objId > = _objects . size ( ) ) {
2019-06-30 16:43:32 +02:00
objId = uint ( - 1 ) ;
2019-03-05 19:28:12 +01:00
}
2019-06-30 16:43:32 +02:00
if ( objId ! = uint ( - 1 ) ) {
2019-03-05 19:28:12 +01:00
* it = _objects . begin ( ) + objId ;
} else {
* it = nullptr ;
}
}
// Offset of inventory in toolbar
_toolbar . setInventoryOffset ( in - > readUint32BE ( ) ) ;
// Level, place, warp position
_currentLevel = in - > readUint32BE ( ) ;
// Use nextPlace to force place move
_nextPlaceId = in - > readUint32BE ( ) ;
// Store alpha and beta for later use
double alpha = in - > readDoubleBE ( ) ;
double beta = in - > readDoubleBE ( ) ;
// Places states
// Store them and use them once we called initNewLevel, we can't call it before because it needs _gameVariables (and especially kCurrentTime) to be correctly set
uint32 placesStates [ 100 ] ;
2019-05-26 18:29:57 +02:00
for ( uint i = 0 ; i < 100 ; i + + ) {
2019-03-05 19:28:12 +01:00
placesStates [ i ] = in - > readUint32BE ( ) ;
}
// Game variables
assert ( _gameVariables . size ( ) < 100 ) ;
2019-05-26 18:29:57 +02:00
for ( Common : : Array < uint > : : iterator it = _gameVariables . begin ( ) ; it ! = _gameVariables . end ( ) ;
2019-03-05 19:28:12 +01:00
it + + ) {
* it = in - > readUint32BE ( ) ;
}
2019-05-26 18:29:57 +02:00
for ( uint i = _gameVariables . size ( ) ; i < 100 ; i + + ) {
2019-05-30 15:44:30 +02:00
// Read the remaining variables but don't use them
( void ) in - > readUint32BE ( ) ;
2019-03-05 19:28:12 +01:00
}
delete in ;
if ( _gameVariables [ GameVariables : : kCurrentTime ] = = 0 ) {
_gameVariables [ GameVariables : : kCurrentTime ] = 1 ;
}
2019-05-20 20:56:35 +02:00
initCountdown ( ) ;
2019-03-05 19:28:12 +01:00
// Everything has been loaded, setup new level
// We will set places states and warp coordinates just after that to avoid them from being reset
initNewLevel ( _currentLevel ) ;
_omni3dMan . setAlpha ( alpha ) ;
_omni3dMan . setBeta ( beta ) ;
// _placeStates has just been resized in initNewLevel
2019-05-26 18:29:57 +02:00
uint i = 0 ;
2019-03-05 19:28:12 +01:00
for ( Common : : Array < PlaceState > : : iterator placeIt = _placeStates . begin ( ) ;
placeIt ! = _placeStates . end ( ) & & i < ARRAYSIZE ( placesStates ) ; placeIt + + , i + + ) {
placeIt - > state = placesStates [ i ] ;
}
return true ;
}
} // End of namespace Versailles
} // End of namespace CryOmni3D