Skip to content

Commit

Permalink
Refactor: Continued resource locator cleanup refactorings
Browse files Browse the repository at this point in the history
  • Loading branch information
danij-deng committed Nov 13, 2012
1 parent c5fd923 commit 99ecbbe
Show file tree
Hide file tree
Showing 7 changed files with 95 additions and 112 deletions.
3 changes: 3 additions & 0 deletions doomsday/engine/portable/include/resourcenamespace.h
Expand Up @@ -28,6 +28,9 @@
#include "pathtree.h"
#include "uri.h"

/// ResourceNamespace names must be at least this number of characters.
#define RESOURCENAMESPACE_MINNAMELENGTH URI_MINSCHEMELENGTH

/**
* @defgroup searchPathFlags Search Path Flags
* @ingroup flags
Expand Down
15 changes: 6 additions & 9 deletions doomsday/engine/portable/include/sys_reslocator.h
Expand Up @@ -35,6 +35,8 @@
#include "uri.h"

#ifdef __cplusplus
#include <de/String>

extern "C" {
#endif

Expand Down Expand Up @@ -193,6 +195,7 @@ resourcenamespaceid_t F_ResourceNamespaceForName(char const* name);
*/
resourcetype_t F_GuessResourceTypeByName(char const* name);

#ifdef __cplusplus
/**
* Apply mapping for this namespace to the specified path (if enabled).
*
Expand All @@ -201,18 +204,12 @@ resourcetype_t F_GuessResourceTypeByName(char const* name);
*
* e.g.: "Models/my/cool/model.dmd" => "$(App.DataPath)/$(GamePlugin.Name)/models/my/cool/model.dmd"
*
* @param rni Unique identifier of the namespace whose mappings to apply.
* @param rnamespaceId Unique identifier of the namespace whose mappings to apply.
* @param path The path to be mapped (applied in-place).
* @return @c true iff mapping was applied to the path.
*/
boolean F_MapGameResourcePath(resourcenamespaceid_t rni, ddstring_t* path);

/**
* Apply all resource namespace mappings to the specified path.
*
* @return @c true iff the path was mapped.
*/
boolean F_ApplyGamePathMapping(ddstring_t* path);
bool F_MapGameResourcePath(resourcenamespaceid_t rnamespaceId, de::String& path);
#endif

