1
0
mirror of https://foundry.openuru.org/gitblit/r/CWE-ou-minkata.git synced 2025-07-18 19:29:09 +00:00

Initial Commit of CyanWorlds.com Engine Open Source Client/Plugin

This commit is contained in:
jwplatt
2011-03-12 12:34:52 -05:00
commit b970ae4bad
3976 changed files with 1301355 additions and 0 deletions

View File

@ -0,0 +1,168 @@
/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
#include "hsFiles.h"
#include <string.h>
#include "hsUtils.h"
#include "hsExceptions.h"
#if HS_BUILD_FOR_MAC
#define kDirChar ':'
#elif HS_BUILD_FOR_WIN32
#define kDirChar '\\'
#else
#define kDirChar '/'
#endif
static const char* FindNameInPath(const char path[])
{
const char* name = ::strrchr(path, kDirChar);
if (name == nil)
name = path;
return name;
}
///////////////////////////////////////////////////////////////////////
#if !HS_BUILD_FOR_PS2
hsFile::hsFile() : fPathAndName(nil), fFILE(nil)
{
}
hsFile::hsFile(const char pathAndName[]) : fPathAndName(nil), fFILE(nil)
{
if (pathAndName)
fPathAndName = hsStrcpy(pathAndName);
}
hsFile::~hsFile()
{
this->SetPathAndName(nil);
}
const char* hsFile::GetPathAndName()
{
return fPathAndName;
}
void hsFile::SetPathAndName(const char pathAndName[])
{
this->Close();
if (fPathAndName)
{ delete[] fPathAndName;
fPathAndName = nil;
}
if (pathAndName)
fPathAndName = hsStrcpy(pathAndName);
}
const char* hsFile::GetName()
{
return FindNameInPath(this->GetPathAndName());
}
FILE* hsFile::OpenFILE(const char mode[], hsBool throwIfFailure)
{
this->Close();
// We call the virtual method here rather than using
// fPathAndName directly, allowing a subclass to construct
// the name if necessary
//
const char* name = this->GetPathAndName();
if (name)
fFILE = ::fopen(name, mode);
hsThrowIfTrue(throwIfFailure && fFILE == nil);
return fFILE;
}
hsStream* hsFile::OpenStream(const char mode[], hsBool throwIfFailure)
{
FILE* file = this->OpenFILE(mode, throwIfFailure);
if (file)
{ hsUNIXStream* stream = TRACKED_NEW hsUNIXStream;
stream->SetFILE(file);
return stream;
}
return nil;
}
void hsFile::Close()
{
if (fFILE)
{ int err = ::fflush(fFILE);
hsIfDebugMessage(err != 0, "fflush failed", err);
err = ::fclose(fFILE);
hsIfDebugMessage(err != 0, "fclose failed", err);
fFILE = nil;
}
}
#endif
///////////////////////////////////////////////////////////////////////
hsBool hsFolderIterator::NextFileSuffix(const char suffix[])
{
while (this->NextFile())
{ const char* fileSuffix = ::strrchr(this->GetFileName(), '.');
if (fileSuffix != nil && ::_stricmp(fileSuffix, suffix) == 0)
return true;
}
return false;
}
int hsFolderIterator::GetPathAndName(char pathandname[])
{
const char* name = this->GetFileName();
int pathLen = hsStrlen(fPath);
// add 1 for null terminator
int totalLen = pathLen + sizeof(kDirChar) + hsStrlen(name) + 1;
hsAssert(totalLen <= kFolderIterator_MaxPath, "Overrun kFolderIterator_MaxPath");
if (pathandname)
{ hsStrcpy(pathandname, fPath);
if (pathLen > 0 && pathandname[pathLen - 1] != kDirChar)
pathandname[pathLen++] = kDirChar;
hsStrcpy(pathandname + pathLen, name);
}
return totalLen;
}
FILE* hsFolderIterator::OpenFILE(const char mode[])
{
char fileName[kFolderIterator_MaxPath];
(void)this->GetPathAndName(fileName);
return ::fopen(fileName, mode);
}

View File

@ -0,0 +1,188 @@
/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
#ifndef hsFiles_Defined
#define hsFiles_Defined
#include "hsStream.h"
#include <stdio.h>
#if HS_BUILD_FOR_UNIX
#include <limits.h>
#define kFolderIterator_MaxPath PATH_MAX
#include <unistd.h>
#define SetCurrentDirectory chdir
#elif !HS_BUILD_FOR_PS2
#define kFolderIterator_MaxPath _MAX_PATH
#else
#define kFolderIterator_MaxPath 255
#endif
#if HS_BUILD_FOR_MAC
#include <Files.h>
#include <Script.h>
#endif
#if HS_BUILD_FOR_WIN32
# define PATH_SEPARATOR '\\'
# define WPATH_SEPARATOR L'\\'
# define PATH_SEPARATOR_STR "\\"
# define WPATH_SEPARATOR_STR L"\\"
#elif HS_BUILD_FOR_UNIX
# define PATH_SEPARATOR '/'
# define WPATH_SEPARATOR L'/'
# define PATH_SEPARATOR_STR "/"
# define WPATH_SEPARATOR_STR L"/"
#endif
///////////////////////////////////////////////////////////////////////
#if !HS_BUILD_FOR_PS2
class hsFile {
hsFile& operator=(const hsFile&); // disallow assignment
protected:
char* fPathAndName;
FILE* fFILE;
public:
hsFile();
hsFile(const char pathAndName[]);
virtual ~hsFile();
const char* GetName();
virtual const char* GetPathAndName();
virtual void SetPathAndName(const char pathAndName[]);
virtual FILE* OpenFILE(const char mode[], hsBool throwIfFailure = false);
virtual hsStream* OpenStream(const char mode[], hsBool throwIfFailure = false);
virtual void Close(); // called automatically in the destructor
};
typedef hsFile hsUnixFile; // for compatibility
#if HS_BUILD_FOR_MAC
class hsMacFile : public hsFile {
enum {
kRefNum_Dirty,
kPathName_Dirty
};
FSSpec fSpec;
Int16 fRefNum;
UInt16 fFlags;
void SetSpecFromName();
void SetNameFromSpec();
public:
hsMacFile();
hsMacFile(const FSSpec* spec);
hsMacFile(const char pathAndName[]);
virtual ~hsMacFile();
const FSSpec* GetSpec() const { return &fSpec; }
void SetSpec(const FSSpec* spec);
hsBool Create(OSType creator, OSType fileType, ScriptCode scriptCode = smSystemScript);
hsBool OpenDataFork(SInt8 permission, Int16* refnum);
// Overrides
virtual const char* GetPathAndName();
virtual void SetPathAndName(const char pathAndName[]);
virtual hsStream* OpenStream(const char mode[], hsBool throwIfFailure = false);
virtual void Close();
};
typedef hsMacFile hsOSFile;
#else
typedef hsFile hsOSFile;
#endif
#endif // HS_BUILD_FOR_PS2
///////////////////////////////////////////////////////////////////////
class hsFolderIterator {
char fPath[kFolderIterator_MaxPath];
struct hsFolderIterator_Data* fData;
bool fCustomFilter;
public:
#ifdef HS_BUILD_FOR_WIN32
hsFolderIterator(const char path[] = nil, bool useCustomFilter=false);
#else
hsFolderIterator(const char path[] = nil, bool unused=true);
hsFolderIterator(const struct FSSpec* spec); // Alt constructor
#endif
virtual ~hsFolderIterator();
const char* GetPath() const { return fPath; }
void SetPath(const char path[]);
void Reset();
hsBool NextFile();
hsBool NextFileSuffix(const char suffix[]);
const char* GetFileName() const;
int GetPathAndName(char pathandname[] = nil);
hsBool IsDirectory( void ) const;
FILE* OpenFILE(const char mode[]);
#if HS_BUILD_FOR_MAC
void SetMacFolder(const char path[]);
void SetMacFolder(OSType folderType);
void SetMacFolder(Int16 vRefNum, Int32 dirID);
hsBool NextMacFile(OSType targetFileType, OSType targetCreator);
const struct FSSpec* GetMacSpec() const;
OSType GetMacFileType() const;
OSType GetMacCreator() const;
#elif HS_BUILD_FOR_WIN32
void SetWinSystemDir(const char subdir[]); // e.g. "Fonts"
void SetFileFilterStr(const char filterStr[]); // e.g. "*.*"
#endif
};
#ifdef HS_BUILD_FOR_WIN32
// only implemented on Win32 for now
class hsWFolderIterator {
wchar fPath[kFolderIterator_MaxPath];
struct hsWFolderIterator_Data* fData;
bool fCustomFilter;
public:
hsWFolderIterator(const wchar path[] = nil, bool useCustomFilter=false);
virtual ~hsWFolderIterator();
const wchar* GetPath() const { return fPath; }
void SetPath(const wchar path[]);
void Reset();
hsBool NextFile();
hsBool NextFileSuffix(const wchar suffix[]);
const wchar* GetFileName() const;
int GetPathAndName(wchar pathandname[] = nil);
hsBool IsDirectory( void ) const;
FILE* OpenFILE(const wchar mode[]);
void SetWinSystemDir(const wchar subdir[]); // e.g. "Fonts"
void SetFileFilterStr(const wchar filterStr[]); // e.g. "*.*"
};
#endif
#endif

View File

