364 lines
8.4 KiB
C++
364 lines
8.4 KiB
C++
/*
|
|
* This file is part of the Colobot: Gold Edition source code
|
|
* Copyright (C) 2001-2021, Daniel Roux, EPSITEC SA & TerranovaTeam
|
|
* http://epsitec.ch; http://colobot.info; http://github.com/colobot
|
|
*
|
|
* 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 3 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, see http://gnu.org/licenses
|
|
*/
|
|
|
|
#include "CBot/CBotFileUtils.h"
|
|
|
|
#include "CBot/CBotClass.h"
|
|
#include "CBot/CBotEnums.h"
|
|
|
|
namespace CBot
|
|
{
|
|
|
|
template<typename T>
|
|
static bool WriteBinary(std::ostream &ostr, T value, unsigned padTo = 0)
|
|
{
|
|
unsigned char chr;
|
|
unsigned count = 1;
|
|
while (value > 127) // unsigned LEB128
|
|
{
|
|
++count;
|
|
chr = (value & 0x7F) | 0x80;
|
|
if (!ostr.write(reinterpret_cast<char*>(&chr), 1)) return false;
|
|
value >>= 7;
|
|
}
|
|
chr = value & 0x7F;
|
|
if (count < padTo) chr |= 0x80;
|
|
if (!ostr.write(reinterpret_cast<char*>(&chr), 1)) return false;
|
|
|
|
if (count < padTo)
|
|
{
|
|
while (++count < padTo)
|
|
if (!(ostr << '\x80')) return false;
|
|
if (!(ostr << '\x00')) return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
template<typename T>
|
|
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<char*>(&chr), 1)) return false;
|
|
if (shift < sizeof(T) * 8 - 1)
|
|
value |= static_cast<T>(chr & 0x7F) << shift;
|
|
shift += 7;
|
|
if ((chr & 0x80) == 0) break;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
template<typename T>
|
|
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<char*>(&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<typename T>
|
|
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<char*>(&chr), 1)) return false;
|
|
if (shift < sizeof(T) * 8 - 1)
|
|
value |= (static_cast<T>(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<T>(-1) << shift;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool WriteWord(std::ostream &ostr, unsigned short w)
|
|
{
|
|
return WriteBinary<unsigned short>(ostr, w);
|
|
}
|
|
|
|
bool ReadWord(std::istream &istr, unsigned short &w)
|
|
{
|
|
return ReadBinary<unsigned short>(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<short>(ostr, s);
|
|
}
|
|
|
|
bool ReadShort(std::istream &istr, short &s)
|
|
{
|
|
return ReadSignedBinary<short>(istr, s);
|
|
}
|
|
|
|
bool WriteUInt32(std::ostream &ostr, uint32_t i)
|
|
{
|
|
return WriteBinary<uint32_t>(ostr, i);
|
|
}
|
|
|
|
bool ReadUInt32(std::istream &istr, uint32_t &i)
|
|
{
|
|
return ReadBinary<uint32_t>(istr, i);
|
|
}
|
|
|
|
bool WriteInt(std::ostream &ostr, int i)
|
|
{
|
|
return WriteSignedBinary<int>(ostr, i);
|
|
}
|
|
|
|
bool ReadInt(std::istream &istr, int &i)
|
|
{
|
|
return ReadSignedBinary<int>(istr, i);
|
|
}
|
|
|
|
bool WriteLong(std::ostream &ostr, long l, unsigned padTo)
|
|
{
|
|
return WriteSignedBinary<long>(ostr, l, padTo);
|
|
}
|
|
|
|
bool ReadLong(std::istream &istr, long &l)
|
|
{
|
|
return ReadSignedBinary<long>(istr, l);
|
|
}
|
|
|
|
bool WriteFloat(std::ostream &ostr, float f)
|
|
{
|
|
union TypeConverter
|
|
{
|
|
float fValue;
|
|
unsigned int iValue;
|
|
};
|
|
|
|
TypeConverter u;
|
|
u.fValue = 0.0f;
|
|
u.iValue = 0;
|
|
|
|
u.fValue = f;
|
|
return WriteBinary<unsigned int>(ostr, u.iValue);
|
|
}
|
|
|
|
bool ReadFloat(std::istream &istr, float &f)
|
|
{
|
|
union TypeConverter
|
|
{
|
|
float fValue;
|
|
unsigned int iValue;
|
|
};
|
|
|
|
TypeConverter u;
|
|
u.fValue = 0.0f;
|
|
u.iValue = 0;
|
|
|
|
if (!ReadBinary<unsigned int>(istr, u.iValue)) return false;
|
|
f = u.fValue;
|
|
return true;
|
|
}
|
|
|
|
bool WriteDouble(std::ostream &ostr, double d)
|
|
{
|
|
union TypeConverter
|
|
{
|
|
double dValue;
|
|
unsigned long iValue;
|
|
};
|
|
|
|
TypeConverter u;
|
|
u.dValue = 0.0;
|
|
u.iValue = 0;
|
|
|
|
u.dValue = d;
|
|
return WriteBinary<unsigned long>(ostr, u.iValue);
|
|
}
|
|
|
|
bool ReadDouble(std::istream &istr, double &d)
|
|
{
|
|
union TypeConverter
|
|
{
|
|
double dValue;
|
|
unsigned long iValue;
|
|
};
|
|
|
|
TypeConverter u;
|
|
u.dValue = 0.0;
|
|
u.iValue = 0;
|
|
|
|
if (!ReadBinary<unsigned long>(istr, u.iValue)) return false;
|
|
d = u.dValue;
|
|
return true;
|
|
}
|
|
|
|
bool WriteString(std::ostream &ostr, const std::string &s)
|
|
{
|
|
if (!WriteBinary<size_t>(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<size_t>(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(istr, w)) return false;
|
|
type.SetType(w);
|
|
|
|
if ( type.Eq( CBotTypIntrinsic ) )
|
|
{
|
|
type = CBotTypResult( w, "point" );
|
|
}
|
|
|
|
if ( type.Eq( CBotTypClass ) )
|
|
{
|
|
std::string s;
|
|
if (!ReadString(istr, s)) return false;
|
|
type = CBotTypResult( w, s );
|
|
}
|
|
|
|
if ( type.Eq( CBotTypArrayPointer ) ||
|
|
type.Eq( CBotTypArrayBody ) )
|
|
{
|
|
CBotTypResult r;
|
|
if (!ReadWord(istr, ww)) return false;
|
|
if (!ReadType(istr, r)) return false;
|
|
type = CBotTypResult( w, r );
|
|
type.SetLimite(static_cast<short>(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;
|
|
}
|
|
|
|
} // namespace CBot
|