2024-04-20 15:57:49 +00:00
|
|
|
|
/*
|
2024-05-16 20:04:13 +00:00
|
|
|
|
* Descent 3
|
|
|
|
|
* Copyright (C) 2024 Parallax Software
|
|
|
|
|
*
|
|
|
|
|
* This program is free software: you can redistribute it and/or modify
|
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
|
|
|
* (at your option) any later version.
|
|
|
|
|
*
|
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
|
*
|
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
*/
|
2024-04-20 15:57:49 +00:00
|
|
|
|
|
2024-07-02 13:04:11 +00:00
|
|
|
|
#include <algorithm>
|
2024-05-16 15:21:30 +00:00
|
|
|
|
#include <cstdio>
|
|
|
|
|
#include <cstdlib>
|
|
|
|
|
#include <cstring>
|
|
|
|
|
#include <cstdarg>
|
|
|
|
|
#include <cerrno>
|
2024-06-17 12:51:46 +00:00
|
|
|
|
#include <filesystem>
|
2024-06-20 13:51:00 +00:00
|
|
|
|
#include <map>
|
2024-05-16 19:31:43 +00:00
|
|
|
|
#include <memory>
|
2024-05-16 20:04:13 +00:00
|
|
|
|
#include <vector>
|
2024-06-17 12:51:46 +00:00
|
|
|
|
|
2024-04-18 00:18:11 +00:00
|
|
|
|
#include "byteswap.h"
|
2024-08-11 16:29:35 +00:00
|
|
|
|
#include "crossplat.h"
|
2024-04-21 20:54:02 +00:00
|
|
|
|
#include "cfile.h"
|
2024-08-19 22:21:05 +00:00
|
|
|
|
#include "ddio.h"
|
2024-04-21 20:54:02 +00:00
|
|
|
|
#include "hogfile.h" //info about library file
|
2024-08-19 22:21:05 +00:00
|
|
|
|
#include "log.h"
|
2024-04-16 03:43:29 +00:00
|
|
|
|
#include "mem.h"
|
2024-08-19 22:21:05 +00:00
|
|
|
|
#include "pserror.h"
|
2024-04-19 14:43:47 +00:00
|
|
|
|
|
2024-04-16 18:56:40 +00:00
|
|
|
|
// Library structures
|
2024-05-30 07:35:17 +00:00
|
|
|
|
struct library_entry {
|
2024-04-16 18:56:40 +00:00
|
|
|
|
char name[PSFILENAME_LEN + 1]; // just the filename part
|
2024-05-16 15:21:30 +00:00
|
|
|
|
uint32_t offset; // offset into library file
|
|
|
|
|
uint32_t length; // length of this file
|
|
|
|
|
uint32_t timestamp; // time and date of file
|
|
|
|
|
uint32_t flags; // misc flags
|
2024-05-30 07:35:17 +00:00
|
|
|
|
};
|
2024-04-19 14:43:47 +00:00
|
|
|
|
|
2024-05-30 07:35:17 +00:00
|
|
|
|
struct library {
|
2024-06-17 12:51:46 +00:00
|
|
|
|
std::filesystem::path name; // includes path + filename
|
2024-05-16 19:31:43 +00:00
|
|
|
|
uint32_t nfiles = 0;
|
2024-05-16 20:04:13 +00:00
|
|
|
|
std::vector<std::unique_ptr<library_entry>> entries;
|
2024-05-16 19:31:43 +00:00
|
|
|
|
std::shared_ptr<library> next;
|
2024-05-16 20:04:13 +00:00
|
|
|
|
int handle = 0; // identifier for this lib
|
2024-05-16 19:31:43 +00:00
|
|
|
|
FILE *file = nullptr; // pointer to file for this lib, if no one using it
|
2024-05-30 07:35:17 +00:00
|
|
|
|
};
|
2024-04-19 14:43:47 +00:00
|
|
|
|
|
2024-05-24 19:26:43 +00:00
|
|
|
|
/* The "root" directories of the D3 file tree
|
|
|
|
|
*
|
|
|
|
|
* Directories that come later in the list override directories that come
|
|
|
|
|
* earlier in the list. For example, if Base_directories[0] / "d3.hog" exists
|
|
|
|
|
* and Base_directories[1] / "d3.hog" also exists, then the one in
|
|
|
|
|
* Base_directories[1] will get used. The one in Base_directories[0] will be
|
|
|
|
|
* ignored.
|
|
|
|
|
*/
|
|
|
|
|
std::vector<std::filesystem::path> Base_directories = {};
|
2024-07-27 12:56:49 +00:00
|
|
|
|
|
2024-06-20 13:51:00 +00:00
|
|
|
|
// Map of paths. If value of entry is true, path is only for specific extensions
|
|
|
|
|
std::map<std::filesystem::path, bool> paths;
|
2024-04-19 14:43:47 +00:00
|
|
|
|
|
2024-06-20 13:51:00 +00:00
|
|
|
|
// Map of extensions <=> relevant paths
|
|
|
|
|
std::map<std::filesystem::path, std::filesystem::path> extensions;
|
2024-04-19 14:43:47 +00:00
|
|
|
|
|
2024-05-16 19:31:43 +00:00
|
|
|
|
std::shared_ptr<library> Libraries;
|
2024-04-16 18:56:40 +00:00
|
|
|
|
int lib_handle = 0;
|
2024-04-19 14:43:47 +00:00
|
|
|
|
|
2024-04-16 18:56:40 +00:00
|
|
|
|
// Structure thrown on disk error
|
2024-04-16 03:43:29 +00:00
|
|
|
|
cfile_error cfe;
|
2024-04-16 18:56:40 +00:00
|
|
|
|
// The message for unexpected end of file
|
2024-04-28 04:39:29 +00:00
|
|
|
|
const char *eof_error = "Unexpected end of file";
|
2024-04-19 14:43:47 +00:00
|
|
|
|
|
2024-07-27 12:56:49 +00:00
|
|
|
|
/* This function should be called at least once before you use anything else
|
|
|
|
|
* from this module.
|
|
|
|
|
*/
|
2024-05-24 19:26:43 +00:00
|
|
|
|
void cf_AddBaseDirectory(const std::filesystem::path &base_directory) {
|
|
|
|
|
if (std::filesystem::exists(base_directory)) {
|
|
|
|
|
Base_directories.push_back(base_directory);
|
|
|
|
|
} else {
|
|
|
|
|
LOG_WARNING << "Ignoring nonexistent base directory: " << base_directory;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* After you call this function, you must call cf_AddBaseDirectory() at least
|
|
|
|
|
* once before you use anything else from this module.
|
|
|
|
|
*/
|
|
|
|
|
void cf_ClearBaseDirectories() {
|
|
|
|
|
Base_directories.clear();
|
2024-07-27 12:56:49 +00:00
|
|
|
|
}
|
|
|
|
|
|
2024-05-24 19:26:43 +00:00
|
|
|
|
|
|
|
|
|
std::filesystem::path cf_LocatePathCaseInsensitiveHelper(const std::filesystem::path &relative_path,
|
|
|
|
|
const std::filesystem::path &starting_dir) {
|
2024-05-22 11:23:17 +00:00
|
|
|
|
#ifdef WIN32
|
2024-05-24 19:26:43 +00:00
|
|
|
|
std::filesystem::path result = starting_dir / relative_path;
|
2024-05-22 11:23:17 +00:00
|
|
|
|
if (std::filesystem::exists(result)) {
|
|
|
|
|
return result;
|
|
|
|
|
} else {
|
|
|
|
|
return {};
|
|
|
|
|
}
|
|
|
|
|
#else
|
|
|
|
|
// Dumb check, maybe there already all ok?
|
|
|
|
|
if (exists((starting_dir / relative_path))) {
|
|
|
|
|
return starting_dir / relative_path;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::filesystem::path result, search_path, search_file;
|
|
|
|
|
|
|
|
|
|
search_path = starting_dir / relative_path.parent_path();
|
|
|
|
|
search_file = relative_path.filename();
|
|
|
|
|
|
|
|
|
|
// If directory does not exist, nothing to search.
|
|
|
|
|
if (!std::filesystem::is_directory(search_path) || search_file.empty()) {
|
|
|
|
|
return {};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Search component in search_path
|
|
|
|
|
auto const &it = std::filesystem::directory_iterator(search_path);
|
|
|
|
|
|
|
|
|
|
auto found = std::find_if(it, end(it), [&search_file, &search_path, &result](const auto& dir_entry) {
|
|
|
|
|
return stricmp(dir_entry.path().filename().u8string().c_str(), search_file.u8string().c_str()) == 0;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (found != end(it)) {
|
|
|
|
|
// Match, append to result
|
|
|
|
|
result = found->path();
|
|
|
|
|
search_path = result;
|
|
|
|
|
} else {
|
|
|
|
|
// Component not found, mission failed
|
|
|
|
|
return {};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
2024-05-24 19:26:43 +00:00
|
|
|
|
std::vector<std::filesystem::path> cf_LocatePathMultiplePathsHelper(const std::filesystem::path &relative_path,
|
|
|
|
|
bool stop_after_first_result) {
|
|
|
|
|
ASSERT(("realative_path should be a relative path.", relative_path.is_relative()));
|
|
|
|
|
std::vector<std::filesystem::path> return_value = { };
|
|
|
|
|
for (auto base_directories_iterator = Base_directories.rbegin();
|
|
|
|
|
base_directories_iterator != Base_directories.rend();
|
|
|
|
|
++base_directories_iterator) {
|
|
|
|
|
ASSERT(("base_directory should be an absolute path.", base_directories_iterator->is_absolute()));
|
|
|
|
|
auto to_append = cf_LocatePathCaseInsensitiveHelper(relative_path, *base_directories_iterator);
|
|
|
|
|
ASSERT(("to_append should be either empty or an absolute path.", to_append.empty() || to_append.is_absolute()));
|
|
|
|
|
if (std::filesystem::exists(to_append)) {
|
|
|
|
|
return_value.insert(return_value.begin(), to_append);
|
|
|
|
|
if (stop_after_first_result) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return return_value;
|
|
|
|
|
}
|
|
|
|
|
|
2024-05-22 11:23:17 +00:00
|
|
|
|
/**
|
2024-05-24 19:26:43 +00:00
|
|
|
|
* Tries to find a relative path inside of one of the Base_directories.
|
2024-05-22 11:23:17 +00:00
|
|
|
|
*
|
|
|
|
|
* @param relative_path A relative path that we’ll hopefully find in
|
2024-05-24 19:26:43 +00:00
|
|
|
|
* one of the Base_directories. You don’t have to get the
|
|
|
|
|
* capitalization of relative_path correct, even on macOS
|
|
|
|
|
* and Linux.
|
2024-05-22 11:23:17 +00:00
|
|
|
|
*
|
2024-05-24 19:26:43 +00:00
|
|
|
|
* @return Either an absolute path that’s inside a base directory or an empty
|
|
|
|
|
* path if nothing is found.
|
2024-05-22 11:23:17 +00:00
|
|
|
|
*/
|
|
|
|
|
std::filesystem::path cf_LocatePath(const std::filesystem::path &relative_path) {
|
2024-05-24 19:26:43 +00:00
|
|
|
|
auto return_value_list = cf_LocatePathMultiplePathsHelper(relative_path, true);
|
|
|
|
|
if (return_value_list.empty()) {
|
|
|
|
|
return "";
|
|
|
|
|
} else {
|
|
|
|
|
return return_value_list.front();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Tries to find multiple relative paths inside of the Base_directories.
|
|
|
|
|
*
|
|
|
|
|
* @param relative_path A relative path that we’ll hopefully find in
|
|
|
|
|
* one or more of the Base_directories. You don’t have to
|
|
|
|
|
* get the capitalization of relative_path correct, even on
|
|
|
|
|
* macOS and Linux.
|
|
|
|
|
*
|
|
|
|
|
* @return A list of absolute paths. Each path will be inside one of the
|
|
|
|
|
* Base_directories.
|
|
|
|
|
*/
|
|
|
|
|
std::vector<std::filesystem::path> cf_LocateMultiplePaths(const std::filesystem::path &relative_path) {
|
|
|
|
|
return cf_LocatePathMultiplePathsHelper(relative_path, false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Not all Base_directories are necessarily writable, but this function will
|
|
|
|
|
* return one that should be writable.
|
|
|
|
|
*/
|
|
|
|
|
std::filesystem::path cf_GetWritableBaseDirectory() {
|
|
|
|
|
return Base_directories.front();
|
2024-05-22 11:23:17 +00:00
|
|
|
|
}
|
|
|
|
|
|
2024-04-16 18:56:40 +00:00
|
|
|
|
// Generates a cfile error
|
2024-04-28 04:39:29 +00:00
|
|
|
|
void ThrowCFileError(int type, CFILE *file, const char *msg) {
|
2024-04-16 18:56:40 +00:00
|
|
|
|
cfe.read_write = type;
|
|
|
|
|
cfe.msg = msg;
|
|
|
|
|
cfe.file = file;
|
|
|
|
|
throw &cfe;
|
2024-04-16 03:43:29 +00:00
|
|
|
|
}
|
2024-04-19 14:43:47 +00:00
|
|
|
|
|
2024-05-07 21:35:28 +00:00
|
|
|
|
static void cf_Close();
|
|
|
|
|
|
|
|
|
|
// searches through the open HOG files, and opens a file if it finds it in any of the libs
|
|
|
|
|
static CFILE *open_file_in_lib(const char *filename);
|
2024-04-19 14:43:47 +00:00
|
|
|
|
|
2024-04-16 18:56:40 +00:00
|
|
|
|
// Opens a HOG file. Future calls to cfopen(), etc. will look in this HOG.
|
2024-05-24 19:26:43 +00:00
|
|
|
|
// Parameters: libname - path to the HOG file, relative to one of the Base_directories.
|
|
|
|
|
// NOTE: libname must be valid for the entire execution of the program. Therefore, Base_directories
|
2024-05-22 11:23:17 +00:00
|
|
|
|
// must not change.
|
2024-04-16 18:56:40 +00:00
|
|
|
|
// Returns: 0 if error, else library handle that can be used to close the library
|
2024-06-17 12:51:46 +00:00
|
|
|
|
int cf_OpenLibrary(const std::filesystem::path &libname) {
|
2024-04-16 18:56:40 +00:00
|
|
|
|
FILE *fp;
|
2024-05-16 15:21:30 +00:00
|
|
|
|
int i;
|
|
|
|
|
uint32_t offset;
|
2024-04-16 18:56:40 +00:00
|
|
|
|
static int first_time = 1;
|
2024-06-17 12:51:46 +00:00
|
|
|
|
tHogHeader header{};
|
|
|
|
|
tHogFileEntry entry{};
|
2024-04-16 18:56:40 +00:00
|
|
|
|
|
2024-05-16 19:31:43 +00:00
|
|
|
|
// allocation library structure
|
|
|
|
|
std::shared_ptr<library> lib = std::make_shared<library>();
|
2024-05-22 11:23:17 +00:00
|
|
|
|
lib->name = cf_LocatePath(libname);
|
2024-06-17 12:51:46 +00:00
|
|
|
|
fp = fopen(lib->name.u8string().c_str(), "rb");
|
2024-05-06 16:52:45 +00:00
|
|
|
|
if (fp == nullptr) {
|
2024-04-16 18:56:40 +00:00
|
|
|
|
return 0; // CF_NO_FILE;
|
2024-05-06 16:52:45 +00:00
|
|
|
|
}
|
2024-04-16 18:56:40 +00:00
|
|
|
|
// check if this if first library opened
|
|
|
|
|
if (first_time) {
|
|
|
|
|
atexit(cf_Close);
|
|
|
|
|
first_time = 0;
|
|
|
|
|
}
|
|
|
|
|
// read HOG header
|
|
|
|
|
if (!ReadHogHeader(fp, &header)) {
|
|
|
|
|
fclose(fp);
|
|
|
|
|
return 0; // CF_BAD_LIB;
|
|
|
|
|
}
|
|
|
|
|
lib->nfiles = header.nfiles;
|
|
|
|
|
// allocate CFILE hog info.
|
2024-05-16 20:04:13 +00:00
|
|
|
|
lib->entries.reserve(lib->nfiles);
|
2024-04-16 18:56:40 +00:00
|
|
|
|
lib->next = Libraries;
|
|
|
|
|
Libraries = lib;
|
|
|
|
|
// set data offset of first file
|
|
|
|
|
offset = header.file_data_offset;
|
|
|
|
|
// Go to index start
|
2024-05-16 15:37:03 +00:00
|
|
|
|
fseek(fp, HOG_HDR_SIZE, SEEK_SET);
|
2024-04-16 18:56:40 +00:00
|
|
|
|
|
|
|
|
|
// read in index table
|
|
|
|
|
for (i = 0; i < lib->nfiles; i++) {
|
|
|
|
|
if (!ReadHogEntry(fp, &entry)) {
|
|
|
|
|
fclose(fp);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
// Make sure files are in order
|
2024-05-16 20:04:13 +00:00
|
|
|
|
ASSERT((i == 0) || (stricmp(entry.name, lib->entries[i - 1]->name) >= 0));
|
2024-04-16 18:56:40 +00:00
|
|
|
|
// Copy into table
|
2024-05-16 20:04:13 +00:00
|
|
|
|
std::unique_ptr<library_entry> lib_entry = std::make_unique<library_entry>();
|
|
|
|
|
strcpy(lib_entry->name, entry.name);
|
|
|
|
|
lib_entry->flags = entry.flags;
|
|
|
|
|
lib_entry->length = entry.len;
|
|
|
|
|
lib_entry->offset = offset;
|
|
|
|
|
lib_entry->timestamp = entry.timestamp;
|
|
|
|
|
lib->entries.push_back(std::move(lib_entry));
|
|
|
|
|
|
|
|
|
|
offset += entry.len;
|
2024-04-16 18:56:40 +00:00
|
|
|
|
}
|
|
|
|
|
// assign a handle
|
|
|
|
|
lib->handle = ++lib_handle;
|
|
|
|
|
// Save the file pointer
|
|
|
|
|
lib->file = fp;
|
2024-05-16 15:37:03 +00:00
|
|
|
|
// Success. Return the handle
|
2024-04-16 18:56:40 +00:00
|
|
|
|
return lib->handle;
|
2024-04-16 03:43:29 +00:00
|
|
|
|
}
|
2024-04-19 14:43:47 +00:00
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Closes a library file.
|
|
|
|
|
* @param handle the handle returned by cf_OpenLibrary()
|
|
|
|
|
*/
|
2024-04-16 18:56:40 +00:00
|
|
|
|
void cf_CloseLibrary(int handle) {
|
2024-05-16 19:31:43 +00:00
|
|
|
|
std::shared_ptr<library> lib, prev;
|
2024-04-16 18:56:40 +00:00
|
|
|
|
for (lib = Libraries; lib; prev = lib, lib = lib->next) {
|
|
|
|
|
if (lib->handle == handle) {
|
|
|
|
|
if (prev)
|
|
|
|
|
prev->next = lib->next;
|
|
|
|
|
else
|
|
|
|
|
Libraries = lib->next;
|
|
|
|
|
if (lib->file)
|
|
|
|
|
fclose(lib->file);
|
2024-04-19 14:43:47 +00:00
|
|
|
|
return; // successful close
|
2024-04-16 18:56:40 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2024-04-16 03:43:29 +00:00
|
|
|
|
}
|
2024-04-19 14:43:47 +00:00
|
|
|
|
|
2024-04-16 18:56:40 +00:00
|
|
|
|
// Closes down the CFILE system, freeing up all data, etc.
|
|
|
|
|
void cf_Close() {
|
2024-05-16 19:31:43 +00:00
|
|
|
|
std::shared_ptr<library> next;
|
2024-04-16 18:56:40 +00:00
|
|
|
|
while (Libraries) {
|
|
|
|
|
next = Libraries->next;
|
|
|
|
|
Libraries = next;
|
|
|
|
|
}
|
2024-04-16 03:43:29 +00:00
|
|
|
|
}
|
2024-06-20 13:51:00 +00:00
|
|
|
|
|
2024-06-21 09:55:25 +00:00
|
|
|
|
bool cf_SetSearchPath(const std::filesystem::path &path, const std::vector<std::filesystem::path> &ext_list) {
|
2024-06-20 13:51:00 +00:00
|
|
|
|
// Don't add non-existing path into search paths
|
|
|
|
|
if (!std::filesystem::is_directory(path))
|
|
|
|
|
return false;
|
2024-04-16 18:56:40 +00:00
|
|
|
|
// Get & store full path
|
2024-06-20 13:51:00 +00:00
|
|
|
|
paths.insert_or_assign(std::filesystem::absolute(path), !ext_list.empty());
|
|
|
|
|
// Set extensions for this path
|
|
|
|
|
if (!ext_list.empty()) {
|
|
|
|
|
for (auto const &ext : ext_list) {
|
|
|
|
|
if (!ext.empty()) {
|
|
|
|
|
extensions.insert_or_assign(ext, path);
|
2024-04-16 22:19:40 +00:00
|
|
|
|
}
|
2024-04-16 18:56:40 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2024-06-20 13:51:00 +00:00
|
|
|
|
return true;
|
2024-04-16 03:43:29 +00:00
|
|
|
|
}
|
|
|
|
|
|
2024-04-19 14:43:47 +00:00
|
|
|
|
/**
|
|
|
|
|
* Removes all search paths that have been added by cf_SetSearchPath
|
|
|
|
|
*/
|
|
|
|
|
void cf_ClearAllSearchPaths() {
|
2024-06-20 13:51:00 +00:00
|
|
|
|
paths.clear();
|
|
|
|
|
extensions.clear();
|
2024-04-16 03:43:29 +00:00
|
|
|
|
}
|
|
|
|
|
|
2024-04-19 14:43:47 +00:00
|
|
|
|
/**
|
|
|
|
|
* Opens a file for reading in a library, given the library id
|
|
|
|
|
* @param filename
|
|
|
|
|
* @param libhandle
|
|
|
|
|
* @return
|
|
|
|
|
*/
|
2024-06-21 09:55:25 +00:00
|
|
|
|
CFILE *cf_OpenFileInLibrary(const std::filesystem::path &filename, int libhandle) {
|
2024-04-16 18:56:40 +00:00
|
|
|
|
if (libhandle <= 0)
|
2024-04-19 14:43:47 +00:00
|
|
|
|
return nullptr;
|
2024-04-16 18:56:40 +00:00
|
|
|
|
|
2024-05-16 19:31:43 +00:00
|
|
|
|
std::shared_ptr<library> lib = Libraries;
|
2024-04-16 18:56:40 +00:00
|
|
|
|
|
|
|
|
|
// find the library that we want to use
|
|
|
|
|
while (lib) {
|
|
|
|
|
if (lib->handle == libhandle)
|
|
|
|
|
break;
|
|
|
|
|
lib = lib->next;
|
|
|
|
|
}
|
|
|
|
|
|
2024-04-19 14:43:47 +00:00
|
|
|
|
if (nullptr == lib) {
|
2024-04-16 18:56:40 +00:00
|
|
|
|
// couldn't find the library handle
|
2024-04-19 14:43:47 +00:00
|
|
|
|
return nullptr;
|
2024-04-16 18:56:40 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// now do a binary search for the file entry
|
2024-07-02 14:22:18 +00:00
|
|
|
|
int i, first = 0, last = lib->nfiles - 1, c;
|
|
|
|
|
bool found = false;
|
2024-04-16 18:56:40 +00:00
|
|
|
|
|
|
|
|
|
do {
|
|
|
|
|
i = (first + last) / 2;
|
2024-06-20 18:32:48 +00:00
|
|
|
|
c = stricmp(filename.u8string().c_str(), lib->entries[i]->name); // compare to current
|
2024-06-21 09:55:25 +00:00
|
|
|
|
if (c == 0) {
|
2024-07-02 14:22:18 +00:00
|
|
|
|
found = true;
|
2024-04-16 18:56:40 +00:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
if (first >= last) // exhausted search
|
|
|
|
|
break;
|
|
|
|
|
if (c > 0) // search key after check key
|
|
|
|
|
first = i + 1;
|
|
|
|
|
else // search key before check key
|
|
|
|
|
last = i - 1;
|
2024-04-19 14:43:47 +00:00
|
|
|
|
} while (true);
|
2024-04-16 18:56:40 +00:00
|
|
|
|
|
|
|
|
|
if (!found)
|
2024-04-19 14:43:47 +00:00
|
|
|
|
return nullptr; // file not in library
|
2024-04-16 18:56:40 +00:00
|
|
|
|
|
|
|
|
|
// open the file for reading
|
|
|
|
|
FILE *fp;
|
|
|
|
|
int r;
|
|
|
|
|
// See if there's an available FILE
|
|
|
|
|
if (lib->file) {
|
|
|
|
|
fp = lib->file;
|
2024-04-19 14:43:47 +00:00
|
|
|
|
lib->file = nullptr;
|
2024-04-16 18:56:40 +00:00
|
|
|
|
} else {
|
2024-06-17 12:51:46 +00:00
|
|
|
|
fp = fopen(lib->name.u8string().c_str(), "rb");
|
2024-04-16 18:56:40 +00:00
|
|
|
|
if (!fp) {
|
2024-09-21 23:01:56 +00:00
|
|
|
|
LOG_ERROR.printf("Error opening library <%s> when opening file <%s>; errno=%d.", lib->name.u8string().c_str(),
|
|
|
|
|
filename.u8string().c_str(), errno);
|
2024-04-16 18:56:40 +00:00
|
|
|
|
Int3();
|
2024-04-19 14:43:47 +00:00
|
|
|
|
return nullptr;
|
2024-04-16 18:56:40 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2024-09-08 11:29:41 +00:00
|
|
|
|
auto cfile = mem_rmalloc<CFILE>();
|
2024-04-16 18:56:40 +00:00
|
|
|
|
if (!cfile)
|
2024-05-07 21:35:28 +00:00
|
|
|
|
Error("Out of memory in cf_OpenFileInLibrary()");
|
2024-05-16 20:04:13 +00:00
|
|
|
|
cfile->name = lib->entries[i]->name;
|
2024-04-16 18:56:40 +00:00
|
|
|
|
cfile->file = fp;
|
|
|
|
|
cfile->lib_handle = lib->handle;
|
2024-05-16 20:04:13 +00:00
|
|
|
|
cfile->size = lib->entries[i]->length;
|
|
|
|
|
cfile->lib_offset = lib->entries[i]->offset;
|
2024-04-16 18:56:40 +00:00
|
|
|
|
cfile->position = 0;
|
|
|
|
|
cfile->flags = 0;
|
|
|
|
|
r = fseek(fp, cfile->lib_offset, SEEK_SET);
|
|
|
|
|
ASSERT(r == 0);
|
|
|
|
|
return cfile;
|
2024-04-16 03:43:29 +00:00
|
|
|
|
}
|
|
|
|
|
|
2024-04-16 18:56:40 +00:00
|
|
|
|
// searches through the open HOG files, and opens a file if it finds it in any of the libs
|
|
|
|
|
CFILE *open_file_in_lib(const char *filename) {
|
|
|
|
|
CFILE *cfile;
|
2024-05-16 19:31:43 +00:00
|
|
|
|
std::shared_ptr<library> lib = Libraries;
|
2024-04-16 18:56:40 +00:00
|
|
|
|
while (lib) {
|
|
|
|
|
int i;
|
|
|
|
|
// Do binary search for the file
|
|
|
|
|
int first = 0, last = lib->nfiles - 1, c, found = 0;
|
|
|
|
|
do {
|
|
|
|
|
i = (first + last) / 2;
|
2024-05-16 20:04:13 +00:00
|
|
|
|
c = stricmp(filename, lib->entries[i]->name); // compare to current
|
|
|
|
|
if (c == 0) { // found it
|
2024-04-16 18:56:40 +00:00
|
|
|
|
found = 1;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
if (first >= last) // exhausted search
|
|
|
|
|
break;
|
|
|
|
|
if (c > 0) // search key after check key
|
|
|
|
|
first = i + 1;
|
|
|
|
|
else // search key before check key
|
|
|
|
|
last = i - 1;
|
2024-04-19 14:43:47 +00:00
|
|
|
|
} while (true);
|
2024-04-16 18:56:40 +00:00
|
|
|
|
if (found) {
|
|
|
|
|
FILE *fp;
|
|
|
|
|
int r;
|
|
|
|
|
// See if there's an available FILE
|
|
|
|
|
if (lib->file) {
|
|
|
|
|
fp = lib->file;
|
2024-04-19 14:43:47 +00:00
|
|
|
|
lib->file = nullptr;
|
2024-04-16 18:56:40 +00:00
|
|
|
|
} else {
|
2024-06-17 12:51:46 +00:00
|
|
|
|
fp = fopen(lib->name.u8string().c_str(), "rb");
|
2024-04-16 18:56:40 +00:00
|
|
|
|
if (!fp) {
|
2024-09-21 23:01:56 +00:00
|
|
|
|
LOG_ERROR.printf("Error opening library <%s> when opening file <%s>; errno=%d.", lib->name.u8string().c_str(),
|
|
|
|
|
filename, errno);
|
2024-04-16 18:56:40 +00:00
|
|
|
|
Int3();
|
2024-04-19 14:43:47 +00:00
|
|
|
|
return nullptr;
|
2024-04-16 18:56:40 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2024-09-08 11:29:41 +00:00
|
|
|
|
cfile = mem_rmalloc<CFILE>();
|
2024-04-16 18:56:40 +00:00
|
|
|
|
if (!cfile)
|
|
|
|
|
Error("Out of memory in open_file_in_lib()");
|
2024-05-16 20:04:13 +00:00
|
|
|
|
cfile->name = lib->entries[i]->name;
|
2024-04-16 18:56:40 +00:00
|
|
|
|
cfile->file = fp;
|
|
|
|
|
cfile->lib_handle = lib->handle;
|
2024-05-16 20:04:13 +00:00
|
|
|
|
cfile->size = lib->entries[i]->length;
|
|
|
|
|
cfile->lib_offset = lib->entries[i]->offset;
|
2024-04-16 18:56:40 +00:00
|
|
|
|
cfile->position = 0;
|
|
|
|
|
cfile->flags = 0;
|
|
|
|
|
r = fseek(fp, cfile->lib_offset, SEEK_SET);
|
|
|
|
|
ASSERT(r == 0);
|
|
|
|
|
return cfile;
|
|
|
|
|
}
|
|
|
|
|
lib = lib->next;
|
|
|
|
|
}
|
2024-04-19 14:43:47 +00:00
|
|
|
|
return nullptr;
|
2024-04-16 03:43:29 +00:00
|
|
|
|
}
|
|
|
|
|
|
2024-05-07 21:35:28 +00:00
|
|
|
|
// look for the file in the specified directory
|
2024-06-17 12:51:46 +00:00
|
|
|
|
static CFILE *open_file_in_directory(const std::filesystem::path &filename, const char *mode,
|
|
|
|
|
const std::filesystem::path &directory);
|
2024-05-07 21:35:28 +00:00
|
|
|
|
|
2024-04-16 18:56:40 +00:00
|
|
|
|
// look for the file in the specified directory
|
2024-06-17 12:51:46 +00:00
|
|
|
|
CFILE *open_file_in_directory(const std::filesystem::path &filename, const char *mode,
|
|
|
|
|
const std::filesystem::path &directory) {
|
2024-04-16 18:56:40 +00:00
|
|
|
|
FILE *fp;
|
|
|
|
|
CFILE *cfile;
|
2024-07-02 14:22:18 +00:00
|
|
|
|
std::filesystem::path using_filename;
|
2024-04-16 18:56:40 +00:00
|
|
|
|
char tmode[3] = "rb";
|
2024-06-20 13:51:00 +00:00
|
|
|
|
if (std::filesystem::is_directory(directory)) {
|
2024-04-16 18:56:40 +00:00
|
|
|
|
// Make a full path
|
2024-07-02 14:22:18 +00:00
|
|
|
|
using_filename = directory / filename;
|
2024-05-22 11:23:17 +00:00
|
|
|
|
} else if (filename.is_absolute()) {
|
|
|
|
|
// no directory specified, and filename is an absolute path
|
2024-07-02 14:22:18 +00:00
|
|
|
|
using_filename = filename;
|
2024-05-22 11:23:17 +00:00
|
|
|
|
} else {
|
|
|
|
|
// no directory specified, and filename is a relative path
|
|
|
|
|
using_filename = cf_LocatePath(filename);
|
2024-07-02 14:22:18 +00:00
|
|
|
|
}
|
|
|
|
|
|
2024-04-16 18:56:40 +00:00
|
|
|
|
// set read or write mode
|
|
|
|
|
tmode[0] = mode[0];
|
2024-07-02 14:22:18 +00:00
|
|
|
|
// if mode is "w", then open in text or binary as requested. If "r", always open in "rb"
|
2024-04-16 18:56:40 +00:00
|
|
|
|
tmode[1] = (mode[0] == 'w') ? mode[1] : 'b';
|
|
|
|
|
// try to open file
|
2024-07-02 14:22:18 +00:00
|
|
|
|
fp = fopen(using_filename.u8string().c_str(), tmode);
|
2024-04-16 03:43:29 +00:00
|
|
|
|
|
2024-06-28 23:02:02 +00:00
|
|
|
|
if (!fp) {
|
2024-05-22 11:23:17 +00:00
|
|
|
|
// File not found
|
2024-06-28 23:02:02 +00:00
|
|
|
|
return nullptr;
|
|
|
|
|
} else {
|
2024-07-02 14:22:18 +00:00
|
|
|
|
using_filename = filename;
|
2024-06-28 23:02:02 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// found the file, open it
|
2024-09-08 11:29:41 +00:00
|
|
|
|
cfile = mem_rmalloc<CFILE>();
|
2024-06-28 23:02:02 +00:00
|
|
|
|
if (!cfile)
|
|
|
|
|
Error("Out of memory in open_file_in_directory()");
|
2024-08-30 11:40:54 +00:00
|
|
|
|
cfile->name = mem_rmalloc<char>((strlen(using_filename.u8string().c_str()) + 1));
|
2024-06-28 23:02:02 +00:00
|
|
|
|
if (!cfile->name)
|
|
|
|
|
Error("Out of memory in open_file_in_directory()");
|
2024-07-02 14:22:18 +00:00
|
|
|
|
strcpy(cfile->name, using_filename.u8string().c_str());
|
2024-06-28 23:02:02 +00:00
|
|
|
|
cfile->file = fp;
|
|
|
|
|
cfile->lib_handle = -1;
|
|
|
|
|
cfile->size = ddio_GetFileLength(fp);
|
|
|
|
|
cfile->lib_offset = 0; // 0 means on disk, not in HOG
|
|
|
|
|
cfile->position = 0;
|
|
|
|
|
cfile->flags = 0;
|
|
|
|
|
return cfile;
|
2024-04-16 03:43:29 +00:00
|
|
|
|
}
|
2024-06-28 23:02:02 +00:00
|
|
|
|
|
2024-04-16 18:56:40 +00:00
|
|
|
|
// Opens a file for reading or writing
|
|
|
|
|
// If a path is specified, will try to open the file only in that path.
|
|
|
|
|
// If no path is specified, will look through search directories and library files.
|
|
|
|
|
// Parameters: filename - the name if the file, with or without a path
|
2024-04-16 03:43:29 +00:00
|
|
|
|
// mode - the standard C mode string
|
2024-04-16 18:56:40 +00:00
|
|
|
|
// Returns: the CFile handle, or NULL if file not opened
|
2024-06-17 12:51:46 +00:00
|
|
|
|
CFILE *cfopen(const std::filesystem::path &filename, const char *mode) {
|
2024-04-16 18:56:40 +00:00
|
|
|
|
CFILE *cfile;
|
2024-06-17 12:51:46 +00:00
|
|
|
|
|
2024-04-16 18:56:40 +00:00
|
|
|
|
// Check for valid mode
|
|
|
|
|
ASSERT((mode[0] == 'r') || (mode[0] == 'w'));
|
|
|
|
|
ASSERT((mode[1] == 'b') || (mode[1] == 't'));
|
|
|
|
|
// get the parts of the pathname
|
2024-06-17 12:51:46 +00:00
|
|
|
|
std::filesystem::path path = filename.parent_path();
|
|
|
|
|
std::filesystem::path fname = filename.stem();
|
|
|
|
|
std::filesystem::path ext = filename.extension();
|
|
|
|
|
|
2024-04-16 18:56:40 +00:00
|
|
|
|
// if there is a path specified, use it instead of the libraries, search dirs, etc.
|
|
|
|
|
// if the file is writable, just open it, instead of looking in libs, etc.
|
2024-06-17 12:51:46 +00:00
|
|
|
|
if (!path.empty() || (mode[0] == 'w')) {
|
|
|
|
|
// use path specified with file
|
|
|
|
|
cfile = open_file_in_directory(filename, mode, std::filesystem::path());
|
|
|
|
|
goto got_file; // don't look in libs, etc.
|
2024-04-16 18:56:40 +00:00
|
|
|
|
}
|
2024-06-17 12:51:46 +00:00
|
|
|
|
|
2024-04-16 18:56:40 +00:00
|
|
|
|
// First look in the directories for this file's extension
|
2024-06-20 13:51:00 +00:00
|
|
|
|
for (auto const &entry : extensions) {
|
|
|
|
|
if (!strnicmp(entry.first.u8string().c_str(), ext.u8string().c_str(), _MAX_EXT)) {
|
2024-06-17 12:51:46 +00:00
|
|
|
|
// found ext
|
2024-06-20 13:51:00 +00:00
|
|
|
|
cfile = open_file_in_directory(filename, mode, entry.second);
|
|
|
|
|
if (cfile) {
|
2024-04-16 18:56:40 +00:00
|
|
|
|
goto got_file;
|
2024-06-20 13:51:00 +00:00
|
|
|
|
}
|
2024-04-16 18:56:40 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2024-06-17 12:51:46 +00:00
|
|
|
|
|
2024-04-16 18:56:40 +00:00
|
|
|
|
// Next look in the general directories
|
2024-06-20 13:51:00 +00:00
|
|
|
|
for (auto const &entry : paths) {
|
|
|
|
|
if (!entry.second) {
|
|
|
|
|
cfile = open_file_in_directory(filename, mode, entry.first);
|
2024-06-17 12:51:46 +00:00
|
|
|
|
if (cfile)
|
2024-04-16 18:56:40 +00:00
|
|
|
|
goto got_file;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// Lastly, try the hog files
|
2024-06-17 12:51:46 +00:00
|
|
|
|
cfile = open_file_in_lib(filename.u8string().c_str());
|
2024-04-16 03:43:29 +00:00
|
|
|
|
got_file:;
|
2024-04-16 18:56:40 +00:00
|
|
|
|
if (cfile) {
|
|
|
|
|
if (mode[0] == 'w')
|
2024-04-19 14:43:47 +00:00
|
|
|
|
cfile->flags |= CFF_WRITING;
|
2024-04-16 18:56:40 +00:00
|
|
|
|
if (mode[1] == 't')
|
2024-04-19 14:43:47 +00:00
|
|
|
|
cfile->flags |= CFF_TEXT;
|
2024-04-16 18:56:40 +00:00
|
|
|
|
}
|
|
|
|
|
return cfile;
|
2024-04-16 03:43:29 +00:00
|
|
|
|
}
|
2024-04-19 14:43:47 +00:00
|
|
|
|
|
2024-04-16 18:56:40 +00:00
|
|
|
|
// Returns the length of the specified file
|
|
|
|
|
// Parameters: cfp - the file pointer returned by cfopen()
|
2024-05-16 15:21:30 +00:00
|
|
|
|
uint32_t cfilelength(CFILE *cfp) { return cfp->size; }
|
2024-04-19 14:43:47 +00:00
|
|
|
|
|
2024-04-16 18:56:40 +00:00
|
|
|
|
// Closes an open CFILE.
|
|
|
|
|
// Parameters: cfile - the file pointer returned by cfopen()
|
|
|
|
|
void cfclose(CFILE *cfp) {
|
|
|
|
|
// Either give the file back to the library, or close it
|
|
|
|
|
if (cfp->lib_handle != -1) {
|
2024-05-16 19:31:43 +00:00
|
|
|
|
std::shared_ptr<library> lib;
|
2024-04-16 18:56:40 +00:00
|
|
|
|
for (lib = Libraries; lib; lib = lib->next) {
|
|
|
|
|
if (lib->handle == cfp->lib_handle) { // found the library
|
|
|
|
|
// if library doesn't already have a file, give it this one
|
2024-04-19 14:43:47 +00:00
|
|
|
|
if (lib->file == nullptr) {
|
2024-04-16 18:56:40 +00:00
|
|
|
|
lib->file = cfp->file;
|
2024-04-19 14:43:47 +00:00
|
|
|
|
cfp->file = nullptr;
|
2024-04-16 18:56:40 +00:00
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// If the file handle wasn't given back to library, close the file
|
|
|
|
|
if (cfp->file)
|
|
|
|
|
fclose(cfp->file);
|
|
|
|
|
// free the name, if allocated
|
|
|
|
|
if (!cfp->lib_offset)
|
|
|
|
|
mem_free(cfp->name);
|
|
|
|
|
// free the cfile struct
|
|
|
|
|
mem_free(cfp);
|
2024-04-16 03:43:29 +00:00
|
|
|
|
}
|
2024-04-19 14:43:47 +00:00
|
|
|
|
|
2024-04-16 18:56:40 +00:00
|
|
|
|
// Just like stdio fgetc(), except works on a CFILE
|
|
|
|
|
// Returns a char or EOF
|
|
|
|
|
int cfgetc(CFILE *cfp) {
|
|
|
|
|
int c;
|
2024-05-24 02:58:46 +00:00
|
|
|
|
static uint8_t ch[3] = "\0\0";
|
2024-04-16 18:56:40 +00:00
|
|
|
|
if (cfp->position >= cfp->size)
|
|
|
|
|
return EOF;
|
|
|
|
|
|
|
|
|
|
fread(ch, sizeof(char), 1, cfp->file);
|
|
|
|
|
c = ch[0];
|
|
|
|
|
// c = getc( cfp->file );
|
|
|
|
|
if (cfeof(cfp))
|
|
|
|
|
c = EOF;
|
|
|
|
|
if (c != EOF) {
|
|
|
|
|
cfp->position++;
|
|
|
|
|
// do special newline handling for text files:
|
|
|
|
|
// if CR or LF by itself, return as newline
|
|
|
|
|
// if CR/LF pair, return as newline
|
2024-04-19 14:43:47 +00:00
|
|
|
|
if (cfp->flags & CFF_TEXT) {
|
2024-04-16 18:56:40 +00:00
|
|
|
|
if (c == 10) // return LF as newline
|
|
|
|
|
c = '\n';
|
|
|
|
|
else if (c == 13) { // check for CR/LF pair
|
|
|
|
|
fread(ch, sizeof(char), 1, cfp->file);
|
|
|
|
|
int cc = ch[0]; // getc(cfp->file);
|
|
|
|
|
// if (cc != EOF) {
|
|
|
|
|
if (!cfeof(cfp)) {
|
|
|
|
|
if (cc == 10) // line feed?
|
|
|
|
|
cfp->position++; //..yes, so swallow it
|
|
|
|
|
else {
|
|
|
|
|
// ungetc(cc,cfp->file); //..no, so put it back
|
|
|
|
|
fseek(cfp->file, -1, SEEK_CUR);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
c = '\n'; // return CR or CR/LF pair as newline
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return c;
|
2024-04-16 03:43:29 +00:00
|
|
|
|
}
|
2024-04-16 18:56:40 +00:00
|
|
|
|
// Just like stdio fseek(), except works on a CFILE
|
2024-05-16 15:21:30 +00:00
|
|
|
|
int cfseek(CFILE *cfp, long offset, int where) {
|
|
|
|
|
int c;
|
|
|
|
|
long goal_position;
|
2024-04-16 18:56:40 +00:00
|
|
|
|
switch (where) {
|
|
|
|
|
case SEEK_SET:
|
|
|
|
|
goal_position = offset;
|
|
|
|
|
break;
|
|
|
|
|
case SEEK_CUR:
|
|
|
|
|
goal_position = cfp->position + offset;
|
|
|
|
|
break;
|
|
|
|
|
case SEEK_END:
|
|
|
|
|
goal_position = cfp->size + offset;
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
c = fseek(cfp->file, cfp->lib_offset + goal_position, SEEK_SET);
|
|
|
|
|
cfp->position = ftell(cfp->file) - cfp->lib_offset;
|
|
|
|
|
return c;
|
2024-04-16 03:43:29 +00:00
|
|
|
|
}
|
2024-04-19 14:43:47 +00:00
|
|
|
|
|
2024-04-16 18:56:40 +00:00
|
|
|
|
// Just like stdio ftell(), except works on a CFILE
|
2024-05-16 15:21:30 +00:00
|
|
|
|
long cftell(CFILE *cfp) { return cfp->position; }
|
2024-04-19 14:43:47 +00:00
|
|
|
|
|
2024-04-16 18:56:40 +00:00
|
|
|
|
// Returns true if at EOF
|
|
|
|
|
int cfeof(CFILE *cfp) { return (cfp->position >= cfp->size); }
|
2024-04-19 14:43:47 +00:00
|
|
|
|
|
2024-04-16 03:43:29 +00:00
|
|
|
|
// Tells if the file exists
|
|
|
|
|
// Returns non-zero if file exists. Also tells if the file is on disk
|
|
|
|
|
// or in a hog - See return values in cfile.h
|
2024-06-17 12:51:46 +00:00
|
|
|
|
int cfexist(const std::filesystem::path &filename) {
|
2024-04-16 18:56:40 +00:00
|
|
|
|
CFILE *cfp;
|
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
|
|
cfp = cfopen(filename, "rb");
|
2024-05-07 21:35:28 +00:00
|
|
|
|
if (!cfp) { // Didn't get file. Why?
|
|
|
|
|
if (errno == EACCES) // File exists, but couldn't open it
|
2024-04-19 14:43:47 +00:00
|
|
|
|
return CFES_ON_DISK; // so say it exists on the disk
|
2024-06-17 12:51:46 +00:00
|
|
|
|
|
2024-04-19 14:43:47 +00:00
|
|
|
|
return CFES_NOT_FOUND; // Say we didn't find the file
|
2024-04-16 18:56:40 +00:00
|
|
|
|
}
|
2024-04-19 14:43:47 +00:00
|
|
|
|
ret = cfp->lib_offset ? CFES_IN_LIBRARY : CFES_ON_DISK;
|
2024-04-16 18:56:40 +00:00
|
|
|
|
cfclose(cfp);
|
|
|
|
|
return ret;
|
2024-04-16 03:43:29 +00:00
|
|
|
|
}
|
2024-04-16 18:56:40 +00:00
|
|
|
|
// Reads the specified number of bytes from a file into the buffer
|
|
|
|
|
// DO NOT USE THIS TO READ STRUCTURES. This function is for byte
|
|
|
|
|
// data, such as a string or a bitmap of 8-bit pixels.
|
|
|
|
|
// Returns the number of bytes read.
|
|
|
|
|
// Throws an exception of type (cfile_error *) if the OS returns an error on read
|
2024-05-24 03:07:26 +00:00
|
|
|
|
int cf_ReadBytes(uint8_t *buf, int count, CFILE *cfp) {
|
2024-04-16 18:56:40 +00:00
|
|
|
|
int i;
|
2024-04-28 04:39:29 +00:00
|
|
|
|
const char *error_msg = eof_error; // default error
|
2024-04-19 14:43:47 +00:00
|
|
|
|
ASSERT(!(cfp->flags & CFF_TEXT));
|
2024-04-16 18:56:40 +00:00
|
|
|
|
if (cfp->position + count <= cfp->size) {
|
|
|
|
|
i = fread(buf, 1, count, cfp->file);
|
|
|
|
|
if (i == count) {
|
|
|
|
|
cfp->position += i;
|
|
|
|
|
return i;
|
|
|
|
|
}
|
|
|
|
|
// if not EOF, then get the error message
|
|
|
|
|
if (!feof(cfp->file))
|
|
|
|
|
error_msg = strerror(errno);
|
|
|
|
|
}
|
2024-09-21 23:01:56 +00:00
|
|
|
|
LOG_ERROR.printf("Error reading %d bytes from position %d of file <%s>; errno=%d.", count, cfp->position, cfp->name,
|
|
|
|
|
errno);
|
2024-04-16 18:56:40 +00:00
|
|
|
|
return 0;
|
2024-04-16 03:43:29 +00:00
|
|
|
|
}
|
2024-04-16 18:56:40 +00:00
|
|
|
|
// The following functions read numeric vales from a CFILE. All values are
|
2024-04-16 03:43:29 +00:00
|
|
|
|
// stored in the file in Intel (little-endian) format. These functions
|
|
|
|
|
// will convert to big-endian if required.
|
|
|
|
|
// These funtions will exit the program with an error if the value
|
|
|
|
|
// cannot be read, so do not call these if you don't require the data
|
2024-04-16 18:56:40 +00:00
|
|
|
|
// to be present.
|
|
|
|
|
// Read and return an integer (32 bits)
|
|
|
|
|
// Throws an exception of type (cfile_error *) if the OS returns an error on read
|
2024-07-03 00:34:45 +00:00
|
|
|
|
int32_t cf_ReadInt(CFILE *cfp, bool little_endian) {
|
2024-04-19 14:43:47 +00:00
|
|
|
|
int32_t i;
|
2024-05-24 03:07:26 +00:00
|
|
|
|
cf_ReadBytes((uint8_t *)&i, sizeof(i), cfp);
|
2024-07-03 00:34:45 +00:00
|
|
|
|
return little_endian ? D3::convert_le(i) : D3::convert_be(i);
|
2024-04-16 03:43:29 +00:00
|
|
|
|
}
|
2024-05-24 03:27:12 +00:00
|
|
|
|
// Read and return a int16_t (16 bits)
|
2024-04-16 18:56:40 +00:00
|
|
|
|
// Throws an exception of type (cfile_error *) if the OS returns an error on read
|
2024-07-03 00:34:45 +00:00
|
|
|
|
int16_t cf_ReadShort(CFILE *cfp, bool little_endian) {
|
2024-04-19 14:43:47 +00:00
|
|
|
|
int16_t i;
|
2024-05-24 03:07:26 +00:00
|
|
|
|
cf_ReadBytes((uint8_t *)&i, sizeof(i), cfp);
|
2024-07-03 00:34:45 +00:00
|
|
|
|
return little_endian ? D3::convert_le(i) : D3::convert_be(i);
|
2024-04-16 03:43:29 +00:00
|
|
|
|
}
|
2024-04-16 18:56:40 +00:00
|
|
|
|
// Read and return a byte (8 bits)
|
|
|
|
|
// Throws an exception of type (cfile_error *) if the OS returns an error on read
|
2024-04-19 14:43:47 +00:00
|
|
|
|
int8_t cf_ReadByte(CFILE *cfp) {
|
2024-05-16 15:21:30 +00:00
|
|
|
|
int8_t i;
|
2024-05-24 03:07:26 +00:00
|
|
|
|
cf_ReadBytes((uint8_t *)&i, sizeof(i), cfp);
|
2024-05-16 15:21:30 +00:00
|
|
|
|
return i;
|
2024-04-16 03:43:29 +00:00
|
|
|
|
}
|
2024-04-16 18:56:40 +00:00
|
|
|
|
// Read and return a float (32 bits)
|
|
|
|
|
// Throws an exception of type (cfile_error *) if the OS returns an error on read
|
2024-04-19 19:05:59 +00:00
|
|
|
|
float cf_ReadFloat(CFILE *cfp) {
|
|
|
|
|
float f;
|
2024-05-24 03:07:26 +00:00
|
|
|
|
cf_ReadBytes((uint8_t *)&f, sizeof(f), cfp);
|
2024-04-16 18:56:40 +00:00
|
|
|
|
return INTEL_FLOAT(f);
|
2024-04-16 03:43:29 +00:00
|
|
|
|
}
|
2024-04-16 18:56:40 +00:00
|
|
|
|
// Read and return a double (64 bits)
|
|
|
|
|
// Throws an exception of type (cfile_error *) if the OS returns an error on read
|
2024-04-19 19:05:59 +00:00
|
|
|
|
double cf_ReadDouble(CFILE *cfp) {
|
|
|
|
|
double f;
|
2024-05-24 03:07:26 +00:00
|
|
|
|
cf_ReadBytes((uint8_t *)&f, sizeof(f), cfp);
|
2024-05-16 15:21:30 +00:00
|
|
|
|
return D3::convert_le<double>(f);
|
2024-04-16 03:43:29 +00:00
|
|
|
|
}
|
2024-04-16 18:56:40 +00:00
|
|
|
|
// Reads a string from a CFILE. If the file is type binary, this
|
|
|
|
|
// function reads until a NULL or EOF is found. If the file is text,
|
|
|
|
|
// the function reads until a newline or EOF is found. The string is always
|
|
|
|
|
// written to the destination buffer null-terminated, without the newline.
|
|
|
|
|
// Parameters: buf - where the string is written
|
2024-04-16 03:43:29 +00:00
|
|
|
|
// n - the maximum string length, including the terminating 0
|
|
|
|
|
// cfp - the CFILE pointer
|
2024-04-16 18:56:40 +00:00
|
|
|
|
// Returns the number of bytes in the string, before the terminator
|
|
|
|
|
// Does not generate an exception on EOF
|
|
|
|
|
int cf_ReadString(char *buf, size_t n, CFILE *cfp) {
|
|
|
|
|
int c;
|
2024-05-16 15:21:30 +00:00
|
|
|
|
int count;
|
2024-04-16 18:56:40 +00:00
|
|
|
|
char *bp;
|
|
|
|
|
if (n == 0)
|
|
|
|
|
return -1;
|
|
|
|
|
bp = buf;
|
|
|
|
|
for (count = 0;; count++) {
|
|
|
|
|
c = cfgetc(cfp);
|
|
|
|
|
if (c == EOF) {
|
|
|
|
|
if (!cfeof(cfp)) // not actually at EOF, so must be error
|
|
|
|
|
ThrowCFileError(CFE_READING, cfp, strerror(errno));
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2024-04-19 14:43:47 +00:00
|
|
|
|
if ((!(cfp->flags & CFF_TEXT) && (c == 0)) || ((cfp->flags & CFF_TEXT) && (c == '\n')))
|
2024-04-16 18:56:40 +00:00
|
|
|
|
break; // end-of-string
|
|
|
|
|
if (count < n - 1) // store char if room in buffer
|
|
|
|
|
*bp++ = c;
|
|
|
|
|
}
|
|
|
|
|
*bp = 0; // write terminator
|
|
|
|
|
return count;
|
2024-04-16 03:43:29 +00:00
|
|
|
|
}
|
2024-04-16 18:56:40 +00:00
|
|
|
|
// Writes the specified number of bytes from a file into the buffer
|
|
|
|
|
// DO NOT USE THIS TO WRITE STRUCTURES. This function is for byte
|
|
|
|
|
// data, such as a string or a bitmap of 8-bit pixels.
|
|
|
|
|
// Returns the number of bytes written.
|
|
|
|
|
// Throws an exception of type (cfile_error *) if the OS returns an error on write
|
2024-05-24 03:07:26 +00:00
|
|
|
|
int cf_WriteBytes(const uint8_t *buf, int count, CFILE *cfp) {
|
2024-04-16 18:56:40 +00:00
|
|
|
|
int i;
|
2024-04-19 14:43:47 +00:00
|
|
|
|
if (!(cfp->flags & CFF_WRITING))
|
2024-04-16 18:56:40 +00:00
|
|
|
|
return 0;
|
|
|
|
|
ASSERT(count > 0);
|
|
|
|
|
i = fwrite(buf, 1, count, cfp->file);
|
|
|
|
|
cfp->position += i;
|
|
|
|
|
if (i != count)
|
|
|
|
|
ThrowCFileError(CFE_WRITING, cfp, strerror(errno));
|
|
|
|
|
return i;
|
2024-04-16 03:43:29 +00:00
|
|
|
|
}
|
2024-04-16 18:56:40 +00:00
|
|
|
|
// Writes a null-terminated string to a file. If the file is type binary,
|
|
|
|
|
// the string is terminated in the file with a null. If the file is type
|
|
|
|
|
// text, the string is terminated with a newline.
|
|
|
|
|
// Parameters: buf - pointer to the string
|
2024-04-16 03:43:29 +00:00
|
|
|
|
// cfp = the CFILE pointer
|
2024-04-16 18:56:40 +00:00
|
|
|
|
// Returns the number of bytes written
|
|
|
|
|
// Throws an exception of type (cfile_error *) if the OS returns an error on write
|
|
|
|
|
int cf_WriteString(CFILE *cfp, const char *buf) {
|
|
|
|
|
int len;
|
|
|
|
|
len = strlen(buf);
|
|
|
|
|
if (len != 0) // write string
|
2024-05-24 03:07:26 +00:00
|
|
|
|
cf_WriteBytes((uint8_t *)buf, len, cfp);
|
2024-04-16 18:56:40 +00:00
|
|
|
|
// Terminate with newline (text file) or NULL (binary file)
|
2024-04-19 14:43:47 +00:00
|
|
|
|
cf_WriteByte(cfp, (cfp->flags & CFF_TEXT) ? '\n' : 0);
|
2024-04-16 18:56:40 +00:00
|
|
|
|
return len + 1;
|
2024-04-16 03:43:29 +00:00
|
|
|
|
}
|
2024-04-19 14:43:47 +00:00
|
|
|
|
|
2024-04-16 18:56:40 +00:00
|
|
|
|
// Just like stdio fprintf(), except works on a CFILE
|
|
|
|
|
int cfprintf(CFILE *cfp, const char *format, ...) {
|
|
|
|
|
va_list args;
|
|
|
|
|
int count;
|
|
|
|
|
va_start(args, format);
|
|
|
|
|
count = vfprintf(cfp->file, format, args);
|
2024-04-18 01:16:39 +00:00
|
|
|
|
va_end(args);
|
2024-04-16 18:56:40 +00:00
|
|
|
|
cfp->position += count + 1; // count doesn't include terminator
|
|
|
|
|
return count;
|
2024-04-16 03:43:29 +00:00
|
|
|
|
}
|
2024-04-19 14:43:47 +00:00
|
|
|
|
|
2024-04-16 18:56:40 +00:00
|
|
|
|
// The following functions write numeric vales to a CFILE. All values are
|
|
|
|
|
// stored to the file in Intel (little-endian) format.
|
2024-04-16 03:43:29 +00:00
|
|
|
|
// All these throw an exception if there's an error on write.
|
2024-04-16 18:56:40 +00:00
|
|
|
|
// Write an integer (32 bits)
|
|
|
|
|
// Throws an exception of type (cfile_error *) if the OS returns an error on write
|
2024-04-19 14:43:47 +00:00
|
|
|
|
void cf_WriteInt(CFILE *cfp, int32_t i) {
|
2024-04-16 18:56:40 +00:00
|
|
|
|
int t = INTEL_INT(i);
|
2024-05-24 03:07:26 +00:00
|
|
|
|
cf_WriteBytes((uint8_t *)&t, sizeof(t), cfp);
|
2024-04-16 03:43:29 +00:00
|
|
|
|
}
|
2024-04-19 14:43:47 +00:00
|
|
|
|
|
2024-05-24 03:27:12 +00:00
|
|
|
|
// Write a int16_t (16 bits)
|
2024-04-16 18:56:40 +00:00
|
|
|
|
// Throws an exception of type (cfile_error *) if the OS returns an error on write
|
2024-04-19 14:43:47 +00:00
|
|
|
|
void cf_WriteShort(CFILE *cfp, int16_t s) {
|
2024-05-24 03:27:12 +00:00
|
|
|
|
int16_t t = INTEL_SHORT(s);
|
2024-05-24 03:07:26 +00:00
|
|
|
|
cf_WriteBytes((uint8_t *)&t, sizeof(t), cfp);
|
2024-04-16 03:43:29 +00:00
|
|
|
|
}
|
2024-04-19 14:43:47 +00:00
|
|
|
|
|
2024-04-16 18:56:40 +00:00
|
|
|
|
// Write a byte (8 bits).
|
|
|
|
|
// Throws an exception of type (cfile_error *) if the OS returns an error on write
|
2024-04-19 14:43:47 +00:00
|
|
|
|
void cf_WriteByte(CFILE *cfp, int8_t b) {
|
2024-04-16 18:56:40 +00:00
|
|
|
|
if (fputc(b, cfp->file) == EOF)
|
|
|
|
|
ThrowCFileError(CFE_WRITING, cfp, strerror(errno));
|
|
|
|
|
cfp->position++;
|
|
|
|
|
// If text file & writing newline, increment again for LF
|
2024-04-19 14:43:47 +00:00
|
|
|
|
if ((cfp->flags & CFF_TEXT) && (b == '\n')) // check for text mode newline
|
2024-04-16 18:56:40 +00:00
|
|
|
|
cfp->position++;
|
2024-04-16 03:43:29 +00:00
|
|
|
|
}
|
2024-04-19 14:43:47 +00:00
|
|
|
|
|
2024-04-16 18:56:40 +00:00
|
|
|
|
// Write a float (32 bits)
|
|
|
|
|
// Throws an exception of type (cfile_error *) if the OS returns an error on write
|
2024-05-16 15:21:30 +00:00
|
|
|
|
void cf_WriteFloat(CFILE *cfp, float f) {
|
2024-04-16 18:56:40 +00:00
|
|
|
|
float t = INTEL_FLOAT(f);
|
2024-05-24 03:07:26 +00:00
|
|
|
|
cf_WriteBytes((uint8_t *)&t, sizeof(t), cfp);
|
2024-04-16 03:43:29 +00:00
|
|
|
|
}
|
2024-04-19 14:43:47 +00:00
|
|
|
|
|
2024-04-16 18:56:40 +00:00
|
|
|
|
// Write a double (64 bits)
|
|
|
|
|
// Throws an exception of type (cfile_error *) if the OS returns an error on write
|
2024-05-16 15:21:30 +00:00
|
|
|
|
void cf_WriteDouble(CFILE *cfp, double d) {
|
|
|
|
|
auto t = D3::convert_le<double>(d);
|
2024-05-24 03:07:26 +00:00
|
|
|
|
cf_WriteBytes((uint8_t *)&t, sizeof(t), cfp);
|
2024-04-16 03:43:29 +00:00
|
|
|
|
}
|
2024-04-19 14:43:47 +00:00
|
|
|
|
|
2024-04-16 18:56:40 +00:00
|
|
|
|
// Copies a file. Returns TRUE if copied ok. Returns FALSE if error opening either file.
|
|
|
|
|
// Throws an exception of type (cfile_error *) if the OS returns an error on read or write
|
2024-06-21 09:55:25 +00:00
|
|
|
|
bool cf_CopyFile(const std::filesystem::path &dest, const std::filesystem::path &src, int copytime) {
|
2024-04-16 18:56:40 +00:00
|
|
|
|
CFILE *infile, *outfile;
|
2024-06-21 09:55:25 +00:00
|
|
|
|
if (!stricmp(dest.u8string().c_str(), src.u8string().c_str()))
|
2024-04-19 14:43:47 +00:00
|
|
|
|
return true; // don't copy files if they are the same
|
2024-04-16 18:56:40 +00:00
|
|
|
|
infile = (CFILE *)cfopen(src, "rb");
|
|
|
|
|
if (!infile)
|
2024-04-19 14:43:47 +00:00
|
|
|
|
return false;
|
2024-04-16 18:56:40 +00:00
|
|
|
|
outfile = (CFILE *)cfopen(dest, "wb");
|
|
|
|
|
if (!outfile) {
|
|
|
|
|
cfclose(infile);
|
2024-04-19 14:43:47 +00:00
|
|
|
|
return false;
|
2024-04-16 18:56:40 +00:00
|
|
|
|
}
|
|
|
|
|
int progress = 0;
|
|
|
|
|
int readcount = 0;
|
|
|
|
|
#define COPY_CHUNK_SIZE 5000
|
2024-05-24 03:07:26 +00:00
|
|
|
|
uint8_t copybuf[COPY_CHUNK_SIZE];
|
2024-04-16 18:56:40 +00:00
|
|
|
|
while (!cfeof(infile)) {
|
2024-05-24 03:07:26 +00:00
|
|
|
|
// uint8_t c;
|
2024-04-16 18:56:40 +00:00
|
|
|
|
|
|
|
|
|
if (progress + COPY_CHUNK_SIZE <= infile->size) {
|
|
|
|
|
readcount = COPY_CHUNK_SIZE;
|
|
|
|
|
} else {
|
|
|
|
|
readcount = infile->size - progress;
|
|
|
|
|
}
|
|
|
|
|
cf_ReadBytes(copybuf, readcount, infile);
|
|
|
|
|
cf_WriteBytes(copybuf, readcount, outfile);
|
|
|
|
|
progress += readcount;
|
|
|
|
|
// c=cf_ReadByte (infile);
|
|
|
|
|
// cf_WriteByte (outfile,c);
|
|
|
|
|
}
|
2024-08-29 21:35:24 +00:00
|
|
|
|
bool nlo = !infile->lib_offset;
|
2024-04-16 18:56:40 +00:00
|
|
|
|
cfclose(infile);
|
|
|
|
|
cfclose(outfile);
|
2024-08-29 21:35:24 +00:00
|
|
|
|
if (nlo && copytime) {
|
2024-04-16 18:56:40 +00:00
|
|
|
|
cf_CopyFileTime(dest, src);
|
|
|
|
|
}
|
2024-04-19 14:43:47 +00:00
|
|
|
|
return true;
|
2024-04-16 03:43:29 +00:00
|
|
|
|
}
|
2024-04-19 14:43:47 +00:00
|
|
|
|
|
2024-04-16 18:56:40 +00:00
|
|
|
|
// Checks to see if two files are different.
|
|
|
|
|
// Returns TRUE if the files are different, or FALSE if they are the same.
|
2024-06-21 10:13:29 +00:00
|
|
|
|
bool cf_Diff(const std::filesystem::path &a, const std::filesystem::path &b) { return (ddio_FileDiff(a, b)); }
|
2024-04-19 14:43:47 +00:00
|
|
|
|
|
2024-04-16 18:56:40 +00:00
|
|
|
|
// Copies the file time from one file to another
|
2024-06-21 09:55:25 +00:00
|
|
|
|
void cf_CopyFileTime(const std::filesystem::path &dest, const std::filesystem::path &src) {
|
|
|
|
|
ddio_CopyFileTime(dest, src);
|
|
|
|
|
}
|
2024-04-19 14:43:47 +00:00
|
|
|
|
|
2024-04-16 03:43:29 +00:00
|
|
|
|
// rewinds cfile position
|
2024-04-16 18:56:40 +00:00
|
|
|
|
void cf_Rewind(CFILE *fp) {
|
|
|
|
|
if (fp->lib_offset) {
|
|
|
|
|
int r = fseek(fp->file, fp->lib_offset, SEEK_SET);
|
|
|
|
|
ASSERT(r == 0);
|
|
|
|
|
} else {
|
|
|
|
|
rewind(fp->file);
|
|
|
|
|
}
|
|
|
|
|
fp->position = 0;
|
2024-04-16 03:43:29 +00:00
|
|
|
|
}
|
2024-04-19 14:43:47 +00:00
|
|
|
|
|
|
|
|
|
// Calculates a 32-bit CRC for the specified file. a return code of -1 means file note found
|
2024-04-16 18:56:40 +00:00
|
|
|
|
#define CRC32_POLYNOMIAL 0xEDB88320L
|
|
|
|
|
#define CRC_BUFFER_SIZE 5000
|
|
|
|
|
|
2024-05-24 02:51:16 +00:00
|
|
|
|
uint32_t cf_CalculateFileCRC(CFILE *infile) {
|
2024-04-16 18:56:40 +00:00
|
|
|
|
int i, j;
|
2024-05-24 03:07:26 +00:00
|
|
|
|
uint8_t crcbuf[CRC_BUFFER_SIZE];
|
2024-04-16 18:56:40 +00:00
|
|
|
|
static bool Cfile_crc_calculated = false;
|
2024-05-24 02:51:16 +00:00
|
|
|
|
static uint32_t CRCTable[256];
|
|
|
|
|
uint32_t crc;
|
|
|
|
|
uint32_t temp1;
|
|
|
|
|
uint32_t temp2;
|
|
|
|
|
uint32_t readlen;
|
2024-04-16 18:56:40 +00:00
|
|
|
|
|
|
|
|
|
// Only make the lookup table once
|
|
|
|
|
if (!Cfile_crc_calculated) {
|
|
|
|
|
Cfile_crc_calculated = true;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i <= 255; i++) {
|
|
|
|
|
crc = i;
|
|
|
|
|
for (j = 8; j > 0; j--) {
|
|
|
|
|
if (crc & 1)
|
|
|
|
|
crc = (crc >> 1) ^ CRC32_POLYNOMIAL;
|
|
|
|
|
else
|
|
|
|
|
crc >>= 1;
|
|
|
|
|
}
|
|
|
|
|
CRCTable[i] = crc;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
crc = 0xffffffffl;
|
|
|
|
|
while (!cfeof(infile)) {
|
|
|
|
|
if ((infile->size - infile->position) < CRC_BUFFER_SIZE)
|
|
|
|
|
readlen = infile->size - infile->position;
|
|
|
|
|
else
|
|
|
|
|
readlen = CRC_BUFFER_SIZE;
|
|
|
|
|
if (!cf_ReadBytes(crcbuf, readlen, infile)) {
|
|
|
|
|
// Doh, error time!
|
|
|
|
|
Int3();
|
|
|
|
|
return 0xFFFFFFFF;
|
|
|
|
|
}
|
2024-05-24 02:51:16 +00:00
|
|
|
|
for (uint32_t a = 0; a < readlen; a++) {
|
2024-04-16 18:56:40 +00:00
|
|
|
|
temp1 = (crc >> 8) & 0x00FFFFFFL;
|
|
|
|
|
temp2 = CRCTable[((int)crc ^ crcbuf[a]) & 0xff];
|
|
|
|
|
crc = temp1 ^ temp2;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return crc ^ 0xffffffffl;
|
2024-04-16 03:43:29 +00:00
|
|
|
|
}
|
|
|
|
|
|
2024-06-21 09:55:25 +00:00
|
|
|
|
uint32_t cf_GetfileCRC(const std::filesystem::path &src) {
|
2024-06-20 18:32:48 +00:00
|
|
|
|
CFILE *infile = cfopen(src, "rb");
|
2024-04-16 18:56:40 +00:00
|
|
|
|
if (!infile)
|
|
|
|
|
return 0xFFFFFFFF;
|
2024-04-16 03:43:29 +00:00
|
|
|
|
|
2024-05-24 02:51:16 +00:00
|
|
|
|
uint32_t crc = cf_CalculateFileCRC(infile);
|
2024-04-16 18:56:40 +00:00
|
|
|
|
cfclose(infile);
|
|
|
|
|
|
|
|
|
|
return crc;
|
2024-04-16 03:43:29 +00:00
|
|
|
|
}
|
|
|
|
|
|
2024-09-21 23:01:56 +00:00
|
|
|
|
int cf_DoForeachFileInLibrary(int handle, const std::filesystem::path &ext,
|
|
|
|
|
const std::function<void(std::filesystem::path)> &func) {
|
|
|
|
|
auto search_library = Libraries;
|
|
|
|
|
while (search_library && search_library->handle != handle) {
|
|
|
|
|
search_library = search_library->next;
|
|
|
|
|
}
|
|
|
|
|
if (!search_library)
|
|
|
|
|
return 0;
|
|
|
|
|
// Iterate entries on found library
|
|
|
|
|
int result = 0;
|
|
|
|
|
for (const auto &item : search_library->entries) {
|
|
|
|
|
if (stricmp(std::filesystem::path(item->name).extension().u8string().c_str(), ext.u8string().c_str()) == 0) {
|
|
|
|
|
func(item->name);
|
|
|
|
|
result++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-21 09:55:25 +00:00
|
|
|
|
bool cf_IsFileInHog(const std::filesystem::path &filename, const std::filesystem::path &hogname) {
|
2024-05-16 19:31:43 +00:00
|
|
|
|
std::shared_ptr<library> lib = Libraries;
|
2024-04-16 18:56:40 +00:00
|
|
|
|
|
|
|
|
|
while (lib) {
|
2024-06-20 18:32:48 +00:00
|
|
|
|
if (stricmp(lib->name.u8string().c_str(), hogname.u8string().c_str()) == 0) {
|
2024-04-16 18:56:40 +00:00
|
|
|
|
// Now look for filename
|
|
|
|
|
CFILE *cf;
|
|
|
|
|
cf = cf_OpenFileInLibrary(filename, lib->handle);
|
|
|
|
|
if (cf) {
|
|
|
|
|
cfclose(cf);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
lib = lib->next;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
2024-04-18 01:16:39 +00:00
|
|
|
|
}
|