@ -0,0 +1,393 @@
/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
#include "hsFiles.h"
#include "hsUtils.h"
#include "hsMemory.h"
#if HS_BUILD_FOR_MAC
#include <Files.h>
#include <Folders.h>
#include <Errors.h>
///////////////////////////////////////////////////////////////////////////
hsMacFile::hsMacFile() : fFlags(kRefNum_Dirty)
{
fSpec.name[0] = 0;
}
hsMacFile::hsMacFile(const char pathAndName[]) : hsFile(pathAndName), fFlags(kRefNum_Dirty)
{
this->SetSpecFromName();
}
hsMacFile::hsMacFile(const FSSpec* spec) : fFlags(kRefNum_Dirty)
{
this->SetSpec(spec);
}
hsMacFile::~hsMacFile()
{
this->Close();
}
void hsMacFile::SetSpec(const FSSpec* spec)
{
if (spec)
fSpec = *spec;
else
fSpec.name[0] = 0;
fFlags |= kPathName_Dirty;
}
void hsMacFile::SetSpecFromName()
{
Str255 pstr;
if (fPathAndName == nil)
fSpec.name[0] = 0;
else
{ hsC2PString(fPathAndName, pstr);
::FSMakeFSSpec(0, 0, pstr, &fSpec);
}
}
void hsMacFile::SetNameFromSpec()
{
CInfoPBRec pb;
Str255 dirNameP;
char dirName[256], temp[256];
int err;
hsP2CString(fSpec.name, temp);
pb.dirInfo.ioNamePtr = dirNameP;
pb.dirInfo.ioVRefNum = fSpec.vRefNum;
pb.dirInfo.ioDrParID = fSpec.parID;
pb.dirInfo.ioFDirIndex = -1;
do {
pb.dirInfo.ioDrDirID = pb.dirInfo.ioDrParID;
err = PBGetCatInfoSync(&pb);
hsThrowIfOSErr(err);
hsP2CString(dirNameP, dirName);
strcat(dirName,":");
strcat(dirName, temp);
strcpy(temp, dirName);
} while( pb.dirInfo.ioDrDirID != fsRtDirID);
hsAssert(fPathAndName == nil, "pathname should be nil");
fPathAndName = hsStrcpy(temp);
}
hsBool hsMacFile::Create(OSType creator, OSType fileType, ScriptCode scriptCode)
{
this->Close();
OSErr err;
(void)::FSpDelete(&fSpec);
err = ::FSpCreate(&fSpec, creator, fileType, scriptCode);
hsIfDebugMessage(err != 0, "FSpCreate failed", err);
return err == 0;
}
#define kFileNotFound_Err -43
hsBool hsMacFile::OpenDataFork(SInt8 perm, Int16* refnum)
{
this->Close();
OSErr err;
err = ::FSpOpenDF(&fSpec, perm, &fRefNum);
if (err == kFileNotFound_Err && (perm & fsWrPerm) && (perm & fsRdPerm) == 0)
{ if (this->Create('HdSp', '????'))
err = ::FSpOpenDF(&fSpec, perm, &fRefNum);
}
if (err == 0)
{ fFlags &= ~kRefNum_Dirty;
if (refnum)
*refnum = fRefNum;
return true;
}
return false;
}
const char* hsMacFile::GetPathAndName()
{
if (fFlags & kPathName_Dirty)
{ this->SetNameFromSpec();
fFlags &= ~kPathName_Dirty;
}
return fPathAndName;
}
void hsMacFile::SetPathAndName(const char pathAndName[])
{
this->hsFile::SetPathAndName(pathAndName);
this->SetSpecFromName();
}
hsStream* hsMacFile::OpenStream(const char mode[], hsBool throwIfFailure)
{
hsThrowIfNilParam(mode);
short refnum;
SInt8 perm = 0;
if (::strchr(mode, 'r'))
perm |= fsRdPerm;
if (::strchr(mode, 'w'))
perm |= fsWrPerm;
if (this->OpenDataFork(perm, &refnum))
{ hsFileStream* stream = TRACKED_NEW hsFileStream;
stream->SetFileRef(refnum);
return stream;
}
hsThrowIfTrue(throwIfFailure);
return nil;
}
void hsMacFile::Close()
{
if (fFlags & kRefNum_Dirty)
{ OSErr err = ::FSClose(fRefNum);
hsIfDebugMessage(err != 0, "FSClose failed", err);
fFlags &= ~kRefNum_Dirty;
}
this->hsFile::Close();
}
///////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////
struct hsFolderIterator_Data {
FSSpec fSpec;
OSType fFileType;
OSType fCreator;
char fCName[_MAX_PATH];
Int16 fCurrIndex;
hsBool fValid;
};
hsFolderIterator::hsFolderIterator(const char path[])
{
fData = TRACKED_NEW hsFolderIterator_Data;
fData->fCurrIndex = 0;
fData->fValid = false;
#if HS_BUILD_FOR_WIN32
this->SetPath(path);
#else
this->SetMacFolder(path);
#endif
}
hsFolderIterator::hsFolderIterator(const struct FSSpec* spec) // Alt Constructor - pass in FSSpec from OpenDlg()
{
fData = TRACKED_NEW hsFolderIterator_Data;
fData->fCurrIndex = 0;
fData->fValid = false;
SetMacFolder(spec->vRefNum, spec->parID);
}
hsFolderIterator::~hsFolderIterator()
{
delete fData;
}
void hsFolderIterator::SetPath(const char path[])
{
fPath[0] = 0;
fData->fValid = false;
fData->fCurrIndex = 0;
if (path)
{
::strcpy(fPath, path);
}
}
///////////////////////////////////////////////////////////////////////////
void hsFolderIterator::SetMacFolder(OSType folderType)
{
fData->fCurrIndex = 0;
fData->fValid = ::FindFolder(kOnSystemDisk, folderType, false,
&fData->fSpec.vRefNum, &fData->fSpec.parID) == 0;
this->Reset();
}
void hsFolderIterator::SetMacFolder(Int16 vRefNum, Int32 dirID)
{
fData->fSpec.vRefNum = vRefNum;
fData->fSpec.parID = dirID;
fData->fCurrIndex = 0;
fData->fValid = true;
this->Reset();
}
void hsFolderIterator::SetMacFolder(const char path[])
{
char tmp[255];
FSSpec fileSpec;
OSErr err;
hsCPathToMacPath(&tmp[1], (char*)path);
tmp[0] = hsStrlen(&tmp[1]);
SetPath((char*)&tmp[1]);
err = FSMakeFSSpec(0, 0, (const unsigned char*)tmp, &fileSpec);
if(err == fnfErr)
{
HSDebugProc("XCmd directory does not exist.");
return;
}
hsAssert(err == noErr, "Error making file spec.");
// by now we should have the file spec for the given
// directory, however, the DirID is the PARENT directory,
// not the child directory. The following steps should
// give us the items we want.
CInfoPBRec pb;
pb.hFileInfo.ioVRefNum = fileSpec.vRefNum;
pb.hFileInfo.ioNamePtr = (StringPtr)fileSpec.name; // The name of the child directory.
pb.hFileInfo.ioDirID = fileSpec.parID; // The ID of the parent directory.
pb.hFileInfo.ioFDirIndex = 0;
pb.hFileInfo.ioCompletion = 0;
err = ::PBGetCatInfoSync(&pb);
hsAssert(err == noErr, "PBGetCatInfoSync() failure.");
fData->fSpec.vRefNum = fileSpec.vRefNum; // Volume reference
fData->fSpec.parID = pb.dirInfo.ioDrDirID; // child directory ID (Finally!)
fData->fCurrIndex = 0;
fData->fValid = true;
this->Reset();
}
///////////////////////////////////////////////////////////////////////////
void hsFolderIterator::Reset()
{
if (fData->fValid)
fData->fCurrIndex = 1;
#ifdef HS_DEBUGGING
else
hsAssert(fData->fCurrIndex == 0, "bad currindex");
#endif
}
hsBool hsFolderIterator::NextFile()
{
if (fData->fCurrIndex == 0)
return false;
CInfoPBRec pb;
do {
pb.hFileInfo.ioVRefNum = fData->fSpec.vRefNum;
pb.hFileInfo.ioNamePtr = (StringPtr)fData->fSpec.name;
pb.hFileInfo.ioDirID = fData->fSpec.parID;
pb.hFileInfo.ioFDirIndex = fData->fCurrIndex++;
OSErr err = ::PBGetCatInfoSync(&pb);
if (err)
{
fData->fCurrIndex = 0;
return false;
}
} while (pb.hFileInfo.ioFlAttrib & ioDirMask);
fData->fFileType = pb.hFileInfo.ioFlFndrInfo.fdType;
fData->fCreator = pb.hFileInfo.ioFlFndrInfo.fdCreator;
return true;
}
const char* hsFolderIterator::GetFileName() const
{
if (fData->fCurrIndex == 0)
throw "end of folder";
// Copy our filename (in pascal format) into a cstring and then return
HSMemory::BlockMove(&fData->fSpec.name[1], fData->fCName, fData->fSpec.name[0]);
fData->fCName[fData->fSpec.name[0]] = 0;
return fData->fCName;
}
////////////////////////////////////////////////////////////////////////////
hsBool hsFolderIterator::NextMacFile(OSType targetFileType, OSType targetCreator)
{
for (;;)
{ if (this->NextFile() == false)
return false;
if ((targetFileType == 0 || targetFileType == this->GetMacFileType()) &&
(targetCreator == 0 || targetCreator == this->GetMacCreator()))
return true;
}
}
const FSSpec* hsFolderIterator::GetMacSpec() const
{
if (fData->fCurrIndex == 0)
throw "end of folder";
return &fData->fSpec;
}
OSType hsFolderIterator::GetMacFileType() const
{
if (fData->fCurrIndex == 0)
throw "end of folder";
return fData->fFileType;
}
OSType hsFolderIterator::GetMacCreator() const
{
if (fData->fCurrIndex == 0)
throw "end of folder";
return fData->fCreator;
}
hsBool hsFolderIterator::IsDirectory( void ) const
{
hsAssert( false, "hsFolderIterator::IsDirectory() not defined on this platform!!!" );
return false;
}
#endif // HS_BUILD_FOR_MAC

View File

@ -0,0 +1,68 @@
/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
#include "hsFiles.h"
#if HS_BUILD_FOR_PS2
hsFolderIterator::hsFolderIterator(const char path[])
{
hsAssert(0,"No folder Interator defined for PS2 -- yet");
}
hsFolderIterator::~hsFolderIterator()
{
}
void hsFolderIterator::SetPath(const char path[])
{
}
///////////////////////////////////////////////////////////////////////////////
void hsFolderIterator::Reset()
{
}
hsBool hsFolderIterator::NextFile()
{
}
const char* hsFolderIterator::GetFileName() const
{
}
hsBool hsFolderIterator::IsDirectory( void ) const
{
hsAssert( false, "hsFolderIterator::IsDirectory() not defined on this platform!!!" );
return false;
}
#endif // HS_BUILD_FOR_PS2

View File

@ -0,0 +1,130 @@
/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
#include "hsFiles.h"
#if HS_BUILD_FOR_UNIX
#include <errno.h>
#include <dirent.h>
#include <limits.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <glob.h>
#include "hsTemplates.h"
#include "plFileUtils.h"
#include "hsStlUtils.h"
struct hsFolderIterator_Data {
glob_t fGlobBuf;
bool fInited;
int fCnt;
hsFolderIterator_Data() : fInited(false), fCnt(0) {}
// ~hsFolderIterator_Data() { fInited=false; globfree(&fData->fGlobBuf); }
};
hsFolderIterator::hsFolderIterator(const char path[], bool)
{
fData = TRACKED_NEW hsFolderIterator_Data;
this->SetPath(path);
}
hsFolderIterator::~hsFolderIterator()
{
this->Reset();
delete fData;
}
void hsFolderIterator::SetPath(const char path[])
{
fPath[0] = 0;
if (path)
{
::strcpy(fPath, path);
}
this->Reset();
}
void hsFolderIterator::Reset()
{
if (fData->fInited)
{
globfree(&fData->fGlobBuf);
fData->fCnt = 0;
fData->fInited=false;
}
}
hsBool hsFolderIterator::NextFile()
{
if (fData->fInited == false)
{
std::string path=fPath;
if(!(strchr(fPath,'*') || strchr(fPath,'?') || strchr(fPath,'[')))
{
if (fPath[strlen(fPath)-1] != PATH_SEPARATOR)
path = path + PATH_SEPARATOR_STR + "*";
else
path = path + "*";
}
if(glob(path.c_str(), 0, NULL, &fData->fGlobBuf) != 0 ) {
return false;
}
fData->fInited=true;
fData->fCnt = 0;
}
return fData->fCnt++ < fData->fGlobBuf.gl_pathc;
}
const char* hsFolderIterator::GetFileName() const
{
if (!fData->fInited || fData->fCnt > fData->fGlobBuf.gl_pathc)
throw "end of folder";
const char* fn=fData->fGlobBuf.gl_pathv[fData->fCnt-1];
return plFileUtils::GetFileName(fn);
}
hsBool hsFolderIterator::IsDirectory( void ) const
{
// rob, please forgive me, this is my best attempt...
if(fData->fCnt > fData->fGlobBuf.gl_pathc )
return false;
struct stat info;
const char* fn=fData->fGlobBuf.gl_pathv[fData->fCnt-1];
if( stat( fn, &info ) )
{
printf("Error calling stat(): %s errno=%d\n", strerror(errno), errno);
return false;
}
return ( info.st_mode & S_IFDIR ) ? true : false;
}
#endif

View File

@ -0,0 +1,304 @@
/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
#include "hsFiles.h"
#if HS_BUILD_FOR_WIN32
#include <windows.h>
#include "hsExceptions.h"
struct hsFolderIterator_Data {
HANDLE fSearchHandle;
WIN32_FIND_DATA fFindData;
Boolean fValid;
};
hsFolderIterator::hsFolderIterator(const char path[], bool useCustomFilter)
{
fCustomFilter = useCustomFilter;
fData = TRACKED_NEW hsFolderIterator_Data;
fData->fSearchHandle = nil;
fData->fValid = true;
if(useCustomFilter)
{
this->SetFileFilterStr(path);
}
else
{
this->SetPath(path);
}
}
hsFolderIterator::~hsFolderIterator()
{
delete fData;
}
void hsFolderIterator::SetPath(const char path[])
{
fCustomFilter = false;
fPath[0] = 0;
if (path)
{
::strcpy(fPath, path);
// Make sure the dir ends with a slash
char lastchar = fPath[strlen(fPath)-1];
if (lastchar != '\\' && lastchar != '/')
strcat(fPath, "\\");
}
this->Reset();
}
void hsFolderIterator::SetWinSystemDir(const char subdir[])
{
int ret = GetWindowsDirectory(fPath, _MAX_PATH);
hsAssert(ret != 0, "Error getting windows directory in UseWindowsFontsPath");
if (subdir)
{ ::strcat(fPath, "\\");
::strcat(fPath, subdir);
::strcat(fPath, "\\");
}
this->Reset();
}
void hsFolderIterator::SetFileFilterStr(const char filterStr[])
{
fPath[0] = 0;
if (filterStr)
{
fCustomFilter = true;
::strcpy(fPath, filterStr);
}
this->Reset();
}
///////////////////////////////////////////////////////////////////////////////
void hsFolderIterator::Reset()
{
if (fData->fSearchHandle)
{ FindClose(fData->fSearchHandle);
fData->fSearchHandle = nil;
}
fData->fValid = true;
}
hsBool hsFolderIterator::NextFile()
{
if (fData->fValid == false)
return false;
if (fData->fSearchHandle == nil)
{ int len = ::strlen(fPath);
if(fCustomFilter == false)
{
fPath[len] = '*';
fPath[len+1] = 0;
}
fData->fSearchHandle = FindFirstFile(fPath, &fData->fFindData);
fPath[len] = 0;
if (fData->fSearchHandle == INVALID_HANDLE_VALUE)
{ fData->fSearchHandle = nil;
fData->fValid = false;
return false;
}
}
else
{ if (FindNextFile(fData->fSearchHandle, &fData->fFindData) == false)
{ FindClose(fData->fSearchHandle);
fData->fSearchHandle = nil;
fData->fValid = false;
return false;
}
}
return true;
}
hsBool hsFolderIterator::IsDirectory( void ) const
{
if( fData->fValid && ( fData->fFindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) )
return true;
return false;
}
const char* hsFolderIterator::GetFileName() const
{
if (fData->fValid == false)
hsThrow( "end of folder");
return fData->fFindData.cFileName;
}
///////////////////////////////////////////////////////////////////////////////
struct hsWFolderIterator_Data {
HANDLE fSearchHandle;
WIN32_FIND_DATAW fFindData;
Boolean fValid;
};
hsWFolderIterator::hsWFolderIterator(const wchar path[], bool useCustomFilter)
{
fCustomFilter = useCustomFilter;
fData = TRACKED_NEW hsWFolderIterator_Data;
fData->fSearchHandle = nil;
fData->fValid = true;
if(useCustomFilter)
SetFileFilterStr(path);
else
SetPath(path);
}
hsWFolderIterator::~hsWFolderIterator()
{
delete fData;
}
void hsWFolderIterator::SetPath(const wchar path[])
{
fCustomFilter = false;
fPath[0] = 0;
if (path)
{
wcscpy(fPath, path);
// Make sure the dir ends with a slash
wchar lastchar = fPath[wcslen(fPath)-1];
if (lastchar != L'\\' && lastchar != L'/')
wcscat(fPath, L"\\");
}
Reset();
}
void hsWFolderIterator::SetWinSystemDir(const wchar subdir[])
{
int ret = GetWindowsDirectoryW(fPath, _MAX_PATH);
hsAssert(ret != 0, "Error getting windows directory in UseWindowsFontsPath");
if (subdir)
{
wcscat(fPath, L"\\");
wcscat(fPath, subdir);
wcscat(fPath, L"\\");
}
Reset();
}
void hsWFolderIterator::SetFileFilterStr(const wchar filterStr[])
{
fPath[0] = 0;
if (filterStr)
{
fCustomFilter = true;
wcscpy(fPath, filterStr);
}
Reset();
}
///////////////////////////////////////////////////////////////////////////////
void hsWFolderIterator::Reset()
{
if (fData->fSearchHandle)
{
FindClose(fData->fSearchHandle);
fData->fSearchHandle = nil;
}
fData->fValid = true;
}
hsBool hsWFolderIterator::NextFile()
{
if (fData->fValid == false)
return false;
if (fData->fSearchHandle == nil)
{
int len = wcslen(fPath);
if(fCustomFilter == false)
{
fPath[len] = L'*';
fPath[len+1] = L'\0';
}
fData->fSearchHandle = FindFirstFileW(fPath, &fData->fFindData);
fPath[len] = 0;
if (fData->fSearchHandle == INVALID_HANDLE_VALUE)
{
fData->fSearchHandle = nil;
fData->fValid = false;
return false;
}
}
else
{
if (FindNextFileW(fData->fSearchHandle, &fData->fFindData) == false)
{
FindClose(fData->fSearchHandle);
fData->fSearchHandle = nil;
fData->fValid = false;
return false;
}
}
return true;
}
hsBool hsWFolderIterator::IsDirectory( void ) const
{
if( fData->fValid && ( fData->fFindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) )
return true;
return false;
}
const wchar* hsWFolderIterator::GetFileName() const
{
if (fData->fValid == false)
hsThrow( "end of folder");
return fData->fFindData.cFileName;
}
#endif // HS_BUILD_FOR_WIN32

View File

@ -0,0 +1,72 @@
/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
#include "plBrowseFolder.h"
#ifdef HS_BUILD_FOR_WIN32
#include <shlobj.h>
bool plBrowseFolder::GetFolder(char *path, const char *startPath, const char *title, HWND hwndOwner)
{
BROWSEINFO bi;
memset(&bi, 0, sizeof(bi));
bi.hwndOwner = hwndOwner;
bi.lpszTitle = title;
bi.lpfn = BrowseCallbackProc;
bi.lParam = (LPARAM) startPath;
ITEMIDLIST *iil = SHBrowseForFolder(&bi);
// Browse failed, or cancel was selected
if (!iil)
return false;
// Browse succeded. Get the path.
else
SHGetPathFromIDList(iil, path);
// Free the memory allocated by SHBrowseForFolder
LPMALLOC pMalloc;
SHGetMalloc(&pMalloc);
pMalloc->Free(iil);
pMalloc->Release();
return true;
}
int CALLBACK plBrowseFolder::BrowseCallbackProc(HWND hwnd, UINT uMsg, LPARAM lParam, LPARAM lpData)
{
switch (uMsg)
{
case BFFM_INITIALIZED:
// lpData should be the lParam passed to SHBrowseForFolder, which is the start path.
if (lpData)
SendMessage(hwnd, BFFM_SETSELECTION, TRUE, lpData);
break;
}
return 0;
}
#endif // HS_BUILD_FOR_WIN32

View File

@ -0,0 +1,58 @@
/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
#ifndef plBrowseFolder_h_inc
#define plBrowseFolder_h_inc
#include "hsConfig.h"
#ifdef HS_BUILD_FOR_WIN32
#include "hsWindows.h"
//
// Gets a directory using the "Browse for Folder" dialog.
//
// path: Buffer to recieve the path. Should be MAX_PATH characters.
// startPath: Initial path.
// title: Not really the title of the dialog, but it's displayed above the
// folder list. Could be used to give instructions.
// hwndOwner: Owner window for dialog box.
//
// Returns true if path contains a valid path, false otherwise (error or user
// clicked cancel.
//
class plBrowseFolder
{
public:
static bool GetFolder(char *path, const char *startPath = NULL, const char *title = NULL, HWND hwndOwner = NULL);
protected:
static int CALLBACK BrowseCallbackProc(HWND hwnd, UINT uMsg, LPARAM lParam, LPARAM lpData);
};
#endif // HS_BUILD_FOR_WIN32
#endif // plBrowseFolder_h_inc

View File

