diff --git a/LEGO1/mxioinfo.cpp b/LEGO1/mxioinfo.cpp index ebe2a480..4a3fdf25 100644 --- a/LEGO1/mxioinfo.cpp +++ b/LEGO1/mxioinfo.cpp @@ -3,7 +3,9 @@ // OFFSET: LEGO1 0x100cc800 MXIOINFO::MXIOINFO() { - memset(&m_info, 0, sizeof(MMIOINFO)); + // This is not good practice, but shouldn't damage anything + // because MXIOINFO has no vtable. + memset(this, 0, sizeof(*this)); } // OFFSET: LEGO1 0x100cc820 @@ -15,35 +17,426 @@ MXIOINFO::~MXIOINFO() // OFFSET: LEGO1 0x100cc830 unsigned short MXIOINFO::Open(const char *filename, DWORD fdwOpen) { - return 0; + OFSTRUCT _unused; + unsigned short result = 0; + + m_lBufOffset = 0; + m_lDiskOffset = 0; + + // Cast of fdWOpen to u16 forces the `movzx` instruction + //m_hmmio = OpenFile(filename, &_unused, (unsigned short)fdwOpen); + m_hmmio = OpenFile(filename, &_unused, fdwOpen); + + if (m_hmmio != HFILE_ERROR) { + m_dwFlags = fdwOpen; + if (fdwOpen & MMIO_ALLOCBUF) { + + // Default buffer length of 8k if none specified + int len = m_cchBuffer ? m_cchBuffer : 8192; + HPSTR buf = new char[len]; + + if (!buf) { + result = MMIOERR_OUTOFMEMORY; + m_cchBuffer = 0; + m_dwFlags &= ~MMIO_ALLOCBUF; + m_pchBuffer = 0; + } else { + m_pchBuffer = buf; + m_cchBuffer = len; + } + + m_pchEndRead = m_pchBuffer; + m_pchNext = m_pchBuffer; + m_pchEndWrite = m_pchBuffer + m_cchBuffer; + } + } else { + result = MMIOERR_CANNOTOPEN; + } + + return result; } // OFFSET: LEGO1 0x100cc8e0 -void MXIOINFO::Close(long arg) +unsigned short MXIOINFO::Close(long arg) { - + unsigned short result = 0; + + if (m_hmmio) { + result = Flush(0); + _lclose(m_hmmio); + m_hmmio = NULL; + + if (m_dwFlags & MMIO_ALLOCBUF) + delete[] m_pchBuffer; + + m_pchEndWrite = 0; + m_pchEndRead = 0; + m_pchBuffer = 0; + m_dwFlags = 0; + } + + return result; } // OFFSET: LEGO1 0x100cc930 unsigned long MXIOINFO::Read(HPSTR pch, LONG cch) { - return 0; + unsigned long bytes_read = 0; + + if (m_pchBuffer) { + + int bytes_left = m_pchEndRead - m_pchNext; + while (cch > 0) { + + if (bytes_left > 0) { + if (cch < bytes_left) + bytes_left = cch; + + memcpy(pch, m_pchNext, bytes_left); + cch -= bytes_left; + + m_pchNext += bytes_left; + bytes_read += bytes_left; + } + + if (cch <= 0 || Advance(0)) + break; + + bytes_left = m_pchEndRead - m_pchNext; + if (bytes_left <= 0) + break; + } + } else if (m_hmmio && cch > 0) { + bytes_read = _hread(m_hmmio, pch, cch); + + if (bytes_read == -1) { + bytes_read = 0; + m_lDiskOffset = _llseek(m_hmmio, 0, SEEK_CUR); + } else { + m_lDiskOffset += bytes_read; + } + } + + return bytes_read; } // OFFSET: LEGO1 0x100cca00 LONG MXIOINFO::Seek(LONG lOffset, int iOrigin) { - return 0; + LONG result = -1; + + // If buffered I/O + if (m_pchBuffer) { + if (iOrigin == SEEK_CUR) { + if (!lOffset) { + // don't seek at all and just return where we are. + return m_lBufOffset + (m_pchNext - m_pchBuffer); + } else { + // With SEEK_CUR, lOffset is a relative offset. + // Get the absolute position instead and use SEEK_SET. + lOffset += m_lBufOffset + (m_pchNext - m_pchBuffer); + iOrigin = SEEK_SET; + } + } else if (iOrigin == SEEK_END) { + // not possible with buffered I/O + return -1; + } + + // else iOrigin == SEEK_SET. + + // is lOffset between the start and end of the buffer? + // i.e. can we do the seek without reading more from disk? + if (lOffset >= m_lBufOffset && lOffset < m_lBufOffset + m_cchBuffer) { + m_pchNext = m_pchBuffer + (lOffset - m_lBufOffset); + result = lOffset; + } else { + // we have to read another chunk from disk. + if (m_hmmio && !Flush(0)) { + m_lDiskOffset = _llseek(m_hmmio, lOffset, iOrigin); + + if (m_lDiskOffset == -1) { + m_lDiskOffset = _llseek(m_hmmio, 0, SEEK_CUR); + } else { + + // align offset to buffer size + int new_offset = lOffset - (lOffset % m_cchBuffer); + m_lBufOffset = new_offset; + + // do we need to seek again? + // (i.e. are we already aligned to buffer size?) + if (lOffset != new_offset) { + m_lDiskOffset = _llseek(m_hmmio, new_offset, SEEK_SET); + + if (m_lDiskOffset == -1) { + m_lDiskOffset = _llseek(m_hmmio, 0, SEEK_CUR); + } + } + + if (m_lBufOffset == m_lDiskOffset) { + // is the file open for writing only? + if ((m_dwFlags & MMIO_RWMODE) && + ((m_dwFlags & MMIO_RWMODE) != MMIO_READWRITE)) { + + m_pchNext = m_pchBuffer - m_lBufOffset + lOffset; + + result = lOffset; + } else { + // We can read from the file. Fill the buffer. + int bytes_read = _hread(m_hmmio, m_pchBuffer, m_cchBuffer); + + if (bytes_read == -1) { + m_lDiskOffset = _llseek(m_hmmio, 0, SEEK_CUR); + } else { + m_lDiskOffset += bytes_read; + m_pchNext = lOffset - m_lBufOffset + m_pchBuffer; + m_pchEndRead = m_pchBuffer + bytes_read; + + if (m_pchNext < m_pchEndRead) { + result = lOffset; + } + } + } + } + } + } + } + } else { + // No buffer so just seek the file directly (if we have a valid handle) + if (m_hmmio) { + // i.e. if we just want to get the current file position + if (iOrigin == SEEK_CUR && lOffset == 0) { + return m_lDiskOffset; + } else { + m_lDiskOffset = _llseek(m_hmmio, lOffset, iOrigin); + + result = m_lDiskOffset; + + if (result == -1) { + m_lDiskOffset = _llseek(m_hmmio, 0, SEEK_CUR); + } + } + } + } + + return result; } // OFFSET: LEGO1 0x100ccbc0 -void MXIOINFO::SetBuffer(LPSTR pchBuffer, LONG cchBuffer, LONG unk) +unsigned short MXIOINFO::SetBuffer(LPSTR pchBuffer, LONG cchBuffer, LONG unk) { - + unsigned short result = Flush(0); + + if (m_dwFlags & MMIO_ALLOCBUF) { + m_dwFlags &= ~MMIO_ALLOCBUF; + delete[] m_pchBuffer; + } + + m_pchBuffer = pchBuffer; + m_cchBuffer = cchBuffer; + m_pchEndWrite = m_pchBuffer + m_cchBuffer; + m_pchEndRead = m_pchBuffer; + + return result; +} + +// OFFSET: LEGO1 0x100ccc10 +unsigned short MXIOINFO::Flush(UINT fuFlush) +{ + unsigned short result = 0; + + // if buffer is dirty + if (m_dwFlags & MMIO_DIRTY) { + // if we have allocated an IO buffer + if (m_pchBuffer) { + // if we have a file open for writing + if (m_hmmio && (m_dwFlags & MMIO_RWMODE)) { + // (pulling this value out into a variable forces it into EBX) + LONG cchBuffer = m_cchBuffer; + if (cchBuffer > 0) { + if (m_lBufOffset != m_lDiskOffset) { + m_lDiskOffset = _llseek(m_hmmio, m_lBufOffset, SEEK_SET); + } + + // Was the previous seek (if required) successful? + if (m_lBufOffset != m_lDiskOffset) { + result = MMIOERR_CANNOTSEEK; + m_lDiskOffset = _llseek(m_hmmio, 0, SEEK_CUR); + } else { + long bytes_written = _hwrite(m_hmmio, m_pchBuffer, cchBuffer); + + if (bytes_written != -1 && bytes_written == cchBuffer) { + m_lDiskOffset += bytes_written; + m_pchNext = m_pchBuffer; + m_dwFlags &= ~MMIO_DIRTY; + } else { + result = MMIOERR_CANNOTWRITE; + m_lDiskOffset = _llseek(m_hmmio, 0, SEEK_CUR); + } + } + } + } else { + result = MMIOERR_CANNOTWRITE; + } + } else { + result = MMIOERR_UNBUFFERED; + } + } + + return result; +} + +// OFFSET: LEGO1 0x100ccd00 +unsigned short MXIOINFO::Advance(UINT fuAdvance) +{ + unsigned short result = 0; + DWORD rwmode = m_dwFlags & MMIO_RWMODE; + + if (m_pchBuffer) { + long cch = m_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_dwFlags & MMIO_DIRTY) && + ((fuAdvance & MMIO_WRITE) || (rwmode == MMIO_READWRITE)) && + cch > 0) { + + if (m_lBufOffset != m_lDiskOffset) { + m_lDiskOffset = _llseek(m_hmmio, m_lBufOffset, SEEK_SET); + } + + if (m_lBufOffset != m_lDiskOffset) { + result = MMIOERR_CANNOTSEEK; + } else { + long bytes_written = _hwrite(m_hmmio, m_pchBuffer, cch); + + if (bytes_written != -1 && bytes_written == cch) { + m_lDiskOffset += bytes_written; + m_pchNext = m_pchBuffer; + m_pchEndRead = m_pchBuffer; + m_dwFlags &= ~MMIO_DIRTY; + } else { + result = MMIOERR_CANNOTWRITE; + } + } + + m_lDiskOffset = _llseek(m_hmmio, 0, SEEK_CUR); + + } + + m_lBufOffset += cch; + if ((!rwmode || rwmode == MMIO_READWRITE) && cch > 0) { + if (m_lBufOffset != m_lDiskOffset) { + m_lDiskOffset = _llseek(m_hmmio, m_lBufOffset, SEEK_SET); + } + + // if previous seek failed + if (m_lBufOffset != m_lDiskOffset) { + result = MMIOERR_CANNOTSEEK; + m_lDiskOffset = _llseek(m_hmmio, 0, SEEK_CUR); + } else { + int bytes_read = _hread(m_hmmio, m_pchBuffer, cch); + + if (bytes_read == -1) { + result = MMIOERR_CANNOTREAD; + m_lDiskOffset = _llseek(m_hmmio, 0, SEEK_CUR); + } else { + m_lDiskOffset += bytes_read; + m_pchNext = m_pchBuffer; + m_pchEndRead = m_pchBuffer + bytes_read; + } + } + } + } else { + result = MMIOERR_UNBUFFERED; + } + + return result; } // OFFSET: LEGO1 0x100cce60 -unsigned short MXIOINFO::Descend(LPMMCKINFO pmmcki, const MMCKINFO *pmmckiParent, UINT fuDescend) +unsigned short MXIOINFO::Descend(MMCKINFO *pmmcki, const MMCKINFO *pmmckiParent, unsigned short fuDescend) { - return 0; + unsigned short result = 0; + + if (!pmmcki) + return MMIOERR_BASE; // ? + + if (!fuDescend) { + pmmcki->dwFlags = 0; + if (Read((char *)pmmcki, 8) != 8) { + result = MMIOERR_CANNOTREAD; + } else { + if (m_pchBuffer) { + pmmcki->dwDataOffset = m_pchNext - m_pchBuffer + m_lBufOffset; + } else { + pmmcki->dwDataOffset = m_lDiskOffset; + } + + if (pmmcki->ckid == FOURCC_RIFF || pmmcki->ckid == FOURCC_LIST) { + if (Read((char *)&pmmcki->fccType, 4) != 4) { + result = MMIOERR_CANNOTREAD; + } + } + } + } else { + DWORD ofs = MAXLONG; + + if (pmmckiParent) + ofs = pmmckiParent->cksize + pmmckiParent->dwDataOffset; + + BOOL running = TRUE; + MMCKINFO tmp; + BOOL read_ok = FALSE; + tmp.dwFlags = 0; + + // This loop is... something + do { + if (Read((char *)&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_pchBuffer) { + tmp.dwDataOffset = m_pchNext - m_pchBuffer + m_lBufOffset; + } else { + tmp.dwDataOffset = m_lDiskOffset; + } + + if (ofs < tmp.dwDataOffset) { + result = MMIOERR_CHUNKNOTFOUND; + running = FALSE; + } else { + if ((fuDescend == MMIO_FINDLIST && tmp.ckid == FOURCC_LIST) || + (fuDescend == MMIO_FINDRIFF && tmp.ckid == FOURCC_RIFF)) { + if (Read((char *)&tmp.fccType, 4) != 4) { + result = MMIOERR_CANNOTREAD; + } else { + if (pmmcki->fccType != tmp.fccType) + continue; + } + running = FALSE; + } else { + if (pmmcki->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(pmmcki, &tmp, sizeof(MMCKINFO)); + + } + + return result; } \ No newline at end of file diff --git a/LEGO1/mxioinfo.h b/LEGO1/mxioinfo.h index c88a55b9..d69d2bf0 100644 --- a/LEGO1/mxioinfo.h +++ b/LEGO1/mxioinfo.h @@ -12,13 +12,39 @@ class MXIOINFO __declspec(dllexport) ~MXIOINFO(); unsigned short Open(const char *filename, DWORD fdwOpen); - void Close(long arg); + unsigned short Close(long arg); LONG Seek(LONG lOffset, int iOrigin); unsigned long Read(HPSTR pch, LONG cch); - void SetBuffer(LPSTR pchBuffer, LONG cchBuffer, LONG unk); - unsigned short Descend(LPMMCKINFO pmmcki, const MMCKINFO *pmmckiParent, UINT fuDescend); + unsigned short SetBuffer(LPSTR pchBuffer, LONG cchBuffer, LONG unk); + unsigned short Flush(UINT); + unsigned short Advance(UINT); + unsigned short Descend(MMCKINFO *pmmcki, const MMCKINFO *pmmckiParent, unsigned short fuDescend); - MMIOINFO m_info; + // The following is the MMIOINFO struct but with h_mmio set to type HFILE. + + /* general fields */ + DWORD m_dwFlags; /* 0 general status flags */ + FOURCC m_fccIOProc; /* 4 pointer to I/O procedure */ + LPMMIOPROC m_pIOProc; /* 8 pointer to I/O procedure */ + UINT m_wErrorRet; /* c place for error to be returned */ + HTASK m_htask; /* 10 alternate local task */ + + /* fields maintained by MMIO functions during buffered I/O */ + LONG m_cchBuffer; /* 14 size of I/O buffer (or 0L) */ + HPSTR m_pchBuffer; /* 18 start of I/O buffer (or NULL) */ + HPSTR m_pchNext; /* 1c pointer to next byte to read/write */ + HPSTR m_pchEndRead; /* 20 pointer to last valid byte to read */ + HPSTR m_pchEndWrite; /* 24 pointer to last byte to write */ + LONG m_lBufOffset; /* 28 disk offset of start of buffer */ + + /* fields maintained by I/O procedure */ + LONG m_lDiskOffset; /* 2c disk offset of next read or write */ + DWORD m_adwInfo[3]; /* 30 data specific to type of MMIOPROC */ + + /* other fields maintained by MMIO */ + DWORD m_dwReserved1; /* 3c reserved for MMIO use */ + DWORD m_dwReserved2; /* 40 reserved for MMIO use */ + HFILE m_hmmio; /* 44 handle to open file */ }; #endif // MXIOINFO_H