1554 lines
41 KiB
C
1554 lines
41 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 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];
|
||
|
|
||
|
LPBYTE pbyRom = NULL;
|
||
|
static HANDLE hRomFile = NULL;
|
||
|
static HANDLE hRomMap = NULL;
|
||
|
DWORD dwRomSize = 0;
|
||
|
BYTE cCurrentRomType = 0; // Model -> hardware
|
||
|
UINT nCurrentClass = 0; // Class -> derivate
|
||
|
BOOL bRomWriteable = TRUE; // flag if ROM writeable
|
||
|
|
||
|
static HANDLE hPort2File = NULL;
|
||
|
static HANDLE hPort2Map = NULL;
|
||
|
LPBYTE pbyPort2 = NULL;
|
||
|
BOOL bPort2Writeable = FALSE;
|
||
|
BOOL bPort2IsShared = FALSE;
|
||
|
DWORD dwPort2Size = 0; // size of mapped port2
|
||
|
DWORD dwPort2Mask = 0;
|
||
|
|
||
|
// 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;
|
||
|
|
||
|
BOOL bBackup = FALSE;
|
||
|
|
||
|
//################
|
||
|
//#
|
||
|
//# 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;
|
||
|
|
||
|
nMaxPathLen -= 4; // need 4 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/10);
|
||
|
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(szEmuDirectory);
|
||
|
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
|
||
|
//#
|
||
|
//################
|
||
|
|
||
|
static WORD CrcRom(VOID) // calculate fingerprint of ROM
|
||
|
{
|
||
|
DWORD dwCount;
|
||
|
DWORD dwFileSize;
|
||
|
DWORD dwCrc = 0;
|
||
|
|
||
|
dwFileSize = GetFileSize(hRomFile, &dwCount); // get real filesize
|
||
|
_ASSERT(dwCount == 0); // isn't created by MapRom()
|
||
|
|
||
|
_ASSERT(pbyRom); // view on ROM
|
||
|
// use checksum, because it's faster
|
||
|
for (dwCount = 0;dwCount < dwFileSize; dwCount+=sizeof(dwCrc))
|
||
|
dwCrc += *((DWORD *) &pbyRom[dwCount]);
|
||
|
|
||
|
return (WORD) dwCrc;
|
||
|
}
|
||
|
|
||
|
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(szEmuDirectory);
|
||
|
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;
|
||
|
}
|
||
|
// check for packed ROM image
|
||
|
if ((*(DWORD *) pbyRom & 0xF0F0F0F0) != 0)
|
||
|
{
|
||
|
UnmapRom(); // free memory
|
||
|
AbortMessage(_T("Packed ROM image detected!"));
|
||
|
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;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
//################
|
||
|
//#
|
||
|
//# Port2
|
||
|
//#
|
||
|
//################
|
||
|
|
||
|
WORD CrcPort2(VOID) // calculate fingerprint of port2
|
||
|
{
|
||
|
DWORD dwCount;
|
||
|
DWORD dwFileSize;
|
||
|
WORD wCrc = 0;
|
||
|
|
||
|
// port2 CRC isn't available
|
||
|
if (pbyPort2 == NULL) return wCrc;
|
||
|
|
||
|
dwFileSize = GetFileSize(hPort2File, &dwCount); // get real filesize
|
||
|
_ASSERT(dwCount == 0); // isn't created by MapPort2()
|
||
|
|
||
|
for (dwCount = 0;dwCount < dwFileSize; ++dwCount)
|
||
|
wCrc = (wCrc >> 4) ^ (((wCrc ^ ((WORD) pbyPort2[dwCount])) & 0xf) * 0x1081);
|
||
|
return wCrc;
|
||
|
}
|
||
|
|
||
|
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
|
||
|
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;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
//################
|
||
|
//#
|
||
|
//# Documents
|
||
|
//#
|
||
|
//################
|
||
|
|
||
|
VOID ResetDocument(VOID)
|
||
|
{
|
||
|
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)
|
||
|
{
|
||
|
HANDLE hFile = INVALID_HANDLE_VALUE;
|
||
|
DWORD lBytesRead,lSizeofChipset;
|
||
|
BYTE pbyFileSignature[16];
|
||
|
LPBYTE pbySig;
|
||
|
UINT ctBytesCompared;
|
||
|
UINT nLength;
|
||
|
|
||
|
SaveBackup();
|
||
|
ResetDocument();
|
||
|
|
||
|
// Open file
|
||
|
if (lstrcmpi(szBackupFilename, szFilename)==0)
|
||
|
{
|
||
|
if (YesNoMessage(_T("Do you want to reload this document ?")) == IDNO)
|
||
|
goto restore;
|
||
|
}
|
||
|
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 (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;
|
||
|
}
|
||
|
|
||
|
// HP48SX/GX
|
||
|
if(cCurrentRomType=='S' || cCurrentRomType=='G')
|
||
|
{
|
||
|
MapPort2((nArgc < 3) ? szPort2Filename : ppArgv[2]);
|
||
|
// port2 changed and card detection enabled
|
||
|
if ( Chipset.wPort2Crc != CrcPort2()
|
||
|
&& (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;
|
||
|
|
||
|
bPort2Writeable = TRUE;
|
||
|
Chipset.cards_status = 0xF;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
LoadBreakpointList(hFile); // load debugger breakpoint list
|
||
|
RomSwitch(Chipset.Bank_FF); // reload ROM view of HP49G and map memory
|
||
|
|
||
|
if (Chipset.wRomCrc != CrcRom()) // ROM changed
|
||
|
{
|
||
|
CpuReset();
|
||
|
Chipset.Shutdn = FALSE; // automatic restart
|
||
|
}
|
||
|
|
||
|
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;
|
||
|
}
|
||
|
|
||
|
BOOL SaveDocument(VOID)
|
||
|
{
|
||
|
DWORD lBytesWritten;
|
||
|
DWORD lSizeofChipset;
|
||
|
LPBYTE pbySig;
|
||
|
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);
|
||
|
|
||
|
// get document signature
|
||
|
pbySig = (Chipset.type=='6' || Chipset.type=='A')
|
||
|
? pbySignatureA : (Chipset.type=='E' ? pbySignatureB : ((Chipset.type!='X') ? pbySignatureE : pbySignatureV));
|
||
|
|
||
|
if (!WriteFile(hCurrentFile, pbySig, 16, &lBytesWritten, NULL))
|
||
|
{
|
||
|
AbortMessage(_T("Could not write into file !"));
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
Chipset.wRomCrc = CrcRom(); // save fingerprint of ROM
|
||
|
Chipset.wPort2Crc = CrcPort2(); // 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;
|
||
|
|
||
|
// 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);
|
||
|
}
|
||
|
bBackup = TRUE;
|
||
|
UpdateWindowStatus();
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
BOOL RestoreBackup(VOID)
|
||
|
{
|
||
|
if (!bBackup) return FALSE;
|
||
|
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]);
|
||
|
}
|
||
|
}
|
||
|
SetWindowPathTitle(szCurrentFilename); // update window title line
|
||
|
SetWindowLocation(hWnd,Chipset.nPosX,Chipset.nPosY);
|
||
|
UpdateWindowStatus();
|
||
|
Map(0x00,0xFF);
|
||
|
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("Emu38 Document (*.E38)\0*.E38\0")
|
||
|
_T("Emu39 Document (*.E39)\0*.E39\0")
|
||
|
_T("Emu48 Document (*.E48)\0*.E48\0")
|
||
|
_T("Emu49 Document (*.E49)\0*.E49\0")
|
||
|
_T("Win48 Document (*.W48)\0*.W48\0")
|
||
|
_T("\0\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/HP49G+ // 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_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("Emu38 Document (*.E38)\0*.E38\0")
|
||
|
_T("Emu39 Document (*.E39)\0*.E39\0")
|
||
|
_T("Emu48 Document (*.E48)\0*.E48\0")
|
||
|
_T("Emu49 Document (*.E49)\0*.E49\0")
|
||
|
_T("Win48 Document (*.W48)\0*.W48\0")
|
||
|
_T("\0\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(VOID)
|
||
|
{
|
||
|
TCHAR szBuffer[ARRAYSIZEOF(szBufferFilename)];
|
||
|
OPENFILENAME ofn;
|
||
|
|
||
|
InitializeOFN(&ofn);
|
||
|
ofn.lpstrFilter = _T("All Files (*.*)\0*.*\0") _T("\0\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 GetSaveObjectFilename(VOID)
|
||
|
{
|
||
|
TCHAR szBuffer[ARRAYSIZEOF(szBufferFilename)];
|
||
|
OPENFILENAME ofn;
|
||
|
|
||
|
InitializeOFN(&ofn);
|
||
|
ofn.lpstrFilter = _T("All Files (*.*)\0*.*\0") _T("\0\0");
|
||
|
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 (i = 0; i < dwSize; i++)
|
||
|
{
|
||
|
BYTE byTwoNibs = lpBuf[i+dwSize];
|
||
|
lpBuf[i*2 ] = (BYTE)(byTwoNibs&0xF);
|
||
|
lpBuf[i*2+1] = (BYTE)(byTwoNibs>>4);
|
||
|
}
|
||
|
|
||
|
if (bBinary == TRUE)
|
||
|
{ // load as binary
|
||
|
dwSize = RPL_ObjectSize(lpBuf+16);
|
||
|
dwAddress = RPL_CreateTemp(dwSize);
|
||
|
if (dwAddress == 0) return S_ERR_BINARY;
|
||
|
|
||
|
Nwrite(lpBuf+16,dwAddress,dwSize);
|
||
|
}
|
||
|
else
|
||
|
{ // load as string
|
||
|
dwSize *= 2;
|
||
|
dwAddress = RPL_CreateTemp(dwSize+10);
|
||
|
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_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
|
||
|
//#
|
||
|
//################
|
||
|
|
||
|
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 *)((LPBYTE)lpbi + (WORD)lpbi->biSize);
|
||
|
nNumColors = DibNumColors(lpbi);
|
||
|
|
||
|
if (nNumColors)
|
||
|
{
|
||
|
// Allocate for the logical palette structure
|
||
|
pPal = HeapAlloc(hHeap,0,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 = 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;
|
||
|
}
|
||
|
|
||
|
HBITMAP LoadBitmapFile(LPCTSTR szFilename)
|
||
|
{
|
||
|
HANDLE hFile;
|
||
|
HANDLE hMap;
|
||
|
LPBYTE pbyFile;
|
||
|
HBITMAP hBitmap;
|
||
|
LPBITMAPFILEHEADER pBmfh;
|
||
|
LPBITMAPINFO pBmi;
|
||
|
|
||
|
SetCurrentDirectory(szEmuDirectory);
|
||
|
hFile = CreateFile(szFilename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
|
||
|
SetCurrentDirectory(szCurrentDirectory);
|
||
|
// opened with GENERIC_READ -> PAGE_READONLY
|
||
|
hMap = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, NULL);
|
||
|
if (hMap == NULL)
|
||
|
{
|
||
|
CloseHandle(hFile);
|
||
|
return NULL;
|
||
|
}
|
||
|
// opened with GENERIC_READ -> PAGE_READONLY -> FILE_MAP_READ
|
||
|
pbyFile = MapViewOfFile(hMap, FILE_MAP_READ, 0, 0, 0);
|
||
|
if (pbyFile == NULL)
|
||
|
{
|
||
|
CloseHandle(hMap);
|
||
|
CloseHandle(hFile);
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
hBitmap = NULL;
|
||
|
pBmfh = (LPBITMAPFILEHEADER)pbyFile;
|
||
|
if (pBmfh->bfType != 0x4D42) goto quit; // "BM"
|
||
|
pBmi = (LPBITMAPINFO)(pbyFile+sizeof(BITMAPFILEHEADER));
|
||
|
|
||
|
_ASSERT(hPalette == NULL); // resource free
|
||
|
hPalette = CreateBIPalette(&pBmi->bmiHeader);
|
||
|
// save old palette
|
||
|
hOldPalette = SelectPalette(hWindowDC, hPalette, FALSE);
|
||
|
RealizePalette(hWindowDC);
|
||
|
|
||
|
hBitmap = CreateDIBitmap(
|
||
|
hWindowDC,
|
||
|
&pBmi->bmiHeader,
|
||
|
CBM_INIT,
|
||
|
pbyFile + pBmfh->bfOffBits,
|
||
|
pBmi, DIB_RGB_COLORS);
|
||
|
_ASSERT(hBitmap != NULL);
|
||
|
|
||
|
quit:
|
||
|
UnmapViewOfFile(pbyFile);
|
||
|
CloseHandle(hMap);
|
||
|
CloseHandle(hFile);
|
||
|
return hBitmap;
|
||
|
}
|