@ -0,0 +1,577 @@
/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
#include "plEncryptedStream.h"
#include "hsUtils.h"
#include "plFileUtils.h"
#include "hsSTLStream.h"
#include <time.h>
static const UInt32 kDefaultKey[4] = { 0x6c0a5452, 0x3827d0f, 0x3a170b92, 0x16db7fc2 };
static const int kEncryptChunkSize = 8;
static const char* kOldMagicString = "BriceIsSmart";
static const char* kMagicString = "whatdoyousee";
static const int kMagicStringLen = 12;
static const int kFileStartOffset = kMagicStringLen + sizeof(UInt32);
static const int kMaxBufferedFileSize = 10*1024;
plEncryptedStream::plEncryptedStream(UInt32* key) :
fRef(nil),
fActualFileSize(0),
fBufferedStream(false),
fRAMStream(nil),
fWriteFileName(nil),
fOpenMode(kOpenFail)
{
if (key)
memcpy(&fKey, key, sizeof(kDefaultKey));
else
memcpy(&fKey, &kDefaultKey, sizeof(kDefaultKey));
}
plEncryptedStream::~plEncryptedStream()
{
}
//
// Tiny Encryption Algorithm
// http://vader.brad.ac.uk/tea/tea.shtml
//
// A potential weakness in this implementation is the fact that a known value
// (length of the original file) is written at the start of the encrypted file. -Colin
//
// Oh, and also there's some kind of potential weakness in TEA that they fixed with XTEA,
// but frankly, who cares. No one is going to break the encryption, they'll just get the
// key out of the exe or memory.
//
void plEncryptedStream::IEncipher(UInt32* const v)
{
register unsigned long y=v[0], z=v[1], sum=0, delta=0x9E3779B9, n=32;
while (n-- > 0)
{
y += (z << 4 ^ z >> 5) + z ^ sum + fKey[sum&3];
sum += delta;
z += (y << 4 ^ y >> 5) + y ^ sum + fKey[sum>>11 & 3];
}
v[0]=y; v[1]=z;
}
void plEncryptedStream::IDecipher(UInt32* const v)
{
register unsigned long y=v[0], z=v[1], sum=0xC6EF3720, delta=0x9E3779B9, n=32;
// sum = delta<<5, in general sum = delta * n
while (n-- > 0)
{
z -= (y << 4 ^ y >> 5) + y ^ sum + fKey[sum>>11 & 3];
sum -= delta;
y -= (z << 4 ^ z >> 5) + z ^ sum + fKey[sum&3];
}
v[0]=y; v[1]=z;
}
hsBool plEncryptedStream::Open(const char* name, const char* mode)
{
wchar* wName = hsStringToWString(name);
wchar* wMode = hsStringToWString(mode);
hsBool ret = Open(wName, wMode);
delete [] wName;
delete [] wMode;
return ret;
}
hsBool plEncryptedStream::Open(const wchar* name, const wchar* mode)
{
if (wcscmp(mode, L"rb") == 0)
{
fRef = _wfopen(name, mode);
fPosition = 0;
if (!fRef)
return false;
// Make sure our special magic string is there
if (!ICheckMagicString(fRef))
{
fclose(fRef);
return false;
}
fread(&fActualFileSize, sizeof(UInt32), 1, fRef);
// The encrypted stream is inefficient if you do reads smaller than
// 8 bytes. Since we do a lot of those, any file under a size threshold
// is buffered in memory
if (fActualFileSize <= kMaxBufferedFileSize)
IBufferFile();
fOpenMode = kOpenRead;
return true;
}
else if (wcscmp(mode, L"wb") == 0)
{
fRAMStream = TRACKED_NEW hsVectorStream;
fWriteFileName = TRACKED_NEW wchar[wcslen(name) + 1];
wcscpy(fWriteFileName, name);
fPosition = 0;
fOpenMode = kOpenWrite;
fBufferedStream = true;
return true;
}
else
{
hsAssert(0, "Unsupported open mode");
fOpenMode = kOpenFail;
return false;
}
}
hsBool plEncryptedStream::Close()
{
int rtn = false;
if (fOpenMode == kOpenWrite)
{
fRAMStream->Rewind();
rtn = IWriteEncypted(fRAMStream, fWriteFileName);
}
if (fRef)
{
rtn = (fclose(fRef) == 0);
fRef = nil;
}
if (fRAMStream)
{
delete fRAMStream;
fRAMStream = nil;
}
if (fWriteFileName)
{
delete [] fWriteFileName;
fWriteFileName = nil;
}
fActualFileSize = 0;
fBufferedStream = false;
fOpenMode = kOpenFail;
return rtn;
}
UInt32 plEncryptedStream::IRead(UInt32 bytes, void* buffer)
{
if (!fRef)
return 0;
int numItems = (int)(::fread(buffer, 1 /*size*/, bytes /*count*/, fRef));
fBytesRead += numItems;
fPosition += numItems;
if ((unsigned)numItems < bytes) {
if (feof(fRef)) {
// EOF ocurred
char str[128];
sprintf(str, "Hit EOF on UNIX Read, only read %d out of requested %d bytes\n", numItems, bytes);
hsDebugMessage(str, 0);
}
else {
hsDebugMessage("Error on UNIX Read", ferror(fRef));
}
}
return numItems;
}
void plEncryptedStream::IBufferFile()
{
fRAMStream = TRACKED_NEW hsVectorStream;
char buf[1024];
while (!AtEnd())
{
UInt32 numRead = Read(1024, buf);
fRAMStream->Write(numRead, buf);
}
fRAMStream->Rewind();
fBufferedStream = true;
fclose(fRef);
fRef = nil;
fPosition = 0;
}
hsBool plEncryptedStream::AtEnd()
{
if (fBufferedStream)
return fRAMStream->AtEnd();
else
return (GetPosition() == fActualFileSize);
}
void plEncryptedStream::Skip(UInt32 delta)
{
if (fBufferedStream)
{
fRAMStream->Skip(delta);
fPosition = fRAMStream->GetPosition();
}
else if (fRef)
{
fBytesRead += delta;
fPosition += delta;
fseek(fRef, delta, SEEK_CUR);
}
}
void plEncryptedStream::Rewind()
{
if (fBufferedStream)
{
fRAMStream->Rewind();
fPosition = fRAMStream->GetPosition();
}
else if (fRef)
{
fBytesRead = 0;
fPosition = 0;
fseek(fRef, kFileStartOffset, SEEK_SET);
}
}
void plEncryptedStream::FastFwd()
{
if (fBufferedStream)
{
fRAMStream->FastFwd();
fPosition = fRAMStream->GetPosition();
}
else if (fRef)
{
fseek(fRef, kFileStartOffset+fActualFileSize, SEEK_SET);
fBytesRead = fPosition = ftell(fRef);
}
}
UInt32 plEncryptedStream::GetEOF()
{
return fActualFileSize;
}
UInt32 plEncryptedStream::Read(UInt32 bytes, void* buffer)
{
if (fBufferedStream)
{
UInt32 numRead = fRAMStream->Read(bytes, buffer);
fPosition = fRAMStream->GetPosition();
return numRead;
}
UInt32 startPos = fPosition;
// Offset into the first buffer (0 if we are aligned on a chunk, which means no extra block read)
UInt32 startChunkPos = startPos % kEncryptChunkSize;
// Amount of data in the partial first chunk (0 if we're aligned)
UInt32 startAmt = (startChunkPos != 0) ? hsMinimum(kEncryptChunkSize - startChunkPos, bytes) : 0;
UInt32 totalNumRead = IRead(bytes, buffer);
UInt32 numMidChunks = (totalNumRead - startAmt) / kEncryptChunkSize;
UInt32 endAmt = (totalNumRead - startAmt) % kEncryptChunkSize;
// If the start position is in the middle of a chunk we need to rewind and
// read that whole chunk in and decrypt it.
if (startChunkPos != 0)
{
// Move to the start of this chunk
SetPosition(startPos-startChunkPos);
// Read in the chunk and decrypt it
char buf[kEncryptChunkSize];
UInt32 numRead = IRead(kEncryptChunkSize, &buf);
IDecipher((UInt32*)&buf);
// Copy the relevant portion to the output buffer
memcpy(buffer, &buf[startChunkPos], startAmt);
SetPosition(startPos+totalNumRead);
}
if (numMidChunks != 0)
{
UInt32* bufferPos = (UInt32*)(((char*)buffer)+startAmt);
for (int i = 0; i < numMidChunks; i++)
{
// Decrypt chunk
IDecipher(bufferPos);
bufferPos += (kEncryptChunkSize / sizeof(UInt32));
}
}
if (endAmt != 0)
{
// Read in the final chunk and decrypt it
char buf[kEncryptChunkSize];
SetPosition(startPos + startAmt + numMidChunks*kEncryptChunkSize);
UInt32 numRead = IRead(kEncryptChunkSize, &buf);
IDecipher((UInt32*)&buf);
memcpy(((char*)buffer)+totalNumRead-endAmt, &buf, endAmt);
SetPosition(startPos+totalNumRead);
}
// If we read into the padding at the end, update the total read to not include that
if (totalNumRead > 0 && startPos + totalNumRead > fActualFileSize)
{
totalNumRead -= (startPos + totalNumRead) - fActualFileSize;
SetPosition(fActualFileSize);
}
return totalNumRead;
}
UInt32 plEncryptedStream::Write(UInt32 bytes, const void* buffer)
{
if (fOpenMode != kOpenWrite)
{
hsAssert(0, "Trying to write to a read stream");
return 0;
}
return fRAMStream->Write(bytes, buffer);
}
bool plEncryptedStream::IWriteEncypted(hsStream* sourceStream, const wchar* outputFile)
{
hsUNIXStream outputStream;
if (!outputStream.Open(outputFile, L"wb"))
return false;
outputStream.Write(kMagicStringLen, kMagicString);
// Save some space to write the file size at the end
outputStream.WriteSwap32(0);
// Write out all the full size encrypted blocks we can
char buf[kEncryptChunkSize];
UInt32 amtRead;
while ((amtRead = sourceStream->Read(kEncryptChunkSize, &buf)) == kEncryptChunkSize)
{
IEncipher((UInt32*)&buf);
outputStream.Write(kEncryptChunkSize, &buf);
}
// Pad with random data and write out the final partial block, if there is one
if (amtRead > 0)
{
static bool seededRand = false;
if (!seededRand)
{
seededRand = true;
srand((unsigned int)time(nil));
}
for (int i = amtRead; i < kEncryptChunkSize; i++)
buf[i] = rand();
IEncipher((UInt32*)&buf);
outputStream.Write(kEncryptChunkSize, &buf);
}
// Write the original file size at the start
UInt32 actualSize = sourceStream->GetPosition();
outputStream.Rewind();
outputStream.Skip(kMagicStringLen);
outputStream.WriteSwap32(actualSize);
outputStream.Close();
return true;
}
bool plEncryptedStream::FileEncrypt(const char* fileName)
{
wchar* wFilename = hsStringToWString(fileName);
bool ret = FileEncrypt(wFilename);
delete [] wFilename;
return ret;
}
bool plEncryptedStream::FileEncrypt(const wchar* fileName)
{
hsUNIXStream sIn;
if (!sIn.Open(fileName))
return false;
// Don't double encrypt any files
if (ICheckMagicString(sIn.GetFILE()))
{
sIn.Close();
return true;
}
sIn.Rewind();
plEncryptedStream sOut;
bool wroteEncrypted = sOut.IWriteEncypted(&sIn, L"crypt.dat");
sIn.Close();
sOut.Close();
if (wroteEncrypted)
{
plFileUtils::RemoveFile(fileName);
plFileUtils::FileMove(L"crypt.dat", fileName);
}
return true;
}
bool plEncryptedStream::FileDecrypt(const char* fileName)
{
wchar* wFilename = hsStringToWString(fileName);
bool ret = FileDecrypt(wFilename);
delete [] wFilename;
return ret;
}
bool plEncryptedStream::FileDecrypt(const wchar* fileName)
{
plEncryptedStream sIn;
if (!sIn.Open(fileName))
return false;
hsUNIXStream sOut;
if (!sOut.Open(L"crypt.dat", L"wb"))
{
sIn.Close();
return false;
}
char buf[1024];
while (!sIn.AtEnd())
{
UInt32 numRead = sIn.Read(sizeof(buf), buf);
sOut.Write(numRead, buf);
}
sIn.Close();
sOut.Close();
plFileUtils::RemoveFile(fileName);
plFileUtils::FileMove(L"crypt.dat", fileName);
return true;
}
bool plEncryptedStream::ICheckMagicString(FILE* fp)
{
char magicString[kMagicStringLen+1];
fread(&magicString, kMagicStringLen, 1, fp);
magicString[kMagicStringLen] = '\0';
return (hsStrEQ(magicString, kMagicString) || hsStrEQ(magicString, kOldMagicString));
}
bool plEncryptedStream::IsEncryptedFile(const char* fileName)
{
wchar* wFilename = hsStringToWString(fileName);
bool ret = IsEncryptedFile(wFilename);
delete [] wFilename;
return ret;
}
bool plEncryptedStream::IsEncryptedFile(const wchar* fileName)
{
FILE* fp = _wfopen(fileName, L"rb");
if (!fp)
return false;
bool isEncrypted = ICheckMagicString(fp);
fclose(fp);
return isEncrypted;
}
hsStream* plEncryptedStream::OpenEncryptedFile(const char* fileName, bool requireEncrypted, UInt32* cryptKey)
{
wchar* wFilename = hsStringToWString(fileName);
hsStream* ret = OpenEncryptedFile(wFilename, requireEncrypted, cryptKey);
delete [] wFilename;
return ret;
}
hsStream* plEncryptedStream::OpenEncryptedFile(const wchar* fileName, bool requireEncrypted, UInt32* cryptKey)
{
#ifndef PLASMA_EXTERNAL_RELEASE
requireEncrypted = false;
#endif
bool isEncrypted = IsEncryptedFile(fileName);
hsStream* s = nil;
if (isEncrypted)
s = TRACKED_NEW plEncryptedStream(cryptKey);
// If this isn't an external release, let them use unencrypted data
else
if (!requireEncrypted)
s = TRACKED_NEW hsUNIXStream;
if (s)
s->Open(fileName, L"rb");
return s;
}
hsStream* plEncryptedStream::OpenEncryptedFileWrite(const char* fileName, UInt32* cryptKey)
{
wchar* wFilename = hsStringToWString(fileName);
hsStream* ret = OpenEncryptedFileWrite(wFilename, cryptKey);
delete [] wFilename;
return ret;
}
hsStream* plEncryptedStream::OpenEncryptedFileWrite(const wchar* fileName, UInt32* cryptKey)
{
hsStream* s = nil;
#ifdef PLASMA_EXTERNAL_RELEASE
s = TRACKED_NEW plEncryptedStream(cryptKey);
#else
s = TRACKED_NEW hsUNIXStream;
#endif
s->Open(fileName, L"wb");
return s;
}

View File

@ -0,0 +1,100 @@
/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
#ifndef plEncryptedStream_h_inc
#define plEncryptedStream_h_inc
#include "hsStream.h"
//
// Encrypt a large file by running FileEncrypt on it. Small files can be done
// in the usual way, but they will be in memory until Close is called. Files
// will be decrypted on the fly during read.operations
//
class plEncryptedStream : public hsStream
{
protected:
FILE* fRef;
UInt32 fKey[4];
UInt32 fActualFileSize;
bool fBufferedStream;
hsStream* fRAMStream;
wchar* fWriteFileName;
enum OpenMode { kOpenRead, kOpenWrite, kOpenFail };
OpenMode fOpenMode;
void IBufferFile();
UInt32 IRead(UInt32 bytes, void* buffer);
void IEncipher(UInt32* const v);
void IDecipher(UInt32* const v);
bool IWriteEncypted(hsStream* sourceStream, const wchar* outputFile);
static bool ICheckMagicString(FILE* fp);
public:
// If you don't pass in a key (4 UInt32's), the default one will be used
plEncryptedStream(UInt32* key=nil);
~plEncryptedStream();
virtual hsBool Open(const char* name, const char* mode = "rb");
virtual hsBool Open(const wchar* name, const wchar* mode = L"rb");
virtual hsBool Close();
virtual UInt32 Read(UInt32 byteCount, void* buffer);
virtual UInt32 Write(UInt32 byteCount, const void* buffer);
virtual hsBool AtEnd();
virtual void Skip(UInt32 deltaByteCount);
virtual void Rewind();
virtual void FastFwd();
virtual UInt32 GetEOF();
UInt32 GetActualFileSize() const { return fActualFileSize;}
static bool FileEncrypt(const char* fileName);
static bool FileEncrypt(const wchar* fileName);
static bool FileDecrypt(const char* fileName);
static bool FileDecrypt(const wchar* fileName);
static bool IsEncryptedFile(const char* fileName);
static bool IsEncryptedFile(const wchar* fileName);
// Attempts to create a read-binary stream for the requested file. If it's
// encrypted, you'll get a plEncryptedStream, otherwise just a standard
// hsUNIXStream. Remember to delete the stream when you're done with it.
static hsStream* OpenEncryptedFile(const char* fileName, bool requireEncrypted = true, UInt32* cryptKey = nil);
static hsStream* OpenEncryptedFile(const wchar* fileName, bool requireEncrypted = true, UInt32* cryptKey = nil);
static hsStream* OpenEncryptedFileWrite(const char* fileName, UInt32* cryptKey = nil);
static hsStream* OpenEncryptedFileWrite(const wchar* fileName, UInt32* cryptKey = nil);
};
#endif // plEncryptedStream_h_inc

View File

