emu48plus-mirror/source/FILES.C
Gwenhael Le Moine af664bab5f
2009-08-29: Updated to version 46
Signed-off-by: Gwenhael Le Moine <gwenhael.le.moine@gmail.com>
2024-03-20 07:46:28 +01:00

2189 lines
57 KiB
C

/*
* files.c
*
* This file is part of Emu48
*
* Copyright (C) 1995 Sebastien Carlier
*
*/
#include "pch.h"
#include "Emu48.h"
#include "ops.h"
#include "io.h" // I/O register definitions
#include "kml.h"
#include "i28f160.h" // flash support
#include "debugger.h"
TCHAR szEmuDirectory[MAX_PATH];
TCHAR szRomDirectory[MAX_PATH];
TCHAR szCurrentDirectory[MAX_PATH];
TCHAR szCurrentKml[MAX_PATH];
TCHAR szBackupKml[MAX_PATH];
TCHAR szCurrentFilename[MAX_PATH];
TCHAR szBackupFilename[MAX_PATH];
TCHAR szBufferFilename[MAX_PATH];
TCHAR szPort2Filename[MAX_PATH];
BYTE cCurrentRomType = 0; // Model -> hardware
UINT nCurrentClass = 0; // Class -> derivate
LPBYTE pbyRom = NULL;
BOOL bRomWriteable = TRUE; // flag if ROM writeable
DWORD dwRomSize = 0;
WORD wRomCrc = 0; // fingerprint of patched ROM
LPBYTE pbyPort2 = NULL;
BOOL bPort2Writeable = FALSE;
BOOL bPort2IsShared = FALSE;
DWORD dwPort2Size = 0; // size of mapped port2
DWORD dwPort2Mask = 0;
WORD wPort2Crc = 0; // fingerprint of port2
BOOL bBackup = FALSE;
static HANDLE hRomFile = NULL;
static HANDLE hRomMap = NULL;
static HANDLE hPort2File = NULL;
static HANDLE hPort2Map = NULL;
// document signatures
static BYTE pbySignatureA[16] = "Emu38 Document\xFE";
static BYTE pbySignatureB[16] = "Emu39 Document\xFE";
static BYTE pbySignatureE[16] = "Emu48 Document\xFE";
static BYTE pbySignatureW[16] = "Win48 Document\xFE";
static BYTE pbySignatureV[16] = "Emu49 Document\xFE";
static HANDLE hCurrentFile = NULL;
static CHIPSET BackupChipset;
//################
//#
//# Window Position Tools
//#
//################
VOID SetWindowLocation(HWND hWnd,INT nPosX,INT nPosY)
{
WINDOWPLACEMENT wndpl;
RECT *pRc = &wndpl.rcNormalPosition;
wndpl.length = sizeof(wndpl);
GetWindowPlacement(hWnd,&wndpl);
pRc->right = pRc->right - pRc->left + nPosX;
pRc->bottom = pRc->bottom - pRc->top + nPosY;
pRc->left = nPosX;
pRc->top = nPosY;
SetWindowPlacement(hWnd,&wndpl);
return;
}
//################
//#
//# Filename Title Helper Tool
//#
//################
DWORD GetCutPathName(LPCTSTR szFileName, LPTSTR szBuffer, DWORD dwBufferLength, INT nCutLength)
{
TCHAR cPath[_MAX_PATH]; // full filename
TCHAR cDrive[_MAX_DRIVE];
TCHAR cDir[_MAX_DIR];
TCHAR cFname[_MAX_FNAME];
TCHAR cExt[_MAX_EXT];
_ASSERT(nCutLength >= 0); // 0 = only drive and name
// split original filename into parts
_tsplitpath(szFileName,cDrive,cDir,cFname,cExt);
if (*cDir != 0) // contain directory part
{
LPTSTR lpFilePart; // address of file name in path
INT nNameLen,nPathLen,nMaxPathLen;
GetFullPathName(szFileName,ARRAYSIZEOF(cPath),cPath,&lpFilePart);
_tsplitpath(cPath,cDrive,cDir,cFname,cExt);
// calculate size of drive/name and path
nNameLen = lstrlen(cDrive) + lstrlen(cFname) + lstrlen(cExt);
nPathLen = lstrlen(cDir);
// maximum length for path
nMaxPathLen = nCutLength - nNameLen;
if (nPathLen > nMaxPathLen) // have to cut path
{
TCHAR cDirTemp[_MAX_DIR] = _T("");
LPTSTR szPtr;
// UNC name
if (cDir[0] == _T('\\') && cDir[1] == _T('\\'))
{
// skip server
if ((szPtr = _tcschr(cDir + 2,_T('\\'))) != NULL)
{
// skip share
if ((szPtr = _tcschr(szPtr + 1,_T('\\'))) != NULL)
{
INT nLength = (INT) (szPtr - cDir);
*szPtr = 0; // set EOS behind share
// enough room for \\server\\share and "\...\"
if (nLength + 5 <= nMaxPathLen)
{
lstrcpyn(cDirTemp,cDir,ARRAYSIZEOF(cDirTemp));
nMaxPathLen -= nLength;
}
}
}
}
lstrcat(cDirTemp,_T("\\..."));
nMaxPathLen -= 5; // need 6 chars for additional "\..." + "\"
if (nMaxPathLen < 0) nMaxPathLen = 0;
// get earliest possible '\' character
szPtr = &cDir[nPathLen - nMaxPathLen];
szPtr = _tcschr(szPtr,_T('\\'));
// not found
if (szPtr == NULL) szPtr = _T("");
lstrcat(cDirTemp,szPtr); // copy path with preample to dir buffer
lstrcpyn(cDir,cDirTemp,ARRAYSIZEOF(cDir));
}
}
_tmakepath(cPath,cDrive,cDir,cFname,cExt);
lstrcpyn(szBuffer,cPath,dwBufferLength);
return lstrlen(szBuffer);
}
VOID SetWindowPathTitle(LPCTSTR szFileName)
{
TCHAR cPath[MAX_PATH];
RECT rectClient;
if (*szFileName != 0) // set new title
{
_ASSERT(hWnd != NULL);
VERIFY(GetClientRect(hWnd,&rectClient));
GetCutPathName(szFileName,cPath,ARRAYSIZEOF(cPath),rectClient.right/11);
SetWindowTitle(cPath);
}
return;
}
//################
//#
//# Patch
//#
//################
static __inline BYTE Asc2Nib(BYTE c)
{
if (c<'0') return 0;
if (c<='9') return c-'0';
if (c<'A') return 0;
if (c<='F') return c-'A'+10;
if (c<'a') return 0;
if (c<='f') return c-'a'+10;
return 0;
}
// functions to restore ROM patches
typedef struct tnode
{
BOOL bPatch; // TRUE = ROM address patched
DWORD dwAddress; // patch address
BYTE byROM; // original ROM value
BYTE byPatch; // patched ROM value
struct tnode *next; // next node
} TREENODE;
static TREENODE *nodePatch = NULL;
static BOOL PatchNibble(DWORD dwAddress, BYTE byPatch)
{
TREENODE *p;
_ASSERT(pbyRom); // ROM defined
if((p = HeapAlloc(hHeap,0,sizeof(TREENODE))) == NULL)
return TRUE;
p->bPatch = TRUE; // address patched
p->dwAddress = dwAddress; // save current values
p->byROM = pbyRom[dwAddress];
p->byPatch = byPatch;
p->next = nodePatch; // save node
nodePatch = p;
pbyRom[dwAddress] = byPatch; // patch ROM
return FALSE;
}
static VOID RestorePatches(VOID)
{
TREENODE *p;
_ASSERT(pbyRom); // ROM defined
while (nodePatch != NULL)
{
// restore original data
pbyRom[nodePatch->dwAddress] = nodePatch->byROM;
p = nodePatch->next; // save pointer to next node
HeapFree(hHeap,0,nodePatch); // free node
nodePatch = p; // new node
}
return;
}
VOID UpdatePatches(BOOL bPatch)
{
TREENODE *p = nodePatch;
_ASSERT(pbyRom); // ROM defined
while (p != NULL)
{
if (bPatch) // patch ROM
{
if (!p->bPatch) // patch only if not patched
{
// use original data for patch restore
p->byROM = pbyRom[p->dwAddress];
// restore patch data
pbyRom[p->dwAddress] = p->byPatch;
p->bPatch = TRUE; // address patched
}
else
{
_ASSERT(FALSE); // call ROM patch on a patched ROM
}
}
else // restore ROM
{
// restore original data
pbyRom[p->dwAddress] = p->byROM;
p->bPatch = FALSE; // address not patched
}
p = p->next; // next node
}
return;
}
BOOL PatchRom(LPCTSTR szFilename)
{
HANDLE hFile = NULL;
DWORD dwFileSizeLow = 0;
DWORD dwFileSizeHigh = 0;
DWORD lBytesRead = 0;
PSZ lpStop,lpBuf = NULL;
DWORD dwAddress = 0;
UINT nPos = 0;
if (pbyRom == NULL) return FALSE;
SetCurrentDirectory((*szRomDirectory == 0) ? szEmuDirectory : szRomDirectory);
hFile = CreateFile(szFilename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL);
SetCurrentDirectory(szCurrentDirectory);
if (hFile == INVALID_HANDLE_VALUE) return FALSE;
dwFileSizeLow = GetFileSize(hFile, &dwFileSizeHigh);
if (dwFileSizeLow <= 5)
{ // file is too small.
CloseHandle(hFile);
return FALSE;
}
if (dwFileSizeHigh != 0)
{ // file is too large.
CloseHandle(hFile);
return FALSE;
}
lpBuf = HeapAlloc(hHeap,0,dwFileSizeLow+1);
if (lpBuf == NULL)
{
CloseHandle(hFile);
return FALSE;
}
ReadFile(hFile, lpBuf, dwFileSizeLow, &lBytesRead, NULL);
CloseHandle(hFile);
lpBuf[dwFileSizeLow] = 0;
nPos = 0;
while (lpBuf[nPos])
{
do // remove blank space
{
if ( (lpBuf[nPos]!=' ')
&&(lpBuf[nPos]!='\n')
&&(lpBuf[nPos]!='\r')
&&(lpBuf[nPos]!='\t')) break;
nPos++;
} while (lpBuf[nPos]);
if (lpBuf[nPos]==';') // comment ?
{
do
{
nPos++;
if (lpBuf[nPos]=='\n')
{
nPos++;
break;
}
} while (lpBuf[nPos]);
continue;
}
dwAddress = strtoul(&lpBuf[nPos], &lpStop, 16);
nPos += (UINT) (lpStop - &lpBuf[nPos]) + 1;
if (*lpStop != ':' || *lpStop == 0)
continue;
while (lpBuf[nPos])
{
if (isxdigit(lpBuf[nPos]) == FALSE) break;
// patch ROM and save original nibble
PatchNibble(dwAddress, Asc2Nib(lpBuf[nPos]));
dwAddress = (dwAddress+1)&(dwRomSize-1);
nPos++;
}
}
HeapFree(hHeap,0,lpBuf);
return TRUE;
}
//################
//#
//# ROM
//#
//################
BOOL CrcRom(WORD *pwChk) // calculate fingerprint of ROM
{
DWORD *pdwData,dwSize;
DWORD dwChk = 0;
_ASSERT(pbyRom); // view on ROM
pdwData = (DWORD *) pbyRom;
_ASSERT((dwRomSize % sizeof(*pdwData)) == 0);
dwSize = dwRomSize / sizeof(*pdwData); // file size in DWORD's
// use checksum, because it's faster
while (dwSize-- > 0)
{
DWORD dwData = *pdwData++;
if ((dwData & 0xF0F0F0F0) != 0) // data packed?
return FALSE;
dwChk += dwData;
}
*pwChk = (WORD) ((dwChk >> 16) + (dwChk & 0xFFFF));
return TRUE;
}
BOOL MapRom(LPCTSTR szFilename)
{
DWORD dwFileSizeHigh;
// open ROM for writing
BOOL bWrite = (cCurrentRomType == 'X' || cCurrentRomType == 'Q') ? bRomWriteable : FALSE; // CdB for HP: add apples
if (pbyRom != NULL)
{
return FALSE;
}
SetCurrentDirectory((*szRomDirectory == 0) ? szEmuDirectory : szRomDirectory);
if (bWrite) // ROM writeable
{
hRomFile = CreateFile(szFilename,
GENERIC_READ|GENERIC_WRITE,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (hRomFile == INVALID_HANDLE_VALUE)
{
bWrite = FALSE; // ROM not writeable
hRomFile = CreateFile(szFilename,
GENERIC_READ,
FILE_SHARE_READ|FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
}
}
else // writing ROM disabled
{
hRomFile = CreateFile(szFilename,
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
}
SetCurrentDirectory(szCurrentDirectory);
if (hRomFile == INVALID_HANDLE_VALUE)
{
hRomFile = NULL;
return FALSE;
}
dwRomSize = GetFileSize(hRomFile, &dwFileSizeHigh);
if (dwFileSizeHigh != 0)
{ // file is too large.
CloseHandle(hRomFile);
hRomFile = NULL;
dwRomSize = 0;
return FALSE;
}
hRomMap = CreateFileMapping(hRomFile, NULL, bWrite ? PAGE_READWRITE : PAGE_WRITECOPY, 0, dwRomSize, NULL);
if (hRomMap == NULL)
{
CloseHandle(hRomFile);
hRomFile = NULL;
dwRomSize = 0;
return FALSE;
}
if (GetLastError() == ERROR_ALREADY_EXISTS)
{
AbortMessage(_T("Sharing file mapping handle."));
}
pbyRom = MapViewOfFile(hRomMap, bWrite ? FILE_MAP_WRITE : FILE_MAP_COPY, 0, 0, dwRomSize);
if (pbyRom == NULL)
{
CloseHandle(hRomMap);
CloseHandle(hRomFile);
hRomMap = NULL;
hRomFile = NULL;
dwRomSize = 0;
return FALSE;
}
return TRUE;
}
VOID UnmapRom(VOID)
{
if (pbyRom == NULL) return;
RestorePatches(); // restore ROM Patches
UnmapViewOfFile(pbyRom);
CloseHandle(hRomMap);
CloseHandle(hRomFile);
pbyRom = NULL;
hRomMap = NULL;
hRomFile = NULL;
dwRomSize = 0;
wRomCrc = 0;
return;
}
//################
//#
//# Port2
//#
//################
BOOL CrcPort2(WORD *pwCrc) // calculate fingerprint of port2
{
DWORD dwCount;
DWORD dwFileSize;
*pwCrc = 0;
// port2 CRC isn't available
if (pbyPort2 == NULL) return TRUE;
dwFileSize = GetFileSize(hPort2File, &dwCount); // get real filesize
_ASSERT(dwCount == 0); // isn't created by MapPort2()
for (dwCount = 0;dwCount < dwFileSize; ++dwCount)
{
if ((pbyPort2[dwCount] & 0xF0) != 0) // data packed?
return FALSE;
*pwCrc = (*pwCrc >> 4) ^ (((*pwCrc ^ ((WORD) pbyPort2[dwCount])) & 0xf) * 0x1081);
}
return TRUE;
}
BOOL MapPort2(LPCTSTR szFilename)
{
DWORD dwFileSizeLo,dwFileSizeHi,dwCount;
if (pbyPort2 != NULL) return FALSE;
bPort2Writeable = TRUE;
dwPort2Size = 0; // reset size of port2
SetCurrentDirectory(szEmuDirectory);
hPort2File = CreateFile(szFilename,
GENERIC_READ|GENERIC_WRITE,
bPort2IsShared ? FILE_SHARE_READ : 0,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (hPort2File == INVALID_HANDLE_VALUE)
{
bPort2Writeable = FALSE;
hPort2File = CreateFile(szFilename,
GENERIC_READ,
bPort2IsShared ? (FILE_SHARE_READ|FILE_SHARE_WRITE) : 0,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (hPort2File == INVALID_HANDLE_VALUE)
{
SetCurrentDirectory(szCurrentDirectory);
hPort2File = NULL;
return FALSE;
}
}
SetCurrentDirectory(szCurrentDirectory);
dwFileSizeLo = GetFileSize(hPort2File, &dwFileSizeHi);
if (dwFileSizeHi != 0)
{ // file is too large.
CloseHandle(hPort2File);
hPort2File = NULL;
dwPort2Mask = 0;
bPort2Writeable = FALSE;
return FALSE;
}
// count number of set bits
for (dwCount = 0, dwFileSizeHi = dwFileSizeLo; dwFileSizeHi != 0;dwFileSizeHi >>= 1)
{
if ((dwFileSizeHi & 0x1) != 0) ++dwCount;
}
// size not 32, 128, 256, 512, 1024, 2048 or 4096 KB
if (dwCount != 1 || (dwFileSizeLo & 0xFF02FFFF) != 0)
{
CloseHandle(hPort2File);
hPort2File = NULL;
dwPort2Mask = 0;
bPort2Writeable = FALSE;
return FALSE;
}
dwPort2Mask = (dwFileSizeLo - 1) >> 18; // mask for valid address lines of the BS-FF
hPort2Map = CreateFileMapping(hPort2File, NULL, bPort2Writeable ? PAGE_READWRITE : PAGE_READONLY,
0, dwFileSizeLo, NULL);
if (hPort2Map == NULL)
{
CloseHandle(hPort2File);
hPort2File = NULL;
dwPort2Mask = 0;
bPort2Writeable = FALSE;
return FALSE;
}
pbyPort2 = MapViewOfFile(hPort2Map, bPort2Writeable ? FILE_MAP_WRITE : FILE_MAP_READ, 0, 0, dwFileSizeLo);
if (pbyPort2 == NULL)
{
CloseHandle(hPort2Map);
CloseHandle(hPort2File);
hPort2Map = NULL;
hPort2File = NULL;
dwPort2Mask = 0;
bPort2Writeable = FALSE;
return FALSE;
}
dwPort2Size = dwFileSizeLo / 2048; // mapping size of port2
if (CrcPort2(&wPort2Crc) == FALSE) // calculate fingerprint of port2
{
UnmapPort2(); // free memory
AbortMessage(_T("Packed Port 2 image detected!"));
return FALSE;
}
return TRUE;
}
VOID UnmapPort2(VOID)
{
if (pbyPort2 == NULL) return;
UnmapViewOfFile(pbyPort2);
CloseHandle(hPort2Map);
CloseHandle(hPort2File);
pbyPort2 = NULL;
hPort2Map = NULL;
hPort2File = NULL;
dwPort2Size = 0; // reset size of port2
dwPort2Mask = 0;
bPort2Writeable = FALSE;
wPort2Crc = 0;
return;
}
//################
//#
//# Documents
//#
//################
static BOOL IsDataPacked(VOID *pMem, DWORD dwSize)
{
DWORD *pdwMem = pMem;
_ASSERT((dwSize % sizeof(DWORD)) == 0);
if ((dwSize % sizeof(DWORD)) != 0) return TRUE;
for (dwSize /= sizeof(DWORD); dwSize-- > 0;)
{
if ((*pdwMem++ & 0xF0F0F0F0) != 0)
return TRUE;
}
return FALSE;
}
VOID ResetDocument(VOID)
{
DisableDebugger();
if (szCurrentKml[0])
{
KillKML();
}
if (hCurrentFile)
{
CloseHandle(hCurrentFile);
hCurrentFile = NULL;
}
szCurrentKml[0] = 0;
szCurrentFilename[0]=0;
if (Chipset.Port0) HeapFree(hHeap,0,Chipset.Port0);
if (Chipset.Port1) HeapFree(hHeap,0,Chipset.Port1);
if (Chipset.Port2) HeapFree(hHeap,0,Chipset.Port2); else UnmapPort2();
ZeroMemory(&Chipset,sizeof(Chipset));
ZeroMemory(&RMap,sizeof(RMap)); // delete MMU mappings
ZeroMemory(&WMap,sizeof(WMap));
UpdateWindowStatus();
return;
}
BOOL NewDocument(VOID)
{
SaveBackup();
ResetDocument();
if (!DisplayChooseKml(0)) goto restore;
if (!InitKML(szCurrentKml,FALSE)) goto restore;
Chipset.type = cCurrentRomType;
if (Chipset.type == '6' || Chipset.type == 'A') // HP38G
{
Chipset.Port0Size = (Chipset.type == 'A') ? 32 : 64;
Chipset.Port1Size = 0;
Chipset.Port2Size = 0;
Chipset.cards_status = 0x0;
}
if (Chipset.type == 'E' || Chipset.type == 'P') // HP39/40G/HP39G+ // CdB for HP: add apples
{
Chipset.Port0Size = 128;
Chipset.Port1Size = 0;
Chipset.Port2Size = 128;
Chipset.cards_status = 0xF;
bPort2Writeable = TRUE; // port2 is writeable
}
if (Chipset.type == 'S') // HP48SX
{
Chipset.Port0Size = 32;
Chipset.Port1Size = 128;
Chipset.Port2Size = 0;
Chipset.cards_status = 0x5;
// use 2nd command line argument if defined
MapPort2((nArgc < 3) ? szPort2Filename : ppArgv[2]);
}
if (Chipset.type == 'G') // HP48GX
{
Chipset.Port0Size = 128;
Chipset.Port1Size = 128;
Chipset.Port2Size = 0;
Chipset.cards_status = 0xA;
// use 2nd command line argument if defined
MapPort2((nArgc < 3) ? szPort2Filename : ppArgv[2]);
}
if (Chipset.type == 'X' || Chipset.type == '2' || Chipset.type == 'Q') // HP49G/HP48Gii/HP49G+ // CdB for HP: add apples
{
Chipset.Port0Size = 256;
Chipset.Port1Size = 128;
Chipset.Port2Size = 128;
Chipset.cards_status = 0xF;
bPort2Writeable = TRUE; // port2 is writeable
FlashInit(); // init flash structure
}
if (Chipset.type == 'Q') // HP49G+ // CdB for HP: add apples
{
Chipset.d0size = 16;
}
Chipset.IORam[LPE] = RST; // set ReSeT bit at power on reset
// allocate port memory
if (Chipset.Port0Size)
{
Chipset.Port0 = HeapAlloc(hHeap,HEAP_ZERO_MEMORY,Chipset.Port0Size*2048);
_ASSERT(Chipset.Port0 != NULL);
}
if (Chipset.Port1Size)
{
Chipset.Port1 = HeapAlloc(hHeap,HEAP_ZERO_MEMORY,Chipset.Port1Size*2048);
_ASSERT(Chipset.Port1 != NULL);
}
if (Chipset.Port2Size)
{
Chipset.Port2 = HeapAlloc(hHeap,HEAP_ZERO_MEMORY,Chipset.Port2Size*2048);
_ASSERT(Chipset.Port2 != NULL);
}
LoadBreakpointList(NULL); // clear debugger breakpoint list
RomSwitch(0); // boot ROM view of HP49G and map memory
return TRUE;
restore:
RestoreBackup();
ResetBackup();
// HP48SX/GX
if(Chipset.type == 'S' || Chipset.type == 'G')
{
// use 2nd command line argument if defined
MapPort2((nArgc < 3) ? szPort2Filename : ppArgv[2]);
}
if (pbyRom)
{
SetWindowLocation(hWnd,Chipset.nPosX,Chipset.nPosY);
Map(0x00,0xFF);
}
return FALSE;
}
BOOL OpenDocument(LPCTSTR szFilename)
{
#define CHECKAREA(s,e) (offsetof(CHIPSET,e)-offsetof(CHIPSET,s)+sizeof(((CHIPSET *)NULL)->e))
HANDLE hFile = INVALID_HANDLE_VALUE;
DWORD lBytesRead,lSizeofChipset;
BYTE pbyFileSignature[16];
LPBYTE pbySig;
UINT ctBytesCompared;
UINT nLength;
// Open file
if (lstrcmpi(szCurrentFilename,szFilename) == 0)
{
if (YesNoMessage(_T("Do you want to reload this document?")) == IDNO)
return TRUE;
}
SaveBackup();
ResetDocument();
hFile = CreateFile(szFilename, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
if (hFile == INVALID_HANDLE_VALUE)
{
AbortMessage(_T("This file is missing or already loaded in another instance of Emu48."));
goto restore;
}
// Read and Compare signature
ReadFile(hFile, pbyFileSignature, 16, &lBytesRead, NULL);
switch (pbyFileSignature[0])
{
case 'E':
pbySig = (pbyFileSignature[3] == '3')
? ((pbyFileSignature[4] == '8') ? pbySignatureA : pbySignatureB)
: ((pbyFileSignature[4] == '8') ? pbySignatureE : pbySignatureV);
for (ctBytesCompared=0; ctBytesCompared<14; ctBytesCompared++)
{
if (pbyFileSignature[ctBytesCompared]!=pbySig[ctBytesCompared])
{
AbortMessage(_T("This file is not a valid Emu48 document."));
goto restore;
}
}
break;
case 'W':
for (ctBytesCompared=0; ctBytesCompared<14; ctBytesCompared++)
{
if (pbyFileSignature[ctBytesCompared]!=pbySignatureW[ctBytesCompared])
{
AbortMessage(_T("This file is not a valid Win48 document."));
goto restore;
}
}
break;
default:
AbortMessage(_T("This file is not a valid document."));
goto restore;
}
switch (pbyFileSignature[14])
{
case 0xFE: // Win48 2.1 / Emu4x 0.99.x format
ReadFile(hFile,&nLength,sizeof(nLength),&lBytesRead,NULL);
#if defined _UNICODE
{
LPSTR szTmp = HeapAlloc(hHeap,0,nLength);
if (szTmp == NULL)
{
AbortMessage(_T("Memory Allocation Failure."));
goto restore;
}
ReadFile(hFile, szTmp, nLength, &lBytesRead, NULL);
MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, szTmp, lBytesRead,
szCurrentKml, ARRAYSIZEOF(szCurrentKml));
HeapFree(hHeap,0,szTmp);
}
#else
{
ReadFile(hFile, szCurrentKml, nLength, &lBytesRead, NULL);
}
#endif
if (nLength != lBytesRead) goto read_err;
szCurrentKml[nLength] = 0;
break;
case 0xFF: // Win48 2.05 format
break;
default:
AbortMessage(_T("This file is for an unknown version of Emu48."));
goto restore;
}
// read chipset size inside file
ReadFile(hFile, &lSizeofChipset, sizeof(lSizeofChipset), &lBytesRead, NULL);
if (lBytesRead != sizeof(lSizeofChipset)) goto read_err;
if (lSizeofChipset <= sizeof(Chipset)) // actual or older chipset version
{
// read chipset content
ZeroMemory(&Chipset,sizeof(Chipset)); // init chipset
ReadFile(hFile, &Chipset, lSizeofChipset, &lBytesRead, NULL);
}
else // newer chipset version
{
// read my used chipset content
ReadFile(hFile, &Chipset, sizeof(Chipset), &lBytesRead, NULL);
// skip rest of chipset
SetFilePointer(hFile, lSizeofChipset-sizeof(Chipset), NULL, FILE_CURRENT);
lSizeofChipset = sizeof(Chipset);
}
Chipset.Port0 = NULL; // delete invalid port pointers
Chipset.Port1 = NULL;
Chipset.Port2 = NULL;
if (lBytesRead != lSizeofChipset) goto read_err;
if (!isModelValid(Chipset.type)) // check for valid model in emulator state file
{
AbortMessage(_T("Emulator state file with invalid calculator model."));
goto restore;
}
SetWindowLocation(hWnd,Chipset.nPosX,Chipset.nPosY);
while (TRUE)
{
if (szCurrentKml[0]) // KML file name
{
BOOL bOK;
bOK = InitKML(szCurrentKml,FALSE);
bOK = bOK && (cCurrentRomType == Chipset.type);
if (bOK) break;
KillKML();
}
if (!DisplayChooseKml(Chipset.type))
goto restore;
}
// reload old button state
ReloadButtons(Chipset.Keyboard_Row,sizeof(Chipset.Keyboard_Row));
FlashInit(); // init flash structure
if (Chipset.Port0Size)
{
Chipset.Port0 = HeapAlloc(hHeap,0,Chipset.Port0Size*2048);
if (Chipset.Port0 == NULL)
{
AbortMessage(_T("Memory Allocation Failure."));
goto restore;
}
ReadFile(hFile, Chipset.Port0, Chipset.Port0Size*2048, &lBytesRead, NULL);
if (lBytesRead != Chipset.Port0Size*2048) goto read_err;
if (IsDataPacked(Chipset.Port0,Chipset.Port0Size*2048)) goto read_err;
}
if (Chipset.Port1Size)
{
Chipset.Port1 = HeapAlloc(hHeap,0,Chipset.Port1Size*2048);
if (Chipset.Port1 == NULL)
{
AbortMessage(_T("Memory Allocation Failure."));
goto restore;
}
ReadFile(hFile, Chipset.Port1, Chipset.Port1Size*2048, &lBytesRead, NULL);
if (lBytesRead != Chipset.Port1Size*2048) goto read_err;
if (IsDataPacked(Chipset.Port1,Chipset.Port1Size*2048)) goto read_err;
}
// HP48SX/GX
if(cCurrentRomType=='S' || cCurrentRomType=='G')
{
MapPort2((nArgc < 3) ? szPort2Filename : ppArgv[2]);
// port2 changed and card detection enabled
if ( Chipset.wPort2Crc != wPort2Crc
&& (Chipset.IORam[CARDCTL] & ECDT) != 0 && (Chipset.IORam[TIMER2_CTRL] & RUN) != 0
)
{
Chipset.HST |= MP; // set Module Pulled
IOBit(SRQ2,NINT,FALSE); // set NINT to low
Chipset.SoftInt = TRUE; // set interrupt
bInterrupt = TRUE;
}
}
else // HP38G, HP39/40G, HP49G
{
if (Chipset.Port2Size)
{
Chipset.Port2 = HeapAlloc(hHeap,0,Chipset.Port2Size*2048);
if (Chipset.Port2 == NULL)
{
AbortMessage(_T("Memory Allocation Failure."));
goto restore;
}
ReadFile(hFile, Chipset.Port2, Chipset.Port2Size*2048, &lBytesRead, NULL);
if (lBytesRead != Chipset.Port2Size*2048) goto read_err;
if (IsDataPacked(Chipset.Port2,Chipset.Port2Size*2048)) goto read_err;
bPort2Writeable = TRUE;
Chipset.cards_status = 0xF;
}
}
RomSwitch(Chipset.Bank_FF); // reload ROM view of HP49G and map memory
if (Chipset.wRomCrc != wRomCrc) // ROM changed
{
CpuReset();
Chipset.Shutdn = FALSE; // automatic restart
}
// check CPU main registers
if (IsDataPacked(Chipset.A,CHECKAREA(A,R4))) goto read_err;
LoadBreakpointList(hFile); // load debugger breakpoint list
lstrcpy(szCurrentFilename, szFilename);
_ASSERT(hCurrentFile == NULL);
hCurrentFile = hFile;
#if defined _USRDLL // DLL version
// notify main proc about current document file
if (pEmuDocumentNotify) pEmuDocumentNotify(szCurrentFilename);
#endif
SetWindowPathTitle(szCurrentFilename); // update window title line
UpdateWindowStatus();
return TRUE;
read_err:
AbortMessage(_T("This file must be truncated, and cannot be loaded."));
restore:
if (INVALID_HANDLE_VALUE != hFile) // close if valid handle
CloseHandle(hFile);
RestoreBackup();
ResetBackup();
// HP48SX/GX
if(cCurrentRomType=='S' || cCurrentRomType=='G')
{
// use 2nd command line argument if defined
MapPort2((nArgc < 3) ? szPort2Filename : ppArgv[2]);
}
return FALSE;
#undef CHECKAREA
}
BOOL SaveDocument(VOID)
{
DWORD lBytesWritten;
DWORD lSizeofChipset;
UINT nLength;
WINDOWPLACEMENT wndpl;
if (hCurrentFile == NULL) return FALSE;
_ASSERT(hWnd); // window open
wndpl.length = sizeof(wndpl); // update saved window position
GetWindowPlacement(hWnd, &wndpl);
Chipset.nPosX = (SWORD) wndpl.rcNormalPosition.left;
Chipset.nPosY = (SWORD) wndpl.rcNormalPosition.top;
SetFilePointer(hCurrentFile,0,NULL,FILE_BEGIN);
if (!WriteFile(hCurrentFile, pbySignatureE, sizeof(pbySignatureE), &lBytesWritten, NULL))
{
AbortMessage(_T("Could not write into file !"));
return FALSE;
}
CrcRom(&Chipset.wRomCrc); // save fingerprint of ROM
CrcPort2(&Chipset.wPort2Crc); // save fingerprint of port2
nLength = lstrlen(szCurrentKml);
WriteFile(hCurrentFile, &nLength, sizeof(nLength), &lBytesWritten, NULL);
#if defined _UNICODE
{
LPSTR szTmp = HeapAlloc(hHeap,0,nLength);
if (szTmp != NULL)
{
WideCharToMultiByte(CP_ACP, WC_COMPOSITECHECK,
szCurrentKml, nLength,
szTmp, nLength, NULL, NULL);
WriteFile(hCurrentFile, szTmp, nLength, &lBytesWritten, NULL);
HeapFree(hHeap,0,szTmp);
}
}
#else
{
WriteFile(hCurrentFile, szCurrentKml, nLength, &lBytesWritten, NULL);
}
#endif
lSizeofChipset = sizeof(CHIPSET);
WriteFile(hCurrentFile, &lSizeofChipset, sizeof(lSizeofChipset), &lBytesWritten, NULL);
WriteFile(hCurrentFile, &Chipset, lSizeofChipset, &lBytesWritten, NULL);
if (Chipset.Port0Size) WriteFile(hCurrentFile, Chipset.Port0, Chipset.Port0Size*2048, &lBytesWritten, NULL);
if (Chipset.Port1Size) WriteFile(hCurrentFile, Chipset.Port1, Chipset.Port1Size*2048, &lBytesWritten, NULL);
if (Chipset.Port2Size) WriteFile(hCurrentFile, Chipset.Port2, Chipset.Port2Size*2048, &lBytesWritten, NULL);
SaveBreakpointList(hCurrentFile); // save debugger breakpoint list
SetEndOfFile(hCurrentFile); // cut the rest
return TRUE;
}
BOOL SaveDocumentAs(LPCTSTR szFilename)
{
HANDLE hFile;
if (hCurrentFile) // already file in use
{
CloseHandle(hCurrentFile); // close it, even it's same, so data always will be saved
hCurrentFile = NULL;
}
hFile = CreateFile(szFilename, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, 0, NULL);
if (hFile == INVALID_HANDLE_VALUE) // error, couldn't create a new file
{
AbortMessage(_T("This file must be currently used by another instance of Emu48."));
return FALSE;
}
lstrcpy(szCurrentFilename, szFilename); // save new file name
hCurrentFile = hFile; // and the corresponding handle
#if defined _USRDLL // DLL version
// notify main proc about current document file
if (pEmuDocumentNotify) pEmuDocumentNotify(szCurrentFilename);
#endif
SetWindowPathTitle(szCurrentFilename); // update window title line
UpdateWindowStatus(); // and draw it
return SaveDocument(); // save current content
}
//################
//#
//# Backup
//#
//################
BOOL SaveBackup(VOID)
{
WINDOWPLACEMENT wndpl;
if (pbyRom == NULL) return FALSE;
_ASSERT(nState != SM_RUN); // emulation engine is running
// save window position
_ASSERT(hWnd); // window open
wndpl.length = sizeof(wndpl); // update saved window position
GetWindowPlacement(hWnd, &wndpl);
Chipset.nPosX = (SWORD) wndpl.rcNormalPosition.left;
Chipset.nPosY = (SWORD) wndpl.rcNormalPosition.top;
lstrcpy(szBackupFilename, szCurrentFilename);
lstrcpy(szBackupKml, szCurrentKml);
if (BackupChipset.Port0) HeapFree(hHeap,0,BackupChipset.Port0);
if (BackupChipset.Port1) HeapFree(hHeap,0,BackupChipset.Port1);
if (BackupChipset.Port2) HeapFree(hHeap,0,BackupChipset.Port2);
CopyMemory(&BackupChipset, &Chipset, sizeof(Chipset));
BackupChipset.Port0 = HeapAlloc(hHeap,0,Chipset.Port0Size*2048);
CopyMemory(BackupChipset.Port0, Chipset.Port0, Chipset.Port0Size*2048);
BackupChipset.Port1 = HeapAlloc(hHeap,0,Chipset.Port1Size*2048);
CopyMemory(BackupChipset.Port1, Chipset.Port1, Chipset.Port1Size*2048);
BackupChipset.Port2 = NULL;
if (Chipset.Port2Size) // internal port2
{
BackupChipset.Port2 = HeapAlloc(hHeap,0,Chipset.Port2Size*2048);
CopyMemory(BackupChipset.Port2, Chipset.Port2, Chipset.Port2Size*2048);
}
CreateBackupBreakpointList();
bBackup = TRUE;
UpdateWindowStatus();
return TRUE;
}
BOOL RestoreBackup(VOID)
{
BOOL bDbgOpen;
if (!bBackup) return FALSE;
bDbgOpen = (nDbgState != DBG_OFF); // debugger window open?
ResetDocument();
// need chipset for contrast setting in InitKML()
Chipset.contrast = BackupChipset.contrast;
if (!InitKML(szBackupKml,TRUE))
{
InitKML(szCurrentKml,TRUE);
return FALSE;
}
lstrcpy(szCurrentKml, szBackupKml);
lstrcpy(szCurrentFilename, szBackupFilename);
if (szCurrentFilename[0])
{
hCurrentFile = CreateFile(szCurrentFilename, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
if (hCurrentFile == INVALID_HANDLE_VALUE)
{
hCurrentFile = NULL;
szCurrentFilename[0] = 0;
}
}
CopyMemory(&Chipset, &BackupChipset, sizeof(Chipset));
Chipset.Port0 = HeapAlloc(hHeap,0,Chipset.Port0Size*2048);
CopyMemory(Chipset.Port0, BackupChipset.Port0, Chipset.Port0Size*2048);
Chipset.Port1 = HeapAlloc(hHeap,0,Chipset.Port1Size*2048);
CopyMemory(Chipset.Port1, BackupChipset.Port1, Chipset.Port1Size*2048);
if (Chipset.Port2Size) // internal port2
{
Chipset.Port2 = HeapAlloc(hHeap,0,Chipset.Port2Size*2048);
CopyMemory(Chipset.Port2, BackupChipset.Port2, Chipset.Port2Size*2048);
}
// map port2
else
{
if(cCurrentRomType=='S' || cCurrentRomType=='G') // HP48SX/GX
{
// use 2nd command line argument if defined
MapPort2((nArgc < 3) ? szPort2Filename : ppArgv[2]);
}
}
Map(0x00,0xFF);
SetWindowPathTitle(szCurrentFilename); // update window title line
SetWindowLocation(hWnd,Chipset.nPosX,Chipset.nPosY);
RestoreBackupBreakpointList(); // restore the debugger breakpoint list
if (bDbgOpen) OnToolDebug(); // reopen the debugger
UpdateWindowStatus();
return TRUE;
}
BOOL ResetBackup(VOID)
{
if (!bBackup) return FALSE;
szBackupFilename[0] = 0;
szBackupKml[0] = 0;
if (BackupChipset.Port0) HeapFree(hHeap,0,BackupChipset.Port0);
if (BackupChipset.Port1) HeapFree(hHeap,0,BackupChipset.Port1);
if (BackupChipset.Port2) HeapFree(hHeap,0,BackupChipset.Port2);
ZeroMemory(&BackupChipset,sizeof(BackupChipset));
bBackup = FALSE;
UpdateWindowStatus();
return TRUE;
}
//################
//#
//# Open File Common Dialog Boxes
//#
//################
static VOID InitializeOFN(LPOPENFILENAME ofn)
{
ZeroMemory((LPVOID)ofn, sizeof(OPENFILENAME));
ofn->lStructSize = sizeof(OPENFILENAME);
ofn->hwndOwner = hWnd;
ofn->Flags = OFN_EXPLORER|OFN_HIDEREADONLY;
return;
}
BOOL GetOpenFilename(VOID)
{
TCHAR szBuffer[ARRAYSIZEOF(szBufferFilename)];
OPENFILENAME ofn;
InitializeOFN(&ofn);
ofn.lpstrFilter =
_T("Emu48 Files (*.e38;*.e39;*.e48;*.e49)\0")
_T("*.e38;*.e39;*.e48;*.e49\0")
_T("HP-38 Files (*.e38)\0*.e38\0")
_T("HP-39 Files (*.e39)\0*.e39\0")
_T("HP-48 Files (*.e48)\0*.e48\0")
_T("HP-49 Files (*.e49)\0*.e49\0")
_T("Win48 Files (*.w48)\0*.w48\0");
ofn.nFilterIndex = 1;
ofn.lpstrFile = szBuffer;
ofn.lpstrFile[0] = 0;
ofn.nMaxFile = ARRAYSIZEOF(szBuffer);
ofn.Flags |= OFN_FILEMUSTEXIST|OFN_PATHMUSTEXIST;
if (GetOpenFileName(&ofn) == FALSE) return FALSE;
_ASSERT(ARRAYSIZEOF(szBufferFilename) == ofn.nMaxFile);
lstrcpy(szBufferFilename, ofn.lpstrFile);
return TRUE;
}
BOOL GetSaveAsFilename(VOID)
{
TCHAR szBuffer[ARRAYSIZEOF(szBufferFilename)];
OPENFILENAME ofn;
InitializeOFN(&ofn);
ofn.lpstrFilter =
_T("HP-38 Files (*.e38)\0*.e38\0")
_T("HP-39 Files (*.e39)\0*.e39\0")
_T("HP-48 Files (*.e48)\0*.e48\0")
_T("HP-49 Files (*.e49)\0*.e49\0");
ofn.lpstrDefExt = _T("e48"); // HP48SX/GX
ofn.nFilterIndex = 3;
if (cCurrentRomType=='6' || cCurrentRomType=='A') // HP38G
{
ofn.lpstrDefExt = _T("e38");
ofn.nFilterIndex = 1;
}
if (cCurrentRomType=='E' || cCurrentRomType=='P') // HP39/40G/hp439G+ // CdB for HP: add apples
{
ofn.lpstrDefExt = _T("e39");
ofn.nFilterIndex = 2;
}
if (cCurrentRomType=='X' || cCurrentRomType=='2' || cCurrentRomType=='Q') // HP49G/HP48Gii/HP49G+ // CdB for HP: add apples
{
ofn.lpstrDefExt = _T("e49");
ofn.nFilterIndex = 4;
}
ofn.lpstrFile = szBuffer;
ofn.lpstrFile[0] = 0;
ofn.nMaxFile = ARRAYSIZEOF(szBuffer);
ofn.Flags |= OFN_CREATEPROMPT|OFN_OVERWRITEPROMPT;
if (GetSaveFileName(&ofn) == FALSE) return FALSE;
_ASSERT(ARRAYSIZEOF(szBufferFilename) == ofn.nMaxFile);
lstrcpy(szBufferFilename, ofn.lpstrFile);
return TRUE;
}
BOOL GetLoadObjectFilename(LPCTSTR lpstrFilter,LPCTSTR lpstrDefExt)
{
TCHAR szBuffer[ARRAYSIZEOF(szBufferFilename)];
OPENFILENAME ofn;
InitializeOFN(&ofn);
ofn.lpstrFilter = lpstrFilter;
ofn.lpstrDefExt = lpstrDefExt;
ofn.nFilterIndex = 1;
ofn.lpstrFile = szBuffer;
ofn.lpstrFile[0] = 0;
ofn.nMaxFile = ARRAYSIZEOF(szBuffer);
ofn.Flags |= OFN_FILEMUSTEXIST|OFN_PATHMUSTEXIST;
if (GetOpenFileName(&ofn) == FALSE) return FALSE;
_ASSERT(ARRAYSIZEOF(szBufferFilename) == ofn.nMaxFile);
lstrcpy(szBufferFilename, ofn.lpstrFile);
return TRUE;
}
BOOL GetSaveObjectFilename(LPCTSTR lpstrFilter,LPCTSTR lpstrDefExt)
{
TCHAR szBuffer[ARRAYSIZEOF(szBufferFilename)];
OPENFILENAME ofn;
InitializeOFN(&ofn);
ofn.lpstrFilter = lpstrFilter;
ofn.lpstrDefExt = lpstrDefExt;
ofn.nFilterIndex = 1;
ofn.lpstrFile = szBuffer;
ofn.lpstrFile[0] = 0;
ofn.nMaxFile = ARRAYSIZEOF(szBuffer);
ofn.Flags |= OFN_CREATEPROMPT|OFN_OVERWRITEPROMPT;
if (GetSaveFileName(&ofn) == FALSE) return FALSE;
_ASSERT(ARRAYSIZEOF(szBufferFilename) == ofn.nMaxFile);
lstrcpy(szBufferFilename, ofn.lpstrFile);
return TRUE;
}
//################
//#
//# Load and Save HP48 Objects
//#
//################
WORD WriteStack(UINT nStkLevel,LPBYTE lpBuf,DWORD dwSize) // separated from LoadObject()
{
BOOL bBinary;
DWORD dwAddress, i;
bBinary = ((lpBuf[dwSize+0]=='H')
&& (lpBuf[dwSize+1]=='P')
&& (lpBuf[dwSize+2]=='H')
&& (lpBuf[dwSize+3]=='P')
&& (lpBuf[dwSize+4]=='4')
&& (lpBuf[dwSize+5]==((cCurrentRomType=='X' || cCurrentRomType=='2' || cCurrentRomType=='Q') ? '9' : '8')) // CdB for HP: add apples
&& (lpBuf[dwSize+6]=='-'));
for (dwAddress = 0, i = 0; i < dwSize; i++)
{
BYTE byTwoNibs = lpBuf[i+dwSize];
lpBuf[dwAddress++] = (BYTE)(byTwoNibs&0xF);
lpBuf[dwAddress++] = (BYTE)(byTwoNibs>>4);
}
dwSize = dwAddress; // unpacked buffer size
if (bBinary == TRUE)
{ // load as binary
dwSize = RPL_ObjectSize(lpBuf+16,dwSize-16);
if (dwSize == BAD_OB) return S_ERR_OBJECT;
dwAddress = RPL_CreateTemp(dwSize,TRUE);
if (dwAddress == 0) return S_ERR_BINARY;
Nwrite(lpBuf+16,dwAddress,dwSize);
}
else
{ // load as string
dwAddress = RPL_CreateTemp(dwSize+10,TRUE);
if (dwAddress == 0) return S_ERR_ASCII;
Write5(dwAddress,0x02A2C); // String
Write5(dwAddress+5,dwSize+5); // length of String
Nwrite(lpBuf,dwAddress+10,dwSize); // data
}
RPL_Push(nStkLevel,dwAddress);
return S_ERR_NO;
}
BOOL LoadObject(LPCTSTR szFilename) // separated stack writing part
{
HANDLE hFile;
DWORD dwFileSizeLow;
DWORD dwFileSizeHigh;
LPBYTE lpBuf;
WORD wError;
hFile = CreateFile(szFilename,
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_FLAG_SEQUENTIAL_SCAN,
NULL);
if (hFile == INVALID_HANDLE_VALUE) return FALSE;
dwFileSizeLow = GetFileSize(hFile, &dwFileSizeHigh);
if (dwFileSizeHigh != 0)
{ // file is too large.
CloseHandle(hFile);
return FALSE;
}
lpBuf = HeapAlloc(hHeap,0,dwFileSizeLow*2);
if (lpBuf == NULL)
{
CloseHandle(hFile);
return FALSE;
}
ReadFile(hFile, lpBuf+dwFileSizeLow, dwFileSizeLow, &dwFileSizeHigh, NULL);
CloseHandle(hFile);
wError = WriteStack(1,lpBuf,dwFileSizeLow);
if (wError == S_ERR_OBJECT)
AbortMessage(_T("This isn't a valid binary file."));
if (wError == S_ERR_BINARY)
AbortMessage(_T("The calculator does not have enough\nfree memory to load this binary file."));
if (wError == S_ERR_ASCII)
AbortMessage(_T("The calculator does not have enough\nfree memory to load this text file."));
HeapFree(hHeap,0,lpBuf);
return (wError == S_ERR_NO);
}
BOOL SaveObject(LPCTSTR szFilename) // separated stack reading part
{
HANDLE hFile;
LPBYTE pbyHeader;
DWORD lBytesWritten;
DWORD dwAddress;
DWORD dwLength;
dwAddress = RPL_Pick(1);
if (dwAddress == 0)
{
AbortMessage(_T("Too Few Arguments."));
return FALSE;
}
dwLength = (RPL_SkipOb(dwAddress) - dwAddress + 1) / 2;
hFile = CreateFile(szFilename, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_FLAG_SEQUENTIAL_SCAN, NULL);
if (hFile == INVALID_HANDLE_VALUE)
{
AbortMessage(_T("Cannot open file."));
return FALSE;
}
pbyHeader = ((cCurrentRomType=='X' || cCurrentRomType=='2' || cCurrentRomType=='Q'))
? BINARYHEADER49
: BINARYHEADER48;
WriteFile(hFile, pbyHeader, 8, &lBytesWritten, NULL);
while (dwLength--)
{
BYTE byByte = Read2(dwAddress);
WriteFile(hFile, &byByte, 1, &lBytesWritten, NULL);
dwAddress += 2;
}
CloseHandle(hFile);
return TRUE;
}
//################
//#
//# Load Bitmap
//#
//################
#define WIDTHBYTES(bits) ((((bits) + 31) / 32) * 4)
typedef struct _BmpFile
{
DWORD dwPos; // actual reading pos
DWORD dwFileSize; // file size
LPBYTE pbyFile; // buffer
} BMPFILE, FAR *LPBMPFILE, *PBMPFILE;
static __inline UINT DibNumColors(BITMAPINFOHEADER CONST *lpbi)
{
if (lpbi->biClrUsed != 0) return (UINT)lpbi->biClrUsed;
/* a 24 bitcount DIB has no color table */
return (lpbi->biBitCount <= 8) ? (1 << lpbi->biBitCount) : 0;
}
static HPALETTE CreateBIPalette(BITMAPINFOHEADER CONST *lpbi)
{
LOGPALETTE* pPal;
HPALETTE hpal = NULL;
UINT nNumColors;
BYTE red;
BYTE green;
BYTE blue;
UINT i;
RGBQUAD* pRgb;
if (!lpbi)
return NULL;
if (lpbi->biSize != sizeof(BITMAPINFOHEADER))
return NULL;
// Get a pointer to the color table and the number of colors in it
pRgb = (RGBQUAD FAR *)((LPBYTE)lpbi + (WORD)lpbi->biSize);
nNumColors = DibNumColors(lpbi);
if (nNumColors)
{
// Allocate for the logical palette structure
pPal = HeapAlloc(hHeap,HEAP_ZERO_MEMORY,sizeof(LOGPALETTE) + nNumColors * sizeof(PALETTEENTRY));
if (!pPal)
return NULL;
pPal->palNumEntries = nNumColors;
pPal->palVersion = 0x300;
// Fill in the palette entries from the DIB color table and
// create a logical color palette.
for (i = 0; i < nNumColors; i++)
{
pPal->palPalEntry[i].peRed = pRgb[i].rgbRed;
pPal->palPalEntry[i].peGreen = pRgb[i].rgbGreen;
pPal->palPalEntry[i].peBlue = pRgb[i].rgbBlue;
pPal->palPalEntry[i].peFlags = (BYTE)0;
}
hpal = CreatePalette(pPal);
HeapFree(hHeap,0,pPal);
}
else
{
// create halftone palette for 16, 24 and 32 bitcount bitmaps
// 16, 24 and 32 bitcount DIB's have no color table entries so, set the
// number of to the maximum value (256).
nNumColors = 256;
pPal = HeapAlloc(hHeap,0,sizeof(LOGPALETTE) + nNumColors * sizeof(PALETTEENTRY));
if (!pPal) return NULL;
pPal->palNumEntries = nNumColors;
pPal->palVersion = 0x300;
red = green = blue = 0;
// Generate 256 (= 8*8*4) RGB combinations to fill the palette
// entries.
for (i = 0; i < pPal->palNumEntries; i++)
{
pPal->palPalEntry[i].peRed = red;
pPal->palPalEntry[i].peGreen = green;
pPal->palPalEntry[i].peBlue = blue;
pPal->palPalEntry[i].peFlags = 0;
if (!(red += 32))
if (!(green += 32))
blue += 64;
}
hpal = CreatePalette(pPal);
HeapFree(hHeap,0,pPal);
}
return hpal;
}
static HBITMAP DecodeBmp(LPBMPFILE pBmp)
{
LPBITMAPFILEHEADER pBmfh;
LPBITMAPINFO pBmi;
HBITMAP hBitmap;
DWORD dwFileSize;
hBitmap = NULL;
// size of bitmap header information
dwFileSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
if (pBmp->dwFileSize < dwFileSize) return NULL;
// check for bitmap
pBmfh = (LPBITMAPFILEHEADER) pBmp->pbyFile;
if (pBmfh->bfType != 0x4D42) return NULL; // "BM"
pBmi = (LPBITMAPINFO) (pBmp->pbyFile + sizeof(BITMAPFILEHEADER));
// size with color table
if (pBmi->bmiHeader.biCompression == BI_BITFIELDS)
{
dwFileSize += 3 * sizeof(DWORD);
}
else
{
dwFileSize += DibNumColors(&pBmi->bmiHeader) * sizeof(RGBQUAD);
}
if (dwFileSize != pBmfh->bfOffBits) return NULL;
// size with bitmap data
if (pBmi->bmiHeader.biCompression != BI_RGB)
{
dwFileSize += pBmi->bmiHeader.biSizeImage;
}
else
{
dwFileSize += WIDTHBYTES(pBmi->bmiHeader.biWidth * pBmi->bmiHeader.biBitCount)
* labs(pBmi->bmiHeader.biHeight);
}
if (pBmp->dwFileSize < dwFileSize) return NULL;
VERIFY(hBitmap = CreateDIBitmap(
hWindowDC,
&pBmi->bmiHeader,
CBM_INIT,
pBmp->pbyFile + pBmfh->bfOffBits,
pBmi, DIB_RGB_COLORS));
if (hBitmap == NULL) return NULL;
_ASSERT(hPalette == NULL); // resource free
hPalette = CreateBIPalette(&pBmi->bmiHeader);
// save old palette
hOldPalette = SelectPalette(hWindowDC, hPalette, FALSE);
RealizePalette(hWindowDC);
return hBitmap;
}
static BOOL ReadGifByte(LPBMPFILE pGif, INT *n)
{
// outside GIF file
if (pGif->dwPos >= pGif->dwFileSize)
return TRUE;
*n = pGif->pbyFile[pGif->dwPos++];
return FALSE;
}
static BOOL ReadGifWord(LPBMPFILE pGif, INT *n)
{
// outside GIF file
if (pGif->dwPos + 1 >= pGif->dwFileSize)
return TRUE;
*n = pGif->pbyFile[pGif->dwPos++];
*n |= (pGif->pbyFile[pGif->dwPos++] << 8);
return FALSE;
}
static HBITMAP DecodeGif(LPBMPFILE pBmp)
{
// this implementation base on the GIF image file
// decoder engine of Free42 (c) by Thomas Okken
HBITMAP hBitmap;
typedef struct cmap
{
WORD biBitCount; // bits used in color map
DWORD biClrUsed; // no of colors in color map
RGBQUAD bmiColors[256]; // color map
} CMAP;
BOOL bHasGlobalCmap;
CMAP sGlb; // data of global color map
INT nWidth,nHeight,nInfo,nBackground,nZero;
LONG lBytesPerLine;
LPBYTE pbyPixels;
BITMAPINFO bmi; // global bitmap info
BOOL bDecoding = TRUE;
hBitmap = NULL;
pBmp->dwPos = 6; // position behind GIF header
/* Bits 6..4 of info contain one less than the "color resolution",
* defined as the number of significant bits per RGB component in
* the source image's color palette. If the source image (from
* which the GIF was generated) was 24-bit true color, the color
* resolution is 8, so the value in bits 6..4 is 7. If the source
* image had an EGA color cube (2x2x2), the color resolution would
* be 2, etc.
* Bit 3 of info must be zero in GIF87a; in GIF89a, if it is set,
* it indicates that the global colormap is sorted, the most
* important entries being first. In PseudoColor environments this
* can be used to make sure to get the most important colors from
* the X server first, to optimize the image's appearance in the
* event that not all the colors from the colormap can actually be
* obtained at the same time.
* The 'zero' field is always 0 in GIF87a; in GIF89a, it indicates
* the pixel aspect ratio, as (PAR + 15) : 64. If PAR is zero,
* this means no aspect ratio information is given, PAR = 1 means
* 1:4 (narrow), PAR = 49 means 1:1 (square), PAR = 255 means
* slightly over 4:1 (wide), etc.
*/
if ( ReadGifWord(pBmp,&nWidth)
|| ReadGifWord(pBmp,&nHeight)
|| ReadGifByte(pBmp,&nInfo)
|| ReadGifByte(pBmp,&nBackground)
|| ReadGifByte(pBmp,&nZero)
|| nZero != 0)
goto quit;
ZeroMemory(&bmi,sizeof(bmi)); // init bitmap info
bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bmi.bmiHeader.biWidth = nWidth;
bmi.bmiHeader.biHeight = nHeight;
bmi.bmiHeader.biPlanes = 1;
bmi.bmiHeader.biBitCount = 24; // create a true color DIB
bmi.bmiHeader.biCompression = BI_RGB;
ZeroMemory(&sGlb,sizeof(sGlb)); // init global color map
bHasGlobalCmap = (nInfo & 0x80) != 0;
sGlb.biBitCount = (nInfo & 7) + 1; // bits used in global color map
sGlb.biClrUsed = (1 << sGlb.biBitCount); // no of colors in global color map
// color table should not exceed 256 colors
_ASSERT(sGlb.biClrUsed <= ARRAYSIZEOF(sGlb.bmiColors));
if (bHasGlobalCmap) // global color map
{
DWORD i;
for (i = 0; i < sGlb.biClrUsed; ++i)
{
int r, g, b;
if (ReadGifByte(pBmp,&r) || ReadGifByte(pBmp,&g) || ReadGifByte(pBmp,&b))
goto quit;
sGlb.bmiColors[i].rgbRed = r;
sGlb.bmiColors[i].rgbGreen = g;
sGlb.bmiColors[i].rgbBlue = b;
}
}
else // no color map
{
DWORD i;
for (i = 0; i < sGlb.biClrUsed; ++i)
{
BYTE k = (BYTE) ((i * sGlb.biClrUsed) / (sGlb.biClrUsed - 1));
sGlb.bmiColors[i].rgbRed = k;
sGlb.bmiColors[i].rgbGreen = k;
sGlb.bmiColors[i].rgbBlue = k;
}
}
// bitmap dimensions
lBytesPerLine = WIDTHBYTES(bmi.bmiHeader.biWidth * bmi.bmiHeader.biBitCount);
bmi.bmiHeader.biSizeImage = lBytesPerLine * bmi.bmiHeader.biHeight;
// create top-down DIB
bmi.bmiHeader.biHeight = -bmi.bmiHeader.biHeight;
// allocate buffer for pixels
VERIFY(hBitmap = CreateDIBSection(hWindowDC,
(LPBITMAPINFO)&bmi,
DIB_RGB_COLORS,
(VOID **)&pbyPixels,
NULL,
0));
if (hBitmap == NULL) goto quit;
// fill pixel buffer with background color
for (nHeight = 0; nHeight < labs(bmi.bmiHeader.biHeight); ++nHeight)
{
LPBYTE pbyLine = pbyPixels + nHeight * lBytesPerLine;
for (nWidth = 0; nWidth < bmi.bmiHeader.biWidth; ++nWidth)
{
*pbyLine++ = sGlb.bmiColors[nBackground].rgbBlue;
*pbyLine++ = sGlb.bmiColors[nBackground].rgbGreen;
*pbyLine++ = sGlb.bmiColors[nBackground].rgbRed;
}
_ASSERT((DWORD) (pbyLine - pbyPixels) <= bmi.bmiHeader.biSizeImage);
}
while (bDecoding)
{
INT nBlockType;
if (ReadGifByte(pBmp,&nBlockType)) goto quit;
switch (nBlockType)
{
case ',': // image
{
CMAP sAct; // data of actual color map
INT nLeft,nTop,nWidth,nHeight;
INT i,nInfo;
BOOL bInterlaced;
INT h,v;
INT nCodesize; // LZW codesize in bits
INT nBytecount;
SHORT prefix_table[4096];
SHORT code_table[4096];
INT nMaxcode;
INT nClearCode;
INT nEndCode;
INT nCurrCodesize;
INT nCurrCode;
INT nOldCode;
INT nBitsNeeded;
BOOL bEndCodeSeen;
// read image dimensions
if ( ReadGifWord(pBmp,&nLeft)
|| ReadGifWord(pBmp,&nTop)
|| ReadGifWord(pBmp,&nWidth)
|| ReadGifWord(pBmp,&nHeight)
|| ReadGifByte(pBmp,&nInfo))
goto quit;
if ( nTop + nHeight > labs(bmi.bmiHeader.biHeight)
|| nLeft + nWidth > bmi.bmiHeader.biWidth)
goto quit;
/* Bit 3 of info must be zero in GIF87a; in GIF89a, if it
* is set, it indicates that the local colormap is sorted,
* the most important entries being first. In PseudoColor
* environments this can be used to make sure to get the
* most important colors from the X server first, to
* optimize the image's appearance in the event that not
* all the colors from the colormap can actually be
* obtained at the same time.
*/
if ((nInfo & 0x80) == 0) // using global color map
{
sAct = sGlb;
}
else // using local color map
{
DWORD i;
sAct.biBitCount = (nInfo & 7) + 1; // bits used in color map
sAct.biClrUsed = (1 << sAct.biBitCount); // no of colors in color map
for (i = 0; i < sAct.biClrUsed; ++i)
{
int r, g, b;
if (ReadGifByte(pBmp,&r) || ReadGifByte(pBmp,&g) || ReadGifByte(pBmp,&b))
goto quit;
sAct.bmiColors[i].rgbRed = r;
sAct.bmiColors[i].rgbGreen = g;
sAct.bmiColors[i].rgbBlue = b;
}
}
// interlaced image
bInterlaced = (nInfo & 0x40) != 0;
h = 0;
v = 0;
if ( ReadGifByte(pBmp,&nCodesize)
|| ReadGifByte(pBmp,&nBytecount))
goto quit;
nMaxcode = (1 << nCodesize);
// preset LZW table
for (i = 0; i < nMaxcode + 2; ++i)
{
prefix_table[i] = -1;
code_table[i] = i;
}
nClearCode = nMaxcode++;
nEndCode = nMaxcode++;
nCurrCodesize = nCodesize + 1;
nCurrCode = 0;
nOldCode = -1;
nBitsNeeded = nCurrCodesize;
bEndCodeSeen = FALSE;
while (nBytecount != 0)
{
for (i = 0; i < nBytecount; ++i)
{
INT nCurrByte;
INT nBitsAvailable;
if (ReadGifByte(pBmp,&nCurrByte))
goto quit;
if (bEndCodeSeen) continue;
nBitsAvailable = 8;
while (nBitsAvailable != 0)
{
INT nBitsCopied = (nBitsNeeded < nBitsAvailable)
? nBitsNeeded
: nBitsAvailable;
INT nBits = nCurrByte >> (8 - nBitsAvailable);
nBits &= 0xFF >> (8 - nBitsCopied);
nCurrCode |= nBits << (nCurrCodesize - nBitsNeeded);
nBitsAvailable -= nBitsCopied;
nBitsNeeded -= nBitsCopied;
if (nBitsNeeded == 0)
{
BYTE byExpanded[4096];
INT nExplen;
do
{
if (nCurrCode == nEndCode)
{
bEndCodeSeen = TRUE;
break;
}
if (nCurrCode == nClearCode)
{
nMaxcode = (1 << nCodesize) + 2;
nCurrCodesize = nCodesize + 1;
nOldCode = -1;
break;
}
if (nCurrCode < nMaxcode)
{
if (nMaxcode < 4096 && nOldCode != -1)
{
INT c = nCurrCode;
while (prefix_table[c] != -1)
c = prefix_table[c];
c = code_table[c];
prefix_table[nMaxcode] = nOldCode;
code_table[nMaxcode] = c;
nMaxcode++;
if (nMaxcode == (1 << nCurrCodesize) && nCurrCodesize < 12)
nCurrCodesize++;
}
}
else
{
INT c;
if (nCurrCode > nMaxcode || nOldCode == -1) goto quit;
_ASSERT(nCurrCode == nMaxcode);
/* Once maxcode == 4096, we can't get here
* any more, because we refuse to raise
* nCurrCodeSize above 12 -- so we can
* never read a bigger code than 4095.
*/
c = nOldCode;
while (prefix_table[c] != -1)
c = prefix_table[c];
c = code_table[c];
prefix_table[nMaxcode] = nOldCode;
code_table[nMaxcode] = c;
nMaxcode++;
if (nMaxcode == (1 << nCurrCodesize) && nCurrCodesize < 12)
nCurrCodesize++;
}
nOldCode = nCurrCode;
// output nCurrCode!
nExplen = 0;
while (nCurrCode != -1)
{
_ASSERT(nExplen < ARRAYSIZEOF(byExpanded));
byExpanded[nExplen++] = (BYTE) code_table[nCurrCode];
nCurrCode = prefix_table[nCurrCode];
}
_ASSERT(nExplen > 0);
while (--nExplen >= 0)
{
// get color map index
BYTE byColIndex = byExpanded[nExplen];
LPBYTE pbyRgbr = pbyPixels + (lBytesPerLine * (nTop + v) + 3 * (nLeft + h));
_ASSERT(pbyRgbr + 2 < pbyPixels + bmi.bmiHeader.biSizeImage);
_ASSERT(byColIndex < sAct.biClrUsed);
*pbyRgbr++ = sAct.bmiColors[byColIndex].rgbBlue;
*pbyRgbr++ = sAct.bmiColors[byColIndex].rgbGreen;
*pbyRgbr = sAct.bmiColors[byColIndex].rgbRed;
if (++h == nWidth)
{
h = 0;
if (bInterlaced)
{
switch (v & 7)
{
case 0:
v += 8;
if (v < nHeight)
break;
/* Some GIF en/decoders go
* straight from the '0'
* pass to the '4' pass
* without checking the
* height, and blow up on
* 2/3/4 pixel high
* interlaced images.
*/
if (nHeight > 4)
v = 4;
else
if (nHeight > 2)
v = 2;
else
if (nHeight > 1)
v = 1;
else
bEndCodeSeen = TRUE;
break;
case 4:
v += 8;
if (v >= nHeight)
v = 2;
break;
case 2:
case 6:
v += 4;
if (v >= nHeight)
v = 1;
break;
case 1:
case 3:
case 5:
case 7:
v += 2;
if (v >= nHeight)
bEndCodeSeen = TRUE;
break;
}
if (bEndCodeSeen)
break; // while (--nExplen >= 0)
}
else // non interlaced
{
if (++v == nHeight)
{
bEndCodeSeen = TRUE;
break; // while (--nExplen >= 0)
}
}
}
}
}
while (FALSE);
nCurrCode = 0;
nBitsNeeded = nCurrCodesize;
}
}
}
if (ReadGifByte(pBmp,&nBytecount))
goto quit;
}
}
break;
case '!': // extension block
{
INT i,nFunctionCode,nByteCount,nDummy;
if (ReadGifByte(pBmp,&nFunctionCode)) goto quit;
if (ReadGifByte(pBmp,&nByteCount)) goto quit;
while (nByteCount != 0)
{
for (i = 0; i < nByteCount; ++i)
{
if (ReadGifByte(pBmp,&nDummy)) goto quit;
}
if (ReadGifByte(pBmp,&nByteCount)) goto quit;
}
}
break;
case ';': // terminator
bDecoding = FALSE;
break;
default: goto quit;
}
}
_ASSERT(bDecoding == FALSE); // decoding successful
// normal decoding exit
_ASSERT(hPalette == NULL); // resource free
hPalette = CreateBIPalette((PBITMAPINFOHEADER) &bmi);
// save old palette
hOldPalette = SelectPalette(hWindowDC, hPalette, FALSE);
RealizePalette(hWindowDC);
quit:
if (hBitmap != NULL && bDecoding) // creation failed
{
DeleteObject(hBitmap); // delete bitmap
hBitmap = NULL;
}
return hBitmap;
}
HBITMAP LoadBitmapFile(LPCTSTR szFilename)
{
HANDLE hFile;
HANDLE hMap;
BMPFILE Bmp;
HBITMAP hBitmap;
SetCurrentDirectory(szEmuDirectory);
hFile = CreateFile(szFilename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
SetCurrentDirectory(szCurrentDirectory);
if (hFile == INVALID_HANDLE_VALUE) return NULL;
Bmp.dwFileSize = GetFileSize(hFile, NULL);
hMap = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, NULL);
if (hMap == NULL)
{
CloseHandle(hFile);
return NULL;
}
Bmp.pbyFile = MapViewOfFile(hMap, FILE_MAP_READ, 0, 0, 0);
if (Bmp.pbyFile == NULL)
{
CloseHandle(hMap);
CloseHandle(hFile);
return NULL;
}
do
{
// check for bitmap file header "BM"
if (Bmp.dwFileSize >= 2 && *(WORD *) Bmp.pbyFile == 0x4D42)
{
hBitmap = DecodeBmp(&Bmp);
break;
}
// check for GIF file header
if ( Bmp.dwFileSize >= 6
&& (memcmp(Bmp.pbyFile,"GIF87a",6) == 0 || memcmp(Bmp.pbyFile,"GIF89a",6) == 0))
{
hBitmap = DecodeGif(&Bmp);
break;
}
// unknown file type
hBitmap = NULL;
}
while (FALSE);
UnmapViewOfFile(Bmp.pbyFile);
CloseHandle(hMap);
CloseHandle(hFile);
return hBitmap;
}