#include "VMScript.h"

#include <ctime>

extern bool g_swap;

// why are these big endian?

void S(UInt16 & a)
{ if(g_swap) a = Swap16(a); }

void S(UInt32 & a)
{ if(g_swap) a = Swap32(a); }

void S(SInt32 & a)
{ if(g_swap) a = Swap32(a); }

void S(UInt64 & a)
{ if(g_swap) a = Swap64(a); }

void S(float & a)
{ if(g_swap) SwapFloat(&a); }

void S(double & a)
{ if(g_swap) SwapDouble(&a); }

void S(time_t & a)
{ if(g_swap) a = Swap64(a); }

STATIC_ASSERT(sizeof(time_t) == 8);

std::string ReadString2(IDataStream * src)
{
	std::string	result;

	UInt16	len = src->Read16();
	S(len);

	result.resize(len);

	src->ReadBuf(&result[0], len);

	return result;
}

std::string ReadString4(IDataStream * src)
{
	std::string	result;

	UInt32	len = src->Read32();
	S(len);

	result.resize(len);

	src->ReadBuf(&result[0], len);

	return result;
}

// StringTable
bool StringTable::Read(IDataStream * src)
{
	UInt16	numStrings = src->Read16();
	S(numStrings);

	m_data.resize(numStrings);

	for(UInt32 i = 0; i < numStrings; i++)
	{
		m_data[i] = ReadString2(src);
	}

	return true;
}

void StringTable::Dump(void)
{
	UInt32	idx = 0;
	for(Storage::iterator iter = m_data.begin(); iter != m_data.end(); ++iter, ++idx)
		_MESSAGE("%04X: %s", idx, iter->c_str());
}

const std::string &	StringTable::Get(UInt32 idx)
{
	static std::string	sInvalidStr = "<invalid>";

	if(idx >= m_data.size())
		return sInvalidStr;

	return m_data[idx];
}

bool StringTable::HasData(UInt32 idx)
{
	if(idx >= m_data.size())
		return false;

	return !m_data[idx].empty();
}

// DebugInfo
bool DebugInfo::Read(IDataStream * src)
{
	m_flags = src->Read8();

	if(m_flags)
	{
		m_sourceModificationTime = src->Read64();
		S(m_sourceModificationTime);

		if(!Read_Functions(src))
			return false;

		if(!Read_PropertyGroups(src))
			return false;

		if(!Read_StructOrder(src))
			return false;
	}

	return true;
}

bool DebugInfo::Read_Functions(IDataStream * src)
{
	UInt16	functionCount = src->Read16();
	S(functionCount);

	m_data.resize(functionCount);

	for(UInt32 i = 0; i < functionCount; i++)
	{
		Info	* info = &m_data[i];

		info->objName = src->Read16();
		info->stateName = src->Read16();
		info->fnName = src->Read16();
		info->type = src->Read8();
		UInt16	instrCount = src->Read16();

		S(info->objName);
		S(info->stateName);
		S(info->fnName);
		S(instrCount);

		info->lineNumbers.resize(instrCount);
		if(instrCount)
			src->ReadBuf(&info->lineNumbers[0], sizeof(UInt16) * instrCount);

		for(UInt32 j = 0; j < instrCount; j++)
			S(info->lineNumbers[j]);
	}

	return true;
}

bool DebugInfo::Read_PropertyGroups(IDataStream * src)
{
	UInt16	groupCount = src->Read16();
	S(groupCount);

	m_propGroup.resize(groupCount);

	for(UInt32 i = 0; i < groupCount; i++)
	{
		PropertyGroup	* info = &m_propGroup[i];

		info->objName = src->Read16();
		info->groupName = src->Read16();
		info->groupDoc = src->Read16();
		info->userFlags = src->Read32();
		UInt16	nameCount = src->Read16();

		S(info->objName);
		S(info->groupName);
		S(info->groupDoc);
		S(info->userFlags);
		S(nameCount);

		info->names.resize(nameCount);
		if(nameCount)
			src->ReadBuf(&info->names[0], sizeof(UInt16) * nameCount);

		for(UInt32 j = 0; j < nameCount; j++)
			S(info->names[j]);
	}

	return true;
}