@ -0,0 +1,500 @@
/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
/////////////////////////////////////////////////////////////////////////////
//
// plFileUtils - Namespace of fun file utilities
//
//// History /////////////////////////////////////////////////////////////////
//
// 5.7.2002 mcn - Created
// 4.8.2003 chip - added FileCopy and FileMove for Unix
//
//////////////////////////////////////////////////////////////////////////////
#include "hsTypes.h"
#include "hsStlUtils.h"
#include "plFileUtils.h"
#include "hsFiles.h"
#include "hsStringTokenizer.h"
#include "hsWindows.h"
#include "../plUnifiedTime/plUnifiedTime.h"
#include "plSecureStream.h" // for the default key
#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>
#if HS_BUILD_FOR_WIN32
#include <direct.h>
#include <io.h>
#endif
#if HS_BUILD_FOR_UNIX
#include <unistd.h>
#include <errno.h>
#include <utime.h>
#endif
//// CreateDir ///////////////////////////////////////////////////////////////
// Creates the directory specified. Returns false if unsuccessful or
// directory already exists
hsBool plFileUtils::CreateDir( const char *path )
{
// Create our directory
#if HS_BUILD_FOR_WIN32
return ( mkdir( path ) == 0 ) ? true : ( errno==EEXIST );
#elif HS_BUILD_FOR_UNIX
return ( mkdir( path, 0777 ) == 0 ) ? true : ( errno==EEXIST );
#endif
}
hsBool plFileUtils::CreateDir( const wchar *path )
{
// Create our directory
#if HS_BUILD_FOR_WIN32
return ( _wmkdir( path ) == 0 ) ? true : ( errno==EEXIST );
#elif HS_BUILD_FOR_UNIX
return ( mkdir( path, 0777 ) == 0 ) ? true : ( errno==EEXIST );
#endif
}
hsBool plFileUtils::RemoveDir(const char* path)
{
return (rmdir(path) == 0);
}
hsBool plFileUtils::RemoveDirTree(const char * path)
{
hsFolderIterator it(path);
while (it.NextFile())
{
const char * fname = it.GetFileName();
if ( fname[0]=='.' )
continue;
char pathAndName[128];
it.GetPathAndName(pathAndName);
if ( it.IsDirectory() )
{
RemoveDirTree( pathAndName );
RemoveDir( pathAndName );
}
else
{
RemoveFile( pathAndName );
}
}
RemoveDir( path );
return 1;
}
//// RemoveFile ////////////////////////////////////////////////////////////
bool plFileUtils::RemoveFile(const char* filename, bool delReadOnly)
{
if (delReadOnly)
chmod(filename, S_IWRITE);
return (unlink(filename) == 0);
}
bool plFileUtils::RemoveFile(const wchar* filename, bool delReadOnly)
{
if (delReadOnly)
_wchmod(filename, S_IWRITE);
return (_wunlink(filename) == 0);
}
bool plFileUtils::FileCopy(const char* existingFile, const char* newFile)
{
wchar* wExisting = hsStringToWString(existingFile);
wchar* wNew = hsStringToWString(newFile);
bool ret = FileCopy(wExisting, wNew);
delete [] wExisting;
delete [] wNew;
return ret;
}
bool plFileUtils::FileCopy(const wchar* existingFile, const wchar* newFile)
{
#if HS_BUILD_FOR_WIN32
return (::CopyFileW(existingFile, newFile, FALSE) != 0);
#elif HS_BUILD_FOR_UNIX
char data[1500];
FILE* fp = fopen(existingFile, "rb");
FILE* fw = fopen(newFile, "w");
int num = 0;
bool retVal = true;
if (fp && fw){
while(!feof(fp)){
num = fread(data, sizeof( char ), 1500, fp);
if( ferror( fp ) ) {
retVal = false;
break;
}
fwrite(data, sizeof( char ), num, fw);
}
fclose(fp);
fclose(fw);
} else {
retVal = false;
}
return retVal;
#else
hsAssert(0, "Not implemented");
return false;
#endif
}
bool plFileUtils::FileMove(const char* existingFile, const char* newFile)
{
#if HS_BUILD_FOR_WIN32
return (::MoveFile(existingFile, newFile) != 0);
#elif HS_BUILD_FOR_UNIX
FileCopy(existingFile,newFile);
return( RemoveFile( existingFile )==0);
#else
hsAssert(0, "Not implemented");
return false;
#endif
}
bool plFileUtils::FileMove(const wchar* existingFile, const wchar* newFile)
{
#if HS_BUILD_FOR_WIN32
return (::MoveFileW(existingFile, newFile) != 0);
#elif HS_BUILD_FOR_UNIX
FileCopy(existingFile,newFile);
return( RemoveFile( existingFile )==0);
#else
hsAssert(0, "Not implemented");
return false;
#endif
}
bool plFileUtils::FileExists(const wchar* file)
{
FILE* fp = _wfopen(file, L"rb");
bool retVal = (fp != nil);
if (fp)
fclose(fp);
return retVal;
}
bool plFileUtils::FileExists(const char* file)
{
FILE* fp = fopen(file, "rb");
bool retVal = (fp != nil);
if (fp)
fclose(fp);
return retVal;
}
//// EnsureFilePathExists ////////////////////////////////////////////////////
// Given a filename with path, makes sure the file's path exists
hsBool plFileUtils::EnsureFilePathExists( const char *filename )
{
wchar* wFilename = hsStringToWString(filename);
hsBool ret = EnsureFilePathExists(wFilename);
delete [] wFilename;
return ret;
}
hsBool plFileUtils::EnsureFilePathExists( const wchar *filename )
{
hsWStringTokenizer izer( filename, L"\\/" );
hsBool lastWorked = false;
wchar token[ kFolderIterator_MaxPath ];
while( izer.Next( token, arrsize( token ) ) && izer.HasMoreTokens() )
{
// Want the full path from the start of the string
lastWorked = CreateDir( izer.fString );
izer.RestoreLastTerminator();
}
return lastWorked;
}
//// GetFileTimes ////////////////////////////////////////////////////////////
// Gets the creation and modification dates of the file specified. Returns
// false if unsuccessful
hsBool plFileUtils::GetFileTimes( const char *path, plUnifiedTime *createTimeOut, plUnifiedTime *modifyTimeOut )
{
struct stat fileInfo;
int result = stat( path, &fileInfo );
if( result != 0 )
return false;
if( createTimeOut != nil )
*createTimeOut = plUnifiedTime( fileInfo.st_ctime );
if( modifyTimeOut != nil )
*modifyTimeOut = plUnifiedTime( fileInfo.st_mtime );
return true;
}
plFileUtils::Modify plFileUtils::CompareModifyTimes(const char* file1, const char* file2)
{
plUnifiedTime modTime1, modTime2;
if (GetFileTimes(file1, nil, &modTime1) &&
GetFileTimes(file2, nil, &modTime2))
{
double diff = plUnifiedTime::GetTimeDifference(modTime1, modTime2);
if (hsABS(diff) <= 2)
return kFilesEqual;
else if (diff > 0)
return kFile1Newer;
else
return kFile2Newer;
}
return kFileError;
}
bool plFileUtils::SetModifyTime( const char * filename, const plUnifiedTime & timestamp )
{
#ifdef HS_BUILD_FOR_WIN32
HANDLE hFile = CreateFile(filename,
GENERIC_WRITE,FILE_SHARE_READ|FILE_SHARE_WRITE,
nil,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,nil);
if (hFile==INVALID_HANDLE_VALUE)
return false;
SYSTEMTIME systime;
systime.wDay = timestamp.GetDay();
systime.wDayOfWeek = timestamp.GetDayOfWeek();
systime.wHour = timestamp.GetHour();
systime.wMilliseconds = 0;
systime.wMinute = timestamp.GetMinute();
systime.wMonth = timestamp.GetMonth();
systime.wSecond = timestamp.GetSecond();
systime.wYear = timestamp.GetYear();
FILETIME localFileTime, filetime;
SystemTimeToFileTime(&systime,&localFileTime);
LocalFileTimeToFileTime(&localFileTime,&filetime);
SetFileTime(hFile,nil,nil,&filetime);
CloseHandle(hFile);
return true;
#elif HS_BUILD_FOR_UNIX
struct stat sbuf;
int result = stat( filename, &sbuf );
if( result )
return false;
struct utimbuf utb;
utb.actime = sbuf.st_atime;
utb.modtime = timestamp.GetSecs();
result = utime( filename, &utb );
if( result )
return false;
return true;
#endif
}
//// StripPath ///////////////////////////////////////////////////////////////
const char* plFileUtils::GetFileName(const char* path)
{
const char* c = strrchr(path, '/');
if (c == nil)
c = strrchr(path, '\\');
if (c == nil)
c = path;
else
c++;
return c;
}
const wchar* plFileUtils::GetFileName(const wchar* path)
{
const wchar* c = wcsrchr(path, L'/');
if (c == nil)
c = wcsrchr(path, L'\\');
if (c == nil)
c = path;
else
c++;
return c;
}
void plFileUtils::StripFile(char* pathAndName)
{
char* fileName = (char*)GetFileName(pathAndName);
if (fileName != pathAndName)
*fileName = '\0';
}
void plFileUtils::StripFile(wchar* pathAndName)
{
wchar* fileName = (wchar*)GetFileName(pathAndName);
if (fileName != pathAndName)
*fileName = L'\0';
}
void plFileUtils::StripExt(char* fileName)
{
char* ext = (char*)GetFileExt(fileName);
if (ext)
*(ext-1) = '\0';
}
const char* plFileUtils::GetFileExt(const char* pathAndName)
{
const char* fileName = GetFileName(pathAndName);
if (fileName)
{
const char* ext = strrchr(fileName, '.');
if (ext)
return ext+1;
}
return nil;
}
const wchar* plFileUtils::GetFileExt(const wchar* pathAndName)
{
const wchar* fileName = GetFileName(pathAndName);
if (fileName)
{
const wchar* ext = wcsrchr(fileName, L'.');
if (ext)
return ext+1;
}
return nil;
}
void plFileUtils::AddSlash(char* path)
{
char lastChar = path[strlen(path)-1];
if (lastChar != '\\' && lastChar != '/')
strcat(path, "\\");
}
void plFileUtils::ConcatFileName(char* path, const char* fileName)
{
AddSlash(path);
strcat(path, fileName);
}
//// GetFileSize /////////////////////////////////////////////////////////////
UInt32 plFileUtils::GetFileSize( const char *path )
{
wchar* wPath = hsStringToWString(path);
UInt32 ret = GetFileSize(wPath);
delete [] wPath;
return ret;
}
UInt32 plFileUtils::GetFileSize( const wchar *path )
{
UInt32 len = 0;
hsUNIXStream str;
if (str.Open(path, L"rb"))
{
len = str.GetEOF();
str.Close();
}
return len;
}
//// GetSecureEncryptionKey //////////////////////////////////////////////////
bool plFileUtils::GetSecureEncryptionKey(const char* filename, UInt32* key, unsigned length)
{
wchar* wFilename = hsStringToWString(filename);
bool ret = GetSecureEncryptionKey(wFilename, key, length);
delete [] wFilename;
return ret;
}
bool plFileUtils::GetSecureEncryptionKey(const wchar* filename, UInt32* key, unsigned length)
{
// looks for an encryption key file in the same directory, and reads it
std::wstring sFilename = filename;
// grab parent directory
unsigned loc = sFilename.rfind(L"\\");
if (loc == std::wstring::npos)
loc = sFilename.rfind(L"/");
std::wstring sDir;
if (loc != std::wstring::npos)
sDir = sFilename.substr(0, loc);
else // no directory
sDir = L"./";
if ((sDir[sDir.length()-1] != L'/') && (sDir[sDir.length()-1] != L'\\'))
sDir += L'/'; // add the slash, if it doesn't has one
// now add the key filename
std::wstring keyFile = sDir + kWKeyFilename;
if (FileExists(keyFile.c_str()))
{
// file exists, read from it
hsUNIXStream file;
file.Open(keyFile.c_str(), L"rb");
unsigned bytesToRead = length * sizeof(UInt32);
byte* buffer = (byte*)ALLOC(bytesToRead);
unsigned bytesRead = file.Read(bytesToRead, buffer);
file.Close();
unsigned memSize = min(bytesToRead, bytesRead);
memcpy(key, buffer, memSize);
FREE(buffer);
return true;
}
else
{
// file doesn't exist, use default key
unsigned memSize = min(length, arrsize(plSecureStream::kDefaultKey));
memSize *= sizeof(UInt32);
memcpy(key, plSecureStream::kDefaultKey, memSize);
return false;
}
}

View File

