mirror of
https://github.com/rwengine/openrw.git
synced 2024-11-07 11:22: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:
parent
cdff0aa275
commit
25f28dbb42
0
rwengine/include/core/FileArchive.hpp
Normal file
0
rwengine/include/core/FileArchive.hpp
Normal file
18
rwengine/include/core/FileHandle.hpp
Normal file
18
rwengine/include/core/FileHandle.hpp
Normal 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;
|
50
rwengine/include/core/FileIndex.hpp
Normal file
50
rwengine/include/core/FileIndex.hpp
Normal 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;
|
||||
};
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -10,7 +10,7 @@
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <WorkContext.hpp>
|
||||
#include <engine/RWTypes.hpp>
|
||||
#include <core/FileHandle.hpp>
|
||||
|
||||
class Model;
|
||||
class GameData;
|
||||
|
@ -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
|
||||
|
130
rwengine/src/core/FileIndex.cpp
Normal file
130
rwengine/src/core/FileIndex.cpp
Normal 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 } );
|
||||
}
|
@ -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");
|
||||
|
53
rwengine/tests/test_FileIndex.cpp
Normal file
53
rwengine/tests/test_FileIndex.cpp
Normal 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()
|
@ -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)
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user