1
0
mirror of https://github.com/rwengine/openrw.git synced 2024-11-22 02:12:45 +01:00

New File handling implementation

- move FileHandle into a separate header
- Implement FileIndex, a system to normalize filenames and sources
This commit is contained in:
Daniel Evans 2015-02-26 03:57:28 +00:00 committed by Daniel Evans
parent 3801e69e81
commit abf43f07c8
11 changed files with 264 additions and 17 deletions

View File

View File

@ -0,0 +1,18 @@
#pragma once
#include <memory>
/**
* @brief Contains a pointer to a file's contents.
*/
struct FileContentsInfo
{
char* data;
size_t length;
~FileContentsInfo() {
delete[] data;
}
};
typedef std::shared_ptr<FileContentsInfo> FileHandle;

View File

@ -0,0 +1,50 @@
#pragma once
#include "FileHandle.hpp"
#include <string>
#include <map>
class FileIndex
{
public:
struct IndexData
{
/// Lowercase identifying filename
std::string filename;
/// Original filename
std::string originalName;
/// Containing directory
std::string directory;
/// The archive filename (if applicable)
std::string archive;
};
/**
* Adds the files contained within the given directory to the
* file index.
*/
void indexDirectory(const std::string& directory);
/**
* Adds the files contained within the given Archive file to the
* file index.
*/
void indexArchive(const std::string& archive);
/**
* Returns true if the file identified by filename is found within
* the file index. If the file is found, the filedata parameter
* is populated.
*/
bool findFile(const std::string& filename, IndexData& filedata);
/**
* Returns a FileHandle for the file if it can be found in the
* file index, otherwise an empty FileHandle is returned.
*/
FileHandle openFile(const std::string& filename);
private:
std::map<std::string, IndexData> files;
};

View File

@ -58,17 +58,4 @@ struct RGBA
};
}
/**
* @brief Returned by GameData::loadFile()
*/
struct FileContentsInfo {
char* data;
size_t length;
~FileContentsInfo() {
delete[] data;
}
};
typedef std::shared_ptr<FileContentsInfo> FileHandle;
#endif

View File

@ -1,7 +1,7 @@
#pragma once
#ifndef _LOADERCUTSCENEDAT_HPP_
#define _LOADERCUTSCENEDAT_HPP_
#include <engine/RWTypes.hpp>
#include <core/FileHandle.hpp>
#include <data/CutsceneData.hpp>
class LoaderCutsceneDAT

View File

@ -10,7 +10,7 @@
#include <vector>
#include <string>
#include <WorkContext.hpp>
#include <engine/RWTypes.hpp>
#include <core/FileHandle.hpp>
class Model;
class GameData;

View File

@ -1,7 +1,7 @@
#pragma once
#ifndef _LOADERGXT_HPP_
#define _LOADERGXT_HPP_
#include <engine/RWTypes.hpp>
#include <core/FileHandle.hpp>
#include <data/GameTexts.hpp>
class LoaderGXT

View File

