Files
GTASource/rage/scaleform/Src/GFxPlayer/GFxLoader.cpp
expvintl 419f2e4752 init
2025-02-23 17:40:52 +08:00

546 lines
16 KiB
C++

/**********************************************************************
Filename : GFxLoader.cpp
Content : GFxPlayer loader implementation
Created : June 30, 2005
Authors :
Copyright : (c) 2001-2006 Scaleform Corp. All Rights Reserved.
Notes :
Licensees may use this file in accordance with the valid Scaleform
Commercial License Agreement provided with the software.
This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING
THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR ANY PURPOSE.
**********************************************************************/
#include "GFile.h"
#include "GDebug.h"
#include "GUTF8Util.h"
#include "GFxLoaderImpl.h"
#include "GImageInfo.h"
#include "GFxImageResource.h"
#include "GImage.h"
// Include GRenderConfig to support GFxImageCreator creating textures.
#include "GFxRenderConfig.h"
#include "GFxAmpServer.h"
#include "GHeapNew.h"
// ***** GFxURLBuilder implementation
// Default TranslateFilename implementation.
void GFxURLBuilder::BuildURL(GString *ppath, const LocationInfo& loc)
{
DefaultBuildURL(ppath, loc);
}
void GFxURLBuilder::DefaultBuildURL(GString *ppath, const LocationInfo& loc)
{
// Determine the file name we should use base on a relative path.
if (IsPathAbsolute(loc.FileName))
*ppath = loc.FileName;
else
{
// If not absolute, concatenate image path to the relative parent path.
UPInt length = loc.ParentPath.GetSize();
if (length > 0)
{
*ppath = loc.ParentPath;
UInt32 cend = loc.ParentPath[length - 1];
if ((cend != '\\') && (cend != '/'))
{
*ppath += "/";
}
*ppath += loc.FileName;
}
else
{
*ppath = loc.FileName;
}
}
}
// Static helper function used to determine if the path is absolute.
bool GFxURLBuilder::IsPathAbsolute(const char *putf8str)
{
// Absolute paths can star with:
// - protocols: 'file://', 'http://'
// - windows drive: 'c:\'
// - UNC share name: '\\share'
// - unix root '/'
// On the other hand, relative paths are:
// - directory: 'directory/file'
// - this directory: './file'
// - parent directory: '../file'
//
// For now, we don't parse '.' or '..' out, but instead let it be concatenated
// to string and let the OS figure it out. This, however, is not good for file
// name matching in library/etc, so it should be improved.
if (!putf8str || !*putf8str)
return true; // Treat empty strings as absolute.
UInt32 charVal = GUTF8Util::DecodeNextChar(&putf8str);
// Fist character of '/' or '\\' means absolute path.
if ((charVal == '/') || (charVal == '\\'))
return true;
while (charVal != 0)
{
// Treat a colon followed by a slash as absolute.
if (charVal == ':')
{
charVal = GUTF8Util::DecodeNextChar(&putf8str);
// Protocol or windows drive. Absolute.
if ((charVal == '/') || (charVal == '\\'))
return true;
}
else if ((charVal == '/') || (charVal == '\\'))
{
// Not a first character (else 'if' above the loop would have caught it).
// Must be a relative path.
break;
}
charVal = GUTF8Util::DecodeNextChar(&putf8str);
}
// We get here for relative paths.
return false;
}
// Modifies path to not include the filename, leaves trailing '/'.
bool GFxURLBuilder::ExtractFilePath(GString *ppath)
{
// And strip off the actual file name.
SPInt length = (SPInt)ppath->GetLength();
SPInt i = length-1;
for (; i>=0; i--)
{
UInt32 charVal = ppath->GetCharAt(i);
// The trailing name will end in either '/' or '\\',
// so just clip it off at that point.
if ((charVal == '/') || (charVal == '\\'))
{
*ppath = ppath->Substring(0, i+1);
break;
}
}
// Technically we can have extra logic somewhere for paths,
// such as enforcing protocol and '/' only based on flags,
// but we keep it simple for now.
return (i >= 0);
}
// ***** Default Image Creator implementation
// Default implementation reads images from DDS/TGA files
// creating GImageInfo objects.
GImageInfoBase* GFxImageCreator::CreateImage(const GFxImageCreateInfo &info)
{
GPtr<GImage> pimage;
UInt twidth = 0, theight = 0;
GMemoryHeap* pheap = info.pHeap ? info.pHeap : GMemory::GetGlobalHeap();
switch(info.Type)
{
case GFxImageCreateInfo::Input_Image:
pimage = info.pImage;
// We have to pass correct size; it is required at least
// when we are initializing image info with a texture.
twidth = pimage->Width;
theight = pimage->Height;
break;
case GFxImageCreateInfo::Input_File:
{
// If we got here, we are responsible for loading an image file.
GPtr<GFile> pfile = *info.pFileOpener->OpenFile(info.pFileInfo->FileName.ToCStr());
if (!pfile)
return 0; // Log ??
// Detecting the file format may be better!?! But then, how do we handle extensions?
// GFxLoader::FileFormatType format = GFxMovieRoot::DetectFileFormat(pfile);
// Load an image into GImage object.
pimage = *LoadBuiltinImage(pfile, info.pFileInfo->Format, info.Use,
info.pLog, info.pJpegSupport, info.pPNGSupport, info.pHeap);
if (!pimage)
return 0;
// info.pFileInfo->TargetWidth is 0 for gradient images; use real width instead
if (info.pFileInfo->TargetWidth)
twidth = info.pFileInfo->TargetWidth;
else
twidth = pimage->Width;
// info.pFileInfo->TargetHeight is 0 for gradient images; use real height instead
if (info.pFileInfo->TargetHeight)
theight = info.pFileInfo->TargetHeight;
else
theight = pimage->Height;
}
break;
// No input - empty image info.
case GFxImageCreateInfo::Input_None:
default:
return GHEAP_NEW(pheap) GImageInfo();
}
// Make a distinction whether to keep the data or not based on
// the KeepBindingData flag in GFxImageInfo.
if (IsKeepingImageData())
return GHEAP_NEW(pheap) GImageInfo(pimage, twidth, theight, false);
// Else, create a texture.
if (!info.pRenderConfig || !info.pRenderConfig->GetRenderer())
{
// We need to either provide a renderer, or use KeepImageBindData flag.
GFC_DEBUG_ERROR(1, "GFxImageCreator failed to create texture; no renderer installed");
return 0;
}
// If renderer can generate Event_DataLost for textures (D3D9), store GImage in GFxImageInfo
// anyway. This is helpful because otherwise images can be lost and wiped out. Users can
// override this behavior with custom creator if desired, handling loss in alternative manner.
if (info.pRenderConfig->GetRendererCapBits() & GRenderer::Cap_CanLoseData)
return GHEAP_NEW(pheap) GImageInfo(pimage, twidth, theight, false);
// If we are working with multi-threaded loading and the renderer does not support texture creation on
// separate threads we have to postpone the actual texture creation until the main renderer thread needs it
// and instruct image info to release image as soon as texture is created
if (info.ThreadedLoading && !(info.pRenderConfig->GetRendererCapBits() & GRenderer::Cap_ThreadedTextureCreation))
return GHEAP_NEW(pheap) GImageInfo(pimage, twidth, theight, true);
// Renderer ok, create a texture-based GFxImageInfo instead. This means that
// the source image memory will be released by our caller.
GPtr<GTexture> ptexture = *info.pRenderConfig->GetRenderer()->CreateTexture();
if (!ptexture || !ptexture->InitTexture(pimage))
return 0;
return GHEAP_NEW(pheap) GImageInfo(ptexture, twidth, theight);
}
// Loads a GImage from an open file, assuming the specified file format.
GImage* GFxImageCreator::LoadBuiltinImage(GFile* pfile,
GFxFileConstants::FileFormatType format,
GFxResource::ResourceUse use,
GFxLog* plog,
GFxJpegSupportBase* pjpegState, GFxPNGSupportBase* ppngState,
GMemoryHeap* pimageHeap)
{
// Open the file and create image.
GImage * pimage = 0;
const char* pfilePath = pfile->GetFilePath();
switch(format)
{
case GFxLoader::File_TGA:
pimage = GImage::ReadTga(pfile,
(use == GFxResource::Use_FontTexture) ?
GImage::Image_A_8 : GImage::Image_None, pimageHeap);
break;
case GFxLoader::File_DDS:
pimage = GImage::ReadDDS(pfile, pimageHeap);
break;
case GFxLoader::File_JPEG:
if (!pjpegState)
{
if(plog)
plog->LogMessage(
"Unable to load JPEG at URL \"%s\" - libjpeg not included\n", pfilePath);
else
{
GFC_DEBUG_WARNING1(1, "Unable to load JPEG at URL \"%s\" - libjpeg not included\n",
pfilePath);
}
}
else
pimage = pjpegState->ReadJpeg(pfile, pimageHeap);
break;
case GFxLoader::File_PNG:
if (!ppngState)
{
if (plog)
plog->LogMessage("Unable to load PNG at URL \"%s\" - GFxPNGState not set\n",
pfilePath);
else
{
GFC_DEBUG_WARNING1(1, "Unable to load PNG at URL \"%s\" - GFxPNGState not included\n",
pfilePath);
}
}
else
pimage = ppngState->CreateImage(pfile, pimageHeap);
break;
default:
// Unknown format!
if (plog)
plog->LogMessage("Default image loader failed to load '%s'", pfilePath);
else
{
GFC_DEBUG_WARNING1(1, "Default image loader failed to load '%s'",
pfilePath);
}
break;
}
return pimage;
}
// ***** GFxStateBag implementation
// Implementation that allows us to override the log.
GFile* GFxStateBag::OpenFileEx(const char *pfilename, GFxLog *plog)
{
GPtr<GFxFileOpenerBase> popener = GetFileOpener();
if (!popener)
{
// Don't even have a way to open the file.
if (plog)
plog->LogError(
"Error: GFxLoader failed to open '%s', GFxFileOpener not installed\n",
pfilename);
return 0;
}
return popener->OpenFileEx(pfilename, plog);
}
// Opens a file using the specified callback.
GFile* GFxStateBag::OpenFile(const char *pfilename)
{
return OpenFileEx(pfilename, GetLog());
}
GImageInfoBase* GFxStateBag::CreateImageInfo(const GFxImageCreateInfo& info)
{
GPtr<GFxImageCreator> pcreator = GetImageCreator();
if (pcreator)
return pcreator->CreateImage(info);
return new GImageInfo(
(info.Type == GFxImageCreateInfo::Input_Image) ? info.pImage : 0,
(info.Type == GFxImageCreateInfo::Input_File) ? info.pFileInfo->TargetWidth : 0,
(info.Type == GFxImageCreateInfo::Input_File) ? info.pFileInfo->TargetHeight : 0);
}
// ***** GFxLoader implementation
// Internal GFC Evaluation License Reader
#ifdef GFC_BUILD_EVAL
#define GFC_EVAL(x) GFx_##x
#define GFC_LIB_NAME_S "GFx"
#include "GFCValidateEval.cpp"
#else
void GFx_ValidateEvaluation() { }
#endif
GFxLoader::GFxLoader(const GFxLoader::LoaderConfig& config)
{
InitLoader(config);
}
GFxLoader::GFxLoader(const GPtr<GFxFileOpenerBase>& pfileOpener,
const GPtr<GFxZlibSupportBase>& pzlib,
const GPtr<GFxJpegSupportBase>& pjpeg)
{
LoaderConfig config(0, pfileOpener, pzlib, pjpeg);
InitLoader(config);
}
// Create a new loader, copying it's library and states.
GFxLoader::GFxLoader(const GFxLoader& src)
{
GFx_ValidateEvaluation();
// Create new LoaderImpl with copied states.
pImpl = GNEW GFxLoaderImpl(src.pImpl);
// Copy strong resource lib reference.
pStrongResourceLib = src.pStrongResourceLib;
if (pStrongResourceLib)
pStrongResourceLib->AddRef();
GFC_DEBUG_ERROR(!pImpl, "GFxLoader::GFxLoader failed to initialize properly, low memory");
}
GFxLoader::~GFxLoader()
{
#ifdef GFX_AMP_SERVER
GFxAmpServer::GetInstance().RemoveLoader(this);
#endif
if (pImpl)
pImpl->Release();
if (pStrongResourceLib)
pStrongResourceLib->Release();
}
void GFxLoader::InitLoader(const LoaderConfig& cfg)
{
GFx_ValidateEvaluation();
bool debugHeap = (cfg.DefLoadFlags & LoadDebugHeap) ? true : false;
DefLoadFlags = cfg.DefLoadFlags;
pStrongResourceLib = GNEW GFxResourceLib(debugHeap);
if ((pImpl = GNEW GFxLoaderImpl(pStrongResourceLib, debugHeap))!=0)
{
SetFileOpener(cfg.pFileOpener);
SetParseControl(GPtr<GFxParseControl>(*GNEW GFxParseControl(GFxParseControl::VerboseParseNone)));
SetZlibSupport(cfg.pZLibSupport);
SetJpegSupport(cfg.pJpegSupport);
SetMeshCacheManager(GPtr<GFxMeshCacheManager>(*GNEW GFxMeshCacheManager(debugHeap)));
}
#ifdef GFX_AMP_SERVER
if (!debugHeap)
{
GFxAmpServer::GetInstance().AddLoader(this);
}
#endif
GFC_DEBUG_ERROR(!pImpl || !pStrongResourceLib,
"GFxLoader::GFxLoader failed to initialize properly, low memory");
}
GFxStateBag* GFxLoader::GetStateBagImpl() const
{
return pImpl;
}
bool GFxLoader::CheckTagLoader(int tagType) const
{
return (pImpl) ? pImpl->CheckTagLoader(tagType) : 0;
};
// Resource library management.
void GFxLoader::SetResourceLib(GFxResourceLib *plib)
{
if (pImpl)
{
if (plib)
plib->AddRef();
if (pStrongResourceLib)
pStrongResourceLib->Release();
pStrongResourceLib = plib;
pImpl->SetWeakResourceLib(plib->GetWeakLib());
}
}
GFxResourceLib* GFxLoader::GetResourceLib() const
{
return pStrongResourceLib;
}
// *** Movie Loading
// Movie Loading APIs just delegate to GFxLoaderImpl after some error checking.
bool GFxLoader::GetMovieInfo(const char *pfilename, GFxMovieInfo *pinfo,
bool getTagCount, UInt loadConstants)
{
if (!pfilename || pfilename[0]==0)
{
GFC_DEBUG_WARNING(1, "GFxLoader::GetMovieInfo - passed filename is empty, no file to load");
return 0;
}
if (!pinfo)
{
GFC_DEBUG_WARNING(1, "GFxLoader::GetMovieInfo - passed GFxMovieInfo pointer is NULL");
return 0;
}
return pImpl ? pImpl->GetMovieInfo(pfilename, pinfo, getTagCount, loadConstants | DefLoadFlags) : 0;
}
GFxMovieDef* GFxLoader::CreateMovie(const char *pfilename, UInt loadConstants, UPInt memoryArena)
{
if (!pfilename || pfilename[0]==0)
{
GFC_DEBUG_WARNING(1, "GFxLoader::CreateMovie - passed filename is empty, no file to load");
return 0;
}
return pImpl ? pImpl->CreateMovie(pfilename, loadConstants | DefLoadFlags, memoryArena) : 0;
}
/*
GFxMovieDef* GFxLoader::CreateMovie(GFile *pfile, UInt loadConstants)
{
if (!pfile || !pfile->IsValid())
{
GFC_DEBUG_WARNING(1, "GFxLoader::CreateMovie - passed file is not valid, no file to load");
return 0;
}
return pImpl ? pImpl->CreateMovie(pfile, 0, loadConstants) : 0;
}
*/
// Unpins all resources held in the library
void GFxLoader::UnpinAll()
{
if (pStrongResourceLib)
pStrongResourceLib->UnpinAll();
}
void GFxLoader::CancelLoading()
{
pImpl->CancelLoading();
}
void GFxImagePackParamsBase::SetTextureConfig(const TextureConfig& config)
{
PackTextureConfig = config;
}
//////////////////////////////////////////////////////////////////////////
#ifdef GFC_BUILD_DEBUG
int GFx_Compile_without_GFC_BUILD_DEBUG;
#else
int GFx_Compile_with_GFC_BUILD_DEBUG;
#endif