scummvm/engines/bagel/boflib/file.cpp
2024-06-14 13:25:39 +01:00

295 lines
7.3 KiB
C++

/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* 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/>.
*
*/
#include "common/macresman.h"
#include "common/stream.h"
#include "common/system.h"
#include "common/savefile.h"
#include "bagel/bagel.h"
#include "bagel/boflib/file.h"
#include "bagel/boflib/file_functions.h"
#include "bagel/boflib/debug.h"
#include "bagel/boflib/log.h"
namespace Bagel {
#define CHUNK_SIZE 0x00007FFF
CBofFile::CBofFile() {
_szFileName[0] = '\0';
}
CBofFile::CBofFile(const char *pszFileName, uint32 lFlags) {
_szFileName[0] = '\0';
assert(pszFileName != nullptr);
// Open now?
if (pszFileName != nullptr) {
open(pszFileName, lFlags);
}
}
CBofFile::~CBofFile() {
assert(isValidObject(this));
close();
}
ErrorCode CBofFile::create(const char *pszFileName, uint32 lFlags) {
assert(isValidObject(this));
assert(pszFileName != nullptr);
assert(strlen(pszFileName) < MAX_DIRPATH);
assert(*pszFileName != '\0');
// Can't create a read-only file
assert(!(lFlags & CBF_READONLY));
_lFlags = lFlags;
// Remember this files name
Common::strcpy_s(_szFileName, pszFileName);
// Create the file
Common::OutSaveFile *save = g_system->getSavefileManager()->openForSaving(pszFileName, false);
if (save != nullptr) {
_stream = new SaveReadWriteStream(save);
if (g_pDebugOptions != nullptr && g_pDebugOptions->_bShowIO) {
logInfo(buildString("Creating file '%s'", _szFileName));
}
} else {
reportError(ERR_FOPEN, "Unable to create %s", _szFileName);
}
return _errCode;
}
ErrorCode CBofFile::open(const char *pszFileName, uint32 lFlags) {
assert(isValidObject(this));
assert(pszFileName != nullptr);
assert(strlen(pszFileName) < MAX_DIRPATH);
assert(*pszFileName != '\0');
// Can't open for both Text and Binary modes
assert(!((lFlags & CBF_TEXT) && (lFlags & CBF_BINARY)));
// Can't overwrite a readonly file
assert(!((lFlags & CBF_READONLY) && (lFlags & CBF_OVERWRITE)));
// Can't create a new file to be readonly (there would be nothing to read!)
assert(!((lFlags & CBF_READONLY) && (lFlags & CBF_CREATE)));
// Keep a copy of these flags
_lFlags = lFlags;
if (_stream)
return _errCode;
if ((lFlags & CBF_CREATE) && ((lFlags & CBF_SAVEFILE) ||
!fileExists(pszFileName))) {
create(pszFileName, lFlags);
} else {
// Remember this files' name
Common::strcpy_s(_szFileName, pszFileName);
if (lFlags & CBF_SAVEFILE) {
_stream = g_system->getSavefileManager()->openForLoading(pszFileName);
if (!_stream)
reportError(ERR_FOPEN, "Could not open %s", pszFileName);
} else {
if (g_engine->getPlatform() == Common::kPlatformMacintosh) {
_stream = Common::MacResManager::openFileOrDataFork(pszFileName);
} else {
_stream = SearchMan.createReadStreamForMember(pszFileName);
}
if (_stream) {
if (g_pDebugOptions != nullptr && g_pDebugOptions->_bShowIO) {
logInfo(buildString("Opened file '%s'", _szFileName));
}
} else {
reportError(ERR_FOPEN, "Could not open %s", pszFileName);
}
}
}
return _errCode;
}
ErrorCode CBofFile::close() {
assert(isValidObject(this));
if (_stream != nullptr) {
if (g_pDebugOptions != nullptr && g_pDebugOptions->_bShowIO) {
logInfo(buildString("Closed file '%s'", _szFileName));
}
delete _stream;
_stream = nullptr;
}
return ERR_NONE;
}
ErrorCode CBofFile::read(void *pDestBuf, int32 lBytes) {
assert(isValidObject(this));
assert(pDestBuf != nullptr);
assert(lBytes >= 0);
Common::SeekableReadStream *rs = dynamic_cast<Common::SeekableReadStream *>(_stream);
assert(rs);
if (!errorOccurred()) {
if (rs != nullptr) {
byte *pBuf = (byte *)pDestBuf;
while (lBytes > 0) {
int nLength = (int)MIN(lBytes, (int32)CHUNK_SIZE);
lBytes -= CHUNK_SIZE;
if ((int)rs->read(pBuf, nLength) != nLength) {
reportError(ERR_FREAD, "Unable to read %d bytes from %s", nLength, _szFileName);
}
pBuf += nLength;
}
} else {
error("Attempt to read from a file that is not open for reading: %s", _szFileName);
}
}
return _errCode;
}
ErrorCode CBofFile::write(const void *pSrcBuf, int32 lBytes) {
assert(isValidObject(this));
Common::WriteStream *ws = dynamic_cast<Common::WriteStream *>(_stream);
if (ws != nullptr) {
// As long as this file is not set for readonly, then write the buffer
if (!(_lFlags & CBF_READONLY)) {
const byte *pBuf = (const byte *)pSrcBuf;
while (lBytes > 0) {
int nLength = (int)MIN(lBytes, (int32)CHUNK_SIZE);
lBytes -= CHUNK_SIZE;
if ((int)ws->write(pBuf, nLength) != nLength) {
reportError(ERR_FWRITE, "Unable to write %d bytes to %s", nLength, _szFileName);
}
pBuf += nLength;
}
// Flush this file's buffer back out right now
commit();
} else {
logWarning(buildString("Attempted to write to the READONLY file '%s'", _szFileName));
}
} else {
logWarning("Attempt to write to a file that is not open");
}
return _errCode;
}
ErrorCode CBofFile::setPosition(uint32 lPos) {
assert(isValidObject(this));
// Only supports files up to 2Gig
assert(lPos < 0x80000000);
Common::SeekableReadStream *rs = dynamic_cast<Common::SeekableReadStream *>(_stream);
Common::SeekableWriteStream *ws = dynamic_cast<Common::SeekableWriteStream *>(_stream);
if (rs && !rs->seek(lPos)) {
reportError(ERR_FSEEK, "Unable to seek to %u in rs", lPos);
}
if (ws && !ws->seek(lPos)) {
reportError(ERR_FSEEK, "Unable to seek to %u in ws", lPos);
}
return _errCode;
}
uint32 CBofFile::getPosition() {
assert(isValidObject(this));
Common::SeekableReadStream *rs = dynamic_cast<Common::SeekableReadStream *>(_stream);
Common::SeekableWriteStream *ws = dynamic_cast<Common::SeekableWriteStream *>(_stream);
if (rs)
return rs->pos();
if (ws)
return ws->pos();
error("getPosition on closed file");
}
ErrorCode CBofFile::seekToEnd() {
assert(isValidObject(this));
Common::SeekableReadStream *rs = dynamic_cast<Common::SeekableReadStream *>(_stream);
Common::SeekableWriteStream *ws = dynamic_cast<Common::SeekableWriteStream *>(_stream);
if (rs)
rs->seek(0, SEEK_END);
else if (ws)
ws->seek(0, SEEK_END);
else
error("Seek in closed file");
return _errCode;
}
uint32 CBofFile::getLength() {
assert(isValidObject(this));
Common::SeekableReadStream *rs = dynamic_cast<Common::SeekableReadStream *>(_stream);
Common::SeekableWriteStream *ws = dynamic_cast<Common::SeekableWriteStream *>(_stream);
if (rs)
return rs->size();
if (ws)
return ws->size();
error("getLength in closed file");
}
void CBofFile::commit() {
assert(isValidObject(this));
Common::SeekableWriteStream *ws = dynamic_cast<Common::SeekableWriteStream *>(_stream);
if (ws)
ws->finalize();
}
} // namespace Bagel