@ -0,0 +1,106 @@
/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
//////////////////////////////////////////////////////////////////////////////
//
// plFileUtils - Namespace of fun file utilities
//
//// History /////////////////////////////////////////////////////////////////
//
// 5.7.2002 mcn - Created
//
//////////////////////////////////////////////////////////////////////////////
#ifndef _plFileUtils_h
#define _plFileUtils_h
class plUnifiedTime;
namespace plFileUtils
{
static const char kKeyFilename[] = "encryption.key";
static const wchar kWKeyFilename[] = L"encryption.key";
// Creates the directory specified. Returns false if unsuccessful or directory already exists
hsBool CreateDir( const char *path );
hsBool CreateDir( const wchar *path );
hsBool RemoveDir(const char* path);
hsBool RemoveDirTree(const char * path);
// delete file from disk
bool RemoveFile(const char* filename, bool delReadOnly=false);
bool RemoveFile(const wchar* filename, bool delReadOnly=false);
bool FileCopy(const char* existingFile, const char* newFile);
bool FileCopy(const wchar* existingFile, const wchar* newFile);
bool FileMove(const char* existingFile, const char* newFile);
bool FileMove(const wchar* existingFile, const wchar* newFile);
bool FileExists(const char* file);
bool FileExists(const wchar* file);
// Given a filename with path, makes sure the file's path exists
hsBool EnsureFilePathExists( const char *filename );
hsBool EnsureFilePathExists( const wchar *filename );
// Gets the creation and modification dates of the file specified. Returns false if unsuccessful
hsBool GetFileTimes( const char *path, plUnifiedTime *createTimeOut, plUnifiedTime *modifyTimeOut );
// Compares file times, taking into account NTFS/FAT32 time issues
enum Modify { kFileError, kFilesEqual, kFile1Newer, kFile2Newer };
Modify CompareModifyTimes(const char* file1, const char* file2);
// Set file modify time
bool SetModifyTime( const char * filename, const plUnifiedTime & time );
// Return a pointer into the given string at the start of the actual filename (i.e. past any path info)
const char* GetFileName(const char* pathAndName);
const wchar* GetFileName(const wchar* pathAndName);
// Get the file extension (without the .), or nil if it doesn't have one
const char* GetFileExt(const char* pathAndName);
const wchar* GetFileExt(const wchar* pathAndName);
// Strips the filename off the given full path
void StripFile(char* pathAndName);
void StripFile(wchar* pathAndName);
void StripExt(char* fileName);
// Get the size of the given file in bytes
UInt32 GetFileSize( const char *path );
UInt32 GetFileSize( const wchar *path );
// Adds a slash to the end of a filename (or does nothing if it's already there)
void AddSlash(char* path);
// Concatenates fileName onto path, making sure to add a slash if necessary
void ConcatFileName(char* path, const char* fileName);
// searches the parent directory of filename for the encryption key file, and reads it
// into the key passed in. Returns false if the key file didn't exist (and sets key to
// the default key)
bool GetSecureEncryptionKey(const char* filename, UInt32* key, unsigned length);
bool GetSecureEncryptionKey(const wchar* filename, UInt32* key, unsigned length);
};
#endif // _plFileUtils_h

View File

@ -0,0 +1,202 @@
/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
//////////////////////////////////////////////////////////////////////////////
// //
// plInitFileReader - Helper class that parses a standard-format .ini file //
// and allows you to specify derived classes to handle //
// interpreting specific portions. //
// //
//////////////////////////////////////////////////////////////////////////////
#include "hsTypes.h"
#include "plInitFileReader.h"
#include "hsStream.h"
#include "hsUtils.h"
#include "hsStringTokenizer.h"
#include "plEncryptedStream.h"
plInitSectionTokenReader::plInitSectionTokenReader( const char *separators ) : fSeparators( separators )
{
}
hsBool plInitSectionTokenReader::ParseLine( const char *line, UInt32 userData )
{
hsStringTokenizer izer( line, fSeparators );
char *token = izer.next();
return IParseToken( token, &izer, userData );
}
void plInitFileReader::IInitReaders( plInitSectionReader **readerArray )
{
UInt32 i;
for( i = 0; readerArray[ i ] != nil; i++ )
fSections.Append( readerArray[ i ] );
hsAssert( fSections.GetCount() > 0, "No sections for initFileReader" );
fCurrSection = fSections[ 0 ];
}
plInitFileReader::plInitFileReader( plInitSectionReader **readerArray, UInt16 lineSize )
{
fRequireEncrypted = true;
fCurrLine = nil;
fLineSize = lineSize;
fStream = fOurStream = nil;
IInitReaders( readerArray );
fUnhandledSection = nil;
}
plInitFileReader::plInitFileReader( const char *fileName, plInitSectionReader **readerArray, UInt16 lineSize )
{
fRequireEncrypted = true;
fCurrLine = nil;
fLineSize = lineSize;
fStream = fOurStream = nil;
IInitReaders( readerArray );
if( !Open( fileName ) )
hsAssert( false, "Constructor open for plInitFileReader failed!" );
fUnhandledSection = nil;
}
plInitFileReader::plInitFileReader( hsStream *stream, plInitSectionReader **readerArray, UInt16 lineSize )
{
fRequireEncrypted = true;
fCurrLine = nil;
fLineSize = lineSize;
fStream = fOurStream = nil;
IInitReaders( readerArray );
if( !Open( stream ) )
hsAssert( false, "Constructor open for plInitFileReader failed!" );
fUnhandledSection = nil;
}
plInitFileReader::~plInitFileReader()
{
Close();
delete [] fCurrLine;
}
hsBool plInitFileReader::Open( const char *fileName )
{
if( fStream != nil )
{
hsAssert( false, "Unable to open initFileReader; already open" );
return false;
}
fOurStream = plEncryptedStream::OpenEncryptedFile( fileName, fRequireEncrypted );
if( fOurStream == nil )
return false;
fStream = fOurStream;
return true;
}
hsBool plInitFileReader::Open( hsStream *stream )
{
if( fStream != nil )
{
hsAssert( false, "Unable to open initFileReader; already open" );
return false;
}
fStream = stream;
return true;
}
hsBool plInitFileReader::Parse( UInt32 userData )
{
hsAssert( fStream != nil, "Nil stream in initFileReader::Parse(); file not yet open?" );
if( fCurrLine == nil )
fCurrLine = TRACKED_NEW char[ fLineSize + 1 ];
// Start parsing lines
while( fStream->ReadLn( fCurrLine, fLineSize ) )
{
// puts( fCurrLine );
// Is line a section header?
if( fCurrLine[ 0 ] == '[' )
{
// Yes--match against our sections and switch to the given one
char *end = strchr( fCurrLine, ']' );
if( end != nil )
*end = 0;
UInt32 i;
bool foundSection = false;
for( i = 0; i < fSections.GetCount(); i++ )
{
if( stricmp( fSections[ i ]->GetSectionName(), &fCurrLine[ 1 ] ) == 0 )
{
fCurrSection = fSections[ i ];
foundSection = true;
break;
}
}
if (!foundSection && fUnhandledSection)
{
fCurrSection = fUnhandledSection;
fCurrSection->SetSectionName(&fCurrLine[1]);
}
}
else
{
// Nope, just a line, pass to our current section tokenizer
if( !fCurrSection->ParseLine( fCurrLine, userData ) )
return false;
}
}
return true;
}
void plInitFileReader::Close( void )
{
if( fStream == nil )
return;
if( fStream == fOurStream )
{
fStream->Close();
delete fOurStream;
fOurStream = nil;
}
fStream = nil;
}

View File

@ -0,0 +1,137 @@
/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
//////////////////////////////////////////////////////////////////////////////
// //
// plInitFileReader - Helper class that parses a standard-format .ini file //
// and allows you to specify derived classes to handle //
// interpreting specific portions. //
// //
//// Usage ///////////////////////////////////////////////////////////////////
// //
// First create a set of derived classes from plInitSectionReader //
// (or plInitSectionTokenReader, to be easier) that will parse the lines //
// for each section of your .ini file. Then create a C-style array of //
// pointers to instances of each reader, the first being the default //
// reader, and ending with a nil pointer (see below). Finally, create //
// a plInitFileReader with the array you created and it'll parse the //
// given file (or stream) and call your readers as needed. You can also //
// optionally pass in a UInt32 for userData that will be passed on to each //
// reader in turn. //
// //
//////////////////////////////////////////////////////////////////////////////
#ifndef _plInitFileReader_h
#define _plInitFileReader_h
#include "hsTypes.h"
#include "hsStream.h"
#include "hsTemplates.h"
//// Base Section Class //////////////////////////////////////////////////////
// Define a derived version of this for each section of your init file.
class plInitSectionReader
{
public:
// Override this to define what [string] your section starts with
virtual const char *GetSectionName( void ) const = 0;
// Override this to parse each line in your section. Return false to abort parsing
virtual hsBool ParseLine( const char *line, UInt32 userData ) = 0;
// Override this if you're defining an unhandled section reader
virtual void SetSectionName(const char* section) {}
};
//// Semi-Derived Class //////////////////////////////////////////////////////
// Half-way derived class for parsing lines by tokens rather than pure
// strings.
class hsStringTokenizer;
class plInitSectionTokenReader : public plInitSectionReader
{
protected:
const char *fSeparators;
// Override this to parse each token in your section. Return false to abort parsing
virtual hsBool IParseToken( const char *token, hsStringTokenizer *tokenizer, UInt32 userData ) = 0;
public:
plInitSectionTokenReader( const char *separators = ",=\t" );
// Overridden for you. Override IParseToken()
virtual hsBool ParseLine( const char *line, UInt32 userData );
};
//// Main Reader Class ///////////////////////////////////////////////////////
// Create one of these and add an array of derived versions of the above
// reader to parse your init file.
class plInitFileReader
{
protected:
hsStream *fStream;
hsStream *fOurStream;
char *fCurrLine;
UInt32 fLineSize;
bool fRequireEncrypted;
plInitSectionReader *fCurrSection;
hsTArray<plInitSectionReader *> fSections;
plInitSectionReader* fUnhandledSection;
void IInitReaders( plInitSectionReader **readerArray );
public:
// The array passed in should be an array of pointers to plInitSectionReader,
// with the last pointer being nil (denoting the end of the array). The first
// element of the array will be the "default" section--i.e. if there is no section
// header at the top of the file, that reader will be used.
plInitFileReader( plInitSectionReader **readerArray, UInt16 lineSize = 256 );
plInitFileReader( const char *fileName, plInitSectionReader **readerArray, UInt16 lineSize = 256 );
plInitFileReader( hsStream *stream, plInitSectionReader **readerArray, UInt16 lineSize = 256 );
virtual ~plInitFileReader();
void SetRequireEncrypted(bool require) { fRequireEncrypted = require; }
bool GetRequireEncrypted() const { return fRequireEncrypted; }
void SetUnhandledSectionReader(plInitSectionReader* reader) { fUnhandledSection = reader; }
hsBool Open( const char *fileName );
hsBool Open( hsStream *stream );
hsBool Parse( UInt32 userData = 0 );
void Close( void );
hsBool IsOpen( void ) const { return fStream != nil; }
};
#endif //_plInitFileReader_h

View File

