mirror of
https://github.com/scp-fs2open/fs2open.github.com.git
synced 2026-11-09 13:01:29 -10:00
309 lines
8.1 KiB
C++
309 lines
8.1 KiB
C++
/*
|
|
* Copyright (C) Volition, Inc. 1999. All rights reserved.
|
|
*
|
|
* All source code herein is the property of Volition, Inc. You may not sell
|
|
* or otherwise commercially exploit the source or things you created based on the
|
|
* source.
|
|
*
|
|
*/
|
|
|
|
|
|
|
|
#define _CFILE_INTERNAL
|
|
|
|
#include <cstdlib>
|
|
#include <cstring>
|
|
#include <cstdio>
|
|
|
|
#ifdef _WIN32
|
|
#include <io.h>
|
|
#include <direct.h>
|
|
#include <windows.h>
|
|
#include <winbase.h> /* needed for memory mapping of file functions */
|
|
#endif
|
|
|
|
#include "cfile/cfile.h"
|
|
#include "cfile/cfilearchive.h"
|
|
#include "cfile/cfilecompression.h"
|
|
#include "luaconf.h"
|
|
|
|
#include <sstream>
|
|
#include <limits>
|
|
|
|
|
|
#define CHECK_POSITION
|
|
|
|
// Called once to setup the low-level reading code.
|
|
void cf_init_lowlevel_read_code( CFILE * cfile, size_t lib_offset, size_t size, size_t pos )
|
|
{
|
|
Assert(cfile != nullptr);
|
|
|
|
cfile->lib_offset = lib_offset;
|
|
cfile->raw_position = pos;
|
|
cfile->size = size;
|
|
|
|
if ( cfile->fp ) {
|
|
if ( cfile->lib_offset ) {
|
|
fseek( cfile->fp, (long)cfile->lib_offset, SEEK_SET );
|
|
}
|
|
|
|
#if defined(CHECK_POSITION) && !defined(NDEBUG)
|
|
auto raw_position = ftell(cfile->fp) - cfile->lib_offset;
|
|
Assert(raw_position == cfile->raw_position);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
//This function checks the file header to see if the file is compressed, if so it fills the correct compression info.
|
|
void cf_check_compression(CFILE* cfile)
|
|
{
|
|
if (cfile->size <= 16)
|
|
return;
|
|
int header=cfread_int(cfile);
|
|
cfseek(cfile, 0, SEEK_SET);
|
|
if (comp_check_header(header) == COMP_HEADER_MATCH)
|
|
comp_create_ci(cfile, header);
|
|
}
|
|
|
|
//This function is called to cleanup compression info when the cfile handle is reused.
|
|
void cf_clear_compression_info(CFILE* cfile)
|
|
{
|
|
if (cfile->compression_info.header != 0)
|
|
{
|
|
free(cfile->compression_info.offsets);
|
|
free(cfile->compression_info.decoder_buffer);
|
|
cfile->compression_info.offsets = nullptr;
|
|
cfile->compression_info.decoder_buffer = nullptr;
|
|
cfile->compression_info.header = 0;
|
|
cfile->compression_info.block_size = 0;
|
|
cfile->compression_info.last_decoded_block_pos = 0;
|
|
cfile->compression_info.last_decoded_block_bytes = 0;
|
|
cfile->compression_info.num_offsets = 0;
|
|
}
|
|
}
|
|
|
|
|
|
// cfeof() Tests for end-of-file on a stream
|
|
//
|
|
// returns a nonzero value after the first read operation that attempts to read
|
|
// past the end of the file. It returns 0 if the current position is not end of file.
|
|
// There is no error return.
|
|
int cfeof(CFILE *cfile)
|
|
{
|
|
Assert(cfile != NULL);
|
|
if (cfile->compression_info.header != 0)
|
|
return comp_feof(cfile);
|
|
int result = 0;
|
|
|
|
#if defined(CHECK_POSITION) && !defined(NDEBUG)
|
|
if (cfile->fp) {
|
|
auto raw_position = ftell(cfile->fp) - cfile->lib_offset;
|
|
Assert(raw_position == cfile->raw_position);
|
|
}
|
|
#endif
|
|
|
|
if (cfile->raw_position >= cfile->size ) {
|
|
result = 1;
|
|
} else {
|
|
result = 0;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
// cftell() returns offset into file
|
|
//
|
|
// returns: success ==> offset into the file
|
|
// error ==> -1
|
|
//
|
|
int cftell( CFILE * cfile )
|
|
{
|
|
Assert(cfile != NULL);
|
|
if (cfile->compression_info.header != 0)
|
|
return (int)comp_ftell(cfile);
|
|
|
|
#if defined(CHECK_POSITION) && !defined(NDEBUG)
|
|
if (cfile->fp) {
|
|
auto raw_position = ftell(cfile->fp) - cfile->lib_offset;
|
|
Assert(raw_position == cfile->raw_position);
|
|
}
|
|
#endif
|
|
|
|
// The rest of the code still uses ints, do an overflow check to detect cases where this fails
|
|
Assertion(cfile->raw_position <= static_cast<size_t>(std::numeric_limits<int>::max()),
|
|
"Integer overflow in cftell, a file is probably too large (but I don't know which one).");
|
|
return (int) cfile->raw_position;
|
|
}
|
|
|
|
|
|
// cfseek() moves the file pointer
|
|
//
|
|
// returns: success ==> 0
|
|
// error ==> non-zero
|
|
//
|
|
int cfseek( CFILE *cfile, int offset, int where )
|
|
{
|
|
|
|
Assert(cfile != NULL);
|
|
|
|
if (cfile->compression_info.header != 0)
|
|
return comp_fseek(cfile, offset, where);
|
|
|
|
// TODO: seek to offset in memory mapped file
|
|
Assert( !cfile->mem_mapped );
|
|
|
|
size_t goal_position;
|
|
|
|
switch( where ) {
|
|
case CF_SEEK_SET:
|
|
goal_position = offset+cfile->lib_offset;
|
|
break;
|
|
case CF_SEEK_CUR:
|
|
{
|
|
goal_position = cfile->raw_position+offset+cfile->lib_offset;
|
|
}
|
|
break;
|
|
case CF_SEEK_END:
|
|
goal_position = cfile->size+offset+cfile->lib_offset;
|
|
break;
|
|
default:
|
|
Int3();
|
|
return 1;
|
|
}
|
|
|
|
// Make sure we don't seek beyond the end of the file
|
|
CAP(goal_position, cfile->lib_offset, cfile->lib_offset + cfile->size);
|
|
|
|
int result = 0;
|
|
|
|
if (cfile->fp) {
|
|
// If we have a file pointer we can also seek in that file
|
|
result = fseek(cfile->fp, (long)goal_position, SEEK_SET );
|
|
Assertion(goal_position >= cfile->lib_offset, "Invalid offset values detected while seeking! Goal was " SIZE_T_ARG ", lib_offset is " SIZE_T_ARG ".", goal_position, cfile->lib_offset);
|
|
}
|
|
// If we only have a data pointer this will do all the work
|
|
cfile->raw_position = goal_position - cfile->lib_offset;
|
|
Assertion(cfile->raw_position <= cfile->size, "Invalid raw_position value detected!");
|
|
|
|
#if defined(CHECK_POSITION) && !defined(NDEBUG)
|
|
if (cfile->fp) {
|
|
auto tmp_offset = ftell(cfile->fp) - cfile->lib_offset;
|
|
Assert(tmp_offset == cfile->raw_position);
|
|
}
|
|
#endif
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
// cfread() reads from a file
|
|
//
|
|
// returns: returns the number of full elements read
|
|
//
|
|
//
|
|
int cfread(void *buf, int elsize, int nelem, CFILE *cfile)
|
|
{
|
|
if(!cf_is_valid(cfile))
|
|
return 0;
|
|
|
|
size_t size = elsize*nelem;
|
|
|
|
if(buf == NULL || size <= 0)
|
|
return 0;
|
|
|
|
if ( (cfile->raw_position+size) > cfile->size ) {
|
|
Assertion(cfile->raw_position <= cfile->size, "Invalid raw_position value detected!");
|
|
size = cfile->size - cfile->raw_position;
|
|
if ( size < 1 ) {
|
|
return 0;
|
|
}
|
|
//mprintf(( "CFILE: EOF encountered in file\n" ));
|
|
}
|
|
|
|
if (cfile->max_read_len) {
|
|
if ( cfile->raw_position+size > cfile->max_read_len ) {
|
|
std::ostringstream s_buf;
|
|
s_buf << "Attempted to read " << size << "-byte(s) beyond length limit";
|
|
|
|
throw cfile::max_read_length(s_buf.str());
|
|
}
|
|
}
|
|
|
|
size_t bytes_read;
|
|
if (cfile->data != nullptr) {
|
|
// This is a file from memory
|
|
bytes_read = size;
|
|
memcpy(buf, reinterpret_cast<const char*>(cfile->data) + cfile->raw_position, size);
|
|
} else if (cfile->compression_info.header != 0) {
|
|
bytes_read = comp_fread(cfile, reinterpret_cast<char*>(buf),size);
|
|
if (bytes_read != size)
|
|
{
|
|
mprintf(("\nFile: %s decompression error. Result was: %d expected: %d\n", cfile->original_filename.c_str(), (int)bytes_read, (int)size));
|
|
Assertion(bytes_read == size, "File decompression error!");
|
|
}
|
|
} else {
|
|
bytes_read = fread(buf, 1, size, cfile->fp);
|
|
}
|
|
|
|
if ( bytes_read > 0 ) {
|
|
cfile->raw_position += bytes_read;
|
|
Assertion(cfile->raw_position <= cfile->size, "Invalid raw_position value detected!");
|
|
}
|
|
|
|
#if defined(CHECK_POSITION) && !defined(NDEBUG)
|
|
//Raw position is not the fp position with compressed files
|
|
if (cfile->fp && cfile->compression_info.header == 0) {
|
|
auto tmp_offset = ftell(cfile->fp) - cfile->lib_offset;
|
|
Assert(tmp_offset == cfile->raw_position);
|
|
}
|
|
#endif
|
|
|
|
return (int)(bytes_read / elsize);
|
|
}
|
|
|
|
int cfread_lua_number(double *buf, CFILE *cfile)
|
|
{
|
|
if(!cf_is_valid(cfile))
|
|
return 0;
|
|
|
|
if(buf == NULL)
|
|
return 0;
|
|
|
|
// cfread() not supported for memory-mapped files
|
|
if(cfile->data != nullptr)
|
|
{
|
|
Warning(LOCATION, "Writing is not supported for mem-mapped files");
|
|
return 0;
|
|
}
|
|
|
|
size_t advance = 0;
|
|
int items_read;
|
|
if (cfile->fp) {
|
|
long orig_pos = ftell(cfile->fp);
|
|
items_read = fscanf(cfile->fp, LUA_NUMBER_SCAN, buf);
|
|
advance = (size_t) (ftell(cfile->fp)-orig_pos);
|
|
} else {
|
|
int read;
|
|
// %n returns the number of bytes currently read so we append that to the scan format at the end so it will return
|
|
// how many bytes we have consumed
|
|
items_read = sscanf(reinterpret_cast<const char*>(cfile->data), LUA_NUMBER_SCAN "%n", buf, &read);
|
|
if (items_read == 2) {
|
|
// We need to correct the items read counter since we read one additional item
|
|
items_read = 1;
|
|
}
|
|
advance = (size_t) read;
|
|
}
|
|
cfile->raw_position += advance;
|
|
Assertion(cfile->raw_position <= cfile->size, "Invalid raw_position value detected!");
|
|
|
|
#if defined(CHECK_POSITION) && !defined(NDEBUG)
|
|
if (cfile->fp) {
|
|
auto tmp_offset = ftell(cfile->fp) - cfile->lib_offset;
|
|
Assert(tmp_offset==cfile->raw_position);
|
|
}
|
|
#endif
|
|
|
|
return items_read;
|
|
|
|
}
|