/**
* Convert a resourceclass_t constant into a string for error/debug messages.
Expand Down
8 changes: 4 additions & 4 deletions doomsday/engine/portable/src/lumpindex.cpp
Expand Up @@ -271,11 +271,11 @@ bool LumpIndex::isValidIndex(lumpnum_t lumpNum) const
return (lumpNum >= 0 && lumpNum < d->lumps.size());
}

static QString invalidIndexMessage(int invalidIdx, int lastValidIdx)
static String invalidIndexMessage(int invalidIdx, int lastValidIdx)
{
QString msg = QString("Invalid lump index %1 ").arg(invalidIdx);
if(lastValidIdx < 0) msg += "(file is empty)";
else msg += QString("(valid range: [0..%2])").arg(lastValidIdx);
String msg = String("Invalid lump index %1").arg(invalidIdx);
if(lastValidIdx < 0) msg += " (file is empty)";
else msg += String(", valid range: [0..%2)").arg(lastValidIdx);
return msg;
}

Expand Down
42 changes: 13 additions & 29 deletions doomsday/engine/portable/src/sys_reslocator.cpp
Expand Up @@ -110,8 +110,6 @@ static de::Str const defaultNamespaceForClass[RESOURCECLASS_COUNT] = {
static ResourceNamespaceInfo* namespaces = 0;
static uint numNamespaces = 0;

#define RESOURCENAMESPACE_MINNAMELENGTH URI_MINSCHEMELENGTH

/**
* @param name Unique symbolic name of this namespace. Must be at least
* @c RESOURCENAMESPACE_MINNAMELENGTH characters long.
Expand Down Expand Up @@ -722,41 +720,27 @@ resourcetype_t F_GuessResourceTypeByName(char const* path)
return RT_NONE; // Unrecognizable.
}

boolean F_MapGameResourcePath(resourcenamespaceid_t rni, ddstring_t* path)
bool F_MapGameResourcePath(resourcenamespaceid_t rni, String& path)
{
ResourceNamespace* rnamespace = namespaceById(rni);
if(!rnamespace) return false;

if(!path || Str_IsEmpty(path)) return false;
if(!rnamespace) return false;
if(path.isEmpty()) return false;

// Are virtual path mappings in effect for this namespace?
ResourceNamespaceInfo* info = namespaceInfoById(rni);
DENG_ASSERT(info);
if(!(info->flags & RNF_USE_VMAP)) return false;

if(info->flags & RNF_USE_VMAP)
{
QByteArray rnamespaceName = rnamespace->name().toUtf8();
int const nameLen = rnamespaceName.length();
int const pathLen = Str_Length(path);

if(nameLen <= pathLen && Str_At(path, nameLen) == '/' &&
!qstrnicmp(rnamespaceName.constData(), Str_Text(path), nameLen))
{
Str_Prepend(path, "$(App.DataPath)/$(GamePlugin.Name)/");
return true;
}
}
String const& namespaceName = rnamespace->name();

return false;
}
// Does this path qualify for mapping?
if(path.length() <= namespaceName.length()) return false;
if(path.at(namespaceName.length()) != '/') return false;
if(!path.beginsWith(namespaceName, Qt::CaseInsensitive)) return false;

boolean F_ApplyGamePathMapping(ddstring_t* path)
{
DENG_ASSERT(path);
uint i = 1;
boolean result = false;
while(i < numNamespaces + 1 && !(result = F_MapGameResourcePath(i++, path)))
{}
return result;
// Yes.
path = String("$(App.DataPath)/$(GamePlugin.Name)") / path;
return true;
}

char const* F_ResourceClassStr(resourceclass_t rclass)
Expand Down
134 changes: 64 additions & 70 deletions doomsday/engine/portable/src/zip.cpp
Expand Up @@ -24,24 +24,23 @@

#include <zlib.h>

#include <QDir>
#include <vector>

#include "de_base.h"
#include "de_filesys.h"
#include "game.h"
#include "lumpcache.h"
#include "pathtree.h"

#include "zip.h"

#include <vector>

#include <de/App>
#include <de/ByteOrder>
#include <de/Error>
#include <de/Log>
#include <de/NativePath>
#include <de/memory.h>
#include <de/memoryzone.h>

static QString invalidIndexMessage(int invalidIdx, int lastValidIdx);
#include "zip.h"

namespace de {

Expand Down Expand Up @@ -137,7 +136,8 @@ typedef struct centralend_s {
} centralend_t;
#pragma pack()

static void ApplyGamePathMappings(ddstring_t* dest, const ddstring_t* src);
static String invalidIndexMessage(int invalidIdx, int lastValidIdx);
static bool applyGamePathMappings(String& path);

class ZipFile : public File1
{
Expand Down Expand Up @@ -373,8 +373,6 @@ struct Zip::Instance
*/
char* pos;
int entryCount = 0;
ddstring_t entryPath;
Str_Init(&entryPath);
for(int pass = 0; pass < 2; ++pass)
{
if(pass == 1)
Expand All @@ -399,30 +397,25 @@ struct Zip::Instance
// Advance the cursor past the variable sized fields.
pos += USHORT(header->fileNameSize) + USHORT(header->extraFieldSize) + USHORT(header->commentSize);

// Copy characters up to fileNameSize.
Str_Clear(&entryPath);
Str_PartAppend(&entryPath, nameStart, 0, USHORT(header->fileNameSize));
String filePath = QDir::fromNativeSeparators(NativePath(nameStart, USHORT(header->fileNameSize)));

// Directories are skipped.
if(ULONG(header->size) == 0 && Str_RAt(&entryPath, 0) == '/')
{
continue;
}
// Skip directories (we don't presently model these).
if(ULONG(header->size) == 0 && filePath.last() == '/') continue;

// Do we support the format of this lump?
if(USHORT(header->compression) != ZFC_NO_COMPRESSION &&
USHORT(header->compression) != ZFC_DEFLATED)
{
if(pass != 0) continue;
LOG_WARNING("Zip %s:'%s' uses an unsupported compression algorithm, ignoring.")
<< de::NativePath(self->composePath()).pretty() << Str_Text(&entryPath);
<< NativePath(self->composePath()).pretty() << NativePath(filePath).pretty();
}

if(USHORT(header->flags) & ZFH_ENCRYPTED)
{
if(pass != 0) continue;
LOG_WARNING("Zip %s:'%s' is encrypted.\n Encryption is not supported, ignoring.")
<< de::NativePath(self->composePath()).pretty() << Str_Text(&entryPath);
<< NativePath(self->composePath()).pretty() << NativePath(filePath).pretty();
}

if(pass == 0)
Expand Down Expand Up @@ -450,26 +443,39 @@ struct Zip::Instance
compressedSize = ULONG(header->size);
}

// Convert all slashes to our internal separator.
F_FixSlashes(&entryPath, &entryPath);

if(DD_GameLoaded())
{
// In some cases the path inside the file is mapped to another virtual location.
ApplyGamePathMappings(&entryPath, &entryPath);
// In some cases the path to the file is mapped to some
// other location in the virtual file system.
String filePathCopy = filePath;
if(applyGamePathMappings(filePathCopy))
{
try
{
// Resolve all symbolic references in the path.
filePath = Uri(filePathCopy, RC_NULL).resolved();
}
catch(de::Uri::ResolveError const& er)
{
LOG_WARNING(er.asText());
}
}
}

// Make it absolute.
F_PrependBasePath(&entryPath, &entryPath);
String basePath = QDir::fromNativeSeparators(DENG2_APP->nativeBasePath());
filePath = basePath / filePath;

QByteArray filePathUtf8 = filePath.toUtf8();