@ -0,0 +1,624 @@
/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
#include "plSecureStream.h"
#include "hsUtils.h"
#include "plFileUtils.h"
#include "hsSTLStream.h"
#include <time.h>
// our default encryption key
const UInt32 plSecureStream::kDefaultKey[4] = { 0x6c0a5452, 0x3827d0f, 0x3a170b92, 0x16db7fc2 };
static const int kEncryptChunkSize = 8;
static const char* kMagicString = "notthedroids";
static const int kMagicStringLen = 12;
static const int kFileStartOffset = kMagicStringLen + sizeof(UInt32);
static const int kMaxBufferedFileSize = 10*1024;
plSecureStream::plSecureStream(hsBool deleteOnExit, UInt32* key) :
fRef(INVALID_HANDLE_VALUE),
fActualFileSize(0),
fBufferedStream(false),
fRAMStream(nil),
fWriteFileName(nil),
fOpenMode(kOpenFail),
fDeleteOnExit(deleteOnExit)
{
if (key)
memcpy(&fKey, key, sizeof(kDefaultKey));
else
memcpy(&fKey, &kDefaultKey, sizeof(kDefaultKey));
}
plSecureStream::~plSecureStream()
{
}
//
// XXTEA
// http://www-users.cs.york.ac.uk/~matthew/TEA/
//
// A potential weakness in this implementation is the fact that a known value
// (length of the original file) is written at the start of the encrypted file. -Colin
//
#define MX (z>>5 ^ y<<2) + (y>>3 ^ z<<4) ^ (sum^y) + (fKey[p&3^e]^z)
void plSecureStream::IEncipher(UInt32* const v, UInt32 n)
{
register unsigned long y=v[0], z=v[n-1], e, delta=0x9E3779B9;
register unsigned long q = 6 + 52/n, p, sum = 0;
while (q-- > 0)
{
sum += delta;
e = (sum >> 2) & 3;
for (p = 0; p < n - 1; p++)
{
y = v[p + 1];
v[p] += MX;
z = v[p];
}
y = v[0];
v[n - 1] += MX;
z = v[n - 1];
}
}
void plSecureStream::IDecipher(UInt32* const v, UInt32 n)
{
register unsigned long y=v[0], z=v[n-1], e, delta=0x9E3779B9;
register unsigned long q = 6 + 52/n, p, sum = q * delta;
while (sum > 0)
{
e = (sum >> 2) & 3;
for (p = n - 1; p > 0; p--)
{
z = v[p - 1];
v[p] -= MX;
y = v[p];
}
z = v[n - 1];
v[0] -= MX;
y = v[0];
sum -= delta;
}
}
hsBool plSecureStream::Open(const char* name, const char* mode)
{
wchar* wName = hsStringToWString(name);
wchar* wMode = hsStringToWString(mode);
hsBool ret = Open(wName, wMode);
delete [] wName;
delete [] wMode;
return ret;
}
hsBool plSecureStream::Open(const wchar* name, const wchar* mode)
{
if (wcscmp(mode, L"rb") == 0)
{
if (fDeleteOnExit)
{
fRef = CreateFileW(name,
GENERIC_READ, // open for reading
0, // no one can open the file until we're done
NULL, // default security
OPEN_EXISTING, // only open existing files (no creation)
FILE_FLAG_DELETE_ON_CLOSE, // delete the file from disk when we close the handle
NULL); // no template
}
else
{
fRef = CreateFileW(name,
GENERIC_READ, // open for reading
0, // no one can open the file until we're done
NULL, // default security
OPEN_EXISTING, // only open existing files (no creation)
FILE_ATTRIBUTE_NORMAL, // normal file attributes
NULL); // no template
}
fPosition = 0;
if (fRef == INVALID_HANDLE_VALUE)
return false;
// Make sure our special magic string is there
if (!ICheckMagicString(fRef))
{
CloseHandle(fRef);
fRef = INVALID_HANDLE_VALUE;
return false;
}
DWORD numBytesRead;
ReadFile(fRef, &fActualFileSize, sizeof(UInt32), &numBytesRead, NULL);
// The encrypted stream is inefficient if you do reads smaller than
// 8 bytes. Since we do a lot of those, any file under a size threshold
// is buffered in memory
if (fActualFileSize <= kMaxBufferedFileSize)
IBufferFile();
fOpenMode = kOpenRead;
return true;
}
else if (wcscmp(mode, L"wb") == 0)
{
fRAMStream = TRACKED_NEW hsVectorStream;
fWriteFileName = TRACKED_NEW wchar[wcslen(name) + 1];
wcscpy(fWriteFileName, name);
fPosition = 0;
fOpenMode = kOpenWrite;
fBufferedStream = true;
return true;
}
else
{
hsAssert(0, "Unsupported open mode");
fOpenMode = kOpenFail;
return false;
}
}
hsBool plSecureStream::Close()
{
int rtn = false;
if (fOpenMode == kOpenWrite)
{
fRAMStream->Rewind();
rtn = IWriteEncrypted(fRAMStream, fWriteFileName);
}
if (fRef != INVALID_HANDLE_VALUE)
{
rtn = CloseHandle(fRef);
fRef = INVALID_HANDLE_VALUE;
}
if (fRAMStream)
{
delete fRAMStream;
fRAMStream = nil;
}
if (fWriteFileName)
{
delete [] fWriteFileName;
fWriteFileName = nil;
}
fActualFileSize = 0;
fBufferedStream = false;
fOpenMode = kOpenFail;
return rtn;
}
UInt32 plSecureStream::IRead(UInt32 bytes, void* buffer)
{
if (fRef == INVALID_HANDLE_VALUE)
return 0;
DWORD numItems;
bool success = (ReadFile(fRef, buffer, bytes, &numItems, NULL) != 0);
fBytesRead += numItems;
fPosition += numItems;
if ((unsigned)numItems < bytes)
{
if (success)
{
// EOF ocurred
char str[128];
sprintf(str, "Hit EOF on Windows read, only read %d out of requested %d bytes\n", numItems, bytes);
hsDebugMessage(str, 0);
}
else
{
hsDebugMessage("Error on Windows read", GetLastError());
}
}
return numItems;
}
void plSecureStream::IBufferFile()
{
fRAMStream = TRACKED_NEW hsVectorStream;
char buf[1024];
while (!AtEnd())
{
UInt32 numRead = Read(1024, buf);
fRAMStream->Write(numRead, buf);
}
fRAMStream->Rewind();
fBufferedStream = true;
CloseHandle(fRef);
fRef = INVALID_HANDLE_VALUE;
fPosition = 0;
}
hsBool plSecureStream::AtEnd()
{
if (fBufferedStream)
return fRAMStream->AtEnd();
else
return (GetPosition() == fActualFileSize);
}
void plSecureStream::Skip(UInt32 delta)
{
if (fBufferedStream)
{
fRAMStream->Skip(delta);
fPosition = fRAMStream->GetPosition();
}
else if (fRef != INVALID_HANDLE_VALUE)
{
fBytesRead += delta;
fPosition += delta;
SetFilePointer(fRef, delta, 0, FILE_CURRENT);
}
}
void plSecureStream::Rewind()
{
if (fBufferedStream)
{
fRAMStream->Rewind();
fPosition = fRAMStream->GetPosition();
}
else if (fRef != INVALID_HANDLE_VALUE)
{
fBytesRead = 0;
fPosition = 0;
SetFilePointer(fRef, kFileStartOffset, 0, FILE_BEGIN);
}
}
void plSecureStream::FastFwd()
{
if (fBufferedStream)
{
fRAMStream->FastFwd();
fPosition = fRAMStream->GetPosition();
}
else if (fRef != INVALID_HANDLE_VALUE)
{
fBytesRead = fPosition = SetFilePointer(fRef, kFileStartOffset + fActualFileSize, 0, FILE_BEGIN);
}
}
UInt32 plSecureStream::GetEOF()
{
return fActualFileSize;
}
UInt32 plSecureStream::Read(UInt32 bytes, void* buffer)
{
if (fBufferedStream)
{
UInt32 numRead = fRAMStream->Read(bytes, buffer);
fPosition = fRAMStream->GetPosition();
return numRead;
}
UInt32 startPos = fPosition;
// Offset into the first buffer (0 if we are aligned on a chunk, which means no extra block read)
UInt32 startChunkPos = startPos % kEncryptChunkSize;
// Amount of data in the partial first chunk (0 if we're aligned)
UInt32 startAmt = (startChunkPos != 0) ? hsMinimum(kEncryptChunkSize - startChunkPos, bytes) : 0;
UInt32 totalNumRead = IRead(bytes, buffer);
UInt32 numMidChunks = (totalNumRead - startAmt) / kEncryptChunkSize;
UInt32 endAmt = (totalNumRead - startAmt) % kEncryptChunkSize;
// If the start position is in the middle of a chunk we need to rewind and
// read that whole chunk in and decrypt it.
if (startChunkPos != 0)
{
// Move to the start of this chunk
SetPosition(startPos-startChunkPos);
// Read in the chunk and decrypt it
char buf[kEncryptChunkSize];
UInt32 numRead = IRead(kEncryptChunkSize, &buf);
IDecipher((UInt32*)&buf, kEncryptChunkSize / sizeof(UInt32));
// Copy the relevant portion to the output buffer
memcpy(buffer, &buf[startChunkPos], startAmt);
SetPosition(startPos+totalNumRead);
}
if (numMidChunks != 0)
{
UInt32* bufferPos = (UInt32*)(((char*)buffer)+startAmt);
for (int i = 0; i < numMidChunks; i++)
{
// Decrypt chunk
IDecipher(bufferPos, kEncryptChunkSize / sizeof(UInt32));
bufferPos += (kEncryptChunkSize / sizeof(UInt32));
}
}
if (endAmt != 0)
{
// Read in the final chunk and decrypt it
char buf[kEncryptChunkSize];
SetPosition(startPos + startAmt + numMidChunks*kEncryptChunkSize);
UInt32 numRead = IRead(kEncryptChunkSize, &buf);
IDecipher((UInt32*)&buf, kEncryptChunkSize / sizeof(UInt32));
memcpy(((char*)buffer)+totalNumRead-endAmt, &buf, endAmt);
SetPosition(startPos+totalNumRead);
}
// If we read into the padding at the end, update the total read to not include that
if (totalNumRead > 0 && startPos + totalNumRead > fActualFileSize)
{
totalNumRead -= (startPos + totalNumRead) - fActualFileSize;
SetPosition(fActualFileSize);
}
return totalNumRead;
}
UInt32 plSecureStream::Write(UInt32 bytes, const void* buffer)
{
if (fOpenMode != kOpenWrite)
{
hsAssert(0, "Trying to write to a read stream");
return 0;
}
return fRAMStream->Write(bytes, buffer);
}
bool plSecureStream::IWriteEncrypted(hsStream* sourceStream, const wchar* outputFile)
{
hsUNIXStream outputStream;
if (!outputStream.Open(outputFile, L"wb"))
return false;
outputStream.Write(kMagicStringLen, kMagicString);
// Save some space to write the file size at the end
outputStream.WriteSwap32(0);
// Write out all the full size encrypted blocks we can
char buf[kEncryptChunkSize];
UInt32 amtRead;
while ((amtRead = sourceStream->Read(kEncryptChunkSize, &buf)) == kEncryptChunkSize)
{
IEncipher((UInt32*)&buf, kEncryptChunkSize / sizeof(UInt32));
outputStream.Write(kEncryptChunkSize, &buf);
}
// Pad with random data and write out the final partial block, if there is one
if (amtRead > 0)
{
static bool seededRand = false;
if (!seededRand)
{
seededRand = true;
srand((unsigned int)time(nil));
}
for (int i = amtRead; i < kEncryptChunkSize; i++)
buf[i] = rand();
IEncipher((UInt32*)&buf, kEncryptChunkSize / sizeof(UInt32));
outputStream.Write(kEncryptChunkSize, &buf);
}
// Write the original file size at the start
UInt32 actualSize = sourceStream->GetPosition();
outputStream.Rewind();
outputStream.Skip(kMagicStringLen);
outputStream.WriteSwap32(actualSize);
outputStream.Close();
return true;
}
bool plSecureStream::FileEncrypt(const char* fileName, UInt32* key /* = nil */)
{
wchar* wFilename = hsStringToWString(fileName);
bool ret = FileEncrypt(wFilename, key);
delete [] wFilename;
return ret;
}
bool plSecureStream::FileEncrypt(const wchar* fileName, UInt32* key /* = nil */)
{
hsUNIXStream sIn;
if (!sIn.Open(fileName))
return false;
// Don't double encrypt any files
if (ICheckMagicString(sIn.GetFILE()))
{
sIn.Close();
return true;
}
sIn.Rewind();
plSecureStream sOut(false, key);
bool wroteEncrypted = sOut.IWriteEncrypted(&sIn, L"crypt.dat");
sIn.Close();
sOut.Close();
if (wroteEncrypted)
{
plFileUtils::RemoveFile(fileName);
plFileUtils::FileMove(L"crypt.dat", fileName);
}
return true;
}
bool plSecureStream::FileDecrypt(const char* fileName, UInt32* key /* = nil */)
{
wchar* wFilename = hsStringToWString(fileName);
bool ret = FileDecrypt(wFilename, key);
delete [] wFilename;
return ret;
}
bool plSecureStream::FileDecrypt(const wchar* fileName, UInt32* key /* = nil */)
{
plSecureStream sIn(false, key);
if (!sIn.Open(fileName))
return false;
hsUNIXStream sOut;
if (!sOut.Open(L"crypt.dat", L"wb"))
{
sIn.Close();
return false;
}
char buf[1024];
while (!sIn.AtEnd())
{
UInt32 numRead = sIn.Read(sizeof(buf), buf);
sOut.Write(numRead, buf);
}
sIn.Close();
sOut.Close();
plFileUtils::RemoveFile(fileName);
plFileUtils::FileMove(L"crypt.dat", fileName);
return true;
}
bool plSecureStream::ICheckMagicString(HANDLE fp)
{
char magicString[kMagicStringLen+1];
DWORD numBytesRead;
ReadFile(fp, &magicString, kMagicStringLen, &numBytesRead, NULL);
magicString[kMagicStringLen] = '\0';
return (hsStrEQ(magicString, kMagicString) != 0);
}
bool plSecureStream::IsSecureFile(const char* fileName)
{
wchar* wFilename = hsStringToWString(fileName);
bool ret = IsSecureFile(wFilename);
delete [] wFilename;
return ret;
}
bool plSecureStream::IsSecureFile(const wchar* fileName)
{
HANDLE fp = INVALID_HANDLE_VALUE;
fp = CreateFileW(fileName,
GENERIC_READ, // open for reading
0, // no one can open the file until we're done
NULL, // default security
OPEN_EXISTING, // only open existing files (no creation)
FILE_ATTRIBUTE_NORMAL, // normal file attributes
NULL); // no template
if (fp == INVALID_HANDLE_VALUE)
return false;
bool isEncrypted = ICheckMagicString(fp);
CloseHandle(fp);
return isEncrypted;
}
hsStream* plSecureStream::OpenSecureFile(const char* fileName, const UInt32 flags /* = kRequireEncryption */, UInt32* key /* = nil */)
{
wchar* wFilename = hsStringToWString(fileName);
hsStream* ret = OpenSecureFile(wFilename, flags, key);
delete [] wFilename;
return ret;
}
hsStream* plSecureStream::OpenSecureFile(const wchar* fileName, const UInt32 flags /* = kRequireEncryption */, UInt32* key /* = nil */)
{
bool requireEncryption = flags & kRequireEncryption;
#ifndef PLASMA_EXTERNAL_RELEASE
requireEncryption = false;
#endif
hsBool deleteOnExit = flags & kDeleteOnExit;
bool isEncrypted = IsSecureFile(fileName);
hsStream* s = nil;
if (isEncrypted)
s = TRACKED_NEW plSecureStream(deleteOnExit, key);
else if (!requireEncryption) // If this isn't an external release, let them use unencrypted data
s = TRACKED_NEW hsUNIXStream;
if (s)
s->Open(fileName, L"rb");
return s;
}
hsStream* plSecureStream::OpenSecureFileWrite(const char* fileName, UInt32* key /* = nil */)
{
wchar* wFilename = hsStringToWString(fileName);
hsStream* ret = OpenSecureFileWrite(wFilename, key);
delete [] wFilename;
return ret;
}
hsStream* plSecureStream::OpenSecureFileWrite(const wchar* fileName, UInt32* key /* = nil */)
{
hsStream* s = nil;
#ifdef PLASMA_EXTERNAL_RELEASE
s = TRACKED_NEW plSecureStream(false, key);
#else
s = TRACKED_NEW hsUNIXStream;
#endif
s->Open(fileName, L"wb");
return s;
}

