- BORLAND/: Borland C++ 4.52 (chosen over 4.5 by byte-match: CODE/RP/CW32.LIB
is identical to 4.52's install lib). BCC32/TLINK32/TLIB/MAKE run natively on
Win11; CODE/BT/OPT.MAK is the shipped BTL4OPT.EXE's exact flag recipe
(extender = Borland PowerPack DPMI32, not Phar Lap TNT).
- restoration/source410/: the literal 1995-form reconstruction of the missing
BT game source (never mixed into CODE/). Round 1-3 state:
* 6 of 10 surviving original TUs COMPILE CLEAN under the period toolchain
(BTMSSN, BTCNSL, BTSCNRL, BTTEAM, BTL4MODE, BTL4ARND) - first builds
since 1996.
* BT_L4/BTL4APP.CPP pilot reconstruction: 12/12 functions, Fail() lands on
its binary-recorded line 400 exactly.
* BT/BTCNSL.HPP: console wire IDs recovered from the binary's ctors
(Killed=9, Damaged=10, ScoreUpdate=13, DeathWithoutHonor=15 [T1];
TeamScore=12 flagged [T4]).
* MUNGA/: 8 engine-header backfills back-dated from the BT412 WinTesla tree
(VDATA numbering decomp-verified; AUDREND's OpenAL-era virtual removed -
the period compiler is the drift detector).
* Tooling: backdate.py (WinTesla->1995 header transform), compile410.sh
(per-TU verification sweep under authentic OPT.MAK flags).
* README: corrected roadmap - MECH.HPP is the capstone grown with the mech
TU reconstructions; BTREG.CPP green = the header-family milestone.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
622 lines
16 KiB
C++
622 lines
16 KiB
C++
//----------------------------------------------------------------------------
|
|
// ObjectWindows
|
|
// (C) Copyright 1993, 1994 by Borland International, All Rights Reserved
|
|
//
|
|
// Implements classes TFileDocument, TFileInStream, TFileOutStream, TFileBuf
|
|
//----------------------------------------------------------------------------
|
|
#pragma hdrignore SECTION
|
|
#include <owl/owlpch.h>
|
|
#include <owl/filedoc.h>
|
|
|
|
#if !defined(SECTION) || SECTION == 1
|
|
|
|
//
|
|
// class TFileBuf
|
|
// ----- --------
|
|
//
|
|
class _OWLCLASS_RTL TFileBuf : public streambuf {
|
|
public:
|
|
enum { shDefault = shReadWrite }; // default file sharing
|
|
|
|
TFileBuf(int fhdl, int omode, streampos seekpos);
|
|
~TFileBuf() {}
|
|
|
|
virtual int overflow(int = EOF);
|
|
virtual int underflow();
|
|
virtual int sync();
|
|
virtual streampos seekoff(streamoff, ios::seek_dir, int);
|
|
|
|
int xfd; // the file descriptor, EOF if closed
|
|
int mode; // the opened mode
|
|
streampos last_seek;
|
|
char lahead[2]; // current input char if unbuffered
|
|
};
|
|
|
|
//
|
|
// class TFileStreamBase
|
|
// ----- ---------------
|
|
//
|
|
class _OWLCLASS_RTL TFileStreamBase : virtual public ios {
|
|
public:
|
|
TFileStreamBase(int fhdl, int omode, streampos seekpos);
|
|
~TFileStreamBase() {}
|
|
TFileBuf buf;
|
|
};
|
|
|
|
//
|
|
// class TFileInStream
|
|
// ----- -------------
|
|
//
|
|
class _OWLCLASS_RTL TFileInStream : public TFileStreamBase, public TInStream {
|
|
public:
|
|
TFileInStream(TFileDocument& doc, HFILE fhdl, int omode, streampos seekpos)
|
|
: TInStream (doc,0,omode), TFileStreamBase(fhdl,omode,seekpos){}
|
|
~TFileInStream();
|
|
};
|
|
|
|
//
|
|
// class TFileOutStream
|
|
// ----- --------------
|
|
//
|
|
class _OWLCLASS_RTL TFileOutStream : public TFileStreamBase, public TOutStream {
|
|
public:
|
|
TFileOutStream(TFileDocument& doc, HFILE fhdl, int omode, streampos seekpos)
|
|
: TOutStream (doc,0,omode), TFileStreamBase(fhdl,omode,seekpos){}
|
|
~TFileOutStream();
|
|
};
|
|
|
|
//
|
|
// class TFileDocument
|
|
// ----- -------------
|
|
//
|
|
|
|
HFILE
|
|
TFileDocument::OpenThisFile(int omode, const char far* name, streampos* pseekpos){
|
|
int how;
|
|
HFILE fhdl;
|
|
bool exists = false;
|
|
int share = omode & shMask;
|
|
|
|
if (share < shCompat)
|
|
share = (omode & ofWrite) ? shRead : shReadWrite;
|
|
|
|
how = ((unsigned)(share-shCompat)) >> 5;
|
|
|
|
if (omode & ofWrite) {
|
|
if (!(omode & (ofAtEnd | ofAppend | ofRead)))
|
|
omode |= ofTruncate; // output implies truncate unless in, app, or ate
|
|
|
|
if (omode & ofRead)
|
|
how |= OF_READWRITE;
|
|
else
|
|
how |= OF_WRITE;
|
|
if (!((omode & ofNoCreate) && !(omode & ofTruncate))) {
|
|
if (!((omode & ofTruncate) && !(omode & (ofNoCreate | ofNoReplace)))) {
|
|
if ((fhdl=::_lopen(name,OF_READ|OF_SHARE_COMPAT)) !=HFILE_ERROR
|
|
|| (fhdl=::_lopen(name,OF_READ|OF_SHARE_DENY_NONE))!=HFILE_ERROR) {
|
|
::_lclose(fhdl);
|
|
exists = true;
|
|
}
|
|
}
|
|
if (!(exists && !(omode & (ofNoCreate | ofNoReplace)))) {
|
|
if ((exists && (omode & ofNoReplace))
|
|
|| (!exists && (omode & ofNoCreate)) )
|
|
return 0;
|
|
if ((fhdl = _lcreat(name, 0)) == HFILE_ERROR)
|
|
return HFILE_ERROR;
|
|
::_lclose(fhdl); // close in order to open with share mode
|
|
}
|
|
}
|
|
}
|
|
else if (omode & ofRead)
|
|
how |= OF_READ;
|
|
else
|
|
return HFILE_ERROR; // must specfify in, out, or in/out
|
|
|
|
if ((fhdl = ::_lopen(name, how)) != HFILE_ERROR) {
|
|
if ((*pseekpos = ::_llseek(fhdl, 0L, (omode & (ofAtEnd|ofAppend))
|
|
? SEEK_END : SEEK_SET)) == long(HFILE_ERROR)) {
|
|
::_lclose(fhdl);
|
|
return HFILE_ERROR;
|
|
}
|
|
}
|
|
#if defined(BI_PLAT_WIN32)
|
|
FileLength = GetFileSize((HANDLE)fhdl, 0);
|
|
InfoPresent = GetFileTime((HANDLE)fhdl, &FileCreateTime, &FileAccessTime,
|
|
&FileUpdateTime);
|
|
#else
|
|
FileLength = filelength(fhdl);
|
|
getftime(fhdl, (struct ftime*)&FileTime);
|
|
InfoPresent = true;
|
|
#endif
|
|
NotifyViews(vnDocOpened,omode);
|
|
return fhdl;
|
|
}
|
|
|
|
void
|
|
TFileDocument::CloseThisFile(HFILE fhdl, int omode)
|
|
{
|
|
if (!IS_PREV_OPEN(omode)) {
|
|
::_lclose(fhdl);
|
|
NotifyViews(vnDocClosed, omode);
|
|
}
|
|
}
|
|
|
|
bool
|
|
TFileDocument::Open(int omode, const char far* path)
|
|
{
|
|
long seekpos;
|
|
if (FHdl != HFILE_ERROR)
|
|
return false; // if already open at document level
|
|
if (path)
|
|
SetDocPath(path);
|
|
if (omode != 0)
|
|
SetOpenMode(omode);
|
|
if ((FHdl = OpenThisFile(GetOpenMode(),GetDocPath(),&seekpos)) == HFILE_ERROR)
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
TFileDocument::Open(HFILE fhdl)
|
|
{
|
|
SetOpenMode(PREV_OPEN | ofReadWrite); // can we determine open mode?
|
|
SetDocPath(0); // can we get path name?
|
|
FHdl = fhdl;
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
TFileDocument::Close()
|
|
{
|
|
if (!TDocument::Close()) // close all children first
|
|
return false;
|
|
if (FHdl != HFILE_ERROR) { // if open at document level
|
|
if (TDocument::IsOpen()) // cannot close document if streams open
|
|
return false; // ?should we close streams here?
|
|
CloseThisFile(FHdl, GetOpenMode());
|
|
FHdl = HFILE_ERROR;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
TFileDocument::Commit(bool force)
|
|
{
|
|
if (!TDocument::Commit(force)) // flush views and child docs
|
|
return false;
|
|
SetDirty(false);
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
TFileDocument::Revert(bool clear)
|
|
{
|
|
if (!TDocument::Revert(clear))
|
|
return false;
|
|
SetDirty(false);
|
|
return true;
|
|
}
|
|
|
|
static char* PropNames[] = {
|
|
"Create Time", // CreateTime
|
|
"Modify Time", // ModifyTime
|
|
"Access Time", // AccessTime
|
|
"Storage Size", // StorageSize
|
|
"File Handle", // FileHandle
|
|
};
|
|
|
|
static int PropFlags[] = {
|
|
pfGetBinary|pfGetText, // CreateTime
|
|
pfGetBinary|pfGetText, // ModifyTime
|
|
pfGetBinary|pfGetText, // AccessTime
|
|
pfGetBinary|pfGetText, // StorageSize
|
|
pfGetBinary, // FileHandle
|
|
};
|
|
|
|
const char*
|
|
TFileDocument::PropertyName(int index)
|
|
{
|
|
if (index <= PrevProperty)
|
|
return TDocument::PropertyName(index);
|
|
else if (index < NextProperty)
|
|
return PropNames[index-PrevProperty-1];
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
TFileDocument::PropertyFlags(int index)
|
|
{
|
|
if (index <= PrevProperty)
|
|
return TDocument::PropertyFlags(index);
|
|
else if (index < NextProperty)
|
|
return PropFlags[index-PrevProperty-1];
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
TFileDocument::FindProperty(const char far* name)
|
|
{
|
|
int i;
|
|
for (i=0; i < NextProperty-PrevProperty-1; i++)
|
|
if (strcmp(PropNames[i], name) == 0)
|
|
return i+PrevProperty+1;
|
|
return TDocument::FindProperty(name);
|
|
}
|
|
|
|
int
|
|
TFileDocument::GetProperty(int prop, void far* dest, int textlen)
|
|
{
|
|
switch(prop) {
|
|
case FileHandle:
|
|
if (textlen)
|
|
return 0;
|
|
*(HFILE FAR*)dest = FHdl;
|
|
return sizeof(FHdl);
|
|
default:
|
|
if (InfoPresent) {
|
|
switch(prop) {
|
|
case StorageSize:
|
|
if (!textlen) {
|
|
*(unsigned long FAR*)dest = FileLength;
|
|
return sizeof(FileLength);
|
|
}
|
|
else {
|
|
char buf[10];
|
|
int len = wsprintf(buf, "%ld", FileLength);
|
|
if (textlen > len)
|
|
textlen = len;
|
|
memcpy(dest, buf, textlen);
|
|
*((char far*)dest + textlen) = 0;
|
|
return len;
|
|
}
|
|
#if defined(BI_PLAT_WIN32)
|
|
case CreateTime:
|
|
return FormatFileTime(&FileCreateTime, dest, textlen);
|
|
case ModifyTime:
|
|
return FormatFileTime(&FileUpdateTime, dest, textlen);
|
|
case AccessTime:
|
|
return FormatFileTime(&FileAccessTime, dest, textlen);
|
|
|
|
#else // DOS file system
|
|
case CreateTime:
|
|
case ModifyTime:
|
|
case AccessTime: {
|
|
struct date propdate;
|
|
struct time proptime;
|
|
propdate.da_year = ((struct ftime*)&FileTime)->ft_year+1980;
|
|
propdate.da_mon = ((struct ftime*)&FileTime)->ft_month;
|
|
propdate.da_day = ((struct ftime*)&FileTime)->ft_day;
|
|
proptime.ti_hour = ((struct ftime*)&FileTime)->ft_hour;
|
|
proptime.ti_min = ((struct ftime*)&FileTime)->ft_min;
|
|
proptime.ti_sec = ((struct ftime*)&FileTime)->ft_tsec*2;
|
|
proptime.ti_hund = 0;
|
|
return FormatDateTime(propdate, proptime, dest, textlen);
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
return TDocument::GetProperty(prop, dest, textlen);
|
|
}
|
|
}
|
|
|
|
bool
|
|
TFileDocument::SetProperty(int prop, const void far* src)
|
|
{
|
|
// File properties currently not settable
|
|
//
|
|
return TDocument::SetProperty(prop, src);
|
|
}
|
|
|
|
TInStream*
|
|
TFileDocument::InStream(int omode, const char far* /*strmId*/)
|
|
{
|
|
HFILE fhdl;
|
|
streampos seekpos;
|
|
if (omode == ofParent)
|
|
omode = GetOpenMode();
|
|
if (!(omode & ofRead))
|
|
return 0;
|
|
if ((fhdl = FHdl) == HFILE_ERROR) { // if file not open at document level
|
|
if ((fhdl=OpenThisFile (omode, GetDocPath(), &seekpos)) == HFILE_ERROR)
|
|
return 0;
|
|
}
|
|
else {
|
|
omode = GetOpenMode() ? (GetOpenMode() & ~ofBinary) | (omode & ofBinary)
|
|
: omode;
|
|
omode |= PREV_OPEN;
|
|
}
|
|
return new TFileInStream(*this, fhdl, omode, seekpos);
|
|
}
|
|
|
|
TOutStream*
|
|
TFileDocument::OutStream(int omode, const char far* /*strmId*/)
|
|
{
|
|
HFILE fhdl;
|
|
streampos seekpos;
|
|
if (omode == ofParent)
|
|
omode = GetOpenMode();
|
|
if (!(omode & ofWrite))
|
|
return 0;
|
|
if ((fhdl = FHdl) == HFILE_ERROR) { // if file not open at document level
|
|
if ((fhdl=OpenThisFile (omode, GetDocPath(), &seekpos)) == HFILE_ERROR)
|
|
return 0;
|
|
}
|
|
else {
|
|
omode = GetOpenMode() ? (GetOpenMode() & ~ofBinary) | (omode & ofBinary)
|
|
: omode;
|
|
omode |= PREV_OPEN;
|
|
}
|
|
return new TFileOutStream(*this, fhdl, omode, seekpos);
|
|
}
|
|
|
|
TFileInStream::~TFileInStream()
|
|
{
|
|
((TFileDocument&)Doc).CloseThisFile(buf.xfd, GetOpenMode());
|
|
}
|
|
|
|
TFileOutStream::~TFileOutStream()
|
|
{
|
|
if (buf.out_waiting())
|
|
buf.overflow(EOF);
|
|
((TFileDocument&)Doc).CloseThisFile(buf.xfd, GetOpenMode());
|
|
}
|
|
|
|
//
|
|
// class TFileBuf
|
|
// ----- --------
|
|
//
|
|
const int B_size = 516; // natural size for a file buffer, plus 4 for putback
|
|
|
|
// make a TFileBuf attached to an open fd
|
|
|
|
TFileBuf::TFileBuf(int fhdl, int omode, long seekpos)
|
|
{
|
|
xfd = fhdl; // assumed to be valid
|
|
mode = omode; // this may not represent the actual mode opened previously
|
|
last_seek = seekpos;
|
|
char* p = new char[B_size];
|
|
if (p) {
|
|
setb(p, p+B_size, 1); // ~streambuf() will delete buffer
|
|
setp(p+4, p+4);
|
|
setg(p, p+4, p+4);
|
|
}
|
|
}
|
|
|
|
// Seek file to position.
|
|
// We take a simple approach, and don't check for small position changes
|
|
// within the current buffer.
|
|
|
|
streampos TFileBuf::seekoff(streamoff off, ios::seek_dir dir, int /*mode*/)
|
|
{
|
|
long loff = off;
|
|
if (out_waiting()) { // flush the output
|
|
if (sync() == EOF)
|
|
return EOF;
|
|
}
|
|
else if (dir == ios::cur) {
|
|
int count = in_avail();
|
|
if (count != 0) {
|
|
loff -= count;
|
|
|
|
// if we're in text mode, need to allow for newlines
|
|
// in the buffer
|
|
if ((mode & ofBinary) == 0) {
|
|
char *tptr = gptr();
|
|
while (tptr != egptr())
|
|
if (*tptr++ == '\n')
|
|
loff--;
|
|
}
|
|
}
|
|
}
|
|
int w = (dir==ios::beg) ? SEEK_SET : ((dir==ios::cur) ? SEEK_CUR:SEEK_END);
|
|
last_seek = ::_llseek(xfd, loff, w);
|
|
if (!unbuffered() && base()) { // set up get and put areas
|
|
int pb = (blen() > 8) ? 4 : 1; // putback area size
|
|
char *b = base();
|
|
setp(b+pb, b+pb);
|
|
setg(b, b+pb, b+pb);
|
|
}
|
|
return (last_seek == long(HFILE_ERROR)) ? EOF : last_seek;
|
|
}
|
|
|
|
int TFileBuf::sync()
|
|
{
|
|
int count = out_waiting();
|
|
if (count) {
|
|
char* curp;
|
|
char* srcp = pbase();
|
|
char* endp = srcp + count;
|
|
|
|
// Convert LF's to CR/LF if text mode
|
|
//
|
|
if ((mode & ofBinary) == 0) {
|
|
for (curp = srcp; curp < endp; curp++) {
|
|
if (*curp == '\n') {
|
|
*curp = '\r';
|
|
count = (int)(curp - srcp) + 1;
|
|
if (::_lwrite(xfd, srcp, count) != count)
|
|
return EOF;
|
|
*(srcp = curp) = '\n';
|
|
}
|
|
}
|
|
count = (int)(curp - srcp); // write what remains in the buffer below
|
|
}
|
|
if (::_lwrite(xfd, srcp, count) != count)
|
|
return EOF;
|
|
|
|
// reset get and put areas
|
|
int pb = (blen() > 8) ? 4 : 1; // putback area size
|
|
char *b = base();
|
|
setp(b+pb, b+blen());
|
|
setg(b, b+pb, b+pb);
|
|
}
|
|
else if (in_avail()) {
|
|
last_seek = ::_llseek(xfd, long(-in_avail()), SEEK_CUR);
|
|
setg(eback(), gptr(), gptr());
|
|
setp(gptr(), gptr());
|
|
if (last_seek == long(HFILE_ERROR))
|
|
return EOF;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int TFileBuf::underflow()
|
|
{
|
|
if ((mode & (ofRead | ofWrite)) == ofWrite)
|
|
return EOF;
|
|
|
|
if (in_avail()) // no action needed
|
|
return (unsigned char) *gptr();
|
|
|
|
int c = 0; // the return value
|
|
int count; // input character count
|
|
|
|
if (!unbuffered() && base()) { // this is buffered
|
|
if (sync() != 0)
|
|
return EOF;
|
|
|
|
// find buffer data
|
|
int pb = (blen() > 8) ? 4 : 1; // putback area size
|
|
char* begp = base() + pb;
|
|
|
|
// read in a new buffer
|
|
count = ::_lread(xfd, begp, blen()-pb);
|
|
if (count == HFILE_ERROR)
|
|
return EOF;
|
|
|
|
// remove CR's if text mode
|
|
if ((mode & ofBinary) == 0) {
|
|
char* endp = begp + count;
|
|
char* dstp = 0;
|
|
char* srcp = 0; // initialized only to prevent compiler warning
|
|
char* curp;
|
|
|
|
for (curp = begp; curp < endp; curp++) {
|
|
if (*curp == '\r') {
|
|
if (dstp) {
|
|
memcpy(dstp, srcp, (int)(curp - srcp));
|
|
dstp += (int)(curp - srcp);
|
|
}
|
|
else
|
|
dstp = curp;
|
|
srcp = curp + 1;
|
|
}
|
|
}
|
|
if (dstp) {
|
|
endp = dstp + (int)(curp - srcp);
|
|
if (curp != srcp)
|
|
memcpy(dstp, srcp, (int)(curp - srcp));
|
|
}
|
|
count = (int)(endp - begp);
|
|
}
|
|
// set up get and put areas
|
|
setg(base(), begp, begp + count);
|
|
setp(begp, begp);
|
|
|
|
if (count)
|
|
c = (unsigned char) *gptr();
|
|
}
|
|
else { // this is not buffered
|
|
for (;;) {
|
|
count = ::_lread(xfd, lahead, 1);
|
|
if (count == HFILE_ERROR) {
|
|
c = EOF;
|
|
setg(0, 0, 0);
|
|
}
|
|
else {
|
|
c = (unsigned char)lahead[0];
|
|
if ((mode & ofBinary) == 0 && c == '\r')
|
|
continue;
|
|
setg(lahead, lahead, lahead+1);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
if (!count)
|
|
c = EOF; // end of file
|
|
return c;
|
|
}
|
|
|
|
// always flush
|
|
|
|
int TFileBuf::overflow(int c)
|
|
{
|
|
if ((mode & (ofRead | ofWrite)) == ofRead)
|
|
return EOF;
|
|
|
|
if (unbuffered() || !base()) {
|
|
if (c != EOF) {
|
|
int count;
|
|
char b[2];
|
|
if (c == '\n' && (mode & ofBinary) == 0) {
|
|
b[0] = '\r';
|
|
b[1] = (char)c;
|
|
count = 2;
|
|
}
|
|
else {
|
|
b[0] = (char)c;
|
|
count = 1;
|
|
}
|
|
if (::_lwrite(xfd, b, count) != count)
|
|
return EOF;
|
|
}
|
|
}
|
|
else { // now we know this is buffered and state is not bad
|
|
|
|
// resets get and put areas
|
|
if (sync() != 0)
|
|
return EOF;
|
|
|
|
// reset get and put areas
|
|
int pb = (blen() > 8) ? 4 : 1; // putback area size
|
|
char *b = base();
|
|
setp(b+pb, b+blen());
|
|
setg(b, b+pb, b+pb);
|
|
|
|
if (c != EOF) {
|
|
sputc(c);
|
|
gbump(1); // pptr and gptr must be the same
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
//
|
|
// class TFileStreamBase
|
|
// ----- ---------------
|
|
//
|
|
|
|
TFileStreamBase::TFileStreamBase(int fhdl, int omode, streampos seekpos)
|
|
: buf(fhdl, omode, seekpos)
|
|
{
|
|
ios::init(&buf);
|
|
}
|
|
|
|
#endif
|
|
|
|
#if !defined(SECTION) || SECTION == 2
|
|
|
|
IMPLEMENT_STREAMABLE1(TFileDocument, TDocument);
|
|
|
|
void*
|
|
TFileDocument::Streamer::Read(ipstream& is, uint32 /*version*/) const
|
|
{
|
|
TFileDocument* o = GetObject();
|
|
o->FHdl = HFILE_ERROR; // initialize to closed file
|
|
o->InfoPresent = false;
|
|
ReadBaseObject((TDocument*)o, is);
|
|
return o;
|
|
}
|
|
|
|
void
|
|
TFileDocument::Streamer::Write(opstream& os) const
|
|
{
|
|
// assumed that document is committed at this point
|
|
WriteBaseObject((TDocument*)GetObject(), os);
|
|
}
|
|
|
|
#endif
|