ZipFile* record =
new ZipFile(*FileHandleBuilder::fromFileLump(*self, lumpIdx, true/*don't buffer*/),
Str_Text(&entryPath),
filePathUtf8.constData(),
FileInfo(self->lastModified(), // Inherited from the file (note recursion).
lumpIdx, baseOffset, ULONG(header->size),
compressedSize),
self);
PathTree::Node* node = lumpDirectory->insert(Uri(Str_Text(&entryPath), RC_NULL));
PathTree::Node* node = lumpDirectory->insert(Uri(filePath, RC_NULL));
node->setUserPointer(record);

lumpIdx++;
Expand All @@ -478,7 +484,6 @@ struct Zip::Instance

// The file central directory is no longer needed.
M_Free(centralDirectory);
Str_Free(&entryPath);
}

static int buildLumpNodeLutWorker(PathTree::Node& node, void* parameters)
Expand Down Expand Up @@ -926,29 +931,25 @@ bool Zip::uncompressRaw(uint8_t* in, size_t inSize, uint8_t* out, size_t outSize
/**
* The path inside the zip might be mapped to another virtual location.
*
* @return @c true= iff @a path was mapped to another location.
*
* @todo This is clearly implemented in the wrong place. Path mapping
* should be done at a higher level.
*
* Data files (pk3, zip, lmp, wad, deh) in the root are mapped to Data/Game/Auto.
* Definition files (ded) in the root are mapped to Defs/Game/Auto.
* Paths that begin with a '@' are mapped to Defs/Game/Auto.
* Paths that begin with a '#' are mapped to Data/Game/Auto.
* Key-named directories at the root are mapped to another location.
*/
static void ApplyGamePathMappings(ddstring_t* dest, ddstring_t const* src)
static bool applyGamePathMappings(String& path)
{
String path = String(Str_Text(src));

// Manually mapped to Defs?
if(path.beginsWith('@'))
{
path.remove(0, 1);
if(path.at(0) == '/') path.remove(0, 1);

path = String("$(App.DefsPath)/$(GamePlugin.Name)/auto/") / path;
return true;
}

// Manually mapped to Data?
else if(path.beginsWith('#'))
if(path.beginsWith('#'))
{
path.remove(0, 1);
if(path.at(0) == '/') path.remove(0, 1);
Expand All @@ -966,9 +967,11 @@ static void ApplyGamePathMappings(ddstring_t* dest, ddstring_t const* src)
}

path = String("$(App.DataPath)/$(GamePlugin.Name)/auto/") / path;
return true;
}

// Implicitly mapped to another location?
else if(!path.contains('/'))
if(!path.contains('/'))
{
// No directory separators; i.e., a root file.
QByteArray fileName = path.fileName().toUtf8();
Expand Down Expand Up @@ -1001,50 +1004,41 @@ static void ApplyGamePathMappings(ddstring_t* dest, ddstring_t const* src)
}
// Kludge end

switch(rclass)
// Mapped to the Data directory?
if(rclass == RC_PACKAGE)
{
case RC_PACKAGE: // Mapped to the Data directory.
path = String("$(App.DataPath)/$(GamePlugin.Name)/auto/") / path;
break;
return true;
}

case RC_DEFINITION: // Mapped to the Defs directory.
// Mapped to the Defs directory?
if(rclass == RC_DEFINITION)
{
path = String("$(App.DefsPath)/$(GamePlugin.Name)/auto/") / path;
break;

default: /* Not mapped */ break;
return true;
}

return false;
}

// Key-named directories in the root might be mapped to another location.
else
uint const numNamespaces = F_NumResourceNamespaces();
for(uint i = 0; i < numNamespaces; ++i)
{
AutoStr* temp = AutoStr_FromTextStd(Str_Text(src));
if(F_ApplyGamePathMapping(temp))
if(F_MapGameResourcePath(resourcenamespaceid_t(i + 1), path))
{
path = String(Str_Text(temp));
return true;
}
}

// Resolve all symbolic references in the path.
try
{
QByteArray resolvedPath = Uri(path, RC_NULL).resolved().toUtf8();
Str_Set(dest, resolvedPath.constData());
return;
}
catch(de::Uri::ResolveError const& er)
{
LOG_WARNING(er.asText());
}

Str_Copy(dest, src);
return false;
}

} // namespace de

static QString invalidIndexMessage(int invalidIdx, int lastValidIdx)
static String invalidIndexMessage(int invalidIdx, int lastValidIdx)
{
QString msg = QString("Invalid lump index %1 ").arg(invalidIdx);
if(lastValidIdx < 0) msg += "(file is empty)";
else msg += QString("(valid range: [0..%2])").arg(lastValidIdx);
String msg = String("Invalid lump index %1").arg(invalidIdx);
if(lastValidIdx < 0) msg += " (file is empty)";
else msg += String(", valid range: [0..%2)").arg(lastValidIdx);
return msg;
}

} // namespace de
1 change: 1 addition & 0 deletions doomsday/libdeng2/include/de/filesys/nativepath.h
Expand Up @@ -58,6 +58,7 @@ class DENG2_PUBLIC NativePath : public String
NativePath(const QString& str);

NativePath(const char* nullTerminatedCStr);
NativePath(const char* cStr, size_type length);

/**
* Assignment.
Expand Down

0 comments on commit 99ecbbe

Please sign in to comment.