diff --git a/src/CBot/CBotClass.cpp b/src/CBot/CBotClass.cpp index f1a658ad..628bb907 100644 --- a/src/CBot/CBotClass.cpp +++ b/src/CBot/CBotClass.cpp @@ -36,7 +36,6 @@ #include "CBot/CBotCStack.h" #include "CBot/CBotDefParam.h" #include "CBot/CBotUtils.h" -#include "CBot/CBotFileUtils.h" #include @@ -364,69 +363,70 @@ void CBotClass::RestoreMethode(long& nIdent, } //////////////////////////////////////////////////////////////////////////////// -bool CBotClass::SaveStaticState(FILE* pf) +bool CBotClass::SaveStaticState(std::ostream &ostr) { - if (!WriteWord( pf, CBOTVERSION*2)) return false; + if (!WriteLong(ostr, CBOTVERSION*2)) return false; // saves the state of static variables in classes for (CBotClass* p : m_publicClasses) { - if (!WriteWord( pf, 1 )) return false; + if (!WriteWord(ostr, 1)) return false; // save the name of the class - if (!WriteString( pf, p->GetName() )) return false; + if (!WriteString(ostr, p->GetName())) return false; CBotVar* pv = p->GetVar(); while( pv != nullptr ) { if ( pv->IsStatic() ) { - if (!WriteWord( pf, 1 )) return false; - if (!WriteString( pf, pv->GetName() )) return false; + if (!WriteWord(ostr, 1)) return false; + if (!WriteString(ostr, pv->GetName())) return false; - if ( !pv->Save0State(pf) ) return false; // common header - if ( !pv->Save1State(pf) ) return false; // saves as the child class - if ( !WriteWord( pf, 0 ) ) return false; + if (!pv->Save0State(ostr)) return false; // common header + if (!pv->Save1State(ostr)) return false; // saves as the child class + if (!WriteWord(ostr, 0)) return false; } pv = pv->GetNext(); } - if (!WriteWord( pf, 0 )) return false; + if (!WriteWord(ostr, 0)) return false; } - if (!WriteWord( pf, 0 )) return false; + if (!WriteWord(ostr, 0)) return false; return true; } //////////////////////////////////////////////////////////////////////////////// -bool CBotClass::RestoreStaticState(FILE* pf) +bool CBotClass::RestoreStaticState(std::istream &istr) { std::string ClassName, VarName; CBotClass* pClass; unsigned short w; - if (!ReadWord( pf, w )) return false; - if ( w != CBOTVERSION*2 ) return false; + long version; + if (!ReadLong(istr, version)) return false; + if (version != CBOTVERSION*2) return false; while (true) { - if (!ReadWord( pf, w )) return false; + if (!ReadWord(istr, w)) return false; if ( w == 0 ) return true; - if (!ReadString( pf, ClassName )) return false; + if (!ReadString(istr, ClassName)) return false; pClass = Find(ClassName); while (true) { - if (!ReadWord( pf, w )) return false; + if (!ReadWord(istr, w)) return false; if ( w == 0 ) break; CBotVar* pVar = nullptr; CBotVar* pv = nullptr; - if (!ReadString( pf, VarName )) return false; + if (!ReadString(istr, VarName)) return false; if ( pClass != nullptr ) pVar = pClass->GetItem(VarName); - if (!CBotVar::RestoreState(pf, pv)) return false; // the temp variable + if (!CBotVar::RestoreState(istr, pv)) return false; // the temp variable if ( pVar != nullptr ) pVar->Copy(pv); delete pv; diff --git a/src/CBot/CBotClass.h b/src/CBot/CBotClass.h index 0f9a8ce4..eeafe2d1 100644 --- a/src/CBot/CBotClass.h +++ b/src/CBot/CBotClass.h @@ -331,18 +331,18 @@ public: static void ClearPublic(); /*! - * \brief SaveStaticState - * \param pf - * \return + * \brief Save all static variables from each public class + * \param ostr Output stream + * \return true on success */ - static bool SaveStaticState(FILE* pf); + static bool SaveStaticState(std::ostream &ostr); /*! - * \brief RestoreStaticState - * \param pf - * \return + * \brief Restore all static variables in each public class + * \param istr Input stream + * \return true on success */ - static bool RestoreStaticState(FILE* pf); + static bool RestoreStaticState(std::istream &istr); /** * \brief Request a lock on this class (for "synchronized" keyword) diff --git a/src/CBot/CBotFileUtils.cpp b/src/CBot/CBotFileUtils.cpp index c473493a..ccde313c 100644 --- a/src/CBot/CBotFileUtils.cpp +++ b/src/CBot/CBotFileUtils.cpp @@ -21,129 +21,251 @@ #include "CBot/CBotClass.h" #include "CBot/CBotEnums.h" -#include "CBot/CBotUtils.h" namespace CBot { - -// file management - -// necessary because it is not possible to do the fopen in the main program -// fwrite and fread in a dll or using the FILE * returned. - -//////////////////////////////////////////////////////////////////////////////// -FILE* fOpen(const char* name, const char* mode) +template +static bool WriteBinary(std::ostream &ostr, T value, unsigned padTo = 0) { - return fopen(name, mode); -} - -//////////////////////////////////////////////////////////////////////////////// -int fClose(FILE* filehandle) -{ - return fclose(filehandle); -} - -//////////////////////////////////////////////////////////////////////////////// -std::size_t fWrite(const void *buffer, - std::size_t elemsize, - std::size_t length, - FILE* filehandle) -{ - return fwrite(buffer, elemsize, length, filehandle); -} - -//////////////////////////////////////////////////////////////////////////////// -std::size_t fRead(void *buffer, - std::size_t elemsize, - std::size_t length, - FILE* filehandle) -{ - return fread(buffer, elemsize, length, filehandle); -} - - -//////////////////////////////////////////////////////////////////////////////// -bool ReadWord(FILE* pf, unsigned short& w) -{ - size_t lg; - - lg = fread(&w, sizeof( unsigned short ), 1, pf ); - - return (lg == 1); -} - -//////////////////////////////////////////////////////////////////////////////// -bool ReadFloat(FILE* pf, float& w) -{ - size_t lg; - - lg = fread(&w, sizeof( float ), 1, pf ); - - return (lg == 1); -} - -//////////////////////////////////////////////////////////////////////////////// -bool WriteLong(FILE* pf, long w) -{ - size_t lg; - - lg = fwrite(&w, sizeof( long ), 1, pf ); - - return (lg == 1); -} - -//////////////////////////////////////////////////////////////////////////////// -bool ReadLong(FILE* pf, long& w) -{ - size_t lg; - - lg = fread(&w, sizeof( long ), 1, pf ); - - return (lg == 1); -} - -//////////////////////////////////////////////////////////////////////////////// -bool ReadString(FILE* pf, std::string& s) -{ - unsigned short w; - char buf[1000]; - size_t lg1, lg2; - - if (!ReadWord(pf, w)) return false; - lg1 = w; - lg2 = fread(buf, 1, lg1, pf ); - buf[lg2] = 0; - - s = buf; - return (lg1 == lg2); -} - -//////////////////////////////////////////////////////////////////////////////// -bool WriteType(FILE* pf, const CBotTypResult &type) -{ - int typ = type.GetType(); - if ( typ == CBotTypIntrinsic ) typ = CBotTypClass; - if ( !WriteWord(pf, typ) ) return false; - if ( typ == CBotTypClass ) + unsigned char chr; + unsigned count = 1; + while (value > 127) // unsigned LEB128 { - CBotClass* p = type.GetClass(); - if ( !WriteString(pf, p->GetName()) ) return false; + ++count; + chr = (value & 0x7F) | 0x80; + if (!ostr.write(reinterpret_cast(&chr), 1)) return false; + value >>= 7; } - if ( type.Eq( CBotTypArrayBody ) || - type.Eq( CBotTypArrayPointer ) ) + chr = value & 0x7F; + if (count < padTo) chr |= 0x80; + if (!ostr.write(reinterpret_cast(&chr), 1)) return false; + + if (count < padTo) { - if ( !WriteWord(pf, type.GetLimite()) ) return false; - if ( !WriteType(pf, type.GetTypElem()) ) return false; + while (++count < padTo) + if (!(ostr << '\x80')) return false; + if (!(ostr << '\x00')) return false; } return true; } -//////////////////////////////////////////////////////////////////////////////// -bool ReadType(FILE* pf, CBotTypResult &type) +template +static bool ReadBinary(std::istream &istr, T &value) +{ + value = 0; + unsigned char chr; + unsigned shift = 0; + while (true) // unsigned LEB128 + { + if (!istr.read(reinterpret_cast(&chr), 1)) return false; + if (shift < sizeof(T) * 8 - 1) + value |= static_cast(chr & 0x7F) << shift; + shift += 7; + if ((chr & 0x80) == 0) break; + } + return true; +} + +template +static bool WriteSignedBinary(std::ostream &ostr, T value, unsigned padTo = 0) +{ + signed char sign = value >> (8 * sizeof(T) - 1); + unsigned count = 0; + while (true) // signed LEB128 + { + ++count; + unsigned char chr = value & 0x7F; + value >>= 7; + if (!(value != sign || ((chr ^ sign) & 0x40) != 0)) + { + if (count < padTo) chr |= 0x80; + if (!ostr.write(reinterpret_cast(&chr), 1)) return false; + break; + } + chr |= 0x80; + if (!ostr.put(chr)) return false; + } + + if (count < padTo) + { + char chr = (sign < 0) ? 0x7F : 0x00; + while (++count < padTo) + if (!ostr.put(chr | 0x80)) return false; + if (!ostr.put(chr)) return false; + } + return true; +} + +template +static bool ReadSignedBinary(std::istream &istr, T &value) +{ + value = 0; + unsigned char chr; + unsigned shift = 0; + while (true) // signed LEB128 + { + if (!istr.read(reinterpret_cast(&chr), 1)) return false; + if (shift < sizeof(T) * 8 - 1) + value |= (static_cast(chr & 0x7F) << shift); + shift += 7; + if ((chr & 0x80) == 0) break; + } + + if (shift >= 8 * sizeof(T) - 1) shift = 8 * sizeof(T) - 1; + if ((chr & 0x40) != 0) + value |= static_cast(-1) << shift; + + return true; +} + +bool WriteWord(std::ostream &ostr, unsigned short w) +{ + return WriteBinary(ostr, w); +} + +bool ReadWord(std::istream &istr, unsigned short &w) +{ + return ReadBinary(istr, w); +} + +bool WriteByte(std::ostream &ostr, char c) +{ + if (!ostr.put(c)) return false; + return true; +} + +bool ReadByte(std::istream &istr, char& c) +{ + if (!istr.get(c)) return false; + return true; +} + +bool WriteShort(std::ostream &ostr, short s) +{ + return WriteSignedBinary(ostr, s); +} + +bool ReadShort(std::istream &istr, short &s) +{ + return ReadSignedBinary(istr, s); +} + +bool WriteInt(std::ostream &ostr, int i) +{ + return WriteSignedBinary(ostr, i); +} + +bool ReadInt(std::istream &istr, int &i) +{ + return ReadSignedBinary(istr, i); +} + +bool WriteLong(std::ostream &ostr, long l, unsigned padTo) +{ + return WriteSignedBinary(ostr, l, padTo); +} + +bool ReadLong(std::istream &istr, long &l) +{ + return ReadSignedBinary(istr, l); +} + +bool WriteFloat(std::ostream &ostr, float f) +{ + union {float fValue; unsigned int iValue;} u; + u.fValue = 0.0f; + u.iValue = 0; + + u.fValue = f; + return WriteBinary(ostr, u.iValue); +} + +bool ReadFloat(std::istream &istr, float &f) +{ + union {float fValue; unsigned int iValue;} u; + u.fValue = 0.0f; + u.iValue = 0; + + if (!ReadBinary(istr, u.iValue)) return false; + f = u.fValue; + return true; +} + +bool WriteDouble(std::ostream &ostr, double d) +{ + union {double dValue; unsigned long iValue;} u; + u.dValue = 0.0; + u.iValue = 0; + + u.dValue = d; + return WriteBinary(ostr, u.iValue); +} + +bool ReadDouble(std::istream &istr, double &d) +{ + union {double dValue; unsigned long iValue;} u; + u.dValue = 0.0; + u.iValue = 0; + + if (!ReadBinary(istr, u.iValue)) return false; + d = u.dValue; + return true; +} + +bool WriteString(std::ostream &ostr, const std::string &s) +{ + if (!WriteBinary(ostr, s.size())) return false; + if (!ostr.write(&(s[0]), s.size())) return false; + + return true; +} + +bool ReadString(std::istream &istr, std::string &s) +{ + size_t length = 0; + if (!ReadBinary(istr, length)) return false; + + s.resize(length); + if (length != 0) + { + if (!istr.read(&(s[0]), length)) return false; + } + return true; +} + +bool WriteType(std::ostream &ostr, const CBotTypResult &type) +{ + int typ = type.GetType(); + if ( typ == CBotTypIntrinsic ) typ = CBotTypClass; + if ( !WriteWord(ostr, typ) ) return false; + if ( typ == CBotTypClass ) + { + CBotClass* p = type.GetClass(); + if (!WriteString(ostr, p->GetName())) return false; + } + if ( type.Eq( CBotTypArrayBody ) || + type.Eq( CBotTypArrayPointer ) ) + { + if (!WriteWord(ostr, type.GetLimite())) return false; + if (!WriteType(ostr, type.GetTypElem())) return false; + } + + if ( type.Eq(CBotTypPointer) ) + { + if (type.GetClass() != nullptr) + { + if (!WriteString(ostr, type.GetClass()->GetName())) return false; + } + else if (!WriteString(ostr, "")) return false; + } + return true; +} + +bool ReadType(std::istream &istr, CBotTypResult &type) { unsigned short w, ww; - if ( !ReadWord(pf, w) ) return false; + if (!ReadWord(istr, w)) return false; type.SetType(w); if ( type.Eq( CBotTypIntrinsic ) ) @@ -154,7 +276,7 @@ bool ReadType(FILE* pf, CBotTypResult &type) if ( type.Eq( CBotTypClass ) ) { std::string s; - if ( !ReadString(pf, s) ) return false; + if (!ReadString(istr, s)) return false; type = CBotTypResult( w, s ); } @@ -162,11 +284,45 @@ bool ReadType(FILE* pf, CBotTypResult &type) type.Eq( CBotTypArrayBody ) ) { CBotTypResult r; - if ( !ReadWord(pf, ww) ) return false; - if ( !ReadType(pf, r) ) return false; + if (!ReadWord(istr, ww)) return false; + if (!ReadType(istr, r)) return false; type = CBotTypResult( w, r ); type.SetLimite(static_cast(ww)); } + + if ( type.Eq(CBotTypPointer) ) + { + std::string className; + if (!ReadString(istr, className)) return false; + type = CBotTypResult(w, className); + } + return true; +} + +bool WriteStream(std::ostream &ostr, std::istream& istr) +{ + if (!istr.seekg(0, istr.end)) return false; + auto size = istr.tellg(); + + if (size == 0) return WriteLong(ostr, 0); + if (!WriteLong(ostr, size)) return false; + + if (!istr.seekg(0, istr.beg)) return false; + if (!(ostr << istr.rdbuf())) return false; + + return true; +} + +bool ReadStream(std::istream& istr, std::ostream &ostr) +{ + long length; + if (!ReadLong(istr, length)) return false; + if (length == 0) return true; + + while (length-- > 0) + { + if (!(ostr << istr.get())) return false; + } return true; } diff --git a/src/CBot/CBotFileUtils.h b/src/CBot/CBotFileUtils.h index fec6298a..8f739cb4 100644 --- a/src/CBot/CBotFileUtils.h +++ b/src/CBot/CBotFileUtils.h @@ -19,7 +19,7 @@ #pragma once -#include +#include #include namespace CBot @@ -28,128 +28,173 @@ namespace CBot class CBotVar; class CBotTypResult; -/////////////////////////////////////////////////////////////////////////////// -// routines for file management (* FILE) - /*! - * \brief fOpen - * \param name - * \param mode - * \return + * \brief Save a linked list if variables + * \param ostr Output stream + * \param pVar First variable in the list + * \return true on success */ -FILE* fOpen(const char* name, const char* mode); - -/*! - * \brief fClose - * \param filehandle - * \return - */ -int fClose(FILE* filehandle); - -/*! - * \brief fWrite - * \param buffer - * \param elemsize - * \param length - * \param filehandle - * \return - */ -std::size_t fWrite(const void *buffer, - std::size_t elemsize, - std::size_t length, - FILE* filehandle); - -/*! - * \brief fRead - * \param buffer - * \param elemsize - * \param length - * \param filehandle - * \return - */ -std::size_t fRead(void *buffer, - std::size_t elemsize, - std::size_t length, - FILE* filehandle); - -/*! - * \brief SaveVars - * \param pf - * \param pVar - * \return - */ -bool SaveVars(FILE* pf, CBotVar* pVar); +bool SaveVars(std::ostream &ostr, CBotVar* pVar); /*! * \brief WriteWord - * \param pf + * \param ostr Output stream * \param w - * \return + * \return true on success */ -bool WriteWord(FILE* pf, unsigned short w); +bool WriteWord(std::ostream &ostr, unsigned short w); /*! * \brief ReadWord - * \param pf - * \param w - * \return + * \param istr Input stream + * \param[out] w + * \return true on success */ -bool ReadWord(FILE* pf, unsigned short& w); +bool ReadWord(std::istream &istr, unsigned short &w); /*! - * \brief ReadLong - * \param pf - * \param w - * \return + * \brief WriteByte + * \param ostr Output stream + * \param c + * \return true on success */ -bool ReadLong(FILE* pf, long& w); +bool WriteByte(std::ostream &ostr, char c); /*! - * \brief WriteFloat - * \param pf - * \param w - * \return + * \brief ReadByte + * \param istr Input stream + * \param[out] c + * \return true on success */ -bool WriteFloat(FILE* pf, float w); +bool ReadByte(std::istream &istr, char& c); + +/*! + * \brief WriteShort + * \param ostr Output stream + * \param s + * \return true on success + */ +bool WriteShort(std::ostream &ostr, short s); + +/*! + * \brief ReadShort + * \param istr Input stream + * \param[out] s + * \return true on success + */ +bool ReadShort(std::istream &istr, short &s); + +/*! + * \brief WriteInt + * \param ostr Output stream + * \param i + * \return true on success + */ +bool WriteInt(std::ostream &ostr, int i); + +/*! + * \brief ReadInt + * \param istr Input stream + * \param[out] i + * \return true on success + */ +bool ReadInt(std::istream &istr, int &i); /*! * \brief WriteLong - * \param pf - * \param w - * \return + * \param ostr Output stream + * \param l + * \param padTo minimum number of bytes to write + * \return true on success */ -bool WriteLong(FILE* pf, long w); +bool WriteLong(std::ostream &ostr, long l, unsigned padTo = 0); + +/*! + * \brief ReadLong + * \param istr Input stream + * \param[out] l + * \return true on success + */ +bool ReadLong(std::istream &istr, long &l); + +/*! + * \brief WriteFloat + * \param ostr Output stream + * \param f + * \return true on success + */ +bool WriteFloat(std::ostream &ostr, float f); /*! * \brief ReadFloat - * \param pf - * \param w - * \return + * \param istr Input stream + * \param[out] f + * \return true on success */ -bool ReadFloat(FILE* pf, float& w); +bool ReadFloat(std::istream &istr, float &f); + +/*! + * \brief WriteDouble + * \param ostr Output stream + * \param d + * \return true on success + */ +bool WriteDouble(std::ostream &ostr, double d); + +/*! + * \brief ReadDouble + * \param istr Input stream + * \param[out] d + * \return true on success + */ +bool ReadDouble(std::istream &istr, double &d); + +/*! + * \brief WriteString + * \param ostr Output stream + * \param s + * \return true on success + */ +bool WriteString(std::ostream &ostr, const std::string &s); /*! * \brief ReadString - * \param pf - * \param s - * \return + * \param istr Input stream + * \param[out] s + * \return true on success */ -bool ReadString(FILE* pf, std::string& s); +bool ReadString(std::istream &istr, std::string &s); /*! * \brief WriteType - * \param pf + * \param ostr Output stream * \param type - * \return + * \return true on success */ -bool WriteType(FILE* pf, const CBotTypResult &type); +bool WriteType(std::ostream &ostr, const CBotTypResult &type); /*! * \brief ReadType - * \param pf - * \param type - * \return + * \param istr Input stream + * \param[out] type + * \return true on success */ -bool ReadType(FILE* pf, CBotTypResult &type); +bool ReadType(std::istream &istr, CBotTypResult &type); + +/*! + * \brief WriteStream + * \param ostr Output stream + * \param istr Input stream + * \return true on success + */ +bool WriteStream(std::ostream &ostr, std::istream& istr); + +/*! + * \brief ReadStream + * \param istr Input stream + * \param ostr Output stream + * \return true on success + */ +bool ReadStream(std::istream& istr, std::ostream &ostr); } // namespace CBot diff --git a/src/CBot/CBotProgram.cpp b/src/CBot/CBotProgram.cpp index e8065f47..14ec2b2e 100644 --- a/src/CBot/CBotProgram.cpp +++ b/src/CBot/CBotProgram.cpp @@ -24,7 +24,6 @@ #include "CBot/CBotCStack.h" #include "CBot/CBotClass.h" #include "CBot/CBotUtils.h" -#include "CBot/CBotFileUtils.h" #include "CBot/CBotInstr/CBotFunction.h" @@ -332,51 +331,56 @@ bool CBotProgram::DefineNum(const std::string& name, long val) } //////////////////////////////////////////////////////////////////////////////// -bool CBotProgram::SaveState(FILE* pf) +bool CBotProgram::SaveState(std::ostream &ostr) { - if (!WriteWord( pf, CBOTVERSION)) return false; + if (!WriteLong(ostr, CBOTVERSION)) return false; if (m_stack != nullptr ) { - if (!WriteWord( pf, 1)) return false; - if (!WriteString( pf, m_entryPoint->GetName() )) return false; - if (!m_stack->SaveState(pf)) return false; + if (!WriteWord(ostr, 1)) return false; + if (!WriteString(ostr, m_entryPoint->GetName())) return false; + if (!m_stack->SaveState(ostr)) return false; } else { - if (!WriteWord( pf, 0)) return false; + if (!WriteWord(ostr, 0)) return false; } return true; } -bool CBotProgram::RestoreState(FILE* pf) +bool CBotProgram::RestoreState(std::istream &istr) { unsigned short w; std::string s; Stop(); - if (!ReadWord( pf, w )) return false; - if ( w != CBOTVERSION ) return false; + long version; + if (!ReadLong(istr, version)) return false; + if ( version != CBOTVERSION ) return false; - if (!ReadWord( pf, w )) return false; + if (!ReadWord(istr, w)) return false; if ( w == 0 ) return true; - if (!ReadString( pf, s )) return false; - Start(s); // point de reprise + // don't restore if compile error exists + if (m_error != CBotNoErr) return false; - if (m_stack != nullptr) + if (!ReadString(istr, s)) return false; + if (!Start(s)) return false; // point de reprise + // Start() already created the new stack + // and called m_stack->SetProgram(this); + + // retrieves the stack from the memory + if (!m_stack->RestoreState(istr, m_stack)) { m_stack->Delete(); m_stack = nullptr; + m_stack = CBotStack::AllocateStack(); // start from the top + m_stack->SetProgram(this); + return false; // signal error } - // retrieves the stack from the memory - m_stack = CBotStack::AllocateStack(); - if (!m_stack->RestoreState(pf, m_stack)) return false; - m_stack->SetProgram(this); // bases for routines - // restored some states in the stack according to the structure m_entryPoint->RestoreState(nullptr, m_stack, m_thisVar); return true; diff --git a/src/CBot/CBotProgram.h b/src/CBot/CBotProgram.h index e258fff0..c448b82c 100644 --- a/src/CBot/CBotProgram.h +++ b/src/CBot/CBotProgram.h @@ -282,27 +282,20 @@ public: /** * \brief Save the current execution status into a file - * \param pf - * \parblock - * file handle - * - * This file handle must have been opened by this library! Otherwise crashes on Windows - * - * TODO: Verify this - * \endparblock + * \param ostr Output stream * \return true on success, false on write error */ - bool SaveState(FILE* pf); + bool SaveState(std::ostream &ostr); /** * \brief Restore the execution state from a file * * The previous program code must already have been recompiled with Compile() before calling this function * - * \param pf file handle + * \param istr Input stream * \return true on success, false on read error */ - bool RestoreState(FILE* pf); + bool RestoreState(std::istream &istr); /** * \brief GetPosition Gives the position of a routine in the original text diff --git a/src/CBot/CBotStack.cpp b/src/CBot/CBotStack.cpp index 9f9db2d6..7f3c0b9a 100644 --- a/src/CBot/CBotStack.cpp +++ b/src/CBot/CBotStack.cpp @@ -26,7 +26,6 @@ #include "CBot/CBotVar/CBotVarPointer.h" #include "CBot/CBotVar/CBotVarClass.h" -#include "CBot/CBotFileUtils.h" #include "CBot/CBotUtils.h" #include "CBot/CBotExternalCall.h" @@ -687,108 +686,106 @@ CBotVar* CBotStack::GetStackVars(std::string& functionName, int level) } //////////////////////////////////////////////////////////////////////////////// -bool CBotStack::SaveState(FILE* pf) +bool CBotStack::SaveState(std::ostream &ostr) { if (m_next2 != nullptr) { - if (!WriteWord(pf, 2)) return false; // a marker of type (m_next2) - if (!m_next2->SaveState(pf)) return false; // saves the next element + if (!WriteWord(ostr, 2)) return false; // a marker of type (m_next2) + if (!m_next2->SaveState(ostr)) return false; // saves the next element } else { - if (!WriteWord(pf, 1)) return false; // a marker of type (m_next) + if (!WriteWord(ostr, 1)) return false; // a marker of type (m_next) } - if (!WriteWord(pf, static_cast(m_block))) return false; - if (!WriteWord(pf, m_state)) return false; - if (!WriteWord(pf, 0)) return false; // for backwards combatibility (m_bDontDelete) - if (!WriteWord(pf, m_step)) return false; + if (!WriteWord(ostr, static_cast(m_block))) return false; + if (!WriteInt(ostr, m_state)) return false; + if (!WriteWord(ostr, 0)) return false; // for backwards combatibility (m_bDontDelete) + if (!WriteInt(ostr, m_step)) return false; - - if (!SaveVars(pf, m_var)) return false; // current result - if (!SaveVars(pf, m_listVar)) return false; // local variables + if (!SaveVars(ostr, m_var)) return false; // current result + if (!SaveVars(ostr, m_listVar)) return false; // local variables if (m_next != nullptr) { - if (!m_next->SaveState(pf)) return false; // saves the next element + if (!m_next->SaveState(ostr)) return false; // saves the next element } else { - if (!WriteWord(pf, 0)) return false; // terminator + if (!WriteWord(ostr, 0)) return false; // 0 - CBotStack::SaveState terminator } return true; } -bool SaveVars(FILE* pf, CBotVar* pVar) +bool SaveVars(std::ostream &ostr, CBotVar* pVar) { while (pVar != nullptr) { - if (!pVar->Save0State(pf)) return false; // common header - if (!pVar->Save1State(pf)) return false; // saves the data + if (!pVar->Save0State(ostr)) return false; // common header + if (!pVar->Save1State(ostr)) return false; // saves the data pVar = pVar->GetNext(); } - return WriteWord(pf, 0); // terminator + return WriteWord(ostr, 0); // 0 - CBot::SaveVars terminator } //////////////////////////////////////////////////////////////////////////////// -bool CBotStack::RestoreState(FILE* pf, CBotStack* &pStack) +bool CBotStack::RestoreState(std::istream &istr, CBotStack* &pStack) { unsigned short w; if (pStack != this) pStack = nullptr; - if (!ReadWord(pf, w)) return false; - if ( w == 0 ) return true; // 0 - terminator + if (!ReadWord(istr, w)) return false; + if ( w == 0 ) return true; // 0 - CBotStack::SaveState terminator if (pStack == nullptr) pStack = AddStack(); if ( w == 2 ) // 2 - m_next2 { - if (!pStack->RestoreState(pf, pStack->m_next2)) return false; + if (!pStack->RestoreState(istr, pStack->m_next2)) return false; } - if (!ReadWord(pf, w)) return false; + if (!ReadWord(istr, w)) return false; pStack->m_block = static_cast(w); - if (!ReadWord(pf, w)) return false; - pStack->SetState(static_cast(w)); + int state; + if (!ReadInt(istr, state)) return false; + pStack->SetState(state); - if (!ReadWord(pf, w)) return false; // backwards compatibility (m_bDontDelete) + if (!ReadWord(istr, w)) return false; // backwards compatibility (m_bDontDelete) - if (!ReadWord(pf, w)) return false; - pStack->m_step = w; + if (!ReadInt(istr, state)) return false; + pStack->m_step = state; - if (!CBotVar::RestoreState(pf, pStack->m_var)) return false; // temp variable - if (!CBotVar::RestoreState(pf, pStack->m_listVar)) return false;// local variables + if (!CBotVar::RestoreState(istr, pStack->m_var)) return false; // temp variable + if (!CBotVar::RestoreState(istr, pStack->m_listVar)) return false; // local variables - return pStack->RestoreState(pf, pStack->m_next); + return pStack->RestoreState(istr, pStack->m_next); } //////////////////////////////////////////////////////////////////////////////// -bool CBotVar::Save0State(FILE* pf) +bool CBotVar::Save0State(std::ostream &ostr) { - if (!WriteWord(pf, 100+static_cast(m_mPrivate)))return false; // private variable? - if (!WriteWord(pf, m_bStatic))return false; // static variable? - if (!WriteWord(pf, m_type.GetType()))return false; // saves the type (always non-zero) + if (!WriteWord(ostr, 100+static_cast(m_mPrivate)))return false; // private variable? + if (!WriteWord(ostr, m_bStatic))return false; // static variable? + if (!WriteWord(ostr, m_type.GetType()))return false; // saves the type (always non-zero) if (m_type.Eq(CBotTypPointer) && GetPointer() != nullptr) { if (GetPointer()->m_bConstructor) // constructor was called? { - if (!WriteWord(pf, (2000 + static_cast(m_binit)) )) return false; - return WriteString(pf, m_token->GetString()); // and variable name + if (!WriteWord(ostr, (2000 + static_cast(m_binit)) )) return false; + return WriteString(ostr, m_token->GetString()); // and variable name } } - if (!WriteWord(pf, static_cast(m_binit))) return false; // variable defined? - return WriteString(pf, m_token->GetString()); // and variable name + if (!WriteWord(ostr, static_cast(m_binit))) return false; // variable defined? + return WriteString(ostr, m_token->GetString()); // and variable name } //////////////////////////////////////////////////////////////////////////////// -bool CBotVar::RestoreState(FILE* pf, CBotVar* &pVar) +bool CBotVar::RestoreState(std::istream &istr, CBotVar* &pVar) { unsigned short w, wi, prv, st; - float ww; - std::string name, s; delete pVar; @@ -798,27 +795,27 @@ bool CBotVar::RestoreState(FILE* pf, CBotVar* &pVar) while ( true ) // retrieves a list { - if (!ReadWord(pf, w)) return false; // private or type? - if ( w == 0 ) return true; + if (!ReadWord(istr, w)) return false; // private or type? + if ( w == 0 ) return true; // 0 - CBot::SaveVars terminator std::string defnum; if ( w == 200 ) { - if (!ReadString(pf, defnum)) return false; // number with identifier - if (!ReadWord(pf, w)) return false; // type + if (!ReadString(istr, defnum)) return false; // number with identifier + if (!ReadWord(istr, w)) return false; // type } prv = 100; st = 0; if ( w >= 100 ) { prv = w; - if (!ReadWord(pf, st)) return false; // static - if (!ReadWord(pf, w)) return false; // type + if (!ReadWord(istr, st)) return false; // static + if (!ReadWord(istr, w)) return false; // type } if ( w == CBotTypClass ) w = CBotTypIntrinsic; // necessarily intrinsic - if (!ReadWord(pf, wi)) return false; // init ? + if (!ReadWord(istr, wi)) return false; // init ? bool bConstructor = false; if (w == CBotTypPointer && wi >= 2000) { @@ -827,31 +824,40 @@ bool CBotVar::RestoreState(FILE* pf, CBotVar* &pVar) } CBotVar::InitType initType = static_cast(wi); - if (!ReadString(pf, name)) return false; // variable name - - CBotToken token(name, std::string()); + std::string varname; + if (!ReadString(istr, varname)) return false; // variable name + CBotToken token(varname, std::string()); bool isClass = false; switch (w) { - case CBotTypInt: case CBotTypBoolean: + char valByte; + if (!ReadByte(istr, valByte)) return false; pNew = CBotVar::Create(token, w); // creates a variable - if (!ReadWord(pf, w)) return false; - pNew->SetValInt(static_cast(w), defnum); + pNew->SetValInt(valByte); + break; + case CBotTypInt: + int valInt; + if (!ReadInt(istr, valInt)) return false; + pNew = CBotVar::Create(token, w); // creates a variable + pNew->SetValInt(valInt, defnum); break; case CBotTypFloat: + float valFloat; + if (!ReadFloat(istr, valFloat)) return false; pNew = CBotVar::Create(token, w); // creates a variable - if (!ReadFloat(pf, ww)) return false; - pNew->SetValFloat(ww); + pNew->SetValFloat(valFloat); break; case CBotTypString: - pNew = CBotVar::Create(token, w); // creates a variable - if (!ReadString(pf, s)) return false; - pNew->SetValString(s); - break; - + { + std::string valString; + if (!ReadString(istr, valString)) return false; + pNew = CBotVar::Create(token, w); // creates a variable + pNew->SetValString(valString); + break; + } // returns an intrinsic object or element of an array case CBotTypIntrinsic: isClass = true; @@ -859,17 +865,16 @@ bool CBotVar::RestoreState(FILE* pf, CBotVar* &pVar) { CBotTypResult r; long id; - if (!ReadType(pf, r)) return false; // complete type - if (!ReadLong(pf, id) ) return false; + if (!ReadType(istr, r)) return false; // complete type + if (!ReadLong(istr, id)) return false; -// if (!ReadString(pf, s)) return false; { CBotVar* p = nullptr; if ( id ) p = CBotVarClass::Find(id) ; pNew = new CBotVarClass(token, r); // directly creates an instance // attention cptuse = 0 - if ( !RestoreState(pf, (static_cast(pNew))->m_pVar)) return false; + if (!RestoreState(istr, (static_cast(pNew))->m_pVar)) return false; pNew->SetIdent(id); if (isClass && p == nullptr) // set id for each item in this instance @@ -895,18 +900,20 @@ bool CBotVar::RestoreState(FILE* pf, CBotVar* &pVar) case CBotTypPointer: case CBotTypNullPointer: - if (!ReadString(pf, s)) return false; // name of the class + { + std::string className; + if (!ReadString(istr, className)) return false; // name of the class { - CBotTypResult ptrType(w, s); - pNew = CBotVar::Create(token, ptrType);// creates a variable // CBotVarClass* p = nullptr; long id; - ReadLong(pf, id); + if (!ReadLong(istr, id)) return false; // if ( id ) p = CBotVarClass::Find(id); // found the instance (made by RestoreInstance) + CBotTypResult ptrType(w, className); + pNew = CBotVar::Create(token, ptrType); // creates a variable // returns a copy of the original instance CBotVar* pInstance = nullptr; - if ( !CBotVar::RestoreState( pf, pInstance ) ) return false; + if (!CBotVar::RestoreState(istr, pInstance)) return false; (static_cast(pNew))->SetPointer( pInstance ); // and point over if (bConstructor) pNew->ConstructorSet(); // constructor was called @@ -916,22 +923,22 @@ bool CBotVar::RestoreState(FILE* pf, CBotVar* &pVar) } break; - + } case CBotTypArrayPointer: { CBotTypResult r; - if (!ReadType(pf, r)) return false; + if (!ReadType(istr, r)) return false; pNew = CBotVar::Create(token, r); // creates a variable // returns a copy of the original instance CBotVar* pInstance = nullptr; - if ( !CBotVar::RestoreState( pf, pInstance ) ) return false; + if (!CBotVar::RestoreState(istr, pInstance)) return false; (static_cast(pNew))->SetPointer( pInstance ); // and point over } break; default: - assert(0); + return false; // signal error } if ( pPrev != nullptr ) pPrev->m_next = pNew; diff --git a/src/CBot/CBotStack.h b/src/CBot/CBotStack.h index a72b3e4a..498ccd78 100644 --- a/src/CBot/CBotStack.h +++ b/src/CBot/CBotStack.h @@ -434,8 +434,8 @@ public: //! \name Write to file //@{ - bool SaveState(FILE* pf); - bool RestoreState(FILE* pf, CBotStack* &pStack); + bool SaveState(std::ostream &ostr); + bool RestoreState(std::istream &istr, CBotStack* &pStack); //@} diff --git a/src/CBot/CBotUtils.cpp b/src/CBot/CBotUtils.cpp index a6c6ef9c..a87e2943 100644 --- a/src/CBot/CBotUtils.cpp +++ b/src/CBot/CBotUtils.cpp @@ -108,38 +108,6 @@ CBotTypResult ArrayType(CBotToken* &p, CBotCStack* pile, CBotTypResult type) return type; } -//////////////////////////////////////////////////////////////////////////////// -bool WriteWord(FILE* pf, unsigned short w) -{ - size_t lg; - - lg = fwrite(&w, sizeof( unsigned short ), 1, pf ); - - return (lg == 1); -} - -//////////////////////////////////////////////////////////////////////////////// -bool WriteString(FILE* pf, std::string s) -{ - size_t lg1, lg2; - - lg1 = s.size(); - if (!WriteWord(pf, lg1)) return false; - - lg2 = fwrite(s.c_str(), 1, lg1, pf ); - return (lg1 == lg2); -} - -//////////////////////////////////////////////////////////////////////////////// -bool WriteFloat(FILE* pf, float w) -{ - size_t lg; - - lg = fwrite(&w, sizeof( float ), 1, pf ); - - return (lg == 1); -} - //////////////////////////////////////////////////////////////////////////////// long GetNumInt(const std::string& str) { diff --git a/src/CBot/CBotUtils.h b/src/CBot/CBotUtils.h index 9042b0f8..5b58535c 100644 --- a/src/CBot/CBotUtils.h +++ b/src/CBot/CBotUtils.h @@ -19,9 +19,8 @@ #pragma once -#include "CBot/CBotTypResult.h" +#include "CBot/CBotFileUtils.h" -#include #include #include @@ -31,6 +30,8 @@ namespace CBot class CBotVar; class CBotToken; class CBotCStack; +class CBotTypResult; + /*! * \brief MakeListVars Transforms the array of pointers to variables in a @@ -58,30 +59,6 @@ CBotTypResult TypeParam(CBotToken* &p, CBotCStack* pile); */ CBotTypResult ArrayType(CBotToken* &p, CBotCStack* pile, CBotTypResult type); -/*! - * \brief WriteWord - * \param pf - * \param w - * \return - */ -bool WriteWord(FILE* pf, unsigned short w); - -/*! - * \brief WriteString - * \param pf - * \param s - * \return - */ -bool WriteString(FILE* pf, std::string s); - -/*! - * \brief WriteFloat - * \param pf - * \param w - * \return - */ -bool WriteFloat(FILE* pf, float w); - /*! * \brief GetNumInt Converts a string into integer may be of the form 0xabc123. * \param str diff --git a/src/CBot/CBotVar/CBotVar.cpp b/src/CBot/CBotVar/CBotVar.cpp index 62ba41f4..4886c033 100644 --- a/src/CBot/CBotVar/CBotVar.cpp +++ b/src/CBot/CBotVar/CBotVar.cpp @@ -134,7 +134,7 @@ void* CBotVar::GetUserPtr() } //////////////////////////////////////////////////////////////////////////////// -bool CBotVar::Save1State(FILE* pf) +bool CBotVar::Save1State(std::ostream &ostr) { // this routine "virtual" must never be called, // there must be a routine for each of the subclasses (CBotVarInt, CBotVarFloat, etc) diff --git a/src/CBot/CBotVar/CBotVar.h b/src/CBot/CBotVar/CBotVar.h index d2805ddf..980b8a71 100644 --- a/src/CBot/CBotVar/CBotVar.h +++ b/src/CBot/CBotVar/CBotVar.h @@ -623,28 +623,28 @@ public: /** * \brief Save common variable header (name, type, etc.) - * \param pf file pointer + * \param ostr Output stream * \return false on write error */ - virtual bool Save0State(FILE* pf); + virtual bool Save0State(std::ostream &ostr); /** * \brief Save variable data * * Overriden in child classes * - * \param pf file pointer + * \param ostr Output stream * \return false on write error */ - virtual bool Save1State(FILE* pf); + virtual bool Save1State(std::ostream &ostr); /** * \brief Restore variable - * \param pf file pointer + * \param istr Input stream * \param[out] pVar Pointer to recieve the variable * \return false on read error */ - static bool RestoreState(FILE* pf, CBotVar* &pVar); + static bool RestoreState(std::istream &istr, CBotVar* &pVar); //@} diff --git a/src/CBot/CBotVar/CBotVarArray.cpp b/src/CBot/CBotVar/CBotVarArray.cpp index 6462d119..05575dc1 100644 --- a/src/CBot/CBotVar/CBotVarArray.cpp +++ b/src/CBot/CBotVar/CBotVarArray.cpp @@ -20,7 +20,6 @@ #include "CBot/CBotVar/CBotVarArray.h" #include "CBot/CBotVar/CBotVarClass.h" #include "CBot/CBotToken.h" -#include "CBot/CBotFileUtils.h" #include "CBot/CBotEnums.h" @@ -137,10 +136,10 @@ std::string CBotVarArray::GetValString() } //////////////////////////////////////////////////////////////////////////////// -bool CBotVarArray::Save1State(FILE* pf) +bool CBotVarArray::Save1State(std::ostream &ostr) { - if ( !WriteType(pf, m_type) ) return false; - return SaveVars(pf, m_pInstance); // saves the instance that manages the table + if (!WriteType(ostr, m_type)) return false; + return SaveVars(ostr, m_pInstance); // saves the instance that manages the table } } // namespace CBot diff --git a/src/CBot/CBotVar/CBotVarArray.h b/src/CBot/CBotVar/CBotVarArray.h index 68b00b28..fb211055 100644 --- a/src/CBot/CBotVar/CBotVarArray.h +++ b/src/CBot/CBotVar/CBotVarArray.h @@ -51,7 +51,7 @@ public: std::string GetValString() override; - bool Save1State(FILE* pf) override; + bool Save1State(std::ostream &ostr) override; private: //! Array data diff --git a/src/CBot/CBotVar/CBotVarBoolean.cpp b/src/CBot/CBotVar/CBotVarBoolean.cpp index d36de078..b733e933 100644 --- a/src/CBot/CBotVar/CBotVarBoolean.cpp +++ b/src/CBot/CBotVar/CBotVarBoolean.cpp @@ -40,9 +40,9 @@ void CBotVarBoolean::Not() SetValInt(!GetValInt()); } -bool CBotVarBoolean::Save1State(FILE* pf) +bool CBotVarBoolean::Save1State(std::ostream &ostr) { - return WriteWord(pf, m_val); // the value of the variable + return WriteByte(ostr, m_val); // the value of the variable } } // namespace CBot diff --git a/src/CBot/CBotVar/CBotVarBoolean.h b/src/CBot/CBotVar/CBotVarBoolean.h index 44774a9f..da810e98 100644 --- a/src/CBot/CBotVar/CBotVarBoolean.h +++ b/src/CBot/CBotVar/CBotVarBoolean.h @@ -37,7 +37,7 @@ public: void XOr(CBotVar* left, CBotVar* right) override; void Not() override; - bool Save1State(FILE* pf) override; + bool Save1State(std::ostream &ostr) override; }; } // namespace CBot diff --git a/src/CBot/CBotVar/CBotVarClass.cpp b/src/CBot/CBotVar/CBotVarClass.cpp index 36952d18..9d587c1f 100644 --- a/src/CBot/CBotVar/CBotVarClass.cpp +++ b/src/CBot/CBotVar/CBotVarClass.cpp @@ -23,8 +23,6 @@ #include "CBot/CBotStack.h" #include "CBot/CBotDefines.h" -#include "CBot/CBotFileUtils.h" - #include "CBot/CBotInstr/CBotInstr.h" #include @@ -464,12 +462,12 @@ bool CBotVarClass::Ne(CBotVar* left, CBotVar* right) } //////////////////////////////////////////////////////////////////////////////// -bool CBotVarClass::Save1State(FILE* pf) +bool CBotVarClass::Save1State(std::ostream &ostr) { - if ( !WriteType(pf, m_type) ) return false; - if ( !WriteLong(pf, m_ItemIdent) ) return false; + if (!WriteType(ostr, m_type)) return false; + if (!WriteLong(ostr, m_ItemIdent)) return false; - return SaveVars(pf, m_pVar); // content of the object + return SaveVars(ostr, m_pVar); // content of the object } } // namespace CBot diff --git a/src/CBot/CBotVar/CBotVarClass.h b/src/CBot/CBotVar/CBotVarClass.h index 55bdd891..34ec732e 100644 --- a/src/CBot/CBotVar/CBotVarClass.h +++ b/src/CBot/CBotVar/CBotVarClass.h @@ -54,7 +54,7 @@ public: CBotVar* GetItemList() override; std::string GetValString() override; - bool Save1State(FILE* pf) override; + bool Save1State(std::ostream &ostr) override; void Update(void* pUser) override; diff --git a/src/CBot/CBotVar/CBotVarFloat.cpp b/src/CBot/CBotVar/CBotVarFloat.cpp index a1c4217f..bfa1c1f9 100644 --- a/src/CBot/CBotVar/CBotVarFloat.cpp +++ b/src/CBot/CBotVar/CBotVarFloat.cpp @@ -22,9 +22,9 @@ namespace CBot { -bool CBotVarFloat::Save1State(FILE* pf) +bool CBotVarFloat::Save1State(std::ostream &ostr) { - return WriteFloat(pf, m_val); // the value of the variable + return WriteFloat(ostr, m_val); // the value of the variable } } // namespace CBot diff --git a/src/CBot/CBotVar/CBotVarFloat.h b/src/CBot/CBotVar/CBotVarFloat.h index 81cbcde0..dfdecd9b 100644 --- a/src/CBot/CBotVar/CBotVarFloat.h +++ b/src/CBot/CBotVar/CBotVarFloat.h @@ -32,7 +32,7 @@ class CBotVarFloat : public CBotVarNumber public: CBotVarFloat(const CBotToken &name) : CBotVarNumber(name) {} - bool Save1State(FILE* pf) override; + bool Save1State(std::ostream &ostr) override; }; } // namespace CBot diff --git a/src/CBot/CBotVar/CBotVarInt.cpp b/src/CBot/CBotVar/CBotVarInt.cpp index 477629ae..852566f9 100644 --- a/src/CBot/CBotVar/CBotVarInt.cpp +++ b/src/CBot/CBotVar/CBotVarInt.cpp @@ -45,17 +45,17 @@ std::string CBotVarInt::GetValString() void CBotVarInt::Neg() { CBotVarNumber::Neg(); - m_defnum.empty(); + m_defnum.clear(); } void CBotVarInt::Inc() { CBotVarNumber::Inc(); - m_defnum.empty(); + m_defnum.clear(); } void CBotVarInt::Dec() { CBotVarNumber::Dec(); - m_defnum.empty(); + m_defnum.clear(); } void CBotVarInt::XOr(CBotVar* left, CBotVar* right) @@ -90,22 +90,23 @@ void CBotVarInt::SR(CBotVar* left, CBotVar* right) void CBotVarInt::Not() { m_val = ~m_val; + m_defnum.clear(); } -bool CBotVarInt::Save0State(FILE* pf) +bool CBotVarInt::Save0State(std::ostream &ostr) { if (!m_defnum.empty()) { - if(!WriteWord(pf, 200)) return false; // special marker - if(!WriteString(pf, m_defnum)) return false; + if(!WriteWord(ostr, 200)) return false; // special marker + if(!WriteString(ostr, m_defnum)) return false; } - return CBotVar::Save0State(pf); + return CBotVar::Save0State(ostr); } -bool CBotVarInt::Save1State(FILE* pf) +bool CBotVarInt::Save1State(std::ostream &ostr) { - return WriteWord(pf, m_val); + return WriteInt(ostr, m_val); } } // namespace CBot diff --git a/src/CBot/CBotVar/CBotVarInt.h b/src/CBot/CBotVar/CBotVarInt.h index 906a402d..5e04135c 100644 --- a/src/CBot/CBotVar/CBotVarInt.h +++ b/src/CBot/CBotVar/CBotVarInt.h @@ -50,8 +50,8 @@ public: void SR(CBotVar* left, CBotVar* right) override; void ASR(CBotVar* left, CBotVar* right) override; - bool Save0State(FILE* pf) override; - bool Save1State(FILE* pf) override; + bool Save0State(std::ostream &ostr) override; + bool Save1State(std::ostream &ostr) override; protected: //! The name if given by DefineNum. diff --git a/src/CBot/CBotVar/CBotVarPointer.cpp b/src/CBot/CBotVar/CBotVarPointer.cpp index 2f78c90a..24f9a5cb 100644 --- a/src/CBot/CBotVar/CBotVarPointer.cpp +++ b/src/CBot/CBotVar/CBotVarPointer.cpp @@ -24,7 +24,6 @@ #include "CBot/CBotEnums.h" #include "CBot/CBotUtils.h" -#include "CBot/CBotFileUtils.h" #include @@ -171,21 +170,21 @@ CBotClass* CBotVarPointer::GetClass() } //////////////////////////////////////////////////////////////////////////////// -bool CBotVarPointer::Save1State(FILE* pf) +bool CBotVarPointer::Save1State(std::ostream &ostr) { if ( m_type.GetClass() != nullptr ) { - if (!WriteString(pf, m_type.GetClass()->GetName())) return false; // name of the class + if (!WriteString(ostr, m_type.GetClass()->GetName())) return false; // name of the class } else { - if (!WriteString(pf, "")) return false; + if (!WriteString(ostr, "")) return false; } - if (!WriteLong(pf, GetIdent())) return false; // the unique reference + if (!WriteLong(ostr, GetIdent())) return false; // the unique reference // also saves the proceedings copies - return SaveVars(pf, GetPointer()); + return SaveVars(ostr, GetPointer()); } //////////////////////////////////////////////////////////////////////////////// diff --git a/src/CBot/CBotVar/CBotVarPointer.h b/src/CBot/CBotVar/CBotVarPointer.h index 75c9bc98..6f9c0155 100644 --- a/src/CBot/CBotVar/CBotVarPointer.h +++ b/src/CBot/CBotVar/CBotVarPointer.h @@ -61,7 +61,7 @@ public: void ConstructorSet() override; - bool Save1State(FILE* pf) override; + bool Save1State(std::ostream &ostr) override; void Update(void* pUser) override; diff --git a/src/CBot/CBotVar/CBotVarString.cpp b/src/CBot/CBotVar/CBotVarString.cpp index 91a0e618..90856b82 100644 --- a/src/CBot/CBotVar/CBotVarString.cpp +++ b/src/CBot/CBotVar/CBotVarString.cpp @@ -37,9 +37,9 @@ bool CBotVarString::Ne(CBotVar* left, CBotVar* right) return left->GetValString() != right->GetValString(); } -bool CBotVarString::Save1State(FILE* pf) +bool CBotVarString::Save1State(std::ostream &ostr) { - return WriteString(pf, m_val); + return WriteString(ostr, m_val); } } // namespace CBot diff --git a/src/CBot/CBotVar/CBotVarString.h b/src/CBot/CBotVar/CBotVarString.h index d01a80f6..ac04a310 100644 --- a/src/CBot/CBotVar/CBotVarString.h +++ b/src/CBot/CBotVar/CBotVarString.h @@ -63,7 +63,7 @@ public: bool Eq(CBotVar* left, CBotVar* right) override; bool Ne(CBotVar* left, CBotVar* right) override; - bool Save1State(FILE* pf) override; + bool Save1State(std::ostream &ostr) override; private: template diff --git a/src/CBot/CBotVar/CBotVarValue.h b/src/CBot/CBotVar/CBotVarValue.h index 76e820e8..5e58075d 100644 --- a/src/CBot/CBotVar/CBotVarValue.h +++ b/src/CBot/CBotVar/CBotVarValue.h @@ -86,7 +86,10 @@ template class CBotVarNumberBase : public CBotVarValue { public: - CBotVarNumberBase(const CBotToken &name) : CBotVarValue(name) {} + CBotVarNumberBase(const CBotToken &name) : CBotVarValue(name) + { + this->m_val = static_cast(0); + } void SetValInt(int val, const std::string &s = "") override { diff --git a/src/level/robotmain.cpp b/src/level/robotmain.cpp index 58d90541..dea86bcb 100644 --- a/src/level/robotmain.cpp +++ b/src/level/robotmain.cpp @@ -4449,10 +4449,8 @@ void CRobotMain::SaveOneScript(CObject *obj) } //! Saves the stack of the program in execution of a robot -bool CRobotMain::SaveFileStack(CObject *obj, FILE *file, int objRank) +bool CRobotMain::SaveFileStack(CObject *obj, std::ostream &ostr) { - if (objRank == -1) return true; - if (! obj->Implements(ObjectInterfaceType::Programmable)) return true; CProgrammableObject* programmable = dynamic_cast(obj); @@ -4460,14 +4458,24 @@ bool CRobotMain::SaveFileStack(CObject *obj, FILE *file, int objRank) ObjectType type = obj->GetType(); if (type == OBJECT_HUMAN) return true; - return programmable->WriteStack(file); + long status = 1; + std::stringstream sstr(""); + + if (!programmable->WriteStack(sstr)) + { + GetLogger()->Error("WriteStack failed at object id = %i\n", obj->GetID()); + status = 100; // marked bad + } + + if (!CBot::WriteLong(ostr, status)) return false; + if (!CBot::WriteStream(ostr, sstr)) return false; + + return true; } //! Resumes the execution stack of the program in a robot -bool CRobotMain::ReadFileStack(CObject *obj, FILE *file, int objRank) +bool CRobotMain::ReadFileStack(CObject *obj, std::istream &istr) { - if (objRank == -1) return true; - if (! obj->Implements(ObjectInterfaceType::Programmable)) return true; CProgrammableObject* programmable = dynamic_cast(obj); @@ -4475,7 +4483,29 @@ bool CRobotMain::ReadFileStack(CObject *obj, FILE *file, int objRank) ObjectType type = obj->GetType(); if (type == OBJECT_HUMAN) return true; - return programmable->ReadStack(file); + long status; + if (!CBot::ReadLong(istr, status)) return false; + + if (status == 100) // was marked bad ? + { + if (!CBot::ReadLong(istr, status)) return false; + if (!istr.seekg(status, istr.cur)) return false; + return true; // next program + } + + if (status == 1) + { + std::stringstream sstr(""); + if (!CBot::ReadStream(istr, sstr)) return false; + + if (!programmable->ReadStack(sstr)) + { + GetLogger()->Error("ReadStack failed at object id = %i\n", obj->GetID()); + } + return true; // next program + } + + return false; // error: status == ?? } std::vector CRobotMain::GetNewScriptNames(ObjectType type) @@ -4670,25 +4700,36 @@ bool CRobotMain::IOWriteScene(std::string filename, std::string filecbot, std::s } // Writes the file of stacks of execution. - FILE* file = CBot::fOpen((CResourceManager::GetSaveLocation() + "/" + filecbot).c_str(), "wb"); - if (file == nullptr) return false; + COutputStream ostr(filecbot); + if (!ostr.is_open()) return false; + bool bError = false; long version = 1; - CBot::fWrite(&version, sizeof(long), 1, file); // version of COLOBOT + CBot::WriteLong(ostr, version); // version of COLOBOT version = CBot::CBotProgram::GetVersion(); - CBot::fWrite(&version, sizeof(long), 1, file); // version of CBOT + CBot::WriteLong(ostr, version); // version of CBOT + CBot::WriteWord(ostr, 0); // TODO - objRank = 0; for (CObject* obj : m_objMan->GetAllObjects()) { if (obj->GetType() == OBJECT_TOTO) continue; if (IsObjectBeingTransported(obj)) continue; if (obj->Implements(ObjectInterfaceType::Destroyable) && dynamic_cast(obj)->IsDying()) continue; - if (!SaveFileStack(obj, file, objRank++)) break; + if (!SaveFileStack(obj, ostr)) + { + GetLogger()->Error("SaveFileStack failed at object id = %i\n", obj->GetID()); + bError = true; + break; + } } - CBot::CBotClass::SaveStaticState(file); - CBot::fClose(file); + + if (!bError && !CBot::CBotClass::SaveStaticState(ostr)) + { + GetLogger()->Error("CBotClass save static state failed\n"); + } + + ostr.close(); if (!emergencySave) { @@ -4846,29 +4887,48 @@ CObject* CRobotMain::IOReadScene(std::string filename, std::string filecbot) m_ui->GetLoadingScreen()->SetProgress(0.95f, RT_LOADING_CBOT_SAVE); // Reads the file of stacks of execution. - FILE* file = CBot::fOpen((CResourceManager::GetSaveLocation() + "/" + filecbot).c_str(), "rb"); - if (file != nullptr) + CInputStream istr(filecbot); + + if (istr.is_open()) { - long version; - CBot::fRead(&version, sizeof(long), 1, file); // version of COLOBOT + bool bError = false; + long version = 0; + CBot::ReadLong(istr, version); // version of COLOBOT if (version == 1) { - CBot::fRead(&version, sizeof(long), 1, file); // version of CBOT + CBot::ReadLong(istr, version); // version of CBOT if (version == CBot::CBotProgram::GetVersion()) { - objRank = 0; - for (CObject* obj : m_objMan->GetAllObjects()) + unsigned short flag; + CBot::ReadWord(istr, flag); // TODO + bError = (flag != 0); + + if (!bError) for (CObject* obj : m_objMan->GetAllObjects()) { if (obj->GetType() == OBJECT_TOTO) continue; if (IsObjectBeingTransported(obj)) continue; if (obj->Implements(ObjectInterfaceType::Destroyable) && dynamic_cast(obj)->IsDying()) continue; - if (!ReadFileStack(obj, file, objRank++)) break; + if (!ReadFileStack(obj, istr)) + { + GetLogger()->Error("ReadFileStack failed at object id = %i\n", obj->GetID()); + bError = true; + break; + } + } + + if (!bError && !CBot::CBotClass::RestoreStaticState(istr)) + { + GetLogger()->Error("CBotClass restore static state failed\n"); + bError = true; } } + else + GetLogger()->Error("cbot.run file is wrong version: %i\n", version); } - CBot::CBotClass::RestoreStaticState(file); - CBot::fClose(file); + + if (bError) GetLogger()->Error("Restoring CBOT state failed at stream position: %li\n", istr.tellg()); + istr.close(); } m_ui->GetLoadingScreen()->SetProgress(1.0f, RT_LOADING_FINISHED); diff --git a/src/level/robotmain.h b/src/level/robotmain.h index 2614f2cc..2b4c7b03 100644 --- a/src/level/robotmain.h +++ b/src/level/robotmain.h @@ -308,8 +308,8 @@ public: void SaveAllScript(); void SaveOneScript(CObject *obj); - bool SaveFileStack(CObject *obj, FILE *file, int objRank); - bool ReadFileStack(CObject *obj, FILE *file, int objRank); + bool SaveFileStack(CObject *obj, std::ostream &ostr); + bool ReadFileStack(CObject *obj, std::istream &istr); //! Return list of scripts to load to robot created in BotFactory std::vector GetNewScriptNames(ObjectType type); diff --git a/src/object/implementation/programmable_impl.cpp b/src/object/implementation/programmable_impl.cpp index 5e370bdb..34be6c59 100644 --- a/src/object/implementation/programmable_impl.cpp +++ b/src/object/implementation/programmable_impl.cpp @@ -143,24 +143,43 @@ bool CProgrammableObjectImpl::IsProgram() // Load a stack of script implementation from a file. -bool CProgrammableObjectImpl::ReadStack(FILE *file) +bool CProgrammableObjectImpl::ReadStack(std::istream &istr) { short op; - CBot::fRead(&op, sizeof(short), 1, file); + if (!CBot::ReadShort(istr, op)) return false; if ( op == 1 ) // run ? { - CBot::fRead(&op, sizeof(short), 1, file); // program rank + if (!CBot::ReadShort(istr, op)) return false; // program rank if ( op >= 0 ) { if (m_object->Implements(ObjectInterfaceType::ProgramStorage)) { - assert(op < static_cast(dynamic_cast(m_object)->GetProgramCount())); + int count = static_cast(dynamic_cast(m_object)->GetProgramCount()); + if (!(op < count)) + { + GetLogger()->Info("Object program count: %i\n", count); + GetLogger()->Error("Error in file: program index out of range: %i\n", op); + return false; + } + m_currentProgram = dynamic_cast(m_object)->GetProgram(op); - if ( !m_currentProgram->script->ReadStack(file) ) return false; + if (!m_currentProgram->script->ReadStack(istr)) + { + GetLogger()->Error("Restore state failed at program index: %i\n", op); + int errNum = m_currentProgram->script->GetError(); + if (errNum != 0) + { + std::string errStr; + m_currentProgram->script->GetError(errStr); + GetLogger()->Error("Program reports error: %i:(%s)\n", errNum, errStr.c_str()); + } + return false; + } } else { + GetLogger()->Error("Object is not a program storage object\n"); return false; } } @@ -171,7 +190,7 @@ bool CProgrammableObjectImpl::ReadStack(FILE *file) // Save the script implementation stack of a file. -bool CProgrammableObjectImpl::WriteStack(FILE *file) +bool CProgrammableObjectImpl::WriteStack(std::ostream &ostr) { short op; @@ -179,21 +198,25 @@ bool CProgrammableObjectImpl::WriteStack(FILE *file) m_currentProgram->script->IsRunning() ) { op = 1; // run - CBot::fWrite(&op, sizeof(short), 1, file); + if (!CBot::WriteShort(ostr, op)) return false; op = -1; if (m_object->Implements(ObjectInterfaceType::ProgramStorage)) { op = dynamic_cast(m_object)->GetProgramIndex(m_currentProgram); } - CBot::fWrite(&op, sizeof(short), 1, file); + if (!CBot::WriteShort(ostr, op)) return false; - return m_currentProgram->script->WriteStack(file); + if (!m_currentProgram->script->WriteStack(ostr)) + { + GetLogger()->Error("Save state failed at program index: %i\n", op); + return false; + } + return true; } op = 0; // stop - CBot::fWrite(&op, sizeof(short), 1, file); - return true; + return CBot::WriteShort(ostr, op); } diff --git a/src/object/implementation/programmable_impl.h b/src/object/implementation/programmable_impl.h index a0b78096..38b8be7c 100644 --- a/src/object/implementation/programmable_impl.h +++ b/src/object/implementation/programmable_impl.h @@ -58,8 +58,8 @@ public: Program* GetCurrentProgram() override; void StopProgram() override; - bool ReadStack(FILE *file) override; - bool WriteStack(FILE *file) override; + bool ReadStack(std::istream &istr) override; + bool WriteStack(std::ostream &ostr) override; void TraceRecordStart() override; void TraceRecordStop() override; diff --git a/src/object/interface/programmable_object.h b/src/object/interface/programmable_object.h index 05f1a92c..1ce0d5d4 100644 --- a/src/object/interface/programmable_object.h +++ b/src/object/interface/programmable_object.h @@ -53,9 +53,9 @@ public: virtual bool IsProgram() = 0; //! Save current execution status to file - virtual bool WriteStack(FILE *file) = 0; + virtual bool WriteStack(std::ostream &ostr) = 0; //! Read current execution status from file - virtual bool ReadStack(FILE *file) = 0; + virtual bool ReadStack(std::istream &istr) = 0; //! Start recording trace virtual void TraceRecordStart() = 0; diff --git a/src/script/script.cpp b/src/script/script.cpp index a94e850f..22ae1024 100644 --- a/src/script/script.cpp +++ b/src/script/script.cpp @@ -1032,16 +1032,16 @@ bool CScript::WriteScript(const char* filename) // Reads a stack of script by execution as a file. -bool CScript::ReadStack(FILE *file) +bool CScript::ReadStack(std::istream &istr) { int nb; - CBot::fRead(&nb, sizeof(int), 1, file); - CBot::fRead(&m_ipf, sizeof(int), 1, file); - CBot::fRead(&m_errMode, sizeof(int), 1, file); + if (!CBot::ReadInt(istr, nb)) return false; + if (!CBot::ReadInt(istr, m_ipf)) return false; + if (!CBot::ReadInt(istr, m_errMode)) return false; if (m_botProg == nullptr) return false; - if ( !m_botProg->RestoreState(file) ) return false; + if (!m_botProg->RestoreState(istr)) return false; m_bRun = true; m_bContinue = false; @@ -1050,16 +1050,16 @@ bool CScript::ReadStack(FILE *file) // Writes a stack of script by execution as a file. -bool CScript::WriteStack(FILE *file) +bool CScript::WriteStack(std::ostream &ostr) { int nb; nb = 2; - CBot::fWrite(&nb, sizeof(int), 1, file); - CBot::fWrite(&m_ipf, sizeof(int), 1, file); - CBot::fWrite(&m_errMode, sizeof(int), 1, file); + if (!CBot::WriteInt(ostr, nb)) return false; + if (!CBot::WriteInt(ostr, m_ipf)) return false; + if (!CBot::WriteInt(ostr, m_errMode)) return false; - return m_botProg->SaveState(file); + return m_botProg->SaveState(ostr); } diff --git a/src/script/script.h b/src/script/script.h index f0f473fb..bc64dc23 100644 --- a/src/script/script.h +++ b/src/script/script.h @@ -88,8 +88,8 @@ public: bool SendScript(const char* text); bool ReadScript(const char* filename); bool WriteScript(const char* filename); - bool ReadStack(FILE *file); - bool WriteStack(FILE *file); + bool ReadStack(std::istream &istr); + bool WriteStack(std::ostream &ostr); bool Compare(CScript* other); void SetFilename(const std::string &filename); diff --git a/test/unit/CBot/CBot_test.cpp b/test/unit/CBot/CBot_test.cpp index cc3261b8..f16637bd 100644 --- a/test/unit/CBot/CBot_test.cpp +++ b/test/unit/CBot/CBot_test.cpp @@ -22,6 +22,9 @@ #include #include +extern bool g_cbotTestSaveState; +bool g_cbotTestSaveState = false; + using namespace CBot; class CBotUT : public testing::Test @@ -197,6 +200,23 @@ private: return ss.str(); } + static void TestSaveAndRestore(CBotProgram* program) + { + std::stringstream sstr(""); + // save + if (!program->SaveState(sstr)) + throw CBotTestFail("CBotProgram::SaveState Failed"); + + if (!CBotClass::SaveStaticState(sstr)) + throw CBotTestFail("CBotClass::SaveStaticState Failed"); + // restore + if (!program->RestoreState(sstr)) + throw CBotTestFail("CBotProgram::RestoreState Failed"); + + if (!CBotClass::RestoreStaticState(sstr)) + throw CBotTestFail("CBotClass::RestoreStaticState Failed"); + } + protected: std::unique_ptr ExecuteTest(const std::string& code, CBotError expectedError = CBotNoErr) { @@ -231,7 +251,17 @@ protected: try { program->Start(test); - while (!program->Run()); + if (g_cbotTestSaveState) + { + while (!program->Run(nullptr, 0)) // save/restore at each step + { + TestSaveAndRestore(program.get()); + } + } + else + { + while (!program->Run(nullptr, 0)); // execute in step mode + } program->GetError(error, cursor1, cursor2); if (error != expectedRuntimeError) { diff --git a/test/unit/main.cpp b/test/unit/main.cpp index 91d640de..8bed282c 100644 --- a/test/unit/main.cpp +++ b/test/unit/main.cpp @@ -21,11 +21,21 @@ #include +extern bool g_cbotTestSaveState; + int main(int argc, char* argv[]) { CLogger logger; ::testing::InitGoogleTest(&argc, argv); + // parse arguments not removed by InitGoogleTest + for (int i = 1; i < argc; ++i) + { + std::string arg(argv[i]); + if (arg == "--CBotUT_TestSaveState") + g_cbotTestSaveState = true; + } + return RUN_ALL_TESTS(); }