@ -0,0 +1,130 @@
#include <core/FileIndex.hpp>
#include <loaders/LoaderIMG.hpp>
#include <algorithm>
#include <fstream>
#include <dirent.h>
#include <sys/types.h>
void FileIndex::indexDirectory(const std::string& directory)
{
DIR* dp = opendir(directory.c_str());
dirent* ep;
std::string realName, lowerName;
if ( dp == NULL ) {
throw std::runtime_error("Unable to open directory: " + directory);
}
while( (ep = readdir(dp)) )
{
realName = ep->d_name;
lowerName = realName;
std::transform(lowerName.begin(), lowerName.end(), lowerName.begin(), ::tolower);
if ( ep->d_type == DT_REG )
{
files[ lowerName ] = {
lowerName,
realName,
directory,
""
};
}
}
}
void FileIndex::indexArchive(const std::string& archive)
{
// Split directory from archive name
auto slash = archive.find_last_of('/');
auto directory = archive.substr(0, slash);
auto archivebasename = archive.substr(slash+1);
auto archivepath = directory + "/" + archivebasename;
LoaderIMG img;
if( ! img.load( archivepath ) )
{
throw std::runtime_error("Failed to load IMG archive: " + archivepath);
}
std::string lowerName;
for( size_t i = 0; i < img.getAssetCount(); ++i )
{
auto& asset = img.getAssetInfoByIndex(i);
if( asset.size == 0 ) continue;
lowerName = asset.name;
std::transform(lowerName.begin(), lowerName.end(), lowerName.begin(), ::tolower);
files[ lowerName ] = {
lowerName,
asset.name,
directory,
archivebasename
};
}
}
bool FileIndex::findFile(const std::string& filename, FileIndex::IndexData& filedata)
{
auto iterator = files.find( filename );
if( iterator == files.end() )
{
return false;
}
filedata = iterator->second;
return true;
}
FileHandle FileIndex::openFile(const std::string& filename)
{
auto iterator = files.find( filename );
if( iterator == files.end() )
{
return nullptr;
}
IndexData& f = iterator->second;
bool isArchive = !f.archive.empty();
auto fsName = f.directory + "/" + f.originalName;
char* data = nullptr;
size_t length = 0;
if( isArchive )
{
fsName = f.directory + "/" + f.archive;
LoaderIMG img;
if( ! img.load(fsName) )
{
throw std::runtime_error("Failed to load IMG archive: " + fsName);
}
LoaderIMGFile file;
if( img.findAssetInfo(f.originalName, file) )
{
length = file.size * 2048;
data = img.loadToMemory(f.originalName);
}
}
else
{
std::ifstream dfile(fsName.c_str());
if ( ! dfile.is_open()) {
throw std::runtime_error("Unable to open file: " + fsName);
}
dfile.seekg(0, std::ios_base::end);
size_t length = dfile.tellg();
dfile.seekg(0);
char *data = new char[length];
dfile.read(data, length);
}
return FileHandle( new FileContentsInfo{ data, length } );
}

View File

@ -12,6 +12,12 @@ LoaderIMG::LoaderIMG()
bool LoaderIMG::load(const std::string& filename)
{
std::string dirName = filename;
auto extpos = dirName.find(".img");
if( extpos != std::string::npos )
{
dirName.erase(extpos);
}
dirName.append(".dir");
FILE* fp = fopen(dirName.c_str(), "rb");

View File

@ -0,0 +1,53 @@
#include <boost/test/unit_test.hpp>
#include <core/FileIndex.hpp>
#include <test_globals.hpp>
BOOST_AUTO_TEST_SUITE(FileIndexTests)
BOOST_AUTO_TEST_CASE(test_index)
{
FileIndex index;
index.indexDirectory(Global::getGamePath()+"/data");
FileIndex::IndexData data;
BOOST_CHECK( index.findFile("cullzone.dat", data) );
BOOST_CHECK_EQUAL( data.filename, "cullzone.dat" );
BOOST_CHECK_EQUAL( data.originalName, "CULLZONE.DAT" );
BOOST_CHECK( data.archive.empty() );
}
BOOST_AUTO_TEST_CASE(test_file)
{
FileIndex index;
index.indexDirectory(Global::getGamePath()+"/data");
auto handle = index.openFile("cullzone.dat");
BOOST_CHECK( handle != nullptr );
}
BOOST_AUTO_TEST_CASE(test_index_archive)
{
FileIndex index;
index.indexArchive(Global::getGamePath()+"/models/gta3.img");
FileIndex::IndexData data;
BOOST_CHECK( index.findFile("landstal.dff", data) );
BOOST_CHECK_EQUAL( data.filename, "landstal.dff" );
BOOST_CHECK_EQUAL( data.originalName, "landstal.dff" );
BOOST_CHECK_EQUAL( data.archive, "gta3.img" );
}
BOOST_AUTO_TEST_CASE(test_file_archive)
{
FileIndex index;
index.indexArchive(Global::getGamePath()+"/models/gta3.img");
auto handle = index.openFile("landstal.dff");
BOOST_CHECK( handle != nullptr );
}
BOOST_AUTO_TEST_SUITE_END()

View File

@ -1,9 +1,12 @@
FILE(GLOB TEST_SOURCES "*.cpp")
FILE(GLOB TEST_SOURCES
"*.cpp"
"${CMAKE_SOURCE_DIR}/rwengine/tests/*.cpp")
ADD_DEFINITIONS(-DBOOST_TEST_DYN_LINK)
add_executable(run_tests ${TEST_SOURCES})
include_directories(include)
include_directories("${CMAKE_SOURCE_DIR}/tests")
find_package(Boost COMPONENTS unit_test_framework REQUIRED)