View File

@ -0,0 +1,111 @@
/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
#ifndef plSecureStream_h_inc
#define plSecureStream_h_inc
#include "hsStream.h"
#include <windows.h>
// A slightly more secure stream then plEncryptedStream in that it uses windows file functions
// to prevent other processes from accessing the file it is working with. It also can be set
// to download the file from a server into a temporary directory (with a mangled name) and
// delete that file on close, thereby minimizing the chance of having that file examined or
// edited.
class plSecureStream: public hsStream
{
protected:
HANDLE fRef;
UInt32 fKey[4];
UInt32 fActualFileSize;
bool fBufferedStream;
hsStream* fRAMStream;
wchar* fWriteFileName;
enum OpenMode {kOpenRead, kOpenWrite, kOpenFail};
OpenMode fOpenMode;
hsBool fDeleteOnExit;
void IBufferFile();
UInt32 IRead(UInt32 bytes, void* buffer);
void IEncipher(UInt32* const v, UInt32 n);
void IDecipher(UInt32* const v, UInt32 n);
bool IWriteEncrypted(hsStream* sourceStream, const wchar* outputFile);
static bool ICheckMagicString(HANDLE fp);
public:
plSecureStream(hsBool deleteOnExit = false, UInt32* key = nil); // uses default key if you don't pass one in
~plSecureStream();
virtual hsBool Open(const char* name, const char* mode = "rb");
virtual hsBool Open(const wchar* name, const wchar* mode = L"rb");
virtual hsBool Close();
virtual UInt32 Read(UInt32 byteCount, void* buffer);
virtual UInt32 Write(UInt32 byteCount, const void* buffer);
virtual hsBool AtEnd();
virtual void Skip(UInt32 deltaByteCount);
virtual void Rewind();
virtual void FastFwd();
virtual UInt32 GetEOF();
UInt32 GetActualFileSize() const {return fActualFileSize;}
static bool FileEncrypt(const char* fileName, UInt32* key = nil);
static bool FileEncrypt(const wchar* fileName, UInt32* key = nil);
static bool FileDecrypt(const char* fileName, UInt32* key = nil);
static bool FileDecrypt(const wchar* fileName, UInt32* key = nil);
enum OpenSecureFileFlags
{
kRequireEncryption = 0x01,
kDeleteOnExit = 0x02,
};
static bool IsSecureFile(const char* fileName);
static bool IsSecureFile(const wchar* fileName);
// Attempts to create a read-binary stream for the requested file (delete the stream
// when you are done with it!)
static hsStream* OpenSecureFile(const char* fileName, const UInt32 flags = kRequireEncryption, UInt32* key = nil);
static hsStream* OpenSecureFile(const wchar* fileName, const UInt32 flags = kRequireEncryption, UInt32* key = nil);
// Attempts to create a write-binary stream for the requested file (delete the stream
// when you are done with it!)
static hsStream* OpenSecureFileWrite(const char* fileName, UInt32* key = nil);
static hsStream* OpenSecureFileWrite(const wchar* fileName, UInt32* key = nil);
static const UInt32 kDefaultKey[4]; // our default encryption key
};
#endif // plSecureStream_h_inc

View File

@ -0,0 +1,196 @@
/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
#include "hsUtils.h"
#include "hsFiles.h"
#include "plStreamSource.h"
#include "plSecureStream.h"
#include "plEncryptedStream.h"
#include "plFileUtils.h"
void ToLower(std::wstring& str)
{
for (unsigned i = 0; i < str.length(); i++)
str[i] = towlower(str[i]);
}
void ReplaceSlashes(std::wstring& path, wchar replaceWith)
{
for (unsigned i = 0; i < path.length(); i++)
{
if ((path[i] == L'\\') || (path[i] == L'/'))
path[i] = replaceWith;
}
}
void plStreamSource::ICleanup()
{
// loop through all the file data records, and delete the streams
std::map<std::wstring, fileData>::iterator curData;
for (curData = fFileData.begin(); curData != fFileData.end(); curData++)
{
curData->second.fStream->Close();
delete curData->second.fStream;
curData->second.fStream = nil;
}
fFileData.clear();
}
void plStreamSource::IBreakupFilename(std::wstring filename, std::wstring& dir, std::wstring& ext)
{
// break the filename up into its parts
char* temp = hsWStringToString(filename.c_str());
std::string sFilename = temp;
std::string sExt = plFileUtils::GetFileExt(temp);
plFileUtils::StripFile(temp);
std::string sDir = temp;
delete [] temp;
if (sDir == sFilename) // no directory
sDir = "";
if (sDir != "")
if ((sDir[sDir.length()-1] == '/') || (sDir[sDir.length()-1] == '\\'))
sDir = sDir.substr(0, sDir.length() - 1); // trim the slash, if it has one
wchar_t* wTemp;
wTemp = hsStringToWString(sDir.c_str());
dir = wTemp;
delete [] wTemp;
wTemp = hsStringToWString(sExt.c_str());
ext = wTemp;
delete [] wTemp;
}
hsStream* plStreamSource::GetFile(std::wstring filename)
{
ToLower(filename);
ReplaceSlashes(filename, L'/');
if (fFileData.find(filename) == fFileData.end())
{
#ifndef PLASMA_EXTERNAL_RELEASE
// internal releases can pull from disk
char* temp = hsWStringToString(filename.c_str());
std::string sFilename = temp;
delete [] temp;
if (plFileUtils::FileExists(sFilename.c_str()))
{
// file exists on disk, cache it
std::wstring dir, ext;
IBreakupFilename(filename, dir, ext);
fFileData[filename].fFilename = filename;
fFileData[filename].fDir = dir;
fFileData[filename].fExt = ext;
if (plSecureStream::IsSecureFile(sFilename.c_str()))
{
UInt32 encryptionKey[4];
plFileUtils::GetSecureEncryptionKey(sFilename.c_str(), encryptionKey, 4);
fFileData[filename].fStream = plSecureStream::OpenSecureFile(sFilename.c_str(), 0, encryptionKey);
}
else // otherwise it is an encrypted or plain stream, this call handles both
fFileData[filename].fStream = plEncryptedStream::OpenEncryptedFile(sFilename.c_str());
return fFileData[filename].fStream;
}
#endif // PLASMA_EXTERNAL_RELEASE
return nil;
}
return fFileData[filename].fStream;
}
std::vector<std::wstring> plStreamSource::GetListOfNames(std::wstring dir, std::wstring ext)
{
ToLower(ext);
ToLower(dir);
ReplaceSlashes(dir, L'/');
if (ext[0] == L'.')
ext = ext.substr(1, ext.length()); // trim the dot, if it has one
if (dir != L"")
if ((dir[dir.length()-1] == L'/') || (dir[dir.length()-1] == L'\\'))
dir = dir.substr(0, dir.length() - 1); // trim the slash, if it has one
// loop through all the file data records, and create the list
std::vector<std::wstring> retVal;
std::map<std::wstring, fileData>::iterator curData;
for (curData = fFileData.begin(); curData != fFileData.end(); curData++)
{
if ((curData->second.fDir == dir.c_str()) && (curData->second.fExt == ext))
retVal.push_back(curData->second.fFilename);
}
#ifndef PLASMA_EXTERNAL_RELEASE
// in internal releases, we can use on-disk files if they exist
// Build the search string as "dir/*.ext"
std::wstring wSearchStr = dir + L"/*." + ext;
char* temp = hsWStringToString(wSearchStr.c_str());
std::string searchStr = temp;
delete [] temp;
hsFolderIterator folderIter(searchStr.c_str(), true);
while (folderIter.NextFile())
{
const char* filename = folderIter.GetFileName();
wchar_t* wTemp = hsStringToWString(filename);
std::wstring wFilename = dir + L"/" + wTemp;
delete [] wTemp;
ToLower(wFilename);
if (fFileData.find(wFilename) == fFileData.end()) // we haven't added it yet
retVal.push_back(wFilename);
}
#endif // PLASMA_EXTERNAL_RELEASE
return retVal;
}
bool plStreamSource::InsertFile(std::wstring filename, hsStream* stream)
{
ToLower(filename);
ReplaceSlashes(filename, L'/');
if (fFileData.find(filename) != fFileData.end())
return false; // duplicate entry, return failure
// break the filename up into its parts
std::wstring dir, ext;
IBreakupFilename(filename, dir, ext);
// copy the data over (takes ownership of the stream!)
fFileData[filename].fFilename = filename;
fFileData[filename].fDir = dir;
fFileData[filename].fExt = ext;
fFileData[filename].fStream = stream;
return true;
}
plStreamSource* plStreamSource::GetInstance()
{
static plStreamSource source;
return &source;
}

View File

@ -0,0 +1,68 @@
/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
#ifndef plStreamSource_h_inc
#define plStreamSource_h_inc
#include "hsStream.h"
#include "hsStlUtils.h"
// A class for holding and accessing file streams. The preloader will insert
// files in here once they are loaded. In internal builds, if a requested file
// is not found, it will be retrieved from disk.
class plStreamSource
{
private:
struct fileData
{
std::wstring fFilename; // includes path
std::wstring fDir; // parent directory
std::wstring fExt;
hsStream* fStream; // we own this pointer, so clean it up
};
std::map<std::wstring, fileData> fFileData; // key is filename
void ICleanup(); // closes all file pointers and cleans up after itself
void IBreakupFilename(std::wstring filename, std::wstring& dir, std::wstring& ext);
plStreamSource() {}
public:
~plStreamSource() {ICleanup();}
// Force a cleanup of all data (some apps need to get at those file again, and they can't while we have them open)
void Cleanup() {ICleanup();}
// File access functions
hsStream* GetFile(std::wstring filename); // internal builds will read from disk if it doesn't exist
std::vector<std::wstring> GetListOfNames(std::wstring dir, std::wstring ext); // internal builds merge from disk
// For other classes to insert files (takes ownership of the stream if successful)
bool InsertFile(std::wstring filename, hsStream* stream);
// Instance handling
static plStreamSource* GetInstance();
};
#endif // plStreamSource_h_inc