diff --git a/LEGO1/mxioinfo.cpp b/LEGO1/mxioinfo.cpp index 2d55ef36..c834f33d 100644 --- a/LEGO1/mxioinfo.cpp +++ b/LEGO1/mxioinfo.cpp @@ -1,9 +1,15 @@ #include "mxioinfo.h" +#include "decomp.h" + +// This class should be 72 bytes in size, same as the MMIOINFO struct. +// The current implementation has MMIOINFO as the only member of the class, +// but this assert will enforce the size if we decide to change that. +DECOMP_SIZE_ASSERT(MXIOINFO, sizeof(MMIOINFO)); // OFFSET: LEGO1 0x100cc800 MXIOINFO::MXIOINFO() { - memset(&m_info, 0, sizeof(MMIOINFO)); + memset(&m_info, 0, sizeof(m_info)); } // OFFSET: LEGO1 0x100cc820 @@ -13,37 +19,427 @@ MXIOINFO::~MXIOINFO() } // OFFSET: LEGO1 0x100cc830 -MxU16 MXIOINFO::Open(const char *filename, DWORD fdwOpen) +MxU16 MXIOINFO::Open(const char *p_filename, MxULong p_flags) { - return 0; + OFSTRUCT _unused; + MxU16 result = 0; + + m_info.lBufOffset = 0; + m_info.lDiskOffset = 0; + + // Cast of p_flags to u16 forces the `movzx` instruction + m_info.hmmio = (HMMIO)OpenFile(p_filename, &_unused, (MxU16)p_flags); + + if ((HFILE)m_info.hmmio != HFILE_ERROR) { + m_info.dwFlags = p_flags; + if (p_flags & MMIO_ALLOCBUF) { + + // Default buffer length of 8k if none specified + int len = m_info.cchBuffer ? m_info.cchBuffer : 8192; + HPSTR buf = new char[len]; + + if (!buf) { + result = MMIOERR_OUTOFMEMORY; + m_info.cchBuffer = 0; + m_info.dwFlags &= ~MMIO_ALLOCBUF; + m_info.pchBuffer = 0; + } else { + m_info.pchBuffer = buf; + m_info.cchBuffer = len; + } + + m_info.pchEndRead = m_info.pchBuffer; + m_info.pchNext = m_info.pchBuffer; + m_info.pchEndWrite = m_info.pchBuffer + m_info.cchBuffer; + } + } else { + result = MMIOERR_CANNOTOPEN; + } + + return result; } // OFFSET: LEGO1 0x100cc8e0 -void MXIOINFO::Close(MxLong arg) +MxU16 MXIOINFO::Close(MxLong p_unused) { - + MxU16 result = 0; + + if (m_info.hmmio) { + result = Flush(0); + _lclose((HFILE)m_info.hmmio); + m_info.hmmio = 0; + + if (m_info.dwFlags & MMIO_ALLOCBUF) + delete[] m_info.pchBuffer; + + m_info.pchEndWrite = 0; + m_info.pchEndRead = 0; + m_info.pchBuffer = 0; + m_info.dwFlags = 0; + } + + return result; } // OFFSET: LEGO1 0x100cc930 -MxULong MXIOINFO::Read(HPSTR pch, LONG cch) +MxLong MXIOINFO::Read(void *p_buf, MxLong p_len) { - return 0; + MxLong bytes_read = 0; + + if (m_info.pchBuffer) { + + int bytes_left = m_info.pchEndRead - m_info.pchNext; + while (p_len > 0) { + + if (bytes_left > 0) { + if (p_len < bytes_left) + bytes_left = p_len; + + memcpy(p_buf, m_info.pchNext, bytes_left); + p_len -= bytes_left; + + m_info.pchNext += bytes_left; + bytes_read += bytes_left; + } + + if (p_len <= 0 || Advance(0)) + break; + + bytes_left = m_info.pchEndRead - m_info.pchNext; + if (bytes_left <= 0) + break; + } + } else if (m_info.hmmio && p_len > 0) { + bytes_read = _hread((HFILE)m_info.hmmio, p_buf, p_len); + + if (bytes_read == -1) { + bytes_read = 0; + m_info.lDiskOffset = _llseek((HFILE)m_info.hmmio, 0, SEEK_CUR); + } else { + m_info.lDiskOffset += bytes_read; + } + } + + return bytes_read; } // OFFSET: LEGO1 0x100cca00 -LONG MXIOINFO::Seek(LONG lOffset, int iOrigin) +MxLong MXIOINFO::Seek(MxLong p_offset, int p_origin) { - return 0; + MxLong result = -1; + + // If buffered I/O + if (m_info.pchBuffer) { + if (p_origin == SEEK_CUR) { + if (!p_offset) { + // don't seek at all and just return where we are. + return m_info.lBufOffset + (m_info.pchNext - m_info.pchBuffer); + } else { + // With SEEK_CUR, p_offset is a relative offset. + // Get the absolute position instead and use SEEK_SET. + p_offset += m_info.lBufOffset + (m_info.pchNext - m_info.pchBuffer); + p_origin = SEEK_SET; + } + } else if (p_origin == SEEK_END) { + // not possible with buffered I/O + return -1; + } + + // else p_origin == SEEK_SET. + + // is p_offset between the start and end of the buffer? + // i.e. can we do the seek without reading more from disk? + if (p_offset >= m_info.lBufOffset && p_offset < m_info.lBufOffset + m_info.cchBuffer) { + m_info.pchNext = m_info.pchBuffer + (p_offset - m_info.lBufOffset); + result = p_offset; + } else { + // we have to read another chunk from disk. + if (m_info.hmmio && !Flush(0)) { + m_info.lDiskOffset = _llseek((HFILE)m_info.hmmio, p_offset, p_origin); + + if (m_info.lDiskOffset == -1) { + m_info.lDiskOffset = _llseek((HFILE)m_info.hmmio, 0, SEEK_CUR); + } else { + + // align offset to buffer size + int new_offset = p_offset - (p_offset % m_info.cchBuffer); + m_info.lBufOffset = new_offset; + + // do we need to seek again? + // (i.e. are we already aligned to buffer size?) + if (p_offset != new_offset) { + m_info.lDiskOffset = _llseek((HFILE)m_info.hmmio, new_offset, SEEK_SET); + + if (m_info.lDiskOffset == -1) { + m_info.lDiskOffset = _llseek((HFILE)m_info.hmmio, 0, SEEK_CUR); + } + } + + if (m_info.lBufOffset == m_info.lDiskOffset) { + // is the file open for writing only? + if ((m_info.dwFlags & MMIO_RWMODE) && + ((m_info.dwFlags & MMIO_RWMODE) != MMIO_READWRITE)) { + + m_info.pchNext = m_info.pchBuffer - m_info.lBufOffset + p_offset; + + result = p_offset; + } else { + // We can read from the file. Fill the buffer. + int bytes_read = _hread((HFILE)m_info.hmmio, m_info.pchBuffer, m_info.cchBuffer); + + if (bytes_read == -1) { + m_info.lDiskOffset = _llseek((HFILE)m_info.hmmio, 0, SEEK_CUR); + } else { + m_info.lDiskOffset += bytes_read; + m_info.pchNext = p_offset - m_info.lBufOffset + m_info.pchBuffer; + m_info.pchEndRead = m_info.pchBuffer + bytes_read; + + if (m_info.pchNext < m_info.pchEndRead) { + result = p_offset; + } + } + } + } + } + } + } + } else { + // No buffer so just seek the file directly (if we have a valid handle) + if (m_info.hmmio) { + // i.e. if we just want to get the current file position + if (p_origin == SEEK_CUR && p_offset == 0) { + return m_info.lDiskOffset; + } else { + m_info.lDiskOffset = _llseek((HFILE)m_info.hmmio, p_offset, p_origin); + + result = m_info.lDiskOffset; + + if (result == -1) { + m_info.lDiskOffset = _llseek((HFILE)m_info.hmmio, 0, SEEK_CUR); + } + } + } + } + + return result; } // OFFSET: LEGO1 0x100ccbc0 -void MXIOINFO::SetBuffer(LPSTR pchBuffer, LONG cchBuffer, LONG unk) +MxU16 MXIOINFO::SetBuffer(char *p_buf, MxLong p_len, MxLong p_unused) { - + MxU16 result = Flush(0); + + if (m_info.dwFlags & MMIO_ALLOCBUF) { + m_info.dwFlags &= ~MMIO_ALLOCBUF; + delete[] m_info.pchBuffer; + } + + m_info.pchBuffer = p_buf; + m_info.cchBuffer = p_len; + m_info.pchEndWrite = m_info.pchBuffer + m_info.cchBuffer; + m_info.pchEndRead = m_info.pchBuffer; + + return result; +} + +// OFFSET: LEGO1 0x100ccc10 +MxU16 MXIOINFO::Flush(MxU16 p_unused) +{ + MxU16 result = 0; + + // if buffer is dirty + if (m_info.dwFlags & MMIO_DIRTY) { + // if we have allocated an IO buffer + if (m_info.pchBuffer) { + // if we have a file open for writing + if (m_info.hmmio && (m_info.dwFlags & MMIO_RWMODE)) { + // (pulling this value out into a variable forces it into EBX) + MxLong cchBuffer = m_info.cchBuffer; + if (cchBuffer > 0) { + if (m_info.lBufOffset != m_info.lDiskOffset) { + m_info.lDiskOffset = _llseek((HFILE)m_info.hmmio, m_info.lBufOffset, SEEK_SET); + } + + // Was the previous seek (if required) successful? + if (m_info.lBufOffset != m_info.lDiskOffset) { + result = MMIOERR_CANNOTSEEK; + m_info.lDiskOffset = _llseek((HFILE)m_info.hmmio, 0, SEEK_CUR); + } else { + MxLong bytes_written = _hwrite((HFILE)m_info.hmmio, m_info.pchBuffer, cchBuffer); + + if (bytes_written != -1 && bytes_written == cchBuffer) { + m_info.lDiskOffset += bytes_written; + m_info.pchNext = m_info.pchBuffer; + m_info.dwFlags &= ~MMIO_DIRTY; + } else { + result = MMIOERR_CANNOTWRITE; + m_info.lDiskOffset = _llseek((HFILE)m_info.hmmio, 0, SEEK_CUR); + } + } + } + } else { + result = MMIOERR_CANNOTWRITE; + } + } else { + result = MMIOERR_UNBUFFERED; + } + } + + return result; +} + +// OFFSET: LEGO1 0x100ccd00 +MxU16 MXIOINFO::Advance(MxU16 p_option) +{ + MxU16 result = 0; + MxULong rwmode = m_info.dwFlags & MMIO_RWMODE; + + if (m_info.pchBuffer) { + MxLong cch = m_info.cchBuffer; + + // If we can and should write to the file, + // if we are being asked to write to the file, + // and if there is a buffer *to* write: + if ((rwmode == MMIO_WRITE || rwmode == MMIO_READWRITE) && + (m_info.dwFlags & MMIO_DIRTY) && + ((p_option & MMIO_WRITE) || (rwmode == MMIO_READWRITE)) && + cch > 0) { + + if (m_info.lBufOffset != m_info.lDiskOffset) { + m_info.lDiskOffset = _llseek((HFILE)m_info.hmmio, m_info.lBufOffset, SEEK_SET); + } + + if (m_info.lBufOffset != m_info.lDiskOffset) { + result = MMIOERR_CANNOTSEEK; + } else { + MxLong bytes_written = _hwrite((HFILE)m_info.hmmio, m_info.pchBuffer, cch); + + if (bytes_written != -1 && bytes_written == cch) { + m_info.lDiskOffset += bytes_written; + m_info.pchNext = m_info.pchBuffer; + m_info.pchEndRead = m_info.pchBuffer; + m_info.dwFlags &= ~MMIO_DIRTY; + } else { + result = MMIOERR_CANNOTWRITE; + } + } + + m_info.lDiskOffset = _llseek((HFILE)m_info.hmmio, 0, SEEK_CUR); + + } + + m_info.lBufOffset += cch; + if ((!rwmode || rwmode == MMIO_READWRITE) && cch > 0) { + if (m_info.lBufOffset != m_info.lDiskOffset) { + m_info.lDiskOffset = _llseek((HFILE)m_info.hmmio, m_info.lBufOffset, SEEK_SET); + } + + // if previous seek failed + if (m_info.lBufOffset != m_info.lDiskOffset) { + result = MMIOERR_CANNOTSEEK; + m_info.lDiskOffset = _llseek((HFILE)m_info.hmmio, 0, SEEK_CUR); + } else { + int bytes_read = _hread((HFILE)m_info.hmmio, m_info.pchBuffer, cch); + + if (bytes_read == -1) { + result = MMIOERR_CANNOTREAD; + m_info.lDiskOffset = _llseek((HFILE)m_info.hmmio, 0, SEEK_CUR); + } else { + m_info.lDiskOffset += bytes_read; + m_info.pchNext = m_info.pchBuffer; + m_info.pchEndRead = m_info.pchBuffer + bytes_read; + } + } + } + } else { + result = MMIOERR_UNBUFFERED; + } + + return result; } // OFFSET: LEGO1 0x100cce60 -MxU16 MXIOINFO::Descend(LPMMCKINFO pmmcki, const MMCKINFO *pmmckiParent, UINT fuDescend) +MxU16 MXIOINFO::Descend(MMCKINFO *p_chunkInfo, const MMCKINFO *p_parentInfo, MxU16 p_descend) { - return 0; + MxU16 result = 0; + + if (!p_chunkInfo) + return MMIOERR_BASE; // ? + + if (!p_descend) { + p_chunkInfo->dwFlags = 0; + if (Read(p_chunkInfo, 8) != 8) { + result = MMIOERR_CANNOTREAD; + } else { + if (m_info.pchBuffer) { + p_chunkInfo->dwDataOffset = m_info.pchNext - m_info.pchBuffer + m_info.lBufOffset; + } else { + p_chunkInfo->dwDataOffset = m_info.lDiskOffset; + } + + if (p_chunkInfo->ckid == FOURCC_RIFF || p_chunkInfo->ckid == FOURCC_LIST) { + if (Read(&p_chunkInfo->fccType, 4) != 4) { + result = MMIOERR_CANNOTREAD; + } + } + } + } else { + MxULong ofs = MAXLONG; + + if (p_parentInfo) + ofs = p_parentInfo->cksize + p_parentInfo->dwDataOffset; + + BOOL running = TRUE; + BOOL read_ok = FALSE; + MMCKINFO tmp; + tmp.dwFlags = 0; + + // This loop is... something + do { + if (Read(&tmp, 8) != 8) { + // If the first read fails, report read error. Else EOF. + result = read_ok ? MMIOERR_CHUNKNOTFOUND : MMIOERR_CANNOTREAD; + running = FALSE; + } else { + read_ok = TRUE; + if (m_info.pchBuffer) { + tmp.dwDataOffset = m_info.pchNext - m_info.pchBuffer + m_info.lBufOffset; + } else { + tmp.dwDataOffset = m_info.lDiskOffset; + } + + if (ofs < tmp.dwDataOffset) { + result = MMIOERR_CHUNKNOTFOUND; + running = FALSE; + } else { + if ((p_descend == MMIO_FINDLIST && tmp.ckid == FOURCC_LIST) || + (p_descend == MMIO_FINDRIFF && tmp.ckid == FOURCC_RIFF)) { + if (Read(&tmp.fccType, 4) != 4) { + result = MMIOERR_CANNOTREAD; + } else { + if (p_chunkInfo->fccType != tmp.fccType) + continue; + } + running = FALSE; + } else { + if (p_chunkInfo->ckid != tmp.ckid) { + if (Seek((tmp.cksize&1)+tmp.cksize, SEEK_CUR) != -1) { + continue; + } else { + result = MMIOERR_CANNOTSEEK; + } + } + running = FALSE; + } + } + } + + } while (running); + + if (!result) + memcpy(p_chunkInfo, &tmp, sizeof(MMCKINFO)); + + } + + return result; } diff --git a/LEGO1/mxioinfo.h b/LEGO1/mxioinfo.h index 5ce0947a..83da930a 100644 --- a/LEGO1/mxioinfo.h +++ b/LEGO1/mxioinfo.h @@ -12,13 +12,17 @@ class MXIOINFO MXIOINFO(); __declspec(dllexport) ~MXIOINFO(); - MxU16 Open(const char *filename, DWORD fdwOpen); - void Close(MxLong arg); - LONG Seek(LONG lOffset, int iOrigin); - MxULong Read(HPSTR pch, LONG cch); - void SetBuffer(LPSTR pchBuffer, LONG cchBuffer, LONG unk); - MxU16 Descend(LPMMCKINFO pmmcki, const MMCKINFO *pmmckiParent, UINT fuDescend); + MxU16 Open(const char *, MxULong); + MxU16 Close(MxLong); + MxLong Read(void *, MxLong); + MxLong Seek(MxLong, int); + MxU16 SetBuffer(char *, MxLong, MxLong); + MxU16 Flush(MxU16); + MxU16 Advance(MxU16); + MxU16 Descend(MMCKINFO *, const MMCKINFO *, MxU16); + // NOTE: In MXIOINFO, the `hmmio` member of MMIOINFO is used like + // an HFILE (int) instead of an HMMIO (WORD). MMIOINFO m_info; };