mirror of
https://foundry.openuru.org/gitblit/r/CWE-ou-minkata.git
synced 2025-07-14 02:27:40 -04:00
CWE Directory Reorganization
Rearrange directory structure of CWE to be loosely equivalent to the H'uru Plasma repository. Part 1: Movement of directories and files.
This commit is contained in:
471
Sources/Plasma/PubUtilLib/plAgeLoader/plAgeLoader.cpp
Normal file
471
Sources/Plasma/PubUtilLib/plAgeLoader/plAgeLoader.cpp
Normal file
@ -0,0 +1,471 @@
|
||||
/*==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 "plAgeLoader.h"
|
||||
#include "hsStream.h"
|
||||
#include "plgDispatch.h"
|
||||
#include "hsResMgr.h"
|
||||
//#include "hsTimer.h"
|
||||
#include "plResPatcher.h"
|
||||
#include "plBackgroundDownloader.h"
|
||||
#include "process.h" // for getpid()
|
||||
|
||||
#include "../pnProduct/pnProduct.h"
|
||||
|
||||
#include "../pnKeyedObject/plKey.h"
|
||||
#include "../pnKeyedObject/plFixedKey.h"
|
||||
#include "../pnSceneObject/plSceneObject.h"
|
||||
#include "../pnMessage/plClientMsg.h"
|
||||
#include "../pnNetCommon/plNetApp.h"
|
||||
|
||||
#include "../plScene/plRelevanceMgr.h"
|
||||
#include "../plResMgr/plKeyFinder.h"
|
||||
#include "../plAgeDescription/plAgeDescription.h"
|
||||
#include "../plSDL/plSDL.h"
|
||||
#include "../plNetClient/plNetClientMgr.h"
|
||||
#include "../plResMgr/plRegistryHelpers.h"
|
||||
#include "../plResMgr/plRegistryNode.h"
|
||||
#include "../plResMgr/plResManager.h"
|
||||
#include "../plFile/plEncryptedStream.h"
|
||||
|
||||
/// TEMP HACK TO LOAD CONSOLE INIT FILES ON AGE LOAD
|
||||
#include "../plMessage/plConsoleMsg.h"
|
||||
#include "../plMessage/plLoadAvatarMsg.h"
|
||||
#include "../plMessage/plAgeLoadedMsg.h"
|
||||
|
||||
|
||||
extern hsBool gDataServerLocal;
|
||||
extern hsBool gUseBackgroundDownloader;
|
||||
|
||||
// static
|
||||
plAgeLoader* plAgeLoader::fInstance=nil;
|
||||
|
||||
//
|
||||
// CONSTRUCT
|
||||
//
|
||||
plAgeLoader::plAgeLoader() :
|
||||
fInitialAgeState(nil),
|
||||
fFlags(0)
|
||||
{
|
||||
}
|
||||
|
||||
//
|
||||
// DESTRUCT
|
||||
//
|
||||
plAgeLoader::~plAgeLoader()
|
||||
{
|
||||
delete fInitialAgeState;
|
||||
fInitialAgeState=nil;
|
||||
|
||||
if ( PendingAgeFniFiles().size() )
|
||||
plNetClientApp::StaticErrorMsg( "~plAgeLoader(): %d pending age fni files", PendingAgeFniFiles().size() );
|
||||
if ( PendingPageOuts().size() )
|
||||
plNetClientApp::StaticErrorMsg( "~plAgeLoader(): %d pending page outs", PendingPageOuts().size() );
|
||||
|
||||
ClearPageExcludeList(); // Clear our debugging exclude list, just to be tidy
|
||||
|
||||
if (fInstance==this)
|
||||
SetInstance(nil);
|
||||
}
|
||||
|
||||
void plAgeLoader::Shutdown()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void plAgeLoader::Init()
|
||||
{
|
||||
RegisterAs( kAgeLoader_KEY );
|
||||
plgDispatch::Dispatch()->RegisterForExactType(plInitialAgeStateLoadedMsg::Index(), GetKey());
|
||||
plgDispatch::Dispatch()->RegisterForExactType(plClientMsg::Index(), GetKey());
|
||||
|
||||
if (!gDataServerLocal && gUseBackgroundDownloader)
|
||||
plBackgroundDownloader::StartThread();
|
||||
}
|
||||
|
||||
//
|
||||
// STATIC
|
||||
//
|
||||
plAgeLoader* plAgeLoader::GetInstance()
|
||||
{
|
||||
return fInstance;
|
||||
}
|
||||
|
||||
//
|
||||
// STATIC
|
||||
//
|
||||
void plAgeLoader::SetInstance(plAgeLoader* inst)
|
||||
{
|
||||
fInstance=inst;
|
||||
}
|
||||
|
||||
//
|
||||
// Plasma Msg Handler
|
||||
//
|
||||
hsBool plAgeLoader::MsgReceive(plMessage* msg)
|
||||
{
|
||||
plInitialAgeStateLoadedMsg *stateMsg = plInitialAgeStateLoadedMsg::ConvertNoRef( msg );
|
||||
if( stateMsg != nil )
|
||||
{
|
||||
// done receiving the initial state of the age from the server
|
||||
return true;
|
||||
}
|
||||
|
||||
plClientMsg* clientMsg = plClientMsg::ConvertNoRef(msg);
|
||||
if (clientMsg && clientMsg->GetClientMsgFlag()==plClientMsg::kInitComplete)
|
||||
{
|
||||
ExecPendingAgeFniFiles(); // exec age-specific fni files
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
return plReceiver::MsgReceive(msg);
|
||||
}
|
||||
|
||||
//
|
||||
// read in the age desc file and page in/out the rooms belonging to the specified age.
|
||||
// return false on error
|
||||
//
|
||||
//============================================================================
|
||||
bool plAgeLoader::LoadAge(const char ageName[])
|
||||
{
|
||||
return ILoadAge(ageName);
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
bool plAgeLoader::UpdateAge(const char ageName[])
|
||||
{
|
||||
bool result = true;
|
||||
|
||||
if (!gDataServerLocal)
|
||||
{
|
||||
plResPatcher myPatcher(ageName);
|
||||
result = myPatcher.Update();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
void plAgeLoader::NotifyAgeLoaded( bool loaded )
|
||||
{
|
||||
if ( loaded )
|
||||
fFlags &= ~kLoadingAge;
|
||||
else
|
||||
fFlags &= ~kUnLoadingAge;
|
||||
|
||||
plAgeLoadedMsg * msg = TRACKED_NEW plAgeLoadedMsg;
|
||||
msg->fLoaded = loaded;
|
||||
msg->Send();
|
||||
}
|
||||
|
||||
|
||||
//// ILoadAge ////////////////////////////////////////////////////////////////
|
||||
// Does the loading-specific stuff for queueing an age to load
|
||||
|
||||
bool plAgeLoader::ILoadAge(const char ageName[])
|
||||
{
|
||||
plNetClientApp* nc = plNetClientApp::GetInstance();
|
||||
ASSERT(!nc->GetFlagsBit(plNetClientApp::kPlayingGame));
|
||||
|
||||
StrCopy(fAgeName, ageName, arrsize(fAgeName));
|
||||
|
||||
nc->DebugMsg( "Net: Loading age %s", fAgeName);
|
||||
|
||||
if ((fFlags & kLoadMask) != 0)
|
||||
ErrorFatal(__LINE__, __FILE__, "Fatal Error:\nAlready loading or unloading an age.\n%S will now exit.", ProductShortName());
|
||||
|
||||
fFlags |= kLoadingAge;
|
||||
|
||||
plAgeBeginLoadingMsg* ageBeginLoading = TRACKED_NEW plAgeBeginLoadingMsg();
|
||||
ageBeginLoading->Send();
|
||||
|
||||
///////////////////////////////////////////////////////
|
||||
|
||||
|
||||
/// Step 1: Update all of the dat files for this age
|
||||
/*
|
||||
UpdateAge(fAgeName);
|
||||
*/
|
||||
|
||||
/// Step 2: Load the keys for this age, so we can find sceneNodes for them
|
||||
// exec age .fni file when data is done loading
|
||||
char consoleIniName[ 256 ];
|
||||
sprintf( consoleIniName, "dat\\%s.fni", fAgeName);
|
||||
fPendingAgeFniFiles.push_back( consoleIniName );
|
||||
|
||||
char csvName[256];
|
||||
sprintf(csvName, "dat\\%s.csv", fAgeName);
|
||||
fPendingAgeCsvFiles.push_back(csvName);
|
||||
|
||||
plSynchEnabler p( false ); // turn off dirty tracking while in this function
|
||||
|
||||
hsStream* stream=GetAgeDescFileStream(fAgeName);
|
||||
if (!stream)
|
||||
{
|
||||
nc->ErrorMsg("Failed loading age. Age desc file %s has nil stream", fAgeName);
|
||||
fFlags &= ~kLoadingAge;
|
||||
return false;
|
||||
}
|
||||
|
||||
plAgeDescription ad;
|
||||
ad.Read(stream);
|
||||
ad.SetAgeName(fAgeName);
|
||||
stream->Close();
|
||||
delete stream;
|
||||
ad.SeekFirstPage();
|
||||
|
||||
plAgePage *page;
|
||||
plKey clientKey = hsgResMgr::ResMgr()->FindKey( kClient_KEY );
|
||||
|
||||
// Copy, exclude pages we want excluded, and collect our scene nodes
|
||||
fCurAgeDescription.CopyFrom(ad);
|
||||
while( ( page = ad.GetNextPage() ) != nil )
|
||||
{
|
||||
if( IsPageExcluded( page, fAgeName) )
|
||||
continue;
|
||||
|
||||
plKey roomKey = plKeyFinder::Instance().FindSceneNodeKey( fAgeName, page->GetName() );
|
||||
if( roomKey != nil )
|
||||
AddPendingPageInRoomKey( roomKey );
|
||||
}
|
||||
ad.SeekFirstPage();
|
||||
|
||||
|
||||
// Tell the client to load-and-hold all the keys for this age, to make the loading process work better
|
||||
plClientMsg *loadAgeKeysMsg = TRACKED_NEW plClientMsg( plClientMsg::kLoadAgeKeys );
|
||||
loadAgeKeysMsg->SetAgeName( fAgeName);
|
||||
loadAgeKeysMsg->Send( clientKey );
|
||||
|
||||
//
|
||||
// Load the Age's SDL Hook object (and it's python modifier)
|
||||
//
|
||||
plUoid oid=nc->GetAgeSDLObjectUoid(fAgeName);
|
||||
plKey ageSDLObjectKey = hsgResMgr::ResMgr()->FindKey(oid);
|
||||
if (ageSDLObjectKey)
|
||||
hsgResMgr::ResMgr()->AddViaNotify(ageSDLObjectKey, TRACKED_NEW plGenRefMsg(nc->GetKey(), plRefMsg::kOnCreate, -1,
|
||||
plNetClientMgr::kAgeSDLHook), plRefFlags::kActiveRef);
|
||||
|
||||
int nPages = 0;
|
||||
|
||||
plClientMsg* pMsg1 = TRACKED_NEW plClientMsg(plClientMsg::kLoadRoom);
|
||||
pMsg1->SetAgeName(fAgeName);
|
||||
|
||||
// Loop and ref!
|
||||
while( ( page = ad.GetNextPage() ) != nil )
|
||||
{
|
||||
if( IsPageExcluded( page, fAgeName) )
|
||||
{
|
||||
nc->DebugMsg( "\tExcluding page %s\n", page->GetName() );
|
||||
continue;
|
||||
}
|
||||
|
||||
nPages++;
|
||||
|
||||
pMsg1->AddRoomLoc(ad.CalcPageLocation(page->GetName()));
|
||||
nc->DebugMsg("\tPaging in room %s\n", page->GetName());
|
||||
}
|
||||
|
||||
pMsg1->Send(clientKey);
|
||||
|
||||
// Send the client a message to let go of the extra keys it was holding on to
|
||||
plClientMsg *dumpAgeKeys = TRACKED_NEW plClientMsg( plClientMsg::kReleaseAgeKeys );
|
||||
dumpAgeKeys->SetAgeName( fAgeName);
|
||||
dumpAgeKeys->Send( clientKey );
|
||||
|
||||
if ( nPages==0 )
|
||||
{
|
||||
// age is done loading because it has no pages?
|
||||
fFlags &= ~kLoadingAge;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//// plUnloadAgeCollector ////////////////////////////////////////////////////
|
||||
// Registry page iterator to collect all the loaded pages of a given age
|
||||
// Note: we have to do an IterateAllPages(), since we want to also catch
|
||||
// pages that are partially loaded, which are skipped in the vanilla
|
||||
// IteratePages() call.
|
||||
|
||||
class plUnloadAgeCollector : public plRegistryPageIterator
|
||||
{
|
||||
public:
|
||||
hsTArray<plRegistryPageNode *> fPages;
|
||||
const char *fAge;
|
||||
|
||||
plUnloadAgeCollector( const char *a ) : fAge( a ) {}
|
||||
|
||||
virtual hsBool EatPage( plRegistryPageNode *page )
|
||||
{
|
||||
if( fAge && stricmp( page->GetPageInfo().GetAge(), fAge ) == 0 )
|
||||
{
|
||||
fPages.Append( page );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
//// IUnloadAge //////////////////////////////////////////////////////////////
|
||||
// Does the UNloading-specific stuff for queueing an age to unload.
|
||||
// Far simpler that ILoadAge :)
|
||||
|
||||
bool plAgeLoader::IUnloadAge()
|
||||
{
|
||||
plNetClientApp* nc = plNetClientApp::GetInstance();
|
||||
nc->DebugMsg( "Net: Unloading age %s", fAgeName);
|
||||
|
||||
hsAssert( (fFlags & kLoadMask)==0, "already loading or unloading an age?");
|
||||
fFlags |= kUnLoadingAge;
|
||||
|
||||
plAgeBeginLoadingMsg* msg = TRACKED_NEW plAgeBeginLoadingMsg();
|
||||
msg->fLoading = false;
|
||||
msg->Send();
|
||||
|
||||
// Note: instead of going from the .age file, we just want a list of what
|
||||
// is REALLY paged in for this age. So ask the resMgr!
|
||||
plUnloadAgeCollector collector( fAgeName);
|
||||
// WARNING: unsafe cast here, but it's ok, until somebody is mean and makes a non-plResManager resMgr
|
||||
( (plResManager *)hsgResMgr::ResMgr() )->IterateAllPages( &collector );
|
||||
|
||||
// Dat was easy...
|
||||
plKey clientKey = hsgResMgr::ResMgr()->FindKey( kClient_KEY );
|
||||
|
||||
// Build up a list of all the rooms we're going to page out
|
||||
plKeyVec newPageOuts;
|
||||
|
||||
int i;
|
||||
for( i = 0; i < collector.fPages.GetCount(); i++ )
|
||||
{
|
||||
plRegistryPageNode *page = collector.fPages[ i ];
|
||||
|
||||
plKey roomKey = plKeyFinder::Instance().FindSceneNodeKey( page->GetPageInfo().GetLocation() );
|
||||
if( roomKey != nil && roomKey->ObjectIsLoaded() )
|
||||
{
|
||||
nc->DebugMsg( "\tPaging out room %s\n", page->GetPageInfo().GetPage() );
|
||||
newPageOuts.push_back(roomKey);
|
||||
}
|
||||
}
|
||||
|
||||
// Put them in our pending page outs
|
||||
for( i = 0; i < newPageOuts.size(); i++ )
|
||||
fPendingPageOuts.push_back(newPageOuts[i]);
|
||||
|
||||
// ...then send the unload messages. That way we ensure the list is complete
|
||||
// before any messages get processed
|
||||
for( i = 0; i < newPageOuts.size(); i++ )
|
||||
{
|
||||
plClientMsg *pMsg1 = TRACKED_NEW plClientMsg( plClientMsg::kUnloadRoom );
|
||||
pMsg1->AddRoomLoc(newPageOuts[i]->GetUoid().GetLocation());
|
||||
pMsg1->Send( clientKey );
|
||||
}
|
||||
|
||||
if ( newPageOuts.size()==0 )
|
||||
{
|
||||
// age is done unloading because it has no pages?
|
||||
NotifyAgeLoaded( false );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void plAgeLoader::ExecPendingAgeFniFiles()
|
||||
{
|
||||
int i;
|
||||
for (i=0;i<PendingAgeFniFiles().size(); i++)
|
||||
{
|
||||
plConsoleMsg *cMsg = TRACKED_NEW plConsoleMsg( plConsoleMsg::kExecuteFile, fPendingAgeFniFiles[i].c_str() );
|
||||
plgDispatch::MsgSend( cMsg );
|
||||
}
|
||||
fPendingAgeFniFiles.clear();
|
||||
}
|
||||
|
||||
void plAgeLoader::ExecPendingAgeCsvFiles()
|
||||
{
|
||||
int i;
|
||||
for (i=0;i<PendingAgeCsvFiles().size(); i++)
|
||||
{
|
||||
hsStream* stream = plEncryptedStream::OpenEncryptedFile(fPendingAgeCsvFiles[i].c_str());
|
||||
if (stream)
|
||||
{
|
||||
plRelevanceMgr::Instance()->ParseCsvInput(stream);
|
||||
stream->Close();
|
||||
delete stream;
|
||||
}
|
||||
}
|
||||
fPendingAgeCsvFiles.clear();
|
||||
}
|
||||
|
||||
//
|
||||
// return alloced stream or nil
|
||||
// static
|
||||
//
|
||||
hsStream* plAgeLoader::GetAgeDescFileStream(const char* ageName)
|
||||
{
|
||||
if (!ageName)
|
||||
return nil;
|
||||
|
||||
char ageDescFileName[256];
|
||||
sprintf(ageDescFileName, "dat\\%s.age", ageName);
|
||||
|
||||
hsStream* stream = plEncryptedStream::OpenEncryptedFile(ageDescFileName);
|
||||
if (!stream)
|
||||
{
|
||||
char str[256];
|
||||
sprintf(str, "Can't find age desc file %s", ageDescFileName);
|
||||
hsAssert(false, str);
|
||||
return nil;
|
||||
}
|
||||
|
||||
return stream;
|
||||
}
|
||||
|
||||
//
|
||||
// sent from server with joinAck
|
||||
//
|
||||
void plAgeLoader::ISetInitialAgeState(plStateDataRecord* s)
|
||||
{
|
||||
hsAssert(fInitialAgeState != s, "duplicate initial age state");
|
||||
delete fInitialAgeState;
|
||||
fInitialAgeState=s;
|
||||
}
|
145
Sources/Plasma/PubUtilLib/plAgeLoader/plAgeLoader.h
Normal file
145
Sources/Plasma/PubUtilLib/plAgeLoader/plAgeLoader.h
Normal file
@ -0,0 +1,145 @@
|
||||
/*==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==*/
|
||||
#ifndef plAgeLoader_h
|
||||
#define plAgeLoader_h
|
||||
|
||||
#include "hsTypes.h"
|
||||
#include "hsStlUtils.h"
|
||||
|
||||
#include "../pnUtils/pnUtils.h"
|
||||
#include "../pnNetBase/pnNetBase.h"
|
||||
#include "../pnKeyedObject/hsKeyedObject.h"
|
||||
#include "../pnKeyedObject/plKey.h"
|
||||
|
||||
#include "../plAgeDescription/plAgeDescription.h"
|
||||
|
||||
#include "../plUUID/plUUID.h"
|
||||
|
||||
//
|
||||
// A singleton class which manages loading and unloading ages and operations associated with that
|
||||
//
|
||||
|
||||
// fwd decls
|
||||
class plStateDataRecord;
|
||||
class plMessage;
|
||||
class plOperationProgress;
|
||||
|
||||
class plAgeLoader : public hsKeyedObject
|
||||
{
|
||||
friend class plNetClientMsgHandler;
|
||||
friend class plNetClientJoinTask;
|
||||
private:
|
||||
typedef std::vector<plKey> plKeyVec;
|
||||
typedef std::vector<std::string> plStringVec;
|
||||
|
||||
enum Flags
|
||||
{
|
||||
kLoadingAge = 0x1,
|
||||
kUnLoadingAge = 0x2,
|
||||
kLoadMask = (kLoadingAge | kUnLoadingAge)
|
||||
};
|
||||
|
||||
static plAgeLoader* fInstance;
|
||||
|
||||
UInt32 fFlags;
|
||||
plStringVec fPendingAgeFniFiles; // list of age .fni files to be parsed
|
||||
plStringVec fPendingAgeCsvFiles; // list of age .csv files to be parsed
|
||||
plKeyVec fPendingPageIns; // keys of rooms which are currently being paged in.
|
||||
plKeyVec fPendingPageOuts; // keys of rooms which are currently being paged out.
|
||||
plAgeDescription fCurAgeDescription;
|
||||
plStateDataRecord* fInitialAgeState;
|
||||
char fAgeName[kMaxAgeNameLength];
|
||||
|
||||
bool ILoadAge(const char ageName[]);
|
||||
bool IUnloadAge();
|
||||
void ISetInitialAgeState(plStateDataRecord* s); // sent from server with joinAck
|
||||
const plStateDataRecord* IGetInitialAgeState() const { return fInitialAgeState; }
|
||||
|
||||
public:
|
||||
plAgeLoader();
|
||||
~plAgeLoader();
|
||||
|
||||
CLASSNAME_REGISTER( plAgeLoader);
|
||||
GETINTERFACE_ANY( plAgeLoader, hsKeyedObject);
|
||||
|
||||
static plAgeLoader* GetInstance();
|
||||
static void SetInstance(plAgeLoader* inst);
|
||||
static hsStream* GetAgeDescFileStream(const char* ageName);
|
||||
|
||||
void Init();
|
||||
void Shutdown();
|
||||
hsBool MsgReceive(plMessage* msg);
|
||||
bool LoadAge(const char ageName[]);
|
||||
bool UnloadAge() { return IUnloadAge(); }
|
||||
bool UpdateAge(const char ageName[]);
|
||||
void NotifyAgeLoaded( bool loaded );
|
||||
|
||||
const plKeyVec& PendingPageOuts() const { return fPendingPageOuts; }
|
||||
const plKeyVec& PendingPageIns() const { return fPendingPageIns; }
|
||||
const plStringVec& PendingAgeCsvFiles() const { return fPendingAgeCsvFiles; }
|
||||
const plStringVec& PendingAgeFniFiles() const { return fPendingAgeFniFiles; }
|
||||
|
||||
void AddPendingPageInRoomKey(plKey r);
|
||||
bool RemovePendingPageInRoomKey(plKey r);
|
||||
bool IsPendingPageInRoomKey(plKey p, int* idx=nil);
|
||||
|
||||
void ExecPendingAgeFniFiles();
|
||||
void ExecPendingAgeCsvFiles();
|
||||
|
||||
// Fun debugging exclude commands (to prevent certain pages from loading)
|
||||
void ClearPageExcludeList( void );
|
||||
void AddExcludedPage( const char *pageName, const char *ageName = nil );
|
||||
bool IsPageExcluded( const plAgePage *page, const char *ageName = nil );
|
||||
|
||||
const plAgeDescription &GetCurrAgeDesc( void ) const { return fCurAgeDescription; }
|
||||
|
||||
// paging
|
||||
void FinishedPagingInRoom(plKey* rmKey, int numRms); // call when finished paging in/out a room
|
||||
void StartPagingOutRoom(plKey* rmKey, int numRms); // call when starting to page in/out a room
|
||||
void FinishedPagingOutRoom(plKey* rmKey, int numRms);
|
||||
// Called on page-in-hold rooms, since we don't want them actually paging out in the NCM (i.e. sending info to the server)
|
||||
void IgnorePagingOutRoom(plKey* rmKey, int numRms);
|
||||
|
||||
bool IsLoadingAge(){ return (fFlags & (kUnLoadingAge | kLoadingAge)); }
|
||||
};
|
||||
|
||||
#endif // plAgeLoader_h
|
50
Sources/Plasma/PubUtilLib/plAgeLoader/plAgeLoaderCreatable.h
Normal file
50
Sources/Plasma/PubUtilLib/plAgeLoader/plAgeLoaderCreatable.h
Normal file
@ -0,0 +1,50 @@
|
||||
/*==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==*/
|
||||
#ifndef plAgeLoaderCreatable_inc
|
||||
#define plAgeLoaderCreatable_inc
|
||||
|
||||
#include "../pnFactory/plCreator.h"
|
||||
|
||||
#include "plAgeLoader.h"
|
||||
REGISTER_CREATABLE( plAgeLoader);
|
||||
|
||||
#endif // plAgeLoaderCreatable_inc
|
337
Sources/Plasma/PubUtilLib/plAgeLoader/plAgeLoaderPaging.cpp
Normal file
337
Sources/Plasma/PubUtilLib/plAgeLoader/plAgeLoaderPaging.cpp
Normal file
@ -0,0 +1,337 @@
|
||||
/*==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 "plAgeLoader.h"
|
||||
|
||||
#include "hsTimer.h"
|
||||
#include "hsResMgr.h"
|
||||
#include "plgDispatch.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "../pnNetCommon/plNetApp.h"
|
||||
#include "../pnKeyedObject/plKey.h"
|
||||
|
||||
#include "../plMessage/plAgeLoadedMsg.h"
|
||||
#include "../plNetMessage/plNetMessage.h"
|
||||
#include "../plProgressMgr/plProgressMgr.h"
|
||||
#include "../plSDL/plSDL.h"
|
||||
#include "../pnDispatch/plDispatch.h"
|
||||
#include "../plResMgr/plResManager.h"
|
||||
|
||||
#include "../plNetClient/plNetClientMgr.h"
|
||||
|
||||
//
|
||||
// if room is reserved or for animations, don't report it to the server.
|
||||
// the server only cares about rooms which have real, networked objects in them.
|
||||
// The server already assigns a global room to everyone for things like avatar and builtin state .
|
||||
//
|
||||
bool ReportRoomToServer(const plKey &key)
|
||||
{
|
||||
plLocation keyLoc=key->GetUoid().GetLocation();
|
||||
bool skip=(keyLoc.IsReserved() || keyLoc.IsVirtual() ||
|
||||
// HACK ALERT - replace with new uoid type flags
|
||||
(key->GetName() &&
|
||||
(!strnicmp(key->GetName(), "global", 6) ||
|
||||
strstr(key->GetName(), "_Male") ||
|
||||
strstr(key->GetName(), "_Female")
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
if (skip)
|
||||
hsLogEntry(plNetApp::StaticDebugMsg("Not reporting room %s to server, reserved=%d, virtual=%d",
|
||||
key->GetName(), keyLoc.IsReserved(), keyLoc.IsVirtual()));
|
||||
|
||||
return !skip;
|
||||
}
|
||||
|
||||
//
|
||||
// call when finished paging in a room.
|
||||
//
|
||||
void plAgeLoader::FinishedPagingInRoom(plKey* rmKey, int numRms)
|
||||
{
|
||||
if (numRms==0)
|
||||
return;
|
||||
|
||||
unsigned pendingPageIns = PendingPageIns().size();
|
||||
|
||||
plNetClientApp* nc = plNetClientApp::GetInstance();
|
||||
|
||||
// Send a msg to the server indicating that we have this room paged in
|
||||
plNetMsgPagingRoom * pagingMsg = TRACKED_NEW plNetMsgPagingRoom;
|
||||
pagingMsg->SetNetProtocol(kNetProtocolCli2Game);
|
||||
int i;
|
||||
for(i=0;i<numRms;i++)
|
||||
{
|
||||
plKey key=rmKey[i];
|
||||
if (!RemovePendingPageInRoomKey(key)) // room is done paging in
|
||||
continue; // we didn't queue this room
|
||||
|
||||
if (!ReportRoomToServer(key))
|
||||
continue;
|
||||
|
||||
pagingMsg->AddRoom(key);
|
||||
hsLogEntry(nc->DebugMsg("\tSending PageIn/RequestState msg, room=%s\n", key->GetName()));
|
||||
}
|
||||
if( pagingMsg->GetNumRooms() > 0 ) // all rooms were reserved
|
||||
{
|
||||
plNetClientMgr * mgr = plNetClientMgr::GetInstance();
|
||||
mgr->AddPendingPagingRoomMsg( pagingMsg );
|
||||
}
|
||||
else
|
||||
delete pagingMsg;
|
||||
|
||||
// If any of these rooms were queued for load by us, then we may be done loading the age.
|
||||
if (pendingPageIns != PendingPageIns().size())
|
||||
{
|
||||
bool ageLoaded = (PendingPageIns().size()==0) && (fFlags & kLoadingAge);
|
||||
if (ageLoaded)
|
||||
{
|
||||
plAgeLoaded2Msg * msg = TRACKED_NEW plAgeLoaded2Msg;
|
||||
msg->Send();
|
||||
// join task will call NotifyAgeLoaded for us later
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// called by the client when a room is finished paging out
|
||||
//
|
||||
void plAgeLoader::FinishedPagingOutRoom(plKey* rmKey, int numRms)
|
||||
{
|
||||
plNetClientApp* nc = plNetClientApp::GetInstance();
|
||||
nc->StayAlive(hsTimer::GetSysSeconds()); // alive
|
||||
|
||||
int i;
|
||||
for(i=0;i<numRms;i++)
|
||||
{
|
||||
plKeyVec::iterator found = std::find( fPendingPageOuts.begin(), fPendingPageOuts.end(), rmKey[ i ] );
|
||||
if( found != fPendingPageOuts.end() )
|
||||
{
|
||||
fPendingPageOuts.erase( found );
|
||||
nc->DebugMsg("Finished paging out room %s", rmKey[i]->GetName());
|
||||
}
|
||||
}
|
||||
|
||||
if (PendingPageOuts().size() == 0 && (fFlags & kUnLoadingAge))
|
||||
{
|
||||
NotifyAgeLoaded( false );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// call when starting to page out a room.
|
||||
// allows server to transfer ownership of room objects to someone else.
|
||||
//
|
||||
void plAgeLoader::StartPagingOutRoom(plKey* rmKey, int numRms)
|
||||
{
|
||||
plNetClientApp* nc = plNetClientApp::GetInstance();
|
||||
|
||||
plNetMsgPagingRoom pagingMsg;
|
||||
pagingMsg.SetNetProtocol(kNetProtocolCli2Game);
|
||||
pagingMsg.SetPagingOut(true);
|
||||
int i;
|
||||
for(i=0;i<numRms;i++)
|
||||
{
|
||||
plKey key=rmKey[i];
|
||||
if (!ReportRoomToServer(key))
|
||||
continue;
|
||||
|
||||
pagingMsg.AddRoom(rmKey[i]);
|
||||
nc->DebugMsg("\tSending PageOut msg, room=%s", rmKey[i]->GetName());
|
||||
}
|
||||
|
||||
if (!pagingMsg.GetNumRooms()) // all rooms were reserved
|
||||
return;
|
||||
|
||||
nc->SendMsg(&pagingMsg);
|
||||
}
|
||||
|
||||
// Client telling us that this page isn't going to get the start/finish combo
|
||||
// on page out, most likely because it was a load-and-hold, not a load. So take
|
||||
// it from our pending list but don't actually process it
|
||||
// Note: right now it's just a dup of FinishPagingOutRoom(), but since the latter
|
||||
// might change later, we go ahead and dup to avoid unnecessary bugs later
|
||||
void plAgeLoader::IgnorePagingOutRoom(plKey* rmKey, int numRms)
|
||||
{
|
||||
plNetClientApp* nc = plNetClientApp::GetInstance();
|
||||
nc->StayAlive(hsTimer::GetSysSeconds()); // alive
|
||||
|
||||
int i;
|
||||
for(i=0;i<numRms;i++)
|
||||
{
|
||||
plKeyVec::iterator found = std::find( fPendingPageOuts.begin(), fPendingPageOuts.end(), rmKey[ i ] );
|
||||
if( found != fPendingPageOuts.end() )
|
||||
{
|
||||
fPendingPageOuts.erase( found );
|
||||
nc->DebugMsg("Ignoring paged out room %s", rmKey[i]->GetName());
|
||||
}
|
||||
}
|
||||
|
||||
if (PendingPageOuts().size() == 0 && (fFlags & kUnLoadingAge))
|
||||
{
|
||||
NotifyAgeLoaded( false );
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////
|
||||
|
||||
bool plAgeLoader::IsPendingPageInRoomKey(plKey pKey, int *idx)
|
||||
{
|
||||
if (pKey)
|
||||
{
|
||||
plKeyVec::iterator result=std::find(fPendingPageIns.begin(), fPendingPageIns.end(), pKey);
|
||||
bool found = result!=fPendingPageIns.end();
|
||||
if (idx)
|
||||
*idx = found ? result-fPendingPageIns.begin() : -1;
|
||||
return found;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void plAgeLoader::AddPendingPageInRoomKey(plKey pKey)
|
||||
{
|
||||
if (!IsPendingPageInRoomKey(pKey))
|
||||
{
|
||||
fPendingPageIns.push_back(pKey);
|
||||
}
|
||||
}
|
||||
|
||||
bool plAgeLoader::RemovePendingPageInRoomKey(plKey pKey)
|
||||
{
|
||||
int idx;
|
||||
if (IsPendingPageInRoomKey(pKey, &idx))
|
||||
{
|
||||
fPendingPageIns.erase(fPendingPageIns.begin()+idx); // remove key from list
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//// Page Exclusion //////////////////////////////////////////////////////////
|
||||
// //
|
||||
// Fun debugging exclude commands (to prevent certain pages from loading) //
|
||||
// //
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class plExcludePage
|
||||
{
|
||||
public:
|
||||
char *fPageName;
|
||||
char *fAgeName;
|
||||
|
||||
plExcludePage() { fPageName = nil; fAgeName = nil; }
|
||||
plExcludePage( char *p, char *a )
|
||||
{
|
||||
fPageName = p;
|
||||
fAgeName = a;
|
||||
}
|
||||
};
|
||||
|
||||
static hsTArray<plExcludePage> sExcludeList;
|
||||
|
||||
void plAgeLoader::ClearPageExcludeList( void )
|
||||
{
|
||||
int i;
|
||||
|
||||
|
||||
for( i = 0; i < sExcludeList.GetCount(); i++ )
|
||||
{
|
||||
delete [] sExcludeList[ i ].fPageName;
|
||||
delete [] sExcludeList[ i ].fAgeName;
|
||||
}
|
||||
}
|
||||
|
||||
void plAgeLoader::AddExcludedPage( const char *pageName, const char *ageName )
|
||||
{
|
||||
char *p = hsStrcpy( pageName );
|
||||
char *a = nil;
|
||||
if( ageName != nil )
|
||||
a = hsStrcpy( ageName );
|
||||
|
||||
sExcludeList.Append( plExcludePage( p, a ) );
|
||||
}
|
||||
|
||||
bool plAgeLoader::IsPageExcluded( const plAgePage *page, const char *ageName )
|
||||
{
|
||||
// check page flags
|
||||
if (page->GetFlags() & plAgePage::kPreventAutoLoad)
|
||||
return true;
|
||||
|
||||
// check exclude list
|
||||
const char* pageName = page->GetName();
|
||||
int i;
|
||||
for( i = 0; i < sExcludeList.GetCount(); i++ )
|
||||
{
|
||||
if( stricmp( pageName, sExcludeList[ i ].fPageName ) == 0 )
|
||||
{
|
||||
if( ageName == nil || sExcludeList[ i ].fAgeName == nil ||
|
||||
stricmp( ageName, sExcludeList[ i ].fAgeName ) == 0 )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check if pages are excluded due to age SDL vars
|
||||
if (page->GetFlags() & plAgePage::kLoadIfSDLPresent)
|
||||
{
|
||||
if (IGetInitialAgeState())
|
||||
{
|
||||
plSimpleStateVariable* sdVar = IGetInitialAgeState()->FindVar(pageName);
|
||||
if (!sdVar)
|
||||
return true; // no sdl var, exclude
|
||||
|
||||
bool value;
|
||||
sdVar->Get(&value);
|
||||
return value ? false : true; // exclude if var is false
|
||||
}
|
||||
else
|
||||
return true; // no age state, exclude
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
545
Sources/Plasma/PubUtilLib/plAgeLoader/plBackgroundDownloader.cpp
Normal file
545
Sources/Plasma/PubUtilLib/plAgeLoader/plBackgroundDownloader.cpp
Normal file
@ -0,0 +1,545 @@
|
||||
/*==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 "plBackgroundDownloader.h"
|
||||
|
||||
#include <process.h>
|
||||
|
||||
#include "../pnUtils/pnUtils.h"
|
||||
#include "../pnNetBase/pnNetBase.h"
|
||||
#include "../plEncryption/plChecksum.h"
|
||||
|
||||
#include "../NucleusLib/inc/hsResMgr.h"
|
||||
|
||||
#include "../plAgeDescription/plAgeManifest.h"
|
||||
#include "../plResMgr/plResManager.h"
|
||||
#include "../plFile/plFileUtils.h"
|
||||
#include "../plFile/plEncryptedStream.h"
|
||||
#include "../plCompression/plZlibStream.h"
|
||||
#include "../plAudioCore/plAudioFileReader.h"
|
||||
#include "../plProgressMgr/plProgressMgr.h"
|
||||
|
||||
#include "../pnAsyncCore/pnAsyncCore.h"
|
||||
#include "../pnNetCli/pnNetCli.h"
|
||||
#include "../plNetGameLib/plNetGameLib.h"
|
||||
|
||||
#include "../pnDispatch/plDispatch.h"
|
||||
#include "../plStatusLog/plStatusLog.h"
|
||||
|
||||
static const unsigned kMaxDownloadTries = 10;
|
||||
static const wchar s_manifest[] = L"AllAges";
|
||||
|
||||
plBackgroundDownloader* plBackgroundDownloader::fInstance = NULL;
|
||||
|
||||
hsBool gUseBackgroundDownloader = false;
|
||||
|
||||
//============================================================================
|
||||
enum DownloaderLogType
|
||||
{
|
||||
kHeader,
|
||||
kInfo,
|
||||
kMajorStatus,
|
||||
kStatus,
|
||||
kError,
|
||||
};
|
||||
void BackgroundDownloaderLog(DownloaderLogType type, const char* format, ...)
|
||||
{
|
||||
UInt32 color = 0;
|
||||
switch (type)
|
||||
{
|
||||
case kHeader: color = plStatusLog::kWhite; break;
|
||||
case kInfo: color = plStatusLog::kBlue; break;
|
||||
case kMajorStatus: color = plStatusLog::kYellow; break;
|
||||
case kStatus: color = plStatusLog::kGreen; break;
|
||||
case kError: color = plStatusLog::kRed; break;
|
||||
}
|
||||
|
||||
static plStatusLog* gStatusLog = nil;
|
||||
if (!gStatusLog)
|
||||
{
|
||||
gStatusLog = plStatusLogMgr::GetInstance().CreateStatusLog(
|
||||
20,
|
||||
"bgdownload.log",
|
||||
plStatusLog::kFilledBackground | plStatusLog::kAlignToTop | plStatusLog::kDeleteForMe);
|
||||
}
|
||||
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
|
||||
gStatusLog->AddLineV(color, format, args);
|
||||
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
class plBGDownloadStream : public plZlibStream
|
||||
{
|
||||
public:
|
||||
plBGDownloadStream() : plZlibStream() {}
|
||||
virtual ~plBGDownloadStream() {}
|
||||
|
||||
virtual UInt32 Write(UInt32 byteCount, const void* buffer);
|
||||
};
|
||||
|
||||
UInt32 plBGDownloadStream::Write(UInt32 byteCount, const void* buffer)
|
||||
{
|
||||
return plZlibStream::Write(byteCount, buffer);
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
static void DownloadFileCallback(ENetError result, void* param, const wchar filename[], hsStream* writer)
|
||||
{
|
||||
plBackgroundDownloader* bgdownloader = (plBackgroundDownloader*)param;
|
||||
|
||||
// Retry download unless shutting down or file not found
|
||||
switch (result) {
|
||||
case kNetSuccess:
|
||||
writer->Close();
|
||||
bgdownloader->DoneWithFile(true);
|
||||
break;
|
||||
|
||||
case kNetErrFileNotFound:
|
||||
case kNetErrRemoteShutdown:
|
||||
writer->Close();
|
||||
bgdownloader->DoneWithFile(false);
|
||||
break;
|
||||
|
||||
default:
|
||||
writer->Rewind();
|
||||
NetCliFileDownloadRequest(
|
||||
filename,
|
||||
writer,
|
||||
DownloadFileCallback,
|
||||
param
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void ManifestCallback(ENetError result, void* param, const wchar group[], const NetCliFileManifestEntry manifest[], unsigned entryCount)
|
||||
{
|
||||
plBackgroundDownloader* bgdownloader = (plBackgroundDownloader*)param;
|
||||
bgdownloader->DoneWithManifest(result == kNetSuccess, manifest, entryCount);
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
plBackgroundDownloader* plBackgroundDownloader::GetInstance()
|
||||
{
|
||||
return fInstance;
|
||||
}
|
||||
|
||||
void plBackgroundDownloader::ThreadMain(void * param)
|
||||
{
|
||||
Init();
|
||||
|
||||
plBackgroundDownloader::GetInstance()->Run();
|
||||
plBackgroundDownloader::GetInstance()->CleanUp();
|
||||
|
||||
Shutdown();
|
||||
}
|
||||
|
||||
void plBackgroundDownloader::StartThread()
|
||||
{
|
||||
_beginthread(plBackgroundDownloader::ThreadMain, 0, NULL);
|
||||
}
|
||||
|
||||
void plBackgroundDownloader::Init()
|
||||
{
|
||||
fInstance = TRACKED_NEW plBackgroundDownloader();
|
||||
}
|
||||
|
||||
void plBackgroundDownloader::Shutdown()
|
||||
{
|
||||
delete fInstance;
|
||||
fInstance = NULL;
|
||||
}
|
||||
|
||||
plBackgroundDownloader::plBackgroundDownloader()
|
||||
{
|
||||
BackgroundDownloaderLog(kHeader, "--- Starting background download ---");
|
||||
|
||||
fBGDownloaderRun = CreateEvent(
|
||||
NULL, // default security attributes
|
||||
TRUE, // manual-reset event
|
||||
FALSE, // initial state is signaled
|
||||
NULL // unnamed
|
||||
);
|
||||
|
||||
fBGDownloaderIsPaused = CreateEvent(
|
||||
NULL, // default security attributes
|
||||
FALSE, // manual-reset event
|
||||
TRUE, // initial state is signaled
|
||||
NULL // unnamed
|
||||
);
|
||||
}
|
||||
|
||||
plBackgroundDownloader::~plBackgroundDownloader()
|
||||
{
|
||||
HANDLE runHandle = fBGDownloaderRun;
|
||||
fBGDownloaderRun = NULL;
|
||||
CloseHandle(runHandle);
|
||||
|
||||
HANDLE pausedHandle = fBGDownloaderIsPaused;
|
||||
fBGDownloaderIsPaused = NULL;
|
||||
CloseHandle(pausedHandle);
|
||||
|
||||
BackgroundDownloaderLog(kHeader, "--- Background download done ---");
|
||||
}
|
||||
|
||||
UInt32 plBackgroundDownloader::IGetDownloadSize()
|
||||
{
|
||||
if (!IGetDataManifest())
|
||||
return 0;
|
||||
|
||||
UInt32 downloadSize = 0;
|
||||
UInt32 downloadFiles = 0;
|
||||
for (MfsFileVec::iterator i = fMfsVec.begin(); i != fMfsVec.end(); ++i)
|
||||
{
|
||||
plManifestFile* mfsFile = (*i);
|
||||
|
||||
if (!mfsFile->IsLocalUpToDate())
|
||||
{
|
||||
downloadFiles++;
|
||||
downloadSize += mfsFile->GetDownloadSize();
|
||||
}
|
||||
}
|
||||
|
||||
BackgroundDownloaderLog(kInfo, "Got download stats, %d files, %d bytes", downloadFiles, downloadSize);
|
||||
|
||||
return downloadSize;
|
||||
}
|
||||
|
||||
bool plBackgroundDownloader::CheckFreeSpace(UInt32 bytesNeeded)
|
||||
{
|
||||
#ifdef HS_BUILD_FOR_WIN32
|
||||
ULARGE_INTEGER freeBytesAvailable, totalNumberOfBytes, neededBytes;
|
||||
if (GetDiskFreeSpaceEx(NULL, &freeBytesAvailable, &totalNumberOfBytes, NULL))
|
||||
{
|
||||
neededBytes.HighPart = 0;
|
||||
neededBytes.LowPart = bytesNeeded;
|
||||
|
||||
if (neededBytes.QuadPart > freeBytesAvailable.QuadPart)
|
||||
{
|
||||
BackgroundDownloaderLog(kInfo, "Not enough disk space (asked for %d bytes)", bytesNeeded);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#endif // HS_BUILD_FOR_WIN32
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool plBackgroundDownloader::IDecompressSound(plManifestFile* mfsFile, bool noOverwrite)
|
||||
{
|
||||
UInt32 flags = mfsFile->GetFlags();
|
||||
|
||||
if ( (hsCheckBits(flags, plManifestFile::kSndFlagCacheSplit) || hsCheckBits(flags, plManifestFile::kSndFlagCacheStereo)) && stricmp(plFileUtils::GetFileExt(mfsFile->GetName()), "ogg") == 0)
|
||||
{
|
||||
plAudioFileReader* reader = plAudioFileReader::CreateReader(mfsFile->GetName(), plAudioCore::kAll, plAudioFileReader::kStreamNative);
|
||||
if (!reader)
|
||||
{
|
||||
BackgroundDownloaderLog(kInfo, "Unable to create audio file reader for %s", mfsFile->GetName());
|
||||
return false;
|
||||
}
|
||||
|
||||
UInt32 size = reader->GetDataSize();
|
||||
delete reader;
|
||||
|
||||
// Make sure we have enough free space
|
||||
if (!CheckFreeSpace(size))
|
||||
return false;
|
||||
|
||||
if (hsCheckBits(flags, plManifestFile::kSndFlagCacheSplit))
|
||||
plAudioFileReader::CacheFile(mfsFile->GetName(), true, noOverwrite);
|
||||
if (hsCheckBits(flags, plManifestFile::kSndFlagCacheStereo))
|
||||
plAudioFileReader::CacheFile(mfsFile->GetName(), false, noOverwrite);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool plBackgroundDownloader::Run()
|
||||
{
|
||||
// Wait to be signaled that we've gotten at least as far as the startup age
|
||||
WaitForSingleObject(fBGDownloaderRun, INFINITE);
|
||||
|
||||
IGetDataManifest();
|
||||
|
||||
plFileUtils::CreateDir("dat");
|
||||
plFileUtils::CreateDir("sfx");
|
||||
|
||||
bool result = true;
|
||||
plResManager* resMgr = ((plResManager*)hsgResMgr::ResMgr());
|
||||
|
||||
for (MfsFileVec::iterator i = fMfsVec.begin(); i != fMfsVec.end(); ++i)
|
||||
{
|
||||
plManifestFile* mfsFile = (*i);
|
||||
|
||||
if (!mfsFile->IsLocalUpToDate())
|
||||
{
|
||||
if (!CheckFreeSpace(mfsFile->GetDiskSize()))
|
||||
return false;
|
||||
|
||||
FileType type = IGetFile(mfsFile);
|
||||
if (type == kPrp)
|
||||
{
|
||||
// Checks for existence before attempting to remove
|
||||
resMgr->RemoveSinglePage(mfsFile->GetName());
|
||||
if (!resMgr->FindSinglePage(mfsFile->GetName()))
|
||||
{
|
||||
resMgr->AddSinglePage(mfsFile->GetName());
|
||||
}
|
||||
}
|
||||
else if (type == kOther)
|
||||
{
|
||||
if (!IDecompressSound(mfsFile, false))
|
||||
{
|
||||
char text[MAX_PATH];
|
||||
StrPrintf(text, arrsize(text), "%s could not be decompressed", mfsFile->GetName());
|
||||
BackgroundDownloaderLog(kInfo, text );
|
||||
hsAssert(false, text);
|
||||
result = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
char text[MAX_PATH];
|
||||
StrPrintf(text, arrsize(text), "Failed downloading file: %s", mfsFile->GetName());
|
||||
BackgroundDownloaderLog(kInfo, text );
|
||||
hsAssert(false, text);
|
||||
result = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void plBackgroundDownloader::CleanUp()
|
||||
{
|
||||
BackgroundDownloaderLog(kMajorStatus, "Cleaning up background downloader..." );
|
||||
|
||||
for (MfsFileVec::iterator i = fMfsVec.begin(); i != fMfsVec.end(); ++i)
|
||||
{
|
||||
plManifestFile* file = (*i);
|
||||
delete file;
|
||||
}
|
||||
fMfsVec.clear();
|
||||
}
|
||||
|
||||
void plBackgroundDownloader::Pause()
|
||||
{
|
||||
if (fBGDownloaderRun != NULL && fBGDownloaderIsPaused != NULL)
|
||||
{
|
||||
ResetEvent(fBGDownloaderRun);
|
||||
WaitForSingleObject(fBGDownloaderIsPaused, INFINITE);
|
||||
|
||||
BackgroundDownloaderLog(kStatus, "--- Background download paused ---");
|
||||
}
|
||||
}
|
||||
|
||||
void plBackgroundDownloader::UnPause()
|
||||
{
|
||||
if (fBGDownloaderRun != NULL && fBGDownloaderIsPaused != NULL)
|
||||
{
|
||||
SetEvent(fBGDownloaderRun);
|
||||
|
||||
BackgroundDownloaderLog(kStatus, "--- Background download resumed ---");
|
||||
}
|
||||
}
|
||||
|
||||
plBackgroundDownloader::FileType plBackgroundDownloader::IGetFile(const plManifestFile* mfsFile)
|
||||
{
|
||||
BackgroundDownloaderLog(kInfo, " Setting up to download file %s", mfsFile->GetName());
|
||||
|
||||
bool downloadDone = false;
|
||||
wchar* wServerPath = hsStringToWString(mfsFile->GetServerPath());
|
||||
int numTries = 0;
|
||||
|
||||
while (!downloadDone)
|
||||
{
|
||||
if (WaitForSingleObject(fBGDownloaderRun, 0) == WAIT_TIMEOUT)
|
||||
SignalObjectAndWait(fBGDownloaderIsPaused, fBGDownloaderRun, INFINITE, FALSE);
|
||||
|
||||
if (numTries >= kMaxDownloadTries)
|
||||
{
|
||||
BackgroundDownloaderLog(kInfo, " Max download tries exceeded (%d). Aborting download...", kMaxDownloadTries);
|
||||
return kFail;
|
||||
}
|
||||
|
||||
plBGDownloadStream* downloadStream = TRACKED_NEW plBGDownloadStream();
|
||||
if (!downloadStream->Open(mfsFile->GetName(), "wb"))
|
||||
{
|
||||
BackgroundDownloaderLog(kInfo, " Unable to create file. Aborting download...");
|
||||
return kFail;
|
||||
}
|
||||
|
||||
BackgroundDownloaderLog(kInfo, " Downloading file %s...", mfsFile->GetName());
|
||||
|
||||
fSuccess = false;
|
||||
fDoneWithFile = false;
|
||||
NetCliFileDownloadRequest(
|
||||
wServerPath,
|
||||
downloadStream,
|
||||
DownloadFileCallback,
|
||||
this
|
||||
);
|
||||
|
||||
while (!fDoneWithFile) {
|
||||
AsyncSleep(100);
|
||||
}
|
||||
|
||||
if (!fSuccess) {
|
||||
// remove partial file and die (server didn't have the file or server is shutting down)
|
||||
plFileUtils::RemoveFile(mfsFile->GetName(), true);
|
||||
BackgroundDownloaderLog(kError, " File %s failed to download.", mfsFile->GetName());
|
||||
}
|
||||
else {
|
||||
AsyncSleep(100);
|
||||
if (downloadStream->DecompressedOk()) {
|
||||
BackgroundDownloaderLog(kInfo, " Decompress successful." );
|
||||
// download and decompress successful, do a md5 check on the resulting file
|
||||
plMD5Checksum localMD5(mfsFile->GetName());
|
||||
if (localMD5 != mfsFile->GetChecksum()) {
|
||||
plFileUtils::RemoveFile(mfsFile->GetName(), true);
|
||||
BackgroundDownloaderLog(kError, " File %s MD5 check FAILED.", mfsFile->GetName());
|
||||
// don't set downloadDone so we attempt to re-download from the server
|
||||
}
|
||||
else {
|
||||
BackgroundDownloaderLog(kInfo, " MD5 check succeeded.");
|
||||
downloadDone = true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
plFileUtils::RemoveFile(mfsFile->GetName(), true);
|
||||
BackgroundDownloaderLog(kError, " File %s failed to decompress.", mfsFile->GetName());
|
||||
// don't set downloadDone so we attempt to re-download from the server
|
||||
}
|
||||
}
|
||||
|
||||
delete downloadStream;
|
||||
|
||||
++numTries;
|
||||
}
|
||||
delete [] wServerPath;
|
||||
|
||||
if (!fSuccess)
|
||||
return kFail;
|
||||
|
||||
if (stricmp(plFileUtils::GetFileExt(mfsFile->GetName()), "prp") == 0)
|
||||
return kPrp;
|
||||
|
||||
return kOther;
|
||||
}
|
||||
|
||||
bool plBackgroundDownloader::IGetDataManifest()
|
||||
{
|
||||
if (fMfsVec.size() > 0)
|
||||
return true;
|
||||
|
||||
BackgroundDownloaderLog(kMajorStatus, "Downloading new manifest from data server..." );
|
||||
|
||||
fSuccess = false;
|
||||
unsigned numTries = 0;
|
||||
while (!fSuccess)
|
||||
{
|
||||
numTries++;
|
||||
fDoneWithFile = false;
|
||||
NetCliFileManifestRequest(ManifestCallback, this, s_manifest);
|
||||
while (!fDoneWithFile)
|
||||
{
|
||||
NetClientUpdate();
|
||||
plgDispatch::Dispatch()->MsgQueueProcess();
|
||||
AsyncSleep(10);
|
||||
}
|
||||
|
||||
if (!fSuccess)
|
||||
{
|
||||
fMfsVec.clear(); // clear out any bad data
|
||||
if (numTries > kMaxDownloadTries)
|
||||
break; // abort
|
||||
}
|
||||
}
|
||||
|
||||
if (fSuccess)
|
||||
BackgroundDownloaderLog(kStatus, "New manifest read; number of files: %d", fMfsVec.size() );
|
||||
else
|
||||
BackgroundDownloaderLog(kStatus, "Failed to download manifest after trying %d times", kMaxDownloadTries);
|
||||
|
||||
return fSuccess;
|
||||
}
|
||||
|
||||
void plBackgroundDownloader::DoneWithFile(bool success)
|
||||
{
|
||||
fDoneWithFile = true;
|
||||
fSuccess = success;
|
||||
}
|
||||
|
||||
void plBackgroundDownloader::DoneWithManifest(bool success, const NetCliFileManifestEntry manifestEntires[], unsigned entryCount)
|
||||
{
|
||||
BackgroundDownloaderLog(kStatus, "New age manifest received. Reading...");
|
||||
|
||||
if (success)
|
||||
{
|
||||
for (unsigned i = 0; i < entryCount; i++)
|
||||
{
|
||||
char* name = hsWStringToString(manifestEntires[i].clientName);
|
||||
char* serverPath = hsWStringToString(manifestEntires[i].downloadName);
|
||||
char* md5Str = hsWStringToString(manifestEntires[i].md5);
|
||||
int size = manifestEntires[i].fileSize;
|
||||
int zipsize = manifestEntires[i].zipSize;
|
||||
int flags = manifestEntires[i].flags;
|
||||
if (stricmp(plFileUtils::GetFileExt(name), "gz"))
|
||||
flags |= plManifestFile::kFlagZipped; // add zipped flag if necessary
|
||||
|
||||
plMD5Checksum sum;
|
||||
sum.SetFromHexString(md5Str);
|
||||
fMfsVec.push_back(TRACKED_NEW plManifestFile(name, serverPath, sum, size, zipsize, flags, false));
|
||||
|
||||
delete [] name;
|
||||
delete [] serverPath;
|
||||
delete [] md5Str;
|
||||
}
|
||||
}
|
||||
|
||||
fDoneWithFile = true;
|
||||
fSuccess = success;
|
||||
}
|
@ -0,0 +1,98 @@
|
||||
/*==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==*/
|
||||
#ifndef plBackgroundDownloader_h_inc
|
||||
#define plBackgroundDownloader_h_inc
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "hsTypes.h"
|
||||
|
||||
class plManifestFile;
|
||||
struct NetCliFileManifestEntry;
|
||||
|
||||
class plBackgroundDownloader
|
||||
{
|
||||
protected:
|
||||
static plBackgroundDownloader* fInstance;
|
||||
static void Init();
|
||||
static void Shutdown();
|
||||
static void ThreadMain(void * param);
|
||||
|
||||
plBackgroundDownloader();
|
||||
~plBackgroundDownloader();
|
||||
|
||||
HANDLE fBGDownloaderRun;
|
||||
HANDLE fBGDownloaderIsPaused;
|
||||
|
||||
enum FileType {kFail, kPrp, kOther};
|
||||
|
||||
typedef std::vector<plManifestFile*> MfsFileVec;
|
||||
MfsFileVec fMfsVec;
|
||||
|
||||
public:
|
||||
|
||||
bool fDoneWithFile;
|
||||
bool fSuccess;
|
||||
|
||||
bool IGetDataManifest();
|
||||
FileType IGetFile(const plManifestFile* mfsFile);
|
||||
UInt32 IGetDownloadSize();
|
||||
bool IDecompressSound(plManifestFile* mfsFile, bool noOverwrite = false);
|
||||
|
||||
public:
|
||||
static plBackgroundDownloader* GetInstance();
|
||||
static void StartThread();
|
||||
|
||||
bool Run();
|
||||
void CleanUp();
|
||||
|
||||
void Pause();
|
||||
void UnPause();
|
||||
|
||||
static bool CheckFreeSpace(UInt32 bytesNeeded);
|
||||
|
||||
// called by download callbacks to tell it we are done with the current file
|
||||
void DoneWithFile(bool success);
|
||||
void DoneWithManifest(bool success, const NetCliFileManifestEntry manifestEntires[], unsigned entryCount);
|
||||
};
|
||||
|
||||
#endif //plBackgroundDownloader_h_inc
|
518
Sources/Plasma/PubUtilLib/plAgeLoader/plResPatcher.cpp
Normal file
518
Sources/Plasma/PubUtilLib/plAgeLoader/plResPatcher.cpp
Normal file
@ -0,0 +1,518 @@
|
||||
/*==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 "plResPatcher.h"
|
||||
|
||||
#include "../NucleusLib/inc/hsResMgr.h"
|
||||
|
||||
#include "../plAgeDescription/plAgeManifest.h"
|
||||
#include "../plResMgr/plResManager.h"
|
||||
#include "../plFile/plFileUtils.h"
|
||||
#include "../plFile/plEncryptedStream.h"
|
||||
#include "../plCompression/plZlibStream.h"
|
||||
#include "../plAudioCore/plAudioFileReader.h"
|
||||
#include "../plProgressMgr/plProgressMgr.h"
|
||||
|
||||
#include "../pnAsyncCore/pnAsyncCore.h"
|
||||
#include "../pnNetCli/pnNetCli.h"
|
||||
#include "../plNetGameLib/plNetGameLib.h"
|
||||
|
||||
#include "../pnDispatch/plDispatch.h"
|
||||
#include "../plStatusLog/plStatusLog.h"
|
||||
|
||||
static const unsigned kMaxDownloadTries = 10;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class plDownloadStream : public plZlibStream
|
||||
{
|
||||
private:
|
||||
plOperationProgress* fProgress;
|
||||
unsigned fBytesReceived;
|
||||
public:
|
||||
plDownloadStream(plOperationProgress* progress) : fProgress(progress), fBytesReceived(0), plZlibStream() {}
|
||||
virtual ~plDownloadStream() {}
|
||||
|
||||
virtual UInt32 Write(UInt32 byteCount, const void* buffer);
|
||||
|
||||
void RewindProgress() {fProgress->Increment(-(hsScalar)fBytesReceived);} // rewind the progress bar by as far as we got
|
||||
};
|
||||
|
||||
UInt32 plDownloadStream::Write(UInt32 byteCount, const void* buffer)
|
||||
{
|
||||
fProgress->Increment((hsScalar)byteCount);
|
||||
fBytesReceived += byteCount;
|
||||
|
||||
return plZlibStream::Write(byteCount, buffer);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static void DownloadFileCallback(ENetError result, void* param, const wchar filename[], hsStream* writer)
|
||||
{
|
||||
plResPatcher* patcher = (plResPatcher*)param;
|
||||
|
||||
// Retry download unless shutting down or file not found
|
||||
switch (result) {
|
||||
case kNetSuccess:
|
||||
writer->Close();
|
||||
patcher->DoneWithFile(true);
|
||||
break;
|
||||
|
||||
case kNetErrFileNotFound:
|
||||
case kNetErrRemoteShutdown:
|
||||
writer->Close();
|
||||
patcher->DoneWithFile(false);
|
||||
break;
|
||||
|
||||
default:
|
||||
writer->Rewind();
|
||||
NetCliFileDownloadRequest(
|
||||
filename,
|
||||
writer,
|
||||
DownloadFileCallback,
|
||||
param
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void ManifestCallback(ENetError result, void* param, const wchar group[], const NetCliFileManifestEntry manifest[], unsigned entryCount)
|
||||
{
|
||||
plResPatcher* patcher = (plResPatcher*)param;
|
||||
patcher->DoneWithManifest(result == kNetSuccess, manifest, entryCount);
|
||||
}
|
||||
|
||||
//// Constructor/Destructor //////////////////////////////////////////////////
|
||||
|
||||
plResPatcher::plResPatcher(const char* ageToPatch, bool showAgeName)
|
||||
{
|
||||
fAgeToPatch = ageToPatch;
|
||||
fAlwaysShowAgeName = showAgeName;
|
||||
IInit();
|
||||
}
|
||||
|
||||
void plResPatcher::IInit()
|
||||
{
|
||||
PatcherLog(kHeader, "--- Starting patch process for %s ---", fAgeToPatch.c_str());
|
||||
}
|
||||
|
||||
plResPatcher::~plResPatcher()
|
||||
{
|
||||
PatcherLog(kHeader, "--- Patch process done for %s ---", fAgeToPatch.c_str());
|
||||
|
||||
for (MfsFileVec::iterator i = fMfsVec.begin(); i != fMfsVec.end(); ++i)
|
||||
{
|
||||
plManifestFile* file = (*i);
|
||||
delete file;
|
||||
}
|
||||
fMfsVec.clear();
|
||||
}
|
||||
|
||||
UInt32 plResPatcher::IGetDownloadSize()
|
||||
{
|
||||
if (!IGetAgeManifest())
|
||||
return 0;
|
||||
|
||||
#ifdef PLASMA_EXTERNAL_RELEASE
|
||||
bool showAgeName = fAlwaysShowAgeName;
|
||||
#else
|
||||
bool showAgeName = true;
|
||||
#endif
|
||||
|
||||
char msg[128];
|
||||
if (!fAgeToPatch.empty())
|
||||
{
|
||||
if (showAgeName)
|
||||
sprintf(msg, "Checking age %s...", fAgeToPatch.c_str());
|
||||
else
|
||||
strcpy(msg, "Checking age...");
|
||||
}
|
||||
else
|
||||
sprintf(msg, "Checking...");
|
||||
|
||||
plOperationProgress* progress = plProgressMgr::GetInstance()->RegisterOperation((hsScalar)(fMfsVec.size()), msg, plProgressMgr::kNone, false, true);
|
||||
|
||||
UInt32 downloadSize = 0;
|
||||
UInt32 downloadFiles = 0;
|
||||
for (MfsFileVec::iterator i = fMfsVec.begin(); i != fMfsVec.end(); ++i)
|
||||
{
|
||||
plManifestFile* mfsFile = (*i);
|
||||
|
||||
if (!mfsFile->IsLocalUpToDate())
|
||||
{
|
||||
downloadFiles++;
|
||||
downloadSize += mfsFile->GetDownloadSize();
|
||||
}
|
||||
|
||||
progress->Increment(1.f);
|
||||
}
|
||||
|
||||
delete progress;
|
||||
|
||||
PatcherLog(kInfo, "Got download stats, %d files, %d bytes", downloadFiles, downloadSize);
|
||||
|
||||
return downloadSize;
|
||||
}
|
||||
|
||||
bool plResPatcher::CheckFreeSpace(UInt32 bytesNeeded)
|
||||
{
|
||||
#ifdef HS_BUILD_FOR_WIN32
|
||||
ULARGE_INTEGER freeBytesAvailable, totalNumberOfBytes, neededBytes;
|
||||
if (GetDiskFreeSpaceEx(NULL, &freeBytesAvailable, &totalNumberOfBytes, NULL))
|
||||
{
|
||||
neededBytes.HighPart = 0;
|
||||
neededBytes.LowPart = bytesNeeded;
|
||||
|
||||
if (neededBytes.QuadPart > freeBytesAvailable.QuadPart)
|
||||
{
|
||||
PatcherLog(kInfo, "Not enough disk space (asked for %d bytes)", bytesNeeded);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#endif // HS_BUILD_FOR_WIN32
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool plResPatcher::IDecompressSound(plManifestFile* mfsFile, bool noOverwrite)
|
||||
{
|
||||
UInt32 flags = mfsFile->GetFlags();
|
||||
|
||||
if ( (hsCheckBits(flags, plManifestFile::kSndFlagCacheSplit) || hsCheckBits(flags, plManifestFile::kSndFlagCacheStereo)) && stricmp(plFileUtils::GetFileExt(mfsFile->GetName()), "ogg") == 0)
|
||||
{
|
||||
plAudioFileReader* reader = plAudioFileReader::CreateReader(mfsFile->GetName(), plAudioCore::kAll, plAudioFileReader::kStreamNative);
|
||||
if (!reader)
|
||||
{
|
||||
PatcherLog(kInfo, "Unable to create audio file reader for %s", mfsFile->GetName());
|
||||
return false;
|
||||
}
|
||||
|
||||
UInt32 size = reader->GetDataSize();
|
||||
delete reader;
|
||||
|
||||
// Make sure we have enough free space
|
||||
if (!CheckFreeSpace(size))
|
||||
return false;
|
||||
|
||||
if (hsCheckBits(flags, plManifestFile::kSndFlagCacheSplit))
|
||||
plAudioFileReader::CacheFile(mfsFile->GetName(), true, noOverwrite);
|
||||
if (hsCheckBits(flags, plManifestFile::kSndFlagCacheStereo))
|
||||
plAudioFileReader::CacheFile(mfsFile->GetName(), false, noOverwrite);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool plResPatcher::Update()
|
||||
{
|
||||
UInt32 downloadSize = IGetDownloadSize();
|
||||
// if download size is 0, nothing to download, but we still need to tell the res manager about the files
|
||||
|
||||
plFileUtils::CreateDir("dat");
|
||||
plFileUtils::CreateDir("sfx");
|
||||
|
||||
if (!CheckFreeSpace(downloadSize))
|
||||
return false;
|
||||
|
||||
#ifdef PLASMA_EXTERNAL_RELEASE
|
||||
bool showAgeName = fAlwaysShowAgeName;
|
||||
#else
|
||||
bool showAgeName = true;
|
||||
#endif
|
||||
|
||||
char msg[128];
|
||||
if (!fAgeToPatch.empty())
|
||||
{
|
||||
if (showAgeName)
|
||||
sprintf(msg, "Downloading %s data...", fAgeToPatch.c_str());
|
||||
else
|
||||
strcpy(msg, "Downloading age data...");
|
||||
}
|
||||
else
|
||||
sprintf(msg, "Downloading...");
|
||||
|
||||
plOperationProgress* progress = plProgressMgr::GetInstance()->RegisterOverallOperation((hsScalar)downloadSize, msg, plProgressMgr::kUpdateText, true);
|
||||
|
||||
bool result = true;
|
||||
plResManager* resMgr = ((plResManager*)hsgResMgr::ResMgr());
|
||||
|
||||
for (MfsFileVec::iterator i = fMfsVec.begin(); i != fMfsVec.end(); ++i)
|
||||
{
|
||||
plManifestFile* mfsFile = (*i);
|
||||
|
||||
if (!mfsFile->IsLocalUpToDate())
|
||||
{
|
||||
FileType type = IGetFile(mfsFile, progress);
|
||||
if (type == kPrp)
|
||||
{
|
||||
// Checks for existence before attempting to remove
|
||||
resMgr->RemoveSinglePage(mfsFile->GetName());
|
||||
}
|
||||
else if (type == kOther)
|
||||
{
|
||||
if (!IDecompressSound(mfsFile, false))
|
||||
{
|
||||
char text[MAX_PATH];
|
||||
StrPrintf(text, arrsize(text), "%s could not be decompressed", mfsFile->GetName());
|
||||
PatcherLog(kInfo, text );
|
||||
hsAssert(false, text);
|
||||
result = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
char text[MAX_PATH];
|
||||
StrPrintf(text, arrsize(text), "Failed downloading file: %s", mfsFile->GetName());
|
||||
PatcherLog(kInfo, text );
|
||||
hsAssert(false, text);
|
||||
result = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!IDecompressSound(mfsFile, true))
|
||||
{
|
||||
char text[MAX_PATH];
|
||||
StrPrintf(text, arrsize(text), "%s could not be decompressed", mfsFile->GetName());
|
||||
PatcherLog(kInfo, text );
|
||||
hsAssert(false, text);
|
||||
result = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!resMgr->FindSinglePage(mfsFile->GetName()) && stricmp(plFileUtils::GetFileExt(mfsFile->GetName()), "prp") == 0)
|
||||
{
|
||||
resMgr->AddSinglePage(mfsFile->GetName());
|
||||
}
|
||||
}
|
||||
|
||||
PatcherLog(kMajorStatus, "Cleaning up patcher..." );
|
||||
delete progress;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
plResPatcher::FileType plResPatcher::IGetFile(const plManifestFile* mfsFile, plOperationProgress* progressBar)
|
||||
{
|
||||
PatcherLog(kInfo, " Setting up to download file %s", mfsFile->GetName());
|
||||
|
||||
bool downloadDone = false;
|
||||
wchar* wServerPath = hsStringToWString(mfsFile->GetServerPath());
|
||||
int numTries = 0;
|
||||
|
||||
while (!downloadDone)
|
||||
{
|
||||
if (numTries >= kMaxDownloadTries)
|
||||
{
|
||||
PatcherLog(kInfo, " Max download tries exceeded (%d). Aborting download...", kMaxDownloadTries);
|
||||
return kFail;
|
||||
}
|
||||
|
||||
plDownloadStream downloadStream(progressBar);
|
||||
if (!downloadStream.Open(mfsFile->GetName(), "wb"))
|
||||
{
|
||||
PatcherLog(kInfo, " Unable to create file. Aborting download...");
|
||||
return kFail;
|
||||
}
|
||||
|
||||
PatcherLog(kInfo, " Downloading file %s...", mfsFile->GetName());
|
||||
|
||||
fSuccess = false;
|
||||
fDoneWithFile = false;
|
||||
NetCliFileDownloadRequest(
|
||||
wServerPath,
|
||||
&downloadStream,
|
||||
DownloadFileCallback,
|
||||
this
|
||||
);
|
||||
|
||||
while (!fDoneWithFile) {
|
||||
NetClientUpdate();
|
||||
plgDispatch::Dispatch()->MsgQueueProcess();
|
||||
AsyncSleep(10);
|
||||
}
|
||||
|
||||
if (!fSuccess) {
|
||||
// remove partial file and die (server didn't have the file or server is shutting down)
|
||||
downloadStream.RewindProgress();
|
||||
plFileUtils::RemoveFile(mfsFile->GetName(), true);
|
||||
PatcherLog(kError, " File %s failed to download.", mfsFile->GetName());
|
||||
downloadDone = true;
|
||||
}
|
||||
else {
|
||||
if (downloadStream.DecompressedOk()) {
|
||||
PatcherLog(kInfo, " Decompress successful." );
|
||||
// download and decompress successful, do a md5 check on the resulting file
|
||||
plMD5Checksum localMD5(mfsFile->GetName());
|
||||
if (localMD5 != mfsFile->GetChecksum()) {
|
||||
downloadStream.RewindProgress();
|
||||
downloadStream.Close();
|
||||
plFileUtils::RemoveFile(mfsFile->GetName(), true);
|
||||
PatcherLog(kError, " File %s MD5 check FAILED.", mfsFile->GetName());
|
||||
// don't set downloadDone so we attempt to re-download from the server
|
||||
}
|
||||
else {
|
||||
downloadStream.Close();
|
||||
PatcherLog(kInfo, " MD5 check succeeded.");
|
||||
downloadDone = true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
downloadStream.RewindProgress();
|
||||
downloadStream.Close();
|
||||
plFileUtils::RemoveFile(mfsFile->GetName(), true);
|
||||
PatcherLog(kError, " File %s failed to decompress.", mfsFile->GetName());
|
||||
// don't set downloadDone so we attempt to re-download from the server
|
||||
}
|
||||
}
|
||||
++numTries;
|
||||
}
|
||||
FREE(wServerPath);
|
||||
|
||||
if (!fSuccess)
|
||||
return kFail;
|
||||
|
||||
if (stricmp(plFileUtils::GetFileExt(mfsFile->GetName()), "prp") == 0)
|
||||
return kPrp;
|
||||
|
||||
return kOther;
|
||||
}
|
||||
|
||||
bool plResPatcher::IGetAgeManifest()
|
||||
{
|
||||
if (fMfsVec.size() > 0)
|
||||
return true;
|
||||
|
||||
PatcherLog(kMajorStatus, "Downloading new manifest from data server..." );
|
||||
|
||||
fSuccess = false;
|
||||
wchar* group = hsStringToWString(fAgeToPatch.c_str());
|
||||
unsigned numTries = 0;
|
||||
while (!fSuccess)
|
||||
{
|
||||
numTries++;
|
||||
fDoneWithFile = false;
|
||||
NetCliFileManifestRequest(ManifestCallback, this, group);
|
||||
while (!fDoneWithFile)
|
||||
{
|
||||
NetClientUpdate();
|
||||
plgDispatch::Dispatch()->MsgQueueProcess();
|
||||
AsyncSleep(10);
|
||||
}
|
||||
|
||||
if (!fSuccess)
|
||||
{
|
||||
fMfsVec.clear(); // clear out any bad data
|
||||
if (numTries > kMaxDownloadTries)
|
||||
break; // abort
|
||||
}
|
||||
}
|
||||
delete [] group;
|
||||
|
||||
if (fSuccess)
|
||||
PatcherLog(kStatus, "New age manifest read; number of files: %d", fMfsVec.size() );
|
||||
else
|
||||
PatcherLog(kStatus, "Failed to download manifest after trying %d times", kMaxDownloadTries);
|
||||
|
||||
return fSuccess;
|
||||
}
|
||||
|
||||
void plResPatcher::DoneWithManifest(bool success, const NetCliFileManifestEntry manifestEntires[], unsigned entryCount)
|
||||
{
|
||||
PatcherLog(kStatus, "New age manifest received. Reading...");
|
||||
|
||||
if (success)
|
||||
{
|
||||
for (unsigned i = 0; i < entryCount; i++)
|
||||
{
|
||||
char* name = hsWStringToString(manifestEntires[i].clientName);
|
||||
char* serverPath = hsWStringToString(manifestEntires[i].downloadName);
|
||||
char* md5Str = hsWStringToString(manifestEntires[i].md5);
|
||||
int size = manifestEntires[i].fileSize;
|
||||
int zipsize = manifestEntires[i].zipSize;
|
||||
int flags = manifestEntires[i].flags;
|
||||
if (stricmp(plFileUtils::GetFileExt(name), "gz"))
|
||||
flags |= plManifestFile::kFlagZipped; // add zipped flag if necessary
|
||||
|
||||
plMD5Checksum sum;
|
||||
sum.SetFromHexString(md5Str);
|
||||
fMfsVec.push_back(TRACKED_NEW plManifestFile(name, serverPath, sum, size, zipsize, flags));
|
||||
|
||||
delete [] name;
|
||||
delete [] serverPath;
|
||||
delete [] md5Str;
|
||||
}
|
||||
}
|
||||
|
||||
fDoneWithFile = true;
|
||||
fSuccess = success;
|
||||
}
|
||||
|
||||
void PatcherLog(PatcherLogType type, const char* format, ...)
|
||||
{
|
||||
UInt32 color = 0;
|
||||
switch (type)
|
||||
{
|
||||
case kHeader: color = plStatusLog::kWhite; break;
|
||||
case kInfo: color = plStatusLog::kBlue; break;
|
||||
case kMajorStatus: color = plStatusLog::kYellow; break;
|
||||
case kStatus: color = plStatusLog::kGreen; break;
|
||||
case kError: color = plStatusLog::kRed; break;
|
||||
}
|
||||
|
||||
static plStatusLog* gStatusLog = nil;
|
||||
if (!gStatusLog)
|
||||
{
|
||||
gStatusLog = plStatusLogMgr::GetInstance().CreateStatusLog(
|
||||
20,
|
||||
"patcher.log",
|
||||
plStatusLog::kFilledBackground | plStatusLog::kAlignToTop | plStatusLog::kDeleteForMe);
|
||||
}
|
||||
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
|
||||
gStatusLog->AddLineV(color, format, args);
|
||||
|
||||
va_end(args);
|
||||
}
|
103
Sources/Plasma/PubUtilLib/plAgeLoader/plResPatcher.h
Normal file
103
Sources/Plasma/PubUtilLib/plAgeLoader/plResPatcher.h
Normal file
@ -0,0 +1,103 @@
|
||||
/*==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==*/
|
||||
#ifndef plResPatcher_h_inc
|
||||
#define plResPatcher_h_inc
|
||||
|
||||
#include "hsStlUtils.h"
|
||||
|
||||
#include "../pnUtils/pnUtils.h"
|
||||
#include "../pnNetBase/pnNetBase.h"
|
||||
#include "../plEncryption/plChecksum.h"
|
||||
|
||||
|
||||
class plManifest;
|
||||
class plManifestFile;
|
||||
class plOperationProgress;
|
||||
struct NetCliFileManifestEntry;
|
||||
|
||||
class plResPatcher
|
||||
{
|
||||
protected:
|
||||
enum FileType {kFail, kPrp, kOther};
|
||||
std::string fAgeToPatch;
|
||||
|
||||
typedef std::vector<plManifestFile*> MfsFileVec;
|
||||
MfsFileVec fMfsVec;
|
||||
|
||||
bool fDoneWithFile;
|
||||
bool fSuccess;
|
||||
bool fAlwaysShowAgeName;
|
||||
|
||||
void IInit();
|
||||
static void ILog(UInt32 type, const char* format, ...);
|
||||
|
||||
FileType IGetFile(const plManifestFile* mfsFile, plOperationProgress* progressBar);
|
||||
bool IGetAgeManifest();
|
||||
|
||||
UInt32 IGetDownloadSize();
|
||||
|
||||
bool IDecompressSound(plManifestFile* mfsFile, bool noOverwrite = false);
|
||||
|
||||
public:
|
||||
plResPatcher(const char* ageToPatch, bool showAgeName = false);
|
||||
~plResPatcher();
|
||||
|
||||
bool Update();
|
||||
|
||||
static bool CheckFreeSpace(UInt32 bytesNeeded);
|
||||
|
||||
// called by download callbacks to tell it we are done with the current file
|
||||
void DoneWithFile(bool success) {fDoneWithFile = true; fSuccess = success;}
|
||||
void DoneWithManifest(bool success, const NetCliFileManifestEntry manifestEntires[], unsigned entryCount);
|
||||
};
|
||||
|
||||
enum PatcherLogType
|
||||
{
|
||||
kHeader,
|
||||
kInfo,
|
||||
kMajorStatus,
|
||||
kStatus,
|
||||
kError,
|
||||
};
|
||||
void PatcherLog(PatcherLogType type, const char* format, ...);
|
||||
|
||||
#endif // _plResPatcher_h
|
Reference in New Issue
Block a user