2020-02-09 11:36:49 +00:00
/*
N - Rage ` s Dinput8 Plugin
2016-01-27 09:11:59 +00:00
( C ) 2002 , 2006 Norbert Wladyka
2020-02-09 11:36:49 +00:00
Author ` s Email : norbert . wladyka @ chello . at
Website : http : //go.to/nrage
2016-01-27 09:11:59 +00:00
This program is free software ; you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
the free Software Foundation ; either version 2 of the License , or
( at your option ) any later version .
This program is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
You should have received a copy of the GNU General Public License
along with this program ; if not , write to the free Software
Foundation , Inc . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307 USA
*/
2020-02-09 11:25:05 +00:00
# include <algorithm>
# include <stdio.h>
2016-01-27 09:11:59 +00:00
# include <windows.h>
2020-02-10 09:57:07 +00:00
# include <commctrl.h>
2016-01-27 09:11:59 +00:00
# include <tchar.h>
2020-02-09 11:25:05 +00:00
# include "commonIncludes.h"
2016-01-27 09:11:59 +00:00
# include "DirectInput.h"
# include "FileAccess.h"
# include "GBCart.h"
2020-02-09 11:25:05 +00:00
# include "Interface.h"
# include "NRagePluginV2.h"
# include "PakIO.h"
using std : : min ;
using std : : max ;
2016-01-27 09:11:59 +00:00
// ProtoTypes
BYTE AddressCRC ( const unsigned char * Address ) ;
BYTE DataCRC ( const unsigned char * Data , const int iLength ) ;
VOID CALLBACK WritebackProc ( HWND hWnd , UINT msg , UINT_PTR idEvent , DWORD dwTime ) ;
bool InitControllerPak ( const int iControl )
// Prepares the Pak
{
2020-02-09 11:36:49 +00:00
if ( ! g_pcControllers [ iControl ] . fPlugged )
return false ;
bool bReturn = false ;
if ( g_pcControllers [ iControl ] . pPakData )
{
SaveControllerPak ( iControl ) ;
CloseControllerPak ( iControl ) ;
}
switch ( g_pcControllers [ iControl ] . PakType )
{
case PAK_MEM :
{
g_pcControllers [ iControl ] . pPakData = P_malloc ( sizeof ( MEMPAK ) ) ;
MEMPAK * mPak = ( MEMPAK * ) g_pcControllers [ iControl ] . pPakData ;
mPak - > bPakType = PAK_MEM ;
mPak - > fReadonly = false ;
mPak - > fDexSave = false ;
mPak - > hMemPakHandle = NULL ;
DWORD dwFilesize = PAK_MEM_SIZE ; // expected file size
TCHAR szBuffer [ MAX_PATH + 1 ] ,
szFullPath [ MAX_PATH + 1 ] ,
* pcFile ;
GetAbsoluteFileName ( szBuffer , g_pcControllers [ iControl ] . szMempakFile , DIRECTORY_MEMPAK ) ;
GetFullPathName ( szBuffer , sizeof ( szFullPath ) / sizeof ( TCHAR ) , szFullPath , & pcFile ) ;
bool isNewfile = ! CheckFileExists ( szBuffer ) ;
if ( pcFile = = NULL )
{ // no Filename specified
WarningMessage ( IDS_ERR_MEM_NOSPEC , MB_OK | MB_ICONWARNING ) ;
g_pcControllers [ iControl ] . PakType = PAK_NONE ;
break ; // memory is freed at the end of this function
}
TCHAR * pcPoint = _tcsrchr ( pcFile , ' . ' ) ;
if ( ! lstrcmpi ( pcPoint , _T ( " .n64 " ) ) )
{
mPak - > fDexSave = true ;
dwFilesize + = PAK_MEM_DEXOFFSET ;
}
else
{
mPak - > fDexSave = false ;
}
HANDLE hFileHandle = CreateFile ( szFullPath , GENERIC_READ | GENERIC_WRITE , FILE_SHARE_READ , NULL , OPEN_ALWAYS , 0 , NULL ) ;
if ( hFileHandle = = INVALID_HANDLE_VALUE )
{ // test if Read-only access is possible
hFileHandle = CreateFile ( szFullPath , GENERIC_READ , FILE_SHARE_READ , NULL , OPEN_ALWAYS , 0 , NULL ) ;
if ( hFileHandle ! = INVALID_HANDLE_VALUE )
{
TCHAR tszTitle [ DEFAULT_BUFFER ] , tszText [ DEFAULT_BUFFER ] ;
LoadString ( g_hResourceDLL , IDS_DLG_MEM_READONLY , tszText , DEFAULT_BUFFER ) ;
LoadString ( g_hResourceDLL , IDS_DLG_WARN_TITLE , tszTitle , DEFAULT_BUFFER ) ;
wsprintf ( szBuffer , tszText , pcFile ) ;
MessageBox ( g_strEmuInfo . hMainWindow , szBuffer , tszTitle , MB_OK | MB_ICONWARNING ) ;
mPak - > fReadonly = true ;
DebugWriteA ( " Ramfile opened in READ ONLY mode. \n " ) ;
}
else
{
TCHAR tszTitle [ DEFAULT_BUFFER ] , tszText [ DEFAULT_BUFFER ] ;
LoadString ( g_hResourceDLL , IDS_ERR_MEMOPEN , tszText , DEFAULT_BUFFER ) ;
LoadString ( g_hResourceDLL , IDS_DLG_WARN_TITLE , tszTitle , DEFAULT_BUFFER ) ;
wsprintf ( szBuffer , tszText , pcFile ) ;
MessageBox ( g_strEmuInfo . hMainWindow , szBuffer , tszTitle , MB_OK | MB_ICONWARNING ) ;
g_pcControllers [ iControl ] . PakType = PAK_NONE ; // set so that CloseControllerPak doesn't try to close a file that isn't open
DebugWrite ( _T ( " Unable to read or create MemPak file %s. \n " ) , pcFile ) ;
break ; // memory is freed at the end of this function
}
}
DWORD dwCurrentSize = GetFileSize ( hFileHandle , NULL ) ;
if ( mPak - > fReadonly )
{
DWORD dwBytesRead = 0 ;
if ( mPak - > fDexSave )
{
SetFilePointer ( hFileHandle , PAK_MEM_DEXOFFSET , NULL , FILE_BEGIN ) ;
}
else
{
SetFilePointer ( hFileHandle , 0L , NULL , FILE_BEGIN ) ;
}
dwFilesize = min ( dwFilesize , GetFileSize ( hFileHandle , NULL ) ) ;
if ( ! isNewfile )
{
mPak - > aMemPakData = ( LPBYTE ) P_malloc ( sizeof ( BYTE ) * PAK_MEM_SIZE ) ;
if ( ReadFile ( hFileHandle , mPak - > aMemPakData , PAK_MEM_SIZE , & dwBytesRead , NULL ) )
{
if ( dwBytesRead < dwFilesize )
FillMemory ( ( LPBYTE ) mPak - > aMemPakData + dwBytesRead , PAK_MEM_SIZE - dwBytesRead , 0xFF ) ;
bReturn = true ;
}
else
P_free ( mPak - > aMemPakData ) ;
}
else
{
FormatMemPak ( mPak - > aMemPakData ) ;
bReturn = true ;
}
CloseHandle ( hFileHandle ) ;
}
else
{
// use mapped file
mPak - > hMemPakHandle = CreateFileMapping ( hFileHandle , NULL , mPak - > fReadonly ? PAGE_READONLY : PAGE_READWRITE , 0 , dwFilesize , NULL ) ;
// test for invalid handle
if ( mPak - > hMemPakHandle = = NULL )
{
// note: please don't move the CloseHandle call before GetLastError
DebugWriteA ( " Couldn't CreateFileMapping for MemPak file : %08x \n " , GetLastError ( ) ) ;
CloseHandle ( hFileHandle ) ;
break ; // memory is freed at the end of the function
}
CloseHandle ( hFileHandle ) ; // we can close the file handle now with no problems
mPak - > aMemPakData = ( LPBYTE ) MapViewOfFile ( mPak - > hMemPakHandle , FILE_MAP_ALL_ACCESS , 0 , 0 , mPak - > fDexSave ? PAK_MEM_SIZE + PAK_MEM_DEXOFFSET : PAK_MEM_SIZE ) ;
// this is a bit tricky:
// if it's a dexsave, move the pakdata pointer forward so it points to where the actual mempak data starts
// we need to make sure to move it back when we UnmapViewOfFile
if ( mPak - > aMemPakData = = NULL )
ErrorMessage ( IDS_ERR_MAPVIEW , GetLastError ( ) , false ) ;
if ( mPak - > fDexSave )
mPak - > aMemPakData + = PAK_MEM_DEXOFFSET ;
2016-01-27 09:11:59 +00:00
if ( dwCurrentSize < dwFilesize )
2020-02-09 11:36:49 +00:00
FillMemory ( ( LPBYTE ) mPak - > aMemPakData + ( mPak - > fDexSave ? dwCurrentSize - PAK_MEM_DEXOFFSET : dwCurrentSize ) , dwFilesize - dwCurrentSize , 0xFF ) ;
if ( isNewfile )
{
if ( mPak - > fDexSave )
{
PVOID pHeader = MapViewOfFile ( mPak - > hMemPakHandle , FILE_MAP_ALL_ACCESS , 0 , 0 , PAK_MEM_DEXOFFSET ) ;
const char szHeader [ ] = " 123-456-STD " ; // "OMG-WTF-BBQ"? --rabid
ZeroMemory ( pHeader , PAK_MEM_DEXOFFSET ) ;
CopyMemory ( pHeader , szHeader , sizeof ( szHeader ) ) ;
FlushViewOfFile ( pHeader , PAK_MEM_DEXOFFSET ) ;
UnmapViewOfFile ( pHeader ) ;
}
FormatMemPak ( mPak - > aMemPakData ) ;
}
bReturn = true ;
}
}
break ;
case PAK_RUMBLE :
{
g_pcControllers [ iControl ] . pPakData = P_malloc ( sizeof ( RUMBLEPAK ) ) ;
RUMBLEPAK * rPak = ( RUMBLEPAK * ) g_pcControllers [ iControl ] . pPakData ;
rPak - > bPakType = PAK_RUMBLE ;
rPak - > fLastData = true ; // statistically, if uninitted it would return true --rabid
// rPak->bRumbleTyp = g_pcControllers[iControl].bRumbleTyp;
// rPak->bRumbleStrength = g_pcControllers[iControl].bRumbleStrength;
// rPak->fVisualRumble = g_pcControllers[iControl].fVisualRumble;
if ( ! g_pcControllers [ iControl ] . fXInput ) //used to make sure only xinput cotroller rumbles --tecnicors
CreateEffectHandle ( iControl , g_pcControllers [ iControl ] . bRumbleTyp , g_pcControllers [ iControl ] . bRumbleStrength ) ;
bReturn = true ;
}
break ;
case PAK_TRANSFER :
{
g_pcControllers [ iControl ] . pPakData = P_malloc ( sizeof ( TRANSFERPAK ) ) ;
LPTRANSFERPAK tPak = ( LPTRANSFERPAK ) g_pcControllers [ iControl ] . pPakData ;
tPak - > bPakType = PAK_TRANSFER ;
tPak - > gbCart . hRomFile = NULL ;
tPak - > gbCart . hRamFile = NULL ;
tPak - > gbCart . RomData = NULL ;
tPak - > gbCart . RamData = NULL ;
/*
* Once the Interface is implemented g_pcControllers [ iControl ] . szTransferRom will hold filename of the GB - Rom
* and g_pcControllers [ iControl ] . szTransferSave holds Filename of the SRAM Save
*
* Here , both files should be opened and the handles stored in tPak ( modify the struct for Your own purposes , only bPakType must stay at first )
*/
//CreateFile( g_pcControllers[iControl].szTransferSave, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_ALWAYS, 0, NULL );
tPak - > iCurrentAccessMode = 0 ;
tPak - > iCurrentBankNo = 0 ;
tPak - > iEnableState = false ;
tPak - > iAccessModeChanged = 0x44 ;
tPak - > bPakInserted = LoadCart ( & tPak - > gbCart , g_pcControllers [ iControl ] . szTransferRom , g_pcControllers [ iControl ] . szTransferSave , _T ( " " ) ) ;
if ( tPak - > bPakInserted )
{
DebugWriteA ( " *** Init Transfer Pak - Success*** \n " ) ;
}
else
{
DebugWriteA ( " *** Init Transfer Pak - FAILURE*** \n " ) ;
}
bReturn = true ;
}
break ;
/*case PAK_VOICE:
{
g_pcControllers [ iControl ] . pPakData = P_malloc ( sizeof ( VOICEPAK ) ) ;
VOICEPAK * vPak = ( VOICEPAK * ) g_pcControllers [ iControl ] . pPakData ;
vPak - > bPakType = PAK_VOICE ;
bReturn = true ;
}
break ; */
case PAK_ADAPTOID :
if ( ! g_pcControllers [ iControl ] . fIsAdaptoid )
g_pcControllers [ iControl ] . PakType = PAK_NONE ;
else
{
g_pcControllers [ iControl ] . pPakData = P_malloc ( sizeof ( ADAPTOIDPAK ) ) ;
ADAPTOIDPAK * aPak = ( ADAPTOIDPAK * ) g_pcControllers [ iControl ] . pPakData ;
aPak - > bPakType = PAK_ADAPTOID ;
aPak - > bIdentifier = 0x80 ;
2016-01-27 09:11:59 +00:00
# ifdef ADAPTOIDPAK_RUMBLEFIX
2020-02-09 11:36:49 +00:00
aPak - > fRumblePak = true ;
2016-01-27 09:11:59 +00:00
# pragma message( "Driver-fix for Rumble with Adaptoid enabled" )
# else
2020-02-09 11:36:49 +00:00
aPak - > fRumblePak = false ;
2016-01-27 09:11:59 +00:00
# endif
2020-02-09 11:36:49 +00:00
bReturn = true ;
}
break ;
/*case PAK_NONE:
break ; */
}
// if there were any unrecoverable errors and we have allocated pPakData, free it and set paktype to NONE
if ( ! bReturn & & g_pcControllers [ iControl ] . pPakData )
CloseControllerPak ( iControl ) ;
return bReturn ;
2016-01-27 09:11:59 +00:00
}
BYTE ReadControllerPak ( const int iControl , LPBYTE Command )
{
2020-02-09 11:36:49 +00:00
BYTE bReturn = RD_ERROR ;
LPBYTE Data = & Command [ 2 ] ;
2016-01-27 09:11:59 +00:00
# ifdef MAKEADRESSCRCCHECK
# pragma message( "Addresscheck for Pak-Reads active" )
2020-02-09 11:36:49 +00:00
if ( AddressCRC ( Command ) ! = ( Command [ 1 ] & 0x1F ) )
{
g_pcControllers [ iControl ] . fPakCRCError = true ;
if ( WarningMessage ( IDS_DLG_MEM_BADADDRESSCRC , MB_OKCANCEL | MB_ICONQUESTION ) = = IDCANCEL )
return RD_ERROR ;
}
2016-01-27 09:11:59 +00:00
# endif
2020-02-09 11:36:49 +00:00
if ( ! g_pcControllers [ iControl ] . pPakData )
return RD_ERROR ;
WORD dwAddress = ( Command [ 0 ] < < 8 ) + ( Command [ 1 ] & 0xE0 ) ;
switch ( * ( BYTE * ) g_pcControllers [ iControl ] . pPakData )
{
case PAK_MEM :
{
MEMPAK * mPak = ( MEMPAK * ) g_pcControllers [ iControl ] . pPakData ;
if ( dwAddress < 0x8000 )
CopyMemory ( Data , & mPak - > aMemPakData [ dwAddress ] , 32 ) ;
else
CopyMemory ( Data , & mPak - > aMemPakTemp [ ( dwAddress % 0x100 ) ] , 32 ) ;
Data [ 32 ] = DataCRC ( Data , 32 ) ;
bReturn = RD_OK ;
}
break ;
case PAK_RUMBLE :
if ( ( dwAddress > = 0x8000 ) & & ( dwAddress < 0x9000 ) )
{
RUMBLEPAK * rPak = ( RUMBLEPAK * ) g_pcControllers [ iControl ] . pPakData ;
if ( rPak - > fLastData )
FillMemory ( Data , 32 , 0x80 ) ;
else
ZeroMemory ( Data , 32 ) ;
if ( g_pcControllers [ iControl ] . fXInput ) // xinput controller rumble --tecnicors
VibrateXInputController ( g_pcControllers [ iControl ] . xiController . nControl , 0 , 0 ) ;
else if ( g_apFFDevice [ iControl ] )
g_apFFDevice [ iControl ] - > Acquire ( ) ;
}
else
ZeroMemory ( Data , 32 ) ;
Data [ 32 ] = DataCRC ( Data , 32 ) ;
bReturn = RD_OK ;
break ;
case PAK_TRANSFER :
{
LPTRANSFERPAK tPak = ( LPTRANSFERPAK ) g_pcControllers [ iControl ] . pPakData ; // TODO: null pointer check on tPak
// set bReturn = RD_OK when implementing Transferpak
bReturn = RD_OK ;
DebugWriteA ( " TPak Read: \n " ) ;
DebugWriteA ( " Address: %04X \n " , dwAddress ) ;
switch ( dwAddress > > 12 )
{
case 0x8 : // if ((dwAddress >= 0x8000) && (dwAddress <= 0x8FFF))
DebugWriteA ( " Query Enable State: %u \n " , tPak - > iEnableState ) ;
if ( tPak - > iEnableState = = false )
ZeroMemory ( Data , 32 ) ;
else
FillMemory ( Data , 32 , 0x84 ) ;
break ;
case 0xB : // if ((dwAddress >= 0xB000) && (dwAddress <= 0xBFFF))
if ( tPak - > iEnableState = = true )
{
DebugWriteA ( " Query Cart. State: " ) ;
if ( tPak - > bPakInserted )
{
if ( tPak - > iCurrentAccessMode = = 1 )
{
FillMemory ( Data , 32 , 0x89 ) ;
DebugWriteA ( " Inserted, Access Mode 1 \n " ) ;
}
else
{
FillMemory ( Data , 32 , 0x80 ) ;
DebugWriteA ( " Inserted, Access Mode 0 \n " ) ;
}
Data [ 0 ] = Data [ 0 ] | tPak - > iAccessModeChanged ;
}
else
{
FillMemory ( Data , 32 , 0x40 ) ; // Cart not inserted.
DebugWriteA ( " Not Inserted \n " ) ;
}
tPak - > iAccessModeChanged = 0 ;
}
break ;
case 0xC :
case 0xD :
case 0xE :
case 0xF : // if ((dwAddress >= 0xC000))
if ( tPak - > iEnableState = = true )
{
DebugWriteA ( " Cart Read: Bank:%i \n " , tPak - > iCurrentBankNo ) ;
DebugWriteA ( " Address:%04X \n " , ( ( dwAddress & 0xFFE0 ) - 0xC000 ) + ( ( tPak - > iCurrentBankNo & 3 ) * 0x4000 ) ) ;
tPak - > gbCart . ptrfnReadCart ( & tPak - > gbCart , ( ( dwAddress & 0xFFE0 ) - 0xC000 ) + ( ( tPak - > iCurrentBankNo & 3 ) * 0x4000 ) , Data ) ;
}
break ;
default :
DebugWriteA ( " WARNING: Unusual Pak Read \n " ) ;
DebugWriteA ( " Address: %04X \n " , dwAddress ) ;
} // end switch (dwAddress >> 12)
2016-01-27 09:11:59 +00:00
# ifdef ENABLE_RAWPAK_DEBUG
2020-02-09 11:36:49 +00:00
DebugWriteA ( " TPak Data: " ) ;
for ( int i = 0 ; i < 32 ; i + + )
{
if ( ( i < 31 ) & & ( ( i & 7 ) = = 0 ) ) DebugWriteA ( " \n " ) ;
DebugWriteByteA ( Data [ i ] ) ;
if ( i < 31 )
{
DebugWriteA ( " , " ) ;
}
}
DebugWriteA ( " \n " ) ;
2016-01-27 09:11:59 +00:00
# endif
2020-02-09 11:36:49 +00:00
Data [ 32 ] = DataCRC ( Data , 32 ) ;
bReturn = RD_OK ;
}
break ;
/*case PAK_VOICE:
break ; */
case PAK_ADAPTOID :
if ( ReadAdaptoidPak ( iControl , dwAddress , Data ) = = DI_OK )
{
Data [ 32 ] = DataCRC ( Data , 32 ) ;
bReturn = RD_OK ;
if ( ( ( ADAPTOIDPAK * ) g_pcControllers [ iControl ] . pPakData ) - > fRumblePak )
{
BYTE bId = ( ( ADAPTOIDPAK * ) g_pcControllers [ iControl ] . pPakData ) - > bIdentifier ;
if ( ( ( dwAddress = = 0x8000 ) & & ( bId = = 0x80 ) & & ( Data [ 0 ] ! = 0x80 ) )
| | ( ( dwAddress = = 0x8000 ) & & ( bId ! = 0x80 ) & & ( Data [ 0 ] ! = 0x00 ) )
| | ( ( dwAddress < 0x8000 ) & & ( Data [ 0 ] ! = 0x00 ) ) )
{
( ( ADAPTOIDPAK * ) g_pcControllers [ iControl ] . pPakData ) - > fRumblePak = false ;
DebugWriteA ( " \n Assuming the inserted Pak AINT a RumblePak \n Disabling Rumblefix \n " ) ;
}
}
}
break ;
/*case PAK_NONE:
break ; */
}
return bReturn ;
2016-01-27 09:11:59 +00:00
}
// Called when the N64 tries to write to the controller pak, e.g. a mempak
BYTE WriteControllerPak ( const int iControl , LPBYTE Command )
{
2020-02-09 11:36:49 +00:00
BYTE bReturn = RD_ERROR ;
BYTE * Data = & Command [ 2 ] ;
2016-01-27 09:11:59 +00:00
# ifdef MAKEADRESSCRCCHECK
# pragma message( "Addresscheck for Pak-Writes active" )
2020-02-09 11:36:49 +00:00
if ( AddressCRC ( Command ) ! = ( Command [ 1 ] & 0x1F ) )
{
g_pcControllers [ iControl ] . fPakCRCError = true ;
if ( WarningMessage ( IDS_DLG_MEM_BADADDRESSCRC , MB_OKCANCEL | MB_ICONQUESTION ) = = IDCANCEL )
return RD_ERROR ;
}
2016-01-27 09:11:59 +00:00
# endif
2020-02-09 11:36:49 +00:00
if ( ! g_pcControllers [ iControl ] . pPakData )
return RD_ERROR ;
WORD dwAddress = ( Command [ 0 ] < < 8 ) + ( Command [ 1 ] & 0xE0 ) ;
switch ( * ( BYTE * ) g_pcControllers [ iControl ] . pPakData )
{
case PAK_MEM :
{
// Switched to memory-mapped file
// That way, if the computer dies due to power loss or something mid-play, the savegame is still there.
MEMPAK * mPak = ( MEMPAK * ) g_pcControllers [ iControl ] . pPakData ;
if ( dwAddress < 0x8000 )
{
CopyMemory ( & mPak - > aMemPakData [ dwAddress ] , Data , 32 ) ;
if ( ! mPak - > fReadonly )
SetTimer ( g_strEmuInfo . hMainWindow , PAK_MEM , 2000 , ( TIMERPROC ) WritebackProc ) ; // if we go 2 seconds without a write, call the Writeback proc (which will flush the cache)
}
else
CopyMemory ( & mPak - > aMemPakTemp [ ( dwAddress % 0x100 ) ] , Data , 32 ) ;
Data [ 32 ] = DataCRC ( Data , 32 ) ;
bReturn = RD_OK ;
}
break ;
case PAK_RUMBLE :
if ( dwAddress = = PAK_IO_RUMBLE )
{
if ( g_pcControllers [ iControl ] . fXInput ) // xinput controller rumble --tecnicors
{
if ( * Data )
VibrateXInputController ( g_pcControllers [ iControl ] . xiController . nControl ) ;
else
VibrateXInputController ( g_pcControllers [ iControl ] . xiController . nControl , 0 , 0 ) ;
goto end_rumble ;
}
if ( g_pcControllers [ iControl ] . fVisualRumble )
FlashWindow ( g_strEmuInfo . hMainWindow , ( * Data ! = 0 ) ? TRUE : FALSE ) ;
if ( g_pcControllers [ iControl ] . bRumbleTyp = = RUMBLE_DIRECT )
{ // Adaptoid Direct Rumble
if ( g_pcControllers [ iControl ] . fIsAdaptoid )
DirectRumbleCommand ( iControl , * Data ) ;
}
else
{ // FF-FeedBack Rumble
if ( g_apdiEffect [ iControl ] )
{
g_apFFDevice [ iControl ] - > Acquire ( ) ;
if ( * Data )
{
// g_apdiEffect[iControl]->Start( 1, DIES_SOLO );
HRESULT hr ;
hr = g_apdiEffect [ iControl ] - > Start ( 1 , DIES_NODOWNLOAD ) ;
if ( hr ! = DI_OK ) // just download if needed( seems to work smoother)
{
hr = g_apdiEffect [ iControl ] - > Start ( 1 , 0 ) ;
if ( hr ! = DI_OK )
{
DebugWriteA ( " Rumble: Can't rumble %d: %lX \n " , iControl , hr ) ;
}
else
DebugWriteA ( " Rumble: DIES_NODOWNLOAD failed, regular OK on control %d \n " , iControl ) ;
}
else
DebugWriteA ( " Rumble: DIES_NODOWNLOAD OK on control %d \n " , iControl ) ;
}
else
{
g_apdiEffect [ iControl ] - > Stop ( ) ;
}
}
}
}
else if ( dwAddress > = 0x8000 & & dwAddress < 0x9000 )
{
RUMBLEPAK * rPak = ( RUMBLEPAK * ) g_pcControllers [ iControl ] . pPakData ;
rPak - > fLastData = ( * Data ) ? true : false ;
}
end_rumble : // added so after xinput controller rumbles, gets here --tecnicors
Data [ 32 ] = DataCRC ( Data , 32 ) ;
bReturn = RD_OK ;
break ;
case PAK_TRANSFER :
{
LPTRANSFERPAK tPak = ( LPTRANSFERPAK ) g_pcControllers [ iControl ] . pPakData ;
// set bReturn = RD_OK when implementing Transferpak
DebugWriteA ( " TPak Write: \n " ) ;
DebugWriteA ( " Address: %04X \n " , dwAddress ) ;
2016-01-27 09:11:59 +00:00
# ifdef ENABLE_RAWPAK_DEBUG
2020-02-09 11:36:49 +00:00
DebugWriteA ( " Data: " ) ;
for ( int i = 0 ; i < 32 ; i + + )
{
if ( ( i < 31 ) & & ( ( i & 7 ) = = 0 ) )
{
DebugWriteA ( " \n " ) ;
}
DebugWriteByteA ( Data [ i ] ) ;
if ( i < 31 )
{
DebugWriteA ( " , " ) ;
}
}
DebugWriteA ( " \n " ) ;
2016-01-27 09:11:59 +00:00
# endif // #ifdef ENABLE_RAWPAK_DEBUG
2020-02-09 11:36:49 +00:00
switch ( dwAddress > > 12 )
{
case 0x8 : // if ((dwAddress >= 0x8000) && (dwAddress <= 0x8FFF))
if ( Data [ 0 ] = = 0xFE )
{
DebugWriteA ( " Cart Disable \n " ) ;
tPak - > iEnableState = false ;
}
else if ( Data [ 0 ] = = 0x84 )
{
DebugWriteA ( " Cart Enable \n " ) ;
tPak - > iEnableState = true ;
}
else
{
DebugWriteA ( " WARNING: Unusual Cart Enable/Disable \n " ) ;
DebugWriteA ( " Address: " ) ;
DebugWriteWordA ( dwAddress ) ;
DebugWriteA ( " \n " ) ;
DebugWriteA ( " Data: " ) ;
DebugWriteByteA ( Data [ 0 ] ) ;
DebugWriteA ( " \n " ) ;
}
break ;
case 0xA : // if ((dwAddress >= 0xA000) && (dwAddress <= 0xAFFF))
if ( tPak - > iEnableState = = true )
{
tPak - > iCurrentBankNo = Data [ 0 ] ;
DebugWriteA ( " Set TPak Bank No:%02X \n " , Data [ 0 ] ) ;
}
break ;
case 0xB : // if ((dwAddress >= 0xB000) && (dwAddress <= 0xBFFF))
if ( tPak - > iEnableState = = true )
{
tPak - > iCurrentAccessMode = Data [ 0 ] & 1 ;
tPak - > iAccessModeChanged = 4 ;
DebugWriteA ( " Set TPak Access Mode: %04X \n " , tPak - > iCurrentAccessMode ) ;
if ( ( Data [ 0 ] ! = 1 ) & & ( Data [ 0 ] ! = 0 ) )
{
DebugWriteA ( " WARNING: Unusual Access Mode Change \n " ) ;
DebugWriteA ( " Address: " ) ;
DebugWriteWordA ( dwAddress ) ;
DebugWriteA ( " \n " ) ;
DebugWriteA ( " Data: " ) ;
DebugWriteByteA ( Data [ 0 ] ) ;
DebugWriteA ( " \n " ) ;
}
}
break ;
case 0xC :
case 0xD :
case 0xE :
case 0xF : // if (dwAddress >= 0xC000)
tPak - > gbCart . ptrfnWriteCart ( & tPak - > gbCart , ( ( dwAddress & 0xFFE0 ) - 0xC000 ) + ( ( tPak - > iCurrentBankNo & 3 ) * 0x4000 ) , Data ) ;
if ( tPak - > gbCart . hRamFile ! = NULL )
SetTimer ( g_strEmuInfo . hMainWindow , PAK_TRANSFER , 2000 , ( TIMERPROC ) WritebackProc ) ; // if we go 2 seconds without a write, call the Writeback proc (which will flush the cache)
break ;
default :
DebugWriteA ( " WARNING: Unusual Pak Write \n " ) ;
DebugWriteA ( " Address: %04X \n " , dwAddress ) ;
} // end switch (dwAddress >> 12)
Data [ 32 ] = DataCRC ( Data , 32 ) ;
bReturn = RD_OK ;
}
break ;
/*case PAK_VOICE:
break ; */
case PAK_ADAPTOID :
if ( ( dwAddress = = PAK_IO_RUMBLE ) & & ( ( ADAPTOIDPAK * ) g_pcControllers [ iControl ] . pPakData ) - > fRumblePak )
{
if ( DirectRumbleCommand ( iControl , * Data ) = = DI_OK )
{
Data [ 32 ] = DataCRC ( Data , 32 ) ;
bReturn = RD_OK ;
}
}
else
{
if ( WriteAdaptoidPak ( iControl , dwAddress , Data ) = = DI_OK )
{
Data [ 32 ] = DataCRC ( Data , 32 ) ;
if ( dwAddress = = 0x8000 )
( ( ADAPTOIDPAK * ) g_pcControllers [ iControl ] . pPakData ) - > bIdentifier = Data [ 0 ] ;
bReturn = RD_OK ;
}
}
break ;
/*case PAK_NONE:
break ; */
}
return bReturn ;
2016-01-27 09:11:59 +00:00
}
void SaveControllerPak ( const int iControl )
{
2020-02-09 11:36:49 +00:00
if ( ! g_pcControllers [ iControl ] . pPakData )
return ;
switch ( * ( BYTE * ) g_pcControllers [ iControl ] . pPakData )
{
case PAK_MEM :
{
MEMPAK * mPak = ( MEMPAK * ) g_pcControllers [ iControl ] . pPakData ;
if ( ! mPak - > fReadonly )
FlushViewOfFile ( mPak - > aMemPakData , PAK_MEM_SIZE ) ; // we've already written the stuff, just flush the cache
}
break ;
case PAK_RUMBLE :
break ;
case PAK_TRANSFER :
{
LPTRANSFERPAK tPak = ( LPTRANSFERPAK ) g_pcControllers [ iControl ] . pPakData ;
// here the changes( if any ) in the SRAM should be saved
if ( tPak - > gbCart . hRamFile ! = NULL )
{
SaveCart ( & tPak - > gbCart , g_pcControllers [ iControl ] . szTransferSave , _T ( " " ) ) ;
DebugWriteA ( " *** Save Transfer Pak *** \n " ) ;
}
}
break ;
case PAK_VOICE :
break ;
case PAK_ADAPTOID :
break ;
/*case PAK_NONE:
break ; */
}
2016-01-27 09:11:59 +00:00
}
// if there is pPakData for the controller, does any closing of handles before freeing the pPakData struct and setting it to NULL
// also sets fPakInitialized to false
void CloseControllerPak ( const int iControl )
{
2020-02-09 11:36:49 +00:00
if ( ! g_pcControllers [ iControl ] . pPakData )
return ;
g_pcControllers [ iControl ] . fPakInitialized = false ;
switch ( * ( BYTE * ) g_pcControllers [ iControl ] . pPakData )
{
case PAK_MEM :
{
MEMPAK * mPak = ( MEMPAK * ) g_pcControllers [ iControl ] . pPakData ;
if ( mPak - > fReadonly )
{
P_free ( mPak - > aMemPakData ) ;
mPak - > aMemPakData = NULL ;
}
else
{
FlushViewOfFile ( mPak - > aMemPakData , PAK_MEM_SIZE ) ;
// if it's a dexsave, our original mapped view is not aMemPakData
UnmapViewOfFile ( mPak - > fDexSave ? mPak - > aMemPakData - PAK_MEM_DEXOFFSET : mPak - > aMemPakData ) ;
if ( mPak - > hMemPakHandle ! = NULL )
CloseHandle ( mPak - > hMemPakHandle ) ;
}
}
break ;
case PAK_RUMBLE :
ReleaseEffect ( g_apdiEffect [ iControl ] ) ;
g_apdiEffect [ iControl ] = NULL ;
break ;
case PAK_TRANSFER :
{
LPTRANSFERPAK tPak = ( LPTRANSFERPAK ) g_pcControllers [ iControl ] . pPakData ;
UnloadCart ( & tPak - > gbCart ) ;
DebugWriteA ( " *** Close Transfer Pak *** \n " ) ;
// close files and free any additionally ressources
}
break ;
case PAK_VOICE :
break ;
case PAK_ADAPTOID :
break ;
/*case PAK_NONE:
break ; */
}
freePakData ( & g_pcControllers [ iControl ] ) ;
return ;
2016-01-27 09:11:59 +00:00
}
// returns the number of remaining blocks in a mempak
// aNoteSizes should be an array of 16 bytes, which will be overwritten with the size in blocks of each note
inline WORD CountBlocks ( const unsigned char * bMemPakBinary , LPBYTE aNoteSizes )
{
2020-02-09 11:36:49 +00:00
WORD wRemainingBlocks = 123 ;
BYTE bNextIndex ;
int i = 0 ;
while ( i < 16 & & wRemainingBlocks < = 123 )
{
aNoteSizes [ i ] = 0 ;
bNextIndex = bMemPakBinary [ 0x307 + ( i * 0x20 ) ] ;
while ( ( bNextIndex > = 5 ) & & ( aNoteSizes [ i ] < wRemainingBlocks ) )
{
aNoteSizes [ i ] + + ;
bNextIndex = bMemPakBinary [ 0x101 + ( bNextIndex * 2 ) ] ;
}
if ( aNoteSizes [ i ] > wRemainingBlocks )
wRemainingBlocks = 0xFF ;
else
wRemainingBlocks - = aNoteSizes [ i ] ;
i + + ;
}
return wRemainingBlocks ;
2016-01-27 09:11:59 +00:00
}
void FormatMemPak ( LPBYTE aMemPak )
{
2020-02-09 11:36:49 +00:00
size_t iRand , n ;
2016-01-27 09:11:59 +00:00
2020-02-09 11:36:49 +00:00
FillMemory ( aMemPak , 0x100 , 0xFF ) ;
aMemPak [ 0 ] = 0x81 ;
2016-01-27 09:11:59 +00:00
2020-02-09 11:36:49 +00:00
// generate a valid code( i hope i can calculate it one day)
BYTE aValidCodes [ ] = { 0x12 , 0xC5 , 0x8F , 0x6F , 0xA4 , 0x28 , 0x5B , 0xCA } ;
BYTE aCode [ 8 ] ;
2016-01-27 09:11:59 +00:00
2020-02-09 11:36:49 +00:00
iRand = ( ( size_t ) aMemPak / 4 ) + ( ( size_t ) g_strEmuInfo . hMainWindow / 4 ) ;
iRand % = sizeof ( aValidCodes ) / 8 ;
for ( n = 0 ; n < 8 ; n + + )
aCode [ n ] = aValidCodes [ n + iRand ] ;
2016-01-27 09:11:59 +00:00
2020-02-09 11:36:49 +00:00
//----------
2016-01-27 09:11:59 +00:00
2020-02-09 11:36:49 +00:00
aMemPak [ 0x20 + 0 ] = aMemPak [ 0x60 + 0 ] = aMemPak [ 0x80 + 0 ] = aMemPak [ 0xC0 + 0 ] = 0xFF ;
aMemPak [ 0x20 + 1 ] = aMemPak [ 0x60 + 1 ] = aMemPak [ 0x80 + 1 ] = aMemPak [ 0xC0 + 1 ] = 0xFF ;
aMemPak [ 0x20 + 2 ] = aMemPak [ 0x60 + 2 ] = aMemPak [ 0x80 + 2 ] = aMemPak [ 0xC0 + 2 ] = 0xFF ;
aMemPak [ 0x20 + 3 ] = aMemPak [ 0x60 + 3 ] = aMemPak [ 0x80 + 3 ] = aMemPak [ 0xC0 + 3 ] = 0xFF ;
2016-01-27 09:11:59 +00:00
2020-02-09 11:36:49 +00:00
aMemPak [ 0x20 + 4 ] = aMemPak [ 0x60 + 4 ] = aMemPak [ 0x80 + 4 ] = aMemPak [ 0xC0 + 4 ] = aCode [ 0 ] ;
aMemPak [ 0x20 + 5 ] = aMemPak [ 0x60 + 5 ] = aMemPak [ 0x80 + 5 ] = aMemPak [ 0xC0 + 5 ] = aCode [ 1 ] ;
aMemPak [ 0x20 + 6 ] = aMemPak [ 0x60 + 6 ] = aMemPak [ 0x80 + 6 ] = aMemPak [ 0xC0 + 6 ] = aCode [ 2 ] ;
aMemPak [ 0x20 + 7 ] = aMemPak [ 0x60 + 7 ] = aMemPak [ 0x80 + 7 ] = aMemPak [ 0xC0 + 7 ] = aCode [ 3 ] ;
2016-01-27 09:11:59 +00:00
2020-02-09 11:36:49 +00:00
//aMemPak[0x30+9] = aMemPak[0x70+9] = aMemPak[0x90+9] = aMemPak[0xD0+9] = 0x01; // not sure
aMemPak [ 0x30 + 10 ] = aMemPak [ 0x70 + 10 ] = aMemPak [ 0x90 + 10 ] = aMemPak [ 0xD0 + 10 ] = 0x01 ;
2016-01-27 09:11:59 +00:00
2020-02-09 11:36:49 +00:00
aMemPak [ 0x30 + 12 ] = aMemPak [ 0x70 + 12 ] = aMemPak [ 0x90 + 12 ] = aMemPak [ 0xD0 + 12 ] = aCode [ 4 ] ;
aMemPak [ 0x30 + 13 ] = aMemPak [ 0x70 + 13 ] = aMemPak [ 0x90 + 13 ] = aMemPak [ 0xD0 + 13 ] = aCode [ 5 ] ;
aMemPak [ 0x30 + 14 ] = aMemPak [ 0x70 + 14 ] = aMemPak [ 0x90 + 14 ] = aMemPak [ 0xD0 + 14 ] = aCode [ 6 ] ;
aMemPak [ 0x30 + 15 ] = aMemPak [ 0x70 + 15 ] = aMemPak [ 0x90 + 15 ] = aMemPak [ 0xD0 + 15 ] = aCode [ 7 ] ;
2016-01-27 09:11:59 +00:00
2020-02-09 11:36:49 +00:00
// Index
ZeroMemory ( & aMemPak [ 0x100 ] , 0x400 ) ;
2016-01-27 09:11:59 +00:00
2020-02-09 11:36:49 +00:00
aMemPak [ 0x100 + 1 ] = aMemPak [ 0x200 + 1 ] = 0x71 ;
for ( int i = 0x00b ; i < 0x100 ; i + = 2 )
aMemPak [ 0x100 + i ] = aMemPak [ 0x200 + i ] = 03 ;
2016-01-27 09:11:59 +00:00
2020-02-09 11:36:49 +00:00
FillMemory ( & aMemPak [ 0x500 ] , 0x7B00 , 0xFF ) ;
2016-01-27 09:11:59 +00:00
}
// Translates a mempak header into a real Unicode string, for display in the Mempaks window
// bNote is now where you want to START translating, Text is where the output gets sent, iChars is the number of TCHARs you want translated
// return value is the number of characters actually translated
// Text automatically gets a terminating null, so make sure there's enough space.
int TranslateNotesW ( const unsigned char * bNote , LPWSTR Text , const int iChars )
{
2020-02-09 11:36:49 +00:00
WCHAR aSpecial [ ] = { 0x0021 , 0x0022 , 0x0023 , 0x0060 , 0x002A , 0x002B , 0x002C , 0x002D , 0x002E , 0x002F , 0x003A , 0x003D , 0x003F , 0x0040 , 0x2122 , 0x00A9 , 0x00AE } ;
// { '!' , '\"', '#' , '`' , '*' , '+' , ',' , '-' , '.' , '/' , ':' , '=' , '?' , '>' , 'tm', '(c)', '(r)' };
const WCHAR aJmap [ ] = { // map of Japanese characters N64 to UTF-16, starting at 0x45
0x00A1 , 0x30A3 , 0x30A5 , 0x30A7 , 0x30A9 , 0x30E3 , 0x30E5 , 0x30E7 , 0x30C3 , 0x30F2 , // small a-i-u-e-o, next are probably small ya-yu-yo, small tsu, wo
0x30F3 , // 0x4F -> 'n'
0x30A2 , 0x30A4 , 0x30A6 , 0x30A8 , 0x30AA , 0x30AB , 0x30AD , 0x30AF , 0x30B1 , 0x30B3 , 0x30B5 , 0x30B7 , 0x30B9 , 0x30BB , 0x30BD , // nil-K-S
0x30BF , 0x30C1 , 0x30C4 , 0x30C6 , 0x30C8 , 0x30CA , 0x30CB , 0x30CC , 0x30CD , 0x30CE , 0x30CF , 0x30D2 , 0x30D5 , 0x30D8 , 0x30DB , // T-N-H
0x30DE , 0x30DF , 0x30E0 , 0x30E1 , 0x30E2 , 0x30E4 , 0x30E6 , 0x30E8 , 0x30E9 , 0x30EA , 0x30EB , 0x30EC , 0x30ED , // M-Y-R
0x30EF , // 'wa'
0x30AC , 0x30AE , 0x30B0 , 0x30B2 , 0x30B4 , 0x30B6 , 0x30B8 , 0x30BA , 0x30BC , 0x30BE , 0x30C0 , 0x30C2 , 0x30C5 , 0x30C7 , 0x30C9 , // G-Z-D
0x30D0 , 0x30D3 , 0x30D6 , 0x30D9 , 0x30DC , 0x30D1 , 0x30D4 , 0x30D7 , 0x30DA , 0x30DD // B-P
} ;
int iReturn = 0 ;
while ( iChars - iReturn > 0 & & * bNote )
{
BYTE b = * bNote ;
if ( b < = 0x0F ) // translate Icons as Spaces
* Text = 0x0020 ;
else if ( b < = 0x19 ) // Numbers
* Text = 0x0020 + b ;
else if ( b < = 0x33 ) // English characters
* Text = 0x0047 + b ;
else if ( b < = 0x44 ) // special symbols
* Text = aSpecial [ b - 0x34 ] ;
else if ( b < = 0x94 ) // Japanese (halfwidth katakana, mapped similarly to JIS X 0201 but not enough that we can use a simple codepage)
{
aSpecial [ 7 ] = 0x30fc ; // change regular dash to Japanese "long sound dash"
* Text = aJmap [ b - 0x45 ] ;
}
else // unknown
* Text = 0x00A4 ; // unknown characters become "currency sign" (looks like a letter o superimposed on an x)
Text + + ;
iReturn + + ;
bNote + + ;
}
* Text = L ' \0 ' ;
return iReturn ;
2016-01-27 09:11:59 +00:00
}
// TODO: rename this function! It serves a completely different function from TranslateNotesW
// Translates a mempak header into an ASCII string for output to .a64 file
// bNote is now where you want to START translating, Text is where the output gets sent, iChars is the number of chars you want translated
// return value is the number of characters actually translated
// Text automatically gets a terminating null, so make sure there's enough space.
int TranslateNotesA ( const unsigned char * bNote , LPSTR Text , const int iChars )
{
2020-02-09 11:36:49 +00:00
const UCHAR aSpecial [ ] = { 0x21 , 0x22 , 0x23 , 0x60 , 0x2A , 0x2B , 0x2C , 0x2D , 0x2E , 0x2F , 0x3A , 0x3D , 0x3F , 0x40 , 0x99 , 0xA9 , 0xAE } ;
// { '!' , '\"', '#' , '`' , '*' , '+' , ',' , '-' , '.' , '/' , ':' , '=' , '?' , '>' , 'tm', '(c)','(r)' };
int iReturn = 0 ;
while ( iChars - iReturn > 0 & & * bNote )
{
BYTE b = * bNote ;
if ( b < = 0x0F ) // translate Icons as Spaces
* Text = 0x20 ;
else if ( b < = 0x19 ) // Numbers
* Text = 0x20 + b ;
else if ( b < = 0x33 ) // English characters
* Text = 0x47 + b ;
else if ( b < = 0x44 ) // special symbols
* Text = aSpecial [ b - 0x34 ] ;
else if ( b < = 0x94 ) // Japan
* Text = 0xC0 + ( b % 40 ) ;
else // unknown
* Text = ( UCHAR ) 0xA4 ; // HACK: this will screw up any save with unknown characters
Text + + ;
iReturn + + ;
bNote + + ;
}
* Text = ' \0 ' ;
return iReturn ;
2016-01-27 09:11:59 +00:00
}
int ReverseNotesA ( LPCSTR Text , LPBYTE Note )
{
2020-02-09 11:36:49 +00:00
const UCHAR aSpecial [ ] = { 0x21 , 0x22 , 0x23 , 0x60 , 0x2A , 0x2B , 0x2C , 0x2D , 0x2E , 0x2F , 0x3A , 0x3D , 0x3F , 0x40 , 0x74 , 0xA9 , 0xAE } ;
// { '!' , '\"', '#' , '`' , '*' , '+' , ',' , '-' , '.' , '/' , ':' , '=' , '?' , '>' , 'tm', '(r)','(c)' };
LPCSTR TextPos = Text ;
while ( * TextPos ! = ' \0 ' )
{
char c = * TextPos ;
* Note = 0x0F ;
if ( c > = ' 0 ' & & c < = ' 9 ' )
* Note = c - ' 0 ' + 0x10 ;
else if ( c > = ' A ' & & c < = ' Z ' )
* Note = c - ' A ' + 0x1A ;
else if ( c > = ' a ' & & c < = ' z ' )
* Note = c - ' a ' + 0x1A ;
else
{
for ( int i = 0 ; i < ARRAYSIZE ( aSpecial ) ; + + i )
{
if ( c = = aSpecial [ i ] )
{
* Note = i + 0x34 ;
break ;
}
}
}
TextPos + + ;
Note + + ;
}
return TextPos - Text ;
2016-01-27 09:11:59 +00:00
}
WORD ShowMemPakContent ( const unsigned char * bMemPakBinary , HWND hListWindow )
{
2020-02-09 11:36:49 +00:00
BYTE bMemPakValid = MPAK_OK ;
TCHAR szBuffer [ 40 ] ;
BYTE aNoteSizes [ 16 ] ;
bool bFirstChar ;
LVITEM lvItem ;
lvItem . mask = LVIF_TEXT | LVIF_PARAM ;
lvItem . iItem = 0 ;
lvItem . iSubItem = 0 ;
lvItem . pszText = szBuffer ;
int i = 0 ,
nNotes = 0 ,
iSum = 0 ,
iRemainingBlocks = 0 ;
for ( i = 0x10A ; i < 0x200 ; i + + )
iSum + = bMemPakBinary [ i ] ;
if ( ( ( iSum % 256 ) = = bMemPakBinary [ 0x101 ] ) )
{
iRemainingBlocks = CountBlocks ( bMemPakBinary , aNoteSizes ) ;
if ( iRemainingBlocks < = 123 )
{
for ( lvItem . lParam = 0 ; lvItem . lParam < 16 ; lvItem . lParam + + )
{
if ( bMemPakBinary [ 0x300 + ( lvItem . lParam * 32 ) ] | |
bMemPakBinary [ 0x301 + ( lvItem . lParam * 32 ) ] | |
bMemPakBinary [ 0x302 + ( lvItem . lParam * 32 ) ] )
{
int iChars = TranslateNotes ( & bMemPakBinary [ 0x300 + ( lvItem . lParam * 32 ) + 0x10 ] , szBuffer , 16 ) ;
if ( TranslateNotes ( & bMemPakBinary [ 0x300 + ( lvItem . lParam * 32 ) + 0x0C ] , & szBuffer [ iChars + 1 ] , 1 ) )
szBuffer [ iChars ] = _T ( ' _ ' ) ;
bFirstChar = true ;
for ( i = 0 ; i < ( int ) lstrlen ( szBuffer ) ; i + + )
{
if ( szBuffer [ i ] = = ' ' )
bFirstChar = true ;
else
{
if ( bFirstChar & & ( szBuffer [ i ] > = ' a ' ) & & ( szBuffer [ i ] < = ' z ' ) )
{
bFirstChar = false ;
szBuffer [ i ] - = 0x20 ;
}
}
}
i = ListView_InsertItem ( hListWindow , & lvItem ) ;
switch ( bMemPakBinary [ 0x303 + ( lvItem . lParam * 32 ) ] )
{
case 0x00 :
LoadString ( g_hResourceDLL , IDS_P_MEM_NOREGION , szBuffer , 40 ) ;
break ;
case 0x37 :
LoadString ( g_hResourceDLL , IDS_P_MEM_BETA , szBuffer , 40 ) ;
break ;
case 0x41 :
LoadString ( g_hResourceDLL , IDS_P_MEM_NTSC , szBuffer , 40 ) ;
break ;
case 0x44 :
LoadString ( g_hResourceDLL , IDS_P_MEM_GERMANY , szBuffer , 40 ) ;
break ;
case 0x45 :
LoadString ( g_hResourceDLL , IDS_P_MEM_USA , szBuffer , 40 ) ;
break ;
case 0x46 :
LoadString ( g_hResourceDLL , IDS_P_MEM_FRANCE , szBuffer , 40 ) ;
break ;
case 0x49 :
LoadString ( g_hResourceDLL , IDS_P_MEM_ITALY , szBuffer , 40 ) ;
break ;
case 0x4A :
LoadString ( g_hResourceDLL , IDS_P_MEM_JAPAN , szBuffer , 40 ) ;
break ;
case 0x50 :
LoadString ( g_hResourceDLL , IDS_P_MEM_EUROPE , szBuffer , 40 ) ;
break ;
case 0x53 :
LoadString ( g_hResourceDLL , IDS_P_MEM_SPAIN , szBuffer , 40 ) ;
break ;
case 0x55 :
LoadString ( g_hResourceDLL , IDS_P_MEM_AUSTRALIA , szBuffer , 40 ) ;
break ;
case 0x58 :
case 0x59 :
LoadString ( g_hResourceDLL , IDS_P_MEM_PAL , szBuffer , 40 ) ;
break ;
default :
{
TCHAR szTemp [ 40 ] ;
LoadString ( g_hResourceDLL , IDS_P_MEM_UNKNOWNREGION , szTemp , 40 ) ;
wsprintf ( szBuffer , szTemp , bMemPakBinary [ 0x303 + ( lvItem . lParam * 32 ) ] ) ;
}
}
ListView_SetItemText ( hListWindow , i , 1 , szBuffer ) ;
wsprintf ( szBuffer , _T ( " %i " ) , aNoteSizes [ lvItem . lParam ] ) ;
ListView_SetItemText ( hListWindow , i , 2 , szBuffer ) ;
nNotes + + ;
}
}
}
else
bMemPakValid = MPAK_DAMAGED ;
}
else
bMemPakValid = MPAK_DAMAGED ;
return MAKEWORD ( ( BYTE ) iRemainingBlocks , bMemPakValid ) ;
2016-01-27 09:11:59 +00:00
}
void HextoTextA ( const unsigned char * Data , LPSTR szText , const int nBytes )
{
2020-02-09 11:36:49 +00:00
const char acValues [ ] = { ' 0 ' , ' 1 ' , ' 2 ' , ' 3 ' , ' 4 ' , ' 5 ' , ' 6 ' , ' 7 ' , ' 8 ' , ' 9 ' ,
' A ' , ' B ' , ' C ' , ' D ' , ' E ' , ' F ' } ;
for ( int i = 0 ; i < nBytes ; i + + )
{
BYTE byte = * Data ;
szText [ 0 ] = acValues [ ( byte > > 4 ) & 0x0F ] ;
szText [ 1 ] = acValues [ byte & 0x0F ] ;
+ + Data ;
szText + = 2 ;
}
* szText = ' \0 ' ;
2016-01-27 09:11:59 +00:00
}
// used when reading in a Note file, to convert text to binary (unserialize)
void TexttoHexA ( LPCSTR szText , LPBYTE Data , const int nBytes )
{
2020-02-09 11:36:49 +00:00
bool fLowByte = false ;
LPCSTR endText = szText + nBytes * 2 ;
for ( ; szText < endText ; + + szText )
{
BYTE bByte = 0 ;
if ( ( ' 0 ' < = * szText ) & & ( * szText < = ' 9 ' ) )
bByte = * szText - ' 0 ' ;
else
{
if ( ( ' A ' < = * szText ) & & ( * szText < = ' F ' ) )
bByte = szText [ 0 ] - ' A ' + 10 ;
else if ( ( ' a ' < = * szText ) & & ( * szText < = ' f ' ) )
bByte = szText [ 0 ] - ' a ' + 10 ;
}
if ( ! fLowByte )
* Data = bByte < < 4 ;
else
{
* Data | = bByte ;
+ + Data ;
}
fLowByte = ! fLowByte ;
}
2016-01-27 09:11:59 +00:00
}
bool SaveNoteFileA ( const unsigned char * aMemPak , const int iNote , LPCTSTR pszFileName )
{
2020-02-09 11:36:49 +00:00
BYTE aNoteSizes [ 16 ] ;
LPBYTE aNote ;
bool bReturn = false ;
if ( CountBlocks ( aMemPak , aNoteSizes ) > 123 )
return false ;
aNote = ( LPBYTE ) P_malloc ( aNoteSizes [ iNote ] * 0x100 + 32 ) ;
if ( ! aNote )
return false ;
CopyMemory ( aNote , & aMemPak [ 0x300 + iNote * 32 ] , 32 ) ;
BYTE bNextIndex = aNote [ 0x7 ] ;
int iBlock = 0 ;
while ( iBlock < aNoteSizes [ iNote ] )
{
CopyMemory ( & aNote [ 32 + iBlock * 0x100 ] , & aMemPak [ bNextIndex * 0x100 ] , 0x100 ) ;
bNextIndex = aMemPak [ 0x101 + ( bNextIndex * 2 ) ] ;
iBlock + + ;
}
HANDLE hFile = CreateFile ( pszFileName , GENERIC_WRITE , 0 , NULL , CREATE_ALWAYS , 0 , NULL ) ;
if ( hFile ! = INVALID_HANDLE_VALUE )
{
SetFilePointer ( hFile , 0L , NULL , FILE_BEGIN ) ;
char szLine [ 70 ] ;
DWORD dwBytesWritten = 0 ;
lstrcpyA ( szLine , " a64-notes \r \n description of game save goes here... \r \n a64-data \r \n " ) ;
WriteFile ( hFile , szLine , lstrlenA ( szLine ) , & dwBytesWritten , NULL ) ;
CopyMemory ( szLine , aNote , 4 ) ;
szLine [ 4 ] = ' ' ;
szLine [ 5 ] = aNote [ 4 ] ;
szLine [ 6 ] = aNote [ 5 ] ;
szLine [ 7 ] = ' ' ;
HextoTextA ( & aNote [ 8 ] , & szLine [ 8 ] , 2 ) ;
int pos = 12 ;
szLine [ pos + + ] = ' ' ;
szLine [ pos + + ] = aNote [ 0x0A ] + ' 0 ' ;
szLine [ pos + + ] = ' ' ;
szLine [ pos + + ] = ' { ' ;
pos + = TranslateNotesA ( & aNote [ 0x0C ] , & szLine [ pos ] , 1 ) ;
szLine [ pos + + ] = ' } ' ;
szLine [ pos + + ] = ' ' ;
szLine [ pos + + ] = ' { ' ;
pos + = TranslateNotesA ( & aNote [ 0x10 ] , & szLine [ pos ] , 16 ) ;
lstrcatA ( szLine , " } \r \n " ) ;
WriteFile ( hFile , szLine , lstrlenA ( szLine ) , & dwBytesWritten , NULL ) ;
for ( int i = 32 ; i < aNoteSizes [ iNote ] * 0x100 + 32 ; i + = 32 )
{
HextoTextA ( & aNote [ i ] , szLine , 32 ) ;
WriteFile ( hFile , szLine , lstrlenA ( szLine ) , & dwBytesWritten , NULL ) ;
WriteFile ( hFile , " \r \n " , 2 , & dwBytesWritten , NULL ) ;
}
WriteFile ( hFile , " a64-crc \r \n " , 9 , & dwBytesWritten , NULL ) ;
// TODO: insert crc here
lstrcpynA ( szLine , " 00000000 \r \n " , 70 ) ;
WriteFile ( hFile , szLine , lstrlenA ( szLine ) , & dwBytesWritten , NULL ) ;
//
WriteFile ( hFile , " a64-end \r \n " , 9 , & dwBytesWritten , NULL ) ;
SetEndOfFile ( hFile ) ;
bReturn = true ;
CloseHandle ( hFile ) ;
}
else
ErrorMessage ( IDS_ERR_NOTEREAD , GetLastError ( ) , false ) ;
P_free ( aNote ) ;
return bReturn ;
2016-01-27 09:11:59 +00:00
}
// read a Note from a file pszFileName (.a64 format), and insert it into the given MemPak
// returns true on success, false otherwise
bool InsertNoteFile ( LPBYTE aMemPak , LPCTSTR pszFileName )
{
2020-02-09 11:36:49 +00:00
// bool bReturn = false;
FILE * nFile = NULL ;
if ( ( nFile = _tfopen ( pszFileName , _T ( " r " ) ) ) ! = NULL )
{
char szLine [ 128 ] ;
fpos_t pDataStart ;
while ( fgets ( szLine , sizeof ( szLine ) - 1 , nFile ) )
{
if ( ! strncmp ( " a64-data " , szLine , 8 ) )
{
fgetpos ( nFile , & pDataStart ) ;
break ;
}
}
// assumes the file keeps going...
// discard the next line
fgets ( szLine , sizeof ( szLine ) - 1 , nFile ) ; // not really necessary to check for EOF, as it will fail gracefully when dwNoteSize is zero.
DWORD dwNoteSize = 0 ;
while ( fgets ( szLine , sizeof ( szLine ) - 1 , nFile ) & & strncmp ( " a64-crc " , szLine , 7 ) )
{
dwNoteSize + + ;
}
dwNoteSize / = 8 ;
BYTE aNoteSizes [ 16 ] ;
WORD wRemainingBlocks ;
int i ,
ifreeNote = - 1 ;
wRemainingBlocks = CountBlocks ( aMemPak , aNoteSizes ) ;
if ( dwNoteSize < = 0 )
{
ErrorMessage ( IDS_ERR_NOTEREAD , 0 , false ) ;
fclose ( nFile ) ;
return false ;
}
else
{
if ( wRemainingBlocks < dwNoteSize )
{
ErrorMessage ( IDS_ERR_MEMPAK_SPACE , 0 , false ) ;
fclose ( nFile ) ;
return false ;
}
else
{
i = 0 ;
while ( ( i < 16 ) & & ( ifreeNote = = - 1 ) )
{
if ( aNoteSizes [ i ] = = 0 )
ifreeNote = i ;
i + + ;
}
if ( ifreeNote = = - 1 )
{
2016-01-27 09:11:59 +00:00
ErrorMessage ( IDS_ERR_MEMPAK_NONOTES , 0 , false ) ;
2020-02-09 11:36:49 +00:00
fclose ( nFile ) ;
return false ;
}
}
}
// HEADER START
// .a64 header should look something like this:
// NBCE 01 0203 0 {} {BLASTCORPS GAME}
// first 4 chars are the first 4 bytes
// next 2 chars are the next 2 bytes
// next 4 chars are bytes 8 and 9, in hex (huh?)
// next character is byte 10, in hex (but only one character this time)
// now we've got two sets of braces... the first one contains byte 12 in encoded form (use ReverseNotesA)
// the second one should contain bytes 16 through 31 (ReverseNotesA)
BYTE * pBlock ;
fsetpos ( nFile , & pDataStart ) ;
if ( ! fgets ( szLine , sizeof ( szLine ) - 1 , nFile ) )
{
ErrorMessage ( IDS_ERR_NOTEEOF , 0 , false ) ;
fclose ( nFile ) ;
return false ;
}
szLine [ strlen ( szLine ) - 1 ] = ' \0 ' ; // remove newline
pBlock = & aMemPak [ 0x300 + ifreeNote * 32 ] ;
CopyMemory ( pBlock , szLine , 4 ) ;
pBlock [ 4 ] = szLine [ 5 ] ;
pBlock [ 5 ] = szLine [ 6 ] ;
TexttoHexA ( & szLine [ 8 ] , & pBlock [ 8 ] , 2 ) ;
pBlock [ 10 ] = szLine [ 13 ] - ' 0 ' ;
int len = lstrlenA ( szLine ) ;
i = 16 ;
while ( ( szLine [ i ] ! = ' } ' ) & & ( i < len ) )
i + + ;
szLine [ i ] = ' \0 ' ;
i + = ReverseNotesA ( & szLine [ 16 ] , & pBlock [ 12 ] ) ;
while ( ( szLine [ i ] ! = ' { ' ) & & ( i < len ) )
i + + ;
if ( i < len )
{
int start = i + 1 ;
while ( ( szLine [ i ] ! = ' } ' ) & & ( i < len ) )
i + + ;
if ( i < len )
{
szLine [ i ] = ' \0 ' ;
ReverseNotesA ( & szLine [ start ] , & pBlock [ 16 ] ) ;
}
}
while ( ( szLine [ i ] ! = ' } ' ) & & ( i < len ) )
i + + ;
szLine [ i ] = ' \0 ' ;
// HEADER END
BYTE bDataBlock = 5 ;
pBlock = & pBlock [ 7 ] ;
BYTE * pDataBlock ;
while ( dwNoteSize > 0 )
{
while ( aMemPak [ 0x101 + bDataBlock * 2 ] ! = 0x03 )
bDataBlock + + ;
* pBlock = bDataBlock ;
pBlock = & aMemPak [ 0x101 + bDataBlock * 2 ] ;
pDataBlock = & aMemPak [ bDataBlock * 0x100 ] ;
for ( i = 0 ; i < 0x100 ; i + = 32 )
{
if ( ! fgets ( szLine , sizeof ( szLine ) - 1 , nFile ) )
{
ErrorMessage ( IDS_ERR_NOTEEOF , 0 , false ) ;
fclose ( nFile ) ;
return false ;
}
szLine [ strlen ( szLine ) - 1 ] = ' \0 ' ; // remove newline
TexttoHexA ( szLine , & pDataBlock [ i ] , 32 ) ;
}
bDataBlock + + ;
dwNoteSize - - ;
}
* pBlock = 0x01 ;
int iSum = 0 ;
for ( i = 0x10A ; i < 0x200 ; i + + )
iSum + = aMemPak [ i ] ;
aMemPak [ 0x101 ] = iSum % 256 ;
CopyMemory ( & aMemPak [ 0x200 ] , & aMemPak [ 0x100 ] , 0x100 ) ;
fclose ( nFile ) ;
return true ;
}
else
ErrorMessage ( IDS_ERR_NOTEREAD , 0 , false ) ;
return false ;
2016-01-27 09:11:59 +00:00
}
// Remove a mempak "Note"
// See "MemPak-Format.doc" for more info
bool RemoveNote ( LPBYTE aMemPak , const int iNote )
{
2020-02-09 11:36:49 +00:00
BYTE bBlock = aMemPak [ 0x307 + iNote * 32 ] ;
int iPos ;
while ( bBlock > = 0x05 )
{
iPos = 0x101 + bBlock * 2 ;
bBlock = aMemPak [ iPos ] ;
aMemPak [ iPos ] = 0x03 ;
}
int i = 0 , iSum = 0 ;
for ( i = 0x10A ; i < 0x200 ; i + + )
iSum + = aMemPak [ i ] ;
aMemPak [ 0x101 ] = iSum % 256 ;
CopyMemory ( & aMemPak [ 0x200 ] , & aMemPak [ 0x100 ] , 0x100 ) ;
ZeroMemory ( & aMemPak [ 0x300 + iNote * 32 ] , 32 ) ;
return true ;
2016-01-27 09:11:59 +00:00
}
BYTE AddressCRC ( const unsigned char * Address )
{
2020-02-09 11:36:49 +00:00
bool HighBit ;
WORD Data = MAKEWORD ( Address [ 1 ] , Address [ 0 ] ) ;
register BYTE Remainder = ( Data > > 11 ) & 0x1F ;
2016-01-27 09:11:59 +00:00
2020-02-09 11:36:49 +00:00
BYTE bBit = 5 ;
2016-01-27 09:11:59 +00:00
2020-02-09 11:36:49 +00:00
while ( bBit < 16 )
{
HighBit = ( Remainder & 0x10 ) ! = 0 ;
Remainder = ( Remainder < < 1 ) & 0x1E ;
2016-01-27 09:11:59 +00:00
2020-02-09 11:36:49 +00:00
Remainder + = ( bBit < 11 & & Data & ( 0x8000 > > bBit ) ) ? 1 : 0 ;
Remainder ^ = ( HighBit ) ? 0x15 : 0 ;
2016-01-27 09:11:59 +00:00
2020-02-09 11:36:49 +00:00
bBit + + ;
}
return Remainder ;
2016-01-27 09:11:59 +00:00
}
BYTE DataCRC ( const unsigned char * Data , const int iLength )
{
2020-02-09 11:36:49 +00:00
register BYTE Remainder = Data [ 0 ] ;
int iByte = 1 ;
BYTE bBit = 0 ;
2016-01-27 09:11:59 +00:00
2020-02-09 11:36:49 +00:00
while ( iByte < = iLength )
{
bool HighBit = ( ( Remainder & 0x80 ) ! = 0 ) ;
Remainder = Remainder < < 1 ;
2016-01-27 09:11:59 +00:00
2020-02-09 11:36:49 +00:00
Remainder + = ( iByte < iLength & & Data [ iByte ] & ( 0x80 > > bBit ) ) ? 1 : 0 ;
2016-01-27 09:11:59 +00:00
2020-02-09 11:36:49 +00:00
Remainder ^ = ( HighBit ) ? 0x85 : 0 ;
2016-01-27 09:11:59 +00:00
2020-02-09 11:36:49 +00:00
bBit + + ;
iByte + = bBit / 8 ;
bBit % = 8 ;
}
2016-01-27 09:11:59 +00:00
2020-02-09 11:36:49 +00:00
return Remainder ;
2016-01-27 09:11:59 +00:00
}
VOID CALLBACK WritebackProc ( HWND hWnd , UINT msg , UINT_PTR idEvent , DWORD dwTime )
{
2020-02-09 11:36:49 +00:00
KillTimer ( hWnd , idEvent ) ; // timer suicide
switch ( idEvent )
{
case PAK_MEM :
DebugWriteA ( " Mempak: WritebackProc flushed file writes \n " ) ;
for ( int i = 0 ; i < 4 ; i + + )
{
MEMPAK * mPak = ( MEMPAK * ) g_pcControllers [ i ] . pPakData ;
if ( mPak & & mPak - > bPakType = = PAK_MEM & & ! mPak - > fReadonly & & mPak - > hMemPakHandle ! = NULL )
FlushViewOfFile ( mPak - > aMemPakData , PAK_MEM_SIZE ) ;
}
return ;
case PAK_TRANSFER :
DebugWriteA ( " TPak: WritebackProc flushed file writes \n " ) ;
for ( int i = 0 ; i < 4 ; i + + )
{
LPTRANSFERPAK tPak = ( LPTRANSFERPAK ) g_pcControllers [ i ] . pPakData ;
if ( tPak & & tPak - > bPakType = = PAK_TRANSFER & & tPak - > bPakInserted & & tPak - > gbCart . hRamFile ! = NULL )
FlushViewOfFile ( tPak - > gbCart . RamData , ( tPak - > gbCart . RomData [ 0x149 ] = = 1 ) ? 0x0800 : tPak - > gbCart . iNumRamBanks * 0x2000 ) ;
}
return ;
}
2016-01-27 09:11:59 +00:00
}