You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
565 lines
13 KiB
565 lines
13 KiB
4 years ago
|
/*==LICENSE==*
|
||
|
|
||
|
CyanWorlds.com Engine - MMOG client, server and tools
|
||
|
Copyright (C) 2011 Cyan Worlds, Inc.
|
||
|
|
||
|
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://www.gnu.org/licenses/>.
|
||
|
|
||
|
Additional permissions under GNU GPL version 3 section 7
|
||
|
|
||
|
If you modify this Program, or any covered work, by linking or
|
||
|
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
|
||
|
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
|
||
|
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
|
||
|
(or a modified version of those libraries),
|
||
|
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
|
||
|
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
|
||
|
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
|
||
|
licensors of this Program grant you additional
|
||
|
permission to convey the resulting work. Corresponding Source for a
|
||
|
non-source form of such a combination shall include the source code for
|
||
|
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
|
||
|
work.
|
||
|
|
||
|
You can contact Cyan Worlds, Inc. by email legal@cyan.com
|
||
|
or by snail mail at:
|
||
|
Cyan Worlds, Inc.
|
||
|
14617 N Newport Hwy
|
||
|
Mead, WA 99021
|
||
|
|
||
|
*==LICENSE==*/
|
||
|
|
||
|
#include "hsStream.h"
|
||
|
#include "plAgeDescription.h"
|
||
|
#include "hsUtils.h"
|
||
|
#include "hsStlUtils.h"
|
||
|
#include "../plFile/hsFiles.h"
|
||
|
#include "../plFile/plInitFileReader.h"
|
||
|
#include "../plFile/plEncryptedStream.h"
|
||
|
#include "hsStringTokenizer.h"
|
||
|
#include <functional>
|
||
|
#include <algorithm>
|
||
|
|
||
|
|
||
|
const UInt32 plAgePage::kInvalidSeqSuffix = (UInt32)-1;
|
||
|
|
||
|
plAgePage::plAgePage( const char *name, UInt32 seqSuffix, Byte flags )
|
||
|
{
|
||
|
fName = name != nil ? hsStrcpy( name ) : nil;
|
||
|
fSeqSuffix = seqSuffix;
|
||
|
fFlags = flags;
|
||
|
}
|
||
|
|
||
|
plAgePage::plAgePage( char *stringFrom ) : fName(nil)
|
||
|
{
|
||
|
SetFromString( stringFrom );
|
||
|
}
|
||
|
|
||
|
plAgePage::plAgePage()
|
||
|
{
|
||
|
fName = nil;
|
||
|
fFlags = 0;
|
||
|
fSeqSuffix = 0;
|
||
|
}
|
||
|
|
||
|
plAgePage::plAgePage( const plAgePage &src ) : fName(nil)
|
||
|
{
|
||
|
fName = src.fName != nil ? hsStrcpy( src.fName ) : nil;
|
||
|
fSeqSuffix = src.fSeqSuffix;
|
||
|
fFlags = src.fFlags;
|
||
|
}
|
||
|
|
||
|
plAgePage::~plAgePage()
|
||
|
{
|
||
|
delete [] fName;
|
||
|
}
|
||
|
|
||
|
plAgePage &plAgePage::operator=( const plAgePage &src )
|
||
|
{
|
||
|
delete [] fName;
|
||
|
fName = src.fName != nil ? hsStrcpy( src.fName ) : nil;
|
||
|
fSeqSuffix = src.fSeqSuffix;
|
||
|
fFlags = src.fFlags;
|
||
|
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
void plAgePage::SetFlags(Byte f, bool on)
|
||
|
{
|
||
|
if (on)
|
||
|
hsSetBits(fFlags, f);
|
||
|
else
|
||
|
hsClearBits(fFlags, f);
|
||
|
}
|
||
|
|
||
|
// now preservs original string
|
||
|
hsBool plAgePage::SetFromString( const char *stringIn )
|
||
|
{
|
||
|
char *c, seps[] = ", \n";
|
||
|
std::string string = stringIn;
|
||
|
|
||
|
// Parse. Format is going to be "pageName[,seqSuffix[,flags]]"
|
||
|
c = strtok( (char*)string.c_str(), seps );
|
||
|
if( c == nil )
|
||
|
return false;
|
||
|
|
||
|
delete [] fName;
|
||
|
fName = hsStrcpy( c );
|
||
|
|
||
|
// Look for seqSuffix
|
||
|
c = strtok( nil, seps );
|
||
|
if( c != nil )
|
||
|
{
|
||
|
fSeqSuffix = atoi( c );
|
||
|
|
||
|
// Look for flags
|
||
|
c = strtok( nil, seps );
|
||
|
if( c != nil )
|
||
|
{
|
||
|
fFlags = atoi( c );
|
||
|
}
|
||
|
else
|
||
|
fFlags = 0;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
fSeqSuffix = kInvalidSeqSuffix;
|
||
|
fFlags = 0;
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
char *plAgePage::GetAsString( void ) const
|
||
|
{
|
||
|
static char str[ 256 ];
|
||
|
|
||
|
|
||
|
// Format is "pageName[,seqSuffix[,flags]]"
|
||
|
if( fFlags != 0 )
|
||
|
sprintf( str, "%s,%d,%d", fName, fSeqSuffix, fFlags );
|
||
|
else
|
||
|
sprintf( str, "%s,%d", fName, fSeqSuffix );
|
||
|
return str;
|
||
|
}
|
||
|
|
||
|
|
||
|
//
|
||
|
// plAgeDescription
|
||
|
//
|
||
|
//
|
||
|
//
|
||
|
|
||
|
// static
|
||
|
char plAgeDescription::kAgeDescPath[]={"dat"PATH_SEPARATOR_STR};
|
||
|
char *plAgeDescription::fCommonPages[] = { "Textures", "BuiltIn" };
|
||
|
|
||
|
// Also gotta init the separators for our helper reading function
|
||
|
plAgeDescription::plAgeDescription() : plInitSectionTokenReader()
|
||
|
{
|
||
|
IInit();
|
||
|
}
|
||
|
|
||
|
|
||
|
plAgeDescription::~plAgeDescription()
|
||
|
{
|
||
|
IDeInit();
|
||
|
}
|
||
|
|
||
|
void plAgeDescription::IDeInit()
|
||
|
{
|
||
|
ClearPageList();
|
||
|
delete [] fName;
|
||
|
}
|
||
|
|
||
|
plAgeDescription::plAgeDescription( const char *fileNameToReadFrom ) : plInitSectionTokenReader()
|
||
|
{
|
||
|
ReadFromFile(fileNameToReadFrom);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Reads from a file, returns false if failed.
|
||
|
//
|
||
|
bool plAgeDescription::ReadFromFile( const char *fileNameToReadFrom )
|
||
|
{
|
||
|
IInit();
|
||
|
|
||
|
hsStream* stream = plEncryptedStream::OpenEncryptedFile(fileNameToReadFrom);
|
||
|
if( !stream )
|
||
|
return false;
|
||
|
|
||
|
Read( stream );
|
||
|
stream->Close();
|
||
|
delete stream;
|
||
|
|
||
|
SetAgeNameFromPath( fileNameToReadFrom );
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
void plAgeDescription::SetAgeNameFromPath( const char *path )
|
||
|
{
|
||
|
delete [] fName;
|
||
|
|
||
|
if( path == nil )
|
||
|
{
|
||
|
fName = nil;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// Construct our name from the path
|
||
|
const char *pathSep1 = strrchr( path, '\\' );
|
||
|
const char *pathSep2 = strrchr( path, '/' );
|
||
|
if( pathSep2 > pathSep1 )
|
||
|
pathSep1 = pathSep2;
|
||
|
if( pathSep1 == nil )
|
||
|
pathSep1 = path;
|
||
|
else
|
||
|
pathSep1++; // Get past the actual character we found
|
||
|
|
||
|
char temp[ 512 ];
|
||
|
strcpy( temp, pathSep1 );
|
||
|
char *end = strrchr( temp, '.' );
|
||
|
if( end != nil )
|
||
|
*end = 0;
|
||
|
|
||
|
fName = hsStrcpy( temp );
|
||
|
}
|
||
|
|
||
|
void plAgeDescription::IInit( void )
|
||
|
{
|
||
|
fName = nil;
|
||
|
fDayLength = 24.0f;
|
||
|
fMaxCapacity = -1;
|
||
|
fLingerTime = 180; // seconds
|
||
|
fSeqPrefix = 0;
|
||
|
fReleaseVersion = 0;
|
||
|
fStart.SetMode( plUnifiedTime::kLocal );
|
||
|
|
||
|
fPageIterator = -1;
|
||
|
}
|
||
|
|
||
|
struct SzDelete { void operator()(char * str) { delete [] str;} };
|
||
|
void plAgeDescription::ClearPageList()
|
||
|
{
|
||
|
fPages.Reset();
|
||
|
}
|
||
|
|
||
|
|
||
|
void plAgeDescription::AppendPage( const char *name, int seqSuffix, Byte flags )
|
||
|
{
|
||
|
fPages.Append( plAgePage( name, ( seqSuffix == -1 ) ? fPages.GetCount() : (UInt32)seqSuffix, flags ) );
|
||
|
}
|
||
|
|
||
|
void plAgeDescription::SeekFirstPage( void )
|
||
|
{
|
||
|
fPageIterator = 0;
|
||
|
}
|
||
|
|
||
|
plAgePage *plAgeDescription::GetNextPage( void )
|
||
|
{
|
||
|
plAgePage *ret = nil;
|
||
|
|
||
|
|
||
|
if( fPageIterator >= 0 && fPageIterator < fPages.GetCount() )
|
||
|
{
|
||
|
ret = &fPages[ fPageIterator++ ];
|
||
|
if( fPageIterator >= fPages.GetCount() )
|
||
|
fPageIterator = -1;
|
||
|
}
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
void plAgeDescription::RemovePage( const char *page )
|
||
|
{
|
||
|
int i;
|
||
|
|
||
|
for( i = 0; i < fPages.GetCount(); i++ )
|
||
|
{
|
||
|
if( strcmp( page, fPages[ i ].GetName() ) == 0 )
|
||
|
{
|
||
|
fPages.Remove( i );
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
plAgePage *plAgeDescription::FindPage( const char *name ) const
|
||
|
{
|
||
|
int i;
|
||
|
|
||
|
|
||
|
for( i = 0; i < fPages.GetCount(); i++ )
|
||
|
{
|
||
|
if( strcmp( name, fPages[ i ].GetName() ) == 0 )
|
||
|
return &fPages[ i ];
|
||
|
}
|
||
|
|
||
|
return nil;
|
||
|
}
|
||
|
|
||
|
plLocation plAgeDescription::CalcPageLocation( const char *page ) const
|
||
|
{
|
||
|
plAgePage *ap = FindPage( page );
|
||
|
if( ap != nil )
|
||
|
{
|
||
|
// Combine our sequence # together
|
||
|
Int32 combined;
|
||
|
hsAssert(abs(fSeqPrefix) < 0xFF, "Age sequence prefex is out of range!"); // sequence prefix can NOT be larger or equal to 1-byte max value
|
||
|
UInt32 suffix = ap->GetSeqSuffix();
|
||
|
hsAssert(suffix <= 0xFFFF, "Page sequence number is out of range!"); // page sequence number can NOT be larger then 2-byte max value
|
||
|
if( fSeqPrefix < 0 ) // we are a global age
|
||
|
combined = -(Int32)( ( ( -fSeqPrefix ) << 16 ) + suffix );
|
||
|
else
|
||
|
combined = ( fSeqPrefix << 16 ) + suffix;
|
||
|
|
||
|
// Now, our 32 bit number looks like the following:
|
||
|
// 0xRRAAPPPP
|
||
|
// - RR is FF when reserved, and 00 when normal
|
||
|
// - AA is the one byte for age sequence prefix (FF not allowed because 0xFFFFFFFFFF is reserved for invalid sequence number)
|
||
|
// - PPPP is the two bytes for page sequence number
|
||
|
|
||
|
if( IsGlobalAge() )
|
||
|
return plLocation::MakeReserved( (UInt32)combined );
|
||
|
else
|
||
|
{
|
||
|
plLocation ret = plLocation::MakeNormal( combined );
|
||
|
if (page && !stricmp(page, "builtin"))
|
||
|
ret.SetFlags(plLocation::kBuiltIn);
|
||
|
return ret;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Just make a blank (invalid) one
|
||
|
plLocation loc;
|
||
|
return loc;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Writes the Age Description File
|
||
|
//
|
||
|
void plAgeDescription::Write(hsStream* stream) const
|
||
|
{
|
||
|
char buf[256];
|
||
|
|
||
|
// Write the date/time
|
||
|
sprintf(buf, "StartDateTime=%010lu\n", (unsigned long)fStart.GetSecs());
|
||
|
stream->WriteString(buf);
|
||
|
|
||
|
// Write the day length
|
||
|
sprintf(buf, "DayLength=%f\n", fDayLength);
|
||
|
stream->WriteString(buf);
|
||
|
|
||
|
// Write the max capacity
|
||
|
sprintf(buf, "MaxCapacity=%d\n", fMaxCapacity);
|
||
|
stream->WriteString(buf);
|
||
|
|
||
|
// Write the linger time
|
||
|
sprintf(buf, "LingerTime=%d\n", fLingerTime);
|
||
|
stream->WriteString(buf);
|
||
|
|
||
|
// Write out the sequence prefix
|
||
|
sprintf( buf, "SequencePrefix=%d\n", fSeqPrefix );
|
||
|
stream->WriteString( buf );
|
||
|
|
||
|
// Write out the release version
|
||
|
sprintf( buf, "ReleaseVersion=%d\n", fReleaseVersion );
|
||
|
stream->WriteString( buf );
|
||
|
|
||
|
// Write out the pages
|
||
|
int i;
|
||
|
for( i = 0; i < fPages.GetCount(); i++ )
|
||
|
{
|
||
|
sprintf(buf, "Page=%s\n", fPages[ i ].GetAsString() );
|
||
|
stream->WriteString(buf);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Somewhat of an overkill, but I created it, so I better use it.
|
||
|
// The really nifty (or scary, depending on your viewpoint) thing is that, since
|
||
|
// we only have one section, we can safely use ourselves as the section reader.
|
||
|
// Later I might just add section readers with function pointers to avoid this need entirely
|
||
|
|
||
|
const char *plAgeDescription::GetSectionName( void ) const
|
||
|
{
|
||
|
return "AgeInfo";
|
||
|
}
|
||
|
|
||
|
hsBool plAgeDescription::IParseToken( const char *token, hsStringTokenizer *tokenizer, UInt32 userData )
|
||
|
{
|
||
|
char *tok;
|
||
|
|
||
|
if( !stricmp( token, "StartDateTime" ) )
|
||
|
{
|
||
|
if( ( tok = tokenizer->next() ) != nil )
|
||
|
{
|
||
|
char buf[11];
|
||
|
strncpy(buf, tok, 10); buf[10] = '\0';
|
||
|
fStart.SetSecs(atoi(buf));
|
||
|
}
|
||
|
}
|
||
|
else if (!stricmp(token, "DayLength"))
|
||
|
{
|
||
|
if( ( tok = tokenizer->next() ) != nil )
|
||
|
fDayLength = (float)atof(tok);
|
||
|
}
|
||
|
else if (!stricmp(token, "Page"))
|
||
|
{
|
||
|
fPages.Append( plAgePage( tokenizer->GetRestOfString() ) );
|
||
|
if( fPages[ fPages.GetCount() - 1 ].GetSeqSuffix() == plAgePage::kInvalidSeqSuffix )
|
||
|
fPages[ fPages.GetCount() - 1 ].SetSeqSuffix( fPages.GetCount() );
|
||
|
}
|
||
|
else if (!stricmp(token, "MaxCapacity"))
|
||
|
{
|
||
|
if( ( tok = tokenizer->next() ) != nil )
|
||
|
fMaxCapacity = atoi(tok);
|
||
|
}
|
||
|
else if (!stricmp(token, "LingerTime"))
|
||
|
{
|
||
|
if( ( tok = tokenizer->next() ) != nil )
|
||
|
fLingerTime = atoi(tok);
|
||
|
}
|
||
|
else if( !stricmp(token, "SequencePrefix"))
|
||
|
{
|
||
|
if( ( tok = tokenizer->next() ) != nil )
|
||
|
fSeqPrefix = atoi(tok);
|
||
|
}
|
||
|
else if( !stricmp(token, "ReleaseVersion"))
|
||
|
{
|
||
|
if( ( tok = tokenizer->next() ) != nil )
|
||
|
fReleaseVersion = atoi(tok);
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Reads the Age Description File
|
||
|
//
|
||
|
void plAgeDescription::Read(hsStream* stream)
|
||
|
{
|
||
|
plInitSectionReader *sections[] = { (plInitSectionReader *)this, nil };
|
||
|
|
||
|
plInitFileReader reader( stream, sections );
|
||
|
|
||
|
if( !reader.IsOpen() )
|
||
|
{
|
||
|
hsAssert( false, "Unable to open age description file for reading" );
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
reader.Parse();
|
||
|
reader.Close();
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// What is the current time in the age (in secs), based on dayLength.
|
||
|
//
|
||
|
int plAgeDescription::GetAgeTimeOfDaySecs(const plUnifiedTime& earthCurrentTime) const
|
||
|
{
|
||
|
double elapsedSecs = GetAgeElapsedSeconds(earthCurrentTime);
|
||
|
int secsInADay = (int)(fDayLength * 60 * 60);
|
||
|
int ageTime = (int)elapsedSecs % secsInADay;
|
||
|
return ageTime;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// What is the current time in the age (from 0-1), based on dayLength.
|
||
|
//
|
||
|
float plAgeDescription::GetAgeTimeOfDayPercent(const plUnifiedTime& earthCurrentTime) const
|
||
|
{
|
||
|
double elapsedSecs = GetAgeElapsedSeconds(earthCurrentTime);
|
||
|
int secsInADay = (int)(fDayLength * 60 * 60);
|
||
|
double ageTime = fmod(elapsedSecs, secsInADay);
|
||
|
float percent=(float)(ageTime/secsInADay);
|
||
|
if (percent<0.f)
|
||
|
percent=0.f;
|
||
|
if (percent>1.f)
|
||
|
percent=1.f;
|
||
|
return percent;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// How old is the age in days.
|
||
|
//
|
||
|
double plAgeDescription::GetAgeElapsedDays(plUnifiedTime earthCurrentTime) const
|
||
|
{
|
||
|
earthCurrentTime.SetMicros(0); // Ignore micros for this calculation
|
||
|
|
||
|
double elapsedSecs = GetAgeElapsedSeconds(earthCurrentTime);
|
||
|
return elapsedSecs / 60.0 / 60.0 / fDayLength; // days and fractions
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// How many seconds have elapsed since the age was born. or how old is the age (in secs)
|
||
|
//
|
||
|
double plAgeDescription::GetAgeElapsedSeconds(const plUnifiedTime & earthCurrentTime) const
|
||
|
{
|
||
|
plUnifiedTime elapsed = earthCurrentTime - fStart;
|
||
|
return elapsed.GetSecsDouble();
|
||
|
}
|
||
|
|
||
|
// Static functions for the available common pages
|
||
|
enum CommonPages
|
||
|
{
|
||
|
kTextures,
|
||
|
kGlobal
|
||
|
};
|
||
|
|
||
|
const char *plAgeDescription::GetCommonPage( int pageType )
|
||
|
{
|
||
|
hsAssert( pageType < kNumCommonPages, "Invalid page type in GetCommonPage()" );
|
||
|
return fCommonPages[ pageType ];
|
||
|
}
|
||
|
|
||
|
void plAgeDescription::AppendCommonPages( void )
|
||
|
{
|
||
|
UInt32 startSuffix = 0xffff, i;
|
||
|
|
||
|
|
||
|
if( IsGlobalAge() )
|
||
|
return;
|
||
|
|
||
|
for( i = 0; i < kNumCommonPages; i++ )
|
||
|
fPages.Append( plAgePage( fCommonPages[ i ], startSuffix - i, 0 ) );
|
||
|
}
|
||
|
|
||
|
void plAgeDescription::CopyFrom(const plAgeDescription& other)
|
||
|
{
|
||
|
IDeInit();
|
||
|
fName = hsStrcpy(other.GetAgeName());
|
||
|
int i;
|
||
|
for(i=0;i<other.fPages.GetCount(); i++)
|
||
|
fPages.Append( other.fPages[ i ] );
|
||
|
|
||
|
fStart = other.fStart;
|
||
|
fDayLength = other.fDayLength;
|
||
|
fMaxCapacity = other.fMaxCapacity;
|
||
|
fLingerTime = other.fLingerTime;
|
||
|
|
||
|
fSeqPrefix = other.fSeqPrefix;
|
||
|
fReleaseVersion = other.fReleaseVersion;
|
||
|
}
|
||
|
|
||
|
bool plAgeDescription::FindLocation(const plLocation& loc) const
|
||
|
{
|
||
|
int i;
|
||
|
for( i = 0; i < fPages.GetCount(); i++ )
|
||
|
{
|
||
|
plLocation pageLoc = CalcPageLocation(fPages[i].GetName());
|
||
|
if (pageLoc == loc)
|
||
|
return true;
|
||
|
}
|
||
|
return false;
|
||
14 years ago
|
}
|