bool DebugInfo::Read_StructOrder(IDataStream * src)
{
	UInt16	structCount = src->Read16();
	S(structCount);

	m_structOrder.resize(structCount);

	for(UInt32 i = 0; i < structCount; i++)
	{
		StructOrder	* info = &m_structOrder[i];

		info->objName = src->Read16();
		info->orderName = src->Read16();
		UInt16	varCount = src->Read16();

		S(info->objName);
		S(info->orderName);
		S(varCount);

		info->names.resize(varCount);
		if(varCount)
			src->ReadBuf(&info->names[0], sizeof(UInt16) * varCount);

		for(UInt32 j = 0; j < varCount; j++)
			S(info->names[j]);
	}

	return true;
}

void DebugInfo::Dump(StringTable * strTable)
{
	_MESSAGE("flags: %02X", m_flags);

	char	timeStr[27];

	ctime_s(timeStr, sizeof(timeStr), &m_sourceModificationTime);
	timeStr[24] = 0;	// remove newline (format is fixed-length, no really)

	_MESSAGE("source modification time = %s", timeStr);

	UInt32	idx = 0;
	for(Storage::iterator iter = m_data.begin(); iter != m_data.end(); ++iter, ++idx)
	{
		Info	* info = &(*iter);

		_MESSAGE("fn %04X:", idx);
		gLog.Indent();

		_MESSAGE("obj: %s", strTable->Get(info->objName).c_str());
		_MESSAGE("state: %s", strTable->Get(info->stateName).c_str());
		_MESSAGE("fn: %s", strTable->Get(info->fnName).c_str());
		_MESSAGE("type: %d", info->type);
		_MESSAGE("instr count: %04X", info->GetNumInstructions());
		if(info->GetNumInstructions())
		{
			_MESSAGE("line numbers:");
			gLog.Indent();

			for(UInt32 i = 0; i < info->lineNumbers.size(); i++)
			{
				_MESSAGE("%04X: %04X", i, info->lineNumbers[i]);
			}

			gLog.Outdent();
		}

		gLog.Outdent();
	}

	idx = 0;
	for(PropertyGroupStorage::iterator iter = m_propGroup.begin(); iter != m_propGroup.end(); ++iter, ++idx)
	{
		PropertyGroup	* info = &(*iter);

		_MESSAGE("propgroup %04X:", idx);
		gLog.Indent();

		_MESSAGE("obj: %s", strTable->Get(info->objName).c_str());
		_MESSAGE("group: %s", strTable->Get(info->groupName).c_str());
		_MESSAGE("doc: %s", strTable->Get(info->groupDoc).c_str());
		_MESSAGE("userflags: %08X", info->userFlags);
		if(info->GetNumNames())
		{
			_MESSAGE("names:");
			gLog.Indent();

			for(UInt32 i = 0; i < info->names.size(); i++)
				_MESSAGE("%04X: %s", i, strTable->Get(info->names[i]).c_str());

			gLog.Outdent();
		}
		gLog.Outdent();
	}

	idx = 0;
	for(StructOrderStorage::iterator iter = m_structOrder.begin(); iter != m_structOrder.end(); ++iter, ++idx)
	{
		StructOrder	* info = &(*iter);

		_MESSAGE("structorder %04X:", idx);
		gLog.Indent();

		_MESSAGE("name: %s", strTable->Get(info->objName).c_str());
		_MESSAGE("order: %s", strTable->Get(info->orderName).c_str());
		if(info->GetNumNames())
		{
			_MESSAGE("vars:");
			gLog.Indent();

			for(UInt32 i = 0; i < info->names.size(); i++)
				_MESSAGE("%04X: %s", i, strTable->Get(info->names[i]).c_str());

			gLog.Outdent();
		}

		gLog.Outdent();
	}
}

// UserFlagTable
bool UserFlagTable::Read(IDataStream * src)
{
	UInt16	count = src->Read16();
	S(count);

	m_data.resize(count);

	for(UInt32 i = 0; i < count; i++)
	{
		Info	* info = &m_data[i];

		info->name = src->Read16();
		info->idx = src->Read8();

		S(info->name);
	}

	return true;
}

void UserFlagTable::Dump(StringTable * strTable)
{
	for(Storage::iterator iter = m_data.begin(); iter != m_data.end(); ++iter)
	{
		_MESSAGE("%02X: %s", iter->idx, strTable->Get(iter->name).c_str());
	}
}

// VarValue
bool VarValue::Read(IDataStream * src)
{
	type = src->Read8();

	switch(type)
	{
	case kType_Unk0:
		break;

	case kType_Identifier:
		data.stringRef = src->Read16();
		S(data.stringRef);
		break;

	case kType_String:
		data.stringRef2 = src->Read16();
		S(data.stringRef2);
		break;

	case kType_Integer:
		data.i = src->Read32();
		S(data.i);
		break;

	case kType_Float:
		data.f = src->ReadFloat();
		S(data.f);
		break;

	case kType_Bool:
		data.b = src->Read8() != 0;
		break;

	default:
		_ERROR("### VarValue: unhandled type %02X", type);
		break;
	}

	return true;
}

void VarValue::Dump(StringTable * strTable)
{
	switch(type)
	{
	case kType_Unk0:
		_MESSAGE("none");
		break;

	case kType_Identifier:
		_MESSAGE("id: %s", strTable->Get(data.stringRef).c_str());
		break;

	case kType_String:
		_MESSAGE("string: \"%s\"", strTable->Get(data.stringRef2).c_str());
		break;

	case kType_Integer:
		_MESSAGE("int: %d", data.i);
		break;

	case kType_Float:
		_MESSAGE("float: %f", data.f);
		break;

	case kType_Bool:
		_MESSAGE("bool: %d", data.b);
		break;

	default:
		_MESSAGE("unknown: type = %d", type);
		break;
	}
}

// FunctionCode
bool FunctionCode::IsEmpty(void)
{
	return instructions.empty();
}

const char * FunctionCode::GetOpName(UInt32 op)
{
	static const char * kOpNames[] =
	{
		"nop",					// 00
		"iadd",
		"fadd",
		"isub",
		"fsub",					// 04
		"imul",
		"fmul",
		"idiv",
		"fdiv",					// 08
		"imod",
		"not",
		"ineg",
		"fneg",					// 0C
		"assign",
		"cast",
		"cmp_eq",
		"cmp_lt",				// 10
		"cmp_le",
		"cmp_gt",
		"cmp_ge",
		"jmp",					// 14
		"jmpt",
		"jmpf",
		"callmethod",
		"callparent",			// 18
		"callstatic",
		"return",
		"strcat",
		"propget",				// 1C
		"propset",
		"array_create",
		"array_length",
		"array_getelement",		// 20
		"array_setelement",
		"array_findelement",
		"array_rfindelement",
		"is",					// 24
		"struct_create",
		"struct_get",
		"struct_set",
		"array_findstruct",		// 28
		"array_rfindstruct",
		"array_add",
		"array_insert",
		"array_removelast",		// 2C
		"array_remove",
		"array_clear",
	};

	if(op < (sizeof(kOpNames) / sizeof(kOpNames[0])))
		return kOpNames[op];
	else
		return "invalid";
}

const char * FunctionCode::GetOpArgDesc(UInt32 op)
{
	static const char * kOpNames[] =
	{
		"",			// 00
		"SII",
		"SFF",
		"SII",
		"SFF",		// 04
		"SII",
		"SFF",
		"SII",
		"SFF",		// 08
		"SII",
		"SA",
		"SI",
		"SF",		// 0C
		"SA",
		"SA",
		"SAA",
		"SAA",		// 10
		"SAA",
		"SAA",
		"SAA",
		"L",		// 14
		"AL",
		"AL",
		"NSS*",
		"NS*",		// 18
		"NNS*",
		"A",
		"SQQ",
		"NSS",		// 1C
		"NSA",
		"Su",
		"SS",
		"SSI",		// 20
		"SIA",
		"SSAI",
		"SSAI",
		"SAT",		// 24
		"S",
		"SSN",
		"SNA",
		"SSQAI",	// 28
		"SSQAI",
		"SAI",
		"SAI",
		"S",		// 2C
		"SII",
		"S",
	};

	if(op < (sizeof(kOpNames) / sizeof(kOpNames[0])))
		return kOpNames[op];
	else
		return NULL;
}

bool FunctionCode::Read(IDataStream * src, UInt16 codeLen)
{
//	_MESSAGE("code: @%016I64X %04X", src->GetOffset(), codeLen);

	instructions.resize(codeLen);

	for(UInt32 i = 0; i < codeLen; i++)
	{
		Instruction	* dst = &instructions[i];

		UInt8	opcode = src->Read8();

		dst->op = opcode;

		if(opcode >= kOp_Invalid)
		{
			_ERROR("invalid opcode %02X", opcode);
			break;
		}

		const char	* argDesc = GetOpArgDesc(opcode);
		UInt32		numArgs = strlen(argDesc);

		for(UInt32 j = 0; j < numArgs; j++)
		{
			VarValue	var;

			var.Read(src);

			dst->args.push_back(var);

			// handle var args
			// starts with an integer storing the number of args coming afterwards
			if(argDesc[j] == '*')
			{
				if(var.type != VarValue::kType_Integer)
				{
					_ERROR("varargs num var type is not integer");
					break;
				}

				UInt32	numVarArgs = var.data.i;

				for(UInt32 k = 0; k < numVarArgs; k++)
				{
					var.Read(src);

					dst->args.push_back(var);
				}
			}
		}
	}

	return true;
}

void FunctionCode::Dump(StringTable * strTable)
{
	UInt32	idx = 0;
	for(InstructionList::iterator iter = instructions.begin(); iter != instructions.end(); ++iter, ++idx)
	{
		_MESSAGE("%04X: %s", idx, GetOpName(iter->op));

		for(Instruction::VarTable::iterator instIter = iter->args.begin(); instIter != iter->args.end(); ++instIter)
		{
			instIter->Dump(strTable);
		}
	}
}

// FunctionInfo
bool FunctionInfo::ParamInfo::Read(IDataStream * src)
{
	name =	src->Read16();
	type =	src->Read16();

	S(name);
	S(type);

	return true;
}

void FunctionInfo::ParamInfo::Dump(StringTable * strTable)
{
	_MESSAGE("%s: %s", strTable->Get(type).c_str(), strTable->Get(name).c_str());
}

bool FunctionInfo::Read(IDataStream * src)
{
	valid = true;

	returnType =	src->Read16();
	docstring =		src->Read16();
	userFlags =		src->Read32();
	flags =			src->Read8();

	S(returnType);
	S(docstring);
	S(userFlags);

	// params

	UInt16	numParams = src->Read16();
	S(numParams);
	params.resize(numParams);

	for(UInt32 i = 0; i < numParams; i++)
		params[i].Read(src);

	// locals

	UInt16	numLocals = src->Read16();
	S(numLocals);
	locals.resize(numLocals);

	for(UInt32 i = 0; i < numLocals; i++)
		locals[i].Read(src);

	// code

	UInt16	codeLen = src->Read16();
	S(codeLen);

	code.Read(src, codeLen);

	return true;
}

void FunctionInfo::Dump(StringTable * strTable)
{
	_MESSAGE("return type: %s", strTable->Get(returnType).c_str());
	if(strTable->HasData(docstring))
		_MESSAGE("docstring: %s", strTable->Get(docstring).c_str());
	_MESSAGE("user flags: %08X", userFlags);
	_MESSAGE("flags: %02X", flags);

	if(!params.empty())
	{
		_MESSAGE("params:");
		gLog.Indent();

		for(ParamTable::iterator iter = params.begin(); iter != params.end(); ++iter)
			iter->Dump(strTable);

		gLog.Outdent();
	}

	if(!locals.empty())
	{
		_MESSAGE("locals:");
		gLog.Indent();

		for(ParamTable::iterator iter = locals.begin(); iter != locals.end(); ++iter)
			iter->Dump(strTable);

		gLog.Outdent();
	}

	if(!code.IsEmpty())
	{
		_MESSAGE("code");
		gLog.Indent();

		code.Dump(strTable);

		gLog.Outdent();
	}
}

void FunctionInfo::MarkInvalid(void)
{
	valid = false;
}

// ObjectTable
bool ObjectTable::Info::StateInfo::Read(IDataStream * src)
{
	name =	src->Read16();

	S(name);

	UInt16	numStates = src->Read16();
	S(numStates);
	functions.resize(numStates);

	for(UInt32 i = 0; i < numStates; i++)
	{
		StateFunction	* info = &functions[i];

		info->name = src->Read16();

		S(info->name);

		info->function.Read(src);
	}

	return true;
}

void ObjectTable::Info::StateInfo::Dump(StringTable * strTable)
{
	_MESSAGE("%s", strTable->Get(name).c_str());

	if(!functions.empty())
	{
		_MESSAGE("functions:");
		gLog.Indent();

		for(FnTable::iterator iter = functions.begin(); iter != functions.end(); ++iter)
		{
			_MESSAGE("%s", strTable->Get(iter->name).c_str());
			gLog.Indent();

			iter->function.Dump(strTable);

			gLog.Outdent();
		}

		gLog.Outdent();
	}
}

bool ObjectTable::Read(IDataStream * src)
{
	UInt16	count = src->Read16();
	S(count);

	m_data.resize(count);

	for(UInt32 i = 0; i < count; i++)
	{
		Info	* info = &m_data[i];

		info->name = src->Read16();
		UInt32	length = src->Read32();

		S(info->name);
		S(length);

		// object

		info->parentClassName =	src->Read16();
		info->docstring =		src->Read16();
		info->constFlag =		src->Read8();
		info->userFlags =		src->Read32();
		info->autoStateName =	src->Read16();

		S(info->parentClassName);
		S(info->docstring);
		S(info->userFlags);
		S(info->autoStateName);

		// structs

		UInt16	structCount = src->Read16();
		S(structCount);
		info->structs.resize(structCount);

		for(UInt32 j = 0; j < structCount; j++)
		{
			Info::StructInfo	* structInfo = &info->structs[j];

			structInfo->name =		src->Read16();
			UInt16 memberCount =	src->Read16();

			S(structInfo->name);
			S(memberCount);

			structInfo->members.resize(memberCount);
			for(UInt32 k = 0; k < memberCount; k++)
			{
				Info::StructInfo::Member	* memberInfo = &structInfo->members[k];

				memberInfo->name =		src->Read16();
				memberInfo->typeName =	src->Read16();
				memberInfo->userFlags =	src->Read32();
				memberInfo->value.Read(src);
				memberInfo->constFlag =	src->Read8();
				memberInfo->docString =	src->Read16();

				S(memberInfo->name);
				S(memberInfo->typeName);
				S(memberInfo->userFlags);
				S(memberInfo->docString);
			}
		}

		// variables

		UInt16	variableCount =	src->Read16();
		S(variableCount);
		info->variables.resize(variableCount);

		for(UInt32 j = 0; j < variableCount; j++)
		{
			Info::VarInfo	* varInfo = &info->variables[j];

			varInfo->name =			src->Read16();
			varInfo->typeName =		src->Read16();
			varInfo->userFlags =	src->Read32();
			varInfo->value.Read(src);
			varInfo->constFlag =	src->Read8();

			S(varInfo->name);
			S(varInfo->typeName);
			S(varInfo->userFlags);
		}

		// properties

		UInt16	propertyCount =	src->Read16();
		S(propertyCount);
		info->properties.resize(propertyCount);

		for(UInt32 j = 0; j < propertyCount; j++)
		{
			Info::PropInfo	* propInfo = &info->properties[j];

			propInfo->name =		src->Read16();
			propInfo->type =		src->Read16();
			propInfo->docstring =	src->Read16();
			propInfo->userFlags =	src->Read32();
			propInfo->flags =		src->Read8();

			if(propInfo->flags & Info::PropInfo::kFlags_AutoVar)
				propInfo->autoVarName =	src->Read16();
			else
				propInfo->autoVarName = 0;

			S(propInfo->name);
			S(propInfo->type);
			S(propInfo->docstring);
			S(propInfo->userFlags);
			S(propInfo->autoVarName);

			if(!(propInfo->flags & Info::PropInfo::kFlags_AutoVar))
			{
				if(propInfo->flags & Info::PropInfo::kFlags_Read)
					propInfo->readHandler.Read(src);
				else
					propInfo->readHandler.MarkInvalid();

				if(propInfo->flags & Info::PropInfo::kFlags_Write)
					propInfo->writeHandler.Read(src);
				else
					propInfo->writeHandler.MarkInvalid();
			}
		}

		// states

		UInt16	stateCount = src->Read16();
		S(stateCount);
		info->states.resize(stateCount);

		for(UInt32 j = 0; j < stateCount; j++)
			info->states[j].Read(src);
	}

	return true;
}

void ObjectTable::Dump(StringTable * strTable)
{
	for(Storage::iterator iter = m_data.begin(); iter != m_data.end(); ++iter)
	{
		_MESSAGE("%s:", strTable->Get(iter->name).c_str());
		gLog.Indent();

		_MESSAGE("parent class: %s", strTable->Get(iter->parentClassName).c_str());
		if(strTable->HasData(iter->docstring))
			_MESSAGE("docstring: %s", strTable->Get(iter->docstring).c_str());
		_MESSAGE("user flags: %08X", iter->userFlags);
		if(strTable->HasData(iter->autoStateName))
			_MESSAGE("auto state: %s", strTable->Get(iter->autoStateName).c_str());

		if(!iter->structs.empty())
		{
			_MESSAGE("structs:");
			gLog.Indent();

			for(Info::StructTable::iterator structIter = iter->structs.begin(); structIter != iter->structs.end(); ++structIter)
			{
				_MESSAGE("name: %s", strTable->Get(structIter->name).c_str());

				for(Info::StructInfo::MemberTable::iterator memberIter = structIter->members.begin(); memberIter != structIter->members.end(); ++memberIter)
				{
					_MESSAGE("name: %s", strTable->Get(memberIter->name).c_str());
					_MESSAGE("type: %s", strTable->Get(memberIter->typeName).c_str());
					_MESSAGE("user flags: %08X", memberIter->userFlags);
					memberIter->value.Dump(strTable);
					if(memberIter->constFlag) _MESSAGE("const");
					_MESSAGE("doc: %s", strTable->Get(memberIter->docString).c_str());
				}
			}

			gLog.Outdent();
		}

		if(!iter->variables.empty())
		{
			_MESSAGE("variables:");
			gLog.Indent();

			for(Info::VarTable::iterator varIter = iter->variables.begin(); varIter != iter->variables.end(); ++varIter)
			{
				_MESSAGE("name: %s", strTable->Get(varIter->name).c_str());
				_MESSAGE("type: %s", strTable->Get(varIter->typeName).c_str());
				_MESSAGE("user flags: %08X", varIter->userFlags);
				varIter->value.Dump(strTable);
				if(varIter->constFlag) _MESSAGE("const");
			}

			gLog.Outdent();
		}

		if(!iter->properties.empty())
		{
			_MESSAGE("properties:");
			gLog.Indent();

			for(Info::PropTable::iterator propIter = iter->properties.begin(); propIter != iter->properties.end(); ++propIter)
			{
				_MESSAGE("name: %s", strTable->Get(propIter->name).c_str());
				_MESSAGE("type: %s", strTable->Get(propIter->type).c_str());
				if(strTable->HasData(propIter->docstring))
					_MESSAGE("docstring: %s", strTable->Get(propIter->docstring).c_str());
				_MESSAGE("user flags: %08X", propIter->userFlags);
				_MESSAGE("flags: %02X", propIter->flags);

				if(propIter->flags & Info::PropInfo::kFlags_AutoVar)
					_MESSAGE("auto var name: %s", strTable->Get(propIter->autoVarName).c_str());
				else
				{
					if(propIter->flags & Info::PropInfo::kFlags_Read)
					{
						_MESSAGE("read handler:");
						gLog.Indent();

						propIter->readHandler.Dump(strTable);

						gLog.Outdent();
					}

					if(propIter->flags & Info::PropInfo::kFlags_Write)
					{
						_MESSAGE("write handler:");
						gLog.Indent();

						propIter->writeHandler.Dump(strTable);

						gLog.Outdent();
					}
				}
			}

			gLog.Outdent();
		}

		if(!iter->states.empty())
		{
			_MESSAGE("states:");
			gLog.Indent();

			for(Info::StateTable::iterator stateIter = iter->states.begin(); stateIter != iter->states.end(); ++stateIter)
				stateIter->Dump(strTable);

			gLog.Outdent();
		}

		gLog.Outdent();
	}
}

// ScriptHeader
void ScriptHeader::Swap(void)
{
	signature = Swap32(signature);
	gameID = Swap16(gameID);
	buildTime = Swap64(buildTime);
}

void ScriptHeader::Dump(void)
{
	char	buildTimeStr[27];

	ctime_s(buildTimeStr, sizeof(buildTimeStr), &buildTime);
	buildTimeStr[24] = 0;	// remove newline (format is fixed-length, no really)

	_MESSAGE("signature: %08X", signature);
	_MESSAGE("version: %02X.%02X", verMajor, verMinor);
	_MESSAGE("game id: %04X", gameID);
	_MESSAGE("build time: %016I64X (%s)", buildTime, buildTimeStr);
}

bool ScriptHeader::Validate(void)
{
	return
		(signature == kSignature) &&
		(verMajor == kVerMajor) &&
		(verMinor == kVerMinor) &&
		(gameID == kGameID_Fallout4);
}

// VMScript
bool VMScript::Read(IDataStream * src)
{
	// header
	src->ReadBuf(&header, sizeof(header));
	if(g_swap) header.Swap();
	if(!header.Validate())
	{
		_ERROR("bad header");
		return false;
	}

	source = ReadString2(src);
	user = ReadString2(src);
	machine = ReadString2(src);

	// strings
	if(!stringTable.Read(src))
	{
		_ERROR("bad string table");
		return false;
	}

	// debug
	if(!debugInfo.Read(src))
	{
		_ERROR("bad debug table");
		return false;
	}

	// user flags
	if(!userFlagTable.Read(src))
	{
		_ERROR("bad user flag table");
		return false;
	}

	// objects
	if(!objectTable.Read(src))
	{
		_ERROR("bad object table");
		return false;
	}

	return true;
}

void VMScript::Dump(void)
{
	// header
	header.Dump();

	_MESSAGE("source = %s", source.c_str());
	_MESSAGE("user = %s", user.c_str());
	_MESSAGE("machine = %s", machine.c_str());

	// strings
#if 1
	_MESSAGE("string table:");
	gLog.Indent();
	stringTable.Dump();
	gLog.Outdent();
#endif

	// debug
#if 1
	_MESSAGE("debug table:");
	gLog.Indent();
	debugInfo.Dump(&stringTable);
	gLog.Outdent();
#endif

	// user flags
	_MESSAGE("user flag table:");
	gLog.Indent();
	userFlagTable.Dump(&stringTable);
	gLog.Outdent();

	// objects
	_MESSAGE("object table:");
	gLog.Indent();
	objectTable.Dump(&stringTable);
	gLog.Outdent();
}
