326 lines
7.7 KiB
C
326 lines
7.7 KiB
C
/**
|
|
* @file ldata.c
|
|
*
|
|
* @brief Wrapper to handle reading/writing the ldata file.
|
|
*
|
|
* Optimizes to minimize the opens and frees, plus tries to read from the
|
|
* filesystem instead of always looking for a packfile.
|
|
*/
|
|
|
|
#include "SDL.h"
|
|
|
|
#include "lephisto.h"
|
|
#include "log.h"
|
|
#include "md5.h"
|
|
#include "lxml.h"
|
|
#include "pack.h"
|
|
#include "lfile.h"
|
|
#include "ldata.h"
|
|
|
|
#define LDATA_FILENAME "ldata" /**< Generic ldata file name. */
|
|
#ifndef LDATA_DEF
|
|
#define LDATA_DEF LDATA_FILENAME /**< Default ldata to use. */
|
|
#endif
|
|
|
|
#define XML_START_ID "Start" /**< XML document tag of module start file. */
|
|
#define START_DATA "../dat/start.xml" /**< Path to module start file. */
|
|
|
|
/* Packfile. */
|
|
static char* ldata_filename = NULL; /**< Packfile name. */
|
|
static Packcache_t* ldata_cache = NULL; /**< Actual packfile. */
|
|
static char* ldata_packName = NULL; /**< Name of the ldata module. */
|
|
|
|
/* File list. */
|
|
static const char** ldata_fileList = NULL; /**< List of the files in the packfile. */
|
|
static uint32_t ldata_fileNList = 0; /**< Number of files in ldata_fileList. */
|
|
|
|
static char** filterList(const char** list, int nlist,
|
|
const char* path, uint32_t* nfiles);
|
|
|
|
/**
|
|
* @brief Check to see if path is an ldata file.
|
|
*
|
|
* Should be called before ldata_open.
|
|
* @param path Path to check to see if it's an ldata file.
|
|
* @return 1 if it is an ldata file, otherwise 0.
|
|
*/
|
|
int ldata_check(char* path) {
|
|
return pack_check(path);
|
|
}
|
|
|
|
/**
|
|
* @brief Set the current ldata path to use.
|
|
*
|
|
* Should be called before ldata_open
|
|
* @param path Path to set.
|
|
* @return 0 on success.
|
|
*/
|
|
int ldata_setPath(char* path) {
|
|
if(ldata_filename != NULL)
|
|
free(ldata_filename);
|
|
ldata_filename = (path == NULL) ? NULL : strdup(path);
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief Open a packfile if needed.
|
|
* @return 0 on success.
|
|
*/
|
|
static int ldata_openPackfile(void) {
|
|
int i;
|
|
char** files;
|
|
int nfiles;
|
|
size_t len;
|
|
|
|
/* Try to find the ldata file. */
|
|
if(ldata_filename == NULL) {
|
|
/* Check ldata with version appended. */
|
|
if(lfile_fileExists("%s-%d.%d.%d", LDATA_FILENAME, VMAJOR, VMINOR, VREV)) {
|
|
ldata_filename = malloc(PATH_MAX);
|
|
snprintf(ldata_filename, PATH_MAX, "%s-%d.%d.%d",
|
|
LDATA_FILENAME, VMAJOR, VMINOR, VREV);
|
|
}
|
|
/* Check default ldata. */
|
|
else if(lfile_fileExists(LDATA_DEF))
|
|
ldata_filename = strdup(LDATA_DEF);
|
|
/* Try to open any ldata in path. */
|
|
else {
|
|
files = lfile_readDir(&nfiles, ".");
|
|
len = strlen(LDATA_FILENAME);
|
|
for(i = 0; i < nfiles; i++) {
|
|
if(strncmp(files[i], LDATA_FILENAME, len)==0) {
|
|
/* Must be a packfile. */
|
|
if(pack_check(files[i]))
|
|
continue;
|
|
|
|
ldata_filename = strdup(files[i]);
|
|
break;
|
|
}
|
|
}
|
|
/* Clean up. */
|
|
for(i = 0; i < nfiles; i++)
|
|
free(files[i]);
|
|
free(files);
|
|
}
|
|
}
|
|
|
|
/* Open the cache. */
|
|
if(lfile_fileExists(ldata_filename) != 1) {
|
|
WARN("Cannot find ldata file!");
|
|
WARN("Please specify ldata file with -d or data in the conf file.");
|
|
exit(1);
|
|
return -1;
|
|
}
|
|
ldata_cache = pack_openCache(ldata_filename);
|
|
if(ldata_cache == NULL)
|
|
WARN("Unalbe to create Packcache from '%s'.", ldata_filename);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief Open the ldata file.
|
|
* @return 0 on success.
|
|
*/
|
|
int ldata_open(void) {
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief Close and clean up the ldata file.
|
|
*/
|
|
void ldata_close(void) {
|
|
/* Destroy the name. */
|
|
if(ldata_packName != NULL) {
|
|
free(ldata_packName);
|
|
ldata_packName = NULL;
|
|
}
|
|
|
|
/* Destroy the list. */
|
|
if(ldata_fileList != NULL) {
|
|
/* No need to free memory since cache does that. */
|
|
ldata_fileList = NULL;
|
|
ldata_fileNList = 0;
|
|
}
|
|
|
|
/* Close the packfile. */
|
|
if(ldata_cache) {
|
|
pack_closeCache(ldata_cache);
|
|
ldata_cache = NULL;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Get the ldata's name.
|
|
*
|
|
* Thread safe (uses ldata_read).
|
|
* @return The ldata's name.
|
|
*/
|
|
const char* ldata_name(void) {
|
|
char* buf;
|
|
uint32_t size;
|
|
xmlNodePtr node;
|
|
xmlDocPtr doc;
|
|
|
|
/* Alreay loaded. */
|
|
if(ldata_packName != NULL)
|
|
return ldata_packName;
|
|
|
|
/* We'll just read it and parse it. */
|
|
buf = ldata_read(START_DATA, &size);
|
|
doc = xmlParseMemory(buf, size);
|
|
|
|
/* Make sure it's what we are looking for. */
|
|
node = doc->xmlChildrenNode;
|
|
if(!xml_isNode(node, XML_START_ID)) {
|
|
ERR("Malformed '"START_DATA"' file: missing root element '"XML_START_ID"'");
|
|
return NULL;
|
|
}
|
|
|
|
/* Check if node is valid. */
|
|
node = node->xmlChildrenNode; /* First system node. */
|
|
if(node == NULL) {
|
|
ERR("Malformed '"START_DATA"' file: does not contain elements");
|
|
return NULL;
|
|
}
|
|
do {
|
|
xmlr_strd(node, "name", ldata_packName);
|
|
} while(xml_nextNode(node));
|
|
|
|
xmlFreeDoc(doc);
|
|
free(buf);
|
|
|
|
/* Check if data name is found. */
|
|
if(ldata_packName == NULL)
|
|
WARN("No ldata packname found.");
|
|
|
|
return ldata_packName;
|
|
}
|
|
|
|
/**
|
|
* @brief Read a file from the ldata.
|
|
* @param filename Name of the file to read.
|
|
* @param[out] filesize Stores the size of the file.
|
|
* @return The file data or NULL on error.
|
|
*/
|
|
void* ldata_read(const char* filename, uint32_t* filesize) {
|
|
char* buf;
|
|
int nbuf;
|
|
/* See if needs to load the packfile. */
|
|
if(ldata_cache == NULL) {
|
|
/* Try to read the file as locally. */
|
|
buf = lfile_readFile(&nbuf, filename);
|
|
if(buf != NULL) {
|
|
*filesize = nbuf;
|
|
return buf;
|
|
}
|
|
|
|
/* Load the packfile. */
|
|
ldata_openPackfile();
|
|
}
|
|
|
|
/* Get data from packfile. */
|
|
return pack_readfileCached(ldata_cache, filename, filesize);
|
|
}
|
|
|
|
/**
|
|
* @brief Create an rwops from a file in the ldata.
|
|
* @param filename Name of the file create rwops of.
|
|
* @retuen rwops that accesses the file in the ldata.
|
|
*/
|
|
SDL_RWops* ldata_rwops(const char* filename) {
|
|
SDL_RWops* rw;
|
|
|
|
if(ldata_cache == NULL) {
|
|
/* Try to open from file. */
|
|
rw = SDL_RWFromFile(filename, "rb");
|
|
if(rw != NULL)
|
|
return rw;
|
|
|
|
/* Load the packfile. */
|
|
ldata_openPackfile();
|
|
}
|
|
|
|
return pack_rwopsCached(ldata_cache, filename);
|
|
}
|
|
|
|
/**
|
|
* @brief Filter a file list to match path.
|
|
*
|
|
* Uses read only data, should be thread safe.
|
|
*
|
|
* @param list List to filter.
|
|
* @param nlist Members in list.
|
|
* @param path Path to filter.
|
|
* @param[out] nfiles Files that match.
|
|
*/
|
|
static char** filterList(const char** list, int nlist,
|
|
const char* path, uint32_t* nfiles) {
|
|
|
|
char** filtered;
|
|
int i, j, k;
|
|
int len;
|
|
|
|
/* Maximum size by default. */
|
|
filtered = malloc(sizeof(char*) * nlist);
|
|
len = strlen(path);
|
|
|
|
/* Filter list. */
|
|
j = 0;
|
|
for(i = 0; i < nlist; i++) {
|
|
/* Must match path. */
|
|
if(strncmp(list[i], path, len) != 0)
|
|
continue;
|
|
|
|
/* Make sure there are no stray '/'. */
|
|
for(k = len; list[i][k] != '\0'; k++)
|
|
if(list[i][k] != '/')
|
|
break;
|
|
if(list[i][k] != '\0')
|
|
continue;
|
|
|
|
/* Copy the file name without the path. */
|
|
filtered[j++] = strdup(&list[i][len]);
|
|
}
|
|
|
|
/* Return results. */
|
|
*nfiles = j;
|
|
return filtered;
|
|
}
|
|
|
|
/**
|
|
* @brief Get the list of files in the ldata.
|
|
* @param path List files in path.
|
|
* @param nfiles Number of files found.
|
|
* @return List of files found.
|
|
*/
|
|
char** ldata_list(const char* path, uint32_t* nfiles) {
|
|
(void)path;
|
|
char** files;
|
|
int n;
|
|
|
|
/* Already loaded the list. */
|
|
if(ldata_fileList != NULL)
|
|
return filterList(ldata_fileList, ldata_fileNList, path, nfiles);
|
|
|
|
/* See if we can load from local directory. */
|
|
if(ldata_cache == NULL) {
|
|
files = lfile_readDir(&n, path);
|
|
|
|
/* Found locally. */
|
|
if(files != NULL) {
|
|
*nfiles = n;
|
|
return files;
|
|
}
|
|
|
|
/* Open packfile. */
|
|
ldata_openPackfile();
|
|
}
|
|
|
|
/* Load list. */
|
|
ldata_fileList = pack_listfilesCached(ldata_cache, &ldata_fileNList);
|
|
|
|
return filterList(ldata_fileList, ldata_fileNList, path, nfiles);
|
|
}
|
|
|