📄 wmlstream.cpp
字号:
// Magic Software, Inc.
// http://www.magic-software.com
// http://www.wild-magic.com
// Copyright (c) 2003. All Rights Reserved
//
// The Wild Magic Library (WML) source code is supplied under the terms of
// the license agreement http://www.magic-software.com/License/WildMagic.pdf
// and may not be copied or disclosed except in accordance with the terms of
// that agreement.
#include "WmlObject.h"
#include "WmlStream.h"
using namespace Wml;
using namespace std;
const char Stream::ms_acTopLevel[] = "Top Level";
//----------------------------------------------------------------------------
Stream::Stream ()
{
m_iBufferSize = 0;
m_iBufferNext = 0;
m_acBuffer = NULL;
}
//----------------------------------------------------------------------------
Stream::~Stream ()
{
RemoveAll();
}
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// top-level object management
//----------------------------------------------------------------------------
bool Stream::Insert (Object* pkObject)
{
if ( pkObject )
{
// An object can only be inserted once.
for (int i = 0; i < (int)m_apkTopLevel.size(); i++)
{
if ( pkObject == m_apkTopLevel[i] )
return false;
}
m_apkTopLevel.push_back(pkObject);
pkObject->IncrementReferences();
return true;
}
return false;
}
//----------------------------------------------------------------------------
bool Stream::Remove (Object* pkObject)
{
if ( pkObject )
{
vector<Object*>::iterator pkIter = m_apkTopLevel.begin();
while ( pkIter != m_apkTopLevel.end() )
{
if ( pkObject == *pkIter )
{
m_apkTopLevel.erase(pkIter);
pkObject->DecrementReferences();
return true;
}
pkIter++;
}
}
return false;
}
//----------------------------------------------------------------------------
void Stream::RemoveAll ()
{
for (int i = 0; i < (int)m_apkTopLevel.size(); i++)
m_apkTopLevel[i]->DecrementReferences();
m_apkTopLevel.clear();
}
//----------------------------------------------------------------------------
int Stream::GetObjectCount ()
{
return (int)m_apkTopLevel.size();
}
//----------------------------------------------------------------------------
Object* Stream::GetObjectAt (int i) const
{
if ( i < (int)m_apkTopLevel.size() )
return m_apkTopLevel[i];
else
return NULL;
}
//----------------------------------------------------------------------------
bool Stream::IsTopLevel (Object* pkObject)
{
for (int i = 0; i < (int)m_apkTopLevel.size(); i++)
{
if ( pkObject == m_apkTopLevel[i] )
return true;
}
return false;
}
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// memory streaming
//----------------------------------------------------------------------------
bool Stream::Load (char* acBuffer, int iSize, int iNext)
{
assert( m_kMap.empty() && m_kOrdered.empty() );
// clear out all previous top level objects
RemoveAll();
// Memory blocks are always current version. The file loader calls
// Stream::Load, but with iNext = Version::LENGTH > 0, so iNext == 0
// means this is a memory block load. This means the caller of
// Stream::Load for a memory block should *not* set iNext.
if ( iNext == 0 )
m_kVersion = Version(Version::MAJOR,Version::MINOR);
// initialize the internal Stream buffer
m_iBufferSize = iSize;
m_iBufferNext = iNext;
m_acBuffer = acBuffer;
// load list of unique objects
Object* pkObject;
while ( m_iBufferNext < m_iBufferSize )
{
// read "Top Level" or RTTI name
char* acString;
Read(acString);
bool bTopLevel = ( strcmp(acString,ms_acTopLevel) == 0 );
if ( bTopLevel )
{
// read RTTI name
delete[] acString;
Read(acString);
}
// get the factory function for the type of object about to be read
map<string,FactoryFunction>::iterator kFIter =
Object::ms_pkFactory->find(string(acString));
assert( kFIter != Object::ms_pkFactory->end() );
if ( kFIter == Object::ms_pkFactory->end() )
{
// cannot find factory function
delete[] acString;
return false;
}
// load the object
FactoryFunction oFactory = kFIter->second;
pkObject = oFactory(*this);
// keep track of all top level objects for application use
if ( bTopLevel )
Insert(pkObject);
delete[] acString;
}
// link the objects
Link* pkLink;
map<Object*,void*>::iterator kOIter;
for (kOIter = m_kMap.begin(); kOIter != m_kMap.end(); kOIter++)
{
pkLink = (Link*)kOIter->second;
pkObject = pkLink->GetObject();
pkObject->Link(*this,pkLink);
}
// delete the stream link records
for (kOIter = m_kMap.begin(); kOIter != m_kMap.end(); kOIter++)
{
pkLink = (Link*)kOIter->second;
delete pkLink;
}
m_kMap.clear();
m_kOrdered.clear();
return true;
}
//----------------------------------------------------------------------------
bool Stream::Save (char*& racBuffer, int& riSize)
{
assert( m_kMap.empty() && m_kOrdered.empty() );
// build list of unique objects
int i;
for (i = 0; i < (int)m_apkTopLevel.size(); i++)
{
assert( m_apkTopLevel[i] );
m_apkTopLevel[i]->Register(*this);
}
// accumulate object bytes used
m_iBufferSize = ((int)m_apkTopLevel.size()) *
StreamBytesString(ms_acTopLevel);
for (i = 0; i < (int)m_kOrdered.size(); i++)
m_iBufferSize += m_kOrdered[i]->GetDiskUsed();
m_acBuffer = new char[m_iBufferSize];
m_iBufferNext = 0;
// save list of unique objects
for (i = 0; i < (int)m_kOrdered.size(); i++)
{
Object* pkObject = m_kOrdered[i];
if ( IsTopLevel(pkObject) )
Write(ms_acTopLevel);
pkObject->Save(*this);
}
// reduce memory, prepare for other calls
m_kMap.clear();
m_kOrdered.clear();
// transfer ownership of buffer to caller
racBuffer = m_acBuffer;
riSize = m_iBufferSize;
m_acBuffer = NULL;
m_iBufferSize = 0;
return true;
}
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// file streaming
//----------------------------------------------------------------------------
bool Stream::Load (const char* acFilename)
{
struct stat kStat;
int iResult = stat(acFilename,&kStat);
if ( iResult != 0 || kStat.st_size < Version::LENGTH )
{
// file does not exist or not large enough to store version string
return false;
}
FILE* pkFile = fopen(acFilename,"rb");
assert( pkFile );
int iSize = kStat.st_size;
char* acBuffer = new char[iSize];
int iRead = (int)fread(acBuffer,sizeof(char),iSize,pkFile);
assert( iRead == iSize );
// get the file version
m_kVersion = Version(acBuffer);
if ( !m_kVersion.IsValid() )
{
delete[] acBuffer;
fclose(pkFile);
return false;
}
Load(acBuffer,iSize,Version::LENGTH);
delete[] acBuffer;
fclose(pkFile);
return true;
}
//----------------------------------------------------------------------------
bool Stream::Save (const char* acFilename)
{
FILE* pkFile = fopen(acFilename,"wb");
if ( !pkFile )
return false;
int iSize;
char* acBuffer;
Save(acBuffer,iSize);
// set the file version
int iWrite = (int)fwrite(Version::CURRENT,sizeof(char),Version::LENGTH,
pkFile);
assert( iWrite == Version::LENGTH );
// write the scene graph
iWrite = (int)fwrite(acBuffer,sizeof(char),iSize,pkFile);
assert( iWrite == iSize );
delete[] acBuffer;
fclose(pkFile);
return true;
}
//----------------------------------------------------------------------------
bool Stream::SaveText (const char* acFilename, int iTabSize)
{
int iCQuantity = GetObjectCount();
StringTree kRoot(1,0,iCQuantity,0);
kRoot.SetString(0,MakeString(acFilename));
int iSlot = 0;
for (int i = 0; i < iCQuantity; i++)
{
Object* pkObject = m_apkTopLevel[i];
assert( pkObject );
kRoot.SetChild(iSlot++,pkObject->SaveStrings());
}
return kRoot.Save(acFilename,iTabSize);
}
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// support functions
//----------------------------------------------------------------------------
bool Stream::InsertInMap (Object* pkKey, void* pvValue)
{
pair<map<Object*,void*>::iterator,bool> kIter = m_kMap.insert(
make_pair(pkKey,pvValue));
return kIter.second;
}
//----------------------------------------------------------------------------
Object* Stream::GetFromMap (Object* pkLinkID)
{
map<Object*,void*>::iterator kOIter = m_kMap.find(pkLinkID);
if ( kOIter != m_kMap.end() )
{
Link* pkLink = (Link*) kOIter->second;
return pkLink->GetObject();
}
else
{
return NULL;
}
}
//----------------------------------------------------------------------------
void Stream::InsertInOrdered (Object* pkObject)
{
m_kOrdered.push_back(pkObject);
}
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// memory and disk usage
//----------------------------------------------------------------------------
int Stream::GetMemoryUsed ()
{
// build list of unique objects
int i;
for (i = 0; i < (int)m_apkTopLevel.size(); i++)
{
if ( m_apkTopLevel[i] )
m_apkTopLevel[i]->Register(*this);
}
// accumulate object bytes used
int iSize = 0;
for (i = 0; i < (int)m_kOrdered.size(); i++)
iSize += m_kOrdered[i]->GetMemoryUsed();
// clear out the structures (reduce memory, prepare for other calls)
m_kMap.clear();
m_kOrdered.clear();
return iSize;
}
//----------------------------------------------------------------------------
int Stream::GetDiskUsed ()
{
// build list of unique objects
int i;
for (i = 0; i < (int)m_apkTopLevel.size(); i++)
{
if ( m_apkTopLevel[i] )
m_apkTopLevel[i]->Register(*this);
}
// accumulate object bytes used
int iSize = 0;
for (i = 0; i < (int)m_kOrdered.size(); i++)
iSize += m_kOrdered[i]->GetDiskUsed();
// clear out the structures (reduce memory, prepare for other calls)
m_kMap.clear();
m_kOrdered.clear();
return iSize;
}
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// string read/write
//----------------------------------------------------------------------------
void Stream::Read (char*& racString)
{
int iLength;
StreamRead(*this,iLength);
if ( iLength > 0 )
{
assert( m_iBufferNext + iLength <= m_iBufferSize );
racString = new char[iLength+1];
char* acSrc = m_acBuffer + m_iBufferNext;
memcpy(racString,acSrc,iLength);
m_iBufferNext += iLength;
racString[iLength] = 0;
}
else
{
racString = NULL;
}
}
//----------------------------------------------------------------------------
void Stream::Write (const char* acString)
{
int iLength = ( acString ? (int)strlen(acString) : 0 );
StreamWrite(*this,iLength);
if ( iLength > 0 )
{
assert( m_iBufferNext + iLength <= m_iBufferSize );
char* acDest = m_acBuffer + m_iBufferNext;
memcpy(acDest,acString,iLength);
m_iBufferNext += iLength;
}
}
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Stream::Link member functions
//----------------------------------------------------------------------------
Stream::Link::Link (Object* pkObject)
{
m_pkObject = pkObject;
m_iCurrent = 0;
}
//----------------------------------------------------------------------------
void Stream::Link::Add (Object* pkLinkID)
{
m_akLinkID.push_back(pkLinkID);
}
//----------------------------------------------------------------------------
void Stream::Link::SetObject (Object* pkObject)
{
m_pkObject = pkObject;
}
//----------------------------------------------------------------------------
Object* Stream::Link::GetObject ()
{
return m_pkObject;
}
//----------------------------------------------------------------------------
int Stream::Link::GetQuantity () const
{
return (int)m_akLinkID.size();
}
//----------------------------------------------------------------------------
Object* Stream::Link::GetLinkID ()
{
assert( m_iCurrent < (int)m_akLinkID.size() );
return m_akLinkID[m_iCurrent++];
}
//----------------------------------------------------------------------------
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -