mirror of
https://github.com/SWG-Source/src.git
synced 2026-07-29 23:15:56 -04:00
Added sharedFile library
This commit is contained in:
@@ -0,0 +1,11 @@
|
||||
cmake_minimum_required(VERSION 2.8)
|
||||
|
||||
project(sharedFile)
|
||||
|
||||
if(WIN32)
|
||||
add_definitions(/D_CRT_SECURE_NO_WARNINGS)
|
||||
endif()
|
||||
|
||||
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include/public)
|
||||
|
||||
add_subdirectory(src)
|
||||
@@ -0,0 +1 @@
|
||||
#include "../../src/shared/AsynchronousLoader.h"
|
||||
@@ -0,0 +1 @@
|
||||
#include "../../src/shared/ConfigSharedFile.h"
|
||||
@@ -0,0 +1 @@
|
||||
#include "../../src/shared/FileManifest.h"
|
||||
@@ -0,0 +1 @@
|
||||
#include "../../src/shared/FileNameUtils.h"
|
||||
@@ -0,0 +1 @@
|
||||
#include "../../src/shared/FileStreamer.h"
|
||||
@@ -0,0 +1 @@
|
||||
#include "../../src/shared/FileStreamerFile.h"
|
||||
@@ -0,0 +1 @@
|
||||
#include "../../src/shared/FileStreamerThread.h"
|
||||
@@ -0,0 +1 @@
|
||||
#include "../../src/shared/FirstSharedFile.h"
|
||||
@@ -0,0 +1 @@
|
||||
#include "../../src/shared/Iff.h"
|
||||
@@ -0,0 +1 @@
|
||||
#include "../../src/shared/IndentedFileWriter.h"
|
||||
@@ -0,0 +1 @@
|
||||
#include "../../src/shared/MemoryFile.h"
|
||||
@@ -0,0 +1,7 @@
|
||||
#if defined(PLATFORM_WIN32)
|
||||
#include "../../src/win32/OsFile.h"
|
||||
#elif defined(PLATFORM_LINUX)
|
||||
#include "../../src/linux/OsFile.h"
|
||||
#else
|
||||
#error unsupported platform
|
||||
#endif
|
||||
@@ -0,0 +1 @@
|
||||
#include "../../src/shared/SetupSharedFile.h"
|
||||
@@ -0,0 +1 @@
|
||||
#include "../../src/shared/TreeFile.h"
|
||||
@@ -0,0 +1 @@
|
||||
#include "../../src/shared/TreeFile_SearchNode.h"
|
||||
@@ -0,0 +1 @@
|
||||
#include "../../src/shared/ZlibFile.h"
|
||||
@@ -0,0 +1,60 @@
|
||||
|
||||
set(SHARED_SOURCES
|
||||
shared/AsynchronousLoader.cpp
|
||||
shared/AsynchronousLoader.h
|
||||
shared/ConfigSharedFile.cpp
|
||||
shared/ConfigSharedFile.h
|
||||
shared/FileManifest.cpp
|
||||
shared/FileManifest.h
|
||||
shared/FileNameUtils.cpp
|
||||
shared/FileNameUtils.h
|
||||
shared/FileStreamer.cpp
|
||||
shared/FileStreamerFile.cpp
|
||||
shared/FileStreamerFile.h
|
||||
shared/FileStreamer.h
|
||||
shared/FileStreamerThread.cpp
|
||||
shared/FileStreamerThread.h
|
||||
shared/FirstSharedFile.h
|
||||
shared/Iff.cpp
|
||||
shared/Iff.h
|
||||
shared/MemoryFile.cpp
|
||||
shared/MemoryFile.h
|
||||
shared/SetupSharedFile.cpp
|
||||
shared/SetupSharedFile.h
|
||||
shared/TreeFile.cpp
|
||||
shared/TreeFile.h
|
||||
shared/TreeFile_SearchNode.cpp
|
||||
shared/TreeFile_SearchNode.h
|
||||
shared/ZlibFile.cpp
|
||||
shared/ZlibFile.h
|
||||
)
|
||||
|
||||
if(WIN32)
|
||||
set(PLATFORM_SOURCES
|
||||
win32/FirstSharedFile.cpp
|
||||
win32/OsFile.cpp
|
||||
win32/OsFile.h
|
||||
)
|
||||
|
||||
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/win32)
|
||||
else()
|
||||
set(PLATFORM_SOURCES
|
||||
linux/OsFile.cpp
|
||||
linux/OsFile.h
|
||||
)
|
||||
|
||||
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/linux)
|
||||
endif()
|
||||
|
||||
include_directories(
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/shared
|
||||
${SWG_ENGINE_SOURCE_DIR}/shared/library/sharedDebug/include/public
|
||||
${SWG_ENGINE_SOURCE_DIR}/shared/library/sharedFoundation/include/public
|
||||
${SWG_ENGINE_SOURCE_DIR}/shared/library/sharedFoundationTypes/include/public
|
||||
${SWG_ENGINE_SOURCE_DIR}/shared/library/sharedMemoryManager/include/public
|
||||
)
|
||||
|
||||
add_library(sharedFile STATIC
|
||||
${SHARED_SOURCES}
|
||||
${PLATFORM_SOURCES}
|
||||
)
|
||||
@@ -0,0 +1,123 @@
|
||||
// ======================================================================
|
||||
//
|
||||
// OsFile.cpp
|
||||
// Copyright 2002, Sony Online Entertainment Inc.
|
||||
// All Rights Reserved.
|
||||
//
|
||||
// ======================================================================
|
||||
|
||||
#include "sharedFile/FirstSharedFile.h"
|
||||
#include "sharedFile/OsFile.h"
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
// ======================================================================
|
||||
|
||||
void OsFile::install()
|
||||
{
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
bool OsFile::exists(const char *fileName)
|
||||
{
|
||||
struct stat statBuffer;
|
||||
if (stat(fileName, &statBuffer) != 0)
|
||||
return false;
|
||||
|
||||
if (S_ISDIR(statBuffer.st_mode))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
int OsFile::getFileSize(const char *fileName)
|
||||
{
|
||||
struct stat statBuffer;
|
||||
int fileSize = 0;
|
||||
|
||||
if (stat(fileName, &statBuffer) == 0)
|
||||
{
|
||||
fileSize = static_cast<int>(statBuffer.st_size);
|
||||
}
|
||||
return fileSize;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
OsFile *OsFile::open(const char *fileName, bool randomAccess)
|
||||
{
|
||||
UNREF(randomAccess);
|
||||
|
||||
if (!exists(fileName))
|
||||
return 0;
|
||||
|
||||
// attempt to open the file
|
||||
const int handle = ::open(fileName, O_RDONLY);
|
||||
FATAL(handle < 0, ("OsFile::open failed to open file %s, errno=%d, which does exist.", fileName, errno));
|
||||
|
||||
return new OsFile(handle, DuplicateString(fileName));
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
OsFile::OsFile(int handle, char *fileName)
|
||||
:
|
||||
m_handle(handle),
|
||||
m_length(0),
|
||||
m_offset(0),
|
||||
m_fileName(fileName)
|
||||
{
|
||||
m_length = lseek(m_handle, 0, SEEK_END);
|
||||
lseek(m_handle, 0, SEEK_SET);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
OsFile::~OsFile()
|
||||
{
|
||||
close(m_handle);
|
||||
delete [] m_fileName;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
int OsFile::length() const
|
||||
{
|
||||
return m_length;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
void OsFile::seek(int newFilePosition)
|
||||
{
|
||||
if (m_offset != newFilePosition)
|
||||
{
|
||||
const int result = lseek(m_handle, newFilePosition, SEEK_SET);
|
||||
DEBUG_FATAL(result != newFilePosition, ("SetFilePointer failed"));
|
||||
UNREF(result);
|
||||
m_offset = newFilePosition;
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
int OsFile::read(void *destinationBuffer, int numberOfBytes)
|
||||
{
|
||||
int result = 0;
|
||||
do
|
||||
{
|
||||
result = ::read(m_handle, destinationBuffer, numberOfBytes);
|
||||
DEBUG_FATAL((result < 0 && errno != EAGAIN), ("Read failed for %s: %d %d %s", m_fileName, result, errno, strerror(errno)));
|
||||
} while (result < 0);
|
||||
|
||||
m_offset += result;
|
||||
return result;
|
||||
}
|
||||
|
||||
// ======================================================================
|
||||
@@ -0,0 +1,51 @@
|
||||
// ======================================================================
|
||||
//
|
||||
// OsFile.h
|
||||
// Copyright 2002, Sony Online Entertainment Inc.
|
||||
// All Rights Reserved.
|
||||
//
|
||||
// ======================================================================
|
||||
|
||||
#ifndef INCLUDED_OsFile_H
|
||||
#define INCLUDED_OsFile_H
|
||||
|
||||
// ======================================================================
|
||||
|
||||
class OsFile
|
||||
{
|
||||
public:
|
||||
|
||||
static void install();
|
||||
|
||||
static bool exists(const char *fileName);
|
||||
static int getFileSize(const char *fileName);
|
||||
static OsFile *open(const char *fileName, bool randomAccess=false);
|
||||
|
||||
public:
|
||||
|
||||
~OsFile();
|
||||
|
||||
int length() const;
|
||||
int tell() const;
|
||||
void seek(int newFilePosition);
|
||||
int read(void *destinationBuffer, int numberOfBytes);
|
||||
|
||||
private:
|
||||
|
||||
OsFile(int handle, char *fileName);
|
||||
|
||||
OsFile();
|
||||
OsFile(const OsFile &);
|
||||
OsFile &operator =(const OsFile &);
|
||||
|
||||
private:
|
||||
|
||||
int m_handle;
|
||||
int m_length;
|
||||
int m_offset;
|
||||
char *m_fileName;
|
||||
};
|
||||
|
||||
// ======================================================================
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,705 @@
|
||||
//
|
||||
// AsynchronousLoader.cpp
|
||||
// Copyright 2002 Sony Online Entertainment
|
||||
// All Rights Reserved
|
||||
//
|
||||
// ======================================================================
|
||||
|
||||
#include "sharedFile/FirstSharedFile.h"
|
||||
#include "sharedFile/AsynchronousLoader.h"
|
||||
|
||||
#include "sharedDebug/DebugFlags.h"
|
||||
#include "sharedFile/ConfigSharedFile.h"
|
||||
#include "sharedFile/Iff.h"
|
||||
#include "sharedFile/MemoryFile.h"
|
||||
#include "sharedFile/TreeFile.h"
|
||||
#include "sharedFoundation/Clock.h"
|
||||
#include "sharedFoundation/ExitChain.h"
|
||||
#include "sharedFoundation/Os.h"
|
||||
#include "sharedFoundation/MemoryBlockManagerMacros.h"
|
||||
#include "sharedFoundation/MemoryBlockManager.h"
|
||||
#include "sharedSynchronization/Mutex.h"
|
||||
#include "sharedSynchronization/Semaphore.h"
|
||||
#include "sharedThread/RunThread.h"
|
||||
#include "sharedThread/ThreadHandle.h"
|
||||
|
||||
#include <deque>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
#ifdef _DEBUG
|
||||
#include <algorithm>
|
||||
#endif
|
||||
|
||||
// ======================================================================
|
||||
|
||||
const Tag TAG_ASYN = TAG(A,S,Y,N);
|
||||
const Tag TAG_EXTN = TAG(E,X,T,N);
|
||||
const Tag TAG_LOAD = TAG(L,O,A,D);
|
||||
|
||||
// ======================================================================
|
||||
|
||||
namespace AsynchronousLoaderNamespace
|
||||
{
|
||||
struct ExtensionFunctions
|
||||
{
|
||||
char *extension;
|
||||
AsynchronousLoader::FetchFunction fetchFunction;
|
||||
AsynchronousLoader::ReleaseFunction releaseFunction;
|
||||
};
|
||||
typedef stdvector<ExtensionFunctions>::fwd ExtensionFunctionsList;
|
||||
|
||||
struct FileRecord
|
||||
{
|
||||
public:
|
||||
int8 alreadyCached;
|
||||
const int8 extensionFunctionsIndex;
|
||||
const char fileName[2];
|
||||
|
||||
private:
|
||||
FileRecord();
|
||||
FileRecord &operator =(const FileRecord &);
|
||||
};
|
||||
typedef stdvector<FileRecord *>::fwd FileRecordList;
|
||||
|
||||
class FileMapComparison
|
||||
{
|
||||
public:
|
||||
bool operator ()(const char *lhs, const char *rhs) const;
|
||||
};
|
||||
|
||||
typedef stdmap<const char *, FileRecordList *, FileMapComparison>::fwd FileMap;
|
||||
|
||||
struct CachedFile
|
||||
{
|
||||
FileRecord *fileRecord;
|
||||
AbstractFile *file;
|
||||
const void *resource;
|
||||
};
|
||||
typedef stdvector<CachedFile>::fwd CachedFiles;
|
||||
typedef stdvector<CachedFiles*>::fwd CachedFilesPool;
|
||||
|
||||
struct Request
|
||||
{
|
||||
FileRecordList *fileRecordList;
|
||||
AsynchronousLoader::Callback callback;
|
||||
void *data;
|
||||
CachedFiles *cachedFiles;
|
||||
};
|
||||
typedef stddeque<Request *>::fwd Requests;
|
||||
|
||||
void remove();
|
||||
void submitRequest(Request *request);
|
||||
void threadRoutine();
|
||||
|
||||
#ifdef _DEBUG
|
||||
static void debugReport();
|
||||
#endif
|
||||
|
||||
bool ms_installed;
|
||||
#ifdef _DEBUG
|
||||
bool ms_debugDisable;
|
||||
bool ms_debugReport;
|
||||
bool ms_suspendThread;
|
||||
bool ms_threadIsSuspended;
|
||||
bool ms_suspendCallbacks;
|
||||
int ms_numberOfRequests;
|
||||
int ms_numberOfPendingRequests;
|
||||
int ms_numberOfCompletedRequests;
|
||||
int ms_numberOfSubmittedRequests;
|
||||
int ms_numberOfRetiredRequests;
|
||||
int ms_numberOfFetchedResources;
|
||||
int ms_numberOfAlreadyCachedFiles;
|
||||
#endif
|
||||
int ms_numberOfCachedBytes;
|
||||
int ms_numberOfPostponedRequests;
|
||||
int const cms_postponeThreshold = 8 * 1024 * 1024;
|
||||
int ms_enabled;
|
||||
ThreadHandle ms_threadHandle;
|
||||
Semaphore ms_eventsPending;
|
||||
Mutex ms_mutex;
|
||||
Requests ms_pendingRequests;
|
||||
Requests ms_completedRequests;
|
||||
char *ms_fileData;
|
||||
FileMap ms_fileMap;
|
||||
ExtensionFunctionsList ms_extensionFunctionsList;
|
||||
CachedFilesPool ms_cachedFilesPool;
|
||||
MemoryBlockManager *ms_requestMemoryBlockManager;
|
||||
}
|
||||
using namespace AsynchronousLoaderNamespace;
|
||||
|
||||
// ======================================================================
|
||||
|
||||
bool AsynchronousLoaderNamespace::FileMapComparison::operator()(const char *lhs, const char *rhs) const
|
||||
{
|
||||
return strcmp(lhs, rhs) < 0;
|
||||
}
|
||||
|
||||
// ======================================================================
|
||||
|
||||
void AsynchronousLoader::install(const char *fileName)
|
||||
{
|
||||
DEBUG_FATAL(ms_installed, ("Already installed"));
|
||||
|
||||
if (!ConfigSharedFile::getEnableAsynchronousLoader())
|
||||
return;
|
||||
|
||||
Iff iff;
|
||||
if (!iff.open(fileName, true))
|
||||
{
|
||||
DEBUG_WARNING(true, ("Could not load asynchronous data file %s\n", fileName));
|
||||
ms_installed = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// add to exit chain
|
||||
ExitChain::add(&AsynchronousLoaderNamespace::remove, "AsynchronousLoader::remove");
|
||||
|
||||
// load all the data
|
||||
iff.enterForm(TAG_ASYN);
|
||||
iff.enterForm(TAG_0001);
|
||||
|
||||
// read all the names
|
||||
iff.enterChunk(TAG_NAME);
|
||||
ms_fileData = iff.read_char(iff.getChunkLengthTotal());
|
||||
iff.exitChunk(TAG_NAME);
|
||||
|
||||
iff.enterChunk(TAG_EXTN);
|
||||
const int numberOfExtensions = iff.getChunkLengthTotal(sizeof(int32));
|
||||
ms_extensionFunctionsList.reserve(numberOfExtensions);
|
||||
ExtensionFunctions extensionFunctions;
|
||||
extensionFunctions.extension = NULL;
|
||||
extensionFunctions.fetchFunction = NULL;
|
||||
extensionFunctions.releaseFunction = NULL;
|
||||
for (int i = 0; i < numberOfExtensions; ++i)
|
||||
{
|
||||
extensionFunctions.extension = ms_fileData + iff.read_int32();
|
||||
|
||||
// hack to deal with bad data where the extension pointed at the entire file name instead of just the exception
|
||||
char * const dot = strrchr(extensionFunctions.extension, '.');
|
||||
if (dot)
|
||||
extensionFunctions.extension = dot + 1;
|
||||
|
||||
ms_extensionFunctionsList.push_back(extensionFunctions);
|
||||
}
|
||||
iff.exitChunk(TAG_EXTN);
|
||||
|
||||
// read all the asynchronous loading records
|
||||
iff.enterChunk(TAG_LOAD);
|
||||
while (iff.getChunkLengthLeft())
|
||||
{
|
||||
const int32 count = iff.read_int32();
|
||||
FileRecordList *fileRecordList = new FileRecordList;
|
||||
fileRecordList->reserve(count);
|
||||
|
||||
for (int i = 0; i < count; ++i)
|
||||
fileRecordList->push_back(reinterpret_cast<FileRecord *>(ms_fileData + iff.read_int32()));
|
||||
|
||||
const bool result = ms_fileMap.insert(FileMap::value_type(fileRecordList->front()->fileName, fileRecordList)).second;
|
||||
UNREF(result);
|
||||
DEBUG_FATAL(!result, ("item was already present"));
|
||||
}
|
||||
iff.exitChunk(TAG_LOAD);
|
||||
|
||||
iff.exitForm(TAG_0001);
|
||||
iff.exitForm(TAG_ASYN);
|
||||
|
||||
ms_requestMemoryBlockManager = new MemoryBlockManager("AsynchronousLoader::Request", true, sizeof(Request), 0, 0, 0);
|
||||
|
||||
ms_threadHandle = runNamedThread("AsynchronousLoader", threadRoutine);
|
||||
switch (ConfigSharedFile::getAsynchronousLoaderPriority())
|
||||
{
|
||||
case -2: ms_threadHandle->setPriority(Thread::kIdle); break;
|
||||
case -1: ms_threadHandle->setPriority(Thread::kLow); break;
|
||||
case 0: ms_threadHandle->setPriority(Thread::kNormal); break;
|
||||
case 1: ms_threadHandle->setPriority(Thread::kHigh); break;
|
||||
case 2: ms_threadHandle->setPriority(Thread::kCritical); break;
|
||||
default: ms_threadHandle->setPriority(Thread::kNormal); break;
|
||||
};
|
||||
|
||||
#ifdef _DEBUG
|
||||
DebugFlags::registerFlag(ms_debugDisable, "SharedFile", "runtimeDisableAsynchronousLoader");
|
||||
DebugFlags::registerFlag(ms_debugReport, "SharedFile", "reportAsynchronousLoader", &debugReport);
|
||||
DebugFlags::registerFlag(ms_suspendCallbacks, "SharedFile", "suspendAsynchronousLoaderCallbacks");
|
||||
DebugFlags::registerFlag(ms_suspendThread, "SharedFile", "suspendAsynchronousLoaderThread");
|
||||
#endif
|
||||
|
||||
ms_installed = true;
|
||||
ms_enabled = 0;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
void AsynchronousLoaderNamespace::remove()
|
||||
{
|
||||
DEBUG_FATAL(!ms_installed, ("not installed"));
|
||||
Request *request = reinterpret_cast<Request*>(ms_requestMemoryBlockManager->allocate());
|
||||
request->fileRecordList = NULL;
|
||||
request->callback = NULL;
|
||||
request->data = NULL;
|
||||
request->cachedFiles = NULL;
|
||||
|
||||
submitRequest(request);
|
||||
|
||||
// wait for all the requests to be serviced
|
||||
bool empty = false;
|
||||
while (!empty)
|
||||
{
|
||||
Os::sleep(250);
|
||||
AsynchronousLoader::processCallbacks();
|
||||
|
||||
ms_mutex.enter();
|
||||
empty = ms_pendingRequests.empty() && ms_completedRequests.empty();
|
||||
ms_mutex.leave();
|
||||
}
|
||||
|
||||
// make sure the thread is dead
|
||||
ms_threadHandle->wait();
|
||||
|
||||
delete [] ms_fileData;
|
||||
|
||||
const FileMap::iterator iEnd = ms_fileMap.end();
|
||||
for (FileMap::iterator i = ms_fileMap.begin(); i != iEnd; ++i)
|
||||
delete i->second;
|
||||
ms_fileMap.clear();
|
||||
|
||||
const CachedFilesPool::iterator jEnd = ms_cachedFilesPool.end();
|
||||
for (CachedFilesPool::iterator j = ms_cachedFilesPool.begin(); j != jEnd; ++j)
|
||||
delete *j;
|
||||
ms_cachedFilesPool.clear();
|
||||
|
||||
delete ms_requestMemoryBlockManager;
|
||||
ms_requestMemoryBlockManager = NULL;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
bool AsynchronousLoader::isInstalled()
|
||||
{
|
||||
return ms_installed;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
bool AsynchronousLoader::isEnabled()
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
if (ms_debugDisable)
|
||||
return false;
|
||||
#endif
|
||||
|
||||
return ms_installed && ms_enabled == 0;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
bool AsynchronousLoader::isIdle()
|
||||
{
|
||||
ms_mutex.enter();
|
||||
bool const idle = ms_pendingRequests.empty() && ms_completedRequests.empty();
|
||||
ms_mutex.leave();
|
||||
|
||||
return idle;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
void AsynchronousLoader::disable()
|
||||
{
|
||||
--ms_enabled;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
void AsynchronousLoader::enable()
|
||||
{
|
||||
++ms_enabled;
|
||||
DEBUG_FATAL(ms_enabled > 0, ("AsynchronousLoader::enable enabled too many times"));
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
#ifdef _DEBUG
|
||||
|
||||
void AsynchronousLoaderNamespace::debugReport()
|
||||
{
|
||||
DEBUG_REPORT_LOG_PRINT(true, ("%5.2f=fps %3d=sub %3d=pend %3d=comp %3d=ret %3d=pst %5d=kb\n", Clock::framesPerSecond(), ms_numberOfSubmittedRequests, ms_numberOfPendingRequests, ms_numberOfCompletedRequests, ms_numberOfRetiredRequests, ms_numberOfPostponedRequests, ms_numberOfCachedBytes / 1024));
|
||||
ms_numberOfSubmittedRequests = 0;
|
||||
ms_numberOfRetiredRequests = 0;
|
||||
ms_numberOfFetchedResources = 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
void AsynchronousLoader::bindFetchReleaseFunctions(const char *extension, FetchFunction fetchFunction, ReleaseFunction releaseFunction)
|
||||
{
|
||||
DEBUG_FATAL((fetchFunction != 0) ^ (releaseFunction != 0), ("Must supply both a fetch & release function, or neither"));
|
||||
|
||||
ExtensionFunctionsList::iterator iEnd = ms_extensionFunctionsList.end();
|
||||
for (ExtensionFunctionsList::iterator i = ms_extensionFunctionsList.begin(); i != iEnd; ++i)
|
||||
{
|
||||
ExtensionFunctions &extensionFunctions = *i;
|
||||
|
||||
if (strcmp(extensionFunctions.extension, extension) == 0)
|
||||
{
|
||||
extensionFunctions.fetchFunction = fetchFunction;
|
||||
extensionFunctions.releaseFunction = releaseFunction;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
void AsynchronousLoader::add(const char *fileName, Callback callback, void *data)
|
||||
{
|
||||
DEBUG_FATAL(!isEnabled(), ("AsynchronousLoader not enabled"));
|
||||
NOT_NULL(callback);
|
||||
|
||||
char buffer[Os::MAX_PATH_LENGTH];
|
||||
TreeFile::fixUpFileName(fileName, buffer);
|
||||
|
||||
const FileMap::const_iterator i = ms_fileMap.find(buffer);
|
||||
if (i != ms_fileMap.end())
|
||||
{
|
||||
Request *request = reinterpret_cast<Request*>(ms_requestMemoryBlockManager->allocate());
|
||||
request->fileRecordList = i->second;
|
||||
request->callback = callback;
|
||||
request->data = data;
|
||||
request->cachedFiles = NULL;
|
||||
submitRequest(request);
|
||||
}
|
||||
else
|
||||
{
|
||||
// couldn't find the file, can't asynchronously load it.
|
||||
(*callback)(data);
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
void AsynchronousLoader::remove(Callback callback, void *data)
|
||||
{
|
||||
ms_mutex.enter();
|
||||
|
||||
{
|
||||
Requests::iterator iEnd = ms_pendingRequests.end();
|
||||
for (Requests::iterator i = ms_pendingRequests.begin(); i != iEnd; ++i)
|
||||
if ((*i)->callback == callback && (*i)->data == data)
|
||||
{
|
||||
(*i)->callback = NULL;
|
||||
(*i)->data = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
Requests::iterator iEnd = ms_completedRequests.end();
|
||||
for (Requests::iterator i = ms_completedRequests.begin(); i != iEnd; ++i)
|
||||
if ((*i)->callback == callback && (*i)->data == data)
|
||||
{
|
||||
(*i)->callback = NULL;
|
||||
(*i)->data = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
ms_mutex.leave();
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
void AsynchronousLoaderNamespace::submitRequest(Request *request)
|
||||
{
|
||||
ms_mutex.enter();
|
||||
ms_pendingRequests.push_back(request);
|
||||
#ifdef _DEBUG
|
||||
++ms_numberOfRequests;
|
||||
++ms_numberOfPendingRequests;
|
||||
++ms_numberOfSubmittedRequests;
|
||||
#endif
|
||||
ms_mutex.leave();
|
||||
|
||||
ms_eventsPending.signal();
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
void AsynchronousLoaderNamespace::threadRoutine()
|
||||
{
|
||||
// loop until a quit request is processed
|
||||
for (;;)
|
||||
{
|
||||
// wait until there is a request to processs
|
||||
ms_eventsPending.wait();
|
||||
|
||||
bool postpone = false;
|
||||
|
||||
// get the request to service
|
||||
ms_mutex.enter();
|
||||
|
||||
Request *request = ms_pendingRequests.front();
|
||||
const FileRecordList *fileRecordList = request->fileRecordList;
|
||||
|
||||
if (fileRecordList && ms_numberOfCachedBytes > cms_postponeThreshold)
|
||||
{
|
||||
postpone = true;
|
||||
ms_numberOfPostponedRequests += 1;
|
||||
}
|
||||
|
||||
ms_mutex.leave();
|
||||
|
||||
if (postpone)
|
||||
continue;
|
||||
|
||||
int bytes = 0;
|
||||
|
||||
// make sure the request is still pending
|
||||
if (fileRecordList && request->callback)
|
||||
{
|
||||
// load all the resource
|
||||
const FileRecordList::const_iterator jEnd = fileRecordList->end();
|
||||
for (FileRecordList::const_iterator j = fileRecordList->begin(); j != jEnd; ++j)
|
||||
{
|
||||
FileRecord *fileRecord = *j;
|
||||
|
||||
CachedFile cachedFile;
|
||||
cachedFile.fileRecord = fileRecord;
|
||||
cachedFile.file = NULL;
|
||||
cachedFile.resource = NULL;
|
||||
|
||||
// check if the resource is already loaded
|
||||
if (!fileRecord->alreadyCached)
|
||||
{
|
||||
// try to increase the reference count on the resource
|
||||
const ExtensionFunctions &extensionFunctions = ms_extensionFunctionsList[fileRecord->extensionFunctionsIndex];
|
||||
if (extensionFunctions.fetchFunction)
|
||||
{
|
||||
cachedFile.resource = extensionFunctions.fetchFunction(fileRecord->fileName);
|
||||
#ifdef _DEBUG
|
||||
++ms_numberOfFetchedResources;
|
||||
#endif
|
||||
}
|
||||
|
||||
// othersize, try to open the file
|
||||
if (!cachedFile.resource)
|
||||
{
|
||||
AbstractFile *file = TreeFile::open(fileRecord->fileName, AbstractFile::PriorityLow, true);
|
||||
if (file)
|
||||
{
|
||||
if (file->isZlibCompressed())
|
||||
{
|
||||
bytes += file->getZlibCompressedLength();
|
||||
cachedFile.file = file;
|
||||
}
|
||||
else
|
||||
{
|
||||
bytes += file->length();
|
||||
MemoryFile *memoryFile = new MemoryFile(file);
|
||||
cachedFile.file = memoryFile;
|
||||
delete file;
|
||||
}
|
||||
|
||||
// mark the file as already loaded
|
||||
if (extensionFunctions.fetchFunction)
|
||||
{
|
||||
ms_mutex.enter();
|
||||
fileRecord->alreadyCached = true;
|
||||
ms_mutex.leave();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// add the file to the loaded list
|
||||
if (cachedFile.file || cachedFile.resource)
|
||||
{
|
||||
if (!request->cachedFiles)
|
||||
{
|
||||
// first try to get one from the pool
|
||||
ms_mutex.enter();
|
||||
|
||||
if (!ms_cachedFilesPool.empty())
|
||||
{
|
||||
request->cachedFiles = ms_cachedFilesPool.back();
|
||||
ms_cachedFilesPool.pop_back();
|
||||
}
|
||||
|
||||
ms_mutex.leave();
|
||||
}
|
||||
|
||||
if (!request->cachedFiles)
|
||||
{
|
||||
// if that didn't work, we have to allocate a new one
|
||||
request->cachedFiles = new CachedFiles;
|
||||
}
|
||||
|
||||
request->cachedFiles->push_back(cachedFile);
|
||||
}
|
||||
}
|
||||
#ifdef _DEBUG
|
||||
else
|
||||
++ms_numberOfAlreadyCachedFiles;
|
||||
#endif
|
||||
}
|
||||
|
||||
// DEBUG_REPORT_LOG_PRINT(true, ("Async loading done %s (%d)\n", request->fileName, request->cachedFiles.size()));
|
||||
}
|
||||
|
||||
// move the request to the completed queue
|
||||
ms_mutex.enter();
|
||||
|
||||
ms_pendingRequests.pop_front();
|
||||
ms_completedRequests.push_back(request);
|
||||
|
||||
#ifdef _DEBUG
|
||||
--ms_numberOfPendingRequests;
|
||||
++ms_numberOfCompletedRequests;
|
||||
#endif
|
||||
ms_numberOfCachedBytes += bytes;
|
||||
|
||||
ms_mutex.leave();
|
||||
|
||||
// a request of no files signals thread termination
|
||||
if (!fileRecordList)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
void AsynchronousLoader::processCallbacks()
|
||||
{
|
||||
#if defined(_DEBUG) && defined(_WIN32)
|
||||
if (ms_suspendThread != ms_threadIsSuspended)
|
||||
{
|
||||
if (ms_suspendThread)
|
||||
ms_threadHandle->suspend();
|
||||
else
|
||||
ms_threadHandle->resume();
|
||||
|
||||
ms_threadIsSuspended = ms_suspendThread;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef _DEBUG
|
||||
if (!ms_suspendCallbacks)
|
||||
#endif
|
||||
{
|
||||
int callbacksAllowed = ConfigSharedFile::getAsynchronousLoaderCallbacksPerFrame();
|
||||
|
||||
// look for completed requests to service
|
||||
for (;;)
|
||||
{
|
||||
ms_mutex.enter();
|
||||
|
||||
if (ms_completedRequests.empty())
|
||||
{
|
||||
ms_mutex.leave();
|
||||
break;
|
||||
}
|
||||
|
||||
Request *request = ms_completedRequests.front();
|
||||
ms_completedRequests.pop_front();
|
||||
|
||||
ms_mutex.leave();
|
||||
|
||||
int bytes = 0;
|
||||
|
||||
if (request->callback)
|
||||
{
|
||||
// add all the preloaded files into the TreeFile cache
|
||||
if (request->cachedFiles)
|
||||
{
|
||||
const CachedFiles::iterator iEnd = request->cachedFiles->end();
|
||||
for (CachedFiles::iterator i = request->cachedFiles->begin(); i != iEnd; ++i)
|
||||
{
|
||||
CachedFile &cachedFile = *i;
|
||||
if (cachedFile.file)
|
||||
{
|
||||
if (cachedFile.file->isZlibCompressed())
|
||||
bytes += cachedFile.file->getZlibCompressedLength();
|
||||
else
|
||||
bytes += cachedFile.file->length();
|
||||
TreeFile::addCachedFile(cachedFile.fileRecord->fileName, cachedFile.file);
|
||||
cachedFile.file = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// allow the object to load
|
||||
(*request->callback)(request->data);
|
||||
|
||||
// pitch the loaded files
|
||||
TreeFile::clearCachedFiles();
|
||||
}
|
||||
|
||||
// free any resources that this asynchronous load still owns
|
||||
if (request->cachedFiles)
|
||||
{
|
||||
const CachedFiles::iterator iEnd = request->cachedFiles->end();
|
||||
for (CachedFiles::iterator i = request->cachedFiles->begin(); i != iEnd; ++i)
|
||||
{
|
||||
CachedFile &cachedFile = *i;
|
||||
|
||||
ms_mutex.enter();
|
||||
cachedFile.fileRecord->alreadyCached = false;
|
||||
ms_mutex.leave();
|
||||
|
||||
if (cachedFile.file)
|
||||
{
|
||||
if (cachedFile.file->isZlibCompressed())
|
||||
bytes += cachedFile.file->getZlibCompressedLength();
|
||||
else
|
||||
bytes += cachedFile.file->length();
|
||||
delete cachedFile.file;
|
||||
cachedFile.file = NULL;
|
||||
}
|
||||
|
||||
if (cachedFile.resource)
|
||||
{
|
||||
ms_extensionFunctionsList[cachedFile.fileRecord->extensionFunctionsIndex].releaseFunction(cachedFile.resource);
|
||||
cachedFile.resource = NULL;
|
||||
}
|
||||
}
|
||||
request->cachedFiles->clear();
|
||||
|
||||
ms_mutex.enter();
|
||||
|
||||
ms_cachedFilesPool.push_back(request->cachedFiles);
|
||||
request->cachedFiles = NULL;
|
||||
|
||||
ms_mutex.leave();
|
||||
}
|
||||
|
||||
ms_mutex.enter();
|
||||
#ifdef _DEBUG
|
||||
--ms_numberOfCompletedRequests;
|
||||
++ms_numberOfRetiredRequests;
|
||||
#endif
|
||||
ms_numberOfCachedBytes -= bytes;
|
||||
ms_mutex.leave();
|
||||
|
||||
// free the request
|
||||
ms_requestMemoryBlockManager->free(request);
|
||||
|
||||
if (callbacksAllowed && --callbacksAllowed == 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
ms_mutex.enter();
|
||||
int postponed = ms_numberOfPostponedRequests;
|
||||
ms_numberOfPostponedRequests = 0;
|
||||
ms_mutex.leave();
|
||||
|
||||
while (postponed)
|
||||
{
|
||||
ms_eventsPending.signal();
|
||||
postponed--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ======================================================================
|
||||
@@ -0,0 +1,41 @@
|
||||
// ======================================================================
|
||||
//
|
||||
// AsynchronousLoader.h
|
||||
// Copyright 2002 Sony Online Entertainment
|
||||
// All Rights Reserved.
|
||||
//
|
||||
// ======================================================================
|
||||
|
||||
#ifndef INCLUDED_AsynchronousLoader_H
|
||||
#define INCLUDED_AsynchronousLoader_H
|
||||
|
||||
// ======================================================================
|
||||
|
||||
class AsynchronousLoader
|
||||
{
|
||||
public:
|
||||
|
||||
typedef void (*Callback)(void *data);
|
||||
typedef const void *(*FetchFunction)(const char *fileName);
|
||||
typedef void (*ReleaseFunction)(const void *resource);
|
||||
|
||||
public:
|
||||
|
||||
static void install(const char *fileName);
|
||||
static bool isInstalled();
|
||||
|
||||
static void disable();
|
||||
static void enable();
|
||||
static bool isEnabled();
|
||||
static bool isIdle();
|
||||
|
||||
static void bindFetchReleaseFunctions(const char *extension, FetchFunction fetchFunction, ReleaseFunction releaseFunction);
|
||||
|
||||
static void add(const char *fileName, Callback callback, void *data);
|
||||
static void remove(Callback callback, void *data);
|
||||
static void processCallbacks();
|
||||
};
|
||||
|
||||
// ======================================================================
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,101 @@
|
||||
// ======================================================================
|
||||
//
|
||||
// ConfigSharedFile.cpp
|
||||
// Copyright 2002, Sony Online Entertainment Inc.
|
||||
// All Rights Reserved.
|
||||
//
|
||||
// ======================================================================
|
||||
|
||||
#include "sharedFile/FirstSharedFile.h"
|
||||
#include "sharedFile/ConfigSharedFile.h"
|
||||
|
||||
#include "sharedFoundation/ConfigFile.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
// ======================================================================
|
||||
|
||||
#define KEY_INT(a,b) (ms_ ## a = ConfigFile::getKeyInt("SharedFile", #a, b))
|
||||
#define KEY_BOOL(a,b) (ms_ ## a = ConfigFile::getKeyBool("SharedFile", #a, b))
|
||||
//#define KEY_FLOAT(a,b) (ms_ ## a = ConfigFile::getKeyFloat("SharedFile", #a, b))
|
||||
//#define KEY_STRING(a,b) (ms_ ## a = ConfigFile::getKeyString("SharedFile", #a, b))
|
||||
|
||||
// ======================================================================
|
||||
|
||||
namespace ConfigSharedFileNamespace
|
||||
{
|
||||
typedef std::vector<char const *> StringPtrArray;
|
||||
bool ms_enableAsynchronousLoader;
|
||||
int ms_asynchronousLoaderPriority;
|
||||
int ms_asynchronousLoaderCallbacksPerFrame;
|
||||
bool ms_validateIff;
|
||||
StringPtrArray ms_preloads; // ConfigFile owns the pointer
|
||||
}
|
||||
|
||||
using namespace ConfigSharedFileNamespace;
|
||||
|
||||
// ======================================================================
|
||||
|
||||
void ConfigSharedFile::install()
|
||||
{
|
||||
KEY_BOOL(enableAsynchronousLoader, true);
|
||||
KEY_INT(asynchronousLoaderPriority, 0);
|
||||
KEY_INT(asynchronousLoaderCallbacksPerFrame, 0);
|
||||
KEY_BOOL(validateIff, false);
|
||||
|
||||
int index = 0;
|
||||
char const * result = 0;
|
||||
do
|
||||
{
|
||||
result = ConfigFile::getKeyString("SharedFile", "preload", index++, 0);
|
||||
if (result)
|
||||
ms_preloads.push_back(result);
|
||||
}
|
||||
while (result);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
bool ConfigSharedFile::getEnableAsynchronousLoader()
|
||||
{
|
||||
return ms_enableAsynchronousLoader;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
int ConfigSharedFile::getAsynchronousLoaderPriority()
|
||||
{
|
||||
return ms_asynchronousLoaderPriority;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
int ConfigSharedFile::getAsynchronousLoaderCallbacksPerFrame()
|
||||
{
|
||||
return ms_asynchronousLoaderCallbacksPerFrame;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
bool ConfigSharedFile::getValidateIff()
|
||||
{
|
||||
return(ms_validateIff);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
int ConfigSharedFile::getNumberOfTreeFilePreloads()
|
||||
{
|
||||
return static_cast<int>(ms_preloads.size());
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
char const * ConfigSharedFile::getTreeFilePreload(int const index)
|
||||
{
|
||||
VALIDATE_RANGE_INCLUSIVE_EXCLUSIVE(0, index, getNumberOfTreeFilePreloads());
|
||||
return ms_preloads[static_cast<size_t>(index)];
|
||||
}
|
||||
|
||||
// ======================================================================
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
// ======================================================================
|
||||
//
|
||||
// ConfigSharedFile.h
|
||||
// Copyright 2002, Sony Online Entertainment Inc.
|
||||
// All Rights Reserved.
|
||||
//
|
||||
// ======================================================================
|
||||
|
||||
#ifndef INCLUDED_ConfigSharedFile_H
|
||||
#define INCLUDED_ConfigSharedFile_H
|
||||
|
||||
// ======================================================================
|
||||
|
||||
class ConfigSharedFile
|
||||
{
|
||||
public:
|
||||
static void install();
|
||||
|
||||
static bool getEnableAsynchronousLoader();
|
||||
static int getAsynchronousLoaderPriority();
|
||||
static int getAsynchronousLoaderCallbacksPerFrame();
|
||||
static bool getValidateIff();
|
||||
static int getNumberOfTreeFilePreloads();
|
||||
static char const * getTreeFilePreload(int index);
|
||||
};
|
||||
|
||||
// ======================================================================
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,438 @@
|
||||
// ======================================================================
|
||||
//
|
||||
// FileManifest.cpp
|
||||
// Portions copyright 1998 Bootprint Entertainment
|
||||
// Portions copyright 2001-2002 Sony Online Entertainment
|
||||
// All Rights Reserved.
|
||||
//
|
||||
// ======================================================================
|
||||
|
||||
#include "sharedFile/FirstSharedFile.h"
|
||||
#include "sharedFile/FileManifest.h"
|
||||
|
||||
#include "sharedFoundation/ConfigFile.h"
|
||||
#include "sharedFoundation/Crc.h"
|
||||
#include "sharedFoundation/ExitChain.h"
|
||||
#include "fileInterface/StdioFile.h"
|
||||
#include "sharedFile/FileNameUtils.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
// ======================================================================
|
||||
|
||||
namespace FileManifestNamespace
|
||||
{
|
||||
struct FileManifestEntry
|
||||
{
|
||||
std::string name;
|
||||
int size;
|
||||
int accesses;
|
||||
std::string scene;
|
||||
};
|
||||
|
||||
typedef std::map<const uint32, FileManifestEntry*> ManifestMap;
|
||||
typedef std::pair<std::string, int> TransitionVectorEntry;
|
||||
typedef std::vector<TransitionVectorEntry> TransitionVector;
|
||||
|
||||
static ManifestMap s_manifest;
|
||||
static TransitionVector s_transitionVector;
|
||||
static bool s_installed = false;
|
||||
static bool s_updateManifest = false;
|
||||
static int s_accessThreshold = -1;
|
||||
static std::string s_currentSceneId = "none";
|
||||
static bool s_isValidScene = false;
|
||||
static bool s_isTransitionScene = true;
|
||||
|
||||
const static std::string s_manifestDataTable = "datatables/manifest/skufree.iff";
|
||||
|
||||
const static std::string s_validScenes [] = {"tutorial", "space_npe_falcon", "dungeon1:npe_shared_station", "dungeon1:npe_dungeon", "space_ord_mantell"};
|
||||
const static int s_numValidScenes = sizeof (s_validScenes) / sizeof (s_validScenes[0]);
|
||||
|
||||
const static std::string s_transitionScenes [] = {"dungeon1", "none", "unknown"};
|
||||
const static int s_numTransitionScenes = sizeof (s_transitionScenes) / sizeof (s_transitionScenes[0]);
|
||||
|
||||
const int fileSizeBufferSize = 512;
|
||||
const int fileNameBufferSize = 512;
|
||||
const int sceneIdBufferSize = 128;
|
||||
}
|
||||
using namespace FileManifestNamespace;
|
||||
|
||||
// ======================================================================
|
||||
|
||||
void FileManifest::install()
|
||||
{
|
||||
DEBUG_FATAL(s_installed, ("FileManifest::install(): already installed"));
|
||||
s_installed = true;
|
||||
|
||||
ExitChain::add(FileManifest::remove, "FileManifest::remove", 0, true);
|
||||
|
||||
#if PRODUCTION == 0
|
||||
// update the access threshold if one was specified
|
||||
s_accessThreshold = ConfigFile::getKeyInt("SharedFile", "fileManifestAccessThreshold", -1, s_accessThreshold);
|
||||
|
||||
// if we are developing, and want to update the manifest, read in the tab file as well
|
||||
const char * manifestFile = ConfigFile::getKeyString("SharedFile", "updateFileManifest", 0, NULL);
|
||||
if (manifestFile != NULL)
|
||||
{
|
||||
s_updateManifest = true;
|
||||
|
||||
if (FileNameUtils::isReadable(manifestFile))
|
||||
{
|
||||
// read in the table version of the manifest
|
||||
StdioFile inputFile(manifestFile,"r");
|
||||
DEBUG_FATAL(!inputFile.isOpen(), ("FileManifet::install(): Could not open %s.", manifestFile));
|
||||
|
||||
int fileLength = inputFile.length();
|
||||
char* fileBuffer = new char[fileLength];
|
||||
memset(fileBuffer, 0, fileLength);
|
||||
|
||||
int bytes_read = inputFile.read(fileBuffer, fileLength);
|
||||
memset(fileBuffer + bytes_read, 0, fileLength - bytes_read);
|
||||
|
||||
char* currentPos = fileBuffer;
|
||||
char* delimeter;
|
||||
|
||||
// skip past the two header lines of the datatable
|
||||
currentPos = strstr(currentPos, "\n");
|
||||
if (currentPos)
|
||||
{
|
||||
++currentPos;
|
||||
currentPos = strstr(currentPos, "\n");
|
||||
}
|
||||
if (currentPos)
|
||||
++currentPos;
|
||||
|
||||
char* fileSizeBuffer = new char[fileSizeBufferSize];
|
||||
int fileSize = 0;
|
||||
char* fileNameBuffer = new char[fileNameBufferSize];
|
||||
char* sceneIdBuffer = new char[sceneIdBufferSize];
|
||||
|
||||
// iterate through the fileBuffer
|
||||
while (currentPos && currentPos < fileBuffer + fileLength && strstr(currentPos, "\n"))
|
||||
{
|
||||
// clear out all values
|
||||
fileSize = 0;
|
||||
memset(fileSizeBuffer, 0, fileSizeBufferSize);
|
||||
memset(fileNameBuffer, 0, fileNameBufferSize);
|
||||
memset(sceneIdBuffer, 0, sceneIdBufferSize);
|
||||
|
||||
// get filename
|
||||
delimeter = strstr(currentPos, "\t");
|
||||
if (!delimeter)
|
||||
{
|
||||
DEBUG_WARNING(true, ("FileManifest::install(): Couldn't find a tab at position %i in file manfiest table, stopped iterating on elements!\n", currentPos - fileBuffer));
|
||||
break;
|
||||
}
|
||||
int numCharacters = delimeter - currentPos;
|
||||
FATAL(numCharacters > fileNameBufferSize, ("Copying too much data!"));
|
||||
strncpy(fileNameBuffer, currentPos, numCharacters);
|
||||
currentPos = delimeter + 1;
|
||||
|
||||
// get sceneId
|
||||
delimeter = strstr(currentPos, "\t");
|
||||
if (!delimeter)
|
||||
{
|
||||
DEBUG_WARNING(true, ("FileManifest::install(): Couldn't find a tab at position %i in file manfiest table, stopped iterating on elements!\n", currentPos - fileBuffer));
|
||||
break;
|
||||
}
|
||||
|
||||
numCharacters = delimeter - currentPos;
|
||||
FATAL(numCharacters > sceneIdBufferSize, ("Copying too much data!"));
|
||||
strncpy(sceneIdBuffer, currentPos, numCharacters);
|
||||
currentPos = delimeter + 1;
|
||||
|
||||
// get filesize
|
||||
delimeter = strstr(currentPos, "\n");
|
||||
if (!delimeter)
|
||||
{
|
||||
DEBUG_WARNING(true, ("FileManifest::install(): Couldn't find a newline at position %i in file manfiest table, stopped iterating on elements!\n", currentPos - fileBuffer));
|
||||
break;
|
||||
}
|
||||
numCharacters = delimeter - currentPos;
|
||||
FATAL(numCharacters > fileSizeBufferSize, ("Copying too much data!"));
|
||||
strncpy(fileSizeBuffer, currentPos, numCharacters);
|
||||
fileSize = atoi(fileSizeBuffer);
|
||||
currentPos = delimeter + 1;
|
||||
|
||||
if (isValidScene(sceneIdBuffer))
|
||||
addStoredManifestEntry(fileNameBuffer, sceneIdBuffer, fileSize);
|
||||
}
|
||||
|
||||
delete [] fileBuffer;
|
||||
delete [] fileSizeBuffer;
|
||||
delete [] fileNameBuffer;
|
||||
delete [] sceneIdBuffer;
|
||||
inputFile.close();
|
||||
}
|
||||
else
|
||||
{
|
||||
DEBUG_WARNING(true, ("FileManifest::install(): Couldn't access %s for reading", manifestFile));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
void FileManifest::remove()
|
||||
{
|
||||
DEBUG_FATAL(!s_installed, ("FileManifest::remove(): not installed"));
|
||||
s_installed = false;
|
||||
|
||||
#if PRODUCTION == 0
|
||||
// dump out the new manifest if requested
|
||||
const char * manifestFile = ConfigFile::getKeyString("SharedFile", "updateFileManifest", 0, NULL);
|
||||
if (manifestFile != NULL)
|
||||
{
|
||||
StdioFile outputFile(manifestFile,"w");
|
||||
DEBUG_FATAL(!outputFile.isOpen(), ("FileManifest::remove(): Could not open %s for writing.", manifestFile));
|
||||
char * buffer = new char[1024];
|
||||
std::vector<std::string> manifestEntries;
|
||||
|
||||
sprintf(buffer, "fileName\tsceneId\tfileSize\n");
|
||||
outputFile.write(strlen(buffer), buffer);
|
||||
sprintf(buffer, "s\ts\ti\n");
|
||||
outputFile.write(strlen(buffer), buffer);
|
||||
|
||||
DEBUG_REPORT_LOG_PRINT(s_accessThreshold >= 0, ("FileManifestAccessReport:\tFile:\tScene:\tSize:\tAccesses:\n"));
|
||||
for (ManifestMap::iterator i = s_manifest.begin(); i != s_manifest.end(); ++i)
|
||||
{
|
||||
if (!i->second)
|
||||
{
|
||||
DEBUG_WARNING(true, ("FileManifest::remove(): Found a null pointer in the fileManifest map!\n"));
|
||||
continue;
|
||||
}
|
||||
|
||||
// output the entry in the file
|
||||
sprintf(buffer, "%s\t%s\t%i\n", ((i->second)->name).c_str(), ((i->second)->scene).c_str(), (i->second)->size);
|
||||
manifestEntries.push_back(buffer);
|
||||
|
||||
// print and log the entry if it is in the access threshold
|
||||
DEBUG_REPORT_LOG_PRINT((i->second)->accesses <= s_accessThreshold, ("FileManifestAccessReport:\t%s\t%s\t%i\t%i\n", ((i->second)->name).c_str(), ((i->second)->scene).c_str(), (i->second)->size, (i->second)->accesses));
|
||||
|
||||
// delete the actual entry
|
||||
delete i->second;
|
||||
i->second = 0;
|
||||
}
|
||||
|
||||
std::sort(manifestEntries.begin(), manifestEntries.end());
|
||||
for (std::vector<std::string>::const_iterator j = manifestEntries.begin(); j != manifestEntries.end(); ++j)
|
||||
outputFile.write(j->length(), j->c_str());
|
||||
|
||||
delete [] buffer;
|
||||
outputFile.close();
|
||||
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
for (ManifestMap::iterator i = s_manifest.begin(); i != s_manifest.end(); ++i)
|
||||
{
|
||||
// delete the actual entry
|
||||
delete i->second;
|
||||
i->second = 0;
|
||||
}
|
||||
|
||||
s_transitionVector.clear();
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
void FileManifest::addNewManifestEntry(const char *fileName, int fileSize)
|
||||
{
|
||||
#if PRODUCTION == 0
|
||||
// check to see if the config option to update the manifest was set
|
||||
if (!s_updateManifest)
|
||||
return;
|
||||
|
||||
// we don't know if the next scene will be valid, so we hang onto these elements just in case
|
||||
if (s_isTransitionScene)
|
||||
addToTransitionVector(fileName, fileSize);
|
||||
|
||||
// if we are not in a valid scene, return
|
||||
if (!s_isValidScene)
|
||||
return;
|
||||
|
||||
// throw away useless data
|
||||
if (!fileName || !strlen(fileName) || !strchr(fileName, '/'))
|
||||
return;
|
||||
|
||||
const uint32 crc = Crc::calculate(fileName);
|
||||
FileManifestEntry * entry = new FileManifestEntry();
|
||||
entry->name = fileName;
|
||||
entry->scene = s_currentSceneId;
|
||||
entry->size = fileSize;
|
||||
entry->accesses = 0;
|
||||
|
||||
std::pair<ManifestMap::iterator, bool> insertReturn = s_manifest.insert(std::pair<const uint32, FileManifestEntry*>(crc, entry));
|
||||
|
||||
// increment the accesses
|
||||
++(((insertReturn.first)->second)->accesses);
|
||||
|
||||
// if the insert failed
|
||||
if (!insertReturn.second)
|
||||
{
|
||||
// make sure the file is using the new fileSize - fileSize is 0 if this is entered due to a TreeFile::exists call
|
||||
if (fileSize)
|
||||
((insertReturn.first)->second)->size = fileSize;
|
||||
|
||||
// delete the new entry we created
|
||||
delete entry;
|
||||
}
|
||||
#else
|
||||
return;
|
||||
#endif
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
void FileManifest::addStoredManifestEntry(const char *fileName, const char * sceneId, int fileSize)
|
||||
{
|
||||
const uint32 crc = Crc::calculate(fileName);
|
||||
FileManifestEntry * entry = new FileManifestEntry();
|
||||
entry->name = fileName;
|
||||
entry->scene = sceneId;
|
||||
entry->size = fileSize;
|
||||
entry->accesses = 0;
|
||||
|
||||
std::pair<ManifestMap::iterator, bool> insertReturn = s_manifest.insert(std::pair<const uint32, FileManifestEntry*>(crc, entry));
|
||||
|
||||
// if the insert failed, delete the entry we created
|
||||
if (!insertReturn.second)
|
||||
delete entry;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
void FileManifest::addToTransitionVector(const char *fileName, int fileSize)
|
||||
{
|
||||
TransitionVectorEntry entry(fileName, fileSize);
|
||||
s_transitionVector.push_back(entry);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
void FileManifest::addTransitionElementsToManifest()
|
||||
{
|
||||
TransitionVector::const_iterator end = s_transitionVector.end();
|
||||
for (TransitionVector::const_iterator i = s_transitionVector.begin(); i != end; ++i)
|
||||
addNewManifestEntry((i->first).c_str(), i->second);
|
||||
clearAllTransitionElements();
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
void FileManifest::clearAllTransitionElements()
|
||||
{
|
||||
s_transitionVector.clear();
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
void FileManifest::setSceneId(const char *newScene)
|
||||
{
|
||||
#if PRODUCTION == 0
|
||||
// check to see if the config option to update the manifest was set
|
||||
if (!s_updateManifest)
|
||||
return;
|
||||
|
||||
// don't do anything if the scene hasn't changed
|
||||
if (s_currentSceneId.compare(newScene) == 0)
|
||||
return;
|
||||
|
||||
if (strlen(newScene) == 0)
|
||||
s_currentSceneId = "unknown";
|
||||
else
|
||||
s_currentSceneId = newScene;
|
||||
|
||||
// update whether this scene is a valid one that we want to add entries into, or if this is a
|
||||
// scene we want to wait on (we wait until we get a valid or invalid scene)
|
||||
// if we get an invalid scene, we throw away the entries we accumulated
|
||||
// if we get a valid scene, we add all the entries we accumulated
|
||||
// this functionality is necessary because of how the client gets information about its scene, and
|
||||
// when it gets information about the buildout location it is in
|
||||
s_isValidScene = isValidScene(s_currentSceneId.c_str());
|
||||
s_isTransitionScene = isTransitionScene(s_currentSceneId.c_str());
|
||||
|
||||
// on any scene transition, we either clear out or use all entries in the transition vector
|
||||
// unless we are going to another wait scene, in which case we keep accumulating
|
||||
if (s_isValidScene)
|
||||
addTransitionElementsToManifest();
|
||||
else if (!s_isTransitionScene)
|
||||
clearAllTransitionElements();
|
||||
#else
|
||||
return;
|
||||
#endif
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
bool FileManifest::isValidScene(const char *scene)
|
||||
{
|
||||
std::string sceneString(scene);
|
||||
for (int i = 0; i < s_numValidScenes; ++i)
|
||||
{
|
||||
// if the scene matches the name, return true
|
||||
if (sceneString.compare(s_validScenes[i]) == 0)
|
||||
return true;
|
||||
//otherwise, if the scene name is <scene name>_<num>, we return true (multiple zones)
|
||||
else if (sceneString.find(s_validScenes[i] + "_", 0) == 0 && sceneString.length() == (s_validScenes[i].length() + 2))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
bool FileManifest::isTransitionScene(const char *scene)
|
||||
{
|
||||
std::string sceneString(scene);
|
||||
for (int i = 0; i < s_numTransitionScenes; ++i)
|
||||
{
|
||||
// if the scene matches the name, return true
|
||||
if (sceneString.compare(s_transitionScenes[i]) == 0)
|
||||
return true;
|
||||
//otherwise, if the scene name is <scene name>_<num>, we return true (multiple zones)
|
||||
else if (sceneString.find(s_transitionScenes[i] + "_", 0) == 0 && sceneString.length() == (s_transitionScenes[i].length() + 2))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
bool FileManifest::contains(const char *fileName)
|
||||
{
|
||||
uint32 crc = Crc::calculate(fileName);
|
||||
return contains(crc);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
bool FileManifest::contains(uint32 crc)
|
||||
{
|
||||
ManifestMap::iterator i = s_manifest.find(crc);
|
||||
|
||||
if (i == s_manifest.end())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
std::string FileManifest::getDatatableName()
|
||||
{
|
||||
return s_manifestDataTable;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
bool FileManifest::shouldUpdateManifest()
|
||||
{
|
||||
return s_updateManifest;
|
||||
}
|
||||
|
||||
// ======================================================================
|
||||
@@ -0,0 +1,59 @@
|
||||
// ======================================================================
|
||||
//
|
||||
// FileManifest.h
|
||||
// Portions copyright 1998 Bootprint Entertainment
|
||||
// Portions copyright 2001-2002 Sony Online Entertainment
|
||||
// All Rights Reserved.
|
||||
//
|
||||
// ======================================================================
|
||||
|
||||
#ifndef INCLUDED_FileManifest_H
|
||||
#define INCLUDED_FileManifest_H
|
||||
|
||||
#include<string>
|
||||
|
||||
// ======================================================================
|
||||
|
||||
/**
|
||||
* This class keeps information about what files have been opened via this client
|
||||
* in order to track what assets are necessary for this client to run on the
|
||||
* server it is connecting to. It will read in and dump out it's info to a file if
|
||||
* the config setting is set.
|
||||
*/
|
||||
class FileManifest
|
||||
{
|
||||
public:
|
||||
|
||||
static void install();
|
||||
static void remove();
|
||||
|
||||
static void addNewManifestEntry(const char *fileName, int fileSize);
|
||||
static void addStoredManifestEntry(const char *fileName, const char *sceneId, int fileSize);
|
||||
static void addToTransitionVector(const char *fileName, int fileSize);
|
||||
|
||||
static void addTransitionElementsToManifest();
|
||||
static void clearAllTransitionElements();
|
||||
|
||||
static void setSceneId(const char *newScene);
|
||||
static bool isValidScene(const char *);
|
||||
static bool isTransitionScene(const char *);
|
||||
|
||||
static bool contains(const char *fileName);
|
||||
static bool contains(uint32 crc);
|
||||
|
||||
static std::string getDatatableName();
|
||||
static bool shouldUpdateManifest();
|
||||
|
||||
private:
|
||||
|
||||
/// disabled
|
||||
FileManifest();
|
||||
/// disabled
|
||||
FileManifest(const FileManifest &);
|
||||
/// disabled
|
||||
FileManifest &operator =(const FileManifest &);
|
||||
};
|
||||
|
||||
// ======================================================================
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,263 @@
|
||||
// ============================================================================
|
||||
//
|
||||
// FileNameUtils.cpp
|
||||
// Copyright Sony Online Entertainment
|
||||
//
|
||||
// ============================================================================
|
||||
|
||||
#include "sharedFile/FirstSharedFile.h"
|
||||
#include "sharedFile/FileNameUtils.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string>
|
||||
|
||||
// ============================================================================
|
||||
//
|
||||
// FileNameUtils
|
||||
//
|
||||
// ============================================================================
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
std::string FileNameUtils::get(std::string const &path, int const flags)
|
||||
{
|
||||
std::string result;
|
||||
|
||||
if (flags & drive)
|
||||
{
|
||||
result += getDrive(path);
|
||||
}
|
||||
if (flags & directory)
|
||||
{
|
||||
result += getDirectory(path);
|
||||
}
|
||||
if (flags & fileName)
|
||||
{
|
||||
result += getFileName(path);
|
||||
}
|
||||
if (flags & extension)
|
||||
{
|
||||
std::string const & extension = getExtension(path);
|
||||
|
||||
if ( !extension.empty()
|
||||
&& (flags & fileName))
|
||||
{
|
||||
result += ".";
|
||||
}
|
||||
|
||||
result += extension;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
bool FileNameUtils::isReadable(std::string const &path)
|
||||
{
|
||||
bool result = false;
|
||||
|
||||
if (!path.empty())
|
||||
{
|
||||
FILE *fp = fopen(path.c_str(), "r");
|
||||
|
||||
result = (fp != NULL);
|
||||
|
||||
if (fp != NULL)
|
||||
{
|
||||
fclose(fp);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
bool FileNameUtils::isWritable(std::string const &path)
|
||||
{
|
||||
bool result = false;
|
||||
|
||||
if (!path.empty())
|
||||
{
|
||||
FILE *fp = fopen(path.c_str(), "w");
|
||||
|
||||
result = (fp != NULL);
|
||||
|
||||
if (fp != NULL)
|
||||
{
|
||||
fclose(fp);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
bool FileNameUtils::isIff(std::string const &path)
|
||||
{
|
||||
bool result = false;
|
||||
FILE *fp = fopen(path.c_str(), "rt");
|
||||
|
||||
if ((fp) &&
|
||||
(getc(fp) == 'F') &&
|
||||
(getc(fp) == 'O') &&
|
||||
(getc(fp) == 'R') &&
|
||||
(getc(fp) == 'M'))
|
||||
{
|
||||
result = true;
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
std::string FileNameUtils::getDrive(std::string const &path)
|
||||
{
|
||||
std::string drive;
|
||||
|
||||
// Check if local drive
|
||||
|
||||
size_t const semicolonIndex = path.find(':');
|
||||
|
||||
if (semicolonIndex != std::string::npos)
|
||||
{
|
||||
drive = path.substr(0, semicolonIndex + 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string fixedUpPath(path);
|
||||
swapChar(fixedUpPath, '\\', '/');
|
||||
|
||||
// Check if network drive
|
||||
|
||||
size_t const doubleSlashIndex = fixedUpPath.find("//");
|
||||
|
||||
if (doubleSlashIndex != std::string::npos)
|
||||
{
|
||||
size_t const firstSlashIndex = fixedUpPath.find('/', doubleSlashIndex + 2);
|
||||
|
||||
if (firstSlashIndex != std::string::npos)
|
||||
{
|
||||
drive = fixedUpPath.substr(doubleSlashIndex, firstSlashIndex - doubleSlashIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return drive;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
std::string FileNameUtils::getDirectory(std::string const &path)
|
||||
{
|
||||
std::string fixedUpPath(path);
|
||||
swapChar(fixedUpPath, '\\', '/');
|
||||
|
||||
std::string directory;
|
||||
|
||||
// Extract the filename position
|
||||
|
||||
size_t const lastSlashIndex = fixedUpPath.rfind('/');
|
||||
|
||||
// Extract network drive position
|
||||
|
||||
size_t const doubleSlashIndex = fixedUpPath.find("//");
|
||||
|
||||
// Extract the directory position
|
||||
|
||||
size_t const firstSlashIndex = fixedUpPath.find('/', (doubleSlashIndex != std::string::npos) ? (doubleSlashIndex + 2) : 0);
|
||||
|
||||
// Extract the directory
|
||||
|
||||
if (firstSlashIndex != std::string::npos)
|
||||
{
|
||||
directory = fixedUpPath.substr(firstSlashIndex, lastSlashIndex - firstSlashIndex + 1);
|
||||
}
|
||||
|
||||
return directory;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
std::string FileNameUtils::getFileName(std::string const &path)
|
||||
{
|
||||
std::string fixedUpPath(path);
|
||||
swapChar(fixedUpPath, '\\', '/');
|
||||
|
||||
std::string fileName;
|
||||
|
||||
// Extract the extension position
|
||||
|
||||
size_t const dotIndex = fixedUpPath.rfind('.');
|
||||
|
||||
// Extract the filename position
|
||||
|
||||
size_t const lastSlashIndex = fixedUpPath.rfind('/');
|
||||
|
||||
// Extract the filename
|
||||
|
||||
if (lastSlashIndex != std::string::npos)
|
||||
{
|
||||
if (dotIndex == std::string::npos)
|
||||
{
|
||||
// Extension was not found
|
||||
|
||||
fileName = fixedUpPath.substr(lastSlashIndex + 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Extension was found
|
||||
|
||||
fileName = fixedUpPath.substr(lastSlashIndex + 1, dotIndex - lastSlashIndex - 1);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// There is no path
|
||||
|
||||
if (dotIndex == std::string::npos)
|
||||
{
|
||||
// Extension was not found
|
||||
|
||||
fileName = fixedUpPath;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Extension was found
|
||||
|
||||
fileName = fixedUpPath.substr(0, dotIndex);
|
||||
}
|
||||
}
|
||||
|
||||
return fileName;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
std::string FileNameUtils::getExtension(std::string const &path)
|
||||
{
|
||||
std::string extension;
|
||||
|
||||
// Extract the extension
|
||||
|
||||
size_t const dotIndex = path.rfind('.');
|
||||
|
||||
if (dotIndex != std::string::npos)
|
||||
{
|
||||
extension = path.substr(dotIndex + 1);
|
||||
}
|
||||
|
||||
return extension;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void FileNameUtils::swapChar(std::string &path, char const sourceCharacter, char const destinationCharacter)
|
||||
{
|
||||
std::string::iterator iterPath = path.begin();
|
||||
|
||||
for (; iterPath != path.end(); ++iterPath)
|
||||
{
|
||||
if (*iterPath == sourceCharacter)
|
||||
{
|
||||
*iterPath = destinationCharacter;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
@@ -0,0 +1,64 @@
|
||||
// ============================================================================
|
||||
//
|
||||
// FileNameUtils.h
|
||||
// Copyright Sony Online Entertainment
|
||||
//
|
||||
// ============================================================================
|
||||
|
||||
#ifndef INCLUDED_FileNameUtils_H
|
||||
#define INCLUDED_FileNameUtils_H
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
class FileNameUtils
|
||||
{
|
||||
public:
|
||||
|
||||
enum
|
||||
{
|
||||
drive = 1L << 0,
|
||||
directory = 1L << 1,
|
||||
fileName = 1L << 2,
|
||||
extension = 1L << 3
|
||||
};
|
||||
|
||||
// Returns a combination of items depending on the enums passed in above.
|
||||
// Ex: get(FileNameUtils::fileName | FileNameUtils::extension) returns
|
||||
// the "filename.extension", notice the dot is automatically appended.
|
||||
|
||||
static std::string get(std::string const &path, int const flags);
|
||||
|
||||
// Returns only the local or network drive
|
||||
|
||||
static std::string getDrive(std::string const &path);
|
||||
|
||||
// Returns only the diretory
|
||||
|
||||
static std::string getDirectory(std::string const &path);
|
||||
|
||||
// Returns only the filename, no extension or dot
|
||||
|
||||
static std::string getFileName(std::string const &path);
|
||||
|
||||
// Returns just the extension, no dot
|
||||
|
||||
static std::string getExtension(std::string const &path);
|
||||
|
||||
static bool isReadable(std::string const &path);
|
||||
static bool isWritable(std::string const &path);
|
||||
static bool isIff(std::string const &path);
|
||||
|
||||
static void swapChar(std::string &path, char const sourceCharacter, char const destinationCharacter);
|
||||
|
||||
private:
|
||||
|
||||
// Disabled
|
||||
|
||||
FileNameUtils();
|
||||
~FileNameUtils();
|
||||
FileNameUtils(FileNameUtils const &);
|
||||
FileNameUtils &operator =(FileNameUtils const &);
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
|
||||
#endif // INCLUDED_FileNameUtils_H
|
||||
@@ -0,0 +1,269 @@
|
||||
// ======================================================================
|
||||
//
|
||||
// FileStreamer.cpp
|
||||
// Portions copyright 1998 Bootprint Entertainment
|
||||
// Portions copyright 2002 Sony Online Entertainment
|
||||
// All Rights Reserved
|
||||
//
|
||||
// ======================================================================
|
||||
|
||||
#include "sharedFile/FirstSharedFile.h"
|
||||
#include "sharedFile/FileStreamer.h"
|
||||
|
||||
#include "sharedFile/FileStreamerThread.h"
|
||||
#include "sharedFile/OsFile.h"
|
||||
#include "sharedFoundation/CrashReportInformation.h"
|
||||
#include "sharedFoundation/ExitChain.h"
|
||||
#include "sharedFoundation/Production.h"
|
||||
#include "sharedFoundation/PerThreadData.h"
|
||||
#include "sharedFoundation/MemoryBlockManager.h"
|
||||
#include "sharedSynchronization/Gate.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
// ======================================================================
|
||||
|
||||
bool FileStreamer::ms_installed;
|
||||
bool FileStreamer::ms_useThread;
|
||||
|
||||
// ======================================================================
|
||||
|
||||
namespace FileStreamerNamespace
|
||||
{
|
||||
bool reportModDirectory(char const * const fileName ,char const * const directory, char const * const description);
|
||||
}
|
||||
|
||||
using namespace FileStreamerNamespace;
|
||||
|
||||
// ======================================================================
|
||||
// Install the FileStreamer
|
||||
|
||||
void FileStreamer::install(bool useThread)
|
||||
{
|
||||
DEBUG_FATAL(ms_installed, ("FileStreamer::install already installed"));
|
||||
ms_installed = true;
|
||||
ms_useThread = useThread;
|
||||
File::install(ms_useThread);
|
||||
|
||||
if (ms_useThread)
|
||||
FileStreamerThread::install();
|
||||
|
||||
ExitChain::add(FileStreamer::remove, "FileStreamer::remove", 0, true);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
/**
|
||||
* Remove the FileStreamer subsystem.
|
||||
*
|
||||
* This function will fatal if it has to wait more than MaxTimeToWait
|
||||
* for the file streamer thread to shut down.
|
||||
*/
|
||||
|
||||
void FileStreamer::remove(void)
|
||||
{
|
||||
DEBUG_FATAL(!ms_installed, ("FileStreamer::remove not installed"));
|
||||
ms_installed = false;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
/**
|
||||
* Check if a file exists.
|
||||
*
|
||||
* This routine does not indicate that the file is actually readable by
|
||||
* the process.
|
||||
*
|
||||
* @param fileName File name to check existence of
|
||||
* @return True if the file exists, otherwise false.
|
||||
*/
|
||||
|
||||
bool FileStreamer::exists(const char *fileName)
|
||||
{
|
||||
return OsFile::exists(fileName);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
/**
|
||||
* Check if a file exists.
|
||||
*
|
||||
* This routine does not indicate that the file is actually readable by
|
||||
* the process.
|
||||
*
|
||||
* @param fileName File name to check existence of
|
||||
* @return True if the file exists, otherwise false.
|
||||
*/
|
||||
|
||||
int FileStreamer::getFileSize(const char *fileName)
|
||||
{
|
||||
return OsFile::getFileSize(fileName);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
/**
|
||||
* Open a file.
|
||||
*
|
||||
* @param fileName [In] File name to open
|
||||
* @param handle [Out] Handle to the opened file
|
||||
* @return True if the file was successfully opened, otherwise false.
|
||||
*/
|
||||
|
||||
FileStreamer::File *FileStreamer::open(const char *fileName, bool randomAccess)
|
||||
{
|
||||
DEBUG_FATAL(!ms_installed, ("not installed"));
|
||||
DEBUG_FATAL(!fileName, ("file name null"));
|
||||
|
||||
OsFile *osFile = OsFile::open(fileName, randomAccess);
|
||||
if (!osFile)
|
||||
return NULL;
|
||||
|
||||
#if PRODUCTION
|
||||
// Stop checking the fileName if one of these returns true
|
||||
reportModDirectory(fileName, "ui/", "UI") ||
|
||||
reportModDirectory(fileName, "texture/", "Texture") ||
|
||||
reportModDirectory(fileName, "effect/", "Effect") ||
|
||||
reportModDirectory(fileName, "appearance/", "Appearance") ||
|
||||
reportModDirectory(fileName, "music/", "Music") ||
|
||||
reportModDirectory(fileName, "sound/", "Sound") ||
|
||||
reportModDirectory(fileName, "camera/", "Camera") ||
|
||||
reportModDirectory(fileName, "shader/", "Shader");
|
||||
#endif
|
||||
|
||||
// return the opened file
|
||||
return new FileStreamer::File(osFile);
|
||||
}
|
||||
|
||||
// ======================================================================
|
||||
|
||||
MemoryBlockManager *FileStreamer::File::ms_memoryBlockManager;
|
||||
bool FileStreamer::File::ms_useThread;
|
||||
|
||||
// ======================================================================
|
||||
|
||||
void FileStreamer::File::install(bool useThread)
|
||||
{
|
||||
ms_memoryBlockManager = new MemoryBlockManager("FileStreamer::File::memoryBlockManager", true, sizeof(FileStreamer::File), 0, 0, 0);
|
||||
ExitChain::add(&remove, "FileStreamer::File");
|
||||
ms_useThread = useThread;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
void FileStreamer::File::remove()
|
||||
{
|
||||
delete ms_memoryBlockManager;
|
||||
ms_memoryBlockManager = NULL;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
void *FileStreamer::File::operator new(size_t size)
|
||||
{
|
||||
UNREF(size);
|
||||
NOT_NULL (ms_memoryBlockManager);
|
||||
DEBUG_FATAL(size != sizeof(FileStreamer::File), ("incorrect File size"));
|
||||
DEBUG_FATAL(size != static_cast<size_t> (ms_memoryBlockManager->getElementSize()), ("installed with bad size"));
|
||||
return ms_memoryBlockManager->allocate();
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
void FileStreamer::File::operator delete(void *pointer)
|
||||
{
|
||||
NOT_NULL (ms_memoryBlockManager);
|
||||
ms_memoryBlockManager->free(pointer);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
FileStreamer::File::File(OsFile *osFile)
|
||||
:
|
||||
m_osFile(osFile),
|
||||
m_offset(0)
|
||||
{
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
FileStreamer::File::~File()
|
||||
{
|
||||
close();
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
bool FileStreamer::File::isOpen() const
|
||||
{
|
||||
return m_osFile != NULL;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
int FileStreamer::File::length() const
|
||||
{
|
||||
DEBUG_FATAL(!isOpen(), ("file is not open"));
|
||||
return m_osFile->length();
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
int FileStreamer::File::read(int offset, void *destinationBuffer, int numberOfBytes, AbstractFile::PriorityType priority)
|
||||
{
|
||||
DEBUG_FATAL(!isOpen(), ("file is not open"));
|
||||
|
||||
if (!ms_useThread)
|
||||
{
|
||||
m_osFile->seek(offset);
|
||||
return m_osFile->read(destinationBuffer, numberOfBytes);
|
||||
}
|
||||
|
||||
int returnValue = -1;
|
||||
|
||||
Gate *gate = PerThreadData::getFileStreamerReadGate();
|
||||
|
||||
// build the request
|
||||
FileStreamerThread::Request *newRequest = new FileStreamerThread::Request;
|
||||
newRequest->type = FileStreamerThread::Request::Read;
|
||||
newRequest->osFile = m_osFile;
|
||||
newRequest->buffer = destinationBuffer;
|
||||
newRequest->offset = offset;
|
||||
newRequest->bytesToBeRead = numberOfBytes;
|
||||
newRequest->bytesRead = 0;
|
||||
newRequest->gate = gate;
|
||||
newRequest->priority = priority;
|
||||
newRequest->returnValue = &returnValue;
|
||||
|
||||
// submit the request
|
||||
FileStreamerThread::submitRequest(newRequest);
|
||||
|
||||
// wait for the request to complete
|
||||
gate->wait();
|
||||
gate->close();
|
||||
|
||||
DEBUG_FATAL(returnValue < 0, ("failed to set the return value"));
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
void FileStreamer::File::close()
|
||||
{
|
||||
if (isOpen())
|
||||
{
|
||||
delete m_osFile;
|
||||
m_osFile = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
// ======================================================================
|
||||
|
||||
bool FileStreamerNamespace::reportModDirectory(char const * const fileName ,char const * const directory, char const * const description)
|
||||
{
|
||||
if (strstr(fileName, directory))
|
||||
{
|
||||
DEBUG_REPORT_LOG(true, ("Loaded %s off filesystem: %s\n", description, fileName));
|
||||
CrashReportInformation::addStaticText("%s file: %s\n", description, fileName);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// ======================================================================
|
||||
@@ -0,0 +1,105 @@
|
||||
// ======================================================================
|
||||
//
|
||||
// FileStreamer.h
|
||||
// Portions copyright 1998 Bootprint Entertainment
|
||||
// Portions copyright 2002 Sony Online Entertainment
|
||||
// All Rights Reserveed
|
||||
//
|
||||
// ======================================================================
|
||||
|
||||
#ifndef INCLUDED_FileStreamer_H
|
||||
#define INCLUDED_FileStreamer_H
|
||||
|
||||
// ======================================================================
|
||||
|
||||
class FileStreamerThread;
|
||||
class MemoryBlockManager;
|
||||
class OsFile;
|
||||
|
||||
#include "fileInterface/AbstractFile.h"
|
||||
|
||||
// ======================================================================
|
||||
/**
|
||||
* Low-level disk access routines.
|
||||
*
|
||||
* This class spawns a thread to take care of all disk access.
|
||||
*
|
||||
* This class basically wraps the FileStreamerThread class, which no other
|
||||
* class should access.
|
||||
*
|
||||
* Game programmers likely want to use the TreeFile class to access disk
|
||||
* files instead of this class.
|
||||
*/
|
||||
|
||||
class FileStreamer
|
||||
{
|
||||
friend class FileStreamerThread;
|
||||
|
||||
public:
|
||||
|
||||
class File;
|
||||
|
||||
public:
|
||||
|
||||
static void install(bool useThread);
|
||||
static void remove();
|
||||
|
||||
static bool exists(const char *fileName);
|
||||
static int getFileSize(const char *fileName);
|
||||
static File *open(const char *fileName, bool randomAccess=false);
|
||||
|
||||
private:
|
||||
|
||||
static bool ms_installed;
|
||||
static bool ms_useThread;
|
||||
};
|
||||
|
||||
// ======================================================================
|
||||
|
||||
class FileStreamer::File
|
||||
{
|
||||
friend class FileStreamerThread;
|
||||
|
||||
public:
|
||||
|
||||
static void install(bool useThread);
|
||||
static void *operator new(size_t size);
|
||||
static void operator delete(void *memory);
|
||||
|
||||
public:
|
||||
|
||||
File(OsFile *osFile);
|
||||
~File();
|
||||
|
||||
bool isOpen() const;
|
||||
int length() const;
|
||||
int read(int offset, void *destinationBuffer, int numberOfBytes, AbstractFile::PriorityType priority);
|
||||
void close();
|
||||
|
||||
private:
|
||||
|
||||
static void remove();
|
||||
|
||||
private:
|
||||
|
||||
File();
|
||||
File(const File &);
|
||||
File &operator =(const File &);
|
||||
|
||||
private:
|
||||
|
||||
static MemoryBlockManager *ms_memoryBlockManager;
|
||||
|
||||
private:
|
||||
|
||||
static bool ms_useThread;
|
||||
|
||||
private:
|
||||
|
||||
OsFile *m_osFile;
|
||||
int m_offset;
|
||||
};
|
||||
|
||||
// ======================================================================
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,180 @@
|
||||
// ======================================================================
|
||||
//
|
||||
// FileStreamerFile.cpp
|
||||
// Copyright 2002, Sony Online Entertainment Inc.
|
||||
// All Rights Reserved.
|
||||
//
|
||||
// ======================================================================
|
||||
|
||||
#include "sharedFile/FirstSharedFile.h"
|
||||
#include "sharedFile/FileStreamerFile.h"
|
||||
|
||||
#include "sharedFile/OsFile.h"
|
||||
#include "sharedFile/FileStreamer.h"
|
||||
#include "sharedFoundation/ExitChain.h"
|
||||
#include "sharedFoundation/MemoryBlockManager.h"
|
||||
|
||||
// ======================================================================
|
||||
|
||||
MemoryBlockManager *FileStreamerFile::ms_memoryBlockManager;
|
||||
|
||||
// ======================================================================
|
||||
|
||||
void FileStreamerFile::install()
|
||||
{
|
||||
DEBUG_FATAL(ms_memoryBlockManager, ("FileStreamerFile::install already installed"));
|
||||
ms_memoryBlockManager = new MemoryBlockManager("FileStreamerFile::memoryBlockManager", true, sizeof(FileStreamerFile), 0, 0, 0);
|
||||
ExitChain::add(&FileStreamerFile::remove, "FileStreamerFile::remove");
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
void FileStreamerFile::remove()
|
||||
{
|
||||
DEBUG_FATAL(!ms_memoryBlockManager,("FileStreamerFile is not installed"));
|
||||
|
||||
delete ms_memoryBlockManager;
|
||||
ms_memoryBlockManager = 0;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
void *FileStreamerFile::operator new(size_t size)
|
||||
{
|
||||
DEBUG_FATAL(!ms_memoryBlockManager,("FileStreamerFile is not installed"));
|
||||
|
||||
// do not try to alloc a descendant class with this allocator
|
||||
DEBUG_FATAL(size != sizeof(FileStreamerFile),("bad size"));
|
||||
UNREF(size);
|
||||
|
||||
return ms_memoryBlockManager->allocate();
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
void FileStreamerFile::operator delete(void *pointer)
|
||||
{
|
||||
DEBUG_FATAL(!ms_memoryBlockManager,("Projectile is not installed"));
|
||||
ms_memoryBlockManager->free(pointer);
|
||||
}
|
||||
|
||||
// ======================================================================
|
||||
|
||||
FileStreamerFile::FileStreamerFile(PriorityType priority, FileStreamer::File &file)
|
||||
: AbstractFile(priority),
|
||||
m_file(&file),
|
||||
m_owner(true),
|
||||
m_baseOffset(0),
|
||||
m_length(m_file->length()),
|
||||
m_offset(0)
|
||||
{
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
FileStreamerFile::FileStreamerFile(PriorityType priority, FileStreamer::File &file, int baseOffset, int length)
|
||||
: AbstractFile(priority),
|
||||
m_file(&file),
|
||||
m_owner(false),
|
||||
m_baseOffset(baseOffset),
|
||||
m_length(length),
|
||||
m_offset(0)
|
||||
{
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
FileStreamerFile::~FileStreamerFile()
|
||||
{
|
||||
close();
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
bool FileStreamerFile::isOpen() const
|
||||
{
|
||||
return m_file != NULL;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
int FileStreamerFile::length() const
|
||||
{
|
||||
DEBUG_FATAL(!isOpen(), ("file not open"));
|
||||
return m_length;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
int FileStreamerFile::tell() const
|
||||
{
|
||||
DEBUG_FATAL(!isOpen(), ("file not open"));
|
||||
return m_offset;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
bool FileStreamerFile::seek(SeekType seekType, int offset)
|
||||
{
|
||||
DEBUG_FATAL(!isOpen(), ("file not open"));
|
||||
|
||||
switch (seekType)
|
||||
{
|
||||
case SeekBegin:
|
||||
m_offset = offset;
|
||||
break;
|
||||
|
||||
case SeekCurrent:
|
||||
m_offset += offset;
|
||||
break;
|
||||
|
||||
case SeekEnd:
|
||||
m_offset = m_length + offset;
|
||||
break;
|
||||
}
|
||||
|
||||
m_offset = clamp(0, m_offset, m_length);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
int FileStreamerFile::read(void *destinationBuffer, int numberOfBytes)
|
||||
{
|
||||
DEBUG_FATAL(!isOpen(), ("file not open"));
|
||||
|
||||
// make sure they don't read more file
|
||||
if (m_offset + numberOfBytes > m_length)
|
||||
numberOfBytes = m_length - m_offset;
|
||||
|
||||
const int bytesRead = m_file->read(m_baseOffset + m_offset, destinationBuffer, numberOfBytes, m_priority);
|
||||
m_offset += bytesRead;
|
||||
return bytesRead;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
int FileStreamerFile::write(int, const void *)
|
||||
{
|
||||
DEBUG_FATAL(true, ("cannot call write on tree files"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
void FileStreamerFile::flush()
|
||||
{
|
||||
DEBUG_FATAL(true, ("cannot call flush on tree files"));
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
void FileStreamerFile::close()
|
||||
{
|
||||
if (m_owner)
|
||||
delete m_file;
|
||||
m_file = NULL;
|
||||
}
|
||||
|
||||
// ======================================================================
|
||||
@@ -0,0 +1,73 @@
|
||||
// ======================================================================
|
||||
//
|
||||
// FileStreamerFile.h
|
||||
// Copyright 2002 Sony Online Entertainment
|
||||
// All rights reserved.
|
||||
//
|
||||
// ======================================================================
|
||||
|
||||
#ifndef INCLUDED_FileStreamerFile_H
|
||||
#define INCLUDED_FileStreamerFile_H
|
||||
|
||||
// ======================================================================
|
||||
|
||||
class MemoryBlockManager;
|
||||
class OsFile;
|
||||
|
||||
#include "fileInterface/AbstractFile.h"
|
||||
#include "sharedFile/FileStreamer.h"
|
||||
|
||||
// ======================================================================
|
||||
|
||||
class FileStreamerFile : public AbstractFile
|
||||
{
|
||||
public:
|
||||
|
||||
static void install();
|
||||
static void *operator new(size_t size);
|
||||
static void operator delete(void *memory);
|
||||
|
||||
public:
|
||||
|
||||
FileStreamerFile(PriorityType priority, FileStreamer::File &file);
|
||||
FileStreamerFile(PriorityType priority, FileStreamer::File &file, int baseOffset, int length);
|
||||
virtual ~FileStreamerFile();
|
||||
|
||||
virtual bool isOpen() const;
|
||||
virtual int length() const;
|
||||
virtual int tell() const;
|
||||
virtual bool seek(SeekType seekType, int offset);
|
||||
virtual int read(void *destinationBuffer, int numberOfBytes);
|
||||
|
||||
virtual int write(int numberOfBytes, const void *sourceBuffer);
|
||||
virtual void flush();
|
||||
|
||||
virtual void close();
|
||||
|
||||
private:
|
||||
|
||||
static MemoryBlockManager *ms_memoryBlockManager;
|
||||
|
||||
private:
|
||||
|
||||
static void remove();
|
||||
|
||||
private:
|
||||
|
||||
FileStreamerFile();
|
||||
FileStreamerFile(const FileStreamerFile &);
|
||||
FileStreamerFile &operator =(const FileStreamerFile &);
|
||||
|
||||
private:
|
||||
|
||||
FileStreamer::File *m_file;
|
||||
const bool m_owner;
|
||||
const int m_baseOffset;
|
||||
const int m_length;
|
||||
int m_offset;
|
||||
};
|
||||
|
||||
// ======================================================================
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,422 @@
|
||||
// ======================================================================
|
||||
//
|
||||
// FileStreamerThread.cpp
|
||||
// Portions copyright 1999 Bootprint Entertainment
|
||||
// Portions copyright 2002 Bootprint Entertainment
|
||||
// All Rights Reserved
|
||||
//
|
||||
// ======================================================================
|
||||
|
||||
#include "sharedFile/FirstSharedFile.h"
|
||||
#include "sharedFile/FileStreamerThread.h"
|
||||
|
||||
#include "fileInterface/AbstractFile.h"
|
||||
#include "sharedFile/FileStreamer.h"
|
||||
#include "sharedFile/FileStreamerFile.h"
|
||||
#include "sharedFile/OsFile.h"
|
||||
#include "sharedFoundation/ExitChain.h"
|
||||
#include "sharedFoundation/PerThreadData.h"
|
||||
#include "sharedFoundation/MemoryBlockManager.h"
|
||||
#include "sharedFoundation/MemoryBlockManager.h"
|
||||
#include "sharedSynchronization/Gate.h"
|
||||
#include "sharedSynchronization/Semaphore.h"
|
||||
#include "sharedSynchronization/Mutex.h"
|
||||
#include "sharedThread/RunThread.h"
|
||||
#include "sharedThread/ThreadHandle.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
// ======================================================================
|
||||
|
||||
bool FileStreamerThread::ms_installed;
|
||||
ThreadHandle FileStreamerThread::ms_threadHandle;
|
||||
Semaphore FileStreamerThread::ms_eventsPending;
|
||||
Mutex FileStreamerThread::ms_queueCriticalSection;
|
||||
volatile FileStreamerThread::Request *FileStreamerThread::ms_firstAudioVideoRequest;
|
||||
volatile FileStreamerThread::Request *FileStreamerThread::ms_lastAudioVideoRequest;
|
||||
volatile FileStreamerThread::Request *FileStreamerThread::ms_firstDataRequest;
|
||||
volatile FileStreamerThread::Request *FileStreamerThread::ms_lastDataRequest;
|
||||
volatile FileStreamerThread::Request *FileStreamerThread::ms_firstLowRequest;
|
||||
volatile FileStreamerThread::Request *FileStreamerThread::ms_lastLowRequest;
|
||||
|
||||
namespace FileStreamerThreadNamespace
|
||||
{
|
||||
int const cms_maxReadSize = 128 * 1024;
|
||||
};
|
||||
using namespace FileStreamerThreadNamespace;
|
||||
|
||||
// ======================================================================
|
||||
|
||||
void FileStreamerThread::install()
|
||||
{
|
||||
DEBUG_FATAL(ms_installed, ("FileStreamerThread::install already installed"));
|
||||
ms_installed = true;
|
||||
|
||||
Request::install();
|
||||
|
||||
// create the thread to handle the file access, will be triggered into action through the eventsPending semaphore
|
||||
ms_threadHandle = runNamedThread("File", threadRoutine);
|
||||
ms_threadHandle->setPriority(Thread::kHigh);
|
||||
|
||||
ExitChain::add(&remove, "FileStreamerThread::remove");
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
void FileStreamerThread::remove()
|
||||
{
|
||||
DEBUG_FATAL(!ms_installed, ("FileStreamerThread::remove not installed"));
|
||||
|
||||
//sumbit a quit request
|
||||
Request *newRequest = new Request;
|
||||
newRequest->type = Request::Quit;
|
||||
newRequest->priority = AbstractFile::PriorityData;
|
||||
newRequest->gate = PerThreadData::getFileStreamerReadGate();
|
||||
submitRequest(newRequest);
|
||||
|
||||
ms_threadHandle->wait();
|
||||
ms_installed = false;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
/**
|
||||
* Put a ruquest into the request queue(s).
|
||||
*
|
||||
* This function puts a new request at the back of the appropriate queue.
|
||||
* All access to the queue is protected by critical sections.
|
||||
*/
|
||||
|
||||
void FileStreamerThread::submitRequest(Request *request)
|
||||
{
|
||||
NOT_NULL(request);
|
||||
|
||||
if (request->priority == AbstractFile::PriorityAudioVideo)
|
||||
{
|
||||
ms_queueCriticalSection.enter();
|
||||
// add it to the linked list of requests
|
||||
if (ms_lastAudioVideoRequest)
|
||||
ms_lastAudioVideoRequest->next = request;
|
||||
else
|
||||
ms_firstAudioVideoRequest = request;
|
||||
ms_lastAudioVideoRequest = request;
|
||||
ms_queueCriticalSection.leave();
|
||||
}
|
||||
else
|
||||
if (request->priority == AbstractFile::PriorityData)
|
||||
{
|
||||
ms_queueCriticalSection.enter();
|
||||
// add it to the linked list of requests
|
||||
if (ms_lastDataRequest)
|
||||
ms_lastDataRequest->next = request;
|
||||
else
|
||||
ms_firstDataRequest = request;
|
||||
ms_lastDataRequest = request;
|
||||
ms_queueCriticalSection.leave();
|
||||
}
|
||||
else
|
||||
if (request->priority == AbstractFile::PriorityLow)
|
||||
{
|
||||
ms_queueCriticalSection.enter();
|
||||
// add it to the linked list of requests
|
||||
if (ms_lastLowRequest)
|
||||
ms_lastLowRequest->next = request;
|
||||
else
|
||||
ms_firstLowRequest = request;
|
||||
ms_lastLowRequest = request;
|
||||
ms_queueCriticalSection.leave();
|
||||
}
|
||||
else
|
||||
{
|
||||
DEBUG_FATAL(true, ("request has unknown priority type"));
|
||||
}
|
||||
|
||||
// signal the thread that a new event is waiting
|
||||
ms_eventsPending.signal();
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
/**
|
||||
* Quit.
|
||||
*
|
||||
* This request is queued and processed after the reads are completed
|
||||
*/
|
||||
|
||||
void FileStreamerThread::processQuit(volatile Request *request)
|
||||
{
|
||||
NOT_NULL(request);
|
||||
|
||||
ExitChain::quit();
|
||||
Gate *gate = request->gate;
|
||||
delete request;
|
||||
gate->open();
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
/**
|
||||
* Function to read data into a buffer.
|
||||
*
|
||||
* This function breaks up reads over cms_maxReadSize into multiple reads by reading
|
||||
* cms_maxReadSize bytes, then updating the request data and putting the request back
|
||||
* on the head of the queue and resignaling the semaphore. When the read is complete
|
||||
* it stores the number of bytes read into the game-held request->returnVal field and
|
||||
* triggers and event to tell the game that we're finished.
|
||||
*/
|
||||
|
||||
void FileStreamerThread::processRead(volatile Request *request)
|
||||
{
|
||||
NOT_NULL(request);
|
||||
|
||||
// shortcut to the file
|
||||
OsFile *osFile = request->osFile;
|
||||
|
||||
// seek to the requested offset
|
||||
osFile->seek(request->offset);
|
||||
|
||||
// read the data
|
||||
if (request->bytesToBeRead > cms_maxReadSize)
|
||||
{
|
||||
// only read up to FileStreamerThread::cms_maxReadSize, then resubmit smaller request
|
||||
const int amountRead = osFile->read(request->buffer, cms_maxReadSize);
|
||||
|
||||
request->offset += amountRead;
|
||||
request->bytesRead += amountRead;
|
||||
request->bytesToBeRead -= amountRead;
|
||||
request->buffer = reinterpret_cast<byte *>(request->buffer) + amountRead;
|
||||
|
||||
// if we read less than we could have, we're done
|
||||
if (amountRead < cms_maxReadSize)
|
||||
{
|
||||
// store final number of bytes read in storage accessible to main thread
|
||||
*request->returnValue = static_cast<int>(request->bytesRead);
|
||||
|
||||
Gate *gate = request->gate;
|
||||
delete request;
|
||||
|
||||
//set event so other main thread continues
|
||||
gate->open();
|
||||
}
|
||||
else
|
||||
{
|
||||
// resubmit request (put it on the head so we get it back first)
|
||||
if (request->priority == AbstractFile::PriorityAudioVideo)
|
||||
{
|
||||
ms_queueCriticalSection.enter();
|
||||
if (ms_firstAudioVideoRequest)
|
||||
{
|
||||
request->next = ms_firstAudioVideoRequest;
|
||||
ms_firstAudioVideoRequest = request;
|
||||
}
|
||||
else
|
||||
{
|
||||
ms_firstAudioVideoRequest = request;
|
||||
ms_lastAudioVideoRequest = request;
|
||||
}
|
||||
ms_queueCriticalSection.leave();
|
||||
}
|
||||
else
|
||||
if (request->priority == AbstractFile::PriorityData)
|
||||
{
|
||||
ms_queueCriticalSection.enter();
|
||||
if (ms_firstDataRequest)
|
||||
{
|
||||
request->next = ms_firstDataRequest;
|
||||
ms_firstDataRequest = request;
|
||||
}
|
||||
else
|
||||
{
|
||||
ms_firstDataRequest = request;
|
||||
ms_lastDataRequest = request;
|
||||
}
|
||||
ms_queueCriticalSection.leave();
|
||||
}
|
||||
else
|
||||
if (request->priority == AbstractFile::PriorityLow)
|
||||
{
|
||||
ms_queueCriticalSection.enter();
|
||||
if (ms_firstLowRequest)
|
||||
{
|
||||
request->next = ms_firstLowRequest;
|
||||
ms_firstLowRequest = request;
|
||||
}
|
||||
else
|
||||
{
|
||||
ms_firstLowRequest = request;
|
||||
ms_lastLowRequest = request;
|
||||
}
|
||||
ms_queueCriticalSection.leave();
|
||||
}
|
||||
else
|
||||
DEBUG_FATAL(true, ("FileStreamerThread::processRead request has unknown priority type"));
|
||||
|
||||
// signal the file thread that a new request is waiting
|
||||
ms_eventsPending.signal();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// fulfill entire read request
|
||||
const int amountRead = osFile->read(request->buffer, request->bytesToBeRead);
|
||||
|
||||
request->bytesRead += amountRead;
|
||||
request->bytesToBeRead -= amountRead;
|
||||
|
||||
// store final number of bytes read in storage accessible to main thread
|
||||
*request->returnValue = static_cast<int>(request->bytesRead);
|
||||
|
||||
Gate *gate = request->gate;
|
||||
delete request;
|
||||
gate->open();
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
/**
|
||||
* Routine where the file thread runs.
|
||||
*
|
||||
* The file thread waits at the WaitForSingleObject until the game thread
|
||||
* triggers it by calling a submit-request function that involves the queues.
|
||||
* It then services the first AudioVideo request, and if there are no AudioVideo requests to
|
||||
* be serviced, a Data request instead. All access to the queue is protected by
|
||||
* critical sections.
|
||||
*/
|
||||
|
||||
void FileStreamerThread::threadRoutine()
|
||||
{
|
||||
bool quit = false;
|
||||
|
||||
//loop until a quit request is processed
|
||||
while (!quit)
|
||||
{
|
||||
volatile Request* request;
|
||||
|
||||
//wait until there is data to processs
|
||||
ms_eventsPending.wait();
|
||||
|
||||
//get the request to service
|
||||
ms_queueCriticalSection.enter();
|
||||
request = NULL;
|
||||
|
||||
//if there is an AudioVideo request, service it first (interrupting any current data request)
|
||||
if (ms_firstAudioVideoRequest)
|
||||
{
|
||||
request = ms_firstAudioVideoRequest;
|
||||
ms_firstAudioVideoRequest = ms_firstAudioVideoRequest->next;
|
||||
if (ms_firstAudioVideoRequest == NULL)
|
||||
ms_lastAudioVideoRequest = NULL;
|
||||
request->next = NULL;
|
||||
}
|
||||
else
|
||||
if (ms_firstDataRequest)
|
||||
{
|
||||
request = ms_firstDataRequest;
|
||||
ms_firstDataRequest = ms_firstDataRequest->next;
|
||||
if (ms_firstDataRequest == NULL)
|
||||
ms_lastDataRequest = NULL;
|
||||
request->next = NULL;
|
||||
}
|
||||
else
|
||||
if (ms_firstLowRequest)
|
||||
{
|
||||
request = ms_firstLowRequest;
|
||||
ms_firstLowRequest = ms_firstLowRequest->next;
|
||||
if (ms_firstLowRequest == NULL)
|
||||
ms_lastLowRequest = NULL;
|
||||
request->next = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
DEBUG_FATAL(true, ("no request waiting"));
|
||||
}
|
||||
|
||||
ms_queueCriticalSection.leave();
|
||||
|
||||
if (request)
|
||||
{
|
||||
switch (request->type)
|
||||
{
|
||||
case Request::Quit:
|
||||
processQuit(request);
|
||||
quit = true;
|
||||
break;
|
||||
|
||||
case Request::Read:
|
||||
processRead(request);
|
||||
break;
|
||||
|
||||
case Request::Unknown:
|
||||
default:
|
||||
DEBUG_FATAL(true, ("FileStreamerThread::threadRoutine unknown request %d", static_cast<int>(request->type)));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ======================================================================
|
||||
|
||||
MemoryBlockManager *FileStreamerThread::Request::ms_memoryBlockManager;
|
||||
|
||||
// ======================================================================
|
||||
|
||||
void FileStreamerThread::Request::install()
|
||||
{
|
||||
ms_memoryBlockManager = new MemoryBlockManager("FileStreamerThread::Request::memoryBlockManager", true, sizeof(Request), 0, 0, 0);
|
||||
ExitChain::add(&remove, "FileStreamerThread::Request");
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
void FileStreamerThread::Request::remove()
|
||||
{
|
||||
delete ms_memoryBlockManager;
|
||||
ms_memoryBlockManager = NULL;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
void *FileStreamerThread::Request::operator new(size_t size)
|
||||
{
|
||||
UNREF(size);
|
||||
NOT_NULL(ms_memoryBlockManager);
|
||||
DEBUG_FATAL(size != sizeof(Request), ("incorrect request size"));
|
||||
DEBUG_FATAL(size != static_cast<size_t>(ms_memoryBlockManager->getElementSize()), ("installed with bad size"));
|
||||
|
||||
return ms_memoryBlockManager->allocate();
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
void FileStreamerThread::Request::operator delete(void *pointer)
|
||||
{
|
||||
NOT_NULL(ms_memoryBlockManager);
|
||||
ms_memoryBlockManager->free(pointer);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
FileStreamerThread::Request::Request()
|
||||
: next(NULL),
|
||||
type(Request::Unknown),
|
||||
osFile(NULL),
|
||||
buffer(0),
|
||||
bytesToBeRead(0),
|
||||
bytesRead(0),
|
||||
gate(NULL),
|
||||
priority(AbstractFile::PriorityData),
|
||||
returnValue(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
FileStreamerThread::Request::~Request()
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
next = NULL;
|
||||
buffer = NULL;
|
||||
gate = NULL;
|
||||
returnValue = NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
// ======================================================================
|
||||
@@ -0,0 +1,136 @@
|
||||
// ======================================================================
|
||||
//
|
||||
// FileStreamerThread.h
|
||||
// Portions copyright 1999 Bootprint Entertainment
|
||||
// Portions copyright 2002 Sony Online Entertainment
|
||||
// All Rights Reserved
|
||||
//
|
||||
// ======================================================================
|
||||
|
||||
#ifndef INCLUDED_FileStreamerThread_H
|
||||
#define INCLUDED_FileStreamerThread_H
|
||||
|
||||
// ======================================================================
|
||||
|
||||
#include "fileInterface/AbstractFile.h"
|
||||
#include "sharedFile/FileStreamer.h"
|
||||
|
||||
class FileStreamerFile;
|
||||
class Gate;
|
||||
class MemoryBlockManager;
|
||||
class Mutex;
|
||||
class OsFile;
|
||||
class Semaphore;
|
||||
class MemoryBlockManager;
|
||||
class Thread;
|
||||
template <class T> class TypedThreadHandle;
|
||||
typedef TypedThreadHandle<Thread> ThreadHandle;
|
||||
|
||||
// ======================================================================
|
||||
|
||||
// Encapsulates thread for file access
|
||||
//
|
||||
// This class represents the file streaming thread. It should only be accessed
|
||||
// by the FileStreamer class.
|
||||
//
|
||||
// All requests, with the exceptions of reads and quits, and responded to immediately.
|
||||
// The "submit-request" paradigm is used to consistancy. Read and quit requests are
|
||||
// put in multiple queues and serviced by priority (audio-visual before plain data before low, etc.)
|
||||
//
|
||||
// AV request will "interrupt" any current data request, and all reads are only serviced
|
||||
// 16K at a time.
|
||||
|
||||
class FileStreamerThread
|
||||
{
|
||||
friend class FileStreamer::File;
|
||||
|
||||
public:
|
||||
|
||||
class Request;
|
||||
|
||||
public:
|
||||
|
||||
static void install();
|
||||
|
||||
private:
|
||||
|
||||
static bool ms_installed;
|
||||
static ThreadHandle ms_threadHandle;
|
||||
static Semaphore ms_eventsPending;
|
||||
static Mutex ms_queueCriticalSection;
|
||||
static volatile Request *ms_firstAudioVideoRequest;
|
||||
static volatile Request *ms_lastAudioVideoRequest;
|
||||
static volatile Request *ms_firstDataRequest;
|
||||
static volatile Request *ms_lastDataRequest;
|
||||
static volatile Request *ms_firstLowRequest;
|
||||
static volatile Request *ms_lastLowRequest;
|
||||
|
||||
private:
|
||||
|
||||
static void remove(void);
|
||||
|
||||
static void verifyOpen(const char *function, int handle);
|
||||
static void threadRoutine();
|
||||
static void submitRequest(Request *request);
|
||||
|
||||
static void processRead(volatile Request *request);
|
||||
static void processQuit(volatile Request *request);
|
||||
};
|
||||
|
||||
// ======================================================================
|
||||
|
||||
class FileStreamerThread::Request
|
||||
{
|
||||
friend class FileStreamerThread;
|
||||
friend class FileStreamerFile;
|
||||
|
||||
public:
|
||||
|
||||
static void install();
|
||||
static void *operator new(size_t size);
|
||||
static void operator delete(void *pointer);
|
||||
|
||||
public:
|
||||
|
||||
//initialized to unknown. Must be changed before usage or it will debug_fatal
|
||||
enum Type
|
||||
{
|
||||
Unknown,
|
||||
Quit,
|
||||
Read
|
||||
};
|
||||
|
||||
volatile Request *next;
|
||||
Type type;
|
||||
OsFile *osFile;
|
||||
void *buffer;
|
||||
int offset;
|
||||
int bytesToBeRead;
|
||||
int bytesRead;
|
||||
|
||||
// event to trigger when finished servicing request
|
||||
Gate *gate;
|
||||
|
||||
// used to place in correct queue for priority service
|
||||
AbstractFile::PriorityType priority;
|
||||
|
||||
// storage held by game thread used to pass back return value
|
||||
int *returnValue;
|
||||
|
||||
public:
|
||||
|
||||
Request();
|
||||
~Request();
|
||||
|
||||
private:
|
||||
|
||||
static MemoryBlockManager *ms_memoryBlockManager;
|
||||
|
||||
private:
|
||||
|
||||
static void remove();
|
||||
};
|
||||
|
||||
// ======================================================================
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,20 @@
|
||||
// ======================================================================
|
||||
//
|
||||
// FirstFile.h
|
||||
// copyright (c) 2001 Sony Online Entertainment
|
||||
//
|
||||
// ======================================================================
|
||||
|
||||
#ifndef INCLUDED_FirstFile_H
|
||||
#define INCLUDED_FirstFile_H
|
||||
|
||||
// ======================================================================
|
||||
|
||||
#include "sharedFoundation/FirstSharedFoundation.h"
|
||||
#include "sharedDebug/FirstSharedDebug.h"
|
||||
#include "sharedMemoryManager/FirstSharedMemoryManager.h"
|
||||
|
||||
// ======================================================================
|
||||
|
||||
#endif
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,129 @@
|
||||
// ======================================================================
|
||||
//
|
||||
// IndentedFileWriter.cpp
|
||||
// Copyright 2004 Sony Online Entertainment, Inc.
|
||||
// All Rights Reserved.
|
||||
//
|
||||
// ======================================================================
|
||||
|
||||
#include "sharedFile/FirstSharedFile.h"
|
||||
#include "sharedFile/IndentedFileWriter.h"
|
||||
#include <cstdio>
|
||||
|
||||
// ======================================================================
|
||||
// class IndentedFileWriter: PUBLIC STATIC
|
||||
// ======================================================================
|
||||
|
||||
IndentedFileWriter *IndentedFileWriter::createWriter(char const *filename)
|
||||
{
|
||||
IndentedFileWriter *const newWriter = new IndentedFileWriter();
|
||||
if (newWriter->createFile(filename))
|
||||
return newWriter;
|
||||
else
|
||||
{
|
||||
// Kill the new writer since we weren't able to open the file for writing.
|
||||
delete newWriter;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
// ======================================================================
|
||||
// class IndentedFileWriter: PUBLIC
|
||||
// ======================================================================
|
||||
|
||||
IndentedFileWriter::~IndentedFileWriter()
|
||||
{
|
||||
if (m_file)
|
||||
{
|
||||
fclose(m_file);
|
||||
m_file = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
void IndentedFileWriter::indent()
|
||||
{
|
||||
++m_indentCount;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
void IndentedFileWriter::unindent()
|
||||
{
|
||||
if (m_indentCount <= 0)
|
||||
{
|
||||
WARNING(true, ("unindent() called when indentation level is [%d], ignoring unindent call.", m_indentCount));
|
||||
return;
|
||||
}
|
||||
--m_indentCount;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
void IndentedFileWriter::writeLine(char const *line) const
|
||||
{
|
||||
FATAL(!m_file, ("writeLine(): m_file is NULL, programmer error."));
|
||||
FATAL(!line, ("writeLine(): line argument is NULL."));
|
||||
|
||||
//-- Write one tab for each indentation level.
|
||||
for (int i = 0; i < m_indentCount; ++i)
|
||||
fputc(static_cast<int>('\t'), m_file);
|
||||
|
||||
//-- Write the line of text.
|
||||
fputs(line, m_file);
|
||||
|
||||
//-- Add a newline.
|
||||
fputc(static_cast<int>('\n'), m_file);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
void IndentedFileWriter::writeLineFormat(char const *format, ...) const
|
||||
{
|
||||
va_list varArgList;
|
||||
|
||||
//-- Capture the variable arguments.
|
||||
va_start(varArgList, format);
|
||||
|
||||
//-- Print the string to the buffer.
|
||||
char buffer[2048];
|
||||
|
||||
int const formatCount = vsnprintf(buffer, sizeof(buffer), format, varArgList);
|
||||
UNREF(formatCount);
|
||||
|
||||
//-- Ensure it's null terminated.
|
||||
buffer[sizeof(buffer) - 1] = '\0';
|
||||
|
||||
//-- Do the real print.
|
||||
writeLine(buffer);
|
||||
|
||||
//-- Release the var args.
|
||||
va_end(varArgList);
|
||||
}
|
||||
|
||||
// ======================================================================
|
||||
// class IndentedFileWriter: PRIVATE
|
||||
// ======================================================================
|
||||
|
||||
IndentedFileWriter::IndentedFileWriter() :
|
||||
m_indentCount(0),
|
||||
m_file(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
bool IndentedFileWriter::createFile(char const *filename)
|
||||
{
|
||||
if (m_file)
|
||||
{
|
||||
WARNING(true, ("m_file is already set, ignoring createFile() call."));
|
||||
return false;
|
||||
}
|
||||
|
||||
m_file = fopen(filename, "w");
|
||||
return (m_file != NULL);
|
||||
}
|
||||
|
||||
// ======================================================================
|
||||
@@ -0,0 +1,48 @@
|
||||
// ======================================================================
|
||||
//
|
||||
// IndentedFileWriter.h
|
||||
// Copyright 2004 Sony Online Entertainment, Inc.
|
||||
// All Rights Reserved.
|
||||
//
|
||||
// ======================================================================
|
||||
|
||||
#ifndef INCLUDED_IndentedFileWriter_H
|
||||
#define INCLUDED_IndentedFileWriter_H
|
||||
|
||||
// ======================================================================
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
// ======================================================================
|
||||
|
||||
class IndentedFileWriter
|
||||
{
|
||||
public:
|
||||
|
||||
static IndentedFileWriter *createWriter(char const *filename);
|
||||
|
||||
public:
|
||||
|
||||
~IndentedFileWriter();
|
||||
|
||||
void indent();
|
||||
void unindent();
|
||||
|
||||
void writeLine(char const *line) const;
|
||||
void writeLineFormat(char const *format, ...) const;
|
||||
|
||||
private:
|
||||
|
||||
IndentedFileWriter();
|
||||
bool createFile(char const *filename);
|
||||
|
||||
private:
|
||||
|
||||
int m_indentCount;
|
||||
FILE *m_file;
|
||||
|
||||
};
|
||||
|
||||
// ======================================================================
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,177 @@
|
||||
// ======================================================================
|
||||
//
|
||||
// MemoryFile.cpp
|
||||
// Copyright 2002, Sony Online Entertainment Inc.
|
||||
// All Rights Reserved.
|
||||
//
|
||||
// ======================================================================
|
||||
|
||||
#include "sharedFile/FirstSharedFile.h"
|
||||
#include "sharedFile/MemoryFile.h"
|
||||
|
||||
#include "sharedFoundation/ExitChain.h"
|
||||
#include "sharedFoundation/MemoryBlockManager.h"
|
||||
|
||||
// ======================================================================
|
||||
|
||||
MemoryBlockManager *MemoryFile::ms_memoryBlockManager;
|
||||
|
||||
// ======================================================================
|
||||
|
||||
void MemoryFile::install()
|
||||
{
|
||||
DEBUG_FATAL(ms_memoryBlockManager, ("MemoryFile::install already installed"));
|
||||
ms_memoryBlockManager = new MemoryBlockManager("MemoryFile::memoryBlockManager", true, sizeof(MemoryFile), 0, 0, 0);
|
||||
ExitChain::add(&remove, ("MemoryFile::remove"));
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
void MemoryFile::remove()
|
||||
{
|
||||
DEBUG_FATAL(!ms_memoryBlockManager,("MemoryFileis not installed"));
|
||||
|
||||
delete ms_memoryBlockManager;
|
||||
ms_memoryBlockManager = 0;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
void *MemoryFile::operator new(size_t size)
|
||||
{
|
||||
DEBUG_FATAL(!ms_memoryBlockManager,("MemoryFile is not installed"));
|
||||
|
||||
// do not try to alloc a descendant class with this allocator
|
||||
DEBUG_FATAL(size != sizeof(MemoryFile),("bad size"));
|
||||
UNREF(size);
|
||||
|
||||
return ms_memoryBlockManager->allocate();
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
void MemoryFile::operator delete(void *pointer)
|
||||
{
|
||||
DEBUG_FATAL(!ms_memoryBlockManager,("Projectile is not installed"));
|
||||
ms_memoryBlockManager->free(pointer);
|
||||
}
|
||||
|
||||
// ======================================================================
|
||||
|
||||
MemoryFile::MemoryFile(byte *buffer, int length)
|
||||
: AbstractFile(PriorityData),
|
||||
m_buffer(buffer),
|
||||
m_length(length),
|
||||
m_offset(0)
|
||||
{
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
MemoryFile::MemoryFile(AbstractFile *file)
|
||||
: AbstractFile(PriorityData),
|
||||
m_buffer(NULL),
|
||||
m_length(file->length()),
|
||||
m_offset(0)
|
||||
{
|
||||
m_buffer = file->readEntireFileAndClose();
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
MemoryFile::~MemoryFile()
|
||||
{
|
||||
close();
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
bool MemoryFile::isOpen() const
|
||||
{
|
||||
return m_buffer != NULL;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
int MemoryFile::length() const
|
||||
{
|
||||
DEBUG_FATAL(!isOpen(), ("file is not open"));
|
||||
return m_length;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
int MemoryFile::tell() const
|
||||
{
|
||||
DEBUG_FATAL(!isOpen(), ("file is not open"));
|
||||
return m_offset;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
bool MemoryFile::seek(SeekType seekType, int offset)
|
||||
{
|
||||
DEBUG_FATAL(!isOpen(), ("file is not open"));
|
||||
switch (seekType)
|
||||
{
|
||||
case SeekBegin:
|
||||
m_offset = offset;
|
||||
break;
|
||||
|
||||
case SeekCurrent:
|
||||
m_offset += offset;
|
||||
break;
|
||||
|
||||
case SeekEnd:
|
||||
m_offset = m_length + offset;
|
||||
break;
|
||||
}
|
||||
|
||||
m_offset = clamp(0, m_offset, m_length);
|
||||
return true;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
int MemoryFile::read(void *destinationBuffer, int numberOfBytes)
|
||||
{
|
||||
DEBUG_FATAL(!isOpen(), ("file is not open"));
|
||||
DEBUG_FATAL(numberOfBytes < 0,("asked to read %d bytes", numberOfBytes));
|
||||
|
||||
// don't let them read past end-of-file
|
||||
if (m_offset + numberOfBytes > m_length)
|
||||
numberOfBytes = m_length - m_offset;
|
||||
|
||||
// read from the buffer
|
||||
memcpy(destinationBuffer, m_buffer + m_offset, numberOfBytes);
|
||||
|
||||
m_offset += numberOfBytes;
|
||||
return numberOfBytes;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
int MemoryFile::write(int, const void *)
|
||||
{
|
||||
DEBUG_FATAL(true, ("writing to a memory file is not supported"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
void MemoryFile::close()
|
||||
{
|
||||
delete [] m_buffer;
|
||||
m_buffer = NULL;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
byte *MemoryFile::readEntireFileAndClose()
|
||||
{
|
||||
byte *result = m_buffer;
|
||||
m_buffer = NULL;
|
||||
return result;
|
||||
}
|
||||
|
||||
// ======================================================================
|
||||
@@ -0,0 +1,65 @@
|
||||
// ======================================================================
|
||||
//
|
||||
// MemoryFile.h
|
||||
// Copyright 2002, Sony Online Entertainment Inc.
|
||||
// All Rights Reserved.
|
||||
//
|
||||
// ======================================================================
|
||||
|
||||
#ifndef INCLUDED_MemoryFile_H
|
||||
#define INCLUDED_MemoryFile_H
|
||||
|
||||
// ======================================================================
|
||||
|
||||
class MemoryBlockManager;
|
||||
|
||||
#include "fileInterface/AbstractFile.h"
|
||||
|
||||
// ======================================================================
|
||||
|
||||
class MemoryFile : public AbstractFile
|
||||
{
|
||||
public:
|
||||
|
||||
static void install();
|
||||
static void *operator new(size_t size);
|
||||
static void operator delete(void* pointer);
|
||||
|
||||
public:
|
||||
|
||||
MemoryFile(byte *buffer, int length);
|
||||
MemoryFile(AbstractFile *file);
|
||||
virtual ~MemoryFile();
|
||||
|
||||
virtual bool isOpen() const;
|
||||
virtual int length() const;
|
||||
virtual int tell() const;
|
||||
virtual bool seek(SeekType seekType, int offset);
|
||||
virtual int read(void *destinationBuffer, int numberOfBytes);
|
||||
virtual int write(int numberOfBytes, const void *sourceBuffer);
|
||||
virtual void close();
|
||||
|
||||
virtual byte *readEntireFileAndClose();
|
||||
|
||||
private:
|
||||
|
||||
MemoryFile();
|
||||
MemoryFile(const MemoryFile &);
|
||||
MemoryFile &operator =(const MemoryFile &);
|
||||
|
||||
static void remove();
|
||||
|
||||
private:
|
||||
|
||||
static MemoryBlockManager *ms_memoryBlockManager;
|
||||
|
||||
private:
|
||||
|
||||
byte *m_buffer;
|
||||
const int m_length;
|
||||
int m_offset;
|
||||
};
|
||||
|
||||
// ======================================================================
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,39 @@
|
||||
// ======================================================================
|
||||
//
|
||||
// SetupSharedFile.cpp
|
||||
// copyright (c) 2001 Sony Online Entertainment
|
||||
//
|
||||
// ======================================================================
|
||||
|
||||
#include "sharedFile/FirstSharedFile.h"
|
||||
#include "sharedFile/SetupSharedFile.h"
|
||||
|
||||
#include "sharedFile/ConfigSharedFile.h"
|
||||
#include "sharedFile/FileManifest.h"
|
||||
#include "sharedFile/FileStreamer.h"
|
||||
#include "sharedFile/FileStreamerFile.h"
|
||||
#include "sharedFile/Iff.h"
|
||||
#include "sharedFile/MemoryFile.h"
|
||||
#include "sharedFile/OsFile.h"
|
||||
#include "sharedFile/TreeFile.h"
|
||||
#include "sharedFile/ZlibFile.h"
|
||||
#include "sharedDebug/InstallTimer.h"
|
||||
|
||||
// ======================================================================
|
||||
|
||||
void SetupSharedFile::install(bool useFileStreamer, uint32 skuBits)
|
||||
{
|
||||
InstallTimer const installTimer("SetupSharedFile::install");
|
||||
|
||||
ConfigSharedFile::install();
|
||||
FileStreamerFile::install();
|
||||
FileStreamer::install(useFileStreamer);
|
||||
OsFile::install();
|
||||
TreeFile::install(skuBits);
|
||||
FileManifest::install();
|
||||
MemoryFile::install();
|
||||
ZlibFile::install();
|
||||
Iff::install();
|
||||
}
|
||||
|
||||
// ======================================================================
|
||||
@@ -0,0 +1,27 @@
|
||||
// ======================================================================
|
||||
//
|
||||
// SetupSharedFile.h
|
||||
// copyright (c) 2001 Sony Online Entertainment
|
||||
//
|
||||
// ======================================================================
|
||||
|
||||
#ifndef INCLUDED_SetupSharedFile_H
|
||||
#define INCLUDED_SetupSharedFile_H
|
||||
|
||||
// ======================================================================
|
||||
|
||||
class SetupSharedFile
|
||||
{
|
||||
public:
|
||||
|
||||
static void install(bool useFileStreamer, uint32 skuBits=0);
|
||||
|
||||
private:
|
||||
SetupSharedFile();
|
||||
SetupSharedFile(const SetupSharedFile &);
|
||||
SetupSharedFile &operator =(const SetupSharedFile &);
|
||||
};
|
||||
|
||||
// ======================================================================
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,971 @@
|
||||
// ======================================================================
|
||||
//
|
||||
// TreeFile.cpp
|
||||
// Portions copyright 1998 Bootprint Entertainment
|
||||
// Portions copyright 2001-2002 Sony Online Entertainment
|
||||
// All Rights Reserved.
|
||||
//
|
||||
// ======================================================================
|
||||
|
||||
#include "sharedFile/FirstSharedFile.h"
|
||||
#include "sharedFile/TreeFile.h"
|
||||
#include "sharedFile/TreeFile_SearchNode.h"
|
||||
|
||||
#include "sharedDebug/DebugFlags.h"
|
||||
#include "sharedDebug/PixCounter.h"
|
||||
#include "sharedFile/ConfigSharedFile.h"
|
||||
#include "sharedFile/FileManifest.h"
|
||||
#include "sharedFile/FileStreamer.h"
|
||||
#include "sharedFoundation/ConfigFile.h"
|
||||
#include "sharedFoundation/ExitChain.h"
|
||||
#include "sharedFoundation/Os.h"
|
||||
#include "sharedFoundation/Production.h"
|
||||
#include "sharedSynchronization/Mutex.h"
|
||||
|
||||
#include <cstdlib>
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
#include <stdio.h>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
// ======================================================================
|
||||
|
||||
bool TreeFile::ms_installed;
|
||||
bool TreeFile::ms_haveCachedFiles;
|
||||
Mutex TreeFile::ms_criticalSection;
|
||||
TreeFile::SearchNodes TreeFile::ms_searchNodes;
|
||||
TreeFile::SearchCache * TreeFile::ms_searchCache;
|
||||
|
||||
int TreeFile::ms_numberOfFilesOpenedTotal;
|
||||
int TreeFile::ms_sizeOfFilesOpenedTotal;
|
||||
#if PRODUCTION == 0
|
||||
bool TreeFile::ms_debugReportFlagShowMetrics;
|
||||
bool TreeFile::ms_debugReportFlagShowSearchPaths;
|
||||
bool TreeFile::ms_debugLogFlag;
|
||||
int TreeFile::ms_unexpectedCacheMisses;
|
||||
#endif
|
||||
|
||||
namespace TreeFileNamespace
|
||||
{
|
||||
static char const * const cms_priorityStrings[] =
|
||||
{
|
||||
"L",
|
||||
"M",
|
||||
"H"
|
||||
};
|
||||
|
||||
class CachedFilesComparator
|
||||
{
|
||||
public:
|
||||
bool operator ()(const char *lhs, const char *rhs) const
|
||||
{
|
||||
return strcmp(lhs, rhs) < 0;
|
||||
}
|
||||
};
|
||||
|
||||
typedef std::map<const char *, AbstractFile *, CachedFilesComparator> CachedFilesMap;
|
||||
static CachedFilesMap cachedFilesMap;
|
||||
|
||||
#if PRODUCTION == 0
|
||||
bool ms_debugLogSynchronousOnly;
|
||||
bool ms_warnTreeFileOpens;
|
||||
|
||||
PixCounter::ResetInteger ms_numberOfFilesOpened[3];
|
||||
PixCounter::ResetInteger ms_sizeOfFilesOpened[3];
|
||||
PixCounter::ResetString ms_treeFilesOpened;
|
||||
#endif
|
||||
}
|
||||
using namespace TreeFileNamespace;
|
||||
|
||||
// ======================================================================
|
||||
// Install the TreeFile system
|
||||
|
||||
void TreeFile::install(uint32 skuBits)
|
||||
{
|
||||
DEBUG_FATAL(ms_installed, ("already installed"));
|
||||
ms_installed = true;
|
||||
|
||||
#if PRODUCTION == 0
|
||||
ms_numberOfFilesOpened[0].bindToCounter("TreeFileLowOpenCount");
|
||||
ms_numberOfFilesOpened[1].bindToCounter("TreeFileMediumOpenCount");
|
||||
ms_numberOfFilesOpened[2].bindToCounter("TreeFileHighOpenCount");
|
||||
ms_sizeOfFilesOpened[0].bindToCounter("TreeFileLowOpenBytes");
|
||||
ms_sizeOfFilesOpened[1].bindToCounter("TreeFileMediumOpenBytes");
|
||||
ms_sizeOfFilesOpened[2].bindToCounter("TreeFileHighOpenBytes");
|
||||
ms_treeFilesOpened.bindToCounter("TreeFilesOpened");
|
||||
#endif
|
||||
|
||||
ExitChain::add(TreeFile::remove, "TreeFile::remove", 0, true);
|
||||
|
||||
// the value 20 is used here for legacy support
|
||||
int const maxPriority = ConfigFile::getKeyInt("SharedFile", "maxSearchPriority", 20);
|
||||
|
||||
// search all the specified skus
|
||||
bool first = true;
|
||||
for (uint32 sku = 0; first || skuBits; first = false, skuBits &= ~(1 << sku), ++sku)
|
||||
{
|
||||
// skip adding and verifying path, tree, and TOC for skus not flagged on the account
|
||||
if (!first && (skuBits & (1 << sku)) == 0)
|
||||
continue;
|
||||
|
||||
// figure out what sku to be loading from. handle 0 to mean no sku numbers are specificed (legacy support)
|
||||
char skuText[8] = { '\0' };
|
||||
if (skuBits)
|
||||
sprintf(skuText, "_%02d_", static_cast<int>(sku));
|
||||
|
||||
// add all the search paths
|
||||
for (int priority = 0; priority <= maxPriority; ++priority)
|
||||
{
|
||||
// add all search paths
|
||||
{
|
||||
char buffer[32];
|
||||
sprintf(buffer, "searchPath%s%d", skuText, priority);
|
||||
|
||||
char const * result;
|
||||
for (int index = 0; (result = ConfigFile::getKeyString("SharedFile", buffer, index, NULL)) != NULL; ++index)
|
||||
TreeFile::addSearchPath(result, priority);
|
||||
}
|
||||
|
||||
// add all search trees
|
||||
{
|
||||
char buffer[32];
|
||||
sprintf(buffer, "searchTree%s%d", skuText, priority);
|
||||
|
||||
char const * result;
|
||||
for (int index = 0; (result = ConfigFile::getKeyString("SharedFile", buffer, index, NULL)) != NULL; ++index)
|
||||
TreeFile::addSearchTree(result, priority);
|
||||
}
|
||||
|
||||
// add in any TOCs
|
||||
{
|
||||
char buffer[32];
|
||||
sprintf(buffer, "searchTOC%s%d", skuText, priority);
|
||||
|
||||
char const * result;
|
||||
for (int index = 0; (result = ConfigFile::getKeyString("SharedFile", buffer, index, NULL)) != NULL; ++index)
|
||||
TreeFile::addSearchTOC(result, priority);
|
||||
}
|
||||
}
|
||||
|
||||
// add cached files
|
||||
{
|
||||
int const numberPreloads = ConfigSharedFile::getNumberOfTreeFilePreloads();
|
||||
for (int i = 0; i < numberPreloads; ++i)
|
||||
{
|
||||
char const * const path = ConfigSharedFile::getTreeFilePreload(i);
|
||||
if (path != 0)
|
||||
{
|
||||
AbstractFile * const file = open(path, AbstractFile::PriorityData, true);
|
||||
if (file)
|
||||
{
|
||||
TreeFile::addCachedFile(path, file);
|
||||
DEBUG_REPORT_LOG(true, ("preloaded %s\n", path));
|
||||
}
|
||||
else
|
||||
{
|
||||
DEBUG_REPORT_LOG(true, ("FAILED preloading %s\n", path));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//-- add an absolute search path after all search nodes
|
||||
{
|
||||
TreeFile::addSearchAbsolute(ConfigFile::getKeyInt("SharedFile", "searchAbsolute", 0, !ms_searchNodes.empty() ? ms_searchNodes.front()->getPriority() + 1 : 0));
|
||||
}
|
||||
|
||||
//-- add search cache after all search nodes
|
||||
{
|
||||
TreeFile::addSearchCache(ConfigFile::getKeyInt("SharedFile", "searchCache", 0, !ms_searchNodes.empty() ? ms_searchNodes.front()->getPriority() + 1 : 0));
|
||||
}
|
||||
|
||||
#if PRODUCTION == 0
|
||||
DebugFlags::registerFlag(ms_debugReportFlagShowMetrics, "SharedFile", "reportTreeFileMetrics", debugReportMetrics);
|
||||
DebugFlags::registerFlag(ms_debugReportFlagShowSearchPaths, "SharedFile", "reportTreeFilePaths", debugReportPaths);
|
||||
DebugFlags::registerFlag(ms_debugLogFlag, "SharedFile", "logTreeFileOpens");
|
||||
DebugFlags::registerFlag(ms_debugLogSynchronousOnly, "SharedFile", "logTreeFileOpensSynchronousOnly");
|
||||
DebugFlags::registerFlag(ms_warnTreeFileOpens, "SharedFile", "warnTreeFileOpens");
|
||||
#endif
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
/**
|
||||
* Remove the TreeFile system.
|
||||
*/
|
||||
|
||||
void TreeFile::remove(void)
|
||||
{
|
||||
clearCachedFiles();
|
||||
|
||||
ms_criticalSection.enter();
|
||||
|
||||
DEBUG_FATAL(!ms_installed, ("not installed"));
|
||||
ms_installed = false;
|
||||
|
||||
// remove all the search nodes
|
||||
const SearchNodes::iterator iEnd = ms_searchNodes.end();
|
||||
for (SearchNodes::iterator i = ms_searchNodes.begin(); i != iEnd; ++i)
|
||||
delete *i;
|
||||
ms_searchNodes.clear();
|
||||
|
||||
ms_searchCache = 0;
|
||||
|
||||
ms_criticalSection.leave();
|
||||
|
||||
#if PRODUCTION == 0
|
||||
DEBUG_WARNING(ms_unexpectedCacheMisses, ("%d unexpected asynchronous loader cache misses occurred", ms_unexpectedCacheMisses));
|
||||
|
||||
DebugFlags::unregisterFlag(ms_debugReportFlagShowMetrics);
|
||||
DebugFlags::unregisterFlag(ms_debugReportFlagShowSearchPaths);
|
||||
DebugFlags::unregisterFlag(ms_debugLogFlag);
|
||||
DebugFlags::unregisterFlag(ms_debugLogSynchronousOnly);
|
||||
DebugFlags::unregisterFlag(ms_warnTreeFileOpens);
|
||||
#endif
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
bool TreeFile::isLoggingFiles()
|
||||
{
|
||||
#if PRODUCTION == 0
|
||||
return ms_debugLogFlag;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
#ifdef _DEBUG
|
||||
void TreeFile::setLogTreeFileOpens(bool const logTreeFileOpens)
|
||||
{
|
||||
ms_debugLogFlag = logTreeFileOpens;
|
||||
}
|
||||
#endif
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
#if PRODUCTION == 0
|
||||
|
||||
void TreeFile::debugReportMetrics()
|
||||
{
|
||||
REPORT_LOG_PRINT(true, ("TreeFile: %d,%d,%d=opened %d,%d,%d=bytes %d=oTotal %d=bTotal %d=cacheMiss\n", ms_numberOfFilesOpened[0].getLastFrameValue(), ms_numberOfFilesOpened[1].getLastFrameValue(), ms_numberOfFilesOpened[2].getLastFrameValue(), ms_sizeOfFilesOpened[0].getLastFrameValue(), ms_sizeOfFilesOpened[1].getLastFrameValue(), ms_sizeOfFilesOpened[2].getLastFrameValue(), ms_numberOfFilesOpenedTotal, ms_sizeOfFilesOpenedTotal, ms_unexpectedCacheMisses));
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
#if PRODUCTION == 0
|
||||
|
||||
void TreeFile::debugReportPaths()
|
||||
{
|
||||
ms_criticalSection.enter();
|
||||
|
||||
DEBUG_REPORT_PRINT(true, ("TreeFile search paths\n"));
|
||||
DEBUG_OUTPUT_STATIC_VIEW_BEGINFRAME("Foundation\\Treefile");
|
||||
DEBUG_OUTPUT_STATIC_VIEW("Foundation\\Treefile", ("TreeFile search paths\n"));
|
||||
|
||||
// Print information about the search paths
|
||||
const SearchNodes::iterator iEnd = ms_searchNodes.end();
|
||||
for (SearchNodes::iterator i = ms_searchNodes.begin(); i != iEnd; ++i)
|
||||
(*i)->debugPrint();
|
||||
|
||||
DEBUG_OUTPUT_STATIC_VIEW_ENDFRAME("Foundation\\Treefile");
|
||||
|
||||
ms_criticalSection.leave();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
bool TreeFile::searchNodePriorityOrder(const SearchNode *a, const SearchNode *b)
|
||||
{
|
||||
return a->getPriority() > b->getPriority();
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
/**
|
||||
* Add a SearchNode to the node list.
|
||||
*
|
||||
* The node list is prioritized from highest to lowest priority. New
|
||||
* nodes with the same priority as old nodes will be inserted after the
|
||||
* last priority match.
|
||||
*/
|
||||
|
||||
void TreeFile::addSearchNode(SearchNode *newNode)
|
||||
{
|
||||
NOT_NULL(newNode);
|
||||
ms_criticalSection.enter();
|
||||
|
||||
SearchNodes::iterator insertionPoint = std::lower_bound(ms_searchNodes.begin(), ms_searchNodes.end(), newNode, searchNodePriorityOrder);
|
||||
IGNORE_RETURN(ms_searchNodes.insert(insertionPoint, newNode));
|
||||
|
||||
ms_criticalSection.leave();
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
/**
|
||||
* Add a SearchAbsolute to the node list.
|
||||
*
|
||||
* This type of search entry will attempt to try to load the file from
|
||||
* the specified path, by prefixing the path before every file name. The
|
||||
* path may be absolute or relative.
|
||||
*
|
||||
* @param path Path to the search directory to add
|
||||
* @param priority Priority for this search entry
|
||||
*/
|
||||
|
||||
void TreeFile::addSearchPath(const char *path, int priority)
|
||||
{
|
||||
addSearchNode(new SearchPath(priority, path));
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
/**
|
||||
* Add a SearchAbsolute to the node list.
|
||||
*
|
||||
* This type of search entry will attempt to try to load the file exactly
|
||||
* as named from the disk subsystem, allowing absolute or relative file
|
||||
* name paths.
|
||||
*
|
||||
* @param priority Search priority for the absolute path
|
||||
*/
|
||||
|
||||
void TreeFile::addSearchAbsolute(int priority)
|
||||
{
|
||||
addSearchNode(new SearchAbsolute(priority));
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
void TreeFile::addSearchCache(int priority)
|
||||
{
|
||||
IS_NULL(ms_searchCache);
|
||||
ms_searchCache = new SearchCache(priority);
|
||||
addSearchNode(ms_searchCache);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
/**
|
||||
* Add a SearchTree to the node list.
|
||||
*
|
||||
* @param fileName File name of the search tree
|
||||
* @param priority Search priority for the search tree
|
||||
*/
|
||||
|
||||
void TreeFile::addSearchTree(const char *fileName, int priority)
|
||||
{
|
||||
if (FileStreamer::exists (fileName))
|
||||
addSearchNode(new SearchTree(priority, fileName));
|
||||
else
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
DEBUG_FATAL(true, ("TreeFile::addSearchTree - [%s] not found", fileName));
|
||||
#else
|
||||
WARNING(true, ("TreeFile::addSearchTree - [%s] not found", fileName));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
/**
|
||||
* Add a SearchTOC to the node list.
|
||||
*
|
||||
* @param fileName File name of the search tree
|
||||
*/
|
||||
|
||||
void TreeFile::addSearchTOC(const char *fileName, int priority)
|
||||
{
|
||||
if (FileStreamer::exists (fileName))
|
||||
addSearchNode(new SearchTOC(priority, fileName));
|
||||
else
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
DEBUG_FATAL(true, ("TreeFile::addSearchTOC - [%s] not found", fileName));
|
||||
#else
|
||||
WARNING(true, ("TreeFile::addSearchTOC - [%s] not found", fileName));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
/**
|
||||
* Validate a SearchTree.
|
||||
*
|
||||
* @param fileName File name of the search tree to validate
|
||||
*/
|
||||
|
||||
bool TreeFile::validateSearchTree(const char *fileName)
|
||||
{
|
||||
return SearchTree::validate(fileName);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
/**
|
||||
* Remove all the search settings.
|
||||
*
|
||||
* This routine will remove all the search trees, paths, and absolute
|
||||
* settings from the TreeFile class.
|
||||
*/
|
||||
|
||||
void TreeFile::removeAllSearches(void)
|
||||
{
|
||||
ms_criticalSection.enter();
|
||||
|
||||
// remove all the search nodes
|
||||
const SearchNodes::iterator iEnd = ms_searchNodes.end();
|
||||
for (SearchNodes::iterator i = ms_searchNodes.begin(); i != iEnd; ++i)
|
||||
delete *i;
|
||||
ms_searchNodes.clear();
|
||||
|
||||
ms_criticalSection.leave();
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
/**
|
||||
* Find the node (if any) the requested file is in.
|
||||
*
|
||||
* @return Pointer to the highest priority node containing the file. If the
|
||||
* file is not found, NULL is returned. If checking for filename collisions is
|
||||
* set on, then this function DEBUG_FATALS if multiple files match
|
||||
*/
|
||||
|
||||
TreeFile::SearchNode *TreeFile::find(const char *fileName)
|
||||
{
|
||||
DEBUG_FATAL(!ms_installed, ("not installed"));
|
||||
|
||||
if (!fileName)
|
||||
{
|
||||
DEBUG_WARNING(true, ("TreeFile::find() Cannot find a null filename"));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (fileName[0] == '\0')
|
||||
{
|
||||
DEBUG_WARNING(true, ("TreeFile::find() Cannot find an empty filename"));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// search the list of nodes looking to see if the specified file exists
|
||||
bool deleted = false;
|
||||
const SearchNodes::iterator iEnd = ms_searchNodes.end();
|
||||
for (SearchNodes::iterator i = ms_searchNodes.begin(); !deleted && i != iEnd; ++i)
|
||||
if ((*i)->exists(fileName, deleted))
|
||||
return *i;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
/**
|
||||
* Check if a specified file can be found.
|
||||
*
|
||||
* @param fileName File name to check for existance
|
||||
* @return True if the file exists, otherwise false
|
||||
*/
|
||||
|
||||
bool TreeFile::exists(const char *fileName)
|
||||
{
|
||||
char fixedFileName[Os::MAX_PATH_LENGTH];
|
||||
fixUpFileName(fixedFileName, fileName, true);
|
||||
|
||||
DEBUG_FATAL(!ms_installed, ("TreeFile::exists not installed"));
|
||||
|
||||
#if PRODUCTION == 0
|
||||
FileManifest::addNewManifestEntry(fixedFileName, 0);
|
||||
#endif
|
||||
|
||||
return (find(fixedFileName) != NULL);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
int TreeFile::getFileSize(const char *fileName)
|
||||
{
|
||||
char fixedFileName[Os::MAX_PATH_LENGTH];
|
||||
fixUpFileName(fixedFileName, fileName, true);
|
||||
|
||||
// search the list of nodes looking to see if the specified file exists
|
||||
bool deleted = false;
|
||||
const SearchNodes::iterator iEnd = ms_searchNodes.end();
|
||||
for (SearchNodes::iterator i = ms_searchNodes.begin(); !deleted && i != iEnd; ++i)
|
||||
{
|
||||
int size = (*i)->getFileSize(fixedFileName, deleted);
|
||||
if (size >= 0)
|
||||
return size;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
/**
|
||||
* This function assumes the output buffer is large enough.
|
||||
* The output path will be the same length or shorter than the input path.
|
||||
*/
|
||||
|
||||
void TreeFile::fixUpFileName(char *output, const char *fileName, bool warning)
|
||||
{
|
||||
bool currentIsSlash = false;
|
||||
bool previousIsSlash = false;
|
||||
|
||||
UNREF(warning);
|
||||
|
||||
#ifdef _DEBUG
|
||||
// since we only emit a warning the first time we see the malformed file name, if we invert the warning-desired flag
|
||||
// it will behave exactly as we want
|
||||
warning = !warning;
|
||||
#endif
|
||||
|
||||
// skip this warnings for paths
|
||||
const char *f = fileName;
|
||||
|
||||
// skip leading "\" or "/"
|
||||
while (f[0] == '\\' || f[0] == '/')
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
if (!warning)
|
||||
{
|
||||
WARNING(true, ("Malformed file name: %s'", fileName));
|
||||
warning = true;
|
||||
}
|
||||
#endif
|
||||
|
||||
++f;
|
||||
}
|
||||
|
||||
// skip leading ".\" or "./"
|
||||
while (f[0] == '.' && (f[1] == '\\' || f[1] == '/'))
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
if (!warning)
|
||||
{
|
||||
WARNING(true, ("Malformed file name: %s'", fileName));
|
||||
warning = true;
|
||||
}
|
||||
#endif
|
||||
|
||||
f += 2;
|
||||
}
|
||||
|
||||
// skip leading "..\" or "../"
|
||||
while (f[0] == '.' && f[1] == '.' && (f[2] == '\\' || f[2] == '/'))
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
if (!warning)
|
||||
{
|
||||
WARNING(true, ("Malformed file name: %s'", fileName));
|
||||
warning = true;
|
||||
}
|
||||
#endif
|
||||
|
||||
f += 3;
|
||||
}
|
||||
|
||||
for (; *f; ++f)
|
||||
{
|
||||
// convert all backslashes to forward slashes, and lowercase all the characters
|
||||
const char c = *f;
|
||||
if (c == '\\' || c == '/')
|
||||
{
|
||||
currentIsSlash = true;
|
||||
*output = '/';
|
||||
}
|
||||
else
|
||||
{
|
||||
currentIsSlash = false;
|
||||
*output = static_cast<char>(tolower(c));
|
||||
}
|
||||
|
||||
// strip repeated forward slashes
|
||||
if (!currentIsSlash || !previousIsSlash)
|
||||
{
|
||||
++output;
|
||||
previousIsSlash = currentIsSlash;
|
||||
}
|
||||
#ifdef _DEBUG
|
||||
else
|
||||
if (!warning)
|
||||
{
|
||||
WARNING(true, ("Malformed file name: %s'", fileName));
|
||||
warning = true;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
*output = '\0';
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
/**
|
||||
* Fix up the file name.
|
||||
*
|
||||
* This function will lowercase the string, change all slashes to forward slashes, remove duplicate slashes,
|
||||
* and more. This function will not strip off full paths that are part of the search directories.
|
||||
*
|
||||
* The output buffer must be at least as large as the input buffer. The output buffer may be the same as
|
||||
* the input buffer, however warning messages may be garbled in that case.
|
||||
*/
|
||||
|
||||
void TreeFile::fixUpFileName(const char *fileName, char *outName)
|
||||
{
|
||||
fixUpFileName(outName, fileName, false);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
/**
|
||||
* Find the node (if any) the requested file is in.
|
||||
*
|
||||
* @return Pointer to the highest priority node containing the file. If the
|
||||
* file is not found, NULL is returned.
|
||||
*/
|
||||
|
||||
bool TreeFile::getPathName(const char *fileName, char *pathName, int pathNameLength)
|
||||
{
|
||||
NOT_NULL(fileName);
|
||||
NOT_NULL(pathName);
|
||||
DEBUG_FATAL(pathNameLength < 1, ("invalid pathNameLength %d", pathNameLength));
|
||||
DEBUG_FATAL(!ms_installed, ("not installed"));
|
||||
|
||||
// search the list of nodes looking to see if the specified file exists
|
||||
SearchNode *node = find(fileName);
|
||||
if (node)
|
||||
{
|
||||
node->getPathName(fileName, pathName, pathNameLength);
|
||||
return true;
|
||||
}
|
||||
|
||||
*pathName = '\0';
|
||||
return false;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
/**
|
||||
* Open a file.
|
||||
*
|
||||
* The handle of the file is copied to the handlePointer. The handle
|
||||
* is -1 on failure.
|
||||
*
|
||||
* If the allowFail parameter is false, this routine will call Fatal
|
||||
* if the file could not be opened
|
||||
*
|
||||
* @param fileName File name to open
|
||||
* @param allowFail True to return false on failure, otherwise Fatal
|
||||
* @return pointer to a newly allocated AbstractFile-derived class (caller must delete), NULL if failure.
|
||||
*/
|
||||
|
||||
AbstractFile* TreeFile::open(const char *fileName, AbstractFile::PriorityType priority, bool allowFail)
|
||||
{
|
||||
AbstractFile *file = NULL;
|
||||
|
||||
char fixedFileName[Os::MAX_PATH_LENGTH];
|
||||
fixUpFileName(fixedFileName, fileName, true);
|
||||
|
||||
// check the cache to see if the file has been preloaded
|
||||
ms_criticalSection.enter();
|
||||
const bool haveCachedFiles = ms_haveCachedFiles;
|
||||
ms_criticalSection.leave();
|
||||
|
||||
if (haveCachedFiles)
|
||||
{
|
||||
ms_criticalSection.enter();
|
||||
|
||||
CachedFilesMap::iterator cachedIterator = cachedFilesMap.find(fixedFileName);
|
||||
if (cachedIterator != cachedFilesMap.end())
|
||||
{
|
||||
file = cachedIterator->second;
|
||||
cachedIterator->second = NULL;
|
||||
}
|
||||
|
||||
ms_criticalSection.leave();
|
||||
|
||||
#if PRODUCTION == 0
|
||||
if (!file && Os::isMainThread())
|
||||
{
|
||||
DEBUG_REPORT_LOG(true, ("unexpected cache miss %d %s\n", ms_unexpectedCacheMisses, fixedFileName));
|
||||
++ms_unexpectedCacheMisses;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (file)
|
||||
{
|
||||
#if PRODUCTION == 0
|
||||
REPORT_LOG(ms_debugLogFlag && !ms_debugLogSynchronousOnly, ("TF::open(%s) %s @ [cached]\n", cms_priorityStrings[priority], fixedFileName));
|
||||
if (PixCounter::connectedToPixProfiler())
|
||||
ms_treeFilesOpened.append("C\t%d\t%s\n", file->length(), fixedFileName);
|
||||
|
||||
FileManifest::addNewManifestEntry(fixedFileName, file->length());
|
||||
#endif
|
||||
return file;
|
||||
}
|
||||
}
|
||||
|
||||
#if PRODUCTION == 0
|
||||
bool first = true;
|
||||
#endif
|
||||
|
||||
bool deleted = false;
|
||||
const SearchNodes::iterator iEnd = ms_searchNodes.end();
|
||||
for (SearchNodes::iterator i = ms_searchNodes.begin(); !file && !deleted && i != iEnd; ++i)
|
||||
{
|
||||
file = (*i)->open(fixedFileName, priority, deleted);
|
||||
|
||||
#if PRODUCTION == 0
|
||||
if (file)
|
||||
{
|
||||
if (PixCounter::connectedToPixProfiler())
|
||||
ms_treeFilesOpened.append("%s\t%d\t%s\n", first ? "F" : cms_priorityStrings[priority], file->length(), fixedFileName);
|
||||
|
||||
if (ms_debugLogFlag || ms_warnTreeFileOpens)
|
||||
{
|
||||
char buffer[Os::MAX_PATH_LENGTH];
|
||||
(*i)->getPathName(fixedFileName, buffer, sizeof(buffer));
|
||||
|
||||
if (ms_warnTreeFileOpens)
|
||||
WARNING(true, ("TF::open(%s) %s @ %s, [size=%d]\n", cms_priorityStrings[priority], fixedFileName, buffer, file->length()));
|
||||
else
|
||||
{
|
||||
REPORT_LOG(!ms_debugLogSynchronousOnly || (ms_debugLogSynchronousOnly && (priority == AbstractFile::PriorityData) && (*i != ms_searchCache)), ("TF::open(%s) %s @ %s, [size=%d]\n", cms_priorityStrings[priority], fixedFileName, buffer, file->length()));
|
||||
DEBUG_OUTPUT_CHANNEL("Foundation\\Treefile", ("TF::open %s -- %s\n", fixedFileName, buffer));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
first = false;
|
||||
#endif
|
||||
}
|
||||
|
||||
if (!file)
|
||||
{
|
||||
FATAL(!allowFail, ("open '%s' not found", fixedFileName));
|
||||
|
||||
#if PRODUCTION == 0
|
||||
FileManifest::addNewManifestEntry(fixedFileName, 0);
|
||||
#endif
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const int length = file->length();
|
||||
++ms_numberOfFilesOpenedTotal;
|
||||
ms_sizeOfFilesOpenedTotal += length;
|
||||
|
||||
#if PRODUCTION == 0
|
||||
++ms_numberOfFilesOpened[priority];
|
||||
ms_sizeOfFilesOpened[priority] += length;
|
||||
|
||||
FileManifest::addNewManifestEntry(fixedFileName, length);
|
||||
#endif
|
||||
|
||||
return file;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
int TreeFile::getNumberOfSearchPaths(void)
|
||||
{
|
||||
int count = 0;
|
||||
|
||||
const SearchNodes::iterator iEnd = ms_searchNodes.end();
|
||||
for (SearchNodes::iterator i = ms_searchNodes.begin(); i != iEnd; ++i)
|
||||
if (dynamic_cast<const SearchPath*>(*i) != 0)
|
||||
count++;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
const char *TreeFile::getSearchPath(int index)
|
||||
{
|
||||
int count = 0;
|
||||
const SearchNodes::iterator iEnd = ms_searchNodes.end();
|
||||
for (SearchNodes::iterator i = ms_searchNodes.begin(); i != iEnd; ++i)
|
||||
{
|
||||
const SearchPath *searchPath = dynamic_cast<const SearchPath*>(*i);
|
||||
if (searchPath != NULL)
|
||||
{
|
||||
if (count == index)
|
||||
return searchPath->getPathName();
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------
|
||||
/**
|
||||
* This function will return the shortest trailing path of its input that
|
||||
* can be loaded by the TreeFile. If no files can be loaded, this routine
|
||||
* will return NULL.
|
||||
*/
|
||||
|
||||
const char *TreeFile::getShortestExistingPath(const char *path)
|
||||
{
|
||||
NOT_NULL(path);
|
||||
|
||||
if (!*path)
|
||||
return NULL;
|
||||
|
||||
const char *result = NULL;
|
||||
do
|
||||
{
|
||||
// check if this one exists
|
||||
if (exists(path))
|
||||
result = path;
|
||||
|
||||
// advance past the next path separator
|
||||
while (*path && *path != '\\' && *path != '/')
|
||||
++path;
|
||||
if (*path)
|
||||
++path;
|
||||
|
||||
} while (*path);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
static bool pathcompare(const char *lhs, const char *rhs)
|
||||
{
|
||||
for ( ; *lhs && *rhs; ++lhs, ++rhs)
|
||||
{
|
||||
char l = static_cast<char>(tolower(*lhs));
|
||||
if (l == '\\')
|
||||
l = '/';
|
||||
|
||||
char r = static_cast<char>(tolower(*rhs));
|
||||
if (r == '\\')
|
||||
r = '/';
|
||||
|
||||
if (l != r)
|
||||
return false;
|
||||
}
|
||||
|
||||
return *lhs == '\0' && *rhs == '/';
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
bool TreeFile::stripTreeFileSearchPathFromFile(const char *inputPath, char *outputPath, int outputPathBufferSize)
|
||||
{
|
||||
// convert the input to an absolute patah
|
||||
char buffer[Os::MAX_PATH_LENGTH];
|
||||
if (!Os::getAbsolutePath(inputPath, buffer, sizeof(buffer)))
|
||||
return false;
|
||||
|
||||
// scan all the search nodes
|
||||
const SearchNodes::iterator iEnd = ms_searchNodes.end();
|
||||
for (SearchNodes::iterator i = ms_searchNodes.begin(); i != iEnd; ++i)
|
||||
{
|
||||
// make sure it's a relative search path
|
||||
const SearchPath *searchPath = dynamic_cast<const SearchPath*>(*i);
|
||||
if (searchPath != NULL)
|
||||
{
|
||||
// check to see if the the search path is a prefix for the requested path
|
||||
const char *path = searchPath->getPathName();
|
||||
|
||||
if (pathcompare(path, buffer))
|
||||
{
|
||||
// make sure the output buffer has enough room
|
||||
int const pathLength = static_cast<int>(strlen(path));
|
||||
int const outputLength = static_cast<int>(strlen(buffer)) - pathLength;
|
||||
if (outputLength > outputPathBufferSize)
|
||||
return false;
|
||||
|
||||
// return the buffer
|
||||
strcpy(outputPath, buffer + pathLength + 1);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
bool TreeFile::stripTreeFileSearchPathFromFile(const std::string &inputPath, std::string &outputPath)
|
||||
{
|
||||
char buffer[Os::MAX_PATH_LENGTH];
|
||||
|
||||
if (!stripTreeFileSearchPathFromFile(inputPath.c_str(), buffer, sizeof(buffer)))
|
||||
return false;
|
||||
|
||||
outputPath = buffer;
|
||||
return true;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
void TreeFile::addCachedFile(const char *fileName, AbstractFile *file)
|
||||
{
|
||||
ms_criticalSection.enter();
|
||||
|
||||
const bool result = cachedFilesMap.insert(CachedFilesMap::value_type(fileName, file)).second;
|
||||
UNREF(result);
|
||||
DEBUG_FATAL(!result, ("item was already present"));
|
||||
ms_haveCachedFiles = true;
|
||||
|
||||
ms_criticalSection.leave();
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
void TreeFile::clearCachedFiles()
|
||||
{
|
||||
ms_criticalSection.enter();
|
||||
|
||||
// delete any remaining files
|
||||
CachedFilesMap::iterator iEnd = cachedFilesMap.end();
|
||||
for (CachedFilesMap::iterator i = cachedFilesMap.begin(); i != iEnd; ++i)
|
||||
if (i->second)
|
||||
{
|
||||
DEBUG_REPORT_LOG(true, ("Unused cached tree file %s(%d)\n", i->first, i->second->length()));
|
||||
delete i->second;
|
||||
}
|
||||
|
||||
cachedFilesMap.clear();
|
||||
ms_haveCachedFiles = false;
|
||||
|
||||
ms_criticalSection.leave();
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
int TreeFile::getNumberOfFilesOpenedTotal()
|
||||
{
|
||||
return ms_numberOfFilesOpenedTotal;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
int TreeFile::getSizeOfFilesOpenedTotal()
|
||||
{
|
||||
return ms_sizeOfFilesOpenedTotal;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
int TreeFile::cacheFile(char const * const fileName)
|
||||
{
|
||||
NOT_NULL(ms_searchCache);
|
||||
return ms_searchCache->addCachedFile(fileName);
|
||||
}
|
||||
|
||||
// ======================================================================
|
||||
|
||||
AbstractFile* TreeFile::TreeFileFactory::createFile(const char *filename, const char *open_type)
|
||||
{
|
||||
UNREF(open_type);
|
||||
return open(filename, AbstractFile::PriorityData, true);
|
||||
}
|
||||
|
||||
// ======================================================================
|
||||
|
||||
@@ -0,0 +1,143 @@
|
||||
// ======================================================================
|
||||
//
|
||||
// TreeFile.h
|
||||
// Portions copyright 1998 Bootprint Entertainment
|
||||
// Portions copyright 2001-2002 Sony Online Entertainment
|
||||
// All Rights Reserved.
|
||||
//
|
||||
// ======================================================================
|
||||
|
||||
#ifndef INCLUDED_TreeFile_H
|
||||
#define INCLUDED_TreeFile_H
|
||||
|
||||
// ======================================================================
|
||||
|
||||
class Compressor;
|
||||
|
||||
// @todo codereorg remove dependency on mutex
|
||||
class Mutex;
|
||||
#include "fileInterface/AbstractFile.h"
|
||||
|
||||
/*NOTE: TreeFile_SearchNode.h is included AFTER TreeFile is defined to allow
|
||||
the subclasses to be broken out into a separate file module without creating
|
||||
additional include dependencies on the modules that use TreeFile.
|
||||
*/
|
||||
|
||||
// ======================================================================
|
||||
|
||||
/**
|
||||
* Provide access to files, whether in a treefile or on actual disk.
|
||||
*
|
||||
* This class is multi-thread safe for modifying the search order, opening
|
||||
* files, and closing files. Applications should not attempt to read a
|
||||
* single opened file from multiple threads at the same time, but it is
|
||||
* acceptable to read from different files in different threads.
|
||||
*/
|
||||
class TreeFile
|
||||
{
|
||||
private:
|
||||
|
||||
// The following are defined in a seperate file (TreeFile_SearchNode.*)
|
||||
class SearchNode;
|
||||
class SearchPath;
|
||||
class SearchAbsolute;
|
||||
class SearchTree;
|
||||
class SearchTOC;
|
||||
class SearchCache;
|
||||
|
||||
friend class SearchNode;
|
||||
friend class TreeFileBuilder;
|
||||
friend class TreeFileBuilderHelper;
|
||||
friend class TreeFileExtractor;
|
||||
|
||||
public:
|
||||
|
||||
class TreeFileFactory : public AbstractFileFactory
|
||||
{
|
||||
public:
|
||||
virtual AbstractFile* createFile(const char *filename, const char *open_type);
|
||||
};
|
||||
friend class TreeFileFactory;
|
||||
|
||||
public:
|
||||
|
||||
static void install(uint32 skuBits);
|
||||
static void remove();
|
||||
|
||||
static bool isLoggingFiles();
|
||||
|
||||
#ifdef _DEBUG
|
||||
static void setLogTreeFileOpens(bool logTreeFileOpens);
|
||||
#endif
|
||||
|
||||
static void debugReportPaths();
|
||||
static void debugReportMetrics();
|
||||
|
||||
static void addSearchPath(const char *path, int priority);
|
||||
static void addSearchAbsolute(int priority);
|
||||
static void addSearchTree(const char *fileName, int priority);
|
||||
static void addSearchTOC(const char *fileName, int priority);
|
||||
static bool validateSearchTree(const char *fileName);
|
||||
static void removeAllSearches();
|
||||
|
||||
static bool exists(const char *fileName);
|
||||
static int getFileSize(const char *fileName);
|
||||
static DLLEXPORT AbstractFile *open(const char *filename, AbstractFile::PriorityType, bool allowFail);
|
||||
|
||||
static void fixUpFileName(const char *fileName, char *outName);
|
||||
static bool getPathName(const char *fileName, char *pathName, int pathNameLength);
|
||||
static const char *getShortestExistingPath(const char *fileName);
|
||||
static bool stripTreeFileSearchPathFromFile(const std::string &inputPath, std::string &outputPath);
|
||||
static bool stripTreeFileSearchPathFromFile(const char *inputPath, char *outputPath, int outputPathBufferLength);
|
||||
|
||||
|
||||
static int getNumberOfSearchPaths();
|
||||
static const char *getSearchPath(int index);
|
||||
|
||||
static void addCachedFile(const char *fileName, AbstractFile *file);
|
||||
static void clearCachedFiles();
|
||||
|
||||
static int getNumberOfFilesOpenedTotal();
|
||||
static int getSizeOfFilesOpenedTotal();
|
||||
|
||||
static int cacheFile(char const * fileName);
|
||||
|
||||
private:
|
||||
|
||||
typedef stdvector<SearchNode *>::fwd SearchNodes;
|
||||
|
||||
private:
|
||||
|
||||
/// disabled
|
||||
TreeFile();
|
||||
/// disabled
|
||||
TreeFile(const TreeFile &);
|
||||
/// disabled
|
||||
TreeFile &operator =(const TreeFile &);
|
||||
|
||||
static void addSearchCache(int priority);
|
||||
static bool searchNodePriorityOrder(const SearchNode *a, const SearchNode *b);
|
||||
static void addSearchNode(SearchNode *newNode);
|
||||
static SearchNode *find(const char *fileName);
|
||||
|
||||
static void fixUpFileName(char *output, const char *filename, bool warning);
|
||||
|
||||
private:
|
||||
|
||||
static bool ms_installed;
|
||||
static bool ms_haveCachedFiles;
|
||||
static Mutex ms_criticalSection;
|
||||
static SearchNodes ms_searchNodes;
|
||||
static SearchCache * ms_searchCache;
|
||||
|
||||
static bool ms_debugReportFlagShowMetrics;
|
||||
static bool ms_debugReportFlagShowSearchPaths;
|
||||
static bool ms_debugLogFlag;
|
||||
static int ms_numberOfFilesOpenedTotal;
|
||||
static int ms_sizeOfFilesOpenedTotal;
|
||||
static int ms_unexpectedCacheMisses;
|
||||
};
|
||||
|
||||
// ======================================================================
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,346 @@
|
||||
// ======================================================================
|
||||
//
|
||||
// TreeFile_SearchNode.h
|
||||
// Portions copyright 1998 Bootprint Entertainment
|
||||
// Portions copyright 2001-2002 Sony Online Entertainment
|
||||
// All Rights Reserved.
|
||||
//
|
||||
// ======================================================================
|
||||
|
||||
#ifndef INCLUDED_TreeFile_SearchNode_H
|
||||
#define INCLUDED_TreeFile_SearchNode_H
|
||||
|
||||
// ======================================================================
|
||||
|
||||
class CrcString;
|
||||
class MemoryBlockManager;
|
||||
|
||||
#include "sharedFile/TreeFile.h"
|
||||
#include "sharedFile/FileStreamer.h"
|
||||
#include "sharedFoundation/LessPointerComparator.h"
|
||||
#include "sharedFoundation/Os.h"
|
||||
#include "sharedFoundation/Tag.h"
|
||||
|
||||
// ======================================================================
|
||||
|
||||
/* This module defines the inner classes of Treefile, to help keep TreeFile.h clean and easy to read.
|
||||
Since all of these classes are private, only TreeFile.h/cpp needs to even know these files exist.
|
||||
*/
|
||||
|
||||
class TreeFile::SearchNode
|
||||
{
|
||||
public:
|
||||
|
||||
explicit SearchNode(int priority);
|
||||
virtual ~SearchNode();
|
||||
|
||||
int getPriority() const;
|
||||
|
||||
virtual void debugPrint() = 0;
|
||||
virtual bool exists(const char *fileName, bool &deleted) const = 0;
|
||||
virtual int getFileSize(const char *fileName, bool &deleted) const = 0;
|
||||
virtual void getPathName(const char *fileName, char *pathName, int pathNameLength) const = 0;
|
||||
virtual AbstractFile *open(const char *fileName, AbstractFile::PriorityType priority, bool &deleted) = 0;
|
||||
|
||||
private:
|
||||
|
||||
SearchNode();
|
||||
SearchNode(const SearchNode &);
|
||||
SearchNode &operator =(const SearchNode &);
|
||||
|
||||
private:
|
||||
|
||||
const int m_priority;
|
||||
};
|
||||
|
||||
// ======================================================================
|
||||
|
||||
inline int TreeFile::SearchNode::getPriority() const
|
||||
{
|
||||
return m_priority;
|
||||
}
|
||||
|
||||
// ======================================================================
|
||||
|
||||
class TreeFile::SearchPath : public TreeFile::SearchNode
|
||||
{
|
||||
public:
|
||||
|
||||
SearchPath(int priority, const char *path);
|
||||
virtual ~SearchPath();
|
||||
|
||||
virtual void debugPrint();
|
||||
virtual bool exists(const char *fileName, bool &deleted) const;
|
||||
virtual int getFileSize(const char *fileName, bool &deleted) const;
|
||||
virtual void getPathName(const char *fileName, char *pathName, int pathNameLength) const;
|
||||
virtual AbstractFile *open(const char *fileName, AbstractFile::PriorityType priority, bool &deleted);
|
||||
|
||||
const char *getPathName() const; //lint !e1411 // Warning -- member with different signature hides virtual member (bug in PC-Lint, incorrect warning)
|
||||
|
||||
private:
|
||||
|
||||
SearchPath();
|
||||
SearchPath(const SearchPath &);
|
||||
SearchPath &operator =(const SearchPath &);
|
||||
|
||||
void makeAbsolutePath(const char *fileName, char *buffer) const;
|
||||
|
||||
private:
|
||||
|
||||
char *m_pathName;
|
||||
int m_pathNameLength;
|
||||
};
|
||||
|
||||
// ======================================================================
|
||||
|
||||
inline const char *TreeFile::SearchPath::getPathName() const
|
||||
{
|
||||
return m_pathName;
|
||||
}
|
||||
|
||||
// ======================================================================
|
||||
|
||||
class TreeFile::SearchAbsolute : public TreeFile::SearchNode
|
||||
{
|
||||
public:
|
||||
|
||||
explicit SearchAbsolute(int priority);
|
||||
virtual ~SearchAbsolute();
|
||||
|
||||
virtual void debugPrint();
|
||||
virtual bool exists(const char *fileName, bool &deleted) const;
|
||||
virtual int getFileSize(const char *fileName, bool &deleted) const;
|
||||
virtual void getPathName(const char *fileName, char *pathName, int pathNameLength) const;
|
||||
virtual AbstractFile *open(const char *fileName, AbstractFile::PriorityType priority, bool &deleted);
|
||||
|
||||
private:
|
||||
|
||||
SearchAbsolute();
|
||||
SearchAbsolute(const SearchAbsolute &);
|
||||
SearchAbsolute &operator =(const SearchAbsolute &);
|
||||
};
|
||||
|
||||
// ======================================================================
|
||||
|
||||
class TreeFile::SearchTree : public TreeFile::SearchNode
|
||||
{
|
||||
// these friend classes are from the treefile tool
|
||||
friend class TOC;
|
||||
friend class FileEntry;
|
||||
friend class TreeFileBuilder;
|
||||
friend class TreeFileExtractor;
|
||||
|
||||
public:
|
||||
|
||||
static bool validate(const char *fileName);
|
||||
static bool isCompressed(int compressorIndex);
|
||||
|
||||
public:
|
||||
|
||||
SearchTree(int priority, const char *fileName);
|
||||
virtual ~SearchTree();
|
||||
|
||||
virtual void debugPrint();
|
||||
virtual bool exists(const char *fileName, bool &deleted) const;
|
||||
virtual int getFileSize(const char *fileName, bool &deleted) const;
|
||||
virtual void getPathName(const char *fileName, char *pathName, int pathNameLength) const;
|
||||
virtual AbstractFile *open(const char *fileName, AbstractFile::PriorityType priority, bool &deleted);
|
||||
|
||||
private:
|
||||
|
||||
// disabled
|
||||
SearchTree();
|
||||
|
||||
// disabled
|
||||
SearchTree(const SearchTree &);
|
||||
|
||||
// disabled
|
||||
SearchTree &operator =(const SearchTree &);
|
||||
|
||||
private:
|
||||
|
||||
bool localExists(const char *fileName, int *index, bool &deleted) const;
|
||||
|
||||
private:
|
||||
|
||||
enum CompressorType
|
||||
{
|
||||
CT_none,
|
||||
CT_deprecated,
|
||||
CT_zlib,
|
||||
CT_max
|
||||
};
|
||||
|
||||
struct Header
|
||||
{
|
||||
Tag token;
|
||||
Tag version;
|
||||
uint32 numberOfFiles;
|
||||
uint32 tocOffset;
|
||||
uint32 tocCompressor;
|
||||
uint32 sizeOfTOC;
|
||||
uint32 blockCompressor;
|
||||
uint32 sizeOfNameBlock;
|
||||
uint32 uncompSizeOfNameBlock;
|
||||
};
|
||||
|
||||
public:
|
||||
|
||||
struct TableOfContentsEntry
|
||||
{
|
||||
uint32 crc;
|
||||
int length;
|
||||
int offset;
|
||||
int compressor;
|
||||
int compressedLength;
|
||||
int fileNameOffset;
|
||||
};
|
||||
|
||||
private:
|
||||
|
||||
char *m_treeFileName;
|
||||
FileStreamer::File *m_treeFile;
|
||||
uint32 m_version;
|
||||
int m_numberOfFiles;
|
||||
char *m_fileNames;
|
||||
TableOfContentsEntry *m_tableOfContents;
|
||||
};
|
||||
|
||||
// ======================================================================
|
||||
|
||||
inline bool TreeFile::SearchTree::isCompressed(int compressorIndex)
|
||||
{
|
||||
DEBUG_FATAL(compressorIndex == CT_deprecated, ("No longer supported compressor"));
|
||||
return compressorIndex != static_cast<int>(CT_none);
|
||||
}
|
||||
|
||||
// ======================================================================
|
||||
|
||||
class TreeFile::SearchTOC : public TreeFile::SearchNode
|
||||
{
|
||||
public:
|
||||
|
||||
static bool validate(const char *fileName);
|
||||
static bool isCompressed(int compressorIndex);
|
||||
|
||||
public:
|
||||
|
||||
SearchTOC(int priority, const char *fileName);
|
||||
virtual ~SearchTOC();
|
||||
|
||||
virtual void debugPrint();
|
||||
virtual bool exists(const char *fileName, bool &deleted) const;
|
||||
virtual int getFileSize(const char *fileName, bool &deleted) const;
|
||||
virtual void getPathName(const char *fileName, char *pathName, int pathNameLength) const;
|
||||
virtual AbstractFile *open(const char *fileName, AbstractFile::PriorityType priority, bool &deleted);
|
||||
|
||||
private:
|
||||
|
||||
// disabled
|
||||
SearchTOC();
|
||||
|
||||
// disabled
|
||||
SearchTOC(const SearchTOC &);
|
||||
|
||||
// disabled
|
||||
SearchTOC &operator =(const SearchTOC &);
|
||||
|
||||
private:
|
||||
|
||||
bool TreeFile::SearchTOC::localExists(const char *fileName, int *index) const;
|
||||
|
||||
private:
|
||||
|
||||
enum CompressorType
|
||||
{
|
||||
CT_none,
|
||||
CT_deprecated,
|
||||
CT_zlib,
|
||||
CT_max
|
||||
};
|
||||
|
||||
struct Header
|
||||
{
|
||||
Tag token;
|
||||
Tag version;
|
||||
uint8 tocCompressor;
|
||||
uint8 fileNameBlockCompressor;
|
||||
uint8 unusedOne;
|
||||
uint8 unusedTwo;
|
||||
uint32 numberOfFiles;
|
||||
uint32 sizeOfTOC;
|
||||
uint32 sizeOfNameBlock;
|
||||
uint32 uncompSizeOfNameBlock;
|
||||
uint32 numberOfTreeFiles;
|
||||
uint32 sizeOfTreeFileNameBlock;
|
||||
};
|
||||
|
||||
public:
|
||||
|
||||
// unused entry is there to verify 32 bit word alignment
|
||||
struct TableOfContentsEntry
|
||||
{
|
||||
uint8 compressor;
|
||||
uint8 unused;
|
||||
uint16 treeFileIndex;
|
||||
uint32 crc;
|
||||
uint32 fileNameOffset;
|
||||
uint32 offset;
|
||||
uint32 length;
|
||||
uint32 compressedLength;
|
||||
};
|
||||
|
||||
private:
|
||||
|
||||
char *m_TOCFileName;
|
||||
FileStreamer::File *m_TOCFile;
|
||||
FileStreamer::File **m_treeFiles;
|
||||
uint32 m_numberOfTreeFiles;
|
||||
uint32 m_numberOfFiles;
|
||||
char *m_treeFileNames;
|
||||
char **m_treeFileNamePointers;
|
||||
TableOfContentsEntry *m_tableOfContents;
|
||||
char *m_fileNames;
|
||||
};
|
||||
|
||||
// ======================================================================
|
||||
|
||||
inline bool TreeFile::SearchTOC::isCompressed(int compressorIndex)
|
||||
{
|
||||
DEBUG_FATAL(compressorIndex == CT_deprecated, ("No longer supported compressor"));
|
||||
return compressorIndex != static_cast<int>(CT_none);
|
||||
}
|
||||
|
||||
// ======================================================================
|
||||
|
||||
class TreeFile::SearchCache : public TreeFile::SearchNode
|
||||
{
|
||||
public:
|
||||
|
||||
explicit SearchCache(int priority);
|
||||
virtual ~SearchCache();
|
||||
|
||||
int addCachedFile(char const * fileName);
|
||||
|
||||
virtual void debugPrint();
|
||||
virtual bool exists(char const * fileName, bool & deleted) const;
|
||||
virtual int getFileSize(char const * fileName, bool & deleted) const;
|
||||
virtual void getPathName(char const * fileName, char * pathName, int pathNameLength) const;
|
||||
virtual AbstractFile *open(char const * fileName, AbstractFile::PriorityType priority, bool & deleted);
|
||||
|
||||
private:
|
||||
|
||||
SearchCache();
|
||||
SearchCache(const SearchCache &);
|
||||
SearchCache &operator =(const SearchCache &);
|
||||
|
||||
private:
|
||||
|
||||
class CachedFile;
|
||||
typedef stdmap<CrcString const *, CachedFile *, LessPointerComparator>::fwd CachedFileMap;
|
||||
CachedFileMap * const m_cachedFileMap;
|
||||
};
|
||||
|
||||
// ======================================================================
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,213 @@
|
||||
// ======================================================================
|
||||
//
|
||||
// ZlibFile.cpp
|
||||
// Copyright 2002, Sony Online Entertainment Inc.
|
||||
// All Rights Reserved.
|
||||
//
|
||||
// ======================================================================
|
||||
|
||||
#include "sharedFile/FirstSharedFile.h"
|
||||
#include "sharedFile/ZlibFile.h"
|
||||
|
||||
#include "sharedFoundation/ExitChain.h"
|
||||
#include "sharedFoundation/MemoryBlockManager.h"
|
||||
#include "sharedCompression/ZlibCompressor.h"
|
||||
#include "sharedFile/MemoryFile.h"
|
||||
|
||||
// ======================================================================
|
||||
|
||||
MemoryBlockManager *ZlibFile::ms_memoryBlockManager;
|
||||
|
||||
// ======================================================================
|
||||
|
||||
void ZlibFile::install()
|
||||
{
|
||||
DEBUG_FATAL(ms_memoryBlockManager, ("ZlibFile::install already installed"));
|
||||
ms_memoryBlockManager = new MemoryBlockManager("ZlibFile::memoryBlockManager", true, sizeof(ZlibFile), 0, 0, 0);
|
||||
ExitChain::add(&remove, ("ZlibFile::remove"));
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
void ZlibFile::remove()
|
||||
{
|
||||
DEBUG_FATAL(!ms_memoryBlockManager,("ZlibFile is not installed"));
|
||||
|
||||
delete ms_memoryBlockManager;
|
||||
ms_memoryBlockManager = 0;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
void *ZlibFile::operator new(size_t size)
|
||||
{
|
||||
DEBUG_FATAL(!ms_memoryBlockManager,("ZlibFile is not installed"));
|
||||
|
||||
// do not try to alloc a descendant class with this allocator
|
||||
DEBUG_FATAL(size != sizeof(ZlibFile),("bad size"));
|
||||
UNREF(size);
|
||||
|
||||
return ms_memoryBlockManager->allocate();
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
void ZlibFile::operator delete(void *pointer)
|
||||
{
|
||||
DEBUG_FATAL(!ms_memoryBlockManager,("ZlibFile is not installed"));
|
||||
ms_memoryBlockManager->free(pointer);
|
||||
}
|
||||
|
||||
// ======================================================================
|
||||
|
||||
ZlibFile::ZlibFile(int uncompressedLength, byte *compressedBuffer, int compressedBufferLength, bool ownsCompressedBuffer)
|
||||
: AbstractFile(PriorityData),
|
||||
m_uncompressedLength(uncompressedLength),
|
||||
m_compressedBuffer(compressedBuffer),
|
||||
m_compressedBufferLength(compressedBufferLength),
|
||||
m_ownsCompressedBuffer(ownsCompressedBuffer),
|
||||
m_decompressedMemoryFile(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
ZlibFile::~ZlibFile()
|
||||
{
|
||||
close();
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
bool ZlibFile::isOpen() const
|
||||
{
|
||||
return m_compressedBuffer || m_decompressedMemoryFile;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
int ZlibFile::length() const
|
||||
{
|
||||
DEBUG_FATAL(!isOpen(), ("file is not open"));
|
||||
return m_uncompressedLength;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
int ZlibFile::tell() const
|
||||
{
|
||||
DEBUG_FATAL(!isOpen(), ("file is not open"));
|
||||
if (!m_decompressedMemoryFile)
|
||||
return 0;
|
||||
|
||||
return m_decompressedMemoryFile->tell();
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
bool ZlibFile::seek(SeekType seekType, int offset)
|
||||
{
|
||||
DEBUG_FATAL(!isOpen(), ("file is not open"));
|
||||
if (!m_decompressedMemoryFile)
|
||||
createDecompressedMemoryFile();
|
||||
return m_decompressedMemoryFile->seek(seekType, offset);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
int ZlibFile::read(void *destinationBuffer, int numberOfBytes)
|
||||
{
|
||||
DEBUG_FATAL(!isOpen(), ("file is not open"));
|
||||
if (!m_decompressedMemoryFile)
|
||||
createDecompressedMemoryFile();
|
||||
return m_decompressedMemoryFile->read(destinationBuffer, numberOfBytes);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
int ZlibFile::write(int, const void *)
|
||||
{
|
||||
DEBUG_FATAL(true, ("writing to a zlib file is not supported"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
void ZlibFile::close()
|
||||
{
|
||||
if (m_ownsCompressedBuffer)
|
||||
delete [] m_compressedBuffer;
|
||||
m_compressedBuffer = NULL;
|
||||
|
||||
delete m_decompressedMemoryFile;
|
||||
m_decompressedMemoryFile = NULL;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
byte * ZlibFile::decompress() const
|
||||
{
|
||||
byte * uncompressedBuffer = new byte[m_uncompressedLength];
|
||||
|
||||
static_cast<void>(ZlibCompressor().expand(m_compressedBuffer, m_compressedBufferLength, uncompressedBuffer, m_uncompressedLength));
|
||||
|
||||
return uncompressedBuffer;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
void ZlibFile::createDecompressedMemoryFile()
|
||||
{
|
||||
MemoryFile * memoryFile = new MemoryFile(decompress(), m_uncompressedLength);
|
||||
close();
|
||||
m_decompressedMemoryFile = memoryFile;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
byte *ZlibFile::readEntireFileAndClose()
|
||||
{
|
||||
DEBUG_FATAL(!isOpen(), ("file is not open"));
|
||||
byte *result = decompress();
|
||||
close();
|
||||
return result;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
bool ZlibFile::isZlibCompressed() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
int ZlibFile::getZlibCompressedLength() const
|
||||
{
|
||||
return m_compressedBufferLength;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
void ZlibFile::getZlibCompressedDataAndClose(byte *& compressedBuffer, int & compressedBufferLength)
|
||||
{
|
||||
DEBUG_FATAL(!isOpen(), ("file is not open"));
|
||||
DEBUG_FATAL(m_decompressedMemoryFile, ("cannot get compressed data after another non-compressed function has been used"));
|
||||
|
||||
if (m_ownsCompressedBuffer)
|
||||
{
|
||||
compressedBuffer = m_compressedBuffer;
|
||||
m_compressedBuffer = NULL;
|
||||
compressedBufferLength = m_compressedBufferLength;
|
||||
}
|
||||
else
|
||||
{
|
||||
compressedBuffer = new byte[m_compressedBufferLength];
|
||||
memcpy(compressedBuffer, m_compressedBuffer, m_compressedBufferLength);
|
||||
compressedBufferLength = m_compressedBufferLength;
|
||||
}
|
||||
|
||||
close();
|
||||
}
|
||||
|
||||
// ======================================================================
|
||||
@@ -0,0 +1,76 @@
|
||||
// ======================================================================
|
||||
//
|
||||
// ZlibFile.h
|
||||
// Copyright 2002, Sony Online Entertainment Inc.
|
||||
// All Rights Reserved.
|
||||
//
|
||||
// ======================================================================
|
||||
|
||||
#ifndef INCLUDED_ZlibFile_H
|
||||
#define INCLUDED_ZlibFile_H
|
||||
|
||||
// ======================================================================
|
||||
|
||||
class MemoryBlockManager;
|
||||
class MemoryFile;
|
||||
|
||||
#include "fileInterface/AbstractFile.h"
|
||||
|
||||
// ======================================================================
|
||||
|
||||
class ZlibFile : public AbstractFile
|
||||
{
|
||||
public:
|
||||
|
||||
static void install();
|
||||
static void *operator new(size_t size);
|
||||
static void operator delete(void* pointer);
|
||||
|
||||
public:
|
||||
|
||||
ZlibFile(int uncompressedLength, byte *compressedBuffer, int compressedLength, bool ownsCompressedBuffer);
|
||||
virtual ~ZlibFile();
|
||||
|
||||
virtual bool isOpen() const;
|
||||
virtual int length() const;
|
||||
virtual int tell() const;
|
||||
virtual bool seek(SeekType seekType, int offset);
|
||||
virtual int read(void *destinationBuffer, int numberOfBytes);
|
||||
virtual int write(int numberOfBytes, const void *sourceBuffer);
|
||||
virtual void close();
|
||||
|
||||
virtual byte *readEntireFileAndClose();
|
||||
|
||||
virtual bool isZlibCompressed() const;
|
||||
virtual int getZlibCompressedLength() const;
|
||||
virtual void getZlibCompressedDataAndClose(byte *& compressedBuffer, int & compressedBufferLength);
|
||||
|
||||
private:
|
||||
|
||||
static void remove();
|
||||
|
||||
private:
|
||||
|
||||
ZlibFile();
|
||||
ZlibFile(const ZlibFile &);
|
||||
ZlibFile &operator =(const ZlibFile &);
|
||||
|
||||
byte * decompress() const;
|
||||
void createDecompressedMemoryFile();
|
||||
|
||||
private:
|
||||
|
||||
static MemoryBlockManager *ms_memoryBlockManager;
|
||||
|
||||
private:
|
||||
|
||||
const int m_uncompressedLength;
|
||||
byte *m_compressedBuffer;
|
||||
const int m_compressedBufferLength;
|
||||
bool m_ownsCompressedBuffer;
|
||||
mutable MemoryFile *m_decompressedMemoryFile;
|
||||
};
|
||||
|
||||
// ======================================================================
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,8 @@
|
||||
// ======================================================================
|
||||
//
|
||||
// FirstFile.cpp
|
||||
// copyright (c) 2001 Sony Online Entertainment
|
||||
//
|
||||
// ======================================================================
|
||||
|
||||
#include "sharedFile/FirstSharedFile.h"
|
||||
@@ -0,0 +1,195 @@
|
||||
// ======================================================================
|
||||
//
|
||||
// OsFile.cpp
|
||||
// Copyright 2002, Sony Online Entertainment Inc.
|
||||
// All Rights Reserved.
|
||||
//
|
||||
// ======================================================================
|
||||
|
||||
#include "sharedFile/FirstSharedFile.h"
|
||||
#include "sharedFile/OsFile.h"
|
||||
|
||||
#ifdef _DEBUG
|
||||
#include "sharedDebug/PerformanceTimer.h"
|
||||
#endif
|
||||
|
||||
namespace OsFileNamespace
|
||||
{
|
||||
float ms_time;
|
||||
}
|
||||
using namespace OsFileNamespace;
|
||||
|
||||
// ======================================================================
|
||||
|
||||
void OsFile::install()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
float OsFile::getSpentTime()
|
||||
{
|
||||
float const result = ms_time;
|
||||
ms_time = 0.0f;
|
||||
return result;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
bool OsFile::exists(const char *fileName)
|
||||
{
|
||||
NOT_NULL(fileName);
|
||||
DWORD attributes = GetFileAttributes(fileName);
|
||||
|
||||
#if _MSC_VER < 1300
|
||||
const DWORD INVALID_FILE_ATTRIBUTES = 0xffffffff;
|
||||
#endif
|
||||
return (attributes != INVALID_FILE_ATTRIBUTES && (attributes & FILE_ATTRIBUTE_DIRECTORY) == 0);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
int OsFile::getFileSize(const char *fileName)
|
||||
{
|
||||
NOT_NULL(fileName);
|
||||
|
||||
#ifdef _DEBUG
|
||||
PerformanceTimer t;
|
||||
t.start();
|
||||
#endif
|
||||
|
||||
HANDLE handle = CreateFile(fileName, 0, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
if (handle == INVALID_HANDLE_VALUE)
|
||||
return -1;
|
||||
|
||||
int const size = GetFileSize(handle, NULL);
|
||||
CloseHandle(handle);
|
||||
|
||||
#ifdef _DEBUG
|
||||
t.stop();
|
||||
ms_time += t.getElapsedTime();
|
||||
#endif
|
||||
return size;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
OsFile *OsFile::open(const char *fileName, bool randomAccess)
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
PerformanceTimer t;
|
||||
t.start();
|
||||
#endif
|
||||
|
||||
// attempt to open the file
|
||||
HANDLE handle = CreateFile(fileName, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | (randomAccess ? FILE_FLAG_RANDOM_ACCESS : 0), NULL);
|
||||
|
||||
// check to make sure the file opened sucessfully
|
||||
if (handle == INVALID_HANDLE_VALUE)
|
||||
return NULL;
|
||||
|
||||
#ifdef _DEBUG
|
||||
t.stop();
|
||||
ms_time += t.getElapsedTime();
|
||||
#endif
|
||||
|
||||
return new OsFile(handle);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
OsFile::OsFile(HANDLE handle)
|
||||
: m_handle(handle),
|
||||
m_offset(0)
|
||||
{
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
OsFile::~OsFile()
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
PerformanceTimer t;
|
||||
t.start();
|
||||
#endif
|
||||
|
||||
CloseHandle(m_handle);
|
||||
|
||||
#ifdef _DEBUG
|
||||
t.stop();
|
||||
ms_time+= t.getElapsedTime();
|
||||
#endif
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
int OsFile::length() const
|
||||
{
|
||||
return GetFileSize(m_handle, NULL);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
void OsFile::seek(int newFilePosition)
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
PerformanceTimer t;
|
||||
t.start();
|
||||
#endif
|
||||
|
||||
if (m_offset != newFilePosition)
|
||||
{
|
||||
const DWORD result = SetFilePointer(m_handle, newFilePosition, NULL, FILE_BEGIN);
|
||||
DEBUG_FATAL(static_cast<int>(result) != newFilePosition, ("SetFilePointer failed"));
|
||||
UNREF(result);
|
||||
m_offset = newFilePosition;
|
||||
}
|
||||
|
||||
#ifdef _DEBUG
|
||||
t.stop();
|
||||
ms_time += t.getElapsedTime();
|
||||
#endif
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
int OsFile::read(void *destinationBuffer, int numberOfBytes)
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
PerformanceTimer t;
|
||||
t.start();
|
||||
#endif
|
||||
|
||||
DWORD amountReadDword;
|
||||
BOOL result = ReadFile(m_handle, destinationBuffer, static_cast<uint>(numberOfBytes), &amountReadDword, NULL);
|
||||
|
||||
// miles crasher hack
|
||||
#if 0
|
||||
FATAL(!result, ("FileStreamerThread::processRead ReadFile failed to read '%d' bytes with error '%d'", static_cast<uint>(numberOfBytes), GetLastError()));
|
||||
#else
|
||||
if(!result)
|
||||
{
|
||||
if(GetLastError() == 998) // access violation - buffer coming from miles hosed
|
||||
{
|
||||
WARNING(true,("FileStreamerThread::processRead ReadFile failed to read '%d' bytes with error '%d'", static_cast<uint>(numberOfBytes), GetLastError()));
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
FATAL(true, ("FileStreamerThread::processRead ReadFile failed to read '%d' bytes with error '%d'", static_cast<uint>(numberOfBytes), GetLastError()));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
// end miles crasher hack
|
||||
|
||||
#ifdef _DEBUG
|
||||
t.stop();
|
||||
ms_time += t.getElapsedTime();
|
||||
#endif
|
||||
|
||||
m_offset += static_cast<int>(amountReadDword);
|
||||
return static_cast<int>(amountReadDword);
|
||||
}
|
||||
|
||||
// ======================================================================
|
||||
@@ -0,0 +1,48 @@
|
||||
// ======================================================================
|
||||
//
|
||||
// OsFile.h
|
||||
// Copyright 2002, Sony Online Entertainment Inc.
|
||||
// All Rights Reserved.
|
||||
//
|
||||
// ======================================================================
|
||||
|
||||
#ifndef INCLUDED_OsFile_H
|
||||
#define INCLUDED_OsFile_H
|
||||
|
||||
// ======================================================================
|
||||
|
||||
class OsFile
|
||||
{
|
||||
public:
|
||||
|
||||
static void install();
|
||||
|
||||
static float getSpentTime();
|
||||
|
||||
static bool exists(const char *fileName);
|
||||
static int getFileSize(const char *fileName);
|
||||
static OsFile *open(const char *fileName, bool randomAccess=false);
|
||||
|
||||
public:
|
||||
|
||||
~OsFile();
|
||||
|
||||
int length() const;
|
||||
int tell() const;
|
||||
void seek(int newFilePosition);
|
||||
int read(void *destinationBuffer, int numberOfBytes);
|
||||
|
||||
private:
|
||||
|
||||
OsFile(HANDLE handle);
|
||||
OsFile(const char *fileName);
|
||||
|
||||
private:
|
||||
|
||||
HANDLE m_handle;
|
||||
int m_offset;
|
||||
};
|
||||
|
||||
// ======================================================================
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user