diff --git a/fex/7z_C/7z.h b/fex/7z_C/7z.h index b7edd3ba..01c4cac6 100644 --- a/fex/7z_C/7z.h +++ b/fex/7z_C/7z.h @@ -1,203 +1,203 @@ -/* 7z.h -- 7z interface -2010-03-11 : Igor Pavlov : Public domain */ - -#ifndef __7Z_H -#define __7Z_H - -#include "7zBuf.h" - -EXTERN_C_BEGIN - -#define k7zStartHeaderSize 0x20 -#define k7zSignatureSize 6 -extern Byte k7zSignature[k7zSignatureSize]; -#define k7zMajorVersion 0 - -enum EIdEnum -{ - k7zIdEnd, - k7zIdHeader, - k7zIdArchiveProperties, - k7zIdAdditionalStreamsInfo, - k7zIdMainStreamsInfo, - k7zIdFilesInfo, - k7zIdPackInfo, - k7zIdUnpackInfo, - k7zIdSubStreamsInfo, - k7zIdSize, - k7zIdCRC, - k7zIdFolder, - k7zIdCodersUnpackSize, - k7zIdNumUnpackStream, - k7zIdEmptyStream, - k7zIdEmptyFile, - k7zIdAnti, - k7zIdName, - k7zIdCTime, - k7zIdATime, - k7zIdMTime, - k7zIdWinAttributes, - k7zIdComment, - k7zIdEncodedHeader, - k7zIdStartPos, - k7zIdDummy -}; - -typedef struct -{ - UInt32 NumInStreams; - UInt32 NumOutStreams; - UInt64 MethodID; - CBuf Props; -} CSzCoderInfo; - -void SzCoderInfo_Init(CSzCoderInfo *p); -void SzCoderInfo_Free(CSzCoderInfo *p, ISzAlloc *alloc); - -typedef struct -{ - UInt32 InIndex; - UInt32 OutIndex; -} CSzBindPair; - -typedef struct -{ - CSzCoderInfo *Coders; - CSzBindPair *BindPairs; - UInt32 *PackStreams; - UInt64 *UnpackSizes; - UInt32 NumCoders; - UInt32 NumBindPairs; - UInt32 NumPackStreams; - int UnpackCRCDefined; - UInt32 UnpackCRC; - - UInt32 NumUnpackStreams; -} CSzFolder; - -void SzFolder_Init(CSzFolder *p); -UInt64 SzFolder_GetUnpackSize(CSzFolder *p); -int SzFolder_FindBindPairForInStream(CSzFolder *p, UInt32 inStreamIndex); -UInt32 SzFolder_GetNumOutStreams(CSzFolder *p); -UInt64 SzFolder_GetUnpackSize(CSzFolder *p); - -SRes SzFolder_Decode(const CSzFolder *folder, const UInt64 *packSizes, - ILookInStream *stream, UInt64 startPos, - Byte *outBuffer, size_t outSize, ISzAlloc *allocMain); - -typedef struct -{ - UInt32 Low; - UInt32 High; -} CNtfsFileTime; - -typedef struct -{ - CNtfsFileTime MTime; - UInt64 Size; - UInt32 Crc; - UInt32 Attrib; - Byte HasStream; - Byte IsDir; - Byte IsAnti; - Byte CrcDefined; - Byte MTimeDefined; - Byte AttribDefined; -} CSzFileItem; - -void SzFile_Init(CSzFileItem *p); - -typedef struct -{ - UInt64 *PackSizes; - Byte *PackCRCsDefined; - UInt32 *PackCRCs; - CSzFolder *Folders; - CSzFileItem *Files; - UInt32 NumPackStreams; - UInt32 NumFolders; - UInt32 NumFiles; -} CSzAr; - -void SzAr_Init(CSzAr *p); -void SzAr_Free(CSzAr *p, ISzAlloc *alloc); - - -/* - SzExtract extracts file from archive - - *outBuffer must be 0 before first call for each new archive. - - Extracting cache: - If you need to decompress more than one file, you can send - these values from previous call: - *blockIndex, - *outBuffer, - *outBufferSize - You can consider "*outBuffer" as cache of solid block. If your archive is solid, - it will increase decompression speed. - - If you use external function, you can declare these 3 cache variables - (blockIndex, outBuffer, outBufferSize) as static in that external function. - - Free *outBuffer and set *outBuffer to 0, if you want to flush cache. -*/ - -typedef struct -{ - CSzAr db; - - UInt64 startPosAfterHeader; - UInt64 dataPos; - - UInt32 *FolderStartPackStreamIndex; - UInt64 *PackStreamStartPositions; - UInt32 *FolderStartFileIndex; - UInt32 *FileIndexToFolderIndexMap; - - size_t *FileNameOffsets; /* in 2-byte steps */ - CBuf FileNames; /* UTF-16-LE */ -} CSzArEx; - -void SzArEx_Init(CSzArEx *p); -void SzArEx_Free(CSzArEx *p, ISzAlloc *alloc); -UInt64 SzArEx_GetFolderStreamPos(const CSzArEx *p, UInt32 folderIndex, UInt32 indexInFolder); -int SzArEx_GetFolderFullPackSize(const CSzArEx *p, UInt32 folderIndex, UInt64 *resSize); - -/* -if dest == NULL, the return value specifies the required size of the buffer, - in 16-bit characters, including the null-terminating character. -if dest != NULL, the return value specifies the number of 16-bit characters that - are written to the dest, including the null-terminating character. */ - -size_t SzArEx_GetFileNameUtf16(const CSzArEx *p, size_t fileIndex, UInt16 *dest); - -SRes SzArEx_Extract( - const CSzArEx *db, - ILookInStream *inStream, - UInt32 fileIndex, /* index of file */ - UInt32 *blockIndex, /* index of solid block */ - Byte **outBuffer, /* pointer to pointer to output buffer (allocated with allocMain) */ - size_t *outBufferSize, /* buffer size for output buffer */ - size_t *offset, /* offset of stream for required file in *outBuffer */ - size_t *outSizeProcessed, /* size of file in *outBuffer */ - ISzAlloc *allocMain, - ISzAlloc *allocTemp); - - -/* -SzArEx_Open Errors: -SZ_ERROR_NO_ARCHIVE -SZ_ERROR_ARCHIVE -SZ_ERROR_UNSUPPORTED -SZ_ERROR_MEM -SZ_ERROR_CRC -SZ_ERROR_INPUT_EOF -SZ_ERROR_FAIL -*/ - -SRes SzArEx_Open(CSzArEx *p, ILookInStream *inStream, ISzAlloc *allocMain, ISzAlloc *allocTemp); - -EXTERN_C_END - -#endif +/* 7z.h -- 7z interface +2010-03-11 : Igor Pavlov : Public domain */ + +#ifndef __7Z_H +#define __7Z_H + +#include "7zBuf.h" + +EXTERN_C_BEGIN + +#define k7zStartHeaderSize 0x20 +#define k7zSignatureSize 6 +extern Byte k7zSignature[k7zSignatureSize]; +#define k7zMajorVersion 0 + +enum EIdEnum +{ + k7zIdEnd, + k7zIdHeader, + k7zIdArchiveProperties, + k7zIdAdditionalStreamsInfo, + k7zIdMainStreamsInfo, + k7zIdFilesInfo, + k7zIdPackInfo, + k7zIdUnpackInfo, + k7zIdSubStreamsInfo, + k7zIdSize, + k7zIdCRC, + k7zIdFolder, + k7zIdCodersUnpackSize, + k7zIdNumUnpackStream, + k7zIdEmptyStream, + k7zIdEmptyFile, + k7zIdAnti, + k7zIdName, + k7zIdCTime, + k7zIdATime, + k7zIdMTime, + k7zIdWinAttributes, + k7zIdComment, + k7zIdEncodedHeader, + k7zIdStartPos, + k7zIdDummy +}; + +typedef struct +{ + UInt32 NumInStreams; + UInt32 NumOutStreams; + UInt64 MethodID; + CBuf Props; +} CSzCoderInfo; + +void SzCoderInfo_Init(CSzCoderInfo *p); +void SzCoderInfo_Free(CSzCoderInfo *p, ISzAlloc *alloc); + +typedef struct +{ + UInt32 InIndex; + UInt32 OutIndex; +} CSzBindPair; + +typedef struct +{ + CSzCoderInfo *Coders; + CSzBindPair *BindPairs; + UInt32 *PackStreams; + UInt64 *UnpackSizes; + UInt32 NumCoders; + UInt32 NumBindPairs; + UInt32 NumPackStreams; + int UnpackCRCDefined; + UInt32 UnpackCRC; + + UInt32 NumUnpackStreams; +} CSzFolder; + +void SzFolder_Init(CSzFolder *p); +UInt64 SzFolder_GetUnpackSize(CSzFolder *p); +int SzFolder_FindBindPairForInStream(CSzFolder *p, UInt32 inStreamIndex); +UInt32 SzFolder_GetNumOutStreams(CSzFolder *p); +UInt64 SzFolder_GetUnpackSize(CSzFolder *p); + +SRes SzFolder_Decode(const CSzFolder *folder, const UInt64 *packSizes, + ILookInStream *stream, UInt64 startPos, + Byte *outBuffer, size_t outSize, ISzAlloc *allocMain); + +typedef struct +{ + UInt32 Low; + UInt32 High; +} CNtfsFileTime; + +typedef struct +{ + CNtfsFileTime MTime; + UInt64 Size; + UInt32 Crc; + UInt32 Attrib; + Byte HasStream; + Byte IsDir; + Byte IsAnti; + Byte CrcDefined; + Byte MTimeDefined; + Byte AttribDefined; +} CSzFileItem; + +void SzFile_Init(CSzFileItem *p); + +typedef struct +{ + UInt64 *PackSizes; + Byte *PackCRCsDefined; + UInt32 *PackCRCs; + CSzFolder *Folders; + CSzFileItem *Files; + UInt32 NumPackStreams; + UInt32 NumFolders; + UInt32 NumFiles; +} CSzAr; + +void SzAr_Init(CSzAr *p); +void SzAr_Free(CSzAr *p, ISzAlloc *alloc); + + +/* + SzExtract extracts file from archive + + *outBuffer must be 0 before first call for each new archive. + + Extracting cache: + If you need to decompress more than one file, you can send + these values from previous call: + *blockIndex, + *outBuffer, + *outBufferSize + You can consider "*outBuffer" as cache of solid block. If your archive is solid, + it will increase decompression speed. + + If you use external function, you can declare these 3 cache variables + (blockIndex, outBuffer, outBufferSize) as static in that external function. + + Free *outBuffer and set *outBuffer to 0, if you want to flush cache. +*/ + +typedef struct +{ + CSzAr db; + + UInt64 startPosAfterHeader; + UInt64 dataPos; + + UInt32 *FolderStartPackStreamIndex; + UInt64 *PackStreamStartPositions; + UInt32 *FolderStartFileIndex; + UInt32 *FileIndexToFolderIndexMap; + + size_t *FileNameOffsets; /* in 2-byte steps */ + CBuf FileNames; /* UTF-16-LE */ +} CSzArEx; + +void SzArEx_Init(CSzArEx *p); +void SzArEx_Free(CSzArEx *p, ISzAlloc *alloc); +UInt64 SzArEx_GetFolderStreamPos(const CSzArEx *p, UInt32 folderIndex, UInt32 indexInFolder); +int SzArEx_GetFolderFullPackSize(const CSzArEx *p, UInt32 folderIndex, UInt64 *resSize); + +/* +if dest == NULL, the return value specifies the required size of the buffer, + in 16-bit characters, including the null-terminating character. +if dest != NULL, the return value specifies the number of 16-bit characters that + are written to the dest, including the null-terminating character. */ + +size_t SzArEx_GetFileNameUtf16(const CSzArEx *p, size_t fileIndex, UInt16 *dest); + +SRes SzArEx_Extract( + const CSzArEx *db, + ILookInStream *inStream, + UInt32 fileIndex, /* index of file */ + UInt32 *blockIndex, /* index of solid block */ + Byte **outBuffer, /* pointer to pointer to output buffer (allocated with allocMain) */ + size_t *outBufferSize, /* buffer size for output buffer */ + size_t *offset, /* offset of stream for required file in *outBuffer */ + size_t *outSizeProcessed, /* size of file in *outBuffer */ + ISzAlloc *allocMain, + ISzAlloc *allocTemp); + + +/* +SzArEx_Open Errors: +SZ_ERROR_NO_ARCHIVE +SZ_ERROR_ARCHIVE +SZ_ERROR_UNSUPPORTED +SZ_ERROR_MEM +SZ_ERROR_CRC +SZ_ERROR_INPUT_EOF +SZ_ERROR_FAIL +*/ + +SRes SzArEx_Open(CSzArEx *p, ILookInStream *inStream, ISzAlloc *allocMain, ISzAlloc *allocTemp); + +EXTERN_C_END + +#endif diff --git a/fex/7z_C/7zAlloc.h b/fex/7z_C/7zAlloc.h index 860f116a..3344e937 100644 --- a/fex/7z_C/7zAlloc.h +++ b/fex/7z_C/7zAlloc.h @@ -1,15 +1,15 @@ -/* 7zAlloc.h -- Allocation functions -2010-10-29 : Igor Pavlov : Public domain */ - -#ifndef __7Z_ALLOC_H -#define __7Z_ALLOC_H - -#include - -void *SzAlloc(void *p, size_t size); -void SzFree(void *p, void *address); - -void *SzAllocTemp(void *p, size_t size); -void SzFreeTemp(void *p, void *address); - -#endif +/* 7zAlloc.h -- Allocation functions +2010-10-29 : Igor Pavlov : Public domain */ + +#ifndef __7Z_ALLOC_H +#define __7Z_ALLOC_H + +#include + +void *SzAlloc(void *p, size_t size); +void SzFree(void *p, void *address); + +void *SzAllocTemp(void *p, size_t size); +void SzFreeTemp(void *p, void *address); + +#endif diff --git a/fex/7z_C/7zBuf.c b/fex/7z_C/7zBuf.c index a35fa2f9..14e7f4e2 100644 --- a/fex/7z_C/7zBuf.c +++ b/fex/7z_C/7zBuf.c @@ -1,36 +1,36 @@ -/* 7zBuf.c -- Byte Buffer -2008-03-28 -Igor Pavlov -Public domain */ - -#include "7zBuf.h" - -void Buf_Init(CBuf *p) -{ - p->data = 0; - p->size = 0; -} - -int Buf_Create(CBuf *p, size_t size, ISzAlloc *alloc) -{ - p->size = 0; - if (size == 0) - { - p->data = 0; - return 1; - } - p->data = (Byte *)alloc->Alloc(alloc, size); - if (p->data != 0) - { - p->size = size; - return 1; - } - return 0; -} - -void Buf_Free(CBuf *p, ISzAlloc *alloc) -{ - alloc->Free(alloc, p->data); - p->data = 0; - p->size = 0; -} +/* 7zBuf.c -- Byte Buffer +2008-03-28 +Igor Pavlov +Public domain */ + +#include "7zBuf.h" + +void Buf_Init(CBuf *p) +{ + p->data = 0; + p->size = 0; +} + +int Buf_Create(CBuf *p, size_t size, ISzAlloc *alloc) +{ + p->size = 0; + if (size == 0) + { + p->data = 0; + return 1; + } + p->data = (Byte *)alloc->Alloc(alloc, size); + if (p->data != 0) + { + p->size = size; + return 1; + } + return 0; +} + +void Buf_Free(CBuf *p, ISzAlloc *alloc) +{ + alloc->Free(alloc, p->data); + p->data = 0; + p->size = 0; +} diff --git a/fex/7z_C/7zBuf.h b/fex/7z_C/7zBuf.h index 88ff0c2f..e9f2f316 100644 --- a/fex/7z_C/7zBuf.h +++ b/fex/7z_C/7zBuf.h @@ -1,39 +1,39 @@ -/* 7zBuf.h -- Byte Buffer -2009-02-07 : Igor Pavlov : Public domain */ - -#ifndef __7Z_BUF_H -#define __7Z_BUF_H - -#include "Types.h" - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct -{ - Byte *data; - size_t size; -} CBuf; - -void Buf_Init(CBuf *p); -int Buf_Create(CBuf *p, size_t size, ISzAlloc *alloc); -void Buf_Free(CBuf *p, ISzAlloc *alloc); - -typedef struct -{ - Byte *data; - size_t size; - size_t pos; -} CDynBuf; - -void DynBuf_Construct(CDynBuf *p); -void DynBuf_SeekToBeg(CDynBuf *p); -int DynBuf_Write(CDynBuf *p, const Byte *buf, size_t size, ISzAlloc *alloc); -void DynBuf_Free(CDynBuf *p, ISzAlloc *alloc); - -#ifdef __cplusplus -} -#endif - -#endif +/* 7zBuf.h -- Byte Buffer +2009-02-07 : Igor Pavlov : Public domain */ + +#ifndef __7Z_BUF_H +#define __7Z_BUF_H + +#include "Types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct +{ + Byte *data; + size_t size; +} CBuf; + +void Buf_Init(CBuf *p); +int Buf_Create(CBuf *p, size_t size, ISzAlloc *alloc); +void Buf_Free(CBuf *p, ISzAlloc *alloc); + +typedef struct +{ + Byte *data; + size_t size; + size_t pos; +} CDynBuf; + +void DynBuf_Construct(CDynBuf *p); +void DynBuf_SeekToBeg(CDynBuf *p); +int DynBuf_Write(CDynBuf *p, const Byte *buf, size_t size, ISzAlloc *alloc); +void DynBuf_Free(CDynBuf *p, ISzAlloc *alloc); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/fex/7z_C/7zCrc.c b/fex/7z_C/7zCrc.c index 5801dabf..a9208496 100644 --- a/fex/7z_C/7zCrc.c +++ b/fex/7z_C/7zCrc.c @@ -1,74 +1,74 @@ -/* 7zCrc.c -- CRC32 calculation -2009-11-23 : Igor Pavlov : Public domain */ - -#include "7zCrc.h" -#include "CpuArch.h" - -#define kCrcPoly 0xEDB88320 - -#ifdef MY_CPU_LE -#define CRC_NUM_TABLES 8 -#else -#define CRC_NUM_TABLES 1 -#endif - -typedef UInt32 (MY_FAST_CALL *CRC_FUNC)(UInt32 v, const void *data, size_t size, const UInt32 *table); - -static CRC_FUNC g_CrcUpdate; -UInt32 g_CrcTable[256 * CRC_NUM_TABLES]; - -#if CRC_NUM_TABLES == 1 - -#define CRC_UPDATE_BYTE_2(crc, b) (table[((crc) ^ (b)) & 0xFF] ^ ((crc) >> 8)) - -static UInt32 MY_FAST_CALL CrcUpdateT1(UInt32 v, const void *data, size_t size, const UInt32 *table) -{ - const Byte *p = (const Byte *)data; - for (; size > 0; size--, p++) - v = CRC_UPDATE_BYTE_2(v, *p); - return v; -} - -#else - -UInt32 MY_FAST_CALL CrcUpdateT4(UInt32 v, const void *data, size_t size, const UInt32 *table); -UInt32 MY_FAST_CALL CrcUpdateT8(UInt32 v, const void *data, size_t size, const UInt32 *table); - -#endif - -UInt32 MY_FAST_CALL CrcUpdate(UInt32 v, const void *data, size_t size) -{ - return g_CrcUpdate(v, data, size, g_CrcTable); -} - -UInt32 MY_FAST_CALL CrcCalc(const void *data, size_t size) -{ - return g_CrcUpdate(CRC_INIT_VAL, data, size, g_CrcTable) ^ CRC_INIT_VAL; -} - -void MY_FAST_CALL CrcGenerateTable() -{ - UInt32 i; - for (i = 0; i < 256; i++) - { - UInt32 r = i; - unsigned j; - for (j = 0; j < 8; j++) - r = (r >> 1) ^ (kCrcPoly & ~((r & 1) - 1)); - g_CrcTable[i] = r; - } - #if CRC_NUM_TABLES == 1 - g_CrcUpdate = CrcUpdateT1; - #else - for (; i < 256 * CRC_NUM_TABLES; i++) - { - UInt32 r = g_CrcTable[i - 256]; - g_CrcTable[i] = g_CrcTable[r & 0xFF] ^ (r >> 8); - } - g_CrcUpdate = CrcUpdateT4; - #ifdef MY_CPU_X86_OR_AMD64 - if (!CPU_Is_InOrder()) - g_CrcUpdate = CrcUpdateT8; - #endif - #endif -} +/* 7zCrc.c -- CRC32 calculation +2009-11-23 : Igor Pavlov : Public domain */ + +#include "7zCrc.h" +#include "CpuArch.h" + +#define kCrcPoly 0xEDB88320 + +#ifdef MY_CPU_LE +#define CRC_NUM_TABLES 8 +#else +#define CRC_NUM_TABLES 1 +#endif + +typedef UInt32 (MY_FAST_CALL *CRC_FUNC)(UInt32 v, const void *data, size_t size, const UInt32 *table); + +static CRC_FUNC g_CrcUpdate; +UInt32 g_CrcTable[256 * CRC_NUM_TABLES]; + +#if CRC_NUM_TABLES == 1 + +#define CRC_UPDATE_BYTE_2(crc, b) (table[((crc) ^ (b)) & 0xFF] ^ ((crc) >> 8)) + +static UInt32 MY_FAST_CALL CrcUpdateT1(UInt32 v, const void *data, size_t size, const UInt32 *table) +{ + const Byte *p = (const Byte *)data; + for (; size > 0; size--, p++) + v = CRC_UPDATE_BYTE_2(v, *p); + return v; +} + +#else + +UInt32 MY_FAST_CALL CrcUpdateT4(UInt32 v, const void *data, size_t size, const UInt32 *table); +UInt32 MY_FAST_CALL CrcUpdateT8(UInt32 v, const void *data, size_t size, const UInt32 *table); + +#endif + +UInt32 MY_FAST_CALL CrcUpdate(UInt32 v, const void *data, size_t size) +{ + return g_CrcUpdate(v, data, size, g_CrcTable); +} + +UInt32 MY_FAST_CALL CrcCalc(const void *data, size_t size) +{ + return g_CrcUpdate(CRC_INIT_VAL, data, size, g_CrcTable) ^ CRC_INIT_VAL; +} + +void MY_FAST_CALL CrcGenerateTable() +{ + UInt32 i; + for (i = 0; i < 256; i++) + { + UInt32 r = i; + unsigned j; + for (j = 0; j < 8; j++) + r = (r >> 1) ^ (kCrcPoly & ~((r & 1) - 1)); + g_CrcTable[i] = r; + } + #if CRC_NUM_TABLES == 1 + g_CrcUpdate = CrcUpdateT1; + #else + for (; i < 256 * CRC_NUM_TABLES; i++) + { + UInt32 r = g_CrcTable[i - 256]; + g_CrcTable[i] = g_CrcTable[r & 0xFF] ^ (r >> 8); + } + g_CrcUpdate = CrcUpdateT4; + #ifdef MY_CPU_X86_OR_AMD64 + if (!CPU_Is_InOrder()) + g_CrcUpdate = CrcUpdateT8; + #endif + #endif +} diff --git a/fex/7z_C/7zCrc.h b/fex/7z_C/7zCrc.h index 4a1ec38c..38e3e5fb 100644 --- a/fex/7z_C/7zCrc.h +++ b/fex/7z_C/7zCrc.h @@ -1,25 +1,25 @@ -/* 7zCrc.h -- CRC32 calculation -2009-11-21 : Igor Pavlov : Public domain */ - -#ifndef __7Z_CRC_H -#define __7Z_CRC_H - -#include "Types.h" - -EXTERN_C_BEGIN - -extern UInt32 g_CrcTable[]; - -/* Call CrcGenerateTable one time before other CRC functions */ -void MY_FAST_CALL CrcGenerateTable(void); - -#define CRC_INIT_VAL 0xFFFFFFFF -#define CRC_GET_DIGEST(crc) ((crc) ^ CRC_INIT_VAL) -#define CRC_UPDATE_BYTE(crc, b) (g_CrcTable[((crc) ^ (b)) & 0xFF] ^ ((crc) >> 8)) - -UInt32 MY_FAST_CALL CrcUpdate(UInt32 crc, const void *data, size_t size); -UInt32 MY_FAST_CALL CrcCalc(const void *data, size_t size); - -EXTERN_C_END - -#endif +/* 7zCrc.h -- CRC32 calculation +2009-11-21 : Igor Pavlov : Public domain */ + +#ifndef __7Z_CRC_H +#define __7Z_CRC_H + +#include "Types.h" + +EXTERN_C_BEGIN + +extern UInt32 g_CrcTable[]; + +/* Call CrcGenerateTable one time before other CRC functions */ +void MY_FAST_CALL CrcGenerateTable(void); + +#define CRC_INIT_VAL 0xFFFFFFFF +#define CRC_GET_DIGEST(crc) ((crc) ^ CRC_INIT_VAL) +#define CRC_UPDATE_BYTE(crc, b) (g_CrcTable[((crc) ^ (b)) & 0xFF] ^ ((crc) >> 8)) + +UInt32 MY_FAST_CALL CrcUpdate(UInt32 crc, const void *data, size_t size); +UInt32 MY_FAST_CALL CrcCalc(const void *data, size_t size); + +EXTERN_C_END + +#endif diff --git a/fex/7z_C/7zCrcOpt.c b/fex/7z_C/7zCrcOpt.c index 6205d716..6c766a20 100644 --- a/fex/7z_C/7zCrcOpt.c +++ b/fex/7z_C/7zCrcOpt.c @@ -1,34 +1,34 @@ -/* 7zCrcOpt.c -- CRC32 calculation : optimized version -2009-11-23 : Igor Pavlov : Public domain */ - -#include "CpuArch.h" - -#ifdef MY_CPU_LE - -#define CRC_UPDATE_BYTE_2(crc, b) (table[((crc) ^ (b)) & 0xFF] ^ ((crc) >> 8)) - -UInt32 MY_FAST_CALL CrcUpdateT4(UInt32 v, const void *data, size_t size, const UInt32 *table) -{ - const Byte *p = (const Byte *)data; - for (; size > 0 && ((unsigned)(ptrdiff_t)p & 3) != 0; size--, p++) - v = CRC_UPDATE_BYTE_2(v, *p); - for (; size >= 4; size -= 4, p += 4) - { - v ^= *(const UInt32 *)p; - v = - table[0x300 + (v & 0xFF)] ^ - table[0x200 + ((v >> 8) & 0xFF)] ^ - table[0x100 + ((v >> 16) & 0xFF)] ^ - table[0x000 + ((v >> 24))]; - } - for (; size > 0; size--, p++) - v = CRC_UPDATE_BYTE_2(v, *p); - return v; -} - -UInt32 MY_FAST_CALL CrcUpdateT8(UInt32 v, const void *data, size_t size, const UInt32 *table) -{ - return CrcUpdateT4(v, data, size, table); -} - -#endif +/* 7zCrcOpt.c -- CRC32 calculation : optimized version +2009-11-23 : Igor Pavlov : Public domain */ + +#include "CpuArch.h" + +#ifdef MY_CPU_LE + +#define CRC_UPDATE_BYTE_2(crc, b) (table[((crc) ^ (b)) & 0xFF] ^ ((crc) >> 8)) + +UInt32 MY_FAST_CALL CrcUpdateT4(UInt32 v, const void *data, size_t size, const UInt32 *table) +{ + const Byte *p = (const Byte *)data; + for (; size > 0 && ((unsigned)(ptrdiff_t)p & 3) != 0; size--, p++) + v = CRC_UPDATE_BYTE_2(v, *p); + for (; size >= 4; size -= 4, p += 4) + { + v ^= *(const UInt32 *)p; + v = + table[0x300 + (v & 0xFF)] ^ + table[0x200 + ((v >> 8) & 0xFF)] ^ + table[0x100 + ((v >> 16) & 0xFF)] ^ + table[0x000 + ((v >> 24))]; + } + for (; size > 0; size--, p++) + v = CRC_UPDATE_BYTE_2(v, *p); + return v; +} + +UInt32 MY_FAST_CALL CrcUpdateT8(UInt32 v, const void *data, size_t size, const UInt32 *table) +{ + return CrcUpdateT4(v, data, size, table); +} + +#endif diff --git a/fex/7z_C/7zDecode.c b/fex/7z_C/7zDecode.c index 33868074..b6d80995 100644 --- a/fex/7z_C/7zDecode.c +++ b/fex/7z_C/7zDecode.c @@ -1,470 +1,470 @@ -/* 7zDec.c -- Decoding from 7z folder -2010-11-02 : Igor Pavlov : Public domain */ - -#include - -/* #define _7ZIP_PPMD_SUPPPORT */ - -#include "7z.h" - -#include "Bcj2.h" -#include "Bra.h" -#include "CpuArch.h" -#include "LzmaDec.h" -#include "Lzma2Dec.h" -#ifdef _7ZIP_PPMD_SUPPPORT -#include "Ppmd7.h" -#endif - -#define k_Copy 0 -#define k_LZMA2 0x21 -#define k_LZMA 0x30101 -#define k_BCJ 0x03030103 -#define k_PPC 0x03030205 -#define k_ARM 0x03030501 -#define k_ARMT 0x03030701 -#define k_SPARC 0x03030805 -#define k_BCJ2 0x0303011B - -#ifdef _7ZIP_PPMD_SUPPPORT - -#define k_PPMD 0x30401 - -typedef struct -{ - IByteIn p; - const Byte *cur; - const Byte *end; - const Byte *begin; - UInt64 processed; - Bool extra; - SRes res; - ILookInStream *inStream; -} CByteInToLook; - -static Byte ReadByte(void *pp) -{ - CByteInToLook *p = (CByteInToLook *)pp; - if (p->cur != p->end) - return *p->cur++; - if (p->res == SZ_OK) - { - size_t size = p->cur - p->begin; - p->processed += size; - p->res = p->inStream->Skip(p->inStream, size); - size = (1 << 25); - p->res = p->inStream->Look(p->inStream, (const void **)&p->begin, &size); - p->cur = p->begin; - p->end = p->begin + size; - if (size != 0) - return *p->cur++;; - } - p->extra = True; - return 0; -} - -static SRes SzDecodePpmd(CSzCoderInfo *coder, UInt64 inSize, ILookInStream *inStream, - Byte *outBuffer, SizeT outSize, ISzAlloc *allocMain) -{ - CPpmd7 ppmd; - CByteInToLook s; - SRes res = SZ_OK; - - s.p.Read = ReadByte; - s.inStream = inStream; - s.begin = s.end = s.cur = NULL; - s.extra = False; - s.res = SZ_OK; - s.processed = 0; - - if (coder->Props.size != 5) - return SZ_ERROR_UNSUPPORTED; - - { - unsigned order = coder->Props.data[0]; - UInt32 memSize = GetUi32(coder->Props.data + 1); - if (order < PPMD7_MIN_ORDER || - order > PPMD7_MAX_ORDER || - memSize < PPMD7_MIN_MEM_SIZE || - memSize > PPMD7_MAX_MEM_SIZE) - return SZ_ERROR_UNSUPPORTED; - Ppmd7_Construct(&ppmd); - if (!Ppmd7_Alloc(&ppmd, memSize, allocMain)) - return SZ_ERROR_MEM; - Ppmd7_Init(&ppmd, order); - } - { - CPpmd7z_RangeDec rc; - Ppmd7z_RangeDec_CreateVTable(&rc); - rc.Stream = &s.p; - if (!Ppmd7z_RangeDec_Init(&rc)) - res = SZ_ERROR_DATA; - else if (s.extra) - res = (s.res != SZ_OK ? s.res : SZ_ERROR_DATA); - else - { - SizeT i; - for (i = 0; i < outSize; i++) - { - int sym = Ppmd7_DecodeSymbol(&ppmd, &rc.p); - if (s.extra || sym < 0) - break; - outBuffer[i] = (Byte)sym; - } - if (i != outSize) - res = (s.res != SZ_OK ? s.res : SZ_ERROR_DATA); - else if (s.processed + (s.cur - s.begin) != inSize || !Ppmd7z_RangeDec_IsFinishedOK(&rc)) - res = SZ_ERROR_DATA; - } - } - Ppmd7_Free(&ppmd, allocMain); - return res; -} - -#endif - - -static SRes SzDecodeLzma(CSzCoderInfo *coder, UInt64 inSize, ILookInStream *inStream, - Byte *outBuffer, SizeT outSize, ISzAlloc *allocMain) -{ - CLzmaDec state; - SRes res = SZ_OK; - - LzmaDec_Construct(&state); - RINOK(LzmaDec_AllocateProbs(&state, coder->Props.data, (unsigned)coder->Props.size, allocMain)); - state.dic = outBuffer; - state.dicBufSize = outSize; - LzmaDec_Init(&state); - - for (;;) - { - Byte *inBuf = NULL; - size_t lookahead = (1 << 18); - if (lookahead > inSize) - lookahead = (size_t)inSize; - res = inStream->Look((void *)inStream, (const void **)&inBuf, &lookahead); - if (res != SZ_OK) - break; - - { - SizeT inProcessed = (SizeT)lookahead, dicPos = state.dicPos; - ELzmaStatus status; - res = LzmaDec_DecodeToDic(&state, outSize, inBuf, &inProcessed, LZMA_FINISH_END, &status); - lookahead -= inProcessed; - inSize -= inProcessed; - if (res != SZ_OK) - break; - if (state.dicPos == state.dicBufSize || (inProcessed == 0 && dicPos == state.dicPos)) - { - if (state.dicBufSize != outSize || lookahead != 0 || - (status != LZMA_STATUS_FINISHED_WITH_MARK && - status != LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK)) - res = SZ_ERROR_DATA; - break; - } - res = inStream->Skip((void *)inStream, inProcessed); - if (res != SZ_OK) - break; - } - } - - LzmaDec_FreeProbs(&state, allocMain); - return res; -} - -static SRes SzDecodeLzma2(CSzCoderInfo *coder, UInt64 inSize, ILookInStream *inStream, - Byte *outBuffer, SizeT outSize, ISzAlloc *allocMain) -{ - CLzma2Dec state; - SRes res = SZ_OK; - - Lzma2Dec_Construct(&state); - if (coder->Props.size != 1) - return SZ_ERROR_DATA; - RINOK(Lzma2Dec_AllocateProbs(&state, coder->Props.data[0], allocMain)); - state.decoder.dic = outBuffer; - state.decoder.dicBufSize = outSize; - Lzma2Dec_Init(&state); - - for (;;) - { - Byte *inBuf = NULL; - size_t lookahead = (1 << 18); - if (lookahead > inSize) - lookahead = (size_t)inSize; - res = inStream->Look((void *)inStream, (const void **)&inBuf, &lookahead); - if (res != SZ_OK) - break; - - { - SizeT inProcessed = (SizeT)lookahead, dicPos = state.decoder.dicPos; - ELzmaStatus status; - res = Lzma2Dec_DecodeToDic(&state, outSize, inBuf, &inProcessed, LZMA_FINISH_END, &status); - lookahead -= inProcessed; - inSize -= inProcessed; - if (res != SZ_OK) - break; - if (state.decoder.dicPos == state.decoder.dicBufSize || (inProcessed == 0 && dicPos == state.decoder.dicPos)) - { - if (state.decoder.dicBufSize != outSize || lookahead != 0 || - (status != LZMA_STATUS_FINISHED_WITH_MARK)) - res = SZ_ERROR_DATA; - break; - } - res = inStream->Skip((void *)inStream, inProcessed); - if (res != SZ_OK) - break; - } - } - - Lzma2Dec_FreeProbs(&state, allocMain); - return res; -} - -static SRes SzDecodeCopy(UInt64 inSize, ILookInStream *inStream, Byte *outBuffer) -{ - while (inSize > 0) - { - void *inBuf; - size_t curSize = (1 << 18); - if (curSize > inSize) - curSize = (size_t)inSize; - RINOK(inStream->Look((void *)inStream, (const void **)&inBuf, &curSize)); - if (curSize == 0) - return SZ_ERROR_INPUT_EOF; - memcpy(outBuffer, inBuf, curSize); - outBuffer += curSize; - inSize -= curSize; - RINOK(inStream->Skip((void *)inStream, curSize)); - } - return SZ_OK; -} - -static Bool IS_MAIN_METHOD(UInt32 m) -{ - switch(m) - { - case k_Copy: - case k_LZMA: - case k_LZMA2: - #ifdef _7ZIP_PPMD_SUPPPORT - case k_PPMD: - #endif - return True; - } - return False; -} - -static Bool IS_SUPPORTED_CODER(const CSzCoderInfo *c) -{ - return - c->NumInStreams == 1 && - c->NumOutStreams == 1 && - c->MethodID <= (UInt32)0xFFFFFFFF && - IS_MAIN_METHOD((UInt32)c->MethodID); -} - -#define IS_BCJ2(c) ((c)->MethodID == k_BCJ2 && (c)->NumInStreams == 4 && (c)->NumOutStreams == 1) - -static SRes CheckSupportedFolder(const CSzFolder *f) -{ - if (f->NumCoders < 1 || f->NumCoders > 4) - return SZ_ERROR_UNSUPPORTED; - if (!IS_SUPPORTED_CODER(&f->Coders[0])) - return SZ_ERROR_UNSUPPORTED; - if (f->NumCoders == 1) - { - if (f->NumPackStreams != 1 || f->PackStreams[0] != 0 || f->NumBindPairs != 0) - return SZ_ERROR_UNSUPPORTED; - return SZ_OK; - } - if (f->NumCoders == 2) - { - CSzCoderInfo *c = &f->Coders[1]; - if (c->MethodID > (UInt32)0xFFFFFFFF || - c->NumInStreams != 1 || - c->NumOutStreams != 1 || - f->NumPackStreams != 1 || - f->PackStreams[0] != 0 || - f->NumBindPairs != 1 || - f->BindPairs[0].InIndex != 1 || - f->BindPairs[0].OutIndex != 0) - return SZ_ERROR_UNSUPPORTED; - switch ((UInt32)c->MethodID) - { - case k_BCJ: - case k_ARM: - break; - default: - return SZ_ERROR_UNSUPPORTED; - } - return SZ_OK; - } - if (f->NumCoders == 4) - { - if (!IS_SUPPORTED_CODER(&f->Coders[1]) || - !IS_SUPPORTED_CODER(&f->Coders[2]) || - !IS_BCJ2(&f->Coders[3])) - return SZ_ERROR_UNSUPPORTED; - if (f->NumPackStreams != 4 || - f->PackStreams[0] != 2 || - f->PackStreams[1] != 6 || - f->PackStreams[2] != 1 || - f->PackStreams[3] != 0 || - f->NumBindPairs != 3 || - f->BindPairs[0].InIndex != 5 || f->BindPairs[0].OutIndex != 0 || - f->BindPairs[1].InIndex != 4 || f->BindPairs[1].OutIndex != 1 || - f->BindPairs[2].InIndex != 3 || f->BindPairs[2].OutIndex != 2) - return SZ_ERROR_UNSUPPORTED; - return SZ_OK; - } - return SZ_ERROR_UNSUPPORTED; -} - -static UInt64 GetSum(const UInt64 *values, UInt32 index) -{ - UInt64 sum = 0; - UInt32 i; - for (i = 0; i < index; i++) - sum += values[i]; - return sum; -} - -#define CASE_BRA_CONV(isa) case k_ ## isa: isa ## _Convert(outBuffer, outSize, 0, 0); break; - -static SRes SzFolder_Decode2(const CSzFolder *folder, const UInt64 *packSizes, - ILookInStream *inStream, UInt64 startPos, - Byte *outBuffer, SizeT outSize, ISzAlloc *allocMain, - Byte *tempBuf[]) -{ - UInt32 ci; - SizeT tempSizes[3] = { 0, 0, 0}; - SizeT tempSize3 = 0; - Byte *tempBuf3 = 0; - - RINOK(CheckSupportedFolder(folder)); - - for (ci = 0; ci < folder->NumCoders; ci++) - { - CSzCoderInfo *coder = &folder->Coders[ci]; - - if (IS_MAIN_METHOD((UInt32)coder->MethodID)) - { - UInt32 si = 0; - UInt64 offset; - UInt64 inSize; - Byte *outBufCur = outBuffer; - SizeT outSizeCur = outSize; - if (folder->NumCoders == 4) - { - UInt32 indices[] = { 3, 2, 0 }; - UInt64 unpackSize = folder->UnpackSizes[ci]; - si = indices[ci]; - if (ci < 2) - { - Byte *temp; - outSizeCur = (SizeT)unpackSize; - if (outSizeCur != unpackSize) - return SZ_ERROR_MEM; - temp = (Byte *)IAlloc_Alloc(allocMain, outSizeCur); - if (temp == 0 && outSizeCur != 0) - return SZ_ERROR_MEM; - outBufCur = tempBuf[1 - ci] = temp; - tempSizes[1 - ci] = outSizeCur; - } - else if (ci == 2) - { - if (unpackSize > outSize) /* check it */ - return SZ_ERROR_PARAM; - tempBuf3 = outBufCur = outBuffer + (outSize - (size_t)unpackSize); - tempSize3 = outSizeCur = (SizeT)unpackSize; - } - else - return SZ_ERROR_UNSUPPORTED; - } - offset = GetSum(packSizes, si); - inSize = packSizes[si]; - RINOK(LookInStream_SeekTo(inStream, startPos + offset)); - - if (coder->MethodID == k_Copy) - { - if (inSize != outSizeCur) /* check it */ - return SZ_ERROR_DATA; - RINOK(SzDecodeCopy(inSize, inStream, outBufCur)); - } - else if (coder->MethodID == k_LZMA) - { - RINOK(SzDecodeLzma(coder, inSize, inStream, outBufCur, outSizeCur, allocMain)); - } - else if (coder->MethodID == k_LZMA2) - { - RINOK(SzDecodeLzma2(coder, inSize, inStream, outBufCur, outSizeCur, allocMain)); - } - else - { - #ifdef _7ZIP_PPMD_SUPPPORT - RINOK(SzDecodePpmd(coder, inSize, inStream, outBufCur, outSizeCur, allocMain)); - #else - return SZ_ERROR_UNSUPPORTED; - #endif - } - } - else if (coder->MethodID == k_BCJ2) - { - UInt64 offset = GetSum(packSizes, 1); - UInt64 s3Size = packSizes[1]; - SRes res; - if (ci != 3) - return SZ_ERROR_UNSUPPORTED; - RINOK(LookInStream_SeekTo(inStream, startPos + offset)); - tempSizes[2] = (SizeT)s3Size; - if (tempSizes[2] != s3Size) - return SZ_ERROR_MEM; - tempBuf[2] = (Byte *)IAlloc_Alloc(allocMain, tempSizes[2]); - if (tempBuf[2] == 0 && tempSizes[2] != 0) - return SZ_ERROR_MEM; - res = SzDecodeCopy(s3Size, inStream, tempBuf[2]); - RINOK(res) - - res = Bcj2_Decode( - tempBuf3, tempSize3, - tempBuf[0], tempSizes[0], - tempBuf[1], tempSizes[1], - tempBuf[2], tempSizes[2], - outBuffer, outSize); - RINOK(res) - } - else - { - if (ci != 1) - return SZ_ERROR_UNSUPPORTED; - switch(coder->MethodID) - { - case k_BCJ: - { - UInt32 state; - x86_Convert_Init(state); - x86_Convert(outBuffer, outSize, 0, &state, 0); - break; - } - CASE_BRA_CONV(ARM) - default: - return SZ_ERROR_UNSUPPORTED; - } - } - } - return SZ_OK; -} - -SRes SzFolder_Decode(const CSzFolder *folder, const UInt64 *packSizes, - ILookInStream *inStream, UInt64 startPos, - Byte *outBuffer, size_t outSize, ISzAlloc *allocMain) -{ - Byte *tempBuf[3] = { 0, 0, 0}; - int i; - SRes res = SzFolder_Decode2(folder, packSizes, inStream, startPos, - outBuffer, (SizeT)outSize, allocMain, tempBuf); - for (i = 0; i < 3; i++) - IAlloc_Free(allocMain, tempBuf[i]); - return res; -} +/* 7zDec.c -- Decoding from 7z folder +2010-11-02 : Igor Pavlov : Public domain */ + +#include + +/* #define _7ZIP_PPMD_SUPPPORT */ + +#include "7z.h" + +#include "Bcj2.h" +#include "Bra.h" +#include "CpuArch.h" +#include "LzmaDec.h" +#include "Lzma2Dec.h" +#ifdef _7ZIP_PPMD_SUPPPORT +#include "Ppmd7.h" +#endif + +#define k_Copy 0 +#define k_LZMA2 0x21 +#define k_LZMA 0x30101 +#define k_BCJ 0x03030103 +#define k_PPC 0x03030205 +#define k_ARM 0x03030501 +#define k_ARMT 0x03030701 +#define k_SPARC 0x03030805 +#define k_BCJ2 0x0303011B + +#ifdef _7ZIP_PPMD_SUPPPORT + +#define k_PPMD 0x30401 + +typedef struct +{ + IByteIn p; + const Byte *cur; + const Byte *end; + const Byte *begin; + UInt64 processed; + Bool extra; + SRes res; + ILookInStream *inStream; +} CByteInToLook; + +static Byte ReadByte(void *pp) +{ + CByteInToLook *p = (CByteInToLook *)pp; + if (p->cur != p->end) + return *p->cur++; + if (p->res == SZ_OK) + { + size_t size = p->cur - p->begin; + p->processed += size; + p->res = p->inStream->Skip(p->inStream, size); + size = (1 << 25); + p->res = p->inStream->Look(p->inStream, (const void **)&p->begin, &size); + p->cur = p->begin; + p->end = p->begin + size; + if (size != 0) + return *p->cur++;; + } + p->extra = True; + return 0; +} + +static SRes SzDecodePpmd(CSzCoderInfo *coder, UInt64 inSize, ILookInStream *inStream, + Byte *outBuffer, SizeT outSize, ISzAlloc *allocMain) +{ + CPpmd7 ppmd; + CByteInToLook s; + SRes res = SZ_OK; + + s.p.Read = ReadByte; + s.inStream = inStream; + s.begin = s.end = s.cur = NULL; + s.extra = False; + s.res = SZ_OK; + s.processed = 0; + + if (coder->Props.size != 5) + return SZ_ERROR_UNSUPPORTED; + + { + unsigned order = coder->Props.data[0]; + UInt32 memSize = GetUi32(coder->Props.data + 1); + if (order < PPMD7_MIN_ORDER || + order > PPMD7_MAX_ORDER || + memSize < PPMD7_MIN_MEM_SIZE || + memSize > PPMD7_MAX_MEM_SIZE) + return SZ_ERROR_UNSUPPORTED; + Ppmd7_Construct(&ppmd); + if (!Ppmd7_Alloc(&ppmd, memSize, allocMain)) + return SZ_ERROR_MEM; + Ppmd7_Init(&ppmd, order); + } + { + CPpmd7z_RangeDec rc; + Ppmd7z_RangeDec_CreateVTable(&rc); + rc.Stream = &s.p; + if (!Ppmd7z_RangeDec_Init(&rc)) + res = SZ_ERROR_DATA; + else if (s.extra) + res = (s.res != SZ_OK ? s.res : SZ_ERROR_DATA); + else + { + SizeT i; + for (i = 0; i < outSize; i++) + { + int sym = Ppmd7_DecodeSymbol(&ppmd, &rc.p); + if (s.extra || sym < 0) + break; + outBuffer[i] = (Byte)sym; + } + if (i != outSize) + res = (s.res != SZ_OK ? s.res : SZ_ERROR_DATA); + else if (s.processed + (s.cur - s.begin) != inSize || !Ppmd7z_RangeDec_IsFinishedOK(&rc)) + res = SZ_ERROR_DATA; + } + } + Ppmd7_Free(&ppmd, allocMain); + return res; +} + +#endif + + +static SRes SzDecodeLzma(CSzCoderInfo *coder, UInt64 inSize, ILookInStream *inStream, + Byte *outBuffer, SizeT outSize, ISzAlloc *allocMain) +{ + CLzmaDec state; + SRes res = SZ_OK; + + LzmaDec_Construct(&state); + RINOK(LzmaDec_AllocateProbs(&state, coder->Props.data, (unsigned)coder->Props.size, allocMain)); + state.dic = outBuffer; + state.dicBufSize = outSize; + LzmaDec_Init(&state); + + for (;;) + { + Byte *inBuf = NULL; + size_t lookahead = (1 << 18); + if (lookahead > inSize) + lookahead = (size_t)inSize; + res = inStream->Look((void *)inStream, (const void **)&inBuf, &lookahead); + if (res != SZ_OK) + break; + + { + SizeT inProcessed = (SizeT)lookahead, dicPos = state.dicPos; + ELzmaStatus status; + res = LzmaDec_DecodeToDic(&state, outSize, inBuf, &inProcessed, LZMA_FINISH_END, &status); + lookahead -= inProcessed; + inSize -= inProcessed; + if (res != SZ_OK) + break; + if (state.dicPos == state.dicBufSize || (inProcessed == 0 && dicPos == state.dicPos)) + { + if (state.dicBufSize != outSize || lookahead != 0 || + (status != LZMA_STATUS_FINISHED_WITH_MARK && + status != LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK)) + res = SZ_ERROR_DATA; + break; + } + res = inStream->Skip((void *)inStream, inProcessed); + if (res != SZ_OK) + break; + } + } + + LzmaDec_FreeProbs(&state, allocMain); + return res; +} + +static SRes SzDecodeLzma2(CSzCoderInfo *coder, UInt64 inSize, ILookInStream *inStream, + Byte *outBuffer, SizeT outSize, ISzAlloc *allocMain) +{ + CLzma2Dec state; + SRes res = SZ_OK; + + Lzma2Dec_Construct(&state); + if (coder->Props.size != 1) + return SZ_ERROR_DATA; + RINOK(Lzma2Dec_AllocateProbs(&state, coder->Props.data[0], allocMain)); + state.decoder.dic = outBuffer; + state.decoder.dicBufSize = outSize; + Lzma2Dec_Init(&state); + + for (;;) + { + Byte *inBuf = NULL; + size_t lookahead = (1 << 18); + if (lookahead > inSize) + lookahead = (size_t)inSize; + res = inStream->Look((void *)inStream, (const void **)&inBuf, &lookahead); + if (res != SZ_OK) + break; + + { + SizeT inProcessed = (SizeT)lookahead, dicPos = state.decoder.dicPos; + ELzmaStatus status; + res = Lzma2Dec_DecodeToDic(&state, outSize, inBuf, &inProcessed, LZMA_FINISH_END, &status); + lookahead -= inProcessed; + inSize -= inProcessed; + if (res != SZ_OK) + break; + if (state.decoder.dicPos == state.decoder.dicBufSize || (inProcessed == 0 && dicPos == state.decoder.dicPos)) + { + if (state.decoder.dicBufSize != outSize || lookahead != 0 || + (status != LZMA_STATUS_FINISHED_WITH_MARK)) + res = SZ_ERROR_DATA; + break; + } + res = inStream->Skip((void *)inStream, inProcessed); + if (res != SZ_OK) + break; + } + } + + Lzma2Dec_FreeProbs(&state, allocMain); + return res; +} + +static SRes SzDecodeCopy(UInt64 inSize, ILookInStream *inStream, Byte *outBuffer) +{ + while (inSize > 0) + { + void *inBuf; + size_t curSize = (1 << 18); + if (curSize > inSize) + curSize = (size_t)inSize; + RINOK(inStream->Look((void *)inStream, (const void **)&inBuf, &curSize)); + if (curSize == 0) + return SZ_ERROR_INPUT_EOF; + memcpy(outBuffer, inBuf, curSize); + outBuffer += curSize; + inSize -= curSize; + RINOK(inStream->Skip((void *)inStream, curSize)); + } + return SZ_OK; +} + +static Bool IS_MAIN_METHOD(UInt32 m) +{ + switch(m) + { + case k_Copy: + case k_LZMA: + case k_LZMA2: + #ifdef _7ZIP_PPMD_SUPPPORT + case k_PPMD: + #endif + return True; + } + return False; +} + +static Bool IS_SUPPORTED_CODER(const CSzCoderInfo *c) +{ + return + c->NumInStreams == 1 && + c->NumOutStreams == 1 && + c->MethodID <= (UInt32)0xFFFFFFFF && + IS_MAIN_METHOD((UInt32)c->MethodID); +} + +#define IS_BCJ2(c) ((c)->MethodID == k_BCJ2 && (c)->NumInStreams == 4 && (c)->NumOutStreams == 1) + +static SRes CheckSupportedFolder(const CSzFolder *f) +{ + if (f->NumCoders < 1 || f->NumCoders > 4) + return SZ_ERROR_UNSUPPORTED; + if (!IS_SUPPORTED_CODER(&f->Coders[0])) + return SZ_ERROR_UNSUPPORTED; + if (f->NumCoders == 1) + { + if (f->NumPackStreams != 1 || f->PackStreams[0] != 0 || f->NumBindPairs != 0) + return SZ_ERROR_UNSUPPORTED; + return SZ_OK; + } + if (f->NumCoders == 2) + { + CSzCoderInfo *c = &f->Coders[1]; + if (c->MethodID > (UInt32)0xFFFFFFFF || + c->NumInStreams != 1 || + c->NumOutStreams != 1 || + f->NumPackStreams != 1 || + f->PackStreams[0] != 0 || + f->NumBindPairs != 1 || + f->BindPairs[0].InIndex != 1 || + f->BindPairs[0].OutIndex != 0) + return SZ_ERROR_UNSUPPORTED; + switch ((UInt32)c->MethodID) + { + case k_BCJ: + case k_ARM: + break; + default: + return SZ_ERROR_UNSUPPORTED; + } + return SZ_OK; + } + if (f->NumCoders == 4) + { + if (!IS_SUPPORTED_CODER(&f->Coders[1]) || + !IS_SUPPORTED_CODER(&f->Coders[2]) || + !IS_BCJ2(&f->Coders[3])) + return SZ_ERROR_UNSUPPORTED; + if (f->NumPackStreams != 4 || + f->PackStreams[0] != 2 || + f->PackStreams[1] != 6 || + f->PackStreams[2] != 1 || + f->PackStreams[3] != 0 || + f->NumBindPairs != 3 || + f->BindPairs[0].InIndex != 5 || f->BindPairs[0].OutIndex != 0 || + f->BindPairs[1].InIndex != 4 || f->BindPairs[1].OutIndex != 1 || + f->BindPairs[2].InIndex != 3 || f->BindPairs[2].OutIndex != 2) + return SZ_ERROR_UNSUPPORTED; + return SZ_OK; + } + return SZ_ERROR_UNSUPPORTED; +} + +static UInt64 GetSum(const UInt64 *values, UInt32 index) +{ + UInt64 sum = 0; + UInt32 i; + for (i = 0; i < index; i++) + sum += values[i]; + return sum; +} + +#define CASE_BRA_CONV(isa) case k_ ## isa: isa ## _Convert(outBuffer, outSize, 0, 0); break; + +static SRes SzFolder_Decode2(const CSzFolder *folder, const UInt64 *packSizes, + ILookInStream *inStream, UInt64 startPos, + Byte *outBuffer, SizeT outSize, ISzAlloc *allocMain, + Byte *tempBuf[]) +{ + UInt32 ci; + SizeT tempSizes[3] = { 0, 0, 0}; + SizeT tempSize3 = 0; + Byte *tempBuf3 = 0; + + RINOK(CheckSupportedFolder(folder)); + + for (ci = 0; ci < folder->NumCoders; ci++) + { + CSzCoderInfo *coder = &folder->Coders[ci]; + + if (IS_MAIN_METHOD((UInt32)coder->MethodID)) + { + UInt32 si = 0; + UInt64 offset; + UInt64 inSize; + Byte *outBufCur = outBuffer; + SizeT outSizeCur = outSize; + if (folder->NumCoders == 4) + { + UInt32 indices[] = { 3, 2, 0 }; + UInt64 unpackSize = folder->UnpackSizes[ci]; + si = indices[ci]; + if (ci < 2) + { + Byte *temp; + outSizeCur = (SizeT)unpackSize; + if (outSizeCur != unpackSize) + return SZ_ERROR_MEM; + temp = (Byte *)IAlloc_Alloc(allocMain, outSizeCur); + if (temp == 0 && outSizeCur != 0) + return SZ_ERROR_MEM; + outBufCur = tempBuf[1 - ci] = temp; + tempSizes[1 - ci] = outSizeCur; + } + else if (ci == 2) + { + if (unpackSize > outSize) /* check it */ + return SZ_ERROR_PARAM; + tempBuf3 = outBufCur = outBuffer + (outSize - (size_t)unpackSize); + tempSize3 = outSizeCur = (SizeT)unpackSize; + } + else + return SZ_ERROR_UNSUPPORTED; + } + offset = GetSum(packSizes, si); + inSize = packSizes[si]; + RINOK(LookInStream_SeekTo(inStream, startPos + offset)); + + if (coder->MethodID == k_Copy) + { + if (inSize != outSizeCur) /* check it */ + return SZ_ERROR_DATA; + RINOK(SzDecodeCopy(inSize, inStream, outBufCur)); + } + else if (coder->MethodID == k_LZMA) + { + RINOK(SzDecodeLzma(coder, inSize, inStream, outBufCur, outSizeCur, allocMain)); + } + else if (coder->MethodID == k_LZMA2) + { + RINOK(SzDecodeLzma2(coder, inSize, inStream, outBufCur, outSizeCur, allocMain)); + } + else + { + #ifdef _7ZIP_PPMD_SUPPPORT + RINOK(SzDecodePpmd(coder, inSize, inStream, outBufCur, outSizeCur, allocMain)); + #else + return SZ_ERROR_UNSUPPORTED; + #endif + } + } + else if (coder->MethodID == k_BCJ2) + { + UInt64 offset = GetSum(packSizes, 1); + UInt64 s3Size = packSizes[1]; + SRes res; + if (ci != 3) + return SZ_ERROR_UNSUPPORTED; + RINOK(LookInStream_SeekTo(inStream, startPos + offset)); + tempSizes[2] = (SizeT)s3Size; + if (tempSizes[2] != s3Size) + return SZ_ERROR_MEM; + tempBuf[2] = (Byte *)IAlloc_Alloc(allocMain, tempSizes[2]); + if (tempBuf[2] == 0 && tempSizes[2] != 0) + return SZ_ERROR_MEM; + res = SzDecodeCopy(s3Size, inStream, tempBuf[2]); + RINOK(res) + + res = Bcj2_Decode( + tempBuf3, tempSize3, + tempBuf[0], tempSizes[0], + tempBuf[1], tempSizes[1], + tempBuf[2], tempSizes[2], + outBuffer, outSize); + RINOK(res) + } + else + { + if (ci != 1) + return SZ_ERROR_UNSUPPORTED; + switch(coder->MethodID) + { + case k_BCJ: + { + UInt32 state; + x86_Convert_Init(state); + x86_Convert(outBuffer, outSize, 0, &state, 0); + break; + } + CASE_BRA_CONV(ARM) + default: + return SZ_ERROR_UNSUPPORTED; + } + } + } + return SZ_OK; +} + +SRes SzFolder_Decode(const CSzFolder *folder, const UInt64 *packSizes, + ILookInStream *inStream, UInt64 startPos, + Byte *outBuffer, size_t outSize, ISzAlloc *allocMain) +{ + Byte *tempBuf[3] = { 0, 0, 0}; + int i; + SRes res = SzFolder_Decode2(folder, packSizes, inStream, startPos, + outBuffer, (SizeT)outSize, allocMain, tempBuf); + for (i = 0; i < 3; i++) + IAlloc_Free(allocMain, tempBuf[i]); + return res; +} diff --git a/fex/7z_C/7zDecode.h b/fex/7z_C/7zDecode.h index 9607915b..e19fe387 100644 --- a/fex/7z_C/7zDecode.h +++ b/fex/7z_C/7zDecode.h @@ -1,13 +1,13 @@ -/* 7zDecode.h -- Decoding from 7z folder -2008-11-23 : Igor Pavlov : Public domain */ - -#ifndef __7Z_DECODE_H -#define __7Z_DECODE_H - -#include "7zItem.h" - -SRes SzDecode(const UInt64 *packSizes, const CSzFolder *folder, - ILookInStream *stream, UInt64 startPos, - Byte *outBuffer, size_t outSize, ISzAlloc *allocMain); - -#endif +/* 7zDecode.h -- Decoding from 7z folder +2008-11-23 : Igor Pavlov : Public domain */ + +#ifndef __7Z_DECODE_H +#define __7Z_DECODE_H + +#include "7zItem.h" + +SRes SzDecode(const UInt64 *packSizes, const CSzFolder *folder, + ILookInStream *stream, UInt64 startPos, + Byte *outBuffer, size_t outSize, ISzAlloc *allocMain); + +#endif diff --git a/fex/7z_C/7zExtract.c b/fex/7z_C/7zExtract.c index e6630855..99ef3654 100644 --- a/fex/7z_C/7zExtract.c +++ b/fex/7z_C/7zExtract.c @@ -1,93 +1,93 @@ -/* 7zExtract.c -- Extracting from 7z archive -2008-11-23 : Igor Pavlov : Public domain */ - -#include "7zCrc.h" -#include "7zDecode.h" -#include "7zExtract.h" - -SRes SzAr_Extract( - const CSzArEx *p, - ILookInStream *inStream, - UInt32 fileIndex, - UInt32 *blockIndex, - Byte **outBuffer, - size_t *outBufferSize, - size_t *offset, - size_t *outSizeProcessed, - ISzAlloc *allocMain, - ISzAlloc *allocTemp) -{ - UInt32 folderIndex = p->FileIndexToFolderIndexMap[fileIndex]; - SRes res = SZ_OK; - *offset = 0; - *outSizeProcessed = 0; - if (folderIndex == (UInt32)-1) - { - IAlloc_Free(allocMain, *outBuffer); - *blockIndex = folderIndex; - *outBuffer = 0; - *outBufferSize = 0; - return SZ_OK; - } - - if (*outBuffer == 0 || *blockIndex != folderIndex) - { - CSzFolder *folder = p->db.Folders + folderIndex; - UInt64 unpackSizeSpec = SzFolder_GetUnpackSize(folder); - size_t unpackSize = (size_t)unpackSizeSpec; - UInt64 startOffset = SzArEx_GetFolderStreamPos(p, folderIndex, 0); - - if (unpackSize != unpackSizeSpec) - return SZ_ERROR_MEM; - *blockIndex = folderIndex; - IAlloc_Free(allocMain, *outBuffer); - *outBuffer = 0; - - RINOK(LookInStream_SeekTo(inStream, startOffset)); - - if (res == SZ_OK) - { - *outBufferSize = unpackSize; - if (unpackSize != 0) - { - *outBuffer = (Byte *)IAlloc_Alloc(allocMain, unpackSize); - if (*outBuffer == 0) - res = SZ_ERROR_MEM; - } - if (res == SZ_OK) - { - res = SzDecode(p->db.PackSizes + - p->FolderStartPackStreamIndex[folderIndex], folder, - inStream, startOffset, - *outBuffer, unpackSize, allocTemp); - if (res == SZ_OK) - { - if (folder->UnpackCRCDefined) - { - if (CrcCalc(*outBuffer, unpackSize) != folder->UnpackCRC) - res = SZ_ERROR_CRC; - } - } - } - } - } - if (res == SZ_OK) - { - UInt32 i; - CSzFileItem *fileItem = p->db.Files + fileIndex; - *offset = 0; - for (i = p->FolderStartFileIndex[folderIndex]; i < fileIndex; i++) - *offset += (UInt32)p->db.Files[i].Size; - *outSizeProcessed = (size_t)fileItem->Size; - if (*offset + *outSizeProcessed > *outBufferSize) - return SZ_ERROR_FAIL; - { - if (fileItem->FileCRCDefined) - { - if (CrcCalc(*outBuffer + *offset, *outSizeProcessed) != fileItem->FileCRC) - res = SZ_ERROR_CRC; - } - } - } - return res; -} +/* 7zExtract.c -- Extracting from 7z archive +2008-11-23 : Igor Pavlov : Public domain */ + +#include "7zCrc.h" +#include "7zDecode.h" +#include "7zExtract.h" + +SRes SzAr_Extract( + const CSzArEx *p, + ILookInStream *inStream, + UInt32 fileIndex, + UInt32 *blockIndex, + Byte **outBuffer, + size_t *outBufferSize, + size_t *offset, + size_t *outSizeProcessed, + ISzAlloc *allocMain, + ISzAlloc *allocTemp) +{ + UInt32 folderIndex = p->FileIndexToFolderIndexMap[fileIndex]; + SRes res = SZ_OK; + *offset = 0; + *outSizeProcessed = 0; + if (folderIndex == (UInt32)-1) + { + IAlloc_Free(allocMain, *outBuffer); + *blockIndex = folderIndex; + *outBuffer = 0; + *outBufferSize = 0; + return SZ_OK; + } + + if (*outBuffer == 0 || *blockIndex != folderIndex) + { + CSzFolder *folder = p->db.Folders + folderIndex; + UInt64 unpackSizeSpec = SzFolder_GetUnpackSize(folder); + size_t unpackSize = (size_t)unpackSizeSpec; + UInt64 startOffset = SzArEx_GetFolderStreamPos(p, folderIndex, 0); + + if (unpackSize != unpackSizeSpec) + return SZ_ERROR_MEM; + *blockIndex = folderIndex; + IAlloc_Free(allocMain, *outBuffer); + *outBuffer = 0; + + RINOK(LookInStream_SeekTo(inStream, startOffset)); + + if (res == SZ_OK) + { + *outBufferSize = unpackSize; + if (unpackSize != 0) + { + *outBuffer = (Byte *)IAlloc_Alloc(allocMain, unpackSize); + if (*outBuffer == 0) + res = SZ_ERROR_MEM; + } + if (res == SZ_OK) + { + res = SzDecode(p->db.PackSizes + + p->FolderStartPackStreamIndex[folderIndex], folder, + inStream, startOffset, + *outBuffer, unpackSize, allocTemp); + if (res == SZ_OK) + { + if (folder->UnpackCRCDefined) + { + if (CrcCalc(*outBuffer, unpackSize) != folder->UnpackCRC) + res = SZ_ERROR_CRC; + } + } + } + } + } + if (res == SZ_OK) + { + UInt32 i; + CSzFileItem *fileItem = p->db.Files + fileIndex; + *offset = 0; + for (i = p->FolderStartFileIndex[folderIndex]; i < fileIndex; i++) + *offset += (UInt32)p->db.Files[i].Size; + *outSizeProcessed = (size_t)fileItem->Size; + if (*offset + *outSizeProcessed > *outBufferSize) + return SZ_ERROR_FAIL; + { + if (fileItem->FileCRCDefined) + { + if (CrcCalc(*outBuffer + *offset, *outSizeProcessed) != fileItem->FileCRC) + res = SZ_ERROR_CRC; + } + } + } + return res; +} diff --git a/fex/7z_C/7zExtract.h b/fex/7z_C/7zExtract.h index 4f956bee..1ca110c6 100644 --- a/fex/7z_C/7zExtract.h +++ b/fex/7z_C/7zExtract.h @@ -1,49 +1,49 @@ -/* 7zExtract.h -- Extracting from 7z archive -2008-11-23 : Igor Pavlov : Public domain */ - -#ifndef __7Z_EXTRACT_H -#define __7Z_EXTRACT_H - -#include "7zIn.h" - -#ifdef __cplusplus - extern "C" { -#endif - -/* - SzExtract extracts file from archive - - *outBuffer must be 0 before first call for each new archive. - - Extracting cache: - If you need to decompress more than one file, you can send - these values from previous call: - *blockIndex, - *outBuffer, - *outBufferSize - You can consider "*outBuffer" as cache of solid block. If your archive is solid, - it will increase decompression speed. - - If you use external function, you can declare these 3 cache variables - (blockIndex, outBuffer, outBufferSize) as static in that external function. - - Free *outBuffer and set *outBuffer to 0, if you want to flush cache. -*/ - -SRes SzAr_Extract( - const CSzArEx *db, - ILookInStream *inStream, - UInt32 fileIndex, /* index of file */ - UInt32 *blockIndex, /* index of solid block */ - Byte **outBuffer, /* pointer to pointer to output buffer (allocated with allocMain) */ - size_t *outBufferSize, /* buffer size for output buffer */ - size_t *offset, /* offset of stream for required file in *outBuffer */ - size_t *outSizeProcessed, /* size of file in *outBuffer */ - ISzAlloc *allocMain, - ISzAlloc *allocTemp); - -#ifdef __cplusplus - } -#endif - -#endif +/* 7zExtract.h -- Extracting from 7z archive +2008-11-23 : Igor Pavlov : Public domain */ + +#ifndef __7Z_EXTRACT_H +#define __7Z_EXTRACT_H + +#include "7zIn.h" + +#ifdef __cplusplus + extern "C" { +#endif + +/* + SzExtract extracts file from archive + + *outBuffer must be 0 before first call for each new archive. + + Extracting cache: + If you need to decompress more than one file, you can send + these values from previous call: + *blockIndex, + *outBuffer, + *outBufferSize + You can consider "*outBuffer" as cache of solid block. If your archive is solid, + it will increase decompression speed. + + If you use external function, you can declare these 3 cache variables + (blockIndex, outBuffer, outBufferSize) as static in that external function. + + Free *outBuffer and set *outBuffer to 0, if you want to flush cache. +*/ + +SRes SzAr_Extract( + const CSzArEx *db, + ILookInStream *inStream, + UInt32 fileIndex, /* index of file */ + UInt32 *blockIndex, /* index of solid block */ + Byte **outBuffer, /* pointer to pointer to output buffer (allocated with allocMain) */ + size_t *outBufferSize, /* buffer size for output buffer */ + size_t *offset, /* offset of stream for required file in *outBuffer */ + size_t *outSizeProcessed, /* size of file in *outBuffer */ + ISzAlloc *allocMain, + ISzAlloc *allocTemp); + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/fex/7z_C/7zHeader.c b/fex/7z_C/7zHeader.c index c44ff4a4..e48faa48 100644 --- a/fex/7z_C/7zHeader.c +++ b/fex/7z_C/7zHeader.c @@ -1,6 +1,6 @@ -/* 7zHeader.c -- 7z Headers -2008-10-04 : Igor Pavlov : Public domain */ - -#include "7zHeader.h" - -Byte k7zSignature[k7zSignatureSize] = {'7', 'z', 0xBC, 0xAF, 0x27, 0x1C}; +/* 7zHeader.c -- 7z Headers +2008-10-04 : Igor Pavlov : Public domain */ + +#include "7zHeader.h" + +Byte k7zSignature[k7zSignatureSize] = {'7', 'z', 0xBC, 0xAF, 0x27, 0x1C}; diff --git a/fex/7z_C/7zHeader.h b/fex/7z_C/7zHeader.h index a8c85e8d..ad095df4 100644 --- a/fex/7z_C/7zHeader.h +++ b/fex/7z_C/7zHeader.h @@ -1,57 +1,57 @@ -/* 7zHeader.h -- 7z Headers -2008-10-04 : Igor Pavlov : Public domain */ - -#ifndef __7Z_HEADER_H -#define __7Z_HEADER_H - -#include "Types.h" - -#define k7zSignatureSize 6 -extern Byte k7zSignature[k7zSignatureSize]; - -#define k7zMajorVersion 0 - -#define k7zStartHeaderSize 0x20 - -enum EIdEnum -{ - k7zIdEnd, - - k7zIdHeader, - - k7zIdArchiveProperties, - - k7zIdAdditionalStreamsInfo, - k7zIdMainStreamsInfo, - k7zIdFilesInfo, - - k7zIdPackInfo, - k7zIdUnpackInfo, - k7zIdSubStreamsInfo, - - k7zIdSize, - k7zIdCRC, - - k7zIdFolder, - - k7zIdCodersUnpackSize, - k7zIdNumUnpackStream, - - k7zIdEmptyStream, - k7zIdEmptyFile, - k7zIdAnti, - - k7zIdName, - k7zIdCTime, - k7zIdATime, - k7zIdMTime, - k7zIdWinAttributes, - k7zIdComment, - - k7zIdEncodedHeader, - - k7zIdStartPos, - k7zIdDummy -}; - -#endif +/* 7zHeader.h -- 7z Headers +2008-10-04 : Igor Pavlov : Public domain */ + +#ifndef __7Z_HEADER_H +#define __7Z_HEADER_H + +#include "Types.h" + +#define k7zSignatureSize 6 +extern Byte k7zSignature[k7zSignatureSize]; + +#define k7zMajorVersion 0 + +#define k7zStartHeaderSize 0x20 + +enum EIdEnum +{ + k7zIdEnd, + + k7zIdHeader, + + k7zIdArchiveProperties, + + k7zIdAdditionalStreamsInfo, + k7zIdMainStreamsInfo, + k7zIdFilesInfo, + + k7zIdPackInfo, + k7zIdUnpackInfo, + k7zIdSubStreamsInfo, + + k7zIdSize, + k7zIdCRC, + + k7zIdFolder, + + k7zIdCodersUnpackSize, + k7zIdNumUnpackStream, + + k7zIdEmptyStream, + k7zIdEmptyFile, + k7zIdAnti, + + k7zIdName, + k7zIdCTime, + k7zIdATime, + k7zIdMTime, + k7zIdWinAttributes, + k7zIdComment, + + k7zIdEncodedHeader, + + k7zIdStartPos, + k7zIdDummy +}; + +#endif diff --git a/fex/7z_C/7zIn.c b/fex/7z_C/7zIn.c index f1a44928..ec93a43f 100644 --- a/fex/7z_C/7zIn.c +++ b/fex/7z_C/7zIn.c @@ -1,1402 +1,1402 @@ -/* 7zIn.c -- 7z Input functions -2010-10-29 : Igor Pavlov : Public domain */ - -#include - -#include "7z.h" -#include "7zCrc.h" -#include "CpuArch.h" - -Byte k7zSignature[k7zSignatureSize] = {'7', 'z', 0xBC, 0xAF, 0x27, 0x1C}; - -#define RINOM(x) { if ((x) == 0) return SZ_ERROR_MEM; } - -#define NUM_FOLDER_CODERS_MAX 32 -#define NUM_CODER_STREAMS_MAX 32 - -void SzCoderInfo_Init(CSzCoderInfo *p) -{ - Buf_Init(&p->Props); -} - -void SzCoderInfo_Free(CSzCoderInfo *p, ISzAlloc *alloc) -{ - Buf_Free(&p->Props, alloc); - SzCoderInfo_Init(p); -} - -void SzFolder_Init(CSzFolder *p) -{ - p->Coders = 0; - p->BindPairs = 0; - p->PackStreams = 0; - p->UnpackSizes = 0; - p->NumCoders = 0; - p->NumBindPairs = 0; - p->NumPackStreams = 0; - p->UnpackCRCDefined = 0; - p->UnpackCRC = 0; - p->NumUnpackStreams = 0; -} - -void SzFolder_Free(CSzFolder *p, ISzAlloc *alloc) -{ - UInt32 i; - if (p->Coders) - for (i = 0; i < p->NumCoders; i++) - SzCoderInfo_Free(&p->Coders[i], alloc); - IAlloc_Free(alloc, p->Coders); - IAlloc_Free(alloc, p->BindPairs); - IAlloc_Free(alloc, p->PackStreams); - IAlloc_Free(alloc, p->UnpackSizes); - SzFolder_Init(p); -} - -UInt32 SzFolder_GetNumOutStreams(CSzFolder *p) -{ - UInt32 result = 0; - UInt32 i; - for (i = 0; i < p->NumCoders; i++) - result += p->Coders[i].NumOutStreams; - return result; -} - -int SzFolder_FindBindPairForInStream(CSzFolder *p, UInt32 inStreamIndex) -{ - UInt32 i; - for (i = 0; i < p->NumBindPairs; i++) - if (p->BindPairs[i].InIndex == inStreamIndex) - return i; - return -1; -} - - -int SzFolder_FindBindPairForOutStream(CSzFolder *p, UInt32 outStreamIndex) -{ - UInt32 i; - for (i = 0; i < p->NumBindPairs; i++) - if (p->BindPairs[i].OutIndex == outStreamIndex) - return i; - return -1; -} - -UInt64 SzFolder_GetUnpackSize(CSzFolder *p) -{ - int i = (int)SzFolder_GetNumOutStreams(p); - if (i == 0) - return 0; - for (i--; i >= 0; i--) - if (SzFolder_FindBindPairForOutStream(p, i) < 0) - return p->UnpackSizes[i]; - /* throw 1; */ - return 0; -} - -void SzFile_Init(CSzFileItem *p) -{ - p->HasStream = 1; - p->IsDir = 0; - p->IsAnti = 0; - p->CrcDefined = 0; - p->MTimeDefined = 0; -} - -void SzAr_Init(CSzAr *p) -{ - p->PackSizes = 0; - p->PackCRCsDefined = 0; - p->PackCRCs = 0; - p->Folders = 0; - p->Files = 0; - p->NumPackStreams = 0; - p->NumFolders = 0; - p->NumFiles = 0; -} - -void SzAr_Free(CSzAr *p, ISzAlloc *alloc) -{ - UInt32 i; - if (p->Folders) - for (i = 0; i < p->NumFolders; i++) - SzFolder_Free(&p->Folders[i], alloc); - - IAlloc_Free(alloc, p->PackSizes); - IAlloc_Free(alloc, p->PackCRCsDefined); - IAlloc_Free(alloc, p->PackCRCs); - IAlloc_Free(alloc, p->Folders); - IAlloc_Free(alloc, p->Files); - SzAr_Init(p); -} - - -void SzArEx_Init(CSzArEx *p) -{ - SzAr_Init(&p->db); - p->FolderStartPackStreamIndex = 0; - p->PackStreamStartPositions = 0; - p->FolderStartFileIndex = 0; - p->FileIndexToFolderIndexMap = 0; - p->FileNameOffsets = 0; - Buf_Init(&p->FileNames); -} - -void SzArEx_Free(CSzArEx *p, ISzAlloc *alloc) -{ - IAlloc_Free(alloc, p->FolderStartPackStreamIndex); - IAlloc_Free(alloc, p->PackStreamStartPositions); - IAlloc_Free(alloc, p->FolderStartFileIndex); - IAlloc_Free(alloc, p->FileIndexToFolderIndexMap); - - IAlloc_Free(alloc, p->FileNameOffsets); - Buf_Free(&p->FileNames, alloc); - - SzAr_Free(&p->db, alloc); - SzArEx_Init(p); -} - -/* -UInt64 GetFolderPackStreamSize(int folderIndex, int streamIndex) const -{ - return PackSizes[FolderStartPackStreamIndex[folderIndex] + streamIndex]; -} - -UInt64 GetFilePackSize(int fileIndex) const -{ - int folderIndex = FileIndexToFolderIndexMap[fileIndex]; - if (folderIndex >= 0) - { - const CSzFolder &folderInfo = Folders[folderIndex]; - if (FolderStartFileIndex[folderIndex] == fileIndex) - return GetFolderFullPackSize(folderIndex); - } - return 0; -} -*/ - -#define MY_ALLOC(T, p, size, alloc) { if ((size) == 0) p = 0; else \ - if ((p = (T *)IAlloc_Alloc(alloc, (size) * sizeof(T))) == 0) return SZ_ERROR_MEM; } - -static SRes SzArEx_Fill(CSzArEx *p, ISzAlloc *alloc) -{ - UInt32 startPos = 0; - UInt64 startPosSize = 0; - UInt32 i; - UInt32 folderIndex = 0; - UInt32 indexInFolder = 0; - MY_ALLOC(UInt32, p->FolderStartPackStreamIndex, p->db.NumFolders, alloc); - for (i = 0; i < p->db.NumFolders; i++) - { - p->FolderStartPackStreamIndex[i] = startPos; - startPos += p->db.Folders[i].NumPackStreams; - } - - MY_ALLOC(UInt64, p->PackStreamStartPositions, p->db.NumPackStreams, alloc); - - for (i = 0; i < p->db.NumPackStreams; i++) - { - p->PackStreamStartPositions[i] = startPosSize; - startPosSize += p->db.PackSizes[i]; - } - - MY_ALLOC(UInt32, p->FolderStartFileIndex, p->db.NumFolders, alloc); - MY_ALLOC(UInt32, p->FileIndexToFolderIndexMap, p->db.NumFiles, alloc); - - for (i = 0; i < p->db.NumFiles; i++) - { - CSzFileItem *file = p->db.Files + i; - int emptyStream = !file->HasStream; - if (emptyStream && indexInFolder == 0) - { - p->FileIndexToFolderIndexMap[i] = (UInt32)-1; - continue; - } - if (indexInFolder == 0) - { - /* - v3.13 incorrectly worked with empty folders - v4.07: Loop for skipping empty folders - */ - for (;;) - { - if (folderIndex >= p->db.NumFolders) - return SZ_ERROR_ARCHIVE; - p->FolderStartFileIndex[folderIndex] = i; - if (p->db.Folders[folderIndex].NumUnpackStreams != 0) - break; - folderIndex++; - } - } - p->FileIndexToFolderIndexMap[i] = folderIndex; - if (emptyStream) - continue; - indexInFolder++; - if (indexInFolder >= p->db.Folders[folderIndex].NumUnpackStreams) - { - folderIndex++; - indexInFolder = 0; - } - } - return SZ_OK; -} - - -UInt64 SzArEx_GetFolderStreamPos(const CSzArEx *p, UInt32 folderIndex, UInt32 indexInFolder) -{ - return p->dataPos + - p->PackStreamStartPositions[p->FolderStartPackStreamIndex[folderIndex] + indexInFolder]; -} - -int SzArEx_GetFolderFullPackSize(const CSzArEx *p, UInt32 folderIndex, UInt64 *resSize) -{ - UInt32 packStreamIndex = p->FolderStartPackStreamIndex[folderIndex]; - CSzFolder *folder = p->db.Folders + folderIndex; - UInt64 size = 0; - UInt32 i; - for (i = 0; i < folder->NumPackStreams; i++) - { - UInt64 t = size + p->db.PackSizes[packStreamIndex + i]; - if (t < size) /* check it */ - return SZ_ERROR_FAIL; - size = t; - } - *resSize = size; - return SZ_OK; -} - - -/* -SRes SzReadTime(const CObjectVector &dataVector, - CObjectVector &files, UInt64 type) -{ - CBoolVector boolVector; - RINOK(ReadBoolVector2(files.Size(), boolVector)) - - CStreamSwitch streamSwitch; - RINOK(streamSwitch.Set(this, &dataVector)); - - for (int i = 0; i < files.Size(); i++) - { - CSzFileItem &file = files[i]; - CArchiveFileTime fileTime; - bool defined = boolVector[i]; - if (defined) - { - UInt32 low, high; - RINOK(SzReadUInt32(low)); - RINOK(SzReadUInt32(high)); - fileTime.dwLowDateTime = low; - fileTime.dwHighDateTime = high; - } - switch(type) - { - case k7zIdCTime: file.IsCTimeDefined = defined; if (defined) file.CTime = fileTime; break; - case k7zIdATime: file.IsATimeDefined = defined; if (defined) file.ATime = fileTime; break; - case k7zIdMTime: file.IsMTimeDefined = defined; if (defined) file.MTime = fileTime; break; - } - } - return SZ_OK; -} -*/ - -static int TestSignatureCandidate(Byte *testBytes) -{ - size_t i; - for (i = 0; i < k7zSignatureSize; i++) - if (testBytes[i] != k7zSignature[i]) - return 0; - return 1; -} - -typedef struct _CSzState -{ - Byte *Data; - size_t Size; -}CSzData; - -static SRes SzReadByte(CSzData *sd, Byte *b) -{ - if (sd->Size == 0) - return SZ_ERROR_ARCHIVE; - sd->Size--; - *b = *sd->Data++; - return SZ_OK; -} - -static SRes SzReadBytes(CSzData *sd, Byte *data, size_t size) -{ - size_t i; - for (i = 0; i < size; i++) - { - RINOK(SzReadByte(sd, data + i)); - } - return SZ_OK; -} - -static SRes SzReadUInt32(CSzData *sd, UInt32 *value) -{ - int i; - *value = 0; - for (i = 0; i < 4; i++) - { - Byte b; - RINOK(SzReadByte(sd, &b)); - *value |= ((UInt32)(b) << (8 * i)); - } - return SZ_OK; -} - -static SRes SzReadNumber(CSzData *sd, UInt64 *value) -{ - Byte firstByte; - Byte mask = 0x80; - int i; - RINOK(SzReadByte(sd, &firstByte)); - *value = 0; - for (i = 0; i < 8; i++) - { - Byte b; - if ((firstByte & mask) == 0) - { - UInt64 highPart = firstByte & (mask - 1); - *value += (highPart << (8 * i)); - return SZ_OK; - } - RINOK(SzReadByte(sd, &b)); - *value |= ((UInt64)b << (8 * i)); - mask >>= 1; - } - return SZ_OK; -} - -static SRes SzReadNumber32(CSzData *sd, UInt32 *value) -{ - UInt64 value64; - RINOK(SzReadNumber(sd, &value64)); - if (value64 >= 0x80000000) - return SZ_ERROR_UNSUPPORTED; - if (value64 >= ((UInt64)(1) << ((sizeof(size_t) - 1) * 8 + 2))) - return SZ_ERROR_UNSUPPORTED; - *value = (UInt32)value64; - return SZ_OK; -} - -static SRes SzReadID(CSzData *sd, UInt64 *value) -{ - return SzReadNumber(sd, value); -} - -static SRes SzSkeepDataSize(CSzData *sd, UInt64 size) -{ - if (size > sd->Size) - return SZ_ERROR_ARCHIVE; - sd->Size -= (size_t)size; - sd->Data += (size_t)size; - return SZ_OK; -} - -static SRes SzSkeepData(CSzData *sd) -{ - UInt64 size; - RINOK(SzReadNumber(sd, &size)); - return SzSkeepDataSize(sd, size); -} - -static SRes SzReadArchiveProperties(CSzData *sd) -{ - for (;;) - { - UInt64 type; - RINOK(SzReadID(sd, &type)); - if (type == k7zIdEnd) - break; - SzSkeepData(sd); - } - return SZ_OK; -} - -static SRes SzWaitAttribute(CSzData *sd, UInt64 attribute) -{ - for (;;) - { - UInt64 type; - RINOK(SzReadID(sd, &type)); - if (type == attribute) - return SZ_OK; - if (type == k7zIdEnd) - return SZ_ERROR_ARCHIVE; - RINOK(SzSkeepData(sd)); - } -} - -static SRes SzReadBoolVector(CSzData *sd, size_t numItems, Byte **v, ISzAlloc *alloc) -{ - Byte b = 0; - Byte mask = 0; - size_t i; - MY_ALLOC(Byte, *v, numItems, alloc); - for (i = 0; i < numItems; i++) - { - if (mask == 0) - { - RINOK(SzReadByte(sd, &b)); - mask = 0x80; - } - (*v)[i] = (Byte)(((b & mask) != 0) ? 1 : 0); - mask >>= 1; - } - return SZ_OK; -} - -static SRes SzReadBoolVector2(CSzData *sd, size_t numItems, Byte **v, ISzAlloc *alloc) -{ - Byte allAreDefined; - size_t i; - RINOK(SzReadByte(sd, &allAreDefined)); - if (allAreDefined == 0) - return SzReadBoolVector(sd, numItems, v, alloc); - MY_ALLOC(Byte, *v, numItems, alloc); - for (i = 0; i < numItems; i++) - (*v)[i] = 1; - return SZ_OK; -} - -static SRes SzReadHashDigests( - CSzData *sd, - size_t numItems, - Byte **digestsDefined, - UInt32 **digests, - ISzAlloc *alloc) -{ - size_t i; - RINOK(SzReadBoolVector2(sd, numItems, digestsDefined, alloc)); - MY_ALLOC(UInt32, *digests, numItems, alloc); - for (i = 0; i < numItems; i++) - if ((*digestsDefined)[i]) - { - RINOK(SzReadUInt32(sd, (*digests) + i)); - } - return SZ_OK; -} - -static SRes SzReadPackInfo( - CSzData *sd, - UInt64 *dataOffset, - UInt32 *numPackStreams, - UInt64 **packSizes, - Byte **packCRCsDefined, - UInt32 **packCRCs, - ISzAlloc *alloc) -{ - UInt32 i; - RINOK(SzReadNumber(sd, dataOffset)); - RINOK(SzReadNumber32(sd, numPackStreams)); - - RINOK(SzWaitAttribute(sd, k7zIdSize)); - - MY_ALLOC(UInt64, *packSizes, (size_t)*numPackStreams, alloc); - - for (i = 0; i < *numPackStreams; i++) - { - RINOK(SzReadNumber(sd, (*packSizes) + i)); - } - - for (;;) - { - UInt64 type; - RINOK(SzReadID(sd, &type)); - if (type == k7zIdEnd) - break; - if (type == k7zIdCRC) - { - RINOK(SzReadHashDigests(sd, (size_t)*numPackStreams, packCRCsDefined, packCRCs, alloc)); - continue; - } - RINOK(SzSkeepData(sd)); - } - if (*packCRCsDefined == 0) - { - MY_ALLOC(Byte, *packCRCsDefined, (size_t)*numPackStreams, alloc); - MY_ALLOC(UInt32, *packCRCs, (size_t)*numPackStreams, alloc); - for (i = 0; i < *numPackStreams; i++) - { - (*packCRCsDefined)[i] = 0; - (*packCRCs)[i] = 0; - } - } - return SZ_OK; -} - -static SRes SzReadSwitch(CSzData *sd) -{ - Byte external; - RINOK(SzReadByte(sd, &external)); - return (external == 0) ? SZ_OK: SZ_ERROR_UNSUPPORTED; -} - -static SRes SzGetNextFolderItem(CSzData *sd, CSzFolder *folder, ISzAlloc *alloc) -{ - UInt32 numCoders, numBindPairs, numPackStreams, i; - UInt32 numInStreams = 0, numOutStreams = 0; - - RINOK(SzReadNumber32(sd, &numCoders)); - if (numCoders > NUM_FOLDER_CODERS_MAX) - return SZ_ERROR_UNSUPPORTED; - folder->NumCoders = numCoders; - - MY_ALLOC(CSzCoderInfo, folder->Coders, (size_t)numCoders, alloc); - - for (i = 0; i < numCoders; i++) - SzCoderInfo_Init(folder->Coders + i); - - for (i = 0; i < numCoders; i++) - { - Byte mainByte; - CSzCoderInfo *coder = folder->Coders + i; - { - unsigned idSize, j; - Byte longID[15]; - RINOK(SzReadByte(sd, &mainByte)); - idSize = (unsigned)(mainByte & 0xF); - RINOK(SzReadBytes(sd, longID, idSize)); - if (idSize > sizeof(coder->MethodID)) - return SZ_ERROR_UNSUPPORTED; - coder->MethodID = 0; - for (j = 0; j < idSize; j++) - coder->MethodID |= (UInt64)longID[idSize - 1 - j] << (8 * j); - - if ((mainByte & 0x10) != 0) - { - RINOK(SzReadNumber32(sd, &coder->NumInStreams)); - RINOK(SzReadNumber32(sd, &coder->NumOutStreams)); - if (coder->NumInStreams > NUM_CODER_STREAMS_MAX || - coder->NumOutStreams > NUM_CODER_STREAMS_MAX) - return SZ_ERROR_UNSUPPORTED; - } - else - { - coder->NumInStreams = 1; - coder->NumOutStreams = 1; - } - if ((mainByte & 0x20) != 0) - { - UInt64 propertiesSize = 0; - RINOK(SzReadNumber(sd, &propertiesSize)); - if (!Buf_Create(&coder->Props, (size_t)propertiesSize, alloc)) - return SZ_ERROR_MEM; - RINOK(SzReadBytes(sd, coder->Props.data, (size_t)propertiesSize)); - } - } - while ((mainByte & 0x80) != 0) - { - RINOK(SzReadByte(sd, &mainByte)); - RINOK(SzSkeepDataSize(sd, (mainByte & 0xF))); - if ((mainByte & 0x10) != 0) - { - UInt32 n; - RINOK(SzReadNumber32(sd, &n)); - RINOK(SzReadNumber32(sd, &n)); - } - if ((mainByte & 0x20) != 0) - { - UInt64 propertiesSize = 0; - RINOK(SzReadNumber(sd, &propertiesSize)); - RINOK(SzSkeepDataSize(sd, propertiesSize)); - } - } - numInStreams += coder->NumInStreams; - numOutStreams += coder->NumOutStreams; - } - - if (numOutStreams == 0) - return SZ_ERROR_UNSUPPORTED; - - folder->NumBindPairs = numBindPairs = numOutStreams - 1; - MY_ALLOC(CSzBindPair, folder->BindPairs, (size_t)numBindPairs, alloc); - - for (i = 0; i < numBindPairs; i++) - { - CSzBindPair *bp = folder->BindPairs + i; - RINOK(SzReadNumber32(sd, &bp->InIndex)); - RINOK(SzReadNumber32(sd, &bp->OutIndex)); - } - - if (numInStreams < numBindPairs) - return SZ_ERROR_UNSUPPORTED; - - folder->NumPackStreams = numPackStreams = numInStreams - numBindPairs; - MY_ALLOC(UInt32, folder->PackStreams, (size_t)numPackStreams, alloc); - - if (numPackStreams == 1) - { - for (i = 0; i < numInStreams ; i++) - if (SzFolder_FindBindPairForInStream(folder, i) < 0) - break; - if (i == numInStreams) - return SZ_ERROR_UNSUPPORTED; - folder->PackStreams[0] = i; - } - else - for (i = 0; i < numPackStreams; i++) - { - RINOK(SzReadNumber32(sd, folder->PackStreams + i)); - } - return SZ_OK; -} - -static SRes SzReadUnpackInfo( - CSzData *sd, - UInt32 *numFolders, - CSzFolder **folders, /* for alloc */ - ISzAlloc *alloc, - ISzAlloc *allocTemp) -{ - UInt32 i; - RINOK(SzWaitAttribute(sd, k7zIdFolder)); - RINOK(SzReadNumber32(sd, numFolders)); - { - RINOK(SzReadSwitch(sd)); - - MY_ALLOC(CSzFolder, *folders, (size_t)*numFolders, alloc); - - for (i = 0; i < *numFolders; i++) - SzFolder_Init((*folders) + i); - - for (i = 0; i < *numFolders; i++) - { - RINOK(SzGetNextFolderItem(sd, (*folders) + i, alloc)); - } - } - - RINOK(SzWaitAttribute(sd, k7zIdCodersUnpackSize)); - - for (i = 0; i < *numFolders; i++) - { - UInt32 j; - CSzFolder *folder = (*folders) + i; - UInt32 numOutStreams = SzFolder_GetNumOutStreams(folder); - - MY_ALLOC(UInt64, folder->UnpackSizes, (size_t)numOutStreams, alloc); - - for (j = 0; j < numOutStreams; j++) - { - RINOK(SzReadNumber(sd, folder->UnpackSizes + j)); - } - } - - for (;;) - { - UInt64 type; - RINOK(SzReadID(sd, &type)); - if (type == k7zIdEnd) - return SZ_OK; - if (type == k7zIdCRC) - { - SRes res; - Byte *crcsDefined = 0; - UInt32 *crcs = 0; - res = SzReadHashDigests(sd, *numFolders, &crcsDefined, &crcs, allocTemp); - if (res == SZ_OK) - { - for (i = 0; i < *numFolders; i++) - { - CSzFolder *folder = (*folders) + i; - folder->UnpackCRCDefined = crcsDefined[i]; - folder->UnpackCRC = crcs[i]; - } - } - IAlloc_Free(allocTemp, crcs); - IAlloc_Free(allocTemp, crcsDefined); - RINOK(res); - continue; - } - RINOK(SzSkeepData(sd)); - } -} - -static SRes SzReadSubStreamsInfo( - CSzData *sd, - UInt32 numFolders, - CSzFolder *folders, - UInt32 *numUnpackStreams, - UInt64 **unpackSizes, - Byte **digestsDefined, - UInt32 **digests, - ISzAlloc *allocTemp) -{ - UInt64 type = 0; - UInt32 i; - UInt32 si = 0; - UInt32 numDigests = 0; - - for (i = 0; i < numFolders; i++) - folders[i].NumUnpackStreams = 1; - *numUnpackStreams = numFolders; - - for (;;) - { - RINOK(SzReadID(sd, &type)); - if (type == k7zIdNumUnpackStream) - { - *numUnpackStreams = 0; - for (i = 0; i < numFolders; i++) - { - UInt32 numStreams; - RINOK(SzReadNumber32(sd, &numStreams)); - folders[i].NumUnpackStreams = numStreams; - *numUnpackStreams += numStreams; - } - continue; - } - if (type == k7zIdCRC || type == k7zIdSize) - break; - if (type == k7zIdEnd) - break; - RINOK(SzSkeepData(sd)); - } - - if (*numUnpackStreams == 0) - { - *unpackSizes = 0; - *digestsDefined = 0; - *digests = 0; - } - else - { - *unpackSizes = (UInt64 *)IAlloc_Alloc(allocTemp, (size_t)*numUnpackStreams * sizeof(UInt64)); - RINOM(*unpackSizes); - *digestsDefined = (Byte *)IAlloc_Alloc(allocTemp, (size_t)*numUnpackStreams * sizeof(Byte)); - RINOM(*digestsDefined); - *digests = (UInt32 *)IAlloc_Alloc(allocTemp, (size_t)*numUnpackStreams * sizeof(UInt32)); - RINOM(*digests); - } - - for (i = 0; i < numFolders; i++) - { - /* - v3.13 incorrectly worked with empty folders - v4.07: we check that folder is empty - */ - UInt64 sum = 0; - UInt32 j; - UInt32 numSubstreams = folders[i].NumUnpackStreams; - if (numSubstreams == 0) - continue; - if (type == k7zIdSize) - for (j = 1; j < numSubstreams; j++) - { - UInt64 size; - RINOK(SzReadNumber(sd, &size)); - (*unpackSizes)[si++] = size; - sum += size; - } - (*unpackSizes)[si++] = SzFolder_GetUnpackSize(folders + i) - sum; - } - if (type == k7zIdSize) - { - RINOK(SzReadID(sd, &type)); - } - - for (i = 0; i < *numUnpackStreams; i++) - { - (*digestsDefined)[i] = 0; - (*digests)[i] = 0; - } - - - for (i = 0; i < numFolders; i++) - { - UInt32 numSubstreams = folders[i].NumUnpackStreams; - if (numSubstreams != 1 || !folders[i].UnpackCRCDefined) - numDigests += numSubstreams; - } - - - si = 0; - for (;;) - { - if (type == k7zIdCRC) - { - int digestIndex = 0; - Byte *digestsDefined2 = 0; - UInt32 *digests2 = 0; - SRes res = SzReadHashDigests(sd, numDigests, &digestsDefined2, &digests2, allocTemp); - if (res == SZ_OK) - { - for (i = 0; i < numFolders; i++) - { - CSzFolder *folder = folders + i; - UInt32 numSubstreams = folder->NumUnpackStreams; - if (numSubstreams == 1 && folder->UnpackCRCDefined) - { - (*digestsDefined)[si] = 1; - (*digests)[si] = folder->UnpackCRC; - si++; - } - else - { - UInt32 j; - for (j = 0; j < numSubstreams; j++, digestIndex++) - { - (*digestsDefined)[si] = digestsDefined2[digestIndex]; - (*digests)[si] = digests2[digestIndex]; - si++; - } - } - } - } - IAlloc_Free(allocTemp, digestsDefined2); - IAlloc_Free(allocTemp, digests2); - RINOK(res); - } - else if (type == k7zIdEnd) - return SZ_OK; - else - { - RINOK(SzSkeepData(sd)); - } - RINOK(SzReadID(sd, &type)); - } -} - - -static SRes SzReadStreamsInfo( - CSzData *sd, - UInt64 *dataOffset, - CSzAr *p, - UInt32 *numUnpackStreams, - UInt64 **unpackSizes, /* allocTemp */ - Byte **digestsDefined, /* allocTemp */ - UInt32 **digests, /* allocTemp */ - ISzAlloc *alloc, - ISzAlloc *allocTemp) -{ - for (;;) - { - UInt64 type; - RINOK(SzReadID(sd, &type)); - if ((UInt64)(int)type != type) - return SZ_ERROR_UNSUPPORTED; - switch((int)type) - { - case k7zIdEnd: - return SZ_OK; - case k7zIdPackInfo: - { - RINOK(SzReadPackInfo(sd, dataOffset, &p->NumPackStreams, - &p->PackSizes, &p->PackCRCsDefined, &p->PackCRCs, alloc)); - break; - } - case k7zIdUnpackInfo: - { - RINOK(SzReadUnpackInfo(sd, &p->NumFolders, &p->Folders, alloc, allocTemp)); - break; - } - case k7zIdSubStreamsInfo: - { - RINOK(SzReadSubStreamsInfo(sd, p->NumFolders, p->Folders, - numUnpackStreams, unpackSizes, digestsDefined, digests, allocTemp)); - break; - } - default: - return SZ_ERROR_UNSUPPORTED; - } - } -} - -size_t SzArEx_GetFileNameUtf16(const CSzArEx *p, size_t fileIndex, UInt16 *dest) -{ - size_t len = p->FileNameOffsets[fileIndex + 1] - p->FileNameOffsets[fileIndex]; - if (dest != 0) - { - size_t i; - const Byte *src = p->FileNames.data + (p->FileNameOffsets[fileIndex] * 2); - for (i = 0; i < len; i++) - dest[i] = GetUi16(src + i * 2); - } - return len; -} - -static SRes SzReadFileNames(const Byte *p, size_t size, UInt32 numFiles, size_t *sizes) -{ - UInt32 i; - size_t pos = 0; - for (i = 0; i < numFiles; i++) - { - sizes[i] = pos; - for (;;) - { - if (pos >= size) - return SZ_ERROR_ARCHIVE; - if (p[pos * 2] == 0 && p[pos * 2 + 1] == 0) - break; - pos++; - } - pos++; - } - sizes[i] = pos; - return (pos == size) ? SZ_OK : SZ_ERROR_ARCHIVE; -} - -static SRes SzReadHeader2( - CSzArEx *p, /* allocMain */ - CSzData *sd, - UInt64 **unpackSizes, /* allocTemp */ - Byte **digestsDefined, /* allocTemp */ - UInt32 **digests, /* allocTemp */ - Byte **emptyStreamVector, /* allocTemp */ - Byte **emptyFileVector, /* allocTemp */ - Byte **lwtVector, /* allocTemp */ - ISzAlloc *allocMain, - ISzAlloc *allocTemp) -{ - UInt64 type; - UInt32 numUnpackStreams = 0; - UInt32 numFiles = 0; - CSzFileItem *files = 0; - UInt32 numEmptyStreams = 0; - UInt32 i; - - RINOK(SzReadID(sd, &type)); - - if (type == k7zIdArchiveProperties) - { - RINOK(SzReadArchiveProperties(sd)); - RINOK(SzReadID(sd, &type)); - } - - - if (type == k7zIdMainStreamsInfo) - { - RINOK(SzReadStreamsInfo(sd, - &p->dataPos, - &p->db, - &numUnpackStreams, - unpackSizes, - digestsDefined, - digests, allocMain, allocTemp)); - p->dataPos += p->startPosAfterHeader; - RINOK(SzReadID(sd, &type)); - } - - if (type == k7zIdEnd) - return SZ_OK; - if (type != k7zIdFilesInfo) - return SZ_ERROR_ARCHIVE; - - RINOK(SzReadNumber32(sd, &numFiles)); - p->db.NumFiles = numFiles; - - MY_ALLOC(CSzFileItem, files, (size_t)numFiles, allocMain); - - p->db.Files = files; - for (i = 0; i < numFiles; i++) - SzFile_Init(files + i); - - for (;;) - { - UInt64 type; - UInt64 size; - RINOK(SzReadID(sd, &type)); - if (type == k7zIdEnd) - break; - RINOK(SzReadNumber(sd, &size)); - if (size > sd->Size) - return SZ_ERROR_ARCHIVE; - if ((UInt64)(int)type != type) - { - RINOK(SzSkeepDataSize(sd, size)); - } - else - switch((int)type) - { - case k7zIdName: - { - size_t namesSize; - RINOK(SzReadSwitch(sd)); - namesSize = (size_t)size - 1; - if ((namesSize & 1) != 0) - return SZ_ERROR_ARCHIVE; - if (!Buf_Create(&p->FileNames, namesSize, allocMain)) - return SZ_ERROR_MEM; - MY_ALLOC(size_t, p->FileNameOffsets, numFiles + 1, allocMain); - memcpy(p->FileNames.data, sd->Data, namesSize); - RINOK(SzReadFileNames(sd->Data, namesSize >> 1, numFiles, p->FileNameOffsets)) - RINOK(SzSkeepDataSize(sd, namesSize)); - break; - } - case k7zIdEmptyStream: - { - RINOK(SzReadBoolVector(sd, numFiles, emptyStreamVector, allocTemp)); - numEmptyStreams = 0; - for (i = 0; i < numFiles; i++) - if ((*emptyStreamVector)[i]) - numEmptyStreams++; - break; - } - case k7zIdEmptyFile: - { - RINOK(SzReadBoolVector(sd, numEmptyStreams, emptyFileVector, allocTemp)); - break; - } - case k7zIdWinAttributes: - { - RINOK(SzReadBoolVector2(sd, numFiles, lwtVector, allocTemp)); - RINOK(SzReadSwitch(sd)); - for (i = 0; i < numFiles; i++) - { - CSzFileItem *f = &files[i]; - Byte defined = (*lwtVector)[i]; - f->AttribDefined = defined; - f->Attrib = 0; - if (defined) - { - RINOK(SzReadUInt32(sd, &f->Attrib)); - } - } - IAlloc_Free(allocTemp, *lwtVector); - *lwtVector = NULL; - break; - } - case k7zIdMTime: - { - RINOK(SzReadBoolVector2(sd, numFiles, lwtVector, allocTemp)); - RINOK(SzReadSwitch(sd)); - for (i = 0; i < numFiles; i++) - { - CSzFileItem *f = &files[i]; - Byte defined = (*lwtVector)[i]; - f->MTimeDefined = defined; - f->MTime.Low = f->MTime.High = 0; - if (defined) - { - RINOK(SzReadUInt32(sd, &f->MTime.Low)); - RINOK(SzReadUInt32(sd, &f->MTime.High)); - } - } - IAlloc_Free(allocTemp, *lwtVector); - *lwtVector = NULL; - break; - } - default: - { - RINOK(SzSkeepDataSize(sd, size)); - } - } - } - - { - UInt32 emptyFileIndex = 0; - UInt32 sizeIndex = 0; - for (i = 0; i < numFiles; i++) - { - CSzFileItem *file = files + i; - file->IsAnti = 0; - if (*emptyStreamVector == 0) - file->HasStream = 1; - else - file->HasStream = (Byte)((*emptyStreamVector)[i] ? 0 : 1); - if (file->HasStream) - { - file->IsDir = 0; - file->Size = (*unpackSizes)[sizeIndex]; - file->Crc = (*digests)[sizeIndex]; - file->CrcDefined = (Byte)(*digestsDefined)[sizeIndex]; - sizeIndex++; - } - else - { - if (*emptyFileVector == 0) - file->IsDir = 1; - else - file->IsDir = (Byte)((*emptyFileVector)[emptyFileIndex] ? 0 : 1); - emptyFileIndex++; - file->Size = 0; - file->Crc = 0; - file->CrcDefined = 0; - } - } - } - return SzArEx_Fill(p, allocMain); -} - -static SRes SzReadHeader( - CSzArEx *p, - CSzData *sd, - ISzAlloc *allocMain, - ISzAlloc *allocTemp) -{ - UInt64 *unpackSizes = 0; - Byte *digestsDefined = 0; - UInt32 *digests = 0; - Byte *emptyStreamVector = 0; - Byte *emptyFileVector = 0; - Byte *lwtVector = 0; - SRes res = SzReadHeader2(p, sd, - &unpackSizes, &digestsDefined, &digests, - &emptyStreamVector, &emptyFileVector, &lwtVector, - allocMain, allocTemp); - IAlloc_Free(allocTemp, unpackSizes); - IAlloc_Free(allocTemp, digestsDefined); - IAlloc_Free(allocTemp, digests); - IAlloc_Free(allocTemp, emptyStreamVector); - IAlloc_Free(allocTemp, emptyFileVector); - IAlloc_Free(allocTemp, lwtVector); - return res; -} - -static SRes SzReadAndDecodePackedStreams2( - ILookInStream *inStream, - CSzData *sd, - CBuf *outBuffer, - UInt64 baseOffset, - CSzAr *p, - UInt64 **unpackSizes, - Byte **digestsDefined, - UInt32 **digests, - ISzAlloc *allocTemp) -{ - - UInt32 numUnpackStreams = 0; - UInt64 dataStartPos; - CSzFolder *folder; - UInt64 unpackSize; - SRes res; - - RINOK(SzReadStreamsInfo(sd, &dataStartPos, p, - &numUnpackStreams, unpackSizes, digestsDefined, digests, - allocTemp, allocTemp)); - - dataStartPos += baseOffset; - if (p->NumFolders != 1) - return SZ_ERROR_ARCHIVE; - - folder = p->Folders; - unpackSize = SzFolder_GetUnpackSize(folder); - - RINOK(LookInStream_SeekTo(inStream, dataStartPos)); - - if (!Buf_Create(outBuffer, (size_t)unpackSize, allocTemp)) - return SZ_ERROR_MEM; - - res = SzFolder_Decode(folder, p->PackSizes, - inStream, dataStartPos, - outBuffer->data, (size_t)unpackSize, allocTemp); - RINOK(res); - if (folder->UnpackCRCDefined) - if (CrcCalc(outBuffer->data, (size_t)unpackSize) != folder->UnpackCRC) - return SZ_ERROR_CRC; - return SZ_OK; -} - -static SRes SzReadAndDecodePackedStreams( - ILookInStream *inStream, - CSzData *sd, - CBuf *outBuffer, - UInt64 baseOffset, - ISzAlloc *allocTemp) -{ - CSzAr p; - UInt64 *unpackSizes = 0; - Byte *digestsDefined = 0; - UInt32 *digests = 0; - SRes res; - SzAr_Init(&p); - res = SzReadAndDecodePackedStreams2(inStream, sd, outBuffer, baseOffset, - &p, &unpackSizes, &digestsDefined, &digests, - allocTemp); - SzAr_Free(&p, allocTemp); - IAlloc_Free(allocTemp, unpackSizes); - IAlloc_Free(allocTemp, digestsDefined); - IAlloc_Free(allocTemp, digests); - return res; -} - -static SRes SzArEx_Open2( - CSzArEx *p, - ILookInStream *inStream, - ISzAlloc *allocMain, - ISzAlloc *allocTemp) -{ - Byte header[k7zStartHeaderSize]; - Int64 startArcPos; - UInt64 nextHeaderOffset, nextHeaderSize; - size_t nextHeaderSizeT; - UInt32 nextHeaderCRC; - CBuf buffer; - SRes res; - - startArcPos = 0; - RINOK(inStream->Seek(inStream, &startArcPos, SZ_SEEK_CUR)); - - RINOK(LookInStream_Read2(inStream, header, k7zStartHeaderSize, SZ_ERROR_NO_ARCHIVE)); - - if (!TestSignatureCandidate(header)) - return SZ_ERROR_NO_ARCHIVE; - if (header[6] != k7zMajorVersion) - return SZ_ERROR_UNSUPPORTED; - - nextHeaderOffset = GetUi64(header + 12); - nextHeaderSize = GetUi64(header + 20); - nextHeaderCRC = GetUi32(header + 28); - - p->startPosAfterHeader = startArcPos + k7zStartHeaderSize; - - if (CrcCalc(header + 12, 20) != GetUi32(header + 8)) - return SZ_ERROR_CRC; - - nextHeaderSizeT = (size_t)nextHeaderSize; - if (nextHeaderSizeT != nextHeaderSize) - return SZ_ERROR_MEM; - if (nextHeaderSizeT == 0) - return SZ_OK; - if (nextHeaderOffset > nextHeaderOffset + nextHeaderSize || - nextHeaderOffset > nextHeaderOffset + nextHeaderSize + k7zStartHeaderSize) - return SZ_ERROR_NO_ARCHIVE; - - { - Int64 pos = 0; - RINOK(inStream->Seek(inStream, &pos, SZ_SEEK_END)); - if ((UInt64)pos < startArcPos + nextHeaderOffset || - (UInt64)pos < startArcPos + k7zStartHeaderSize + nextHeaderOffset || - (UInt64)pos < startArcPos + k7zStartHeaderSize + nextHeaderOffset + nextHeaderSize) - return SZ_ERROR_INPUT_EOF; - } - - RINOK(LookInStream_SeekTo(inStream, startArcPos + k7zStartHeaderSize + nextHeaderOffset)); - - if (!Buf_Create(&buffer, nextHeaderSizeT, allocTemp)) - return SZ_ERROR_MEM; - - res = LookInStream_Read(inStream, buffer.data, nextHeaderSizeT); - if (res == SZ_OK) - { - res = SZ_ERROR_ARCHIVE; - if (CrcCalc(buffer.data, nextHeaderSizeT) == nextHeaderCRC) - { - CSzData sd; - UInt64 type; - sd.Data = buffer.data; - sd.Size = buffer.size; - res = SzReadID(&sd, &type); - if (res == SZ_OK) - { - if (type == k7zIdEncodedHeader) - { - CBuf outBuffer; - Buf_Init(&outBuffer); - res = SzReadAndDecodePackedStreams(inStream, &sd, &outBuffer, p->startPosAfterHeader, allocTemp); - if (res != SZ_OK) - Buf_Free(&outBuffer, allocTemp); - else - { - Buf_Free(&buffer, allocTemp); - buffer.data = outBuffer.data; - buffer.size = outBuffer.size; - sd.Data = buffer.data; - sd.Size = buffer.size; - res = SzReadID(&sd, &type); - } - } - } - if (res == SZ_OK) - { - if (type == k7zIdHeader) - res = SzReadHeader(p, &sd, allocMain, allocTemp); - else - res = SZ_ERROR_UNSUPPORTED; - } - } - } - Buf_Free(&buffer, allocTemp); - return res; -} - -SRes SzArEx_Open(CSzArEx *p, ILookInStream *inStream, ISzAlloc *allocMain, ISzAlloc *allocTemp) -{ - SRes res = SzArEx_Open2(p, inStream, allocMain, allocTemp); - if (res != SZ_OK) - SzArEx_Free(p, allocMain); - return res; -} - -SRes SzArEx_Extract( - const CSzArEx *p, - ILookInStream *inStream, - UInt32 fileIndex, - UInt32 *blockIndex, - Byte **outBuffer, - size_t *outBufferSize, - size_t *offset, - size_t *outSizeProcessed, - ISzAlloc *allocMain, - ISzAlloc *allocTemp) -{ - UInt32 folderIndex = p->FileIndexToFolderIndexMap[fileIndex]; - SRes res = SZ_OK; - *offset = 0; - *outSizeProcessed = 0; - if (folderIndex == (UInt32)-1) - { - IAlloc_Free(allocMain, *outBuffer); - *blockIndex = folderIndex; - *outBuffer = 0; - *outBufferSize = 0; - return SZ_OK; - } - - if (*outBuffer == 0 || *blockIndex != folderIndex) - { - CSzFolder *folder = p->db.Folders + folderIndex; - UInt64 unpackSizeSpec = SzFolder_GetUnpackSize(folder); - size_t unpackSize = (size_t)unpackSizeSpec; - UInt64 startOffset = SzArEx_GetFolderStreamPos(p, folderIndex, 0); - - if (unpackSize != unpackSizeSpec) - return SZ_ERROR_MEM; - *blockIndex = folderIndex; - IAlloc_Free(allocMain, *outBuffer); - *outBuffer = 0; - - RINOK(LookInStream_SeekTo(inStream, startOffset)); - - if (res == SZ_OK) - { - *outBufferSize = unpackSize; - if (unpackSize != 0) - { - *outBuffer = (Byte *)IAlloc_Alloc(allocMain, unpackSize); - if (*outBuffer == 0) - res = SZ_ERROR_MEM; - } - if (res == SZ_OK) - { - res = SzFolder_Decode(folder, - p->db.PackSizes + p->FolderStartPackStreamIndex[folderIndex], - inStream, startOffset, - *outBuffer, unpackSize, allocTemp); - if (res == SZ_OK) - { - if (folder->UnpackCRCDefined) - { - if (CrcCalc(*outBuffer, unpackSize) != folder->UnpackCRC) - res = SZ_ERROR_CRC; - } - } - } - } - } - if (res == SZ_OK) - { - UInt32 i; - CSzFileItem *fileItem = p->db.Files + fileIndex; - *offset = 0; - for (i = p->FolderStartFileIndex[folderIndex]; i < fileIndex; i++) - *offset += (UInt32)p->db.Files[i].Size; - *outSizeProcessed = (size_t)fileItem->Size; - if (*offset + *outSizeProcessed > *outBufferSize) - return SZ_ERROR_FAIL; - if (fileItem->CrcDefined && CrcCalc(*outBuffer + *offset, *outSizeProcessed) != fileItem->Crc) - res = SZ_ERROR_CRC; - } - return res; -} +/* 7zIn.c -- 7z Input functions +2010-10-29 : Igor Pavlov : Public domain */ + +#include + +#include "7z.h" +#include "7zCrc.h" +#include "CpuArch.h" + +Byte k7zSignature[k7zSignatureSize] = {'7', 'z', 0xBC, 0xAF, 0x27, 0x1C}; + +#define RINOM(x) { if ((x) == 0) return SZ_ERROR_MEM; } + +#define NUM_FOLDER_CODERS_MAX 32 +#define NUM_CODER_STREAMS_MAX 32 + +void SzCoderInfo_Init(CSzCoderInfo *p) +{ + Buf_Init(&p->Props); +} + +void SzCoderInfo_Free(CSzCoderInfo *p, ISzAlloc *alloc) +{ + Buf_Free(&p->Props, alloc); + SzCoderInfo_Init(p); +} + +void SzFolder_Init(CSzFolder *p) +{ + p->Coders = 0; + p->BindPairs = 0; + p->PackStreams = 0; + p->UnpackSizes = 0; + p->NumCoders = 0; + p->NumBindPairs = 0; + p->NumPackStreams = 0; + p->UnpackCRCDefined = 0; + p->UnpackCRC = 0; + p->NumUnpackStreams = 0; +} + +void SzFolder_Free(CSzFolder *p, ISzAlloc *alloc) +{ + UInt32 i; + if (p->Coders) + for (i = 0; i < p->NumCoders; i++) + SzCoderInfo_Free(&p->Coders[i], alloc); + IAlloc_Free(alloc, p->Coders); + IAlloc_Free(alloc, p->BindPairs); + IAlloc_Free(alloc, p->PackStreams); + IAlloc_Free(alloc, p->UnpackSizes); + SzFolder_Init(p); +} + +UInt32 SzFolder_GetNumOutStreams(CSzFolder *p) +{ + UInt32 result = 0; + UInt32 i; + for (i = 0; i < p->NumCoders; i++) + result += p->Coders[i].NumOutStreams; + return result; +} + +int SzFolder_FindBindPairForInStream(CSzFolder *p, UInt32 inStreamIndex) +{ + UInt32 i; + for (i = 0; i < p->NumBindPairs; i++) + if (p->BindPairs[i].InIndex == inStreamIndex) + return i; + return -1; +} + + +int SzFolder_FindBindPairForOutStream(CSzFolder *p, UInt32 outStreamIndex) +{ + UInt32 i; + for (i = 0; i < p->NumBindPairs; i++) + if (p->BindPairs[i].OutIndex == outStreamIndex) + return i; + return -1; +} + +UInt64 SzFolder_GetUnpackSize(CSzFolder *p) +{ + int i = (int)SzFolder_GetNumOutStreams(p); + if (i == 0) + return 0; + for (i--; i >= 0; i--) + if (SzFolder_FindBindPairForOutStream(p, i) < 0) + return p->UnpackSizes[i]; + /* throw 1; */ + return 0; +} + +void SzFile_Init(CSzFileItem *p) +{ + p->HasStream = 1; + p->IsDir = 0; + p->IsAnti = 0; + p->CrcDefined = 0; + p->MTimeDefined = 0; +} + +void SzAr_Init(CSzAr *p) +{ + p->PackSizes = 0; + p->PackCRCsDefined = 0; + p->PackCRCs = 0; + p->Folders = 0; + p->Files = 0; + p->NumPackStreams = 0; + p->NumFolders = 0; + p->NumFiles = 0; +} + +void SzAr_Free(CSzAr *p, ISzAlloc *alloc) +{ + UInt32 i; + if (p->Folders) + for (i = 0; i < p->NumFolders; i++) + SzFolder_Free(&p->Folders[i], alloc); + + IAlloc_Free(alloc, p->PackSizes); + IAlloc_Free(alloc, p->PackCRCsDefined); + IAlloc_Free(alloc, p->PackCRCs); + IAlloc_Free(alloc, p->Folders); + IAlloc_Free(alloc, p->Files); + SzAr_Init(p); +} + + +void SzArEx_Init(CSzArEx *p) +{ + SzAr_Init(&p->db); + p->FolderStartPackStreamIndex = 0; + p->PackStreamStartPositions = 0; + p->FolderStartFileIndex = 0; + p->FileIndexToFolderIndexMap = 0; + p->FileNameOffsets = 0; + Buf_Init(&p->FileNames); +} + +void SzArEx_Free(CSzArEx *p, ISzAlloc *alloc) +{ + IAlloc_Free(alloc, p->FolderStartPackStreamIndex); + IAlloc_Free(alloc, p->PackStreamStartPositions); + IAlloc_Free(alloc, p->FolderStartFileIndex); + IAlloc_Free(alloc, p->FileIndexToFolderIndexMap); + + IAlloc_Free(alloc, p->FileNameOffsets); + Buf_Free(&p->FileNames, alloc); + + SzAr_Free(&p->db, alloc); + SzArEx_Init(p); +} + +/* +UInt64 GetFolderPackStreamSize(int folderIndex, int streamIndex) const +{ + return PackSizes[FolderStartPackStreamIndex[folderIndex] + streamIndex]; +} + +UInt64 GetFilePackSize(int fileIndex) const +{ + int folderIndex = FileIndexToFolderIndexMap[fileIndex]; + if (folderIndex >= 0) + { + const CSzFolder &folderInfo = Folders[folderIndex]; + if (FolderStartFileIndex[folderIndex] == fileIndex) + return GetFolderFullPackSize(folderIndex); + } + return 0; +} +*/ + +#define MY_ALLOC(T, p, size, alloc) { if ((size) == 0) p = 0; else \ + if ((p = (T *)IAlloc_Alloc(alloc, (size) * sizeof(T))) == 0) return SZ_ERROR_MEM; } + +static SRes SzArEx_Fill(CSzArEx *p, ISzAlloc *alloc) +{ + UInt32 startPos = 0; + UInt64 startPosSize = 0; + UInt32 i; + UInt32 folderIndex = 0; + UInt32 indexInFolder = 0; + MY_ALLOC(UInt32, p->FolderStartPackStreamIndex, p->db.NumFolders, alloc); + for (i = 0; i < p->db.NumFolders; i++) + { + p->FolderStartPackStreamIndex[i] = startPos; + startPos += p->db.Folders[i].NumPackStreams; + } + + MY_ALLOC(UInt64, p->PackStreamStartPositions, p->db.NumPackStreams, alloc); + + for (i = 0; i < p->db.NumPackStreams; i++) + { + p->PackStreamStartPositions[i] = startPosSize; + startPosSize += p->db.PackSizes[i]; + } + + MY_ALLOC(UInt32, p->FolderStartFileIndex, p->db.NumFolders, alloc); + MY_ALLOC(UInt32, p->FileIndexToFolderIndexMap, p->db.NumFiles, alloc); + + for (i = 0; i < p->db.NumFiles; i++) + { + CSzFileItem *file = p->db.Files + i; + int emptyStream = !file->HasStream; + if (emptyStream && indexInFolder == 0) + { + p->FileIndexToFolderIndexMap[i] = (UInt32)-1; + continue; + } + if (indexInFolder == 0) + { + /* + v3.13 incorrectly worked with empty folders + v4.07: Loop for skipping empty folders + */ + for (;;) + { + if (folderIndex >= p->db.NumFolders) + return SZ_ERROR_ARCHIVE; + p->FolderStartFileIndex[folderIndex] = i; + if (p->db.Folders[folderIndex].NumUnpackStreams != 0) + break; + folderIndex++; + } + } + p->FileIndexToFolderIndexMap[i] = folderIndex; + if (emptyStream) + continue; + indexInFolder++; + if (indexInFolder >= p->db.Folders[folderIndex].NumUnpackStreams) + { + folderIndex++; + indexInFolder = 0; + } + } + return SZ_OK; +} + + +UInt64 SzArEx_GetFolderStreamPos(const CSzArEx *p, UInt32 folderIndex, UInt32 indexInFolder) +{ + return p->dataPos + + p->PackStreamStartPositions[p->FolderStartPackStreamIndex[folderIndex] + indexInFolder]; +} + +int SzArEx_GetFolderFullPackSize(const CSzArEx *p, UInt32 folderIndex, UInt64 *resSize) +{ + UInt32 packStreamIndex = p->FolderStartPackStreamIndex[folderIndex]; + CSzFolder *folder = p->db.Folders + folderIndex; + UInt64 size = 0; + UInt32 i; + for (i = 0; i < folder->NumPackStreams; i++) + { + UInt64 t = size + p->db.PackSizes[packStreamIndex + i]; + if (t < size) /* check it */ + return SZ_ERROR_FAIL; + size = t; + } + *resSize = size; + return SZ_OK; +} + + +/* +SRes SzReadTime(const CObjectVector &dataVector, + CObjectVector &files, UInt64 type) +{ + CBoolVector boolVector; + RINOK(ReadBoolVector2(files.Size(), boolVector)) + + CStreamSwitch streamSwitch; + RINOK(streamSwitch.Set(this, &dataVector)); + + for (int i = 0; i < files.Size(); i++) + { + CSzFileItem &file = files[i]; + CArchiveFileTime fileTime; + bool defined = boolVector[i]; + if (defined) + { + UInt32 low, high; + RINOK(SzReadUInt32(low)); + RINOK(SzReadUInt32(high)); + fileTime.dwLowDateTime = low; + fileTime.dwHighDateTime = high; + } + switch(type) + { + case k7zIdCTime: file.IsCTimeDefined = defined; if (defined) file.CTime = fileTime; break; + case k7zIdATime: file.IsATimeDefined = defined; if (defined) file.ATime = fileTime; break; + case k7zIdMTime: file.IsMTimeDefined = defined; if (defined) file.MTime = fileTime; break; + } + } + return SZ_OK; +} +*/ + +static int TestSignatureCandidate(Byte *testBytes) +{ + size_t i; + for (i = 0; i < k7zSignatureSize; i++) + if (testBytes[i] != k7zSignature[i]) + return 0; + return 1; +} + +typedef struct _CSzState +{ + Byte *Data; + size_t Size; +}CSzData; + +static SRes SzReadByte(CSzData *sd, Byte *b) +{ + if (sd->Size == 0) + return SZ_ERROR_ARCHIVE; + sd->Size--; + *b = *sd->Data++; + return SZ_OK; +} + +static SRes SzReadBytes(CSzData *sd, Byte *data, size_t size) +{ + size_t i; + for (i = 0; i < size; i++) + { + RINOK(SzReadByte(sd, data + i)); + } + return SZ_OK; +} + +static SRes SzReadUInt32(CSzData *sd, UInt32 *value) +{ + int i; + *value = 0; + for (i = 0; i < 4; i++) + { + Byte b; + RINOK(SzReadByte(sd, &b)); + *value |= ((UInt32)(b) << (8 * i)); + } + return SZ_OK; +} + +static SRes SzReadNumber(CSzData *sd, UInt64 *value) +{ + Byte firstByte; + Byte mask = 0x80; + int i; + RINOK(SzReadByte(sd, &firstByte)); + *value = 0; + for (i = 0; i < 8; i++) + { + Byte b; + if ((firstByte & mask) == 0) + { + UInt64 highPart = firstByte & (mask - 1); + *value += (highPart << (8 * i)); + return SZ_OK; + } + RINOK(SzReadByte(sd, &b)); + *value |= ((UInt64)b << (8 * i)); + mask >>= 1; + } + return SZ_OK; +} + +static SRes SzReadNumber32(CSzData *sd, UInt32 *value) +{ + UInt64 value64; + RINOK(SzReadNumber(sd, &value64)); + if (value64 >= 0x80000000) + return SZ_ERROR_UNSUPPORTED; + if (value64 >= ((UInt64)(1) << ((sizeof(size_t) - 1) * 8 + 2))) + return SZ_ERROR_UNSUPPORTED; + *value = (UInt32)value64; + return SZ_OK; +} + +static SRes SzReadID(CSzData *sd, UInt64 *value) +{ + return SzReadNumber(sd, value); +} + +static SRes SzSkeepDataSize(CSzData *sd, UInt64 size) +{ + if (size > sd->Size) + return SZ_ERROR_ARCHIVE; + sd->Size -= (size_t)size; + sd->Data += (size_t)size; + return SZ_OK; +} + +static SRes SzSkeepData(CSzData *sd) +{ + UInt64 size; + RINOK(SzReadNumber(sd, &size)); + return SzSkeepDataSize(sd, size); +} + +static SRes SzReadArchiveProperties(CSzData *sd) +{ + for (;;) + { + UInt64 type; + RINOK(SzReadID(sd, &type)); + if (type == k7zIdEnd) + break; + SzSkeepData(sd); + } + return SZ_OK; +} + +static SRes SzWaitAttribute(CSzData *sd, UInt64 attribute) +{ + for (;;) + { + UInt64 type; + RINOK(SzReadID(sd, &type)); + if (type == attribute) + return SZ_OK; + if (type == k7zIdEnd) + return SZ_ERROR_ARCHIVE; + RINOK(SzSkeepData(sd)); + } +} + +static SRes SzReadBoolVector(CSzData *sd, size_t numItems, Byte **v, ISzAlloc *alloc) +{ + Byte b = 0; + Byte mask = 0; + size_t i; + MY_ALLOC(Byte, *v, numItems, alloc); + for (i = 0; i < numItems; i++) + { + if (mask == 0) + { + RINOK(SzReadByte(sd, &b)); + mask = 0x80; + } + (*v)[i] = (Byte)(((b & mask) != 0) ? 1 : 0); + mask >>= 1; + } + return SZ_OK; +} + +static SRes SzReadBoolVector2(CSzData *sd, size_t numItems, Byte **v, ISzAlloc *alloc) +{ + Byte allAreDefined; + size_t i; + RINOK(SzReadByte(sd, &allAreDefined)); + if (allAreDefined == 0) + return SzReadBoolVector(sd, numItems, v, alloc); + MY_ALLOC(Byte, *v, numItems, alloc); + for (i = 0; i < numItems; i++) + (*v)[i] = 1; + return SZ_OK; +} + +static SRes SzReadHashDigests( + CSzData *sd, + size_t numItems, + Byte **digestsDefined, + UInt32 **digests, + ISzAlloc *alloc) +{ + size_t i; + RINOK(SzReadBoolVector2(sd, numItems, digestsDefined, alloc)); + MY_ALLOC(UInt32, *digests, numItems, alloc); + for (i = 0; i < numItems; i++) + if ((*digestsDefined)[i]) + { + RINOK(SzReadUInt32(sd, (*digests) + i)); + } + return SZ_OK; +} + +static SRes SzReadPackInfo( + CSzData *sd, + UInt64 *dataOffset, + UInt32 *numPackStreams, + UInt64 **packSizes, + Byte **packCRCsDefined, + UInt32 **packCRCs, + ISzAlloc *alloc) +{ + UInt32 i; + RINOK(SzReadNumber(sd, dataOffset)); + RINOK(SzReadNumber32(sd, numPackStreams)); + + RINOK(SzWaitAttribute(sd, k7zIdSize)); + + MY_ALLOC(UInt64, *packSizes, (size_t)*numPackStreams, alloc); + + for (i = 0; i < *numPackStreams; i++) + { + RINOK(SzReadNumber(sd, (*packSizes) + i)); + } + + for (;;) + { + UInt64 type; + RINOK(SzReadID(sd, &type)); + if (type == k7zIdEnd) + break; + if (type == k7zIdCRC) + { + RINOK(SzReadHashDigests(sd, (size_t)*numPackStreams, packCRCsDefined, packCRCs, alloc)); + continue; + } + RINOK(SzSkeepData(sd)); + } + if (*packCRCsDefined == 0) + { + MY_ALLOC(Byte, *packCRCsDefined, (size_t)*numPackStreams, alloc); + MY_ALLOC(UInt32, *packCRCs, (size_t)*numPackStreams, alloc); + for (i = 0; i < *numPackStreams; i++) + { + (*packCRCsDefined)[i] = 0; + (*packCRCs)[i] = 0; + } + } + return SZ_OK; +} + +static SRes SzReadSwitch(CSzData *sd) +{ + Byte external; + RINOK(SzReadByte(sd, &external)); + return (external == 0) ? SZ_OK: SZ_ERROR_UNSUPPORTED; +} + +static SRes SzGetNextFolderItem(CSzData *sd, CSzFolder *folder, ISzAlloc *alloc) +{ + UInt32 numCoders, numBindPairs, numPackStreams, i; + UInt32 numInStreams = 0, numOutStreams = 0; + + RINOK(SzReadNumber32(sd, &numCoders)); + if (numCoders > NUM_FOLDER_CODERS_MAX) + return SZ_ERROR_UNSUPPORTED; + folder->NumCoders = numCoders; + + MY_ALLOC(CSzCoderInfo, folder->Coders, (size_t)numCoders, alloc); + + for (i = 0; i < numCoders; i++) + SzCoderInfo_Init(folder->Coders + i); + + for (i = 0; i < numCoders; i++) + { + Byte mainByte; + CSzCoderInfo *coder = folder->Coders + i; + { + unsigned idSize, j; + Byte longID[15]; + RINOK(SzReadByte(sd, &mainByte)); + idSize = (unsigned)(mainByte & 0xF); + RINOK(SzReadBytes(sd, longID, idSize)); + if (idSize > sizeof(coder->MethodID)) + return SZ_ERROR_UNSUPPORTED; + coder->MethodID = 0; + for (j = 0; j < idSize; j++) + coder->MethodID |= (UInt64)longID[idSize - 1 - j] << (8 * j); + + if ((mainByte & 0x10) != 0) + { + RINOK(SzReadNumber32(sd, &coder->NumInStreams)); + RINOK(SzReadNumber32(sd, &coder->NumOutStreams)); + if (coder->NumInStreams > NUM_CODER_STREAMS_MAX || + coder->NumOutStreams > NUM_CODER_STREAMS_MAX) + return SZ_ERROR_UNSUPPORTED; + } + else + { + coder->NumInStreams = 1; + coder->NumOutStreams = 1; + } + if ((mainByte & 0x20) != 0) + { + UInt64 propertiesSize = 0; + RINOK(SzReadNumber(sd, &propertiesSize)); + if (!Buf_Create(&coder->Props, (size_t)propertiesSize, alloc)) + return SZ_ERROR_MEM; + RINOK(SzReadBytes(sd, coder->Props.data, (size_t)propertiesSize)); + } + } + while ((mainByte & 0x80) != 0) + { + RINOK(SzReadByte(sd, &mainByte)); + RINOK(SzSkeepDataSize(sd, (mainByte & 0xF))); + if ((mainByte & 0x10) != 0) + { + UInt32 n; + RINOK(SzReadNumber32(sd, &n)); + RINOK(SzReadNumber32(sd, &n)); + } + if ((mainByte & 0x20) != 0) + { + UInt64 propertiesSize = 0; + RINOK(SzReadNumber(sd, &propertiesSize)); + RINOK(SzSkeepDataSize(sd, propertiesSize)); + } + } + numInStreams += coder->NumInStreams; + numOutStreams += coder->NumOutStreams; + } + + if (numOutStreams == 0) + return SZ_ERROR_UNSUPPORTED; + + folder->NumBindPairs = numBindPairs = numOutStreams - 1; + MY_ALLOC(CSzBindPair, folder->BindPairs, (size_t)numBindPairs, alloc); + + for (i = 0; i < numBindPairs; i++) + { + CSzBindPair *bp = folder->BindPairs + i; + RINOK(SzReadNumber32(sd, &bp->InIndex)); + RINOK(SzReadNumber32(sd, &bp->OutIndex)); + } + + if (numInStreams < numBindPairs) + return SZ_ERROR_UNSUPPORTED; + + folder->NumPackStreams = numPackStreams = numInStreams - numBindPairs; + MY_ALLOC(UInt32, folder->PackStreams, (size_t)numPackStreams, alloc); + + if (numPackStreams == 1) + { + for (i = 0; i < numInStreams ; i++) + if (SzFolder_FindBindPairForInStream(folder, i) < 0) + break; + if (i == numInStreams) + return SZ_ERROR_UNSUPPORTED; + folder->PackStreams[0] = i; + } + else + for (i = 0; i < numPackStreams; i++) + { + RINOK(SzReadNumber32(sd, folder->PackStreams + i)); + } + return SZ_OK; +} + +static SRes SzReadUnpackInfo( + CSzData *sd, + UInt32 *numFolders, + CSzFolder **folders, /* for alloc */ + ISzAlloc *alloc, + ISzAlloc *allocTemp) +{ + UInt32 i; + RINOK(SzWaitAttribute(sd, k7zIdFolder)); + RINOK(SzReadNumber32(sd, numFolders)); + { + RINOK(SzReadSwitch(sd)); + + MY_ALLOC(CSzFolder, *folders, (size_t)*numFolders, alloc); + + for (i = 0; i < *numFolders; i++) + SzFolder_Init((*folders) + i); + + for (i = 0; i < *numFolders; i++) + { + RINOK(SzGetNextFolderItem(sd, (*folders) + i, alloc)); + } + } + + RINOK(SzWaitAttribute(sd, k7zIdCodersUnpackSize)); + + for (i = 0; i < *numFolders; i++) + { + UInt32 j; + CSzFolder *folder = (*folders) + i; + UInt32 numOutStreams = SzFolder_GetNumOutStreams(folder); + + MY_ALLOC(UInt64, folder->UnpackSizes, (size_t)numOutStreams, alloc); + + for (j = 0; j < numOutStreams; j++) + { + RINOK(SzReadNumber(sd, folder->UnpackSizes + j)); + } + } + + for (;;) + { + UInt64 type; + RINOK(SzReadID(sd, &type)); + if (type == k7zIdEnd) + return SZ_OK; + if (type == k7zIdCRC) + { + SRes res; + Byte *crcsDefined = 0; + UInt32 *crcs = 0; + res = SzReadHashDigests(sd, *numFolders, &crcsDefined, &crcs, allocTemp); + if (res == SZ_OK) + { + for (i = 0; i < *numFolders; i++) + { + CSzFolder *folder = (*folders) + i; + folder->UnpackCRCDefined = crcsDefined[i]; + folder->UnpackCRC = crcs[i]; + } + } + IAlloc_Free(allocTemp, crcs); + IAlloc_Free(allocTemp, crcsDefined); + RINOK(res); + continue; + } + RINOK(SzSkeepData(sd)); + } +} + +static SRes SzReadSubStreamsInfo( + CSzData *sd, + UInt32 numFolders, + CSzFolder *folders, + UInt32 *numUnpackStreams, + UInt64 **unpackSizes, + Byte **digestsDefined, + UInt32 **digests, + ISzAlloc *allocTemp) +{ + UInt64 type = 0; + UInt32 i; + UInt32 si = 0; + UInt32 numDigests = 0; + + for (i = 0; i < numFolders; i++) + folders[i].NumUnpackStreams = 1; + *numUnpackStreams = numFolders; + + for (;;) + { + RINOK(SzReadID(sd, &type)); + if (type == k7zIdNumUnpackStream) + { + *numUnpackStreams = 0; + for (i = 0; i < numFolders; i++) + { + UInt32 numStreams; + RINOK(SzReadNumber32(sd, &numStreams)); + folders[i].NumUnpackStreams = numStreams; + *numUnpackStreams += numStreams; + } + continue; + } + if (type == k7zIdCRC || type == k7zIdSize) + break; + if (type == k7zIdEnd) + break; + RINOK(SzSkeepData(sd)); + } + + if (*numUnpackStreams == 0) + { + *unpackSizes = 0; + *digestsDefined = 0; + *digests = 0; + } + else + { + *unpackSizes = (UInt64 *)IAlloc_Alloc(allocTemp, (size_t)*numUnpackStreams * sizeof(UInt64)); + RINOM(*unpackSizes); + *digestsDefined = (Byte *)IAlloc_Alloc(allocTemp, (size_t)*numUnpackStreams * sizeof(Byte)); + RINOM(*digestsDefined); + *digests = (UInt32 *)IAlloc_Alloc(allocTemp, (size_t)*numUnpackStreams * sizeof(UInt32)); + RINOM(*digests); + } + + for (i = 0; i < numFolders; i++) + { + /* + v3.13 incorrectly worked with empty folders + v4.07: we check that folder is empty + */ + UInt64 sum = 0; + UInt32 j; + UInt32 numSubstreams = folders[i].NumUnpackStreams; + if (numSubstreams == 0) + continue; + if (type == k7zIdSize) + for (j = 1; j < numSubstreams; j++) + { + UInt64 size; + RINOK(SzReadNumber(sd, &size)); + (*unpackSizes)[si++] = size; + sum += size; + } + (*unpackSizes)[si++] = SzFolder_GetUnpackSize(folders + i) - sum; + } + if (type == k7zIdSize) + { + RINOK(SzReadID(sd, &type)); + } + + for (i = 0; i < *numUnpackStreams; i++) + { + (*digestsDefined)[i] = 0; + (*digests)[i] = 0; + } + + + for (i = 0; i < numFolders; i++) + { + UInt32 numSubstreams = folders[i].NumUnpackStreams; + if (numSubstreams != 1 || !folders[i].UnpackCRCDefined) + numDigests += numSubstreams; + } + + + si = 0; + for (;;) + { + if (type == k7zIdCRC) + { + int digestIndex = 0; + Byte *digestsDefined2 = 0; + UInt32 *digests2 = 0; + SRes res = SzReadHashDigests(sd, numDigests, &digestsDefined2, &digests2, allocTemp); + if (res == SZ_OK) + { + for (i = 0; i < numFolders; i++) + { + CSzFolder *folder = folders + i; + UInt32 numSubstreams = folder->NumUnpackStreams; + if (numSubstreams == 1 && folder->UnpackCRCDefined) + { + (*digestsDefined)[si] = 1; + (*digests)[si] = folder->UnpackCRC; + si++; + } + else + { + UInt32 j; + for (j = 0; j < numSubstreams; j++, digestIndex++) + { + (*digestsDefined)[si] = digestsDefined2[digestIndex]; + (*digests)[si] = digests2[digestIndex]; + si++; + } + } + } + } + IAlloc_Free(allocTemp, digestsDefined2); + IAlloc_Free(allocTemp, digests2); + RINOK(res); + } + else if (type == k7zIdEnd) + return SZ_OK; + else + { + RINOK(SzSkeepData(sd)); + } + RINOK(SzReadID(sd, &type)); + } +} + + +static SRes SzReadStreamsInfo( + CSzData *sd, + UInt64 *dataOffset, + CSzAr *p, + UInt32 *numUnpackStreams, + UInt64 **unpackSizes, /* allocTemp */ + Byte **digestsDefined, /* allocTemp */ + UInt32 **digests, /* allocTemp */ + ISzAlloc *alloc, + ISzAlloc *allocTemp) +{ + for (;;) + { + UInt64 type; + RINOK(SzReadID(sd, &type)); + if ((UInt64)(int)type != type) + return SZ_ERROR_UNSUPPORTED; + switch((int)type) + { + case k7zIdEnd: + return SZ_OK; + case k7zIdPackInfo: + { + RINOK(SzReadPackInfo(sd, dataOffset, &p->NumPackStreams, + &p->PackSizes, &p->PackCRCsDefined, &p->PackCRCs, alloc)); + break; + } + case k7zIdUnpackInfo: + { + RINOK(SzReadUnpackInfo(sd, &p->NumFolders, &p->Folders, alloc, allocTemp)); + break; + } + case k7zIdSubStreamsInfo: + { + RINOK(SzReadSubStreamsInfo(sd, p->NumFolders, p->Folders, + numUnpackStreams, unpackSizes, digestsDefined, digests, allocTemp)); + break; + } + default: + return SZ_ERROR_UNSUPPORTED; + } + } +} + +size_t SzArEx_GetFileNameUtf16(const CSzArEx *p, size_t fileIndex, UInt16 *dest) +{ + size_t len = p->FileNameOffsets[fileIndex + 1] - p->FileNameOffsets[fileIndex]; + if (dest != 0) + { + size_t i; + const Byte *src = p->FileNames.data + (p->FileNameOffsets[fileIndex] * 2); + for (i = 0; i < len; i++) + dest[i] = GetUi16(src + i * 2); + } + return len; +} + +static SRes SzReadFileNames(const Byte *p, size_t size, UInt32 numFiles, size_t *sizes) +{ + UInt32 i; + size_t pos = 0; + for (i = 0; i < numFiles; i++) + { + sizes[i] = pos; + for (;;) + { + if (pos >= size) + return SZ_ERROR_ARCHIVE; + if (p[pos * 2] == 0 && p[pos * 2 + 1] == 0) + break; + pos++; + } + pos++; + } + sizes[i] = pos; + return (pos == size) ? SZ_OK : SZ_ERROR_ARCHIVE; +} + +static SRes SzReadHeader2( + CSzArEx *p, /* allocMain */ + CSzData *sd, + UInt64 **unpackSizes, /* allocTemp */ + Byte **digestsDefined, /* allocTemp */ + UInt32 **digests, /* allocTemp */ + Byte **emptyStreamVector, /* allocTemp */ + Byte **emptyFileVector, /* allocTemp */ + Byte **lwtVector, /* allocTemp */ + ISzAlloc *allocMain, + ISzAlloc *allocTemp) +{ + UInt64 type; + UInt32 numUnpackStreams = 0; + UInt32 numFiles = 0; + CSzFileItem *files = 0; + UInt32 numEmptyStreams = 0; + UInt32 i; + + RINOK(SzReadID(sd, &type)); + + if (type == k7zIdArchiveProperties) + { + RINOK(SzReadArchiveProperties(sd)); + RINOK(SzReadID(sd, &type)); + } + + + if (type == k7zIdMainStreamsInfo) + { + RINOK(SzReadStreamsInfo(sd, + &p->dataPos, + &p->db, + &numUnpackStreams, + unpackSizes, + digestsDefined, + digests, allocMain, allocTemp)); + p->dataPos += p->startPosAfterHeader; + RINOK(SzReadID(sd, &type)); + } + + if (type == k7zIdEnd) + return SZ_OK; + if (type != k7zIdFilesInfo) + return SZ_ERROR_ARCHIVE; + + RINOK(SzReadNumber32(sd, &numFiles)); + p->db.NumFiles = numFiles; + + MY_ALLOC(CSzFileItem, files, (size_t)numFiles, allocMain); + + p->db.Files = files; + for (i = 0; i < numFiles; i++) + SzFile_Init(files + i); + + for (;;) + { + UInt64 type; + UInt64 size; + RINOK(SzReadID(sd, &type)); + if (type == k7zIdEnd) + break; + RINOK(SzReadNumber(sd, &size)); + if (size > sd->Size) + return SZ_ERROR_ARCHIVE; + if ((UInt64)(int)type != type) + { + RINOK(SzSkeepDataSize(sd, size)); + } + else + switch((int)type) + { + case k7zIdName: + { + size_t namesSize; + RINOK(SzReadSwitch(sd)); + namesSize = (size_t)size - 1; + if ((namesSize & 1) != 0) + return SZ_ERROR_ARCHIVE; + if (!Buf_Create(&p->FileNames, namesSize, allocMain)) + return SZ_ERROR_MEM; + MY_ALLOC(size_t, p->FileNameOffsets, numFiles + 1, allocMain); + memcpy(p->FileNames.data, sd->Data, namesSize); + RINOK(SzReadFileNames(sd->Data, namesSize >> 1, numFiles, p->FileNameOffsets)) + RINOK(SzSkeepDataSize(sd, namesSize)); + break; + } + case k7zIdEmptyStream: + { + RINOK(SzReadBoolVector(sd, numFiles, emptyStreamVector, allocTemp)); + numEmptyStreams = 0; + for (i = 0; i < numFiles; i++) + if ((*emptyStreamVector)[i]) + numEmptyStreams++; + break; + } + case k7zIdEmptyFile: + { + RINOK(SzReadBoolVector(sd, numEmptyStreams, emptyFileVector, allocTemp)); + break; + } + case k7zIdWinAttributes: + { + RINOK(SzReadBoolVector2(sd, numFiles, lwtVector, allocTemp)); + RINOK(SzReadSwitch(sd)); + for (i = 0; i < numFiles; i++) + { + CSzFileItem *f = &files[i]; + Byte defined = (*lwtVector)[i]; + f->AttribDefined = defined; + f->Attrib = 0; + if (defined) + { + RINOK(SzReadUInt32(sd, &f->Attrib)); + } + } + IAlloc_Free(allocTemp, *lwtVector); + *lwtVector = NULL; + break; + } + case k7zIdMTime: + { + RINOK(SzReadBoolVector2(sd, numFiles, lwtVector, allocTemp)); + RINOK(SzReadSwitch(sd)); + for (i = 0; i < numFiles; i++) + { + CSzFileItem *f = &files[i]; + Byte defined = (*lwtVector)[i]; + f->MTimeDefined = defined; + f->MTime.Low = f->MTime.High = 0; + if (defined) + { + RINOK(SzReadUInt32(sd, &f->MTime.Low)); + RINOK(SzReadUInt32(sd, &f->MTime.High)); + } + } + IAlloc_Free(allocTemp, *lwtVector); + *lwtVector = NULL; + break; + } + default: + { + RINOK(SzSkeepDataSize(sd, size)); + } + } + } + + { + UInt32 emptyFileIndex = 0; + UInt32 sizeIndex = 0; + for (i = 0; i < numFiles; i++) + { + CSzFileItem *file = files + i; + file->IsAnti = 0; + if (*emptyStreamVector == 0) + file->HasStream = 1; + else + file->HasStream = (Byte)((*emptyStreamVector)[i] ? 0 : 1); + if (file->HasStream) + { + file->IsDir = 0; + file->Size = (*unpackSizes)[sizeIndex]; + file->Crc = (*digests)[sizeIndex]; + file->CrcDefined = (Byte)(*digestsDefined)[sizeIndex]; + sizeIndex++; + } + else + { + if (*emptyFileVector == 0) + file->IsDir = 1; + else + file->IsDir = (Byte)((*emptyFileVector)[emptyFileIndex] ? 0 : 1); + emptyFileIndex++; + file->Size = 0; + file->Crc = 0; + file->CrcDefined = 0; + } + } + } + return SzArEx_Fill(p, allocMain); +} + +static SRes SzReadHeader( + CSzArEx *p, + CSzData *sd, + ISzAlloc *allocMain, + ISzAlloc *allocTemp) +{ + UInt64 *unpackSizes = 0; + Byte *digestsDefined = 0; + UInt32 *digests = 0; + Byte *emptyStreamVector = 0; + Byte *emptyFileVector = 0; + Byte *lwtVector = 0; + SRes res = SzReadHeader2(p, sd, + &unpackSizes, &digestsDefined, &digests, + &emptyStreamVector, &emptyFileVector, &lwtVector, + allocMain, allocTemp); + IAlloc_Free(allocTemp, unpackSizes); + IAlloc_Free(allocTemp, digestsDefined); + IAlloc_Free(allocTemp, digests); + IAlloc_Free(allocTemp, emptyStreamVector); + IAlloc_Free(allocTemp, emptyFileVector); + IAlloc_Free(allocTemp, lwtVector); + return res; +} + +static SRes SzReadAndDecodePackedStreams2( + ILookInStream *inStream, + CSzData *sd, + CBuf *outBuffer, + UInt64 baseOffset, + CSzAr *p, + UInt64 **unpackSizes, + Byte **digestsDefined, + UInt32 **digests, + ISzAlloc *allocTemp) +{ + + UInt32 numUnpackStreams = 0; + UInt64 dataStartPos; + CSzFolder *folder; + UInt64 unpackSize; + SRes res; + + RINOK(SzReadStreamsInfo(sd, &dataStartPos, p, + &numUnpackStreams, unpackSizes, digestsDefined, digests, + allocTemp, allocTemp)); + + dataStartPos += baseOffset; + if (p->NumFolders != 1) + return SZ_ERROR_ARCHIVE; + + folder = p->Folders; + unpackSize = SzFolder_GetUnpackSize(folder); + + RINOK(LookInStream_SeekTo(inStream, dataStartPos)); + + if (!Buf_Create(outBuffer, (size_t)unpackSize, allocTemp)) + return SZ_ERROR_MEM; + + res = SzFolder_Decode(folder, p->PackSizes, + inStream, dataStartPos, + outBuffer->data, (size_t)unpackSize, allocTemp); + RINOK(res); + if (folder->UnpackCRCDefined) + if (CrcCalc(outBuffer->data, (size_t)unpackSize) != folder->UnpackCRC) + return SZ_ERROR_CRC; + return SZ_OK; +} + +static SRes SzReadAndDecodePackedStreams( + ILookInStream *inStream, + CSzData *sd, + CBuf *outBuffer, + UInt64 baseOffset, + ISzAlloc *allocTemp) +{ + CSzAr p; + UInt64 *unpackSizes = 0; + Byte *digestsDefined = 0; + UInt32 *digests = 0; + SRes res; + SzAr_Init(&p); + res = SzReadAndDecodePackedStreams2(inStream, sd, outBuffer, baseOffset, + &p, &unpackSizes, &digestsDefined, &digests, + allocTemp); + SzAr_Free(&p, allocTemp); + IAlloc_Free(allocTemp, unpackSizes); + IAlloc_Free(allocTemp, digestsDefined); + IAlloc_Free(allocTemp, digests); + return res; +} + +static SRes SzArEx_Open2( + CSzArEx *p, + ILookInStream *inStream, + ISzAlloc *allocMain, + ISzAlloc *allocTemp) +{ + Byte header[k7zStartHeaderSize]; + Int64 startArcPos; + UInt64 nextHeaderOffset, nextHeaderSize; + size_t nextHeaderSizeT; + UInt32 nextHeaderCRC; + CBuf buffer; + SRes res; + + startArcPos = 0; + RINOK(inStream->Seek(inStream, &startArcPos, SZ_SEEK_CUR)); + + RINOK(LookInStream_Read2(inStream, header, k7zStartHeaderSize, SZ_ERROR_NO_ARCHIVE)); + + if (!TestSignatureCandidate(header)) + return SZ_ERROR_NO_ARCHIVE; + if (header[6] != k7zMajorVersion) + return SZ_ERROR_UNSUPPORTED; + + nextHeaderOffset = GetUi64(header + 12); + nextHeaderSize = GetUi64(header + 20); + nextHeaderCRC = GetUi32(header + 28); + + p->startPosAfterHeader = startArcPos + k7zStartHeaderSize; + + if (CrcCalc(header + 12, 20) != GetUi32(header + 8)) + return SZ_ERROR_CRC; + + nextHeaderSizeT = (size_t)nextHeaderSize; + if (nextHeaderSizeT != nextHeaderSize) + return SZ_ERROR_MEM; + if (nextHeaderSizeT == 0) + return SZ_OK; + if (nextHeaderOffset > nextHeaderOffset + nextHeaderSize || + nextHeaderOffset > nextHeaderOffset + nextHeaderSize + k7zStartHeaderSize) + return SZ_ERROR_NO_ARCHIVE; + + { + Int64 pos = 0; + RINOK(inStream->Seek(inStream, &pos, SZ_SEEK_END)); + if ((UInt64)pos < startArcPos + nextHeaderOffset || + (UInt64)pos < startArcPos + k7zStartHeaderSize + nextHeaderOffset || + (UInt64)pos < startArcPos + k7zStartHeaderSize + nextHeaderOffset + nextHeaderSize) + return SZ_ERROR_INPUT_EOF; + } + + RINOK(LookInStream_SeekTo(inStream, startArcPos + k7zStartHeaderSize + nextHeaderOffset)); + + if (!Buf_Create(&buffer, nextHeaderSizeT, allocTemp)) + return SZ_ERROR_MEM; + + res = LookInStream_Read(inStream, buffer.data, nextHeaderSizeT); + if (res == SZ_OK) + { + res = SZ_ERROR_ARCHIVE; + if (CrcCalc(buffer.data, nextHeaderSizeT) == nextHeaderCRC) + { + CSzData sd; + UInt64 type; + sd.Data = buffer.data; + sd.Size = buffer.size; + res = SzReadID(&sd, &type); + if (res == SZ_OK) + { + if (type == k7zIdEncodedHeader) + { + CBuf outBuffer; + Buf_Init(&outBuffer); + res = SzReadAndDecodePackedStreams(inStream, &sd, &outBuffer, p->startPosAfterHeader, allocTemp); + if (res != SZ_OK) + Buf_Free(&outBuffer, allocTemp); + else + { + Buf_Free(&buffer, allocTemp); + buffer.data = outBuffer.data; + buffer.size = outBuffer.size; + sd.Data = buffer.data; + sd.Size = buffer.size; + res = SzReadID(&sd, &type); + } + } + } + if (res == SZ_OK) + { + if (type == k7zIdHeader) + res = SzReadHeader(p, &sd, allocMain, allocTemp); + else + res = SZ_ERROR_UNSUPPORTED; + } + } + } + Buf_Free(&buffer, allocTemp); + return res; +} + +SRes SzArEx_Open(CSzArEx *p, ILookInStream *inStream, ISzAlloc *allocMain, ISzAlloc *allocTemp) +{ + SRes res = SzArEx_Open2(p, inStream, allocMain, allocTemp); + if (res != SZ_OK) + SzArEx_Free(p, allocMain); + return res; +} + +SRes SzArEx_Extract( + const CSzArEx *p, + ILookInStream *inStream, + UInt32 fileIndex, + UInt32 *blockIndex, + Byte **outBuffer, + size_t *outBufferSize, + size_t *offset, + size_t *outSizeProcessed, + ISzAlloc *allocMain, + ISzAlloc *allocTemp) +{ + UInt32 folderIndex = p->FileIndexToFolderIndexMap[fileIndex]; + SRes res = SZ_OK; + *offset = 0; + *outSizeProcessed = 0; + if (folderIndex == (UInt32)-1) + { + IAlloc_Free(allocMain, *outBuffer); + *blockIndex = folderIndex; + *outBuffer = 0; + *outBufferSize = 0; + return SZ_OK; + } + + if (*outBuffer == 0 || *blockIndex != folderIndex) + { + CSzFolder *folder = p->db.Folders + folderIndex; + UInt64 unpackSizeSpec = SzFolder_GetUnpackSize(folder); + size_t unpackSize = (size_t)unpackSizeSpec; + UInt64 startOffset = SzArEx_GetFolderStreamPos(p, folderIndex, 0); + + if (unpackSize != unpackSizeSpec) + return SZ_ERROR_MEM; + *blockIndex = folderIndex; + IAlloc_Free(allocMain, *outBuffer); + *outBuffer = 0; + + RINOK(LookInStream_SeekTo(inStream, startOffset)); + + if (res == SZ_OK) + { + *outBufferSize = unpackSize; + if (unpackSize != 0) + { + *outBuffer = (Byte *)IAlloc_Alloc(allocMain, unpackSize); + if (*outBuffer == 0) + res = SZ_ERROR_MEM; + } + if (res == SZ_OK) + { + res = SzFolder_Decode(folder, + p->db.PackSizes + p->FolderStartPackStreamIndex[folderIndex], + inStream, startOffset, + *outBuffer, unpackSize, allocTemp); + if (res == SZ_OK) + { + if (folder->UnpackCRCDefined) + { + if (CrcCalc(*outBuffer, unpackSize) != folder->UnpackCRC) + res = SZ_ERROR_CRC; + } + } + } + } + } + if (res == SZ_OK) + { + UInt32 i; + CSzFileItem *fileItem = p->db.Files + fileIndex; + *offset = 0; + for (i = p->FolderStartFileIndex[folderIndex]; i < fileIndex; i++) + *offset += (UInt32)p->db.Files[i].Size; + *outSizeProcessed = (size_t)fileItem->Size; + if (*offset + *outSizeProcessed > *outBufferSize) + return SZ_ERROR_FAIL; + if (fileItem->CrcDefined && CrcCalc(*outBuffer + *offset, *outSizeProcessed) != fileItem->Crc) + res = SZ_ERROR_CRC; + } + return res; +} diff --git a/fex/7z_C/7zIn.h b/fex/7z_C/7zIn.h index 278a8450..89e0fb85 100644 --- a/fex/7z_C/7zIn.h +++ b/fex/7z_C/7zIn.h @@ -1,49 +1,49 @@ -/* 7zIn.h -- 7z Input functions -2008-11-23 : Igor Pavlov : Public domain */ - -#ifndef __7Z_IN_H -#define __7Z_IN_H - -#include "7zHeader.h" -#include "7zItem.h" - -#ifdef __cplusplus - extern "C" { -#endif - -typedef struct -{ - CSzAr db; - - UInt64 startPosAfterHeader; - UInt64 dataPos; - - UInt32 *FolderStartPackStreamIndex; - UInt64 *PackStreamStartPositions; - UInt32 *FolderStartFileIndex; - UInt32 *FileIndexToFolderIndexMap; -} CSzArEx; - -void SzArEx_Init(CSzArEx *p); -void SzArEx_Free(CSzArEx *p, ISzAlloc *alloc); -UInt64 SzArEx_GetFolderStreamPos(const CSzArEx *p, UInt32 folderIndex, UInt32 indexInFolder); -int SzArEx_GetFolderFullPackSize(const CSzArEx *p, UInt32 folderIndex, UInt64 *resSize); - -/* -Errors: -SZ_ERROR_NO_ARCHIVE -SZ_ERROR_ARCHIVE -SZ_ERROR_UNSUPPORTED -SZ_ERROR_MEM -SZ_ERROR_CRC -SZ_ERROR_INPUT_EOF -SZ_ERROR_FAIL -*/ - -SRes SzArEx_Open(CSzArEx *p, ILookInStream *inStream, ISzAlloc *allocMain, ISzAlloc *allocTemp); - -#ifdef __cplusplus - } -#endif - -#endif +/* 7zIn.h -- 7z Input functions +2008-11-23 : Igor Pavlov : Public domain */ + +#ifndef __7Z_IN_H +#define __7Z_IN_H + +#include "7zHeader.h" +#include "7zItem.h" + +#ifdef __cplusplus + extern "C" { +#endif + +typedef struct +{ + CSzAr db; + + UInt64 startPosAfterHeader; + UInt64 dataPos; + + UInt32 *FolderStartPackStreamIndex; + UInt64 *PackStreamStartPositions; + UInt32 *FolderStartFileIndex; + UInt32 *FileIndexToFolderIndexMap; +} CSzArEx; + +void SzArEx_Init(CSzArEx *p); +void SzArEx_Free(CSzArEx *p, ISzAlloc *alloc); +UInt64 SzArEx_GetFolderStreamPos(const CSzArEx *p, UInt32 folderIndex, UInt32 indexInFolder); +int SzArEx_GetFolderFullPackSize(const CSzArEx *p, UInt32 folderIndex, UInt64 *resSize); + +/* +Errors: +SZ_ERROR_NO_ARCHIVE +SZ_ERROR_ARCHIVE +SZ_ERROR_UNSUPPORTED +SZ_ERROR_MEM +SZ_ERROR_CRC +SZ_ERROR_INPUT_EOF +SZ_ERROR_FAIL +*/ + +SRes SzArEx_Open(CSzArEx *p, ILookInStream *inStream, ISzAlloc *allocMain, ISzAlloc *allocTemp); + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/fex/7z_C/7zItem.c b/fex/7z_C/7zItem.c index ffe1cd77..4a092614 100644 --- a/fex/7z_C/7zItem.c +++ b/fex/7z_C/7zItem.c @@ -1,129 +1,129 @@ -/* 7zItem.c -- 7z Items -2008-10-04 : Igor Pavlov : Public domain */ - -#include "7zItem.h" - -void SzCoderInfo_Init(CSzCoderInfo *p) -{ - Buf_Init(&p->Props); -} - -void SzCoderInfo_Free(CSzCoderInfo *p, ISzAlloc *alloc) -{ - Buf_Free(&p->Props, alloc); - SzCoderInfo_Init(p); -} - -void SzFolder_Init(CSzFolder *p) -{ - p->Coders = 0; - p->BindPairs = 0; - p->PackStreams = 0; - p->UnpackSizes = 0; - p->NumCoders = 0; - p->NumBindPairs = 0; - p->NumPackStreams = 0; - p->UnpackCRCDefined = 0; - p->UnpackCRC = 0; - p->NumUnpackStreams = 0; -} - -static -void SzFolder_Free(CSzFolder *p, ISzAlloc *alloc) -{ - UInt32 i; - if (p->Coders) - for (i = 0; i < p->NumCoders; i++) - SzCoderInfo_Free(&p->Coders[i], alloc); - IAlloc_Free(alloc, p->Coders); - IAlloc_Free(alloc, p->BindPairs); - IAlloc_Free(alloc, p->PackStreams); - IAlloc_Free(alloc, p->UnpackSizes); - SzFolder_Init(p); -} - -UInt32 SzFolder_GetNumOutStreams(CSzFolder *p) -{ - UInt32 result = 0; - UInt32 i; - for (i = 0; i < p->NumCoders; i++) - result += p->Coders[i].NumOutStreams; - return result; -} - -int SzFolder_FindBindPairForInStream(CSzFolder *p, UInt32 inStreamIndex) -{ - UInt32 i; - for (i = 0; i < p->NumBindPairs; i++) - if (p->BindPairs[i].InIndex == inStreamIndex) - return i; - return -1; -} - - -static -int SzFolder_FindBindPairForOutStream(CSzFolder *p, UInt32 outStreamIndex) -{ - UInt32 i; - for (i = 0; i < p->NumBindPairs; i++) - if (p->BindPairs[i].OutIndex == outStreamIndex) - return i; - return -1; -} - -UInt64 SzFolder_GetUnpackSize(CSzFolder *p) -{ - int i = (int)SzFolder_GetNumOutStreams(p); - if (i == 0) - return 0; - for (i--; i >= 0; i--) - if (SzFolder_FindBindPairForOutStream(p, i) < 0) - return p->UnpackSizes[i]; - /* throw 1; */ - return 0; -} - -void SzFile_Init(CSzFileItem *p) -{ - p->HasStream = 1; - p->IsDir = 0; - p->IsAnti = 0; - p->FileCRCDefined = 0; - p->MTimeDefined = 0; - p->Name = 0; -} - -static void SzFile_Free(CSzFileItem *p, ISzAlloc *alloc) -{ - IAlloc_Free(alloc, p->Name); - SzFile_Init(p); -} - -void SzAr_Init(CSzAr *p) -{ - p->PackSizes = 0; - p->PackCRCsDefined = 0; - p->PackCRCs = 0; - p->Folders = 0; - p->Files = 0; - p->NumPackStreams = 0; - p->NumFolders = 0; - p->NumFiles = 0; -} - -void SzAr_Free(CSzAr *p, ISzAlloc *alloc) -{ - UInt32 i; - if (p->Folders) - for (i = 0; i < p->NumFolders; i++) - SzFolder_Free(&p->Folders[i], alloc); - if (p->Files) - for (i = 0; i < p->NumFiles; i++) - SzFile_Free(&p->Files[i], alloc); - IAlloc_Free(alloc, p->PackSizes); - IAlloc_Free(alloc, p->PackCRCsDefined); - IAlloc_Free(alloc, p->PackCRCs); - IAlloc_Free(alloc, p->Folders); - IAlloc_Free(alloc, p->Files); - SzAr_Init(p); -} +/* 7zItem.c -- 7z Items +2008-10-04 : Igor Pavlov : Public domain */ + +#include "7zItem.h" + +void SzCoderInfo_Init(CSzCoderInfo *p) +{ + Buf_Init(&p->Props); +} + +void SzCoderInfo_Free(CSzCoderInfo *p, ISzAlloc *alloc) +{ + Buf_Free(&p->Props, alloc); + SzCoderInfo_Init(p); +} + +void SzFolder_Init(CSzFolder *p) +{ + p->Coders = 0; + p->BindPairs = 0; + p->PackStreams = 0; + p->UnpackSizes = 0; + p->NumCoders = 0; + p->NumBindPairs = 0; + p->NumPackStreams = 0; + p->UnpackCRCDefined = 0; + p->UnpackCRC = 0; + p->NumUnpackStreams = 0; +} + +static +void SzFolder_Free(CSzFolder *p, ISzAlloc *alloc) +{ + UInt32 i; + if (p->Coders) + for (i = 0; i < p->NumCoders; i++) + SzCoderInfo_Free(&p->Coders[i], alloc); + IAlloc_Free(alloc, p->Coders); + IAlloc_Free(alloc, p->BindPairs); + IAlloc_Free(alloc, p->PackStreams); + IAlloc_Free(alloc, p->UnpackSizes); + SzFolder_Init(p); +} + +UInt32 SzFolder_GetNumOutStreams(CSzFolder *p) +{ + UInt32 result = 0; + UInt32 i; + for (i = 0; i < p->NumCoders; i++) + result += p->Coders[i].NumOutStreams; + return result; +} + +int SzFolder_FindBindPairForInStream(CSzFolder *p, UInt32 inStreamIndex) +{ + UInt32 i; + for (i = 0; i < p->NumBindPairs; i++) + if (p->BindPairs[i].InIndex == inStreamIndex) + return i; + return -1; +} + + +static +int SzFolder_FindBindPairForOutStream(CSzFolder *p, UInt32 outStreamIndex) +{ + UInt32 i; + for (i = 0; i < p->NumBindPairs; i++) + if (p->BindPairs[i].OutIndex == outStreamIndex) + return i; + return -1; +} + +UInt64 SzFolder_GetUnpackSize(CSzFolder *p) +{ + int i = (int)SzFolder_GetNumOutStreams(p); + if (i == 0) + return 0; + for (i--; i >= 0; i--) + if (SzFolder_FindBindPairForOutStream(p, i) < 0) + return p->UnpackSizes[i]; + /* throw 1; */ + return 0; +} + +void SzFile_Init(CSzFileItem *p) +{ + p->HasStream = 1; + p->IsDir = 0; + p->IsAnti = 0; + p->FileCRCDefined = 0; + p->MTimeDefined = 0; + p->Name = 0; +} + +static void SzFile_Free(CSzFileItem *p, ISzAlloc *alloc) +{ + IAlloc_Free(alloc, p->Name); + SzFile_Init(p); +} + +void SzAr_Init(CSzAr *p) +{ + p->PackSizes = 0; + p->PackCRCsDefined = 0; + p->PackCRCs = 0; + p->Folders = 0; + p->Files = 0; + p->NumPackStreams = 0; + p->NumFolders = 0; + p->NumFiles = 0; +} + +void SzAr_Free(CSzAr *p, ISzAlloc *alloc) +{ + UInt32 i; + if (p->Folders) + for (i = 0; i < p->NumFolders; i++) + SzFolder_Free(&p->Folders[i], alloc); + if (p->Files) + for (i = 0; i < p->NumFiles; i++) + SzFile_Free(&p->Files[i], alloc); + IAlloc_Free(alloc, p->PackSizes); + IAlloc_Free(alloc, p->PackCRCsDefined); + IAlloc_Free(alloc, p->PackCRCs); + IAlloc_Free(alloc, p->Folders); + IAlloc_Free(alloc, p->Files); + SzAr_Init(p); +} diff --git a/fex/7z_C/7zItem.h b/fex/7z_C/7zItem.h index dbd21b81..7ef24731 100644 --- a/fex/7z_C/7zItem.h +++ b/fex/7z_C/7zItem.h @@ -1,83 +1,83 @@ -/* 7zItem.h -- 7z Items -2008-10-04 : Igor Pavlov : Public domain */ - -#ifndef __7Z_ITEM_H -#define __7Z_ITEM_H - -#include "7zBuf.h" - -typedef struct -{ - UInt32 NumInStreams; - UInt32 NumOutStreams; - UInt64 MethodID; - CBuf Props; -} CSzCoderInfo; - -void SzCoderInfo_Init(CSzCoderInfo *p); -void SzCoderInfo_Free(CSzCoderInfo *p, ISzAlloc *alloc); - -typedef struct -{ - UInt32 InIndex; - UInt32 OutIndex; -} CBindPair; - -typedef struct -{ - CSzCoderInfo *Coders; - CBindPair *BindPairs; - UInt32 *PackStreams; - UInt64 *UnpackSizes; - UInt32 NumCoders; - UInt32 NumBindPairs; - UInt32 NumPackStreams; - int UnpackCRCDefined; - UInt32 UnpackCRC; - - UInt32 NumUnpackStreams; -} CSzFolder; - -void SzFolder_Init(CSzFolder *p); -UInt64 SzFolder_GetUnpackSize(CSzFolder *p); -int SzFolder_FindBindPairForInStream(CSzFolder *p, UInt32 inStreamIndex); -UInt32 SzFolder_GetNumOutStreams(CSzFolder *p); - -typedef struct -{ - UInt32 Low; - UInt32 High; -} CNtfsFileTime; - -typedef struct -{ - CNtfsFileTime MTime; - UInt64 Size; - char *Name; - UInt32 FileCRC; - - Byte HasStream; - Byte IsDir; - Byte IsAnti; - Byte FileCRCDefined; - Byte MTimeDefined; -} CSzFileItem; - -void SzFile_Init(CSzFileItem *p); - -typedef struct -{ - UInt64 *PackSizes; - Byte *PackCRCsDefined; - UInt32 *PackCRCs; - CSzFolder *Folders; - CSzFileItem *Files; - UInt32 NumPackStreams; - UInt32 NumFolders; - UInt32 NumFiles; -} CSzAr; - -void SzAr_Init(CSzAr *p); -void SzAr_Free(CSzAr *p, ISzAlloc *alloc); - -#endif +/* 7zItem.h -- 7z Items +2008-10-04 : Igor Pavlov : Public domain */ + +#ifndef __7Z_ITEM_H +#define __7Z_ITEM_H + +#include "7zBuf.h" + +typedef struct +{ + UInt32 NumInStreams; + UInt32 NumOutStreams; + UInt64 MethodID; + CBuf Props; +} CSzCoderInfo; + +void SzCoderInfo_Init(CSzCoderInfo *p); +void SzCoderInfo_Free(CSzCoderInfo *p, ISzAlloc *alloc); + +typedef struct +{ + UInt32 InIndex; + UInt32 OutIndex; +} CBindPair; + +typedef struct +{ + CSzCoderInfo *Coders; + CBindPair *BindPairs; + UInt32 *PackStreams; + UInt64 *UnpackSizes; + UInt32 NumCoders; + UInt32 NumBindPairs; + UInt32 NumPackStreams; + int UnpackCRCDefined; + UInt32 UnpackCRC; + + UInt32 NumUnpackStreams; +} CSzFolder; + +void SzFolder_Init(CSzFolder *p); +UInt64 SzFolder_GetUnpackSize(CSzFolder *p); +int SzFolder_FindBindPairForInStream(CSzFolder *p, UInt32 inStreamIndex); +UInt32 SzFolder_GetNumOutStreams(CSzFolder *p); + +typedef struct +{ + UInt32 Low; + UInt32 High; +} CNtfsFileTime; + +typedef struct +{ + CNtfsFileTime MTime; + UInt64 Size; + char *Name; + UInt32 FileCRC; + + Byte HasStream; + Byte IsDir; + Byte IsAnti; + Byte FileCRCDefined; + Byte MTimeDefined; +} CSzFileItem; + +void SzFile_Init(CSzFileItem *p); + +typedef struct +{ + UInt64 *PackSizes; + Byte *PackCRCsDefined; + UInt32 *PackCRCs; + CSzFolder *Folders; + CSzFileItem *Files; + UInt32 NumPackStreams; + UInt32 NumFolders; + UInt32 NumFiles; +} CSzAr; + +void SzAr_Init(CSzAr *p); +void SzAr_Free(CSzAr *p, ISzAlloc *alloc); + +#endif diff --git a/fex/7z_C/7zStream.c b/fex/7z_C/7zStream.c index f0959fb0..0ebb7b5f 100644 --- a/fex/7z_C/7zStream.c +++ b/fex/7z_C/7zStream.c @@ -1,169 +1,169 @@ -/* 7zStream.c -- 7z Stream functions -2010-03-11 : Igor Pavlov : Public domain */ - -#include - -#include "Types.h" - -SRes SeqInStream_Read2(ISeqInStream *stream, void *buf, size_t size, SRes errorType) -{ - while (size != 0) - { - size_t processed = size; - RINOK(stream->Read(stream, buf, &processed)); - if (processed == 0) - return errorType; - buf = (void *)((Byte *)buf + processed); - size -= processed; - } - return SZ_OK; -} - -SRes SeqInStream_Read(ISeqInStream *stream, void *buf, size_t size) -{ - return SeqInStream_Read2(stream, buf, size, SZ_ERROR_INPUT_EOF); -} - -SRes SeqInStream_ReadByte(ISeqInStream *stream, Byte *buf) -{ - size_t processed = 1; - RINOK(stream->Read(stream, buf, &processed)); - return (processed == 1) ? SZ_OK : SZ_ERROR_INPUT_EOF; -} - -SRes LookInStream_SeekTo(ILookInStream *stream, UInt64 offset) -{ - Int64 t = offset; - return stream->Seek(stream, &t, SZ_SEEK_SET); -} - -SRes LookInStream_LookRead(ILookInStream *stream, void *buf, size_t *size) -{ - const void *lookBuf; - if (*size == 0) - return SZ_OK; - RINOK(stream->Look(stream, &lookBuf, size)); - memcpy(buf, lookBuf, *size); - return stream->Skip(stream, *size); -} - -SRes LookInStream_Read2(ILookInStream *stream, void *buf, size_t size, SRes errorType) -{ - while (size != 0) - { - size_t processed = size; - RINOK(stream->Read(stream, buf, &processed)); - if (processed == 0) - return errorType; - buf = (void *)((Byte *)buf + processed); - size -= processed; - } - return SZ_OK; -} - -SRes LookInStream_Read(ILookInStream *stream, void *buf, size_t size) -{ - return LookInStream_Read2(stream, buf, size, SZ_ERROR_INPUT_EOF); -} - -static SRes LookToRead_Look_Lookahead(void *pp, const void **buf, size_t *size) -{ - SRes res = SZ_OK; - CLookToRead *p = (CLookToRead *)pp; - size_t size2 = p->size - p->pos; - if (size2 == 0 && *size > 0) - { - p->pos = 0; - size2 = LookToRead_BUF_SIZE; - res = p->realStream->Read(p->realStream, p->buf, &size2); - p->size = size2; - } - if (size2 < *size) - *size = size2; - *buf = p->buf + p->pos; - return res; -} - -static SRes LookToRead_Look_Exact(void *pp, const void **buf, size_t *size) -{ - SRes res = SZ_OK; - CLookToRead *p = (CLookToRead *)pp; - size_t size2 = p->size - p->pos; - if (size2 == 0 && *size > 0) - { - p->pos = 0; - if (*size > LookToRead_BUF_SIZE) - *size = LookToRead_BUF_SIZE; - res = p->realStream->Read(p->realStream, p->buf, size); - size2 = p->size = *size; - } - if (size2 < *size) - *size = size2; - *buf = p->buf + p->pos; - return res; -} - -static SRes LookToRead_Skip(void *pp, size_t offset) -{ - CLookToRead *p = (CLookToRead *)pp; - p->pos += offset; - return SZ_OK; -} - -static SRes LookToRead_Read(void *pp, void *buf, size_t *size) -{ - CLookToRead *p = (CLookToRead *)pp; - size_t rem = p->size - p->pos; - if (rem == 0) - return p->realStream->Read(p->realStream, buf, size); - if (rem > *size) - rem = *size; - memcpy(buf, p->buf + p->pos, rem); - p->pos += rem; - *size = rem; - return SZ_OK; -} - -static SRes LookToRead_Seek(void *pp, Int64 *pos, ESzSeek origin) -{ - CLookToRead *p = (CLookToRead *)pp; - p->pos = p->size = 0; - return p->realStream->Seek(p->realStream, pos, origin); -} - -void LookToRead_CreateVTable(CLookToRead *p, int lookahead) -{ - p->s.Look = lookahead ? - LookToRead_Look_Lookahead : - LookToRead_Look_Exact; - p->s.Skip = LookToRead_Skip; - p->s.Read = LookToRead_Read; - p->s.Seek = LookToRead_Seek; -} - -void LookToRead_Init(CLookToRead *p) -{ - p->pos = p->size = 0; -} - -static SRes SecToLook_Read(void *pp, void *buf, size_t *size) -{ - CSecToLook *p = (CSecToLook *)pp; - return LookInStream_LookRead(p->realStream, buf, size); -} - -void SecToLook_CreateVTable(CSecToLook *p) -{ - p->s.Read = SecToLook_Read; -} - -static SRes SecToRead_Read(void *pp, void *buf, size_t *size) -{ - CSecToRead *p = (CSecToRead *)pp; - return p->realStream->Read(p->realStream, buf, size); -} - -void SecToRead_CreateVTable(CSecToRead *p) -{ - p->s.Read = SecToRead_Read; -} +/* 7zStream.c -- 7z Stream functions +2010-03-11 : Igor Pavlov : Public domain */ + +#include + +#include "Types.h" + +SRes SeqInStream_Read2(ISeqInStream *stream, void *buf, size_t size, SRes errorType) +{ + while (size != 0) + { + size_t processed = size; + RINOK(stream->Read(stream, buf, &processed)); + if (processed == 0) + return errorType; + buf = (void *)((Byte *)buf + processed); + size -= processed; + } + return SZ_OK; +} + +SRes SeqInStream_Read(ISeqInStream *stream, void *buf, size_t size) +{ + return SeqInStream_Read2(stream, buf, size, SZ_ERROR_INPUT_EOF); +} + +SRes SeqInStream_ReadByte(ISeqInStream *stream, Byte *buf) +{ + size_t processed = 1; + RINOK(stream->Read(stream, buf, &processed)); + return (processed == 1) ? SZ_OK : SZ_ERROR_INPUT_EOF; +} + +SRes LookInStream_SeekTo(ILookInStream *stream, UInt64 offset) +{ + Int64 t = offset; + return stream->Seek(stream, &t, SZ_SEEK_SET); +} + +SRes LookInStream_LookRead(ILookInStream *stream, void *buf, size_t *size) +{ + const void *lookBuf; + if (*size == 0) + return SZ_OK; + RINOK(stream->Look(stream, &lookBuf, size)); + memcpy(buf, lookBuf, *size); + return stream->Skip(stream, *size); +} + +SRes LookInStream_Read2(ILookInStream *stream, void *buf, size_t size, SRes errorType) +{ + while (size != 0) + { + size_t processed = size; + RINOK(stream->Read(stream, buf, &processed)); + if (processed == 0) + return errorType; + buf = (void *)((Byte *)buf + processed); + size -= processed; + } + return SZ_OK; +} + +SRes LookInStream_Read(ILookInStream *stream, void *buf, size_t size) +{ + return LookInStream_Read2(stream, buf, size, SZ_ERROR_INPUT_EOF); +} + +static SRes LookToRead_Look_Lookahead(void *pp, const void **buf, size_t *size) +{ + SRes res = SZ_OK; + CLookToRead *p = (CLookToRead *)pp; + size_t size2 = p->size - p->pos; + if (size2 == 0 && *size > 0) + { + p->pos = 0; + size2 = LookToRead_BUF_SIZE; + res = p->realStream->Read(p->realStream, p->buf, &size2); + p->size = size2; + } + if (size2 < *size) + *size = size2; + *buf = p->buf + p->pos; + return res; +} + +static SRes LookToRead_Look_Exact(void *pp, const void **buf, size_t *size) +{ + SRes res = SZ_OK; + CLookToRead *p = (CLookToRead *)pp; + size_t size2 = p->size - p->pos; + if (size2 == 0 && *size > 0) + { + p->pos = 0; + if (*size > LookToRead_BUF_SIZE) + *size = LookToRead_BUF_SIZE; + res = p->realStream->Read(p->realStream, p->buf, size); + size2 = p->size = *size; + } + if (size2 < *size) + *size = size2; + *buf = p->buf + p->pos; + return res; +} + +static SRes LookToRead_Skip(void *pp, size_t offset) +{ + CLookToRead *p = (CLookToRead *)pp; + p->pos += offset; + return SZ_OK; +} + +static SRes LookToRead_Read(void *pp, void *buf, size_t *size) +{ + CLookToRead *p = (CLookToRead *)pp; + size_t rem = p->size - p->pos; + if (rem == 0) + return p->realStream->Read(p->realStream, buf, size); + if (rem > *size) + rem = *size; + memcpy(buf, p->buf + p->pos, rem); + p->pos += rem; + *size = rem; + return SZ_OK; +} + +static SRes LookToRead_Seek(void *pp, Int64 *pos, ESzSeek origin) +{ + CLookToRead *p = (CLookToRead *)pp; + p->pos = p->size = 0; + return p->realStream->Seek(p->realStream, pos, origin); +} + +void LookToRead_CreateVTable(CLookToRead *p, int lookahead) +{ + p->s.Look = lookahead ? + LookToRead_Look_Lookahead : + LookToRead_Look_Exact; + p->s.Skip = LookToRead_Skip; + p->s.Read = LookToRead_Read; + p->s.Seek = LookToRead_Seek; +} + +void LookToRead_Init(CLookToRead *p) +{ + p->pos = p->size = 0; +} + +static SRes SecToLook_Read(void *pp, void *buf, size_t *size) +{ + CSecToLook *p = (CSecToLook *)pp; + return LookInStream_LookRead(p->realStream, buf, size); +} + +void SecToLook_CreateVTable(CSecToLook *p) +{ + p->s.Read = SecToLook_Read; +} + +static SRes SecToRead_Read(void *pp, void *buf, size_t *size) +{ + CSecToRead *p = (CSecToRead *)pp; + return p->realStream->Read(p->realStream, buf, size); +} + +void SecToRead_CreateVTable(CSecToRead *p) +{ + p->s.Read = SecToRead_Read; +} diff --git a/fex/7z_C/Bcj2.c b/fex/7z_C/Bcj2.c index 474bdd45..20199ce5 100644 --- a/fex/7z_C/Bcj2.c +++ b/fex/7z_C/Bcj2.c @@ -1,132 +1,132 @@ -/* Bcj2.c -- Converter for x86 code (BCJ2) -2008-10-04 : Igor Pavlov : Public domain */ - -#include "Bcj2.h" - -#ifdef _LZMA_PROB32 -#define CProb UInt32 -#else -#define CProb UInt16 -#endif - -#define IsJcc(b0, b1) ((b0) == 0x0F && ((b1) & 0xF0) == 0x80) -#define IsJ(b0, b1) ((b1 & 0xFE) == 0xE8 || IsJcc(b0, b1)) - -#define kNumTopBits 24 -#define kTopValue ((UInt32)1 << kNumTopBits) - -#define kNumBitModelTotalBits 11 -#define kBitModelTotal (1 << kNumBitModelTotalBits) -#define kNumMoveBits 5 - -#define RC_READ_BYTE (*buffer++) -#define RC_TEST { if (buffer == bufferLim) return SZ_ERROR_DATA; } -#define RC_INIT2 code = 0; range = 0xFFFFFFFF; \ - { int i; for (i = 0; i < 5; i++) { RC_TEST; code = (code << 8) | RC_READ_BYTE; }} - -#define NORMALIZE if (range < kTopValue) { RC_TEST; range <<= 8; code = (code << 8) | RC_READ_BYTE; } - -#define IF_BIT_0(p) ttt = *(p); bound = (range >> kNumBitModelTotalBits) * ttt; if (code < bound) -#define UPDATE_0(p) range = bound; *(p) = (CProb)(ttt + ((kBitModelTotal - ttt) >> kNumMoveBits)); NORMALIZE; -#define UPDATE_1(p) range -= bound; code -= bound; *(p) = (CProb)(ttt - (ttt >> kNumMoveBits)); NORMALIZE; - -int Bcj2_Decode( - const Byte *buf0, SizeT size0, - const Byte *buf1, SizeT size1, - const Byte *buf2, SizeT size2, - const Byte *buf3, SizeT size3, - Byte *outBuf, SizeT outSize) -{ - CProb p[256 + 2]; - SizeT inPos = 0, outPos = 0; - - const Byte *buffer, *bufferLim; - UInt32 range, code; - Byte prevByte = 0; - - unsigned int i; - for (i = 0; i < sizeof(p) / sizeof(p[0]); i++) - p[i] = kBitModelTotal >> 1; - - buffer = buf3; - bufferLim = buffer + size3; - RC_INIT2 - - if (outSize == 0) - return SZ_OK; - - for (;;) - { - Byte b; - CProb *prob; - UInt32 bound; - UInt32 ttt; - - SizeT limit = size0 - inPos; - if (outSize - outPos < limit) - limit = outSize - outPos; - while (limit != 0) - { - Byte b = buf0[inPos]; - outBuf[outPos++] = b; - if (IsJ(prevByte, b)) - break; - inPos++; - prevByte = b; - limit--; - } - - if (limit == 0 || outPos == outSize) - break; - - b = buf0[inPos++]; - - if (b == 0xE8) - prob = p + prevByte; - else if (b == 0xE9) - prob = p + 256; - else - prob = p + 257; - - IF_BIT_0(prob) - { - UPDATE_0(prob) - prevByte = b; - } - else - { - UInt32 dest; - const Byte *v; - UPDATE_1(prob) - if (b == 0xE8) - { - v = buf1; - if (size1 < 4) - return SZ_ERROR_DATA; - buf1 += 4; - size1 -= 4; - } - else - { - v = buf2; - if (size2 < 4) - return SZ_ERROR_DATA; - buf2 += 4; - size2 -= 4; - } - dest = (((UInt32)v[0] << 24) | ((UInt32)v[1] << 16) | - ((UInt32)v[2] << 8) | ((UInt32)v[3])) - ((UInt32)outPos + 4); - outBuf[outPos++] = (Byte)dest; - if (outPos == outSize) - break; - outBuf[outPos++] = (Byte)(dest >> 8); - if (outPos == outSize) - break; - outBuf[outPos++] = (Byte)(dest >> 16); - if (outPos == outSize) - break; - outBuf[outPos++] = prevByte = (Byte)(dest >> 24); - } - } - return (outPos == outSize) ? SZ_OK : SZ_ERROR_DATA; -} +/* Bcj2.c -- Converter for x86 code (BCJ2) +2008-10-04 : Igor Pavlov : Public domain */ + +#include "Bcj2.h" + +#ifdef _LZMA_PROB32 +#define CProb UInt32 +#else +#define CProb UInt16 +#endif + +#define IsJcc(b0, b1) ((b0) == 0x0F && ((b1) & 0xF0) == 0x80) +#define IsJ(b0, b1) ((b1 & 0xFE) == 0xE8 || IsJcc(b0, b1)) + +#define kNumTopBits 24 +#define kTopValue ((UInt32)1 << kNumTopBits) + +#define kNumBitModelTotalBits 11 +#define kBitModelTotal (1 << kNumBitModelTotalBits) +#define kNumMoveBits 5 + +#define RC_READ_BYTE (*buffer++) +#define RC_TEST { if (buffer == bufferLim) return SZ_ERROR_DATA; } +#define RC_INIT2 code = 0; range = 0xFFFFFFFF; \ + { int i; for (i = 0; i < 5; i++) { RC_TEST; code = (code << 8) | RC_READ_BYTE; }} + +#define NORMALIZE if (range < kTopValue) { RC_TEST; range <<= 8; code = (code << 8) | RC_READ_BYTE; } + +#define IF_BIT_0(p) ttt = *(p); bound = (range >> kNumBitModelTotalBits) * ttt; if (code < bound) +#define UPDATE_0(p) range = bound; *(p) = (CProb)(ttt + ((kBitModelTotal - ttt) >> kNumMoveBits)); NORMALIZE; +#define UPDATE_1(p) range -= bound; code -= bound; *(p) = (CProb)(ttt - (ttt >> kNumMoveBits)); NORMALIZE; + +int Bcj2_Decode( + const Byte *buf0, SizeT size0, + const Byte *buf1, SizeT size1, + const Byte *buf2, SizeT size2, + const Byte *buf3, SizeT size3, + Byte *outBuf, SizeT outSize) +{ + CProb p[256 + 2]; + SizeT inPos = 0, outPos = 0; + + const Byte *buffer, *bufferLim; + UInt32 range, code; + Byte prevByte = 0; + + unsigned int i; + for (i = 0; i < sizeof(p) / sizeof(p[0]); i++) + p[i] = kBitModelTotal >> 1; + + buffer = buf3; + bufferLim = buffer + size3; + RC_INIT2 + + if (outSize == 0) + return SZ_OK; + + for (;;) + { + Byte b; + CProb *prob; + UInt32 bound; + UInt32 ttt; + + SizeT limit = size0 - inPos; + if (outSize - outPos < limit) + limit = outSize - outPos; + while (limit != 0) + { + Byte b = buf0[inPos]; + outBuf[outPos++] = b; + if (IsJ(prevByte, b)) + break; + inPos++; + prevByte = b; + limit--; + } + + if (limit == 0 || outPos == outSize) + break; + + b = buf0[inPos++]; + + if (b == 0xE8) + prob = p + prevByte; + else if (b == 0xE9) + prob = p + 256; + else + prob = p + 257; + + IF_BIT_0(prob) + { + UPDATE_0(prob) + prevByte = b; + } + else + { + UInt32 dest; + const Byte *v; + UPDATE_1(prob) + if (b == 0xE8) + { + v = buf1; + if (size1 < 4) + return SZ_ERROR_DATA; + buf1 += 4; + size1 -= 4; + } + else + { + v = buf2; + if (size2 < 4) + return SZ_ERROR_DATA; + buf2 += 4; + size2 -= 4; + } + dest = (((UInt32)v[0] << 24) | ((UInt32)v[1] << 16) | + ((UInt32)v[2] << 8) | ((UInt32)v[3])) - ((UInt32)outPos + 4); + outBuf[outPos++] = (Byte)dest; + if (outPos == outSize) + break; + outBuf[outPos++] = (Byte)(dest >> 8); + if (outPos == outSize) + break; + outBuf[outPos++] = (Byte)(dest >> 16); + if (outPos == outSize) + break; + outBuf[outPos++] = prevByte = (Byte)(dest >> 24); + } + } + return (outPos == outSize) ? SZ_OK : SZ_ERROR_DATA; +} diff --git a/fex/7z_C/Bcj2.h b/fex/7z_C/Bcj2.h index d9d857bc..dbc05414 100644 --- a/fex/7z_C/Bcj2.h +++ b/fex/7z_C/Bcj2.h @@ -1,38 +1,38 @@ -/* Bcj2.h -- Converter for x86 code (BCJ2) -2009-02-07 : Igor Pavlov : Public domain */ - -#ifndef __BCJ2_H -#define __BCJ2_H - -#include "Types.h" - -#ifdef __cplusplus -extern "C" { -#endif - -/* -Conditions: - outSize <= FullOutputSize, - where FullOutputSize is full size of output stream of x86_2 filter. - -If buf0 overlaps outBuf, there are two required conditions: - 1) (buf0 >= outBuf) - 2) (buf0 + size0 >= outBuf + FullOutputSize). - -Returns: - SZ_OK - SZ_ERROR_DATA - Data error -*/ - -int Bcj2_Decode( - const Byte *buf0, SizeT size0, - const Byte *buf1, SizeT size1, - const Byte *buf2, SizeT size2, - const Byte *buf3, SizeT size3, - Byte *outBuf, SizeT outSize); - -#ifdef __cplusplus -} -#endif - -#endif +/* Bcj2.h -- Converter for x86 code (BCJ2) +2009-02-07 : Igor Pavlov : Public domain */ + +#ifndef __BCJ2_H +#define __BCJ2_H + +#include "Types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* +Conditions: + outSize <= FullOutputSize, + where FullOutputSize is full size of output stream of x86_2 filter. + +If buf0 overlaps outBuf, there are two required conditions: + 1) (buf0 >= outBuf) + 2) (buf0 + size0 >= outBuf + FullOutputSize). + +Returns: + SZ_OK + SZ_ERROR_DATA - Data error +*/ + +int Bcj2_Decode( + const Byte *buf0, SizeT size0, + const Byte *buf1, SizeT size1, + const Byte *buf2, SizeT size2, + const Byte *buf3, SizeT size3, + Byte *outBuf, SizeT outSize); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/fex/7z_C/Bra.c b/fex/7z_C/Bra.c index 2a0f147b..2e47b141 100644 --- a/fex/7z_C/Bra.c +++ b/fex/7z_C/Bra.c @@ -1,133 +1,133 @@ -/* Bra.c -- Converters for RISC code -2010-04-16 : Igor Pavlov : Public domain */ - -#include "Bra.h" - -SizeT ARM_Convert(Byte *data, SizeT size, UInt32 ip, int encoding) -{ - SizeT i; - if (size < 4) - return 0; - size -= 4; - ip += 8; - for (i = 0; i <= size; i += 4) - { - if (data[i + 3] == 0xEB) - { - UInt32 dest; - UInt32 src = ((UInt32)data[i + 2] << 16) | ((UInt32)data[i + 1] << 8) | (data[i + 0]); - src <<= 2; - if (encoding) - dest = ip + (UInt32)i + src; - else - dest = src - (ip + (UInt32)i); - dest >>= 2; - data[i + 2] = (Byte)(dest >> 16); - data[i + 1] = (Byte)(dest >> 8); - data[i + 0] = (Byte)dest; - } - } - return i; -} - -SizeT ARMT_Convert(Byte *data, SizeT size, UInt32 ip, int encoding) -{ - SizeT i; - if (size < 4) - return 0; - size -= 4; - ip += 4; - for (i = 0; i <= size; i += 2) - { - if ((data[i + 1] & 0xF8) == 0xF0 && - (data[i + 3] & 0xF8) == 0xF8) - { - UInt32 dest; - UInt32 src = - (((UInt32)data[i + 1] & 0x7) << 19) | - ((UInt32)data[i + 0] << 11) | - (((UInt32)data[i + 3] & 0x7) << 8) | - (data[i + 2]); - - src <<= 1; - if (encoding) - dest = ip + (UInt32)i + src; - else - dest = src - (ip + (UInt32)i); - dest >>= 1; - - data[i + 1] = (Byte)(0xF0 | ((dest >> 19) & 0x7)); - data[i + 0] = (Byte)(dest >> 11); - data[i + 3] = (Byte)(0xF8 | ((dest >> 8) & 0x7)); - data[i + 2] = (Byte)dest; - i += 2; - } - } - return i; -} - -SizeT PPC_Convert(Byte *data, SizeT size, UInt32 ip, int encoding) -{ - SizeT i; - if (size < 4) - return 0; - size -= 4; - for (i = 0; i <= size; i += 4) - { - if ((data[i] >> 2) == 0x12 && (data[i + 3] & 3) == 1) - { - UInt32 src = ((UInt32)(data[i + 0] & 3) << 24) | - ((UInt32)data[i + 1] << 16) | - ((UInt32)data[i + 2] << 8) | - ((UInt32)data[i + 3] & (~3)); - - UInt32 dest; - if (encoding) - dest = ip + (UInt32)i + src; - else - dest = src - (ip + (UInt32)i); - data[i + 0] = (Byte)(0x48 | ((dest >> 24) & 0x3)); - data[i + 1] = (Byte)(dest >> 16); - data[i + 2] = (Byte)(dest >> 8); - data[i + 3] &= 0x3; - data[i + 3] |= dest; - } - } - return i; -} - -SizeT SPARC_Convert(Byte *data, SizeT size, UInt32 ip, int encoding) -{ - UInt32 i; - if (size < 4) - return 0; - size -= 4; - for (i = 0; i <= size; i += 4) - { - if ((data[i] == 0x40 && (data[i + 1] & 0xC0) == 0x00) || - (data[i] == 0x7F && (data[i + 1] & 0xC0) == 0xC0)) - { - UInt32 src = - ((UInt32)data[i + 0] << 24) | - ((UInt32)data[i + 1] << 16) | - ((UInt32)data[i + 2] << 8) | - ((UInt32)data[i + 3]); - UInt32 dest; - - src <<= 2; - if (encoding) - dest = ip + i + src; - else - dest = src - (ip + i); - dest >>= 2; - - dest = (((0 - ((dest >> 22) & 1)) << 22) & 0x3FFFFFFF) | (dest & 0x3FFFFF) | 0x40000000; - - data[i + 0] = (Byte)(dest >> 24); - data[i + 1] = (Byte)(dest >> 16); - data[i + 2] = (Byte)(dest >> 8); - data[i + 3] = (Byte)dest; - } - } - return i; -} +/* Bra.c -- Converters for RISC code +2010-04-16 : Igor Pavlov : Public domain */ + +#include "Bra.h" + +SizeT ARM_Convert(Byte *data, SizeT size, UInt32 ip, int encoding) +{ + SizeT i; + if (size < 4) + return 0; + size -= 4; + ip += 8; + for (i = 0; i <= size; i += 4) + { + if (data[i + 3] == 0xEB) + { + UInt32 dest; + UInt32 src = ((UInt32)data[i + 2] << 16) | ((UInt32)data[i + 1] << 8) | (data[i + 0]); + src <<= 2; + if (encoding) + dest = ip + (UInt32)i + src; + else + dest = src - (ip + (UInt32)i); + dest >>= 2; + data[i + 2] = (Byte)(dest >> 16); + data[i + 1] = (Byte)(dest >> 8); + data[i + 0] = (Byte)dest; + } + } + return i; +} + +SizeT ARMT_Convert(Byte *data, SizeT size, UInt32 ip, int encoding) +{ + SizeT i; + if (size < 4) + return 0; + size -= 4; + ip += 4; + for (i = 0; i <= size; i += 2) + { + if ((data[i + 1] & 0xF8) == 0xF0 && + (data[i + 3] & 0xF8) == 0xF8) + { + UInt32 dest; + UInt32 src = + (((UInt32)data[i + 1] & 0x7) << 19) | + ((UInt32)data[i + 0] << 11) | + (((UInt32)data[i + 3] & 0x7) << 8) | + (data[i + 2]); + + src <<= 1; + if (encoding) + dest = ip + (UInt32)i + src; + else + dest = src - (ip + (UInt32)i); + dest >>= 1; + + data[i + 1] = (Byte)(0xF0 | ((dest >> 19) & 0x7)); + data[i + 0] = (Byte)(dest >> 11); + data[i + 3] = (Byte)(0xF8 | ((dest >> 8) & 0x7)); + data[i + 2] = (Byte)dest; + i += 2; + } + } + return i; +} + +SizeT PPC_Convert(Byte *data, SizeT size, UInt32 ip, int encoding) +{ + SizeT i; + if (size < 4) + return 0; + size -= 4; + for (i = 0; i <= size; i += 4) + { + if ((data[i] >> 2) == 0x12 && (data[i + 3] & 3) == 1) + { + UInt32 src = ((UInt32)(data[i + 0] & 3) << 24) | + ((UInt32)data[i + 1] << 16) | + ((UInt32)data[i + 2] << 8) | + ((UInt32)data[i + 3] & (~3)); + + UInt32 dest; + if (encoding) + dest = ip + (UInt32)i + src; + else + dest = src - (ip + (UInt32)i); + data[i + 0] = (Byte)(0x48 | ((dest >> 24) & 0x3)); + data[i + 1] = (Byte)(dest >> 16); + data[i + 2] = (Byte)(dest >> 8); + data[i + 3] &= 0x3; + data[i + 3] |= dest; + } + } + return i; +} + +SizeT SPARC_Convert(Byte *data, SizeT size, UInt32 ip, int encoding) +{ + UInt32 i; + if (size < 4) + return 0; + size -= 4; + for (i = 0; i <= size; i += 4) + { + if ((data[i] == 0x40 && (data[i + 1] & 0xC0) == 0x00) || + (data[i] == 0x7F && (data[i + 1] & 0xC0) == 0xC0)) + { + UInt32 src = + ((UInt32)data[i + 0] << 24) | + ((UInt32)data[i + 1] << 16) | + ((UInt32)data[i + 2] << 8) | + ((UInt32)data[i + 3]); + UInt32 dest; + + src <<= 2; + if (encoding) + dest = ip + i + src; + else + dest = src - (ip + i); + dest >>= 2; + + dest = (((0 - ((dest >> 22) & 1)) << 22) & 0x3FFFFFFF) | (dest & 0x3FFFFF) | 0x40000000; + + data[i + 0] = (Byte)(dest >> 24); + data[i + 1] = (Byte)(dest >> 16); + data[i + 2] = (Byte)(dest >> 8); + data[i + 3] = (Byte)dest; + } + } + return i; +} diff --git a/fex/7z_C/Bra.h b/fex/7z_C/Bra.h index 9c91e332..5748c1c0 100644 --- a/fex/7z_C/Bra.h +++ b/fex/7z_C/Bra.h @@ -1,68 +1,68 @@ -/* Bra.h -- Branch converters for executables -2009-02-07 : Igor Pavlov : Public domain */ - -#ifndef __BRA_H -#define __BRA_H - -#include "Types.h" - -#ifdef __cplusplus -extern "C" { -#endif - -/* -These functions convert relative addresses to absolute addresses -in CALL instructions to increase the compression ratio. - - In: - data - data buffer - size - size of data - ip - current virtual Instruction Pinter (IP) value - state - state variable for x86 converter - encoding - 0 (for decoding), 1 (for encoding) - - Out: - state - state variable for x86 converter - - Returns: - The number of processed bytes. If you call these functions with multiple calls, - you must start next call with first byte after block of processed bytes. - - Type Endian Alignment LookAhead - - x86 little 1 4 - ARMT little 2 2 - ARM little 4 0 - PPC big 4 0 - SPARC big 4 0 - IA64 little 16 0 - - size must be >= Alignment + LookAhead, if it's not last block. - If (size < Alignment + LookAhead), converter returns 0. - - Example: - - UInt32 ip = 0; - for () - { - ; size must be >= Alignment + LookAhead, if it's not last block - SizeT processed = Convert(data, size, ip, 1); - data += processed; - size -= processed; - ip += processed; - } -*/ - -#define x86_Convert_Init(state) { state = 0; } -SizeT x86_Convert(Byte *data, SizeT size, UInt32 ip, UInt32 *state, int encoding); -SizeT ARM_Convert(Byte *data, SizeT size, UInt32 ip, int encoding); -SizeT ARMT_Convert(Byte *data, SizeT size, UInt32 ip, int encoding); -SizeT PPC_Convert(Byte *data, SizeT size, UInt32 ip, int encoding); -SizeT SPARC_Convert(Byte *data, SizeT size, UInt32 ip, int encoding); -SizeT IA64_Convert(Byte *data, SizeT size, UInt32 ip, int encoding); - -#ifdef __cplusplus -} -#endif - -#endif +/* Bra.h -- Branch converters for executables +2009-02-07 : Igor Pavlov : Public domain */ + +#ifndef __BRA_H +#define __BRA_H + +#include "Types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* +These functions convert relative addresses to absolute addresses +in CALL instructions to increase the compression ratio. + + In: + data - data buffer + size - size of data + ip - current virtual Instruction Pinter (IP) value + state - state variable for x86 converter + encoding - 0 (for decoding), 1 (for encoding) + + Out: + state - state variable for x86 converter + + Returns: + The number of processed bytes. If you call these functions with multiple calls, + you must start next call with first byte after block of processed bytes. + + Type Endian Alignment LookAhead + + x86 little 1 4 + ARMT little 2 2 + ARM little 4 0 + PPC big 4 0 + SPARC big 4 0 + IA64 little 16 0 + + size must be >= Alignment + LookAhead, if it's not last block. + If (size < Alignment + LookAhead), converter returns 0. + + Example: + + UInt32 ip = 0; + for () + { + ; size must be >= Alignment + LookAhead, if it's not last block + SizeT processed = Convert(data, size, ip, 1); + data += processed; + size -= processed; + ip += processed; + } +*/ + +#define x86_Convert_Init(state) { state = 0; } +SizeT x86_Convert(Byte *data, SizeT size, UInt32 ip, UInt32 *state, int encoding); +SizeT ARM_Convert(Byte *data, SizeT size, UInt32 ip, int encoding); +SizeT ARMT_Convert(Byte *data, SizeT size, UInt32 ip, int encoding); +SizeT PPC_Convert(Byte *data, SizeT size, UInt32 ip, int encoding); +SizeT SPARC_Convert(Byte *data, SizeT size, UInt32 ip, int encoding); +SizeT IA64_Convert(Byte *data, SizeT size, UInt32 ip, int encoding); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/fex/7z_C/Bra86.c b/fex/7z_C/Bra86.c index 93566cb2..1ee0e709 100644 --- a/fex/7z_C/Bra86.c +++ b/fex/7z_C/Bra86.c @@ -1,85 +1,85 @@ -/* Bra86.c -- Converter for x86 code (BCJ) -2008-10-04 : Igor Pavlov : Public domain */ - -#include "Bra.h" - -#define Test86MSByte(b) ((b) == 0 || (b) == 0xFF) - -const Byte kMaskToAllowedStatus[8] = {1, 1, 1, 0, 1, 0, 0, 0}; -const Byte kMaskToBitNumber[8] = {0, 1, 2, 2, 3, 3, 3, 3}; - -SizeT x86_Convert(Byte *data, SizeT size, UInt32 ip, UInt32 *state, int encoding) -{ - SizeT bufferPos = 0, prevPosT; - UInt32 prevMask = *state & 0x7; - if (size < 5) - return 0; - ip += 5; - prevPosT = (SizeT)0 - 1; - - for (;;) - { - Byte *p = data + bufferPos; - Byte *limit = data + size - 4; - for (; p < limit; p++) - if ((*p & 0xFE) == 0xE8) - break; - bufferPos = (SizeT)(p - data); - if (p >= limit) - break; - prevPosT = bufferPos - prevPosT; - if (prevPosT > 3) - prevMask = 0; - else - { - prevMask = (prevMask << ((int)prevPosT - 1)) & 0x7; - if (prevMask != 0) - { - Byte b = p[4 - kMaskToBitNumber[prevMask]]; - if (!kMaskToAllowedStatus[prevMask] || Test86MSByte(b)) - { - prevPosT = bufferPos; - prevMask = ((prevMask << 1) & 0x7) | 1; - bufferPos++; - continue; - } - } - } - prevPosT = bufferPos; - - if (Test86MSByte(p[4])) - { - UInt32 src = ((UInt32)p[4] << 24) | ((UInt32)p[3] << 16) | ((UInt32)p[2] << 8) | ((UInt32)p[1]); - UInt32 dest; - for (;;) - { - Byte b; - int index; - if (encoding) - dest = (ip + (UInt32)bufferPos) + src; - else - dest = src - (ip + (UInt32)bufferPos); - if (prevMask == 0) - break; - index = kMaskToBitNumber[prevMask] * 8; - b = (Byte)(dest >> (24 - index)); - if (!Test86MSByte(b)) - break; - src = dest ^ ((1 << (32 - index)) - 1); - } - p[4] = (Byte)(~(((dest >> 24) & 1) - 1)); - p[3] = (Byte)(dest >> 16); - p[2] = (Byte)(dest >> 8); - p[1] = (Byte)dest; - bufferPos += 5; - } - else - { - prevMask = ((prevMask << 1) & 0x7) | 1; - bufferPos++; - } - } - prevPosT = bufferPos - prevPosT; - *state = ((prevPosT > 3) ? 0 : ((prevMask << ((int)prevPosT - 1)) & 0x7)); - return bufferPos; -} +/* Bra86.c -- Converter for x86 code (BCJ) +2008-10-04 : Igor Pavlov : Public domain */ + +#include "Bra.h" + +#define Test86MSByte(b) ((b) == 0 || (b) == 0xFF) + +const Byte kMaskToAllowedStatus[8] = {1, 1, 1, 0, 1, 0, 0, 0}; +const Byte kMaskToBitNumber[8] = {0, 1, 2, 2, 3, 3, 3, 3}; + +SizeT x86_Convert(Byte *data, SizeT size, UInt32 ip, UInt32 *state, int encoding) +{ + SizeT bufferPos = 0, prevPosT; + UInt32 prevMask = *state & 0x7; + if (size < 5) + return 0; + ip += 5; + prevPosT = (SizeT)0 - 1; + + for (;;) + { + Byte *p = data + bufferPos; + Byte *limit = data + size - 4; + for (; p < limit; p++) + if ((*p & 0xFE) == 0xE8) + break; + bufferPos = (SizeT)(p - data); + if (p >= limit) + break; + prevPosT = bufferPos - prevPosT; + if (prevPosT > 3) + prevMask = 0; + else + { + prevMask = (prevMask << ((int)prevPosT - 1)) & 0x7; + if (prevMask != 0) + { + Byte b = p[4 - kMaskToBitNumber[prevMask]]; + if (!kMaskToAllowedStatus[prevMask] || Test86MSByte(b)) + { + prevPosT = bufferPos; + prevMask = ((prevMask << 1) & 0x7) | 1; + bufferPos++; + continue; + } + } + } + prevPosT = bufferPos; + + if (Test86MSByte(p[4])) + { + UInt32 src = ((UInt32)p[4] << 24) | ((UInt32)p[3] << 16) | ((UInt32)p[2] << 8) | ((UInt32)p[1]); + UInt32 dest; + for (;;) + { + Byte b; + int index; + if (encoding) + dest = (ip + (UInt32)bufferPos) + src; + else + dest = src - (ip + (UInt32)bufferPos); + if (prevMask == 0) + break; + index = kMaskToBitNumber[prevMask] * 8; + b = (Byte)(dest >> (24 - index)); + if (!Test86MSByte(b)) + break; + src = dest ^ ((1 << (32 - index)) - 1); + } + p[4] = (Byte)(~(((dest >> 24) & 1) - 1)); + p[3] = (Byte)(dest >> 16); + p[2] = (Byte)(dest >> 8); + p[1] = (Byte)dest; + bufferPos += 5; + } + else + { + prevMask = ((prevMask << 1) & 0x7) | 1; + bufferPos++; + } + } + prevPosT = bufferPos - prevPosT; + *state = ((prevPosT > 3) ? 0 : ((prevMask << ((int)prevPosT - 1)) & 0x7)); + return bufferPos; +} diff --git a/fex/7z_C/CpuArch.c b/fex/7z_C/CpuArch.c index 36e7680d..260cc1f4 100644 --- a/fex/7z_C/CpuArch.c +++ b/fex/7z_C/CpuArch.c @@ -1,168 +1,168 @@ -/* CpuArch.c -- CPU specific code -2010-10-26: Igor Pavlov : Public domain */ - -#include "CpuArch.h" - -#ifdef MY_CPU_X86_OR_AMD64 - -#if (defined(_MSC_VER) && !defined(MY_CPU_AMD64)) || defined(__GNUC__) -#define USE_ASM -#endif - -#if defined(USE_ASM) && !defined(MY_CPU_AMD64) -static UInt32 CheckFlag(UInt32 flag) -{ - #ifdef _MSC_VER - __asm pushfd; - __asm pop EAX; - __asm mov EDX, EAX; - __asm xor EAX, flag; - __asm push EAX; - __asm popfd; - __asm pushfd; - __asm pop EAX; - __asm xor EAX, EDX; - __asm push EDX; - __asm popfd; - __asm and flag, EAX; - #else - __asm__ __volatile__ ( - "pushf\n\t" - "pop %%EAX\n\t" - "movl %%EAX,%%EDX\n\t" - "xorl %0,%%EAX\n\t" - "push %%EAX\n\t" - "popf\n\t" - "pushf\n\t" - "pop %%EAX\n\t" - "xorl %%EDX,%%EAX\n\t" - "push %%EDX\n\t" - "popf\n\t" - "andl %%EAX, %0\n\t": - "=c" (flag) : "c" (flag)); - #endif - return flag; -} -#define CHECK_CPUID_IS_SUPPORTED if (CheckFlag(1 << 18) == 0 || CheckFlag(1 << 21) == 0) return False; -#else -#define CHECK_CPUID_IS_SUPPORTED -#endif - -static void MyCPUID(UInt32 function, UInt32 *a, UInt32 *b, UInt32 *c, UInt32 *d) -{ - #ifdef USE_ASM - - #ifdef _MSC_VER - - UInt32 a2, b2, c2, d2; - __asm xor EBX, EBX; - __asm xor ECX, ECX; - __asm xor EDX, EDX; - __asm mov EAX, function; - __asm cpuid; - __asm mov a2, EAX; - __asm mov b2, EBX; - __asm mov c2, ECX; - __asm mov d2, EDX; - - *a = a2; - *b = b2; - *c = c2; - *d = d2; - - #else - - __asm__ __volatile__ ( - "cpuid" - : "=a" (*a) , - "=b" (*b) , - "=c" (*c) , - "=d" (*d) - : "0" (function)) ; - - #endif - - #else - - int CPUInfo[4]; - __cpuid(CPUInfo, function); - *a = CPUInfo[0]; - *b = CPUInfo[1]; - *c = CPUInfo[2]; - *d = CPUInfo[3]; - - #endif -} - -Bool x86cpuid_CheckAndRead(Cx86cpuid *p) -{ - CHECK_CPUID_IS_SUPPORTED - MyCPUID(0, &p->maxFunc, &p->vendor[0], &p->vendor[2], &p->vendor[1]); - MyCPUID(1, &p->ver, &p->b, &p->c, &p->d); - return True; -} - -static UInt32 kVendors[][3] = -{ - { 0x756E6547, 0x49656E69, 0x6C65746E}, - { 0x68747541, 0x69746E65, 0x444D4163}, - { 0x746E6543, 0x48727561, 0x736C7561} -}; - -int x86cpuid_GetFirm(const Cx86cpuid *p) -{ - unsigned i; - for (i = 0; i < sizeof(kVendors) / sizeof(kVendors[i]); i++) - { - const UInt32 *v = kVendors[i]; - if (v[0] == p->vendor[0] && - v[1] == p->vendor[1] && - v[2] == p->vendor[2]) - return (int)i; - } - return -1; -} - -Bool CPU_Is_InOrder() -{ - Cx86cpuid p; - int firm; - UInt32 family, model; - if (!x86cpuid_CheckAndRead(&p)) - return True; - family = x86cpuid_GetFamily(&p); - model = x86cpuid_GetModel(&p); - firm = x86cpuid_GetFirm(&p); - switch (firm) - { - case CPU_FIRM_INTEL: return (family < 6 || (family == 6 && model == 0x100C)); - case CPU_FIRM_AMD: return (family < 5 || (family == 5 && (model < 6 || model == 0xA))); - case CPU_FIRM_VIA: return (family < 6 || (family == 6 && model < 0xF)); - } - return True; -} - -#if !defined(MY_CPU_AMD64) && defined(_WIN32) -static Bool CPU_Sys_Is_SSE_Supported() -{ - OSVERSIONINFO vi; - vi.dwOSVersionInfoSize = sizeof(vi); - if (!GetVersionEx(&vi)) - return False; - return (vi.dwMajorVersion >= 5); -} -#define CHECK_SYS_SSE_SUPPORT if (!CPU_Sys_Is_SSE_Supported()) return False; -#else -#define CHECK_SYS_SSE_SUPPORT -#endif - -Bool CPU_Is_Aes_Supported() -{ - Cx86cpuid p; - CHECK_SYS_SSE_SUPPORT - if (!x86cpuid_CheckAndRead(&p)) - return False; - return (p.c >> 25) & 1; -} - -#endif +/* CpuArch.c -- CPU specific code +2010-10-26: Igor Pavlov : Public domain */ + +#include "CpuArch.h" + +#ifdef MY_CPU_X86_OR_AMD64 + +#if (defined(_MSC_VER) && !defined(MY_CPU_AMD64)) || defined(__GNUC__) +#define USE_ASM +#endif + +#if defined(USE_ASM) && !defined(MY_CPU_AMD64) +static UInt32 CheckFlag(UInt32 flag) +{ + #ifdef _MSC_VER + __asm pushfd; + __asm pop EAX; + __asm mov EDX, EAX; + __asm xor EAX, flag; + __asm push EAX; + __asm popfd; + __asm pushfd; + __asm pop EAX; + __asm xor EAX, EDX; + __asm push EDX; + __asm popfd; + __asm and flag, EAX; + #else + __asm__ __volatile__ ( + "pushf\n\t" + "pop %%EAX\n\t" + "movl %%EAX,%%EDX\n\t" + "xorl %0,%%EAX\n\t" + "push %%EAX\n\t" + "popf\n\t" + "pushf\n\t" + "pop %%EAX\n\t" + "xorl %%EDX,%%EAX\n\t" + "push %%EDX\n\t" + "popf\n\t" + "andl %%EAX, %0\n\t": + "=c" (flag) : "c" (flag)); + #endif + return flag; +} +#define CHECK_CPUID_IS_SUPPORTED if (CheckFlag(1 << 18) == 0 || CheckFlag(1 << 21) == 0) return False; +#else +#define CHECK_CPUID_IS_SUPPORTED +#endif + +static void MyCPUID(UInt32 function, UInt32 *a, UInt32 *b, UInt32 *c, UInt32 *d) +{ + #ifdef USE_ASM + + #ifdef _MSC_VER + + UInt32 a2, b2, c2, d2; + __asm xor EBX, EBX; + __asm xor ECX, ECX; + __asm xor EDX, EDX; + __asm mov EAX, function; + __asm cpuid; + __asm mov a2, EAX; + __asm mov b2, EBX; + __asm mov c2, ECX; + __asm mov d2, EDX; + + *a = a2; + *b = b2; + *c = c2; + *d = d2; + + #else + + __asm__ __volatile__ ( + "cpuid" + : "=a" (*a) , + "=b" (*b) , + "=c" (*c) , + "=d" (*d) + : "0" (function)) ; + + #endif + + #else + + int CPUInfo[4]; + __cpuid(CPUInfo, function); + *a = CPUInfo[0]; + *b = CPUInfo[1]; + *c = CPUInfo[2]; + *d = CPUInfo[3]; + + #endif +} + +Bool x86cpuid_CheckAndRead(Cx86cpuid *p) +{ + CHECK_CPUID_IS_SUPPORTED + MyCPUID(0, &p->maxFunc, &p->vendor[0], &p->vendor[2], &p->vendor[1]); + MyCPUID(1, &p->ver, &p->b, &p->c, &p->d); + return True; +} + +static UInt32 kVendors[][3] = +{ + { 0x756E6547, 0x49656E69, 0x6C65746E}, + { 0x68747541, 0x69746E65, 0x444D4163}, + { 0x746E6543, 0x48727561, 0x736C7561} +}; + +int x86cpuid_GetFirm(const Cx86cpuid *p) +{ + unsigned i; + for (i = 0; i < sizeof(kVendors) / sizeof(kVendors[i]); i++) + { + const UInt32 *v = kVendors[i]; + if (v[0] == p->vendor[0] && + v[1] == p->vendor[1] && + v[2] == p->vendor[2]) + return (int)i; + } + return -1; +} + +Bool CPU_Is_InOrder() +{ + Cx86cpuid p; + int firm; + UInt32 family, model; + if (!x86cpuid_CheckAndRead(&p)) + return True; + family = x86cpuid_GetFamily(&p); + model = x86cpuid_GetModel(&p); + firm = x86cpuid_GetFirm(&p); + switch (firm) + { + case CPU_FIRM_INTEL: return (family < 6 || (family == 6 && model == 0x100C)); + case CPU_FIRM_AMD: return (family < 5 || (family == 5 && (model < 6 || model == 0xA))); + case CPU_FIRM_VIA: return (family < 6 || (family == 6 && model < 0xF)); + } + return True; +} + +#if !defined(MY_CPU_AMD64) && defined(_WIN32) +static Bool CPU_Sys_Is_SSE_Supported() +{ + OSVERSIONINFO vi; + vi.dwOSVersionInfoSize = sizeof(vi); + if (!GetVersionEx(&vi)) + return False; + return (vi.dwMajorVersion >= 5); +} +#define CHECK_SYS_SSE_SUPPORT if (!CPU_Sys_Is_SSE_Supported()) return False; +#else +#define CHECK_SYS_SSE_SUPPORT +#endif + +Bool CPU_Is_Aes_Supported() +{ + Cx86cpuid p; + CHECK_SYS_SSE_SUPPORT + if (!x86cpuid_CheckAndRead(&p)) + return False; + return (p.c >> 25) & 1; +} + +#endif diff --git a/fex/7z_C/CpuArch.h b/fex/7z_C/CpuArch.h index 0a709bb2..01930c7e 100644 --- a/fex/7z_C/CpuArch.h +++ b/fex/7z_C/CpuArch.h @@ -1,155 +1,155 @@ -/* CpuArch.h -- CPU specific code -2010-10-26: Igor Pavlov : Public domain */ - -#ifndef __CPU_ARCH_H -#define __CPU_ARCH_H - -#include "Types.h" - -EXTERN_C_BEGIN - -/* -MY_CPU_LE means that CPU is LITTLE ENDIAN. -If MY_CPU_LE is not defined, we don't know about that property of platform (it can be LITTLE ENDIAN). - -MY_CPU_LE_UNALIGN means that CPU is LITTLE ENDIAN and CPU supports unaligned memory accesses. -If MY_CPU_LE_UNALIGN is not defined, we don't know about these properties of platform. -*/ - -#if defined(_M_X64) || defined(_M_AMD64) || defined(__x86_64__) -#define MY_CPU_AMD64 -#endif - -#if defined(MY_CPU_AMD64) || defined(_M_IA64) -#define MY_CPU_64BIT -#endif - -#if defined(_M_IX86) || defined(__i386__) -#define MY_CPU_X86 -#endif - -#if defined(MY_CPU_X86) || defined(MY_CPU_AMD64) -#define MY_CPU_X86_OR_AMD64 -#endif - -#if defined(MY_CPU_X86) || defined(_M_ARM) -#define MY_CPU_32BIT -#endif - -#if defined(_WIN32) && defined(_M_ARM) -#define MY_CPU_ARM_LE -#endif - -#if defined(_WIN32) && defined(_M_IA64) -#define MY_CPU_IA64_LE -#endif - -#if defined(MY_CPU_X86_OR_AMD64) -#define MY_CPU_LE_UNALIGN -#endif - -#if defined(MY_CPU_X86_OR_AMD64) || defined(MY_CPU_ARM_LE) || defined(MY_CPU_IA64_LE) || defined(__ARMEL__) || defined(__MIPSEL__) || defined(__LITTLE_ENDIAN__) -#define MY_CPU_LE -#endif - -#if defined(__BIG_ENDIAN__) -#define MY_CPU_BE -#endif - -#if defined(MY_CPU_LE) && defined(MY_CPU_BE) -Stop_Compiling_Bad_Endian -#endif - -#ifdef MY_CPU_LE_UNALIGN - -#define GetUi16(p) (*(const UInt16 *)(p)) -#define GetUi32(p) (*(const UInt32 *)(p)) -#define GetUi64(p) (*(const UInt64 *)(p)) -#define SetUi16(p, d) *(UInt16 *)(p) = (d); -#define SetUi32(p, d) *(UInt32 *)(p) = (d); -#define SetUi64(p, d) *(UInt64 *)(p) = (d); - -#else - -#define GetUi16(p) (((const Byte *)(p))[0] | ((UInt16)((const Byte *)(p))[1] << 8)) - -#define GetUi32(p) ( \ - ((const Byte *)(p))[0] | \ - ((UInt32)((const Byte *)(p))[1] << 8) | \ - ((UInt32)((const Byte *)(p))[2] << 16) | \ - ((UInt32)((const Byte *)(p))[3] << 24)) - -#define GetUi64(p) (GetUi32(p) | ((UInt64)GetUi32(((const Byte *)(p)) + 4) << 32)) - -#define SetUi16(p, d) { UInt32 _x_ = (d); \ - ((Byte *)(p))[0] = (Byte)_x_; \ - ((Byte *)(p))[1] = (Byte)(_x_ >> 8); } - -#define SetUi32(p, d) { UInt32 _x_ = (d); \ - ((Byte *)(p))[0] = (Byte)_x_; \ - ((Byte *)(p))[1] = (Byte)(_x_ >> 8); \ - ((Byte *)(p))[2] = (Byte)(_x_ >> 16); \ - ((Byte *)(p))[3] = (Byte)(_x_ >> 24); } - -#define SetUi64(p, d) { UInt64 _x64_ = (d); \ - SetUi32(p, (UInt32)_x64_); \ - SetUi32(((Byte *)(p)) + 4, (UInt32)(_x64_ >> 32)); } - -#endif - -#if defined(MY_CPU_LE_UNALIGN) && defined(_WIN64) && (_MSC_VER >= 1300) - -#pragma intrinsic(_byteswap_ulong) -#pragma intrinsic(_byteswap_uint64) -#define GetBe32(p) _byteswap_ulong(*(const UInt32 *)(const Byte *)(p)) -#define GetBe64(p) _byteswap_uint64(*(const UInt64 *)(const Byte *)(p)) - -#else - -#define GetBe32(p) ( \ - ((UInt32)((const Byte *)(p))[0] << 24) | \ - ((UInt32)((const Byte *)(p))[1] << 16) | \ - ((UInt32)((const Byte *)(p))[2] << 8) | \ - ((const Byte *)(p))[3] ) - -#define GetBe64(p) (((UInt64)GetBe32(p) << 32) | GetBe32(((const Byte *)(p)) + 4)) - -#endif - -#define GetBe16(p) (((UInt16)((const Byte *)(p))[0] << 8) | ((const Byte *)(p))[1]) - - -#ifdef MY_CPU_X86_OR_AMD64 - -typedef struct -{ - UInt32 maxFunc; - UInt32 vendor[3]; - UInt32 ver; - UInt32 b; - UInt32 c; - UInt32 d; -} Cx86cpuid; - -enum -{ - CPU_FIRM_INTEL, - CPU_FIRM_AMD, - CPU_FIRM_VIA -}; - -Bool x86cpuid_CheckAndRead(Cx86cpuid *p); -int x86cpuid_GetFirm(const Cx86cpuid *p); - -#define x86cpuid_GetFamily(p) (((p)->ver >> 8) & 0xFF00F) -#define x86cpuid_GetModel(p) (((p)->ver >> 4) & 0xF00F) -#define x86cpuid_GetStepping(p) ((p)->ver & 0xF) - -Bool CPU_Is_InOrder(); -Bool CPU_Is_Aes_Supported(); - -#endif - -EXTERN_C_END - -#endif +/* CpuArch.h -- CPU specific code +2010-10-26: Igor Pavlov : Public domain */ + +#ifndef __CPU_ARCH_H +#define __CPU_ARCH_H + +#include "Types.h" + +EXTERN_C_BEGIN + +/* +MY_CPU_LE means that CPU is LITTLE ENDIAN. +If MY_CPU_LE is not defined, we don't know about that property of platform (it can be LITTLE ENDIAN). + +MY_CPU_LE_UNALIGN means that CPU is LITTLE ENDIAN and CPU supports unaligned memory accesses. +If MY_CPU_LE_UNALIGN is not defined, we don't know about these properties of platform. +*/ + +#if defined(_M_X64) || defined(_M_AMD64) || defined(__x86_64__) +#define MY_CPU_AMD64 +#endif + +#if defined(MY_CPU_AMD64) || defined(_M_IA64) +#define MY_CPU_64BIT +#endif + +#if defined(_M_IX86) || defined(__i386__) +#define MY_CPU_X86 +#endif + +#if defined(MY_CPU_X86) || defined(MY_CPU_AMD64) +#define MY_CPU_X86_OR_AMD64 +#endif + +#if defined(MY_CPU_X86) || defined(_M_ARM) +#define MY_CPU_32BIT +#endif + +#if defined(_WIN32) && defined(_M_ARM) +#define MY_CPU_ARM_LE +#endif + +#if defined(_WIN32) && defined(_M_IA64) +#define MY_CPU_IA64_LE +#endif + +#if defined(MY_CPU_X86_OR_AMD64) +#define MY_CPU_LE_UNALIGN +#endif + +#if defined(MY_CPU_X86_OR_AMD64) || defined(MY_CPU_ARM_LE) || defined(MY_CPU_IA64_LE) || defined(__ARMEL__) || defined(__MIPSEL__) || defined(__LITTLE_ENDIAN__) +#define MY_CPU_LE +#endif + +#if defined(__BIG_ENDIAN__) +#define MY_CPU_BE +#endif + +#if defined(MY_CPU_LE) && defined(MY_CPU_BE) +Stop_Compiling_Bad_Endian +#endif + +#ifdef MY_CPU_LE_UNALIGN + +#define GetUi16(p) (*(const UInt16 *)(p)) +#define GetUi32(p) (*(const UInt32 *)(p)) +#define GetUi64(p) (*(const UInt64 *)(p)) +#define SetUi16(p, d) *(UInt16 *)(p) = (d); +#define SetUi32(p, d) *(UInt32 *)(p) = (d); +#define SetUi64(p, d) *(UInt64 *)(p) = (d); + +#else + +#define GetUi16(p) (((const Byte *)(p))[0] | ((UInt16)((const Byte *)(p))[1] << 8)) + +#define GetUi32(p) ( \ + ((const Byte *)(p))[0] | \ + ((UInt32)((const Byte *)(p))[1] << 8) | \ + ((UInt32)((const Byte *)(p))[2] << 16) | \ + ((UInt32)((const Byte *)(p))[3] << 24)) + +#define GetUi64(p) (GetUi32(p) | ((UInt64)GetUi32(((const Byte *)(p)) + 4) << 32)) + +#define SetUi16(p, d) { UInt32 _x_ = (d); \ + ((Byte *)(p))[0] = (Byte)_x_; \ + ((Byte *)(p))[1] = (Byte)(_x_ >> 8); } + +#define SetUi32(p, d) { UInt32 _x_ = (d); \ + ((Byte *)(p))[0] = (Byte)_x_; \ + ((Byte *)(p))[1] = (Byte)(_x_ >> 8); \ + ((Byte *)(p))[2] = (Byte)(_x_ >> 16); \ + ((Byte *)(p))[3] = (Byte)(_x_ >> 24); } + +#define SetUi64(p, d) { UInt64 _x64_ = (d); \ + SetUi32(p, (UInt32)_x64_); \ + SetUi32(((Byte *)(p)) + 4, (UInt32)(_x64_ >> 32)); } + +#endif + +#if defined(MY_CPU_LE_UNALIGN) && defined(_WIN64) && (_MSC_VER >= 1300) + +#pragma intrinsic(_byteswap_ulong) +#pragma intrinsic(_byteswap_uint64) +#define GetBe32(p) _byteswap_ulong(*(const UInt32 *)(const Byte *)(p)) +#define GetBe64(p) _byteswap_uint64(*(const UInt64 *)(const Byte *)(p)) + +#else + +#define GetBe32(p) ( \ + ((UInt32)((const Byte *)(p))[0] << 24) | \ + ((UInt32)((const Byte *)(p))[1] << 16) | \ + ((UInt32)((const Byte *)(p))[2] << 8) | \ + ((const Byte *)(p))[3] ) + +#define GetBe64(p) (((UInt64)GetBe32(p) << 32) | GetBe32(((const Byte *)(p)) + 4)) + +#endif + +#define GetBe16(p) (((UInt16)((const Byte *)(p))[0] << 8) | ((const Byte *)(p))[1]) + + +#ifdef MY_CPU_X86_OR_AMD64 + +typedef struct +{ + UInt32 maxFunc; + UInt32 vendor[3]; + UInt32 ver; + UInt32 b; + UInt32 c; + UInt32 d; +} Cx86cpuid; + +enum +{ + CPU_FIRM_INTEL, + CPU_FIRM_AMD, + CPU_FIRM_VIA +}; + +Bool x86cpuid_CheckAndRead(Cx86cpuid *p); +int x86cpuid_GetFirm(const Cx86cpuid *p); + +#define x86cpuid_GetFamily(p) (((p)->ver >> 8) & 0xFF00F) +#define x86cpuid_GetModel(p) (((p)->ver >> 4) & 0xF00F) +#define x86cpuid_GetStepping(p) ((p)->ver & 0xF) + +Bool CPU_Is_InOrder(); +Bool CPU_Is_Aes_Supported(); + +#endif + +EXTERN_C_END + +#endif diff --git a/fex/7z_C/Lzma2Dec.c b/fex/7z_C/Lzma2Dec.c index 838631d4..f38a5d48 100644 --- a/fex/7z_C/Lzma2Dec.c +++ b/fex/7z_C/Lzma2Dec.c @@ -1,371 +1,371 @@ -/* Lzma2Dec.c -- LZMA2 Decoder -2009-05-03 : Igor Pavlov : Public domain */ - -/* #define SHOW_DEBUG_INFO */ - -#ifdef SHOW_DEBUG_INFO -#include -#endif - -#include - -#include "Lzma2Dec.h" - -/* -00000000 - EOS -00000001 U U - Uncompressed Reset Dic -00000010 U U - Uncompressed No Reset -100uuuuu U U P P - LZMA no reset -101uuuuu U U P P - LZMA reset state -110uuuuu U U P P S - LZMA reset state + new prop -111uuuuu U U P P S - LZMA reset state + new prop + reset dic - - u, U - Unpack Size - P - Pack Size - S - Props -*/ - -#define LZMA2_CONTROL_LZMA (1 << 7) -#define LZMA2_CONTROL_COPY_NO_RESET 2 -#define LZMA2_CONTROL_COPY_RESET_DIC 1 -#define LZMA2_CONTROL_EOF 0 - -#define LZMA2_IS_UNCOMPRESSED_STATE(p) (((p)->control & LZMA2_CONTROL_LZMA) == 0) - -#define LZMA2_GET_LZMA_MODE(p) (((p)->control >> 5) & 3) -#define LZMA2_IS_THERE_PROP(mode) ((mode) >= 2) - -#define LZMA2_LCLP_MAX 4 -#define LZMA2_DIC_SIZE_FROM_PROP(p) (((UInt32)2 | ((p) & 1)) << ((p) / 2 + 11)) - -#ifdef SHOW_DEBUG_INFO -#define PRF(x) x -#else -#define PRF(x) -#endif - -typedef enum -{ - LZMA2_STATE_CONTROL, - LZMA2_STATE_UNPACK0, - LZMA2_STATE_UNPACK1, - LZMA2_STATE_PACK0, - LZMA2_STATE_PACK1, - LZMA2_STATE_PROP, - LZMA2_STATE_DATA, - LZMA2_STATE_DATA_CONT, - LZMA2_STATE_FINISHED, - LZMA2_STATE_ERROR -} ELzma2State; - -static SRes Lzma2Dec_GetOldProps(Byte prop, Byte *props) -{ - UInt32 dicSize; - if (prop > 40) - return SZ_ERROR_UNSUPPORTED; - dicSize = (prop == 40) ? 0xFFFFFFFF : LZMA2_DIC_SIZE_FROM_PROP(prop); - props[0] = (Byte)LZMA2_LCLP_MAX; - props[1] = (Byte)(dicSize); - props[2] = (Byte)(dicSize >> 8); - props[3] = (Byte)(dicSize >> 16); - props[4] = (Byte)(dicSize >> 24); - return SZ_OK; -} - -SRes Lzma2Dec_AllocateProbs(CLzma2Dec *p, Byte prop, ISzAlloc *alloc) -{ - Byte props[LZMA_PROPS_SIZE]; - RINOK(Lzma2Dec_GetOldProps(prop, props)); - return LzmaDec_AllocateProbs(&p->decoder, props, LZMA_PROPS_SIZE, alloc); -} - -SRes Lzma2Dec_Allocate(CLzma2Dec *p, Byte prop, ISzAlloc *alloc) -{ - Byte props[LZMA_PROPS_SIZE]; - RINOK(Lzma2Dec_GetOldProps(prop, props)); - return LzmaDec_Allocate(&p->decoder, props, LZMA_PROPS_SIZE, alloc); -} - -void Lzma2Dec_Init(CLzma2Dec *p) -{ - p->state = LZMA2_STATE_CONTROL; - p->needInitDic = True; - p->needInitState = True; - p->needInitProp = True; - LzmaDec_Init(&p->decoder); -} - -static ELzma2State Lzma2Dec_UpdateState(CLzma2Dec *p, Byte b) -{ - switch(p->state) - { - case LZMA2_STATE_CONTROL: - p->control = b; - PRF(printf("\n %4X ", p->decoder.dicPos)); - PRF(printf(" %2X", b)); - if (p->control == 0) - return LZMA2_STATE_FINISHED; - if (LZMA2_IS_UNCOMPRESSED_STATE(p)) - { - if ((p->control & 0x7F) > 2) - return LZMA2_STATE_ERROR; - p->unpackSize = 0; - } - else - p->unpackSize = (UInt32)(p->control & 0x1F) << 16; - return LZMA2_STATE_UNPACK0; - - case LZMA2_STATE_UNPACK0: - p->unpackSize |= (UInt32)b << 8; - return LZMA2_STATE_UNPACK1; - - case LZMA2_STATE_UNPACK1: - p->unpackSize |= (UInt32)b; - p->unpackSize++; - PRF(printf(" %8d", p->unpackSize)); - return (LZMA2_IS_UNCOMPRESSED_STATE(p)) ? LZMA2_STATE_DATA : LZMA2_STATE_PACK0; - - case LZMA2_STATE_PACK0: - p->packSize = (UInt32)b << 8; - return LZMA2_STATE_PACK1; - - case LZMA2_STATE_PACK1: - p->packSize |= (UInt32)b; - p->packSize++; - PRF(printf(" %8d", p->packSize)); - return LZMA2_IS_THERE_PROP(LZMA2_GET_LZMA_MODE(p)) ? LZMA2_STATE_PROP: - (p->needInitProp ? LZMA2_STATE_ERROR : LZMA2_STATE_DATA); - - case LZMA2_STATE_PROP: - { - int lc, lp; - if (b >= (9 * 5 * 5)) - return LZMA2_STATE_ERROR; - lc = b % 9; - b /= 9; - p->decoder.prop.pb = b / 5; - lp = b % 5; - if (lc + lp > LZMA2_LCLP_MAX) - return LZMA2_STATE_ERROR; - p->decoder.prop.lc = lc; - p->decoder.prop.lp = lp; - p->needInitProp = False; - return LZMA2_STATE_DATA; - } - } - return LZMA2_STATE_ERROR; -} - -static void LzmaDec_UpdateWithUncompressed(CLzmaDec *p, const Byte *src, SizeT size) -{ - memcpy(p->dic + p->dicPos, src, size); - p->dicPos += size; - if (p->checkDicSize == 0 && p->prop.dicSize - p->processedPos <= size) - p->checkDicSize = p->prop.dicSize; - p->processedPos += (UInt32)size; -} - -static -void LzmaDec_InitDicAndState(CLzmaDec *p, Bool initDic, Bool initState) -{ - p->needFlush = 1; - p->remainLen = 0; - p->tempBufSize = 0; - - if (initDic) - { - p->processedPos = 0; - p->checkDicSize = 0; - p->needInitState = 1; - } - if (initState) - p->needInitState = 1; -} - -SRes Lzma2Dec_DecodeToDic(CLzma2Dec *p, SizeT dicLimit, - const Byte *src, SizeT *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status) -{ - SizeT inSize = *srcLen; - *srcLen = 0; - *status = LZMA_STATUS_NOT_SPECIFIED; - - while (p->state != LZMA2_STATE_FINISHED) - { - SizeT dicPos = p->decoder.dicPos; - if (p->state == LZMA2_STATE_ERROR) - return SZ_ERROR_DATA; - if (dicPos == dicLimit && finishMode == LZMA_FINISH_ANY) - { - *status = LZMA_STATUS_NOT_FINISHED; - return SZ_OK; - } - if (p->state != LZMA2_STATE_DATA && p->state != LZMA2_STATE_DATA_CONT) - { - if (*srcLen == inSize) - { - *status = LZMA_STATUS_NEEDS_MORE_INPUT; - return SZ_OK; - } - (*srcLen)++; - p->state = Lzma2Dec_UpdateState(p, *src++); - continue; - } - { - SizeT destSizeCur = dicLimit - dicPos; - SizeT srcSizeCur = inSize - *srcLen; - ELzmaFinishMode curFinishMode = LZMA_FINISH_ANY; - - if (p->unpackSize <= destSizeCur) - { - destSizeCur = (SizeT)p->unpackSize; - curFinishMode = LZMA_FINISH_END; - } - - if (LZMA2_IS_UNCOMPRESSED_STATE(p)) - { - if (*srcLen == inSize) - { - *status = LZMA_STATUS_NEEDS_MORE_INPUT; - return SZ_OK; - } - - if (p->state == LZMA2_STATE_DATA) - { - Bool initDic = (p->control == LZMA2_CONTROL_COPY_RESET_DIC); - if (initDic) - p->needInitProp = p->needInitState = True; - else if (p->needInitDic) - return SZ_ERROR_DATA; - p->needInitDic = False; - LzmaDec_InitDicAndState(&p->decoder, initDic, False); - } - - if (srcSizeCur > destSizeCur) - srcSizeCur = destSizeCur; - - if (srcSizeCur == 0) - return SZ_ERROR_DATA; - - LzmaDec_UpdateWithUncompressed(&p->decoder, src, srcSizeCur); - - src += srcSizeCur; - *srcLen += srcSizeCur; - p->unpackSize -= (UInt32)srcSizeCur; - p->state = (p->unpackSize == 0) ? LZMA2_STATE_CONTROL : LZMA2_STATE_DATA_CONT; - } - else - { - SizeT outSizeProcessed; - SRes res; - - if (p->state == LZMA2_STATE_DATA) - { - int mode = LZMA2_GET_LZMA_MODE(p); - Bool initDic = (mode == 3); - Bool initState = (mode > 0); - if ((!initDic && p->needInitDic) || (!initState && p->needInitState)) - return SZ_ERROR_DATA; - - LzmaDec_InitDicAndState(&p->decoder, initDic, initState); - p->needInitDic = False; - p->needInitState = False; - p->state = LZMA2_STATE_DATA_CONT; - } - if (srcSizeCur > p->packSize) - srcSizeCur = (SizeT)p->packSize; - - res = LzmaDec_DecodeToDic(&p->decoder, dicPos + destSizeCur, src, &srcSizeCur, curFinishMode, status); - - src += srcSizeCur; - *srcLen += srcSizeCur; - p->packSize -= (UInt32)srcSizeCur; - - outSizeProcessed = p->decoder.dicPos - dicPos; - p->unpackSize -= (UInt32)outSizeProcessed; - - RINOK(res); - if (*status == LZMA_STATUS_NEEDS_MORE_INPUT) - return res; - - if (srcSizeCur == 0 && outSizeProcessed == 0) - { - if (*status != LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK || - p->unpackSize != 0 || p->packSize != 0) - return SZ_ERROR_DATA; - p->state = LZMA2_STATE_CONTROL; - } - if (*status == LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK) - *status = LZMA_STATUS_NOT_FINISHED; - } - } - } - *status = LZMA_STATUS_FINISHED_WITH_MARK; - return SZ_OK; -} - -SRes Lzma2Dec_DecodeToBuf(CLzma2Dec *p, Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status) -{ - SizeT outSize = *destLen, inSize = *srcLen; - *srcLen = *destLen = 0; - for (;;) - { - SizeT srcSizeCur = inSize, outSizeCur, dicPos; - ELzmaFinishMode curFinishMode; - SRes res; - if (p->decoder.dicPos == p->decoder.dicBufSize) - p->decoder.dicPos = 0; - dicPos = p->decoder.dicPos; - if (outSize > p->decoder.dicBufSize - dicPos) - { - outSizeCur = p->decoder.dicBufSize; - curFinishMode = LZMA_FINISH_ANY; - } - else - { - outSizeCur = dicPos + outSize; - curFinishMode = finishMode; - } - - res = Lzma2Dec_DecodeToDic(p, outSizeCur, src, &srcSizeCur, curFinishMode, status); - src += srcSizeCur; - inSize -= srcSizeCur; - *srcLen += srcSizeCur; - outSizeCur = p->decoder.dicPos - dicPos; - memcpy(dest, p->decoder.dic + dicPos, outSizeCur); - dest += outSizeCur; - outSize -= outSizeCur; - *destLen += outSizeCur; - if (res != 0) - return res; - if (outSizeCur == 0 || outSize == 0) - return SZ_OK; - } -} - -SRes Lzma2Decode(Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen, - Byte prop, ELzmaFinishMode finishMode, ELzmaStatus *status, ISzAlloc *alloc) -{ - CLzma2Dec decoder; - SRes res; - SizeT outSize = *destLen, inSize = *srcLen; - Byte props[LZMA_PROPS_SIZE]; - - Lzma2Dec_Construct(&decoder); - - *destLen = *srcLen = 0; - *status = LZMA_STATUS_NOT_SPECIFIED; - decoder.decoder.dic = dest; - decoder.decoder.dicBufSize = outSize; - - RINOK(Lzma2Dec_GetOldProps(prop, props)); - RINOK(LzmaDec_AllocateProbs(&decoder.decoder, props, LZMA_PROPS_SIZE, alloc)); - - *srcLen = inSize; - res = Lzma2Dec_DecodeToDic(&decoder, outSize, src, srcLen, finishMode, status); - *destLen = decoder.decoder.dicPos; - if (res == SZ_OK && *status == LZMA_STATUS_NEEDS_MORE_INPUT) - res = SZ_ERROR_INPUT_EOF; - - LzmaDec_FreeProbs(&decoder.decoder, alloc); - return res; -} +/* Lzma2Dec.c -- LZMA2 Decoder +2009-05-03 : Igor Pavlov : Public domain */ + +/* #define SHOW_DEBUG_INFO */ + +#ifdef SHOW_DEBUG_INFO +#include +#endif + +#include + +#include "Lzma2Dec.h" + +/* +00000000 - EOS +00000001 U U - Uncompressed Reset Dic +00000010 U U - Uncompressed No Reset +100uuuuu U U P P - LZMA no reset +101uuuuu U U P P - LZMA reset state +110uuuuu U U P P S - LZMA reset state + new prop +111uuuuu U U P P S - LZMA reset state + new prop + reset dic + + u, U - Unpack Size + P - Pack Size + S - Props +*/ + +#define LZMA2_CONTROL_LZMA (1 << 7) +#define LZMA2_CONTROL_COPY_NO_RESET 2 +#define LZMA2_CONTROL_COPY_RESET_DIC 1 +#define LZMA2_CONTROL_EOF 0 + +#define LZMA2_IS_UNCOMPRESSED_STATE(p) (((p)->control & LZMA2_CONTROL_LZMA) == 0) + +#define LZMA2_GET_LZMA_MODE(p) (((p)->control >> 5) & 3) +#define LZMA2_IS_THERE_PROP(mode) ((mode) >= 2) + +#define LZMA2_LCLP_MAX 4 +#define LZMA2_DIC_SIZE_FROM_PROP(p) (((UInt32)2 | ((p) & 1)) << ((p) / 2 + 11)) + +#ifdef SHOW_DEBUG_INFO +#define PRF(x) x +#else +#define PRF(x) +#endif + +typedef enum +{ + LZMA2_STATE_CONTROL, + LZMA2_STATE_UNPACK0, + LZMA2_STATE_UNPACK1, + LZMA2_STATE_PACK0, + LZMA2_STATE_PACK1, + LZMA2_STATE_PROP, + LZMA2_STATE_DATA, + LZMA2_STATE_DATA_CONT, + LZMA2_STATE_FINISHED, + LZMA2_STATE_ERROR +} ELzma2State; + +static SRes Lzma2Dec_GetOldProps(Byte prop, Byte *props) +{ + UInt32 dicSize; + if (prop > 40) + return SZ_ERROR_UNSUPPORTED; + dicSize = (prop == 40) ? 0xFFFFFFFF : LZMA2_DIC_SIZE_FROM_PROP(prop); + props[0] = (Byte)LZMA2_LCLP_MAX; + props[1] = (Byte)(dicSize); + props[2] = (Byte)(dicSize >> 8); + props[3] = (Byte)(dicSize >> 16); + props[4] = (Byte)(dicSize >> 24); + return SZ_OK; +} + +SRes Lzma2Dec_AllocateProbs(CLzma2Dec *p, Byte prop, ISzAlloc *alloc) +{ + Byte props[LZMA_PROPS_SIZE]; + RINOK(Lzma2Dec_GetOldProps(prop, props)); + return LzmaDec_AllocateProbs(&p->decoder, props, LZMA_PROPS_SIZE, alloc); +} + +SRes Lzma2Dec_Allocate(CLzma2Dec *p, Byte prop, ISzAlloc *alloc) +{ + Byte props[LZMA_PROPS_SIZE]; + RINOK(Lzma2Dec_GetOldProps(prop, props)); + return LzmaDec_Allocate(&p->decoder, props, LZMA_PROPS_SIZE, alloc); +} + +void Lzma2Dec_Init(CLzma2Dec *p) +{ + p->state = LZMA2_STATE_CONTROL; + p->needInitDic = True; + p->needInitState = True; + p->needInitProp = True; + LzmaDec_Init(&p->decoder); +} + +static ELzma2State Lzma2Dec_UpdateState(CLzma2Dec *p, Byte b) +{ + switch(p->state) + { + case LZMA2_STATE_CONTROL: + p->control = b; + PRF(printf("\n %4X ", p->decoder.dicPos)); + PRF(printf(" %2X", b)); + if (p->control == 0) + return LZMA2_STATE_FINISHED; + if (LZMA2_IS_UNCOMPRESSED_STATE(p)) + { + if ((p->control & 0x7F) > 2) + return LZMA2_STATE_ERROR; + p->unpackSize = 0; + } + else + p->unpackSize = (UInt32)(p->control & 0x1F) << 16; + return LZMA2_STATE_UNPACK0; + + case LZMA2_STATE_UNPACK0: + p->unpackSize |= (UInt32)b << 8; + return LZMA2_STATE_UNPACK1; + + case LZMA2_STATE_UNPACK1: + p->unpackSize |= (UInt32)b; + p->unpackSize++; + PRF(printf(" %8d", p->unpackSize)); + return (LZMA2_IS_UNCOMPRESSED_STATE(p)) ? LZMA2_STATE_DATA : LZMA2_STATE_PACK0; + + case LZMA2_STATE_PACK0: + p->packSize = (UInt32)b << 8; + return LZMA2_STATE_PACK1; + + case LZMA2_STATE_PACK1: + p->packSize |= (UInt32)b; + p->packSize++; + PRF(printf(" %8d", p->packSize)); + return LZMA2_IS_THERE_PROP(LZMA2_GET_LZMA_MODE(p)) ? LZMA2_STATE_PROP: + (p->needInitProp ? LZMA2_STATE_ERROR : LZMA2_STATE_DATA); + + case LZMA2_STATE_PROP: + { + int lc, lp; + if (b >= (9 * 5 * 5)) + return LZMA2_STATE_ERROR; + lc = b % 9; + b /= 9; + p->decoder.prop.pb = b / 5; + lp = b % 5; + if (lc + lp > LZMA2_LCLP_MAX) + return LZMA2_STATE_ERROR; + p->decoder.prop.lc = lc; + p->decoder.prop.lp = lp; + p->needInitProp = False; + return LZMA2_STATE_DATA; + } + } + return LZMA2_STATE_ERROR; +} + +static void LzmaDec_UpdateWithUncompressed(CLzmaDec *p, const Byte *src, SizeT size) +{ + memcpy(p->dic + p->dicPos, src, size); + p->dicPos += size; + if (p->checkDicSize == 0 && p->prop.dicSize - p->processedPos <= size) + p->checkDicSize = p->prop.dicSize; + p->processedPos += (UInt32)size; +} + +static +void LzmaDec_InitDicAndState(CLzmaDec *p, Bool initDic, Bool initState) +{ + p->needFlush = 1; + p->remainLen = 0; + p->tempBufSize = 0; + + if (initDic) + { + p->processedPos = 0; + p->checkDicSize = 0; + p->needInitState = 1; + } + if (initState) + p->needInitState = 1; +} + +SRes Lzma2Dec_DecodeToDic(CLzma2Dec *p, SizeT dicLimit, + const Byte *src, SizeT *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status) +{ + SizeT inSize = *srcLen; + *srcLen = 0; + *status = LZMA_STATUS_NOT_SPECIFIED; + + while (p->state != LZMA2_STATE_FINISHED) + { + SizeT dicPos = p->decoder.dicPos; + if (p->state == LZMA2_STATE_ERROR) + return SZ_ERROR_DATA; + if (dicPos == dicLimit && finishMode == LZMA_FINISH_ANY) + { + *status = LZMA_STATUS_NOT_FINISHED; + return SZ_OK; + } + if (p->state != LZMA2_STATE_DATA && p->state != LZMA2_STATE_DATA_CONT) + { + if (*srcLen == inSize) + { + *status = LZMA_STATUS_NEEDS_MORE_INPUT; + return SZ_OK; + } + (*srcLen)++; + p->state = Lzma2Dec_UpdateState(p, *src++); + continue; + } + { + SizeT destSizeCur = dicLimit - dicPos; + SizeT srcSizeCur = inSize - *srcLen; + ELzmaFinishMode curFinishMode = LZMA_FINISH_ANY; + + if (p->unpackSize <= destSizeCur) + { + destSizeCur = (SizeT)p->unpackSize; + curFinishMode = LZMA_FINISH_END; + } + + if (LZMA2_IS_UNCOMPRESSED_STATE(p)) + { + if (*srcLen == inSize) + { + *status = LZMA_STATUS_NEEDS_MORE_INPUT; + return SZ_OK; + } + + if (p->state == LZMA2_STATE_DATA) + { + Bool initDic = (p->control == LZMA2_CONTROL_COPY_RESET_DIC); + if (initDic) + p->needInitProp = p->needInitState = True; + else if (p->needInitDic) + return SZ_ERROR_DATA; + p->needInitDic = False; + LzmaDec_InitDicAndState(&p->decoder, initDic, False); + } + + if (srcSizeCur > destSizeCur) + srcSizeCur = destSizeCur; + + if (srcSizeCur == 0) + return SZ_ERROR_DATA; + + LzmaDec_UpdateWithUncompressed(&p->decoder, src, srcSizeCur); + + src += srcSizeCur; + *srcLen += srcSizeCur; + p->unpackSize -= (UInt32)srcSizeCur; + p->state = (p->unpackSize == 0) ? LZMA2_STATE_CONTROL : LZMA2_STATE_DATA_CONT; + } + else + { + SizeT outSizeProcessed; + SRes res; + + if (p->state == LZMA2_STATE_DATA) + { + int mode = LZMA2_GET_LZMA_MODE(p); + Bool initDic = (mode == 3); + Bool initState = (mode > 0); + if ((!initDic && p->needInitDic) || (!initState && p->needInitState)) + return SZ_ERROR_DATA; + + LzmaDec_InitDicAndState(&p->decoder, initDic, initState); + p->needInitDic = False; + p->needInitState = False; + p->state = LZMA2_STATE_DATA_CONT; + } + if (srcSizeCur > p->packSize) + srcSizeCur = (SizeT)p->packSize; + + res = LzmaDec_DecodeToDic(&p->decoder, dicPos + destSizeCur, src, &srcSizeCur, curFinishMode, status); + + src += srcSizeCur; + *srcLen += srcSizeCur; + p->packSize -= (UInt32)srcSizeCur; + + outSizeProcessed = p->decoder.dicPos - dicPos; + p->unpackSize -= (UInt32)outSizeProcessed; + + RINOK(res); + if (*status == LZMA_STATUS_NEEDS_MORE_INPUT) + return res; + + if (srcSizeCur == 0 && outSizeProcessed == 0) + { + if (*status != LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK || + p->unpackSize != 0 || p->packSize != 0) + return SZ_ERROR_DATA; + p->state = LZMA2_STATE_CONTROL; + } + if (*status == LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK) + *status = LZMA_STATUS_NOT_FINISHED; + } + } + } + *status = LZMA_STATUS_FINISHED_WITH_MARK; + return SZ_OK; +} + +SRes Lzma2Dec_DecodeToBuf(CLzma2Dec *p, Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status) +{ + SizeT outSize = *destLen, inSize = *srcLen; + *srcLen = *destLen = 0; + for (;;) + { + SizeT srcSizeCur = inSize, outSizeCur, dicPos; + ELzmaFinishMode curFinishMode; + SRes res; + if (p->decoder.dicPos == p->decoder.dicBufSize) + p->decoder.dicPos = 0; + dicPos = p->decoder.dicPos; + if (outSize > p->decoder.dicBufSize - dicPos) + { + outSizeCur = p->decoder.dicBufSize; + curFinishMode = LZMA_FINISH_ANY; + } + else + { + outSizeCur = dicPos + outSize; + curFinishMode = finishMode; + } + + res = Lzma2Dec_DecodeToDic(p, outSizeCur, src, &srcSizeCur, curFinishMode, status); + src += srcSizeCur; + inSize -= srcSizeCur; + *srcLen += srcSizeCur; + outSizeCur = p->decoder.dicPos - dicPos; + memcpy(dest, p->decoder.dic + dicPos, outSizeCur); + dest += outSizeCur; + outSize -= outSizeCur; + *destLen += outSizeCur; + if (res != 0) + return res; + if (outSizeCur == 0 || outSize == 0) + return SZ_OK; + } +} + +SRes Lzma2Decode(Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen, + Byte prop, ELzmaFinishMode finishMode, ELzmaStatus *status, ISzAlloc *alloc) +{ + CLzma2Dec decoder; + SRes res; + SizeT outSize = *destLen, inSize = *srcLen; + Byte props[LZMA_PROPS_SIZE]; + + Lzma2Dec_Construct(&decoder); + + *destLen = *srcLen = 0; + *status = LZMA_STATUS_NOT_SPECIFIED; + decoder.decoder.dic = dest; + decoder.decoder.dicBufSize = outSize; + + RINOK(Lzma2Dec_GetOldProps(prop, props)); + RINOK(LzmaDec_AllocateProbs(&decoder.decoder, props, LZMA_PROPS_SIZE, alloc)); + + *srcLen = inSize; + res = Lzma2Dec_DecodeToDic(&decoder, outSize, src, srcLen, finishMode, status); + *destLen = decoder.decoder.dicPos; + if (res == SZ_OK && *status == LZMA_STATUS_NEEDS_MORE_INPUT) + res = SZ_ERROR_INPUT_EOF; + + LzmaDec_FreeProbs(&decoder.decoder, alloc); + return res; +} diff --git a/fex/7z_C/Lzma2Dec.h b/fex/7z_C/Lzma2Dec.h index 827698de..6bc07bbc 100644 --- a/fex/7z_C/Lzma2Dec.h +++ b/fex/7z_C/Lzma2Dec.h @@ -1,84 +1,84 @@ -/* Lzma2Dec.h -- LZMA2 Decoder -2009-05-03 : Igor Pavlov : Public domain */ - -#ifndef __LZMA2_DEC_H -#define __LZMA2_DEC_H - -#include "LzmaDec.h" - -#ifdef __cplusplus -extern "C" { -#endif - -/* ---------- State Interface ---------- */ - -typedef struct -{ - CLzmaDec decoder; - UInt32 packSize; - UInt32 unpackSize; - int state; - Byte control; - Bool needInitDic; - Bool needInitState; - Bool needInitProp; -} CLzma2Dec; - -#define Lzma2Dec_Construct(p) LzmaDec_Construct(&(p)->decoder) -#define Lzma2Dec_FreeProbs(p, alloc) LzmaDec_FreeProbs(&(p)->decoder, alloc); -#define Lzma2Dec_Free(p, alloc) LzmaDec_Free(&(p)->decoder, alloc); - -SRes Lzma2Dec_AllocateProbs(CLzma2Dec *p, Byte prop, ISzAlloc *alloc); -SRes Lzma2Dec_Allocate(CLzma2Dec *p, Byte prop, ISzAlloc *alloc); -void Lzma2Dec_Init(CLzma2Dec *p); - - -/* -finishMode: - It has meaning only if the decoding reaches output limit (*destLen or dicLimit). - LZMA_FINISH_ANY - use smallest number of input bytes - LZMA_FINISH_END - read EndOfStream marker after decoding - -Returns: - SZ_OK - status: - LZMA_STATUS_FINISHED_WITH_MARK - LZMA_STATUS_NOT_FINISHED - LZMA_STATUS_NEEDS_MORE_INPUT - SZ_ERROR_DATA - Data error -*/ - -SRes Lzma2Dec_DecodeToDic(CLzma2Dec *p, SizeT dicLimit, - const Byte *src, SizeT *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status); - -SRes Lzma2Dec_DecodeToBuf(CLzma2Dec *p, Byte *dest, SizeT *destLen, - const Byte *src, SizeT *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status); - - -/* ---------- One Call Interface ---------- */ - -/* -finishMode: - It has meaning only if the decoding reaches output limit (*destLen). - LZMA_FINISH_ANY - use smallest number of input bytes - LZMA_FINISH_END - read EndOfStream marker after decoding - -Returns: - SZ_OK - status: - LZMA_STATUS_FINISHED_WITH_MARK - LZMA_STATUS_NOT_FINISHED - SZ_ERROR_DATA - Data error - SZ_ERROR_MEM - Memory allocation error - SZ_ERROR_UNSUPPORTED - Unsupported properties - SZ_ERROR_INPUT_EOF - It needs more bytes in input buffer (src). -*/ - -SRes Lzma2Decode(Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen, - Byte prop, ELzmaFinishMode finishMode, ELzmaStatus *status, ISzAlloc *alloc); - -#ifdef __cplusplus -} -#endif - -#endif +/* Lzma2Dec.h -- LZMA2 Decoder +2009-05-03 : Igor Pavlov : Public domain */ + +#ifndef __LZMA2_DEC_H +#define __LZMA2_DEC_H + +#include "LzmaDec.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* ---------- State Interface ---------- */ + +typedef struct +{ + CLzmaDec decoder; + UInt32 packSize; + UInt32 unpackSize; + int state; + Byte control; + Bool needInitDic; + Bool needInitState; + Bool needInitProp; +} CLzma2Dec; + +#define Lzma2Dec_Construct(p) LzmaDec_Construct(&(p)->decoder) +#define Lzma2Dec_FreeProbs(p, alloc) LzmaDec_FreeProbs(&(p)->decoder, alloc); +#define Lzma2Dec_Free(p, alloc) LzmaDec_Free(&(p)->decoder, alloc); + +SRes Lzma2Dec_AllocateProbs(CLzma2Dec *p, Byte prop, ISzAlloc *alloc); +SRes Lzma2Dec_Allocate(CLzma2Dec *p, Byte prop, ISzAlloc *alloc); +void Lzma2Dec_Init(CLzma2Dec *p); + + +/* +finishMode: + It has meaning only if the decoding reaches output limit (*destLen or dicLimit). + LZMA_FINISH_ANY - use smallest number of input bytes + LZMA_FINISH_END - read EndOfStream marker after decoding + +Returns: + SZ_OK + status: + LZMA_STATUS_FINISHED_WITH_MARK + LZMA_STATUS_NOT_FINISHED + LZMA_STATUS_NEEDS_MORE_INPUT + SZ_ERROR_DATA - Data error +*/ + +SRes Lzma2Dec_DecodeToDic(CLzma2Dec *p, SizeT dicLimit, + const Byte *src, SizeT *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status); + +SRes Lzma2Dec_DecodeToBuf(CLzma2Dec *p, Byte *dest, SizeT *destLen, + const Byte *src, SizeT *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status); + + +/* ---------- One Call Interface ---------- */ + +/* +finishMode: + It has meaning only if the decoding reaches output limit (*destLen). + LZMA_FINISH_ANY - use smallest number of input bytes + LZMA_FINISH_END - read EndOfStream marker after decoding + +Returns: + SZ_OK + status: + LZMA_STATUS_FINISHED_WITH_MARK + LZMA_STATUS_NOT_FINISHED + SZ_ERROR_DATA - Data error + SZ_ERROR_MEM - Memory allocation error + SZ_ERROR_UNSUPPORTED - Unsupported properties + SZ_ERROR_INPUT_EOF - It needs more bytes in input buffer (src). +*/ + +SRes Lzma2Decode(Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen, + Byte prop, ELzmaFinishMode finishMode, ELzmaStatus *status, ISzAlloc *alloc); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/fex/7z_C/LzmaDec.c b/fex/7z_C/LzmaDec.c index 876a1d86..08966d5e 100644 --- a/fex/7z_C/LzmaDec.c +++ b/fex/7z_C/LzmaDec.c @@ -1,1000 +1,1000 @@ -/* LzmaDec.c -- LZMA Decoder -2009-09-20 : Igor Pavlov : Public domain */ - -#include "LzmaDec.h" - -#include - -#define kNumTopBits 24 -#define kTopValue ((UInt32)1 << kNumTopBits) - -#define kNumBitModelTotalBits 11 -#define kBitModelTotal (1 << kNumBitModelTotalBits) -#define kNumMoveBits 5 - -#define RC_INIT_SIZE 5 - -#define NORMALIZE if (range < kTopValue) { range <<= 8; code = (code << 8) | (*buf++); } - -#define IF_BIT_0(p) ttt = *(p); NORMALIZE; bound = (range >> kNumBitModelTotalBits) * ttt; if (code < bound) -#define UPDATE_0(p) range = bound; *(p) = (CLzmaProb)(ttt + ((kBitModelTotal - ttt) >> kNumMoveBits)); -#define UPDATE_1(p) range -= bound; code -= bound; *(p) = (CLzmaProb)(ttt - (ttt >> kNumMoveBits)); -#define GET_BIT2(p, i, A0, A1) IF_BIT_0(p) \ - { UPDATE_0(p); i = (i + i); A0; } else \ - { UPDATE_1(p); i = (i + i) + 1; A1; } -#define GET_BIT(p, i) GET_BIT2(p, i, ; , ;) - -#define TREE_GET_BIT(probs, i) { GET_BIT((probs + i), i); } -#define TREE_DECODE(probs, limit, i) \ - { i = 1; do { TREE_GET_BIT(probs, i); } while (i < limit); i -= limit; } - -/* #define _LZMA_SIZE_OPT */ - -#ifdef _LZMA_SIZE_OPT -#define TREE_6_DECODE(probs, i) TREE_DECODE(probs, (1 << 6), i) -#else -#define TREE_6_DECODE(probs, i) \ - { i = 1; \ - TREE_GET_BIT(probs, i); \ - TREE_GET_BIT(probs, i); \ - TREE_GET_BIT(probs, i); \ - TREE_GET_BIT(probs, i); \ - TREE_GET_BIT(probs, i); \ - TREE_GET_BIT(probs, i); \ - i -= 0x40; } -#endif - -#define NORMALIZE_CHECK if (range < kTopValue) { if (buf >= bufLimit) return DUMMY_ERROR; range <<= 8; code = (code << 8) | (*buf++); } - -#define IF_BIT_0_CHECK(p) ttt = *(p); NORMALIZE_CHECK; bound = (range >> kNumBitModelTotalBits) * ttt; if (code < bound) -#define UPDATE_0_CHECK range = bound; -#define UPDATE_1_CHECK range -= bound; code -= bound; -#define GET_BIT2_CHECK(p, i, A0, A1) IF_BIT_0_CHECK(p) \ - { UPDATE_0_CHECK; i = (i + i); A0; } else \ - { UPDATE_1_CHECK; i = (i + i) + 1; A1; } -#define GET_BIT_CHECK(p, i) GET_BIT2_CHECK(p, i, ; , ;) -#define TREE_DECODE_CHECK(probs, limit, i) \ - { i = 1; do { GET_BIT_CHECK(probs + i, i) } while (i < limit); i -= limit; } - - -#define kNumPosBitsMax 4 -#define kNumPosStatesMax (1 << kNumPosBitsMax) - -#define kLenNumLowBits 3 -#define kLenNumLowSymbols (1 << kLenNumLowBits) -#define kLenNumMidBits 3 -#define kLenNumMidSymbols (1 << kLenNumMidBits) -#define kLenNumHighBits 8 -#define kLenNumHighSymbols (1 << kLenNumHighBits) - -#define LenChoice 0 -#define LenChoice2 (LenChoice + 1) -#define LenLow (LenChoice2 + 1) -#define LenMid (LenLow + (kNumPosStatesMax << kLenNumLowBits)) -#define LenHigh (LenMid + (kNumPosStatesMax << kLenNumMidBits)) -#define kNumLenProbs (LenHigh + kLenNumHighSymbols) - - -#define kNumStates 12 -#define kNumLitStates 7 - -#define kStartPosModelIndex 4 -#define kEndPosModelIndex 14 -#define kNumFullDistances (1 << (kEndPosModelIndex >> 1)) - -#define kNumPosSlotBits 6 -#define kNumLenToPosStates 4 - -#define kNumAlignBits 4 -#define kAlignTableSize (1 << kNumAlignBits) - -#define kMatchMinLen 2 -#define kMatchSpecLenStart (kMatchMinLen + kLenNumLowSymbols + kLenNumMidSymbols + kLenNumHighSymbols) - -#define IsMatch 0 -#define IsRep (IsMatch + (kNumStates << kNumPosBitsMax)) -#define IsRepG0 (IsRep + kNumStates) -#define IsRepG1 (IsRepG0 + kNumStates) -#define IsRepG2 (IsRepG1 + kNumStates) -#define IsRep0Long (IsRepG2 + kNumStates) -#define PosSlot (IsRep0Long + (kNumStates << kNumPosBitsMax)) -#define SpecPos (PosSlot + (kNumLenToPosStates << kNumPosSlotBits)) -#define Align (SpecPos + kNumFullDistances - kEndPosModelIndex) -#define LenCoder (Align + kAlignTableSize) -#define RepLenCoder (LenCoder + kNumLenProbs) -#define Literal (RepLenCoder + kNumLenProbs) - -#define LZMA_BASE_SIZE 1846 -#define LZMA_LIT_SIZE 768 - -#define LzmaProps_GetNumProbs(p) ((UInt32)LZMA_BASE_SIZE + (LZMA_LIT_SIZE << ((p)->lc + (p)->lp))) - -#if Literal != LZMA_BASE_SIZE -StopCompilingDueBUG -#endif - -#define LZMA_DIC_MIN (1 << 12) - -/* First LZMA-symbol is always decoded. -And it decodes new LZMA-symbols while (buf < bufLimit), but "buf" is without last normalization -Out: - Result: - SZ_OK - OK - SZ_ERROR_DATA - Error - p->remainLen: - < kMatchSpecLenStart : normal remain - = kMatchSpecLenStart : finished - = kMatchSpecLenStart + 1 : Flush marker - = kMatchSpecLenStart + 2 : State Init Marker -*/ - -static int MY_FAST_CALL LzmaDec_DecodeReal(CLzmaDec *p, SizeT limit, const Byte *bufLimit) -{ - CLzmaProb *probs = p->probs; - - unsigned state = p->state; - UInt32 rep0 = p->reps[0], rep1 = p->reps[1], rep2 = p->reps[2], rep3 = p->reps[3]; - unsigned pbMask = ((unsigned)1 << (p->prop.pb)) - 1; - unsigned lpMask = ((unsigned)1 << (p->prop.lp)) - 1; - unsigned lc = p->prop.lc; - - Byte *dic = p->dic; - SizeT dicBufSize = p->dicBufSize; - SizeT dicPos = p->dicPos; - - UInt32 processedPos = p->processedPos; - UInt32 checkDicSize = p->checkDicSize; - unsigned len = 0; - - const Byte *buf = p->buf; - UInt32 range = p->range; - UInt32 code = p->code; - - do - { - CLzmaProb *prob; - UInt32 bound; - unsigned ttt; - unsigned posState = processedPos & pbMask; - - prob = probs + IsMatch + (state << kNumPosBitsMax) + posState; - IF_BIT_0(prob) - { - unsigned symbol; - UPDATE_0(prob); - prob = probs + Literal; - if (checkDicSize != 0 || processedPos != 0) - prob += (LZMA_LIT_SIZE * (((processedPos & lpMask) << lc) + - (dic[(dicPos == 0 ? dicBufSize : dicPos) - 1] >> (8 - lc)))); - - if (state < kNumLitStates) - { - state -= (state < 4) ? state : 3; - symbol = 1; - do { GET_BIT(prob + symbol, symbol) } while (symbol < 0x100); - } - else - { - unsigned matchByte = p->dic[(dicPos - rep0) + ((dicPos < rep0) ? dicBufSize : 0)]; - unsigned offs = 0x100; - state -= (state < 10) ? 3 : 6; - symbol = 1; - do - { - unsigned bit; - CLzmaProb *probLit; - matchByte <<= 1; - bit = (matchByte & offs); - probLit = prob + offs + bit + symbol; - GET_BIT2(probLit, symbol, offs &= ~bit, offs &= bit) - } - while (symbol < 0x100); - } - dic[dicPos++] = (Byte)symbol; - processedPos++; - continue; - } - else - { - UPDATE_1(prob); - prob = probs + IsRep + state; - IF_BIT_0(prob) - { - UPDATE_0(prob); - state += kNumStates; - prob = probs + LenCoder; - } - else - { - UPDATE_1(prob); - if (checkDicSize == 0 && processedPos == 0) - return SZ_ERROR_DATA; - prob = probs + IsRepG0 + state; - IF_BIT_0(prob) - { - UPDATE_0(prob); - prob = probs + IsRep0Long + (state << kNumPosBitsMax) + posState; - IF_BIT_0(prob) - { - UPDATE_0(prob); - dic[dicPos] = dic[(dicPos - rep0) + ((dicPos < rep0) ? dicBufSize : 0)]; - dicPos++; - processedPos++; - state = state < kNumLitStates ? 9 : 11; - continue; - } - UPDATE_1(prob); - } - else - { - UInt32 distance; - UPDATE_1(prob); - prob = probs + IsRepG1 + state; - IF_BIT_0(prob) - { - UPDATE_0(prob); - distance = rep1; - } - else - { - UPDATE_1(prob); - prob = probs + IsRepG2 + state; - IF_BIT_0(prob) - { - UPDATE_0(prob); - distance = rep2; - } - else - { - UPDATE_1(prob); - distance = rep3; - rep3 = rep2; - } - rep2 = rep1; - } - rep1 = rep0; - rep0 = distance; - } - state = state < kNumLitStates ? 8 : 11; - prob = probs + RepLenCoder; - } - { - unsigned limit, offset; - CLzmaProb *probLen = prob + LenChoice; - IF_BIT_0(probLen) - { - UPDATE_0(probLen); - probLen = prob + LenLow + (posState << kLenNumLowBits); - offset = 0; - limit = (1 << kLenNumLowBits); - } - else - { - UPDATE_1(probLen); - probLen = prob + LenChoice2; - IF_BIT_0(probLen) - { - UPDATE_0(probLen); - probLen = prob + LenMid + (posState << kLenNumMidBits); - offset = kLenNumLowSymbols; - limit = (1 << kLenNumMidBits); - } - else - { - UPDATE_1(probLen); - probLen = prob + LenHigh; - offset = kLenNumLowSymbols + kLenNumMidSymbols; - limit = (1 << kLenNumHighBits); - } - } - TREE_DECODE(probLen, limit, len); - len += offset; - } - - if (state >= kNumStates) - { - UInt32 distance; - prob = probs + PosSlot + - ((len < kNumLenToPosStates ? len : kNumLenToPosStates - 1) << kNumPosSlotBits); - TREE_6_DECODE(prob, distance); - if (distance >= kStartPosModelIndex) - { - unsigned posSlot = (unsigned)distance; - int numDirectBits = (int)(((distance >> 1) - 1)); - distance = (2 | (distance & 1)); - if (posSlot < kEndPosModelIndex) - { - distance <<= numDirectBits; - prob = probs + SpecPos + distance - posSlot - 1; - { - UInt32 mask = 1; - unsigned i = 1; - do - { - GET_BIT2(prob + i, i, ; , distance |= mask); - mask <<= 1; - } - while (--numDirectBits != 0); - } - } - else - { - numDirectBits -= kNumAlignBits; - do - { - NORMALIZE - range >>= 1; - - { - UInt32 t; - code -= range; - t = (0 - ((UInt32)code >> 31)); /* (UInt32)((Int32)code >> 31) */ - distance = (distance << 1) + (t + 1); - code += range & t; - } - /* - distance <<= 1; - if (code >= range) - { - code -= range; - distance |= 1; - } - */ - } - while (--numDirectBits != 0); - prob = probs + Align; - distance <<= kNumAlignBits; - { - unsigned i = 1; - GET_BIT2(prob + i, i, ; , distance |= 1); - GET_BIT2(prob + i, i, ; , distance |= 2); - GET_BIT2(prob + i, i, ; , distance |= 4); - GET_BIT2(prob + i, i, ; , distance |= 8); - } - if (distance == (UInt32)0xFFFFFFFF) - { - len += kMatchSpecLenStart; - state -= kNumStates; - break; - } - } - } - rep3 = rep2; - rep2 = rep1; - rep1 = rep0; - rep0 = distance + 1; - if (checkDicSize == 0) - { - if (distance >= processedPos) - return SZ_ERROR_DATA; - } - else if (distance >= checkDicSize) - return SZ_ERROR_DATA; - state = (state < kNumStates + kNumLitStates) ? kNumLitStates : kNumLitStates + 3; - } - - len += kMatchMinLen; - - if (limit == dicPos) - return SZ_ERROR_DATA; - { - SizeT rem = limit - dicPos; - unsigned curLen = ((rem < len) ? (unsigned)rem : len); - SizeT pos = (dicPos - rep0) + ((dicPos < rep0) ? dicBufSize : 0); - - processedPos += curLen; - - len -= curLen; - if (pos + curLen <= dicBufSize) - { - Byte *dest = dic + dicPos; - ptrdiff_t src = (ptrdiff_t)pos - (ptrdiff_t)dicPos; - const Byte *lim = dest + curLen; - dicPos += curLen; - do - *(dest) = (Byte)*(dest + src); - while (++dest != lim); - } - else - { - do - { - dic[dicPos++] = dic[pos]; - if (++pos == dicBufSize) - pos = 0; - } - while (--curLen != 0); - } - } - } - } - while (dicPos < limit && buf < bufLimit); - NORMALIZE; - p->buf = buf; - p->range = range; - p->code = code; - p->remainLen = len; - p->dicPos = dicPos; - p->processedPos = processedPos; - p->reps[0] = rep0; - p->reps[1] = rep1; - p->reps[2] = rep2; - p->reps[3] = rep3; - p->state = state; - - return SZ_OK; -} - -static void MY_FAST_CALL LzmaDec_WriteRem(CLzmaDec *p, SizeT limit) -{ - if (p->remainLen != 0 && p->remainLen < kMatchSpecLenStart) - { - Byte *dic = p->dic; - SizeT dicPos = p->dicPos; - SizeT dicBufSize = p->dicBufSize; - unsigned len = p->remainLen; - UInt32 rep0 = p->reps[0]; - if (limit - dicPos < len) - len = (unsigned)(limit - dicPos); - - if (p->checkDicSize == 0 && p->prop.dicSize - p->processedPos <= len) - p->checkDicSize = p->prop.dicSize; - - p->processedPos += len; - p->remainLen -= len; - while (len-- != 0) - { - dic[dicPos] = dic[(dicPos - rep0) + ((dicPos < rep0) ? dicBufSize : 0)]; - dicPos++; - } - p->dicPos = dicPos; - } -} - -static int MY_FAST_CALL LzmaDec_DecodeReal2(CLzmaDec *p, SizeT limit, const Byte *bufLimit) -{ - do - { - SizeT limit2 = limit; - if (p->checkDicSize == 0) - { - UInt32 rem = p->prop.dicSize - p->processedPos; - if (limit - p->dicPos > rem) - limit2 = p->dicPos + rem; - } - RINOK(LzmaDec_DecodeReal(p, limit2, bufLimit)); - if (p->processedPos >= p->prop.dicSize) - p->checkDicSize = p->prop.dicSize; - LzmaDec_WriteRem(p, limit); - } - while (p->dicPos < limit && p->buf < bufLimit && p->remainLen < kMatchSpecLenStart); - - if (p->remainLen > kMatchSpecLenStart) - { - p->remainLen = kMatchSpecLenStart; - } - return 0; -} - -typedef enum -{ - DUMMY_ERROR, /* unexpected end of input stream */ - DUMMY_LIT, - DUMMY_MATCH, - DUMMY_REP -} ELzmaDummy; - -static ELzmaDummy LzmaDec_TryDummy(const CLzmaDec *p, const Byte *buf, SizeT inSize) -{ - UInt32 range = p->range; - UInt32 code = p->code; - const Byte *bufLimit = buf + inSize; - CLzmaProb *probs = p->probs; - unsigned state = p->state; - ELzmaDummy res; - - { - CLzmaProb *prob; - UInt32 bound; - unsigned ttt; - unsigned posState = (p->processedPos) & ((1 << p->prop.pb) - 1); - - prob = probs + IsMatch + (state << kNumPosBitsMax) + posState; - IF_BIT_0_CHECK(prob) - { - UPDATE_0_CHECK - - /* if (bufLimit - buf >= 7) return DUMMY_LIT; */ - - prob = probs + Literal; - if (p->checkDicSize != 0 || p->processedPos != 0) - prob += (LZMA_LIT_SIZE * - ((((p->processedPos) & ((1 << (p->prop.lp)) - 1)) << p->prop.lc) + - (p->dic[(p->dicPos == 0 ? p->dicBufSize : p->dicPos) - 1] >> (8 - p->prop.lc)))); - - if (state < kNumLitStates) - { - unsigned symbol = 1; - do { GET_BIT_CHECK(prob + symbol, symbol) } while (symbol < 0x100); - } - else - { - unsigned matchByte = p->dic[p->dicPos - p->reps[0] + - ((p->dicPos < p->reps[0]) ? p->dicBufSize : 0)]; - unsigned offs = 0x100; - unsigned symbol = 1; - do - { - unsigned bit; - CLzmaProb *probLit; - matchByte <<= 1; - bit = (matchByte & offs); - probLit = prob + offs + bit + symbol; - GET_BIT2_CHECK(probLit, symbol, offs &= ~bit, offs &= bit) - } - while (symbol < 0x100); - } - res = DUMMY_LIT; - } - else - { - unsigned len; - UPDATE_1_CHECK; - - prob = probs + IsRep + state; - IF_BIT_0_CHECK(prob) - { - UPDATE_0_CHECK; - state = 0; - prob = probs + LenCoder; - res = DUMMY_MATCH; - } - else - { - UPDATE_1_CHECK; - res = DUMMY_REP; - prob = probs + IsRepG0 + state; - IF_BIT_0_CHECK(prob) - { - UPDATE_0_CHECK; - prob = probs + IsRep0Long + (state << kNumPosBitsMax) + posState; - IF_BIT_0_CHECK(prob) - { - UPDATE_0_CHECK; - NORMALIZE_CHECK; - return DUMMY_REP; - } - else - { - UPDATE_1_CHECK; - } - } - else - { - UPDATE_1_CHECK; - prob = probs + IsRepG1 + state; - IF_BIT_0_CHECK(prob) - { - UPDATE_0_CHECK; - } - else - { - UPDATE_1_CHECK; - prob = probs + IsRepG2 + state; - IF_BIT_0_CHECK(prob) - { - UPDATE_0_CHECK; - } - else - { - UPDATE_1_CHECK; - } - } - } - state = kNumStates; - prob = probs + RepLenCoder; - } - { - unsigned limit, offset; - CLzmaProb *probLen = prob + LenChoice; - IF_BIT_0_CHECK(probLen) - { - UPDATE_0_CHECK; - probLen = prob + LenLow + (posState << kLenNumLowBits); - offset = 0; - limit = 1 << kLenNumLowBits; - } - else - { - UPDATE_1_CHECK; - probLen = prob + LenChoice2; - IF_BIT_0_CHECK(probLen) - { - UPDATE_0_CHECK; - probLen = prob + LenMid + (posState << kLenNumMidBits); - offset = kLenNumLowSymbols; - limit = 1 << kLenNumMidBits; - } - else - { - UPDATE_1_CHECK; - probLen = prob + LenHigh; - offset = kLenNumLowSymbols + kLenNumMidSymbols; - limit = 1 << kLenNumHighBits; - } - } - TREE_DECODE_CHECK(probLen, limit, len); - len += offset; - } - - if (state < 4) - { - unsigned posSlot; - prob = probs + PosSlot + - ((len < kNumLenToPosStates ? len : kNumLenToPosStates - 1) << - kNumPosSlotBits); - TREE_DECODE_CHECK(prob, 1 << kNumPosSlotBits, posSlot); - if (posSlot >= kStartPosModelIndex) - { - int numDirectBits = ((posSlot >> 1) - 1); - - /* if (bufLimit - buf >= 8) return DUMMY_MATCH; */ - - if (posSlot < kEndPosModelIndex) - { - prob = probs + SpecPos + ((2 | (posSlot & 1)) << numDirectBits) - posSlot - 1; - } - else - { - numDirectBits -= kNumAlignBits; - do - { - NORMALIZE_CHECK - range >>= 1; - code -= range & (((code - range) >> 31) - 1); - /* if (code >= range) code -= range; */ - } - while (--numDirectBits != 0); - prob = probs + Align; - numDirectBits = kNumAlignBits; - } - { - unsigned i = 1; - do - { - GET_BIT_CHECK(prob + i, i); - } - while (--numDirectBits != 0); - } - } - } - } - } - NORMALIZE_CHECK; - return res; -} - - -static void LzmaDec_InitRc(CLzmaDec *p, const Byte *data) -{ - p->code = ((UInt32)data[1] << 24) | ((UInt32)data[2] << 16) | ((UInt32)data[3] << 8) | ((UInt32)data[4]); - p->range = 0xFFFFFFFF; - p->needFlush = 0; -} - -static -void LzmaDec_InitDicAndState(CLzmaDec *p, Bool initDic, Bool initState) -{ - p->needFlush = 1; - p->remainLen = 0; - p->tempBufSize = 0; - - if (initDic) - { - p->processedPos = 0; - p->checkDicSize = 0; - p->needInitState = 1; - } - if (initState) - p->needInitState = 1; -} - -void LzmaDec_Init(CLzmaDec *p) -{ - p->dicPos = 0; - LzmaDec_InitDicAndState(p, True, True); -} - -static void LzmaDec_InitStateReal(CLzmaDec *p) -{ - UInt32 numProbs = Literal + ((UInt32)LZMA_LIT_SIZE << (p->prop.lc + p->prop.lp)); - UInt32 i; - CLzmaProb *probs = p->probs; - for (i = 0; i < numProbs; i++) - probs[i] = kBitModelTotal >> 1; - p->reps[0] = p->reps[1] = p->reps[2] = p->reps[3] = 1; - p->state = 0; - p->needInitState = 0; -} - -SRes LzmaDec_DecodeToDic(CLzmaDec *p, SizeT dicLimit, const Byte *src, SizeT *srcLen, - ELzmaFinishMode finishMode, ELzmaStatus *status) -{ - SizeT inSize = *srcLen; - (*srcLen) = 0; - LzmaDec_WriteRem(p, dicLimit); - - *status = LZMA_STATUS_NOT_SPECIFIED; - - while (p->remainLen != kMatchSpecLenStart) - { - int checkEndMarkNow; - - if (p->needFlush != 0) - { - for (; inSize > 0 && p->tempBufSize < RC_INIT_SIZE; (*srcLen)++, inSize--) - p->tempBuf[p->tempBufSize++] = *src++; - if (p->tempBufSize < RC_INIT_SIZE) - { - *status = LZMA_STATUS_NEEDS_MORE_INPUT; - return SZ_OK; - } - if (p->tempBuf[0] != 0) - return SZ_ERROR_DATA; - - LzmaDec_InitRc(p, p->tempBuf); - p->tempBufSize = 0; - } - - checkEndMarkNow = 0; - if (p->dicPos >= dicLimit) - { - if (p->remainLen == 0 && p->code == 0) - { - *status = LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK; - return SZ_OK; - } - if (finishMode == LZMA_FINISH_ANY) - { - *status = LZMA_STATUS_NOT_FINISHED; - return SZ_OK; - } - if (p->remainLen != 0) - { - *status = LZMA_STATUS_NOT_FINISHED; - return SZ_ERROR_DATA; - } - checkEndMarkNow = 1; - } - - if (p->needInitState) - LzmaDec_InitStateReal(p); - - if (p->tempBufSize == 0) - { - SizeT processed; - const Byte *bufLimit; - if (inSize < LZMA_REQUIRED_INPUT_MAX || checkEndMarkNow) - { - int dummyRes = LzmaDec_TryDummy(p, src, inSize); - if (dummyRes == DUMMY_ERROR) - { - memcpy(p->tempBuf, src, inSize); - p->tempBufSize = (unsigned)inSize; - (*srcLen) += inSize; - *status = LZMA_STATUS_NEEDS_MORE_INPUT; - return SZ_OK; - } - if (checkEndMarkNow && dummyRes != DUMMY_MATCH) - { - *status = LZMA_STATUS_NOT_FINISHED; - return SZ_ERROR_DATA; - } - bufLimit = src; - } - else - bufLimit = src + inSize - LZMA_REQUIRED_INPUT_MAX; - p->buf = src; - if (LzmaDec_DecodeReal2(p, dicLimit, bufLimit) != 0) - return SZ_ERROR_DATA; - processed = (SizeT)(p->buf - src); - (*srcLen) += processed; - src += processed; - inSize -= processed; - } - else - { - unsigned rem = p->tempBufSize, lookAhead = 0; - while (rem < LZMA_REQUIRED_INPUT_MAX && lookAhead < inSize) - p->tempBuf[rem++] = src[lookAhead++]; - p->tempBufSize = rem; - if (rem < LZMA_REQUIRED_INPUT_MAX || checkEndMarkNow) - { - int dummyRes = LzmaDec_TryDummy(p, p->tempBuf, rem); - if (dummyRes == DUMMY_ERROR) - { - (*srcLen) += lookAhead; - *status = LZMA_STATUS_NEEDS_MORE_INPUT; - return SZ_OK; - } - if (checkEndMarkNow && dummyRes != DUMMY_MATCH) - { - *status = LZMA_STATUS_NOT_FINISHED; - return SZ_ERROR_DATA; - } - } - p->buf = p->tempBuf; - if (LzmaDec_DecodeReal2(p, dicLimit, p->buf) != 0) - return SZ_ERROR_DATA; - lookAhead -= (rem - (unsigned)(p->buf - p->tempBuf)); - (*srcLen) += lookAhead; - src += lookAhead; - inSize -= lookAhead; - p->tempBufSize = 0; - } - } - if (p->code == 0) - *status = LZMA_STATUS_FINISHED_WITH_MARK; - return (p->code == 0) ? SZ_OK : SZ_ERROR_DATA; -} - -SRes LzmaDec_DecodeToBuf(CLzmaDec *p, Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status) -{ - SizeT outSize = *destLen; - SizeT inSize = *srcLen; - *srcLen = *destLen = 0; - for (;;) - { - SizeT inSizeCur = inSize, outSizeCur, dicPos; - ELzmaFinishMode curFinishMode; - SRes res; - if (p->dicPos == p->dicBufSize) - p->dicPos = 0; - dicPos = p->dicPos; - if (outSize > p->dicBufSize - dicPos) - { - outSizeCur = p->dicBufSize; - curFinishMode = LZMA_FINISH_ANY; - } - else - { - outSizeCur = dicPos + outSize; - curFinishMode = finishMode; - } - - res = LzmaDec_DecodeToDic(p, outSizeCur, src, &inSizeCur, curFinishMode, status); - src += inSizeCur; - inSize -= inSizeCur; - *srcLen += inSizeCur; - outSizeCur = p->dicPos - dicPos; - memcpy(dest, p->dic + dicPos, outSizeCur); - dest += outSizeCur; - outSize -= outSizeCur; - *destLen += outSizeCur; - if (res != 0) - return res; - if (outSizeCur == 0 || outSize == 0) - return SZ_OK; - } -} - -void LzmaDec_FreeProbs(CLzmaDec *p, ISzAlloc *alloc) -{ - alloc->Free(alloc, p->probs); - p->probs = 0; -} - -static void LzmaDec_FreeDict(CLzmaDec *p, ISzAlloc *alloc) -{ - alloc->Free(alloc, p->dic); - p->dic = 0; -} - -void LzmaDec_Free(CLzmaDec *p, ISzAlloc *alloc) -{ - LzmaDec_FreeProbs(p, alloc); - LzmaDec_FreeDict(p, alloc); -} - -SRes LzmaProps_Decode(CLzmaProps *p, const Byte *data, unsigned size) -{ - UInt32 dicSize; - Byte d; - - if (size < LZMA_PROPS_SIZE) - return SZ_ERROR_UNSUPPORTED; - else - dicSize = data[1] | ((UInt32)data[2] << 8) | ((UInt32)data[3] << 16) | ((UInt32)data[4] << 24); - - if (dicSize < LZMA_DIC_MIN) - dicSize = LZMA_DIC_MIN; - p->dicSize = dicSize; - - d = data[0]; - if (d >= (9 * 5 * 5)) - return SZ_ERROR_UNSUPPORTED; - - p->lc = d % 9; - d /= 9; - p->pb = d / 5; - p->lp = d % 5; - - return SZ_OK; -} - -static SRes LzmaDec_AllocateProbs2(CLzmaDec *p, const CLzmaProps *propNew, ISzAlloc *alloc) -{ - UInt32 numProbs = LzmaProps_GetNumProbs(propNew); - if (p->probs == 0 || numProbs != p->numProbs) - { - LzmaDec_FreeProbs(p, alloc); - p->probs = (CLzmaProb *)alloc->Alloc(alloc, numProbs * sizeof(CLzmaProb)); - p->numProbs = numProbs; - if (p->probs == 0) - return SZ_ERROR_MEM; - } - return SZ_OK; -} - -SRes LzmaDec_AllocateProbs(CLzmaDec *p, const Byte *props, unsigned propsSize, ISzAlloc *alloc) -{ - CLzmaProps propNew; - RINOK(LzmaProps_Decode(&propNew, props, propsSize)); - RINOK(LzmaDec_AllocateProbs2(p, &propNew, alloc)); - p->prop = propNew; - return SZ_OK; -} - -SRes LzmaDec_Allocate(CLzmaDec *p, const Byte *props, unsigned propsSize, ISzAlloc *alloc) -{ - CLzmaProps propNew; - SizeT dicBufSize; - RINOK(LzmaProps_Decode(&propNew, props, propsSize)); - RINOK(LzmaDec_AllocateProbs2(p, &propNew, alloc)); - dicBufSize = propNew.dicSize; - if (p->dic == 0 || dicBufSize != p->dicBufSize) - { - LzmaDec_FreeDict(p, alloc); - p->dic = (Byte *)alloc->Alloc(alloc, dicBufSize); - if (p->dic == 0) - { - LzmaDec_FreeProbs(p, alloc); - return SZ_ERROR_MEM; - } - } - p->dicBufSize = dicBufSize; - p->prop = propNew; - return SZ_OK; -} - -SRes LzmaDecode(Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen, - const Byte *propData, unsigned propSize, ELzmaFinishMode finishMode, - ELzmaStatus *status, ISzAlloc *alloc) -{ - CLzmaDec p; - SRes res; - SizeT inSize = *srcLen; - SizeT outSize = *destLen; - *srcLen = *destLen = 0; - if (inSize < RC_INIT_SIZE) - return SZ_ERROR_INPUT_EOF; - - LzmaDec_Construct(&p); - res = LzmaDec_AllocateProbs(&p, propData, propSize, alloc); - if (res != 0) - return res; - p.dic = dest; - p.dicBufSize = outSize; - - LzmaDec_Init(&p); - - *srcLen = inSize; - res = LzmaDec_DecodeToDic(&p, outSize, src, srcLen, finishMode, status); - - if (res == SZ_OK && *status == LZMA_STATUS_NEEDS_MORE_INPUT) - res = SZ_ERROR_INPUT_EOF; - - (*destLen) = p.dicPos; - LzmaDec_FreeProbs(&p, alloc); - return res; -} +/* LzmaDec.c -- LZMA Decoder +2009-09-20 : Igor Pavlov : Public domain */ + +#include "LzmaDec.h" + +#include + +#define kNumTopBits 24 +#define kTopValue ((UInt32)1 << kNumTopBits) + +#define kNumBitModelTotalBits 11 +#define kBitModelTotal (1 << kNumBitModelTotalBits) +#define kNumMoveBits 5 + +#define RC_INIT_SIZE 5 + +#define NORMALIZE if (range < kTopValue) { range <<= 8; code = (code << 8) | (*buf++); } + +#define IF_BIT_0(p) ttt = *(p); NORMALIZE; bound = (range >> kNumBitModelTotalBits) * ttt; if (code < bound) +#define UPDATE_0(p) range = bound; *(p) = (CLzmaProb)(ttt + ((kBitModelTotal - ttt) >> kNumMoveBits)); +#define UPDATE_1(p) range -= bound; code -= bound; *(p) = (CLzmaProb)(ttt - (ttt >> kNumMoveBits)); +#define GET_BIT2(p, i, A0, A1) IF_BIT_0(p) \ + { UPDATE_0(p); i = (i + i); A0; } else \ + { UPDATE_1(p); i = (i + i) + 1; A1; } +#define GET_BIT(p, i) GET_BIT2(p, i, ; , ;) + +#define TREE_GET_BIT(probs, i) { GET_BIT((probs + i), i); } +#define TREE_DECODE(probs, limit, i) \ + { i = 1; do { TREE_GET_BIT(probs, i); } while (i < limit); i -= limit; } + +/* #define _LZMA_SIZE_OPT */ + +#ifdef _LZMA_SIZE_OPT +#define TREE_6_DECODE(probs, i) TREE_DECODE(probs, (1 << 6), i) +#else +#define TREE_6_DECODE(probs, i) \ + { i = 1; \ + TREE_GET_BIT(probs, i); \ + TREE_GET_BIT(probs, i); \ + TREE_GET_BIT(probs, i); \ + TREE_GET_BIT(probs, i); \ + TREE_GET_BIT(probs, i); \ + TREE_GET_BIT(probs, i); \ + i -= 0x40; } +#endif + +#define NORMALIZE_CHECK if (range < kTopValue) { if (buf >= bufLimit) return DUMMY_ERROR; range <<= 8; code = (code << 8) | (*buf++); } + +#define IF_BIT_0_CHECK(p) ttt = *(p); NORMALIZE_CHECK; bound = (range >> kNumBitModelTotalBits) * ttt; if (code < bound) +#define UPDATE_0_CHECK range = bound; +#define UPDATE_1_CHECK range -= bound; code -= bound; +#define GET_BIT2_CHECK(p, i, A0, A1) IF_BIT_0_CHECK(p) \ + { UPDATE_0_CHECK; i = (i + i); A0; } else \ + { UPDATE_1_CHECK; i = (i + i) + 1; A1; } +#define GET_BIT_CHECK(p, i) GET_BIT2_CHECK(p, i, ; , ;) +#define TREE_DECODE_CHECK(probs, limit, i) \ + { i = 1; do { GET_BIT_CHECK(probs + i, i) } while (i < limit); i -= limit; } + + +#define kNumPosBitsMax 4 +#define kNumPosStatesMax (1 << kNumPosBitsMax) + +#define kLenNumLowBits 3 +#define kLenNumLowSymbols (1 << kLenNumLowBits) +#define kLenNumMidBits 3 +#define kLenNumMidSymbols (1 << kLenNumMidBits) +#define kLenNumHighBits 8 +#define kLenNumHighSymbols (1 << kLenNumHighBits) + +#define LenChoice 0 +#define LenChoice2 (LenChoice + 1) +#define LenLow (LenChoice2 + 1) +#define LenMid (LenLow + (kNumPosStatesMax << kLenNumLowBits)) +#define LenHigh (LenMid + (kNumPosStatesMax << kLenNumMidBits)) +#define kNumLenProbs (LenHigh + kLenNumHighSymbols) + + +#define kNumStates 12 +#define kNumLitStates 7 + +#define kStartPosModelIndex 4 +#define kEndPosModelIndex 14 +#define kNumFullDistances (1 << (kEndPosModelIndex >> 1)) + +#define kNumPosSlotBits 6 +#define kNumLenToPosStates 4 + +#define kNumAlignBits 4 +#define kAlignTableSize (1 << kNumAlignBits) + +#define kMatchMinLen 2 +#define kMatchSpecLenStart (kMatchMinLen + kLenNumLowSymbols + kLenNumMidSymbols + kLenNumHighSymbols) + +#define IsMatch 0 +#define IsRep (IsMatch + (kNumStates << kNumPosBitsMax)) +#define IsRepG0 (IsRep + kNumStates) +#define IsRepG1 (IsRepG0 + kNumStates) +#define IsRepG2 (IsRepG1 + kNumStates) +#define IsRep0Long (IsRepG2 + kNumStates) +#define PosSlot (IsRep0Long + (kNumStates << kNumPosBitsMax)) +#define SpecPos (PosSlot + (kNumLenToPosStates << kNumPosSlotBits)) +#define Align (SpecPos + kNumFullDistances - kEndPosModelIndex) +#define LenCoder (Align + kAlignTableSize) +#define RepLenCoder (LenCoder + kNumLenProbs) +#define Literal (RepLenCoder + kNumLenProbs) + +#define LZMA_BASE_SIZE 1846 +#define LZMA_LIT_SIZE 768 + +#define LzmaProps_GetNumProbs(p) ((UInt32)LZMA_BASE_SIZE + (LZMA_LIT_SIZE << ((p)->lc + (p)->lp))) + +#if Literal != LZMA_BASE_SIZE +StopCompilingDueBUG +#endif + +#define LZMA_DIC_MIN (1 << 12) + +/* First LZMA-symbol is always decoded. +And it decodes new LZMA-symbols while (buf < bufLimit), but "buf" is without last normalization +Out: + Result: + SZ_OK - OK + SZ_ERROR_DATA - Error + p->remainLen: + < kMatchSpecLenStart : normal remain + = kMatchSpecLenStart : finished + = kMatchSpecLenStart + 1 : Flush marker + = kMatchSpecLenStart + 2 : State Init Marker +*/ + +static int MY_FAST_CALL LzmaDec_DecodeReal(CLzmaDec *p, SizeT limit, const Byte *bufLimit) +{ + CLzmaProb *probs = p->probs; + + unsigned state = p->state; + UInt32 rep0 = p->reps[0], rep1 = p->reps[1], rep2 = p->reps[2], rep3 = p->reps[3]; + unsigned pbMask = ((unsigned)1 << (p->prop.pb)) - 1; + unsigned lpMask = ((unsigned)1 << (p->prop.lp)) - 1; + unsigned lc = p->prop.lc; + + Byte *dic = p->dic; + SizeT dicBufSize = p->dicBufSize; + SizeT dicPos = p->dicPos; + + UInt32 processedPos = p->processedPos; + UInt32 checkDicSize = p->checkDicSize; + unsigned len = 0; + + const Byte *buf = p->buf; + UInt32 range = p->range; + UInt32 code = p->code; + + do + { + CLzmaProb *prob; + UInt32 bound; + unsigned ttt; + unsigned posState = processedPos & pbMask; + + prob = probs + IsMatch + (state << kNumPosBitsMax) + posState; + IF_BIT_0(prob) + { + unsigned symbol; + UPDATE_0(prob); + prob = probs + Literal; + if (checkDicSize != 0 || processedPos != 0) + prob += (LZMA_LIT_SIZE * (((processedPos & lpMask) << lc) + + (dic[(dicPos == 0 ? dicBufSize : dicPos) - 1] >> (8 - lc)))); + + if (state < kNumLitStates) + { + state -= (state < 4) ? state : 3; + symbol = 1; + do { GET_BIT(prob + symbol, symbol) } while (symbol < 0x100); + } + else + { + unsigned matchByte = p->dic[(dicPos - rep0) + ((dicPos < rep0) ? dicBufSize : 0)]; + unsigned offs = 0x100; + state -= (state < 10) ? 3 : 6; + symbol = 1; + do + { + unsigned bit; + CLzmaProb *probLit; + matchByte <<= 1; + bit = (matchByte & offs); + probLit = prob + offs + bit + symbol; + GET_BIT2(probLit, symbol, offs &= ~bit, offs &= bit) + } + while (symbol < 0x100); + } + dic[dicPos++] = (Byte)symbol; + processedPos++; + continue; + } + else + { + UPDATE_1(prob); + prob = probs + IsRep + state; + IF_BIT_0(prob) + { + UPDATE_0(prob); + state += kNumStates; + prob = probs + LenCoder; + } + else + { + UPDATE_1(prob); + if (checkDicSize == 0 && processedPos == 0) + return SZ_ERROR_DATA; + prob = probs + IsRepG0 + state; + IF_BIT_0(prob) + { + UPDATE_0(prob); + prob = probs + IsRep0Long + (state << kNumPosBitsMax) + posState; + IF_BIT_0(prob) + { + UPDATE_0(prob); + dic[dicPos] = dic[(dicPos - rep0) + ((dicPos < rep0) ? dicBufSize : 0)]; + dicPos++; + processedPos++; + state = state < kNumLitStates ? 9 : 11; + continue; + } + UPDATE_1(prob); + } + else + { + UInt32 distance; + UPDATE_1(prob); + prob = probs + IsRepG1 + state; + IF_BIT_0(prob) + { + UPDATE_0(prob); + distance = rep1; + } + else + { + UPDATE_1(prob); + prob = probs + IsRepG2 + state; + IF_BIT_0(prob) + { + UPDATE_0(prob); + distance = rep2; + } + else + { + UPDATE_1(prob); + distance = rep3; + rep3 = rep2; + } + rep2 = rep1; + } + rep1 = rep0; + rep0 = distance; + } + state = state < kNumLitStates ? 8 : 11; + prob = probs + RepLenCoder; + } + { + unsigned limit, offset; + CLzmaProb *probLen = prob + LenChoice; + IF_BIT_0(probLen) + { + UPDATE_0(probLen); + probLen = prob + LenLow + (posState << kLenNumLowBits); + offset = 0; + limit = (1 << kLenNumLowBits); + } + else + { + UPDATE_1(probLen); + probLen = prob + LenChoice2; + IF_BIT_0(probLen) + { + UPDATE_0(probLen); + probLen = prob + LenMid + (posState << kLenNumMidBits); + offset = kLenNumLowSymbols; + limit = (1 << kLenNumMidBits); + } + else + { + UPDATE_1(probLen); + probLen = prob + LenHigh; + offset = kLenNumLowSymbols + kLenNumMidSymbols; + limit = (1 << kLenNumHighBits); + } + } + TREE_DECODE(probLen, limit, len); + len += offset; + } + + if (state >= kNumStates) + { + UInt32 distance; + prob = probs + PosSlot + + ((len < kNumLenToPosStates ? len : kNumLenToPosStates - 1) << kNumPosSlotBits); + TREE_6_DECODE(prob, distance); + if (distance >= kStartPosModelIndex) + { + unsigned posSlot = (unsigned)distance; + int numDirectBits = (int)(((distance >> 1) - 1)); + distance = (2 | (distance & 1)); + if (posSlot < kEndPosModelIndex) + { + distance <<= numDirectBits; + prob = probs + SpecPos + distance - posSlot - 1; + { + UInt32 mask = 1; + unsigned i = 1; + do + { + GET_BIT2(prob + i, i, ; , distance |= mask); + mask <<= 1; + } + while (--numDirectBits != 0); + } + } + else + { + numDirectBits -= kNumAlignBits; + do + { + NORMALIZE + range >>= 1; + + { + UInt32 t; + code -= range; + t = (0 - ((UInt32)code >> 31)); /* (UInt32)((Int32)code >> 31) */ + distance = (distance << 1) + (t + 1); + code += range & t; + } + /* + distance <<= 1; + if (code >= range) + { + code -= range; + distance |= 1; + } + */ + } + while (--numDirectBits != 0); + prob = probs + Align; + distance <<= kNumAlignBits; + { + unsigned i = 1; + GET_BIT2(prob + i, i, ; , distance |= 1); + GET_BIT2(prob + i, i, ; , distance |= 2); + GET_BIT2(prob + i, i, ; , distance |= 4); + GET_BIT2(prob + i, i, ; , distance |= 8); + } + if (distance == (UInt32)0xFFFFFFFF) + { + len += kMatchSpecLenStart; + state -= kNumStates; + break; + } + } + } + rep3 = rep2; + rep2 = rep1; + rep1 = rep0; + rep0 = distance + 1; + if (checkDicSize == 0) + { + if (distance >= processedPos) + return SZ_ERROR_DATA; + } + else if (distance >= checkDicSize) + return SZ_ERROR_DATA; + state = (state < kNumStates + kNumLitStates) ? kNumLitStates : kNumLitStates + 3; + } + + len += kMatchMinLen; + + if (limit == dicPos) + return SZ_ERROR_DATA; + { + SizeT rem = limit - dicPos; + unsigned curLen = ((rem < len) ? (unsigned)rem : len); + SizeT pos = (dicPos - rep0) + ((dicPos < rep0) ? dicBufSize : 0); + + processedPos += curLen; + + len -= curLen; + if (pos + curLen <= dicBufSize) + { + Byte *dest = dic + dicPos; + ptrdiff_t src = (ptrdiff_t)pos - (ptrdiff_t)dicPos; + const Byte *lim = dest + curLen; + dicPos += curLen; + do + *(dest) = (Byte)*(dest + src); + while (++dest != lim); + } + else + { + do + { + dic[dicPos++] = dic[pos]; + if (++pos == dicBufSize) + pos = 0; + } + while (--curLen != 0); + } + } + } + } + while (dicPos < limit && buf < bufLimit); + NORMALIZE; + p->buf = buf; + p->range = range; + p->code = code; + p->remainLen = len; + p->dicPos = dicPos; + p->processedPos = processedPos; + p->reps[0] = rep0; + p->reps[1] = rep1; + p->reps[2] = rep2; + p->reps[3] = rep3; + p->state = state; + + return SZ_OK; +} + +static void MY_FAST_CALL LzmaDec_WriteRem(CLzmaDec *p, SizeT limit) +{ + if (p->remainLen != 0 && p->remainLen < kMatchSpecLenStart) + { + Byte *dic = p->dic; + SizeT dicPos = p->dicPos; + SizeT dicBufSize = p->dicBufSize; + unsigned len = p->remainLen; + UInt32 rep0 = p->reps[0]; + if (limit - dicPos < len) + len = (unsigned)(limit - dicPos); + + if (p->checkDicSize == 0 && p->prop.dicSize - p->processedPos <= len) + p->checkDicSize = p->prop.dicSize; + + p->processedPos += len; + p->remainLen -= len; + while (len-- != 0) + { + dic[dicPos] = dic[(dicPos - rep0) + ((dicPos < rep0) ? dicBufSize : 0)]; + dicPos++; + } + p->dicPos = dicPos; + } +} + +static int MY_FAST_CALL LzmaDec_DecodeReal2(CLzmaDec *p, SizeT limit, const Byte *bufLimit) +{ + do + { + SizeT limit2 = limit; + if (p->checkDicSize == 0) + { + UInt32 rem = p->prop.dicSize - p->processedPos; + if (limit - p->dicPos > rem) + limit2 = p->dicPos + rem; + } + RINOK(LzmaDec_DecodeReal(p, limit2, bufLimit)); + if (p->processedPos >= p->prop.dicSize) + p->checkDicSize = p->prop.dicSize; + LzmaDec_WriteRem(p, limit); + } + while (p->dicPos < limit && p->buf < bufLimit && p->remainLen < kMatchSpecLenStart); + + if (p->remainLen > kMatchSpecLenStart) + { + p->remainLen = kMatchSpecLenStart; + } + return 0; +} + +typedef enum +{ + DUMMY_ERROR, /* unexpected end of input stream */ + DUMMY_LIT, + DUMMY_MATCH, + DUMMY_REP +} ELzmaDummy; + +static ELzmaDummy LzmaDec_TryDummy(const CLzmaDec *p, const Byte *buf, SizeT inSize) +{ + UInt32 range = p->range; + UInt32 code = p->code; + const Byte *bufLimit = buf + inSize; + CLzmaProb *probs = p->probs; + unsigned state = p->state; + ELzmaDummy res; + + { + CLzmaProb *prob; + UInt32 bound; + unsigned ttt; + unsigned posState = (p->processedPos) & ((1 << p->prop.pb) - 1); + + prob = probs + IsMatch + (state << kNumPosBitsMax) + posState; + IF_BIT_0_CHECK(prob) + { + UPDATE_0_CHECK + + /* if (bufLimit - buf >= 7) return DUMMY_LIT; */ + + prob = probs + Literal; + if (p->checkDicSize != 0 || p->processedPos != 0) + prob += (LZMA_LIT_SIZE * + ((((p->processedPos) & ((1 << (p->prop.lp)) - 1)) << p->prop.lc) + + (p->dic[(p->dicPos == 0 ? p->dicBufSize : p->dicPos) - 1] >> (8 - p->prop.lc)))); + + if (state < kNumLitStates) + { + unsigned symbol = 1; + do { GET_BIT_CHECK(prob + symbol, symbol) } while (symbol < 0x100); + } + else + { + unsigned matchByte = p->dic[p->dicPos - p->reps[0] + + ((p->dicPos < p->reps[0]) ? p->dicBufSize : 0)]; + unsigned offs = 0x100; + unsigned symbol = 1; + do + { + unsigned bit; + CLzmaProb *probLit; + matchByte <<= 1; + bit = (matchByte & offs); + probLit = prob + offs + bit + symbol; + GET_BIT2_CHECK(probLit, symbol, offs &= ~bit, offs &= bit) + } + while (symbol < 0x100); + } + res = DUMMY_LIT; + } + else + { + unsigned len; + UPDATE_1_CHECK; + + prob = probs + IsRep + state; + IF_BIT_0_CHECK(prob) + { + UPDATE_0_CHECK; + state = 0; + prob = probs + LenCoder; + res = DUMMY_MATCH; + } + else + { + UPDATE_1_CHECK; + res = DUMMY_REP; + prob = probs + IsRepG0 + state; + IF_BIT_0_CHECK(prob) + { + UPDATE_0_CHECK; + prob = probs + IsRep0Long + (state << kNumPosBitsMax) + posState; + IF_BIT_0_CHECK(prob) + { + UPDATE_0_CHECK; + NORMALIZE_CHECK; + return DUMMY_REP; + } + else + { + UPDATE_1_CHECK; + } + } + else + { + UPDATE_1_CHECK; + prob = probs + IsRepG1 + state; + IF_BIT_0_CHECK(prob) + { + UPDATE_0_CHECK; + } + else + { + UPDATE_1_CHECK; + prob = probs + IsRepG2 + state; + IF_BIT_0_CHECK(prob) + { + UPDATE_0_CHECK; + } + else + { + UPDATE_1_CHECK; + } + } + } + state = kNumStates; + prob = probs + RepLenCoder; + } + { + unsigned limit, offset; + CLzmaProb *probLen = prob + LenChoice; + IF_BIT_0_CHECK(probLen) + { + UPDATE_0_CHECK; + probLen = prob + LenLow + (posState << kLenNumLowBits); + offset = 0; + limit = 1 << kLenNumLowBits; + } + else + { + UPDATE_1_CHECK; + probLen = prob + LenChoice2; + IF_BIT_0_CHECK(probLen) + { + UPDATE_0_CHECK; + probLen = prob + LenMid + (posState << kLenNumMidBits); + offset = kLenNumLowSymbols; + limit = 1 << kLenNumMidBits; + } + else + { + UPDATE_1_CHECK; + probLen = prob + LenHigh; + offset = kLenNumLowSymbols + kLenNumMidSymbols; + limit = 1 << kLenNumHighBits; + } + } + TREE_DECODE_CHECK(probLen, limit, len); + len += offset; + } + + if (state < 4) + { + unsigned posSlot; + prob = probs + PosSlot + + ((len < kNumLenToPosStates ? len : kNumLenToPosStates - 1) << + kNumPosSlotBits); + TREE_DECODE_CHECK(prob, 1 << kNumPosSlotBits, posSlot); + if (posSlot >= kStartPosModelIndex) + { + int numDirectBits = ((posSlot >> 1) - 1); + + /* if (bufLimit - buf >= 8) return DUMMY_MATCH; */ + + if (posSlot < kEndPosModelIndex) + { + prob = probs + SpecPos + ((2 | (posSlot & 1)) << numDirectBits) - posSlot - 1; + } + else + { + numDirectBits -= kNumAlignBits; + do + { + NORMALIZE_CHECK + range >>= 1; + code -= range & (((code - range) >> 31) - 1); + /* if (code >= range) code -= range; */ + } + while (--numDirectBits != 0); + prob = probs + Align; + numDirectBits = kNumAlignBits; + } + { + unsigned i = 1; + do + { + GET_BIT_CHECK(prob + i, i); + } + while (--numDirectBits != 0); + } + } + } + } + } + NORMALIZE_CHECK; + return res; +} + + +static void LzmaDec_InitRc(CLzmaDec *p, const Byte *data) +{ + p->code = ((UInt32)data[1] << 24) | ((UInt32)data[2] << 16) | ((UInt32)data[3] << 8) | ((UInt32)data[4]); + p->range = 0xFFFFFFFF; + p->needFlush = 0; +} + +static +void LzmaDec_InitDicAndState(CLzmaDec *p, Bool initDic, Bool initState) +{ + p->needFlush = 1; + p->remainLen = 0; + p->tempBufSize = 0; + + if (initDic) + { + p->processedPos = 0; + p->checkDicSize = 0; + p->needInitState = 1; + } + if (initState) + p->needInitState = 1; +} + +void LzmaDec_Init(CLzmaDec *p) +{ + p->dicPos = 0; + LzmaDec_InitDicAndState(p, True, True); +} + +static void LzmaDec_InitStateReal(CLzmaDec *p) +{ + UInt32 numProbs = Literal + ((UInt32)LZMA_LIT_SIZE << (p->prop.lc + p->prop.lp)); + UInt32 i; + CLzmaProb *probs = p->probs; + for (i = 0; i < numProbs; i++) + probs[i] = kBitModelTotal >> 1; + p->reps[0] = p->reps[1] = p->reps[2] = p->reps[3] = 1; + p->state = 0; + p->needInitState = 0; +} + +SRes LzmaDec_DecodeToDic(CLzmaDec *p, SizeT dicLimit, const Byte *src, SizeT *srcLen, + ELzmaFinishMode finishMode, ELzmaStatus *status) +{ + SizeT inSize = *srcLen; + (*srcLen) = 0; + LzmaDec_WriteRem(p, dicLimit); + + *status = LZMA_STATUS_NOT_SPECIFIED; + + while (p->remainLen != kMatchSpecLenStart) + { + int checkEndMarkNow; + + if (p->needFlush != 0) + { + for (; inSize > 0 && p->tempBufSize < RC_INIT_SIZE; (*srcLen)++, inSize--) + p->tempBuf[p->tempBufSize++] = *src++; + if (p->tempBufSize < RC_INIT_SIZE) + { + *status = LZMA_STATUS_NEEDS_MORE_INPUT; + return SZ_OK; + } + if (p->tempBuf[0] != 0) + return SZ_ERROR_DATA; + + LzmaDec_InitRc(p, p->tempBuf); + p->tempBufSize = 0; + } + + checkEndMarkNow = 0; + if (p->dicPos >= dicLimit) + { + if (p->remainLen == 0 && p->code == 0) + { + *status = LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK; + return SZ_OK; + } + if (finishMode == LZMA_FINISH_ANY) + { + *status = LZMA_STATUS_NOT_FINISHED; + return SZ_OK; + } + if (p->remainLen != 0) + { + *status = LZMA_STATUS_NOT_FINISHED; + return SZ_ERROR_DATA; + } + checkEndMarkNow = 1; + } + + if (p->needInitState) + LzmaDec_InitStateReal(p); + + if (p->tempBufSize == 0) + { + SizeT processed; + const Byte *bufLimit; + if (inSize < LZMA_REQUIRED_INPUT_MAX || checkEndMarkNow) + { + int dummyRes = LzmaDec_TryDummy(p, src, inSize); + if (dummyRes == DUMMY_ERROR) + { + memcpy(p->tempBuf, src, inSize); + p->tempBufSize = (unsigned)inSize; + (*srcLen) += inSize; + *status = LZMA_STATUS_NEEDS_MORE_INPUT; + return SZ_OK; + } + if (checkEndMarkNow && dummyRes != DUMMY_MATCH) + { + *status = LZMA_STATUS_NOT_FINISHED; + return SZ_ERROR_DATA; + } + bufLimit = src; + } + else + bufLimit = src + inSize - LZMA_REQUIRED_INPUT_MAX; + p->buf = src; + if (LzmaDec_DecodeReal2(p, dicLimit, bufLimit) != 0) + return SZ_ERROR_DATA; + processed = (SizeT)(p->buf - src); + (*srcLen) += processed; + src += processed; + inSize -= processed; + } + else + { + unsigned rem = p->tempBufSize, lookAhead = 0; + while (rem < LZMA_REQUIRED_INPUT_MAX && lookAhead < inSize) + p->tempBuf[rem++] = src[lookAhead++]; + p->tempBufSize = rem; + if (rem < LZMA_REQUIRED_INPUT_MAX || checkEndMarkNow) + { + int dummyRes = LzmaDec_TryDummy(p, p->tempBuf, rem); + if (dummyRes == DUMMY_ERROR) + { + (*srcLen) += lookAhead; + *status = LZMA_STATUS_NEEDS_MORE_INPUT; + return SZ_OK; + } + if (checkEndMarkNow && dummyRes != DUMMY_MATCH) + { + *status = LZMA_STATUS_NOT_FINISHED; + return SZ_ERROR_DATA; + } + } + p->buf = p->tempBuf; + if (LzmaDec_DecodeReal2(p, dicLimit, p->buf) != 0) + return SZ_ERROR_DATA; + lookAhead -= (rem - (unsigned)(p->buf - p->tempBuf)); + (*srcLen) += lookAhead; + src += lookAhead; + inSize -= lookAhead; + p->tempBufSize = 0; + } + } + if (p->code == 0) + *status = LZMA_STATUS_FINISHED_WITH_MARK; + return (p->code == 0) ? SZ_OK : SZ_ERROR_DATA; +} + +SRes LzmaDec_DecodeToBuf(CLzmaDec *p, Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status) +{ + SizeT outSize = *destLen; + SizeT inSize = *srcLen; + *srcLen = *destLen = 0; + for (;;) + { + SizeT inSizeCur = inSize, outSizeCur, dicPos; + ELzmaFinishMode curFinishMode; + SRes res; + if (p->dicPos == p->dicBufSize) + p->dicPos = 0; + dicPos = p->dicPos; + if (outSize > p->dicBufSize - dicPos) + { + outSizeCur = p->dicBufSize; + curFinishMode = LZMA_FINISH_ANY; + } + else + { + outSizeCur = dicPos + outSize; + curFinishMode = finishMode; + } + + res = LzmaDec_DecodeToDic(p, outSizeCur, src, &inSizeCur, curFinishMode, status); + src += inSizeCur; + inSize -= inSizeCur; + *srcLen += inSizeCur; + outSizeCur = p->dicPos - dicPos; + memcpy(dest, p->dic + dicPos, outSizeCur); + dest += outSizeCur; + outSize -= outSizeCur; + *destLen += outSizeCur; + if (res != 0) + return res; + if (outSizeCur == 0 || outSize == 0) + return SZ_OK; + } +} + +void LzmaDec_FreeProbs(CLzmaDec *p, ISzAlloc *alloc) +{ + alloc->Free(alloc, p->probs); + p->probs = 0; +} + +static void LzmaDec_FreeDict(CLzmaDec *p, ISzAlloc *alloc) +{ + alloc->Free(alloc, p->dic); + p->dic = 0; +} + +void LzmaDec_Free(CLzmaDec *p, ISzAlloc *alloc) +{ + LzmaDec_FreeProbs(p, alloc); + LzmaDec_FreeDict(p, alloc); +} + +SRes LzmaProps_Decode(CLzmaProps *p, const Byte *data, unsigned size) +{ + UInt32 dicSize; + Byte d; + + if (size < LZMA_PROPS_SIZE) + return SZ_ERROR_UNSUPPORTED; + else + dicSize = data[1] | ((UInt32)data[2] << 8) | ((UInt32)data[3] << 16) | ((UInt32)data[4] << 24); + + if (dicSize < LZMA_DIC_MIN) + dicSize = LZMA_DIC_MIN; + p->dicSize = dicSize; + + d = data[0]; + if (d >= (9 * 5 * 5)) + return SZ_ERROR_UNSUPPORTED; + + p->lc = d % 9; + d /= 9; + p->pb = d / 5; + p->lp = d % 5; + + return SZ_OK; +} + +static SRes LzmaDec_AllocateProbs2(CLzmaDec *p, const CLzmaProps *propNew, ISzAlloc *alloc) +{ + UInt32 numProbs = LzmaProps_GetNumProbs(propNew); + if (p->probs == 0 || numProbs != p->numProbs) + { + LzmaDec_FreeProbs(p, alloc); + p->probs = (CLzmaProb *)alloc->Alloc(alloc, numProbs * sizeof(CLzmaProb)); + p->numProbs = numProbs; + if (p->probs == 0) + return SZ_ERROR_MEM; + } + return SZ_OK; +} + +SRes LzmaDec_AllocateProbs(CLzmaDec *p, const Byte *props, unsigned propsSize, ISzAlloc *alloc) +{ + CLzmaProps propNew; + RINOK(LzmaProps_Decode(&propNew, props, propsSize)); + RINOK(LzmaDec_AllocateProbs2(p, &propNew, alloc)); + p->prop = propNew; + return SZ_OK; +} + +SRes LzmaDec_Allocate(CLzmaDec *p, const Byte *props, unsigned propsSize, ISzAlloc *alloc) +{ + CLzmaProps propNew; + SizeT dicBufSize; + RINOK(LzmaProps_Decode(&propNew, props, propsSize)); + RINOK(LzmaDec_AllocateProbs2(p, &propNew, alloc)); + dicBufSize = propNew.dicSize; + if (p->dic == 0 || dicBufSize != p->dicBufSize) + { + LzmaDec_FreeDict(p, alloc); + p->dic = (Byte *)alloc->Alloc(alloc, dicBufSize); + if (p->dic == 0) + { + LzmaDec_FreeProbs(p, alloc); + return SZ_ERROR_MEM; + } + } + p->dicBufSize = dicBufSize; + p->prop = propNew; + return SZ_OK; +} + +SRes LzmaDecode(Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen, + const Byte *propData, unsigned propSize, ELzmaFinishMode finishMode, + ELzmaStatus *status, ISzAlloc *alloc) +{ + CLzmaDec p; + SRes res; + SizeT inSize = *srcLen; + SizeT outSize = *destLen; + *srcLen = *destLen = 0; + if (inSize < RC_INIT_SIZE) + return SZ_ERROR_INPUT_EOF; + + LzmaDec_Construct(&p); + res = LzmaDec_AllocateProbs(&p, propData, propSize, alloc); + if (res != 0) + return res; + p.dic = dest; + p.dicBufSize = outSize; + + LzmaDec_Init(&p); + + *srcLen = inSize; + res = LzmaDec_DecodeToDic(&p, outSize, src, srcLen, finishMode, status); + + if (res == SZ_OK && *status == LZMA_STATUS_NEEDS_MORE_INPUT) + res = SZ_ERROR_INPUT_EOF; + + (*destLen) = p.dicPos; + LzmaDec_FreeProbs(&p, alloc); + return res; +} diff --git a/fex/7z_C/LzmaDec.h b/fex/7z_C/LzmaDec.h index 6741a644..bf7f084b 100644 --- a/fex/7z_C/LzmaDec.h +++ b/fex/7z_C/LzmaDec.h @@ -1,231 +1,231 @@ -/* LzmaDec.h -- LZMA Decoder -2009-02-07 : Igor Pavlov : Public domain */ - -#ifndef __LZMA_DEC_H -#define __LZMA_DEC_H - -#include "Types.h" - -#ifdef __cplusplus -extern "C" { -#endif - -/* #define _LZMA_PROB32 */ -/* _LZMA_PROB32 can increase the speed on some CPUs, - but memory usage for CLzmaDec::probs will be doubled in that case */ - -#ifdef _LZMA_PROB32 -#define CLzmaProb UInt32 -#else -#define CLzmaProb UInt16 -#endif - - -/* ---------- LZMA Properties ---------- */ - -#define LZMA_PROPS_SIZE 5 - -typedef struct _CLzmaProps -{ - unsigned lc, lp, pb; - UInt32 dicSize; -} CLzmaProps; - -/* LzmaProps_Decode - decodes properties -Returns: - SZ_OK - SZ_ERROR_UNSUPPORTED - Unsupported properties -*/ - -SRes LzmaProps_Decode(CLzmaProps *p, const Byte *data, unsigned size); - - -/* ---------- LZMA Decoder state ---------- */ - -/* LZMA_REQUIRED_INPUT_MAX = number of required input bytes for worst case. - Num bits = log2((2^11 / 31) ^ 22) + 26 < 134 + 26 = 160; */ - -#define LZMA_REQUIRED_INPUT_MAX 20 - -typedef struct -{ - CLzmaProps prop; - CLzmaProb *probs; - Byte *dic; - const Byte *buf; - UInt32 range, code; - SizeT dicPos; - SizeT dicBufSize; - UInt32 processedPos; - UInt32 checkDicSize; - unsigned state; - UInt32 reps[4]; - unsigned remainLen; - int needFlush; - int needInitState; - UInt32 numProbs; - unsigned tempBufSize; - Byte tempBuf[LZMA_REQUIRED_INPUT_MAX]; -} CLzmaDec; - -#define LzmaDec_Construct(p) { (p)->dic = 0; (p)->probs = 0; } - -void LzmaDec_Init(CLzmaDec *p); - -/* There are two types of LZMA streams: - 0) Stream with end mark. That end mark adds about 6 bytes to compressed size. - 1) Stream without end mark. You must know exact uncompressed size to decompress such stream. */ - -typedef enum -{ - LZMA_FINISH_ANY, /* finish at any point */ - LZMA_FINISH_END /* block must be finished at the end */ -} ELzmaFinishMode; - -/* ELzmaFinishMode has meaning only if the decoding reaches output limit !!! - - You must use LZMA_FINISH_END, when you know that current output buffer - covers last bytes of block. In other cases you must use LZMA_FINISH_ANY. - - If LZMA decoder sees end marker before reaching output limit, it returns SZ_OK, - and output value of destLen will be less than output buffer size limit. - You can check status result also. - - You can use multiple checks to test data integrity after full decompression: - 1) Check Result and "status" variable. - 2) Check that output(destLen) = uncompressedSize, if you know real uncompressedSize. - 3) Check that output(srcLen) = compressedSize, if you know real compressedSize. - You must use correct finish mode in that case. */ - -typedef enum -{ - LZMA_STATUS_NOT_SPECIFIED, /* use main error code instead */ - LZMA_STATUS_FINISHED_WITH_MARK, /* stream was finished with end mark. */ - LZMA_STATUS_NOT_FINISHED, /* stream was not finished */ - LZMA_STATUS_NEEDS_MORE_INPUT, /* you must provide more input bytes */ - LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK /* there is probability that stream was finished without end mark */ -} ELzmaStatus; - -/* ELzmaStatus is used only as output value for function call */ - - -/* ---------- Interfaces ---------- */ - -/* There are 3 levels of interfaces: - 1) Dictionary Interface - 2) Buffer Interface - 3) One Call Interface - You can select any of these interfaces, but don't mix functions from different - groups for same object. */ - - -/* There are two variants to allocate state for Dictionary Interface: - 1) LzmaDec_Allocate / LzmaDec_Free - 2) LzmaDec_AllocateProbs / LzmaDec_FreeProbs - You can use variant 2, if you set dictionary buffer manually. - For Buffer Interface you must always use variant 1. - -LzmaDec_Allocate* can return: - SZ_OK - SZ_ERROR_MEM - Memory allocation error - SZ_ERROR_UNSUPPORTED - Unsupported properties -*/ - -SRes LzmaDec_AllocateProbs(CLzmaDec *p, const Byte *props, unsigned propsSize, ISzAlloc *alloc); -void LzmaDec_FreeProbs(CLzmaDec *p, ISzAlloc *alloc); - -SRes LzmaDec_Allocate(CLzmaDec *state, const Byte *prop, unsigned propsSize, ISzAlloc *alloc); -void LzmaDec_Free(CLzmaDec *state, ISzAlloc *alloc); - -/* ---------- Dictionary Interface ---------- */ - -/* You can use it, if you want to eliminate the overhead for data copying from - dictionary to some other external buffer. - You must work with CLzmaDec variables directly in this interface. - - STEPS: - LzmaDec_Constr() - LzmaDec_Allocate() - for (each new stream) - { - LzmaDec_Init() - while (it needs more decompression) - { - LzmaDec_DecodeToDic() - use data from CLzmaDec::dic and update CLzmaDec::dicPos - } - } - LzmaDec_Free() -*/ - -/* LzmaDec_DecodeToDic - - The decoding to internal dictionary buffer (CLzmaDec::dic). - You must manually update CLzmaDec::dicPos, if it reaches CLzmaDec::dicBufSize !!! - -finishMode: - It has meaning only if the decoding reaches output limit (dicLimit). - LZMA_FINISH_ANY - Decode just dicLimit bytes. - LZMA_FINISH_END - Stream must be finished after dicLimit. - -Returns: - SZ_OK - status: - LZMA_STATUS_FINISHED_WITH_MARK - LZMA_STATUS_NOT_FINISHED - LZMA_STATUS_NEEDS_MORE_INPUT - LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK - SZ_ERROR_DATA - Data error -*/ - -SRes LzmaDec_DecodeToDic(CLzmaDec *p, SizeT dicLimit, - const Byte *src, SizeT *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status); - - -/* ---------- Buffer Interface ---------- */ - -/* It's zlib-like interface. - See LzmaDec_DecodeToDic description for information about STEPS and return results, - but you must use LzmaDec_DecodeToBuf instead of LzmaDec_DecodeToDic and you don't need - to work with CLzmaDec variables manually. - -finishMode: - It has meaning only if the decoding reaches output limit (*destLen). - LZMA_FINISH_ANY - Decode just destLen bytes. - LZMA_FINISH_END - Stream must be finished after (*destLen). -*/ - -SRes LzmaDec_DecodeToBuf(CLzmaDec *p, Byte *dest, SizeT *destLen, - const Byte *src, SizeT *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status); - - -/* ---------- One Call Interface ---------- */ - -/* LzmaDecode - -finishMode: - It has meaning only if the decoding reaches output limit (*destLen). - LZMA_FINISH_ANY - Decode just destLen bytes. - LZMA_FINISH_END - Stream must be finished after (*destLen). - -Returns: - SZ_OK - status: - LZMA_STATUS_FINISHED_WITH_MARK - LZMA_STATUS_NOT_FINISHED - LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK - SZ_ERROR_DATA - Data error - SZ_ERROR_MEM - Memory allocation error - SZ_ERROR_UNSUPPORTED - Unsupported properties - SZ_ERROR_INPUT_EOF - It needs more bytes in input buffer (src). -*/ - -SRes LzmaDecode(Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen, - const Byte *propData, unsigned propSize, ELzmaFinishMode finishMode, - ELzmaStatus *status, ISzAlloc *alloc); - -#ifdef __cplusplus -} -#endif - -#endif +/* LzmaDec.h -- LZMA Decoder +2009-02-07 : Igor Pavlov : Public domain */ + +#ifndef __LZMA_DEC_H +#define __LZMA_DEC_H + +#include "Types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* #define _LZMA_PROB32 */ +/* _LZMA_PROB32 can increase the speed on some CPUs, + but memory usage for CLzmaDec::probs will be doubled in that case */ + +#ifdef _LZMA_PROB32 +#define CLzmaProb UInt32 +#else +#define CLzmaProb UInt16 +#endif + + +/* ---------- LZMA Properties ---------- */ + +#define LZMA_PROPS_SIZE 5 + +typedef struct _CLzmaProps +{ + unsigned lc, lp, pb; + UInt32 dicSize; +} CLzmaProps; + +/* LzmaProps_Decode - decodes properties +Returns: + SZ_OK + SZ_ERROR_UNSUPPORTED - Unsupported properties +*/ + +SRes LzmaProps_Decode(CLzmaProps *p, const Byte *data, unsigned size); + + +/* ---------- LZMA Decoder state ---------- */ + +/* LZMA_REQUIRED_INPUT_MAX = number of required input bytes for worst case. + Num bits = log2((2^11 / 31) ^ 22) + 26 < 134 + 26 = 160; */ + +#define LZMA_REQUIRED_INPUT_MAX 20 + +typedef struct +{ + CLzmaProps prop; + CLzmaProb *probs; + Byte *dic; + const Byte *buf; + UInt32 range, code; + SizeT dicPos; + SizeT dicBufSize; + UInt32 processedPos; + UInt32 checkDicSize; + unsigned state; + UInt32 reps[4]; + unsigned remainLen; + int needFlush; + int needInitState; + UInt32 numProbs; + unsigned tempBufSize; + Byte tempBuf[LZMA_REQUIRED_INPUT_MAX]; +} CLzmaDec; + +#define LzmaDec_Construct(p) { (p)->dic = 0; (p)->probs = 0; } + +void LzmaDec_Init(CLzmaDec *p); + +/* There are two types of LZMA streams: + 0) Stream with end mark. That end mark adds about 6 bytes to compressed size. + 1) Stream without end mark. You must know exact uncompressed size to decompress such stream. */ + +typedef enum +{ + LZMA_FINISH_ANY, /* finish at any point */ + LZMA_FINISH_END /* block must be finished at the end */ +} ELzmaFinishMode; + +/* ELzmaFinishMode has meaning only if the decoding reaches output limit !!! + + You must use LZMA_FINISH_END, when you know that current output buffer + covers last bytes of block. In other cases you must use LZMA_FINISH_ANY. + + If LZMA decoder sees end marker before reaching output limit, it returns SZ_OK, + and output value of destLen will be less than output buffer size limit. + You can check status result also. + + You can use multiple checks to test data integrity after full decompression: + 1) Check Result and "status" variable. + 2) Check that output(destLen) = uncompressedSize, if you know real uncompressedSize. + 3) Check that output(srcLen) = compressedSize, if you know real compressedSize. + You must use correct finish mode in that case. */ + +typedef enum +{ + LZMA_STATUS_NOT_SPECIFIED, /* use main error code instead */ + LZMA_STATUS_FINISHED_WITH_MARK, /* stream was finished with end mark. */ + LZMA_STATUS_NOT_FINISHED, /* stream was not finished */ + LZMA_STATUS_NEEDS_MORE_INPUT, /* you must provide more input bytes */ + LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK /* there is probability that stream was finished without end mark */ +} ELzmaStatus; + +/* ELzmaStatus is used only as output value for function call */ + + +/* ---------- Interfaces ---------- */ + +/* There are 3 levels of interfaces: + 1) Dictionary Interface + 2) Buffer Interface + 3) One Call Interface + You can select any of these interfaces, but don't mix functions from different + groups for same object. */ + + +/* There are two variants to allocate state for Dictionary Interface: + 1) LzmaDec_Allocate / LzmaDec_Free + 2) LzmaDec_AllocateProbs / LzmaDec_FreeProbs + You can use variant 2, if you set dictionary buffer manually. + For Buffer Interface you must always use variant 1. + +LzmaDec_Allocate* can return: + SZ_OK + SZ_ERROR_MEM - Memory allocation error + SZ_ERROR_UNSUPPORTED - Unsupported properties +*/ + +SRes LzmaDec_AllocateProbs(CLzmaDec *p, const Byte *props, unsigned propsSize, ISzAlloc *alloc); +void LzmaDec_FreeProbs(CLzmaDec *p, ISzAlloc *alloc); + +SRes LzmaDec_Allocate(CLzmaDec *state, const Byte *prop, unsigned propsSize, ISzAlloc *alloc); +void LzmaDec_Free(CLzmaDec *state, ISzAlloc *alloc); + +/* ---------- Dictionary Interface ---------- */ + +/* You can use it, if you want to eliminate the overhead for data copying from + dictionary to some other external buffer. + You must work with CLzmaDec variables directly in this interface. + + STEPS: + LzmaDec_Constr() + LzmaDec_Allocate() + for (each new stream) + { + LzmaDec_Init() + while (it needs more decompression) + { + LzmaDec_DecodeToDic() + use data from CLzmaDec::dic and update CLzmaDec::dicPos + } + } + LzmaDec_Free() +*/ + +/* LzmaDec_DecodeToDic + + The decoding to internal dictionary buffer (CLzmaDec::dic). + You must manually update CLzmaDec::dicPos, if it reaches CLzmaDec::dicBufSize !!! + +finishMode: + It has meaning only if the decoding reaches output limit (dicLimit). + LZMA_FINISH_ANY - Decode just dicLimit bytes. + LZMA_FINISH_END - Stream must be finished after dicLimit. + +Returns: + SZ_OK + status: + LZMA_STATUS_FINISHED_WITH_MARK + LZMA_STATUS_NOT_FINISHED + LZMA_STATUS_NEEDS_MORE_INPUT + LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK + SZ_ERROR_DATA - Data error +*/ + +SRes LzmaDec_DecodeToDic(CLzmaDec *p, SizeT dicLimit, + const Byte *src, SizeT *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status); + + +/* ---------- Buffer Interface ---------- */ + +/* It's zlib-like interface. + See LzmaDec_DecodeToDic description for information about STEPS and return results, + but you must use LzmaDec_DecodeToBuf instead of LzmaDec_DecodeToDic and you don't need + to work with CLzmaDec variables manually. + +finishMode: + It has meaning only if the decoding reaches output limit (*destLen). + LZMA_FINISH_ANY - Decode just destLen bytes. + LZMA_FINISH_END - Stream must be finished after (*destLen). +*/ + +SRes LzmaDec_DecodeToBuf(CLzmaDec *p, Byte *dest, SizeT *destLen, + const Byte *src, SizeT *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status); + + +/* ---------- One Call Interface ---------- */ + +/* LzmaDecode + +finishMode: + It has meaning only if the decoding reaches output limit (*destLen). + LZMA_FINISH_ANY - Decode just destLen bytes. + LZMA_FINISH_END - Stream must be finished after (*destLen). + +Returns: + SZ_OK + status: + LZMA_STATUS_FINISHED_WITH_MARK + LZMA_STATUS_NOT_FINISHED + LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK + SZ_ERROR_DATA - Data error + SZ_ERROR_MEM - Memory allocation error + SZ_ERROR_UNSUPPORTED - Unsupported properties + SZ_ERROR_INPUT_EOF - It needs more bytes in input buffer (src). +*/ + +SRes LzmaDecode(Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen, + const Byte *propData, unsigned propSize, ELzmaFinishMode finishMode, + ELzmaStatus *status, ISzAlloc *alloc); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/fex/7z_C/Types.h b/fex/7z_C/Types.h index f193ce2f..7732c240 100644 --- a/fex/7z_C/Types.h +++ b/fex/7z_C/Types.h @@ -1,254 +1,254 @@ -/* Types.h -- Basic types -2010-10-09 : Igor Pavlov : Public domain */ - -#ifndef __7Z_TYPES_H -#define __7Z_TYPES_H - -#include - -#ifdef _WIN32 -#include -#endif - -#ifndef EXTERN_C_BEGIN -#ifdef __cplusplus -#define EXTERN_C_BEGIN extern "C" { -#define EXTERN_C_END } -#else -#define EXTERN_C_BEGIN -#define EXTERN_C_END -#endif -#endif - -EXTERN_C_BEGIN - -#define SZ_OK 0 - -#define SZ_ERROR_DATA 1 -#define SZ_ERROR_MEM 2 -#define SZ_ERROR_CRC 3 -#define SZ_ERROR_UNSUPPORTED 4 -#define SZ_ERROR_PARAM 5 -#define SZ_ERROR_INPUT_EOF 6 -#define SZ_ERROR_OUTPUT_EOF 7 -#define SZ_ERROR_READ 8 -#define SZ_ERROR_WRITE 9 -#define SZ_ERROR_PROGRESS 10 -#define SZ_ERROR_FAIL 11 -#define SZ_ERROR_THREAD 12 - -#define SZ_ERROR_ARCHIVE 16 -#define SZ_ERROR_NO_ARCHIVE 17 - -typedef int SRes; - -#ifdef _WIN32 -typedef DWORD WRes; -#else -typedef int WRes; -#endif - -#ifndef RINOK -#define RINOK(x) { int __result__ = (x); if (__result__ != 0) return __result__; } -#endif - -typedef unsigned char Byte; -typedef short Int16; -typedef unsigned short UInt16; - -#ifdef _LZMA_UINT32_IS_ULONG -typedef long Int32; -typedef unsigned long UInt32; -#else -typedef int Int32; -typedef unsigned int UInt32; -#endif - -#ifdef _SZ_NO_INT_64 - -/* define _SZ_NO_INT_64, if your compiler doesn't support 64-bit integers. - NOTES: Some code will work incorrectly in that case! */ - -typedef long Int64; -typedef unsigned long UInt64; - -#else - -#if defined(_MSC_VER) || defined(__BORLANDC__) -typedef __int64 Int64; -typedef unsigned __int64 UInt64; -#define UINT64_CONST(n) n -#else -typedef long long int Int64; -typedef unsigned long long int UInt64; -#define UINT64_CONST(n) n ## ULL -#endif - -#endif - -#ifdef _LZMA_NO_SYSTEM_SIZE_T -typedef UInt32 SizeT; -#else -typedef size_t SizeT; -#endif - -typedef int Bool; -#define True 1 -#define False 0 - - -#ifdef _WIN32 -#define MY_STD_CALL __stdcall -#else -#define MY_STD_CALL -#endif - -#ifdef _MSC_VER - -#if _MSC_VER >= 1300 -#define MY_NO_INLINE __declspec(noinline) -#else -#define MY_NO_INLINE -#endif - -#define MY_CDECL __cdecl -#define MY_FAST_CALL __fastcall - -#else - -#define MY_CDECL -#define MY_FAST_CALL - -#endif - - -/* The following interfaces use first parameter as pointer to structure */ - -typedef struct -{ - Byte (*Read)(void *p); /* reads one byte, returns 0 in case of EOF or error */ -} IByteIn; - -typedef struct -{ - void (*Write)(void *p, Byte b); -} IByteOut; - -typedef struct -{ - SRes (*Read)(void *p, void *buf, size_t *size); - /* if (input(*size) != 0 && output(*size) == 0) means end_of_stream. - (output(*size) < input(*size)) is allowed */ -} ISeqInStream; - -/* it can return SZ_ERROR_INPUT_EOF */ -SRes SeqInStream_Read(ISeqInStream *stream, void *buf, size_t size); -SRes SeqInStream_Read2(ISeqInStream *stream, void *buf, size_t size, SRes errorType); -SRes SeqInStream_ReadByte(ISeqInStream *stream, Byte *buf); - -typedef struct -{ - size_t (*Write)(void *p, const void *buf, size_t size); - /* Returns: result - the number of actually written bytes. - (result < size) means error */ -} ISeqOutStream; - -typedef enum -{ - SZ_SEEK_SET = 0, - SZ_SEEK_CUR = 1, - SZ_SEEK_END = 2 -} ESzSeek; - -typedef struct -{ - SRes (*Read)(void *p, void *buf, size_t *size); /* same as ISeqInStream::Read */ - SRes (*Seek)(void *p, Int64 *pos, ESzSeek origin); -} ISeekInStream; - -typedef struct -{ - SRes (*Look)(void *p, const void **buf, size_t *size); - /* if (input(*size) != 0 && output(*size) == 0) means end_of_stream. - (output(*size) > input(*size)) is not allowed - (output(*size) < input(*size)) is allowed */ - SRes (*Skip)(void *p, size_t offset); - /* offset must be <= output(*size) of Look */ - - SRes (*Read)(void *p, void *buf, size_t *size); - /* reads directly (without buffer). It's same as ISeqInStream::Read */ - SRes (*Seek)(void *p, Int64 *pos, ESzSeek origin); -} ILookInStream; - -SRes LookInStream_LookRead(ILookInStream *stream, void *buf, size_t *size); -SRes LookInStream_SeekTo(ILookInStream *stream, UInt64 offset); - -/* reads via ILookInStream::Read */ -SRes LookInStream_Read2(ILookInStream *stream, void *buf, size_t size, SRes errorType); -SRes LookInStream_Read(ILookInStream *stream, void *buf, size_t size); - -#define LookToRead_BUF_SIZE (1 << 14) - -typedef struct -{ - ILookInStream s; - ISeekInStream *realStream; - size_t pos; - size_t size; - Byte buf[LookToRead_BUF_SIZE]; -} CLookToRead; - -void LookToRead_CreateVTable(CLookToRead *p, int lookahead); -void LookToRead_Init(CLookToRead *p); - -typedef struct -{ - ISeqInStream s; - ILookInStream *realStream; -} CSecToLook; - -void SecToLook_CreateVTable(CSecToLook *p); - -typedef struct -{ - ISeqInStream s; - ILookInStream *realStream; -} CSecToRead; - -void SecToRead_CreateVTable(CSecToRead *p); - -typedef struct -{ - SRes (*Progress)(void *p, UInt64 inSize, UInt64 outSize); - /* Returns: result. (result != SZ_OK) means break. - Value (UInt64)(Int64)-1 for size means unknown value. */ -} ICompressProgress; - -typedef struct -{ - void *(*Alloc)(void *p, size_t size); - void (*Free)(void *p, void *address); /* address can be 0 */ -} ISzAlloc; - -#define IAlloc_Alloc(p, size) (p)->Alloc((p), size) -#define IAlloc_Free(p, a) (p)->Free((p), a) - -#ifdef _WIN32 - -#define CHAR_PATH_SEPARATOR '\\' -#define WCHAR_PATH_SEPARATOR L'\\' -#define STRING_PATH_SEPARATOR "\\" -#define WSTRING_PATH_SEPARATOR L"\\" - -#else - -#define CHAR_PATH_SEPARATOR '/' -#define WCHAR_PATH_SEPARATOR L'/' -#define STRING_PATH_SEPARATOR "/" -#define WSTRING_PATH_SEPARATOR L"/" - -#endif - -EXTERN_C_END - -#endif +/* Types.h -- Basic types +2010-10-09 : Igor Pavlov : Public domain */ + +#ifndef __7Z_TYPES_H +#define __7Z_TYPES_H + +#include + +#ifdef _WIN32 +#include +#endif + +#ifndef EXTERN_C_BEGIN +#ifdef __cplusplus +#define EXTERN_C_BEGIN extern "C" { +#define EXTERN_C_END } +#else +#define EXTERN_C_BEGIN +#define EXTERN_C_END +#endif +#endif + +EXTERN_C_BEGIN + +#define SZ_OK 0 + +#define SZ_ERROR_DATA 1 +#define SZ_ERROR_MEM 2 +#define SZ_ERROR_CRC 3 +#define SZ_ERROR_UNSUPPORTED 4 +#define SZ_ERROR_PARAM 5 +#define SZ_ERROR_INPUT_EOF 6 +#define SZ_ERROR_OUTPUT_EOF 7 +#define SZ_ERROR_READ 8 +#define SZ_ERROR_WRITE 9 +#define SZ_ERROR_PROGRESS 10 +#define SZ_ERROR_FAIL 11 +#define SZ_ERROR_THREAD 12 + +#define SZ_ERROR_ARCHIVE 16 +#define SZ_ERROR_NO_ARCHIVE 17 + +typedef int SRes; + +#ifdef _WIN32 +typedef DWORD WRes; +#else +typedef int WRes; +#endif + +#ifndef RINOK +#define RINOK(x) { int __result__ = (x); if (__result__ != 0) return __result__; } +#endif + +typedef unsigned char Byte; +typedef short Int16; +typedef unsigned short UInt16; + +#ifdef _LZMA_UINT32_IS_ULONG +typedef long Int32; +typedef unsigned long UInt32; +#else +typedef int Int32; +typedef unsigned int UInt32; +#endif + +#ifdef _SZ_NO_INT_64 + +/* define _SZ_NO_INT_64, if your compiler doesn't support 64-bit integers. + NOTES: Some code will work incorrectly in that case! */ + +typedef long Int64; +typedef unsigned long UInt64; + +#else + +#if defined(_MSC_VER) || defined(__BORLANDC__) +typedef __int64 Int64; +typedef unsigned __int64 UInt64; +#define UINT64_CONST(n) n +#else +typedef long long int Int64; +typedef unsigned long long int UInt64; +#define UINT64_CONST(n) n ## ULL +#endif + +#endif + +#ifdef _LZMA_NO_SYSTEM_SIZE_T +typedef UInt32 SizeT; +#else +typedef size_t SizeT; +#endif + +typedef int Bool; +#define True 1 +#define False 0 + + +#ifdef _WIN32 +#define MY_STD_CALL __stdcall +#else +#define MY_STD_CALL +#endif + +#ifdef _MSC_VER + +#if _MSC_VER >= 1300 +#define MY_NO_INLINE __declspec(noinline) +#else +#define MY_NO_INLINE +#endif + +#define MY_CDECL __cdecl +#define MY_FAST_CALL __fastcall + +#else + +#define MY_CDECL +#define MY_FAST_CALL + +#endif + + +/* The following interfaces use first parameter as pointer to structure */ + +typedef struct +{ + Byte (*Read)(void *p); /* reads one byte, returns 0 in case of EOF or error */ +} IByteIn; + +typedef struct +{ + void (*Write)(void *p, Byte b); +} IByteOut; + +typedef struct +{ + SRes (*Read)(void *p, void *buf, size_t *size); + /* if (input(*size) != 0 && output(*size) == 0) means end_of_stream. + (output(*size) < input(*size)) is allowed */ +} ISeqInStream; + +/* it can return SZ_ERROR_INPUT_EOF */ +SRes SeqInStream_Read(ISeqInStream *stream, void *buf, size_t size); +SRes SeqInStream_Read2(ISeqInStream *stream, void *buf, size_t size, SRes errorType); +SRes SeqInStream_ReadByte(ISeqInStream *stream, Byte *buf); + +typedef struct +{ + size_t (*Write)(void *p, const void *buf, size_t size); + /* Returns: result - the number of actually written bytes. + (result < size) means error */ +} ISeqOutStream; + +typedef enum +{ + SZ_SEEK_SET = 0, + SZ_SEEK_CUR = 1, + SZ_SEEK_END = 2 +} ESzSeek; + +typedef struct +{ + SRes (*Read)(void *p, void *buf, size_t *size); /* same as ISeqInStream::Read */ + SRes (*Seek)(void *p, Int64 *pos, ESzSeek origin); +} ISeekInStream; + +typedef struct +{ + SRes (*Look)(void *p, const void **buf, size_t *size); + /* if (input(*size) != 0 && output(*size) == 0) means end_of_stream. + (output(*size) > input(*size)) is not allowed + (output(*size) < input(*size)) is allowed */ + SRes (*Skip)(void *p, size_t offset); + /* offset must be <= output(*size) of Look */ + + SRes (*Read)(void *p, void *buf, size_t *size); + /* reads directly (without buffer). It's same as ISeqInStream::Read */ + SRes (*Seek)(void *p, Int64 *pos, ESzSeek origin); +} ILookInStream; + +SRes LookInStream_LookRead(ILookInStream *stream, void *buf, size_t *size); +SRes LookInStream_SeekTo(ILookInStream *stream, UInt64 offset); + +/* reads via ILookInStream::Read */ +SRes LookInStream_Read2(ILookInStream *stream, void *buf, size_t size, SRes errorType); +SRes LookInStream_Read(ILookInStream *stream, void *buf, size_t size); + +#define LookToRead_BUF_SIZE (1 << 14) + +typedef struct +{ + ILookInStream s; + ISeekInStream *realStream; + size_t pos; + size_t size; + Byte buf[LookToRead_BUF_SIZE]; +} CLookToRead; + +void LookToRead_CreateVTable(CLookToRead *p, int lookahead); +void LookToRead_Init(CLookToRead *p); + +typedef struct +{ + ISeqInStream s; + ILookInStream *realStream; +} CSecToLook; + +void SecToLook_CreateVTable(CSecToLook *p); + +typedef struct +{ + ISeqInStream s; + ILookInStream *realStream; +} CSecToRead; + +void SecToRead_CreateVTable(CSecToRead *p); + +typedef struct +{ + SRes (*Progress)(void *p, UInt64 inSize, UInt64 outSize); + /* Returns: result. (result != SZ_OK) means break. + Value (UInt64)(Int64)-1 for size means unknown value. */ +} ICompressProgress; + +typedef struct +{ + void *(*Alloc)(void *p, size_t size); + void (*Free)(void *p, void *address); /* address can be 0 */ +} ISzAlloc; + +#define IAlloc_Alloc(p, size) (p)->Alloc((p), size) +#define IAlloc_Free(p, a) (p)->Free((p), a) + +#ifdef _WIN32 + +#define CHAR_PATH_SEPARATOR '\\' +#define WCHAR_PATH_SEPARATOR L'\\' +#define STRING_PATH_SEPARATOR "\\" +#define WSTRING_PATH_SEPARATOR L"\\" + +#else + +#define CHAR_PATH_SEPARATOR '/' +#define WCHAR_PATH_SEPARATOR L'/' +#define STRING_PATH_SEPARATOR "/" +#define WSTRING_PATH_SEPARATOR L"/" + +#endif + +EXTERN_C_END + +#endif diff --git a/fex/fex/Binary_Extractor.h b/fex/fex/Binary_Extractor.h index d8cca642..339a0873 100644 --- a/fex/fex/Binary_Extractor.h +++ b/fex/fex/Binary_Extractor.h @@ -1,26 +1,26 @@ -// Presents a single file as an "archive" of just that file. - -// File_Extractor 1.0.0 -#ifndef BINARY_EXTRACTOR_H -#define BINARY_EXTRACTOR_H - -#include "File_Extractor.h" - -class Binary_Extractor : public File_Extractor { -public: - Binary_Extractor(); - virtual ~Binary_Extractor(); - -protected: - virtual blargg_err_t open_path_v(); - virtual blargg_err_t open_v(); - virtual void close_v(); - - virtual blargg_err_t next_v(); - virtual blargg_err_t rewind_v(); - - virtual blargg_err_t stat_v(); - virtual blargg_err_t extract_v( void*, int ); -}; - -#endif +// Presents a single file as an "archive" of just that file. + +// File_Extractor 1.0.0 +#ifndef BINARY_EXTRACTOR_H +#define BINARY_EXTRACTOR_H + +#include "File_Extractor.h" + +class Binary_Extractor : public File_Extractor { +public: + Binary_Extractor(); + virtual ~Binary_Extractor(); + +protected: + virtual blargg_err_t open_path_v(); + virtual blargg_err_t open_v(); + virtual void close_v(); + + virtual blargg_err_t next_v(); + virtual blargg_err_t rewind_v(); + + virtual blargg_err_t stat_v(); + virtual blargg_err_t extract_v( void*, int ); +}; + +#endif diff --git a/fex/fex/Data_Reader.cpp b/fex/fex/Data_Reader.cpp index e5a8d49a..83b09e2f 100644 --- a/fex/fex/Data_Reader.cpp +++ b/fex/fex/Data_Reader.cpp @@ -1,765 +1,765 @@ -// File_Extractor 1.0.0. http://www.slack.net/~ant/ - -#include "Data_Reader.h" - -#include "blargg_endian.h" -#include -#include -#include - -#if BLARGG_UTF8_PATHS - #include -#endif - -/* Copyright (C) 2005-2009 Shay Green. This module is free software; you -can redistribute it and/or modify it under the terms of the GNU Lesser -General Public License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. This -module 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 Lesser General Public License for more -details. You should have received a copy of the GNU Lesser General Public -License along with this module; if not, write to the Free Software Foundation, -Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ - -#include "blargg_source.h" - -// Data_Reader - -blargg_err_t Data_Reader::read( void* p, int n ) -{ - assert( n >= 0 ); - - if ( n < 0 ) - return blargg_err_caller; - - if ( n <= 0 ) - return blargg_ok; - - if ( n > remain() ) - return blargg_err_file_eof; - - blargg_err_t err = read_v( p, n ); - if ( !err ) - remain_ -= n; - - return err; -} - -blargg_err_t Data_Reader::read_avail( void* p, int* n_ ) -{ - assert( *n_ >= 0 ); - - int n = min( *n_, remain() ); - *n_ = 0; - - if ( n < 0 ) - return blargg_err_caller; - - if ( n <= 0 ) - return blargg_ok; - - blargg_err_t err = read_v( p, n ); - if ( !err ) - { - remain_ -= n; - *n_ = n; - } - - return err; -} - -blargg_err_t Data_Reader::read_avail( void* p, long* n ) -{ - int i = STATIC_CAST(int, *n); - blargg_err_t err = read_avail( p, &i ); - *n = i; - return err; -} - -blargg_err_t Data_Reader::skip_v( int count ) -{ - char buf [512]; - while ( count ) - { - int n = min( count, (int) sizeof buf ); - count -= n; - RETURN_ERR( read_v( buf, n ) ); - } - return blargg_ok; -} - -blargg_err_t Data_Reader::skip( int n ) -{ - assert( n >= 0 ); - - if ( n < 0 ) - return blargg_err_caller; - - if ( n <= 0 ) - return blargg_ok; - - if ( n > remain() ) - return blargg_err_file_eof; - - blargg_err_t err = skip_v( n ); - if ( !err ) - remain_ -= n; - - return err; -} - - -// File_Reader - -blargg_err_t File_Reader::seek( int n ) -{ - assert( n >= 0 ); - - if ( n < 0 ) - return blargg_err_caller; - - if ( n == tell() ) - return blargg_ok; - - if ( n > size() ) - return blargg_err_file_eof; - - blargg_err_t err = seek_v( n ); - if ( !err ) - set_tell( n ); - - return err; -} - -blargg_err_t File_Reader::skip_v( int n ) -{ - return seek_v( tell() + n ); -} - - -// Subset_Reader - -Subset_Reader::Subset_Reader( Data_Reader* dr, int size ) : - in( dr ) -{ - set_remain( min( size, dr->remain() ) ); -} - -blargg_err_t Subset_Reader::read_v( void* p, int s ) -{ - return in->read( p, s ); -} - - -// Remaining_Reader - -Remaining_Reader::Remaining_Reader( void const* h, int size, Data_Reader* r ) : - in( r ) -{ - header = h; - header_remain = size; - - set_remain( size + r->remain() ); -} - -blargg_err_t Remaining_Reader::read_v( void* out, int count ) -{ - int first = min( count, header_remain ); - if ( first ) - { - memcpy( out, header, first ); - header = STATIC_CAST(char const*, header) + first; - header_remain -= first; - } - - return in->read( STATIC_CAST(char*, out) + first, count - first ); -} - - -// Mem_File_Reader - -Mem_File_Reader::Mem_File_Reader( const void* p, long s ) : - begin( STATIC_CAST(const char*, p) ) -{ - set_size( s ); -} - -blargg_err_t Mem_File_Reader::read_v( void* p, int s ) -{ - memcpy( p, begin + tell(), s ); - return blargg_ok; -} - -blargg_err_t Mem_File_Reader::seek_v( int ) -{ - return blargg_ok; -} - - -// Callback_Reader - -Callback_Reader::Callback_Reader( callback_t c, long s, void* d ) : - callback( c ), - user_data( d ) -{ - set_remain( s ); -} - -blargg_err_t Callback_Reader::read_v( void* out, int count ) -{ - return callback( user_data, out, count ); -} - - -// Callback_File_Reader - -Callback_File_Reader::Callback_File_Reader( callback_t c, long s, void* d ) : - callback( c ), - user_data( d ) -{ - set_size( s ); -} - -blargg_err_t Callback_File_Reader::read_v( void* out, int count ) -{ - return callback( user_data, out, count, tell() ); -} - -blargg_err_t Callback_File_Reader::seek_v( int ) -{ - return blargg_ok; -} - -static const BOOST::uint8_t mask_tab[6]={0x80,0xE0,0xF0,0xF8,0xFC,0xFE}; - -static const BOOST::uint8_t val_tab[6]={0,0xC0,0xE0,0xF0,0xF8,0xFC}; - -size_t utf8_char_len_from_header( char p_c ) -{ - BOOST::uint8_t c = (BOOST::uint8_t)p_c; - - size_t cnt = 0; - for(;;) - { - if ( ( p_c & mask_tab[cnt] ) == val_tab[cnt] ) break; - if ( ++cnt >= 6 ) return 0; - } - - return cnt + 1; -} - -size_t utf8_decode_char( const char *p_utf8, unsigned & wide, size_t mmax ) -{ - const BOOST::uint8_t * utf8 = ( const BOOST::uint8_t* )p_utf8; - - if ( mmax == 0 ) - { - wide = 0; - return 0; - } - - if ( utf8[0] < 0x80 ) - { - wide = utf8[0]; - return utf8[0]>0 ? 1 : 0; - } - if ( mmax > 6 ) mmax = 6; - wide = 0; - - unsigned res=0; - unsigned n; - unsigned cnt=0; - for(;;) - { - if ( ( *utf8 & mask_tab[cnt] ) == val_tab[cnt] ) break; - if ( ++cnt >= mmax ) return 0; - } - cnt++; - - if ( cnt==2 && !( *utf8 & 0x1E ) ) return 0; - - if ( cnt == 1 ) - res = *utf8; - else - res = ( 0xFF >> ( cnt + 1 ) ) & *utf8; - - for ( n = 1; n < cnt; n++ ) - { - if ( ( utf8[n] & 0xC0 ) != 0x80 ) - return 0; - if ( !res && n == 2 && !( ( utf8[n] & 0x7F ) >> ( 7 - cnt ) ) ) - return 0; - - res = ( res << 6 ) | ( utf8[n] & 0x3F ); - } - - wide = res; - - return cnt; -} - -size_t utf8_encode_char( unsigned wide, char * target ) -{ - size_t count; - - if ( wide < 0x80 ) - count = 1; - else if ( wide < 0x800 ) - count = 2; - else if ( wide < 0x10000 ) - count = 3; - else if ( wide < 0x200000 ) - count = 4; - else if ( wide < 0x4000000 ) - count = 5; - else if ( wide <= 0x7FFFFFFF ) - count = 6; - else - return 0; - - if ( target == 0 ) - return count; - - switch ( count ) - { - case 6: - target[5] = 0x80 | ( wide & 0x3F ); - wide = wide >> 6; - wide |= 0x4000000; - case 5: - target[4] = 0x80 | ( wide & 0x3F ); - wide = wide >> 6; - wide |= 0x200000; - case 4: - target[3] = 0x80 | ( wide & 0x3F ); - wide = wide >> 6; - wide |= 0x10000; - case 3: - target[2] = 0x80 | ( wide & 0x3F ); - wide = wide >> 6; - wide |= 0x800; - case 2: - target[1] = 0x80 | ( wide & 0x3F ); - wide = wide >> 6; - wide |= 0xC0; - case 1: - target[0] = wide; - } - - return count; -} - -size_t utf16_encode_char( unsigned cur_wchar, wchar_t * out ) -{ - if ( cur_wchar < 0x10000 ) - { - if ( out ) *out = (wchar_t) cur_wchar; return 1; - } - else if ( cur_wchar < ( 1 << 20 ) ) - { - unsigned c = cur_wchar - 0x10000; - //MSDN: - //The first (high) surrogate is a 16-bit code value in the range U+D800 to U+DBFF. The second (low) surrogate is a 16-bit code value in the range U+DC00 to U+DFFF. Using surrogates, Unicode can support over one million characters. For more details about surrogates, refer to The Unicode Standard, version 2.0. - if ( out ) - { - out[0] = ( wchar_t )( 0xD800 | ( 0x3FF & ( c >> 10 ) ) ); - out[1] = ( wchar_t )( 0xDC00 | ( 0x3FF & c ) ) ; - } - return 2; - } - else - { - if ( out ) *out = '?'; return 1; - } -} - -size_t utf16_decode_char( const wchar_t * p_source, unsigned * p_out, size_t p_source_length ) -{ - if ( p_source_length == 0 ) return 0; - else if ( p_source_length == 1 ) - { - *p_out = p_source[0]; - return 1; - } - else - { - size_t retval = 0; - unsigned decoded = p_source[0]; - if ( decoded != 0 ) - { - retval = 1; - if ( ( decoded & 0xFC00 ) == 0xD800 ) - { - unsigned low = p_source[1]; - if ( ( low & 0xFC00 ) == 0xDC00 ) - { - decoded = 0x10000 + ( ( ( decoded & 0x3FF ) << 10 ) | ( low & 0x3FF ) ); - retval = 2; - } - } - } - *p_out = decoded; - return retval; - } -} - -// Converts wide-character path to UTF-8. Free result with free(). Only supported on Windows. -char* blargg_to_utf8( const wchar_t* wpath ) -{ - if ( wpath == NULL ) - return NULL; - - size_t needed = 0; - size_t mmax = wcslen( wpath ); - if ( mmax <= 0 ) - return NULL; - - size_t ptr = 0; - while ( ptr < mmax ) - { - unsigned wide = 0; - size_t char_len = utf16_decode_char( wpath + ptr, &wide, mmax - ptr ); - if ( !char_len ) break; - ptr += char_len; - needed += utf8_encode_char( wide, 0 ); - } - if ( needed <= 0 ) - return NULL; - - char* path = (char*) calloc( needed + 1, 1 ); - if ( path == NULL ) - return NULL; - - ptr = 0; - size_t actual = 0; - while ( ptr < mmax && actual < needed ) - { - unsigned wide = 0; - size_t char_len = utf16_decode_char( wpath + ptr, &wide, mmax - ptr ); - if ( !char_len ) break; - ptr += char_len; - actual += utf8_encode_char( wide, path + actual ); - } - - if ( actual == 0 ) - { - free( path ); - return NULL; - } - - assert( actual == needed ); - return path; -} - -// Converts UTF-8 path to wide-character. Free result with free() Only supported on Windows. -wchar_t* blargg_to_wide( const char* path ) -{ - if ( path == NULL ) - return NULL; - - size_t mmax = strlen( path ); - if ( mmax <= 0 ) - return NULL; - - size_t needed = 0; - size_t ptr = 0; - while ( ptr < mmax ) - { - unsigned wide = 0; - size_t char_len = utf8_decode_char( path + ptr, wide, mmax - ptr ); - if ( !char_len ) break; - ptr += char_len; - needed += utf16_encode_char( wide, 0 ); - } - if ( needed <= 0 ) - return NULL; - - wchar_t* wpath = (wchar_t*) calloc( needed + 1, sizeof *wpath ); - if ( wpath == NULL ) - return NULL; - - ptr = 0; - size_t actual = 0; - while ( ptr < mmax && actual < needed ) - { - unsigned wide = 0; - size_t char_len = utf8_decode_char( path + ptr, wide, mmax - ptr ); - if ( !char_len ) break; - ptr += char_len; - actual += utf16_encode_char( wide, wpath + actual ); - } - if ( actual == 0 ) - { - free( wpath ); - return NULL; - } - - assert( actual == needed ); - return wpath; -} - -#ifdef _WIN32 - -static FILE* blargg_fopen( const char path [], const char mode [] ) -{ - FILE* file = NULL; - wchar_t* wmode = NULL; - wchar_t* wpath = NULL; - - wpath = blargg_to_wide( path ); - if ( wpath ) - { - wmode = blargg_to_wide( mode ); - if ( wmode ) - file = _wfopen( wpath, wmode ); - } - - // Save and restore errno in case free() clears it - int saved_errno = errno; - free( wmode ); - free( wpath ); - errno = saved_errno; - - return file; -} - -#else - -static inline FILE* blargg_fopen( const char path [], const char mode [] ) -{ - return fopen( path, mode ); -} - -#endif - - -// Std_File_Reader - -Std_File_Reader::Std_File_Reader() -{ - file_ = NULL; -} - -Std_File_Reader::~Std_File_Reader() -{ - close(); -} - -static blargg_err_t blargg_fopen( FILE** out, const char path [] ) -{ - errno = 0; - *out = blargg_fopen( path, "rb" ); - if ( !*out ) - { - #ifdef ENOENT - if ( errno == ENOENT ) - return blargg_err_file_missing; - #endif - #ifdef ENOMEM - if ( errno == ENOMEM ) - return blargg_err_memory; - #endif - return blargg_err_file_read; - } - - return blargg_ok; -} - -static blargg_err_t blargg_fsize( FILE* f, long* out ) -{ - if ( fseek( f, 0, SEEK_END ) ) - return blargg_err_file_io; - - *out = ftell( f ); - if ( *out < 0 ) - return blargg_err_file_io; - - if ( fseek( f, 0, SEEK_SET ) ) - return blargg_err_file_io; - - return blargg_ok; -} - -blargg_err_t Std_File_Reader::open( const char path [] ) -{ - close(); - - FILE* f; - RETURN_ERR( blargg_fopen( &f, path ) ); - - long s; - blargg_err_t err = blargg_fsize( f, &s ); - if ( err ) - { - fclose( f ); - return err; - } - - file_ = f; - set_size( s ); - - return blargg_ok; -} - -void Std_File_Reader::make_unbuffered() -{ - if ( setvbuf( STATIC_CAST(FILE*, file_), NULL, _IONBF, 0 ) ) - check( false ); // shouldn't fail, but OK if it does -} - -blargg_err_t Std_File_Reader::read_v( void* p, int s ) -{ - if ( (size_t) s != fread( p, 1, s, STATIC_CAST(FILE*, file_) ) ) - { - // Data_Reader's wrapper should prevent EOF - check( !feof( STATIC_CAST(FILE*, file_) ) ); - - return blargg_err_file_io; - } - - return blargg_ok; -} - -blargg_err_t Std_File_Reader::seek_v( int n ) -{ - if ( fseek( STATIC_CAST(FILE*, file_), n, SEEK_SET ) ) - { - // Data_Reader's wrapper should prevent EOF - check( !feof( STATIC_CAST(FILE*, file_) ) ); - - return blargg_err_file_io; - } - - return blargg_ok; -} - -void Std_File_Reader::close() -{ - if ( file_ ) - { - fclose( STATIC_CAST(FILE*, file_) ); - file_ = NULL; - } -} - - -// Gzip_File_Reader - -#ifdef HAVE_ZLIB_H - -#include "zlib.h" - -static const char* get_gzip_eof( const char path [], long* eof ) -{ - FILE* file; - RETURN_ERR( blargg_fopen( &file, path ) ); - - int const h_size = 4; - unsigned char h [h_size]; - - // read four bytes to ensure that we can seek to -4 later - if ( fread( h, 1, h_size, file ) != (size_t) h_size || h[0] != 0x1F || h[1] != 0x8B ) - { - // Not gzipped - if ( ferror( file ) ) - return blargg_err_file_io; - - if ( fseek( file, 0, SEEK_END ) ) - return blargg_err_file_io; - - *eof = ftell( file ); - if ( *eof < 0 ) - return blargg_err_file_io; - } - else - { - // Gzipped; get uncompressed size from end - if ( fseek( file, -h_size, SEEK_END ) ) - return blargg_err_file_io; - - if ( fread( h, 1, h_size, file ) != (size_t) h_size ) - return blargg_err_file_io; - - *eof = get_le32( h ); - } - - if ( fclose( file ) ) - check( false ); - - return blargg_ok; -} - -Gzip_File_Reader::Gzip_File_Reader() -{ - file_ = NULL; -} - -Gzip_File_Reader::~Gzip_File_Reader() -{ - close(); -} - -blargg_err_t Gzip_File_Reader::open( const char path [] ) -{ - close(); - - long s; - RETURN_ERR( get_gzip_eof( path, &s ) ); - - file_ = gzopen( path, "rb" ); - if ( !file_ ) - return blargg_err_file_read; - - set_size( s ); - return blargg_ok; -} - -static blargg_err_t convert_gz_error( gzFile file ) -{ - int err; - gzerror( file, &err ); - - switch ( err ) - { - case Z_STREAM_ERROR: break; - case Z_DATA_ERROR: return blargg_err_file_corrupt; - case Z_MEM_ERROR: return blargg_err_memory; - case Z_BUF_ERROR: break; - } - return blargg_err_internal; -} - -blargg_err_t Gzip_File_Reader::read_v( void* p, int s ) -{ - int result = gzread( file_, p, s ); - if ( result != s ) - { - if ( result < 0 ) - return convert_gz_error( file_ ); - - return blargg_err_file_corrupt; - } - - return blargg_ok; -} - -blargg_err_t Gzip_File_Reader::seek_v( int n ) -{ - if ( gzseek( file_, n, SEEK_SET ) < 0 ) - return convert_gz_error( file_ ); - - return blargg_ok; -} - -void Gzip_File_Reader::close() -{ - if ( file_ ) - { - if ( gzclose( file_ ) ) - check( false ); - file_ = NULL; - } -} - -#endif +// File_Extractor 1.0.0. http://www.slack.net/~ant/ + +#include "Data_Reader.h" + +#include "blargg_endian.h" +#include +#include +#include + +#if BLARGG_UTF8_PATHS + #include +#endif + +/* Copyright (C) 2005-2009 Shay Green. This module is free software; you +can redistribute it and/or modify it under the terms of the GNU Lesser +General Public License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. This +module 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 Lesser General Public License for more +details. You should have received a copy of the GNU Lesser General Public +License along with this module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +// Data_Reader + +blargg_err_t Data_Reader::read( void* p, int n ) +{ + assert( n >= 0 ); + + if ( n < 0 ) + return blargg_err_caller; + + if ( n <= 0 ) + return blargg_ok; + + if ( n > remain() ) + return blargg_err_file_eof; + + blargg_err_t err = read_v( p, n ); + if ( !err ) + remain_ -= n; + + return err; +} + +blargg_err_t Data_Reader::read_avail( void* p, int* n_ ) +{ + assert( *n_ >= 0 ); + + int n = min( *n_, remain() ); + *n_ = 0; + + if ( n < 0 ) + return blargg_err_caller; + + if ( n <= 0 ) + return blargg_ok; + + blargg_err_t err = read_v( p, n ); + if ( !err ) + { + remain_ -= n; + *n_ = n; + } + + return err; +} + +blargg_err_t Data_Reader::read_avail( void* p, long* n ) +{ + int i = STATIC_CAST(int, *n); + blargg_err_t err = read_avail( p, &i ); + *n = i; + return err; +} + +blargg_err_t Data_Reader::skip_v( int count ) +{ + char buf [512]; + while ( count ) + { + int n = min( count, (int) sizeof buf ); + count -= n; + RETURN_ERR( read_v( buf, n ) ); + } + return blargg_ok; +} + +blargg_err_t Data_Reader::skip( int n ) +{ + assert( n >= 0 ); + + if ( n < 0 ) + return blargg_err_caller; + + if ( n <= 0 ) + return blargg_ok; + + if ( n > remain() ) + return blargg_err_file_eof; + + blargg_err_t err = skip_v( n ); + if ( !err ) + remain_ -= n; + + return err; +} + + +// File_Reader + +blargg_err_t File_Reader::seek( int n ) +{ + assert( n >= 0 ); + + if ( n < 0 ) + return blargg_err_caller; + + if ( n == tell() ) + return blargg_ok; + + if ( n > size() ) + return blargg_err_file_eof; + + blargg_err_t err = seek_v( n ); + if ( !err ) + set_tell( n ); + + return err; +} + +blargg_err_t File_Reader::skip_v( int n ) +{ + return seek_v( tell() + n ); +} + + +// Subset_Reader + +Subset_Reader::Subset_Reader( Data_Reader* dr, int size ) : + in( dr ) +{ + set_remain( min( size, dr->remain() ) ); +} + +blargg_err_t Subset_Reader::read_v( void* p, int s ) +{ + return in->read( p, s ); +} + + +// Remaining_Reader + +Remaining_Reader::Remaining_Reader( void const* h, int size, Data_Reader* r ) : + in( r ) +{ + header = h; + header_remain = size; + + set_remain( size + r->remain() ); +} + +blargg_err_t Remaining_Reader::read_v( void* out, int count ) +{ + int first = min( count, header_remain ); + if ( first ) + { + memcpy( out, header, first ); + header = STATIC_CAST(char const*, header) + first; + header_remain -= first; + } + + return in->read( STATIC_CAST(char*, out) + first, count - first ); +} + + +// Mem_File_Reader + +Mem_File_Reader::Mem_File_Reader( const void* p, long s ) : + begin( STATIC_CAST(const char*, p) ) +{ + set_size( s ); +} + +blargg_err_t Mem_File_Reader::read_v( void* p, int s ) +{ + memcpy( p, begin + tell(), s ); + return blargg_ok; +} + +blargg_err_t Mem_File_Reader::seek_v( int ) +{ + return blargg_ok; +} + + +// Callback_Reader + +Callback_Reader::Callback_Reader( callback_t c, long s, void* d ) : + callback( c ), + user_data( d ) +{ + set_remain( s ); +} + +blargg_err_t Callback_Reader::read_v( void* out, int count ) +{ + return callback( user_data, out, count ); +} + + +// Callback_File_Reader + +Callback_File_Reader::Callback_File_Reader( callback_t c, long s, void* d ) : + callback( c ), + user_data( d ) +{ + set_size( s ); +} + +blargg_err_t Callback_File_Reader::read_v( void* out, int count ) +{ + return callback( user_data, out, count, tell() ); +} + +blargg_err_t Callback_File_Reader::seek_v( int ) +{ + return blargg_ok; +} + +static const BOOST::uint8_t mask_tab[6]={0x80,0xE0,0xF0,0xF8,0xFC,0xFE}; + +static const BOOST::uint8_t val_tab[6]={0,0xC0,0xE0,0xF0,0xF8,0xFC}; + +size_t utf8_char_len_from_header( char p_c ) +{ + BOOST::uint8_t c = (BOOST::uint8_t)p_c; + + size_t cnt = 0; + for(;;) + { + if ( ( p_c & mask_tab[cnt] ) == val_tab[cnt] ) break; + if ( ++cnt >= 6 ) return 0; + } + + return cnt + 1; +} + +size_t utf8_decode_char( const char *p_utf8, unsigned & wide, size_t mmax ) +{ + const BOOST::uint8_t * utf8 = ( const BOOST::uint8_t* )p_utf8; + + if ( mmax == 0 ) + { + wide = 0; + return 0; + } + + if ( utf8[0] < 0x80 ) + { + wide = utf8[0]; + return utf8[0]>0 ? 1 : 0; + } + if ( mmax > 6 ) mmax = 6; + wide = 0; + + unsigned res=0; + unsigned n; + unsigned cnt=0; + for(;;) + { + if ( ( *utf8 & mask_tab[cnt] ) == val_tab[cnt] ) break; + if ( ++cnt >= mmax ) return 0; + } + cnt++; + + if ( cnt==2 && !( *utf8 & 0x1E ) ) return 0; + + if ( cnt == 1 ) + res = *utf8; + else + res = ( 0xFF >> ( cnt + 1 ) ) & *utf8; + + for ( n = 1; n < cnt; n++ ) + { + if ( ( utf8[n] & 0xC0 ) != 0x80 ) + return 0; + if ( !res && n == 2 && !( ( utf8[n] & 0x7F ) >> ( 7 - cnt ) ) ) + return 0; + + res = ( res << 6 ) | ( utf8[n] & 0x3F ); + } + + wide = res; + + return cnt; +} + +size_t utf8_encode_char( unsigned wide, char * target ) +{ + size_t count; + + if ( wide < 0x80 ) + count = 1; + else if ( wide < 0x800 ) + count = 2; + else if ( wide < 0x10000 ) + count = 3; + else if ( wide < 0x200000 ) + count = 4; + else if ( wide < 0x4000000 ) + count = 5; + else if ( wide <= 0x7FFFFFFF ) + count = 6; + else + return 0; + + if ( target == 0 ) + return count; + + switch ( count ) + { + case 6: + target[5] = 0x80 | ( wide & 0x3F ); + wide = wide >> 6; + wide |= 0x4000000; + case 5: + target[4] = 0x80 | ( wide & 0x3F ); + wide = wide >> 6; + wide |= 0x200000; + case 4: + target[3] = 0x80 | ( wide & 0x3F ); + wide = wide >> 6; + wide |= 0x10000; + case 3: + target[2] = 0x80 | ( wide & 0x3F ); + wide = wide >> 6; + wide |= 0x800; + case 2: + target[1] = 0x80 | ( wide & 0x3F ); + wide = wide >> 6; + wide |= 0xC0; + case 1: + target[0] = wide; + } + + return count; +} + +size_t utf16_encode_char( unsigned cur_wchar, wchar_t * out ) +{ + if ( cur_wchar < 0x10000 ) + { + if ( out ) *out = (wchar_t) cur_wchar; return 1; + } + else if ( cur_wchar < ( 1 << 20 ) ) + { + unsigned c = cur_wchar - 0x10000; + //MSDN: + //The first (high) surrogate is a 16-bit code value in the range U+D800 to U+DBFF. The second (low) surrogate is a 16-bit code value in the range U+DC00 to U+DFFF. Using surrogates, Unicode can support over one million characters. For more details about surrogates, refer to The Unicode Standard, version 2.0. + if ( out ) + { + out[0] = ( wchar_t )( 0xD800 | ( 0x3FF & ( c >> 10 ) ) ); + out[1] = ( wchar_t )( 0xDC00 | ( 0x3FF & c ) ) ; + } + return 2; + } + else + { + if ( out ) *out = '?'; return 1; + } +} + +size_t utf16_decode_char( const wchar_t * p_source, unsigned * p_out, size_t p_source_length ) +{ + if ( p_source_length == 0 ) return 0; + else if ( p_source_length == 1 ) + { + *p_out = p_source[0]; + return 1; + } + else + { + size_t retval = 0; + unsigned decoded = p_source[0]; + if ( decoded != 0 ) + { + retval = 1; + if ( ( decoded & 0xFC00 ) == 0xD800 ) + { + unsigned low = p_source[1]; + if ( ( low & 0xFC00 ) == 0xDC00 ) + { + decoded = 0x10000 + ( ( ( decoded & 0x3FF ) << 10 ) | ( low & 0x3FF ) ); + retval = 2; + } + } + } + *p_out = decoded; + return retval; + } +} + +// Converts wide-character path to UTF-8. Free result with free(). Only supported on Windows. +char* blargg_to_utf8( const wchar_t* wpath ) +{ + if ( wpath == NULL ) + return NULL; + + size_t needed = 0; + size_t mmax = wcslen( wpath ); + if ( mmax <= 0 ) + return NULL; + + size_t ptr = 0; + while ( ptr < mmax ) + { + unsigned wide = 0; + size_t char_len = utf16_decode_char( wpath + ptr, &wide, mmax - ptr ); + if ( !char_len ) break; + ptr += char_len; + needed += utf8_encode_char( wide, 0 ); + } + if ( needed <= 0 ) + return NULL; + + char* path = (char*) calloc( needed + 1, 1 ); + if ( path == NULL ) + return NULL; + + ptr = 0; + size_t actual = 0; + while ( ptr < mmax && actual < needed ) + { + unsigned wide = 0; + size_t char_len = utf16_decode_char( wpath + ptr, &wide, mmax - ptr ); + if ( !char_len ) break; + ptr += char_len; + actual += utf8_encode_char( wide, path + actual ); + } + + if ( actual == 0 ) + { + free( path ); + return NULL; + } + + assert( actual == needed ); + return path; +} + +// Converts UTF-8 path to wide-character. Free result with free() Only supported on Windows. +wchar_t* blargg_to_wide( const char* path ) +{ + if ( path == NULL ) + return NULL; + + size_t mmax = strlen( path ); + if ( mmax <= 0 ) + return NULL; + + size_t needed = 0; + size_t ptr = 0; + while ( ptr < mmax ) + { + unsigned wide = 0; + size_t char_len = utf8_decode_char( path + ptr, wide, mmax - ptr ); + if ( !char_len ) break; + ptr += char_len; + needed += utf16_encode_char( wide, 0 ); + } + if ( needed <= 0 ) + return NULL; + + wchar_t* wpath = (wchar_t*) calloc( needed + 1, sizeof *wpath ); + if ( wpath == NULL ) + return NULL; + + ptr = 0; + size_t actual = 0; + while ( ptr < mmax && actual < needed ) + { + unsigned wide = 0; + size_t char_len = utf8_decode_char( path + ptr, wide, mmax - ptr ); + if ( !char_len ) break; + ptr += char_len; + actual += utf16_encode_char( wide, wpath + actual ); + } + if ( actual == 0 ) + { + free( wpath ); + return NULL; + } + + assert( actual == needed ); + return wpath; +} + +#ifdef _WIN32 + +static FILE* blargg_fopen( const char path [], const char mode [] ) +{ + FILE* file = NULL; + wchar_t* wmode = NULL; + wchar_t* wpath = NULL; + + wpath = blargg_to_wide( path ); + if ( wpath ) + { + wmode = blargg_to_wide( mode ); + if ( wmode ) + file = _wfopen( wpath, wmode ); + } + + // Save and restore errno in case free() clears it + int saved_errno = errno; + free( wmode ); + free( wpath ); + errno = saved_errno; + + return file; +} + +#else + +static inline FILE* blargg_fopen( const char path [], const char mode [] ) +{ + return fopen( path, mode ); +} + +#endif + + +// Std_File_Reader + +Std_File_Reader::Std_File_Reader() +{ + file_ = NULL; +} + +Std_File_Reader::~Std_File_Reader() +{ + close(); +} + +static blargg_err_t blargg_fopen( FILE** out, const char path [] ) +{ + errno = 0; + *out = blargg_fopen( path, "rb" ); + if ( !*out ) + { + #ifdef ENOENT + if ( errno == ENOENT ) + return blargg_err_file_missing; + #endif + #ifdef ENOMEM + if ( errno == ENOMEM ) + return blargg_err_memory; + #endif + return blargg_err_file_read; + } + + return blargg_ok; +} + +static blargg_err_t blargg_fsize( FILE* f, long* out ) +{ + if ( fseek( f, 0, SEEK_END ) ) + return blargg_err_file_io; + + *out = ftell( f ); + if ( *out < 0 ) + return blargg_err_file_io; + + if ( fseek( f, 0, SEEK_SET ) ) + return blargg_err_file_io; + + return blargg_ok; +} + +blargg_err_t Std_File_Reader::open( const char path [] ) +{ + close(); + + FILE* f; + RETURN_ERR( blargg_fopen( &f, path ) ); + + long s; + blargg_err_t err = blargg_fsize( f, &s ); + if ( err ) + { + fclose( f ); + return err; + } + + file_ = f; + set_size( s ); + + return blargg_ok; +} + +void Std_File_Reader::make_unbuffered() +{ + if ( setvbuf( STATIC_CAST(FILE*, file_), NULL, _IONBF, 0 ) ) + check( false ); // shouldn't fail, but OK if it does +} + +blargg_err_t Std_File_Reader::read_v( void* p, int s ) +{ + if ( (size_t) s != fread( p, 1, s, STATIC_CAST(FILE*, file_) ) ) + { + // Data_Reader's wrapper should prevent EOF + check( !feof( STATIC_CAST(FILE*, file_) ) ); + + return blargg_err_file_io; + } + + return blargg_ok; +} + +blargg_err_t Std_File_Reader::seek_v( int n ) +{ + if ( fseek( STATIC_CAST(FILE*, file_), n, SEEK_SET ) ) + { + // Data_Reader's wrapper should prevent EOF + check( !feof( STATIC_CAST(FILE*, file_) ) ); + + return blargg_err_file_io; + } + + return blargg_ok; +} + +void Std_File_Reader::close() +{ + if ( file_ ) + { + fclose( STATIC_CAST(FILE*, file_) ); + file_ = NULL; + } +} + + +// Gzip_File_Reader + +#ifdef HAVE_ZLIB_H + +#include "zlib.h" + +static const char* get_gzip_eof( const char path [], long* eof ) +{ + FILE* file; + RETURN_ERR( blargg_fopen( &file, path ) ); + + int const h_size = 4; + unsigned char h [h_size]; + + // read four bytes to ensure that we can seek to -4 later + if ( fread( h, 1, h_size, file ) != (size_t) h_size || h[0] != 0x1F || h[1] != 0x8B ) + { + // Not gzipped + if ( ferror( file ) ) + return blargg_err_file_io; + + if ( fseek( file, 0, SEEK_END ) ) + return blargg_err_file_io; + + *eof = ftell( file ); + if ( *eof < 0 ) + return blargg_err_file_io; + } + else + { + // Gzipped; get uncompressed size from end + if ( fseek( file, -h_size, SEEK_END ) ) + return blargg_err_file_io; + + if ( fread( h, 1, h_size, file ) != (size_t) h_size ) + return blargg_err_file_io; + + *eof = get_le32( h ); + } + + if ( fclose( file ) ) + check( false ); + + return blargg_ok; +} + +Gzip_File_Reader::Gzip_File_Reader() +{ + file_ = NULL; +} + +Gzip_File_Reader::~Gzip_File_Reader() +{ + close(); +} + +blargg_err_t Gzip_File_Reader::open( const char path [] ) +{ + close(); + + long s; + RETURN_ERR( get_gzip_eof( path, &s ) ); + + file_ = gzopen( path, "rb" ); + if ( !file_ ) + return blargg_err_file_read; + + set_size( s ); + return blargg_ok; +} + +static blargg_err_t convert_gz_error( gzFile file ) +{ + int err; + gzerror( file, &err ); + + switch ( err ) + { + case Z_STREAM_ERROR: break; + case Z_DATA_ERROR: return blargg_err_file_corrupt; + case Z_MEM_ERROR: return blargg_err_memory; + case Z_BUF_ERROR: break; + } + return blargg_err_internal; +} + +blargg_err_t Gzip_File_Reader::read_v( void* p, int s ) +{ + int result = gzread( file_, p, s ); + if ( result != s ) + { + if ( result < 0 ) + return convert_gz_error( file_ ); + + return blargg_err_file_corrupt; + } + + return blargg_ok; +} + +blargg_err_t Gzip_File_Reader::seek_v( int n ) +{ + if ( gzseek( file_, n, SEEK_SET ) < 0 ) + return convert_gz_error( file_ ); + + return blargg_ok; +} + +void Gzip_File_Reader::close() +{ + if ( file_ ) + { + if ( gzclose( file_ ) ) + check( false ); + file_ = NULL; + } +} + +#endif diff --git a/fex/fex/Data_Reader.h b/fex/fex/Data_Reader.h index ad58f522..be206f7b 100644 --- a/fex/fex/Data_Reader.h +++ b/fex/fex/Data_Reader.h @@ -1,264 +1,264 @@ -// Lightweight interface for reading data from byte stream - -// File_Extractor 1.0.0 -#ifndef DATA_READER_H -#define DATA_READER_H - -#include "blargg_common.h" - -/* Some functions accept a long instead of int for convenience where caller has -a long due to some other interface, and would otherwise have to get a warning, -or cast it (and verify that it wasn't outside the range of an int). - -To really support huge (>2GB) files, long isn't a solution, since there's no -guarantee it's more than 32 bits. We'd need to use long long (if available), or -something compiler-specific, and change all places file sizes or offsets are -used. */ - -// Supports reading and finding out how many bytes are remaining -class Data_Reader { -public: - - // Reads min(*n,remain()) bytes and sets *n to this number, thus trying to read more - // tham remain() bytes doesn't result in error, just *n being set to remain(). - blargg_err_t read_avail( void* p, int* n ); - blargg_err_t read_avail( void* p, long* n ); - - // Reads exactly n bytes, or returns error if they couldn't ALL be read. - // Reading past end of file results in blargg_err_file_eof. - blargg_err_t read( void* p, int n ); - - // Number of bytes remaining until end of file - int remain() const { return remain_; } - - // Reads and discards n bytes. Skipping past end of file results in blargg_err_file_eof. - blargg_err_t skip( int n ); - - virtual ~Data_Reader() { } - -private: - // noncopyable - Data_Reader( const Data_Reader& ); - Data_Reader& operator = ( const Data_Reader& ); - -// Derived interface -protected: - Data_Reader() : remain_( 0 ) { } - - // Sets remain - void set_remain( int n ) { assert( n >= 0 ); remain_ = n; } - - // Do same as read(). Guaranteed that 0 < n <= remain(). Value of remain() is updated - // AFTER this call succeeds, not before. set_remain() should NOT be called from this. - virtual blargg_err_t read_v( void*, int n ) BLARGG_PURE( { (void)n; return blargg_ok; } ) - - // Do same as skip(). Guaranteed that 0 < n <= remain(). Default just reads data - // and discards it. Value of remain() is updated AFTER this call succeeds, not - // before. set_remain() should NOT be called from this. - virtual blargg_err_t skip_v( int n ); - -// Implementation -public: - BLARGG_DISABLE_NOTHROW - -private: - int remain_; -}; - - -// Supports seeking in addition to Data_Reader operations -class File_Reader : public Data_Reader { -public: - - // Size of file - int size() const { return size_; } - - // Current position in file - int tell() const { return size_ - remain(); } - - // Goes to new position - blargg_err_t seek( int ); - -// Derived interface -protected: - // Sets size and resets position - void set_size( int n ) { size_ = n; Data_Reader::set_remain( n ); } - void set_size( long n ) { set_size( STATIC_CAST(int, n) ); } - - // Sets reported position - void set_tell( int i ) { assert( 0 <= i && i <= size_ ); Data_Reader::set_remain( size_ - i ); } - - // Do same as seek(). Guaranteed that 0 <= n <= size(). Value of tell() is updated - // AFTER this call succeeds, not before. set_* functions should NOT be called from this. - virtual blargg_err_t seek_v( int n ) BLARGG_PURE( { (void)n; return blargg_ok; } ) - -// Implementation -protected: - File_Reader() : size_( 0 ) { } - - virtual blargg_err_t skip_v( int ); - -private: - int size_; - - void set_remain(); // avoid accidental use of set_remain -}; - - -// Reads from file on disk -class Std_File_Reader : public File_Reader { -public: - - // Opens file - blargg_err_t open( const char path [] ); - - // Closes file if one was open - void close(); - - // Switches to unbuffered mode. Useful if buffering is already being - // done at a higher level. - void make_unbuffered(); - -// Implementation -public: - Std_File_Reader(); - virtual ~Std_File_Reader(); - -protected: - virtual blargg_err_t read_v( void*, int ); - virtual blargg_err_t seek_v( int ); - -private: - void* file_; -}; - - -// Treats range of memory as a file -class Mem_File_Reader : public File_Reader { -public: - - Mem_File_Reader( const void* begin, long size ); - -// Implementation -protected: - virtual blargg_err_t read_v( void*, int ); - virtual blargg_err_t seek_v( int ); - -private: - const char* const begin; -}; - - -// Allows only count bytes to be read from reader passed -class Subset_Reader : public Data_Reader { -public: - - Subset_Reader( Data_Reader*, int count ); - -// Implementation -protected: - virtual blargg_err_t read_v( void*, int ); - -private: - Data_Reader* const in; -}; - - -// Joins already-read header and remaining data into original file. -// Meant for cases where you've already read header and don't want -// to seek and re-read data (for efficiency). -class Remaining_Reader : public Data_Reader { -public: - - Remaining_Reader( void const* header, int header_size, Data_Reader* ); - -// Implementation -protected: - virtual blargg_err_t read_v( void*, int ); - -private: - Data_Reader* const in; - void const* header; - int header_remain; -}; - - -// Invokes callback function to read data -extern "C" { // necessary to be usable from C - typedef const char* (*callback_reader_func_t)( - void* user_data, // Same value passed to constructor - void* out, // Buffer to place data into - int count // Number of bytes to read - ); -} -class Callback_Reader : public Data_Reader { -public: - typedef callback_reader_func_t callback_t; - Callback_Reader( callback_t, long size, void* user_data ); - -// Implementation -protected: - virtual blargg_err_t read_v( void*, int ); - -private: - callback_t const callback; - void* const user_data; -}; - - -// Invokes callback function to read data -extern "C" { // necessary to be usable from C - typedef const char* (*callback_file_reader_func_t)( - void* user_data, // Same value passed to constructor - void* out, // Buffer to place data into - int count, // Number of bytes to read - int pos // Position in file to read from - ); -} -class Callback_File_Reader : public File_Reader { -public: - typedef callback_file_reader_func_t callback_t; - Callback_File_Reader( callback_t, long size, void* user_data ); - -// Implementation -protected: - virtual blargg_err_t read_v( void*, int ); - virtual blargg_err_t seek_v( int ); - -private: - callback_t const callback; - void* const user_data; -}; - - -#ifdef HAVE_ZLIB_H - -// Reads file compressed with gzip (or uncompressed) -class Gzip_File_Reader : public File_Reader { -public: - - // Opens possibly gzipped file - blargg_err_t open( const char path [] ); - - // Closes file if one was open - void close(); - -// Implementation -public: - Gzip_File_Reader(); - ~Gzip_File_Reader(); - -protected: - virtual blargg_err_t read_v( void*, int ); - virtual blargg_err_t seek_v( int ); - -private: - // void* so "zlib.h" doesn't have to be included here - void* file_; -}; -#endif - -char* blargg_to_utf8( const wchar_t* ); -wchar_t* blargg_to_wide( const char* ); - -#endif +// Lightweight interface for reading data from byte stream + +// File_Extractor 1.0.0 +#ifndef DATA_READER_H +#define DATA_READER_H + +#include "blargg_common.h" + +/* Some functions accept a long instead of int for convenience where caller has +a long due to some other interface, and would otherwise have to get a warning, +or cast it (and verify that it wasn't outside the range of an int). + +To really support huge (>2GB) files, long isn't a solution, since there's no +guarantee it's more than 32 bits. We'd need to use long long (if available), or +something compiler-specific, and change all places file sizes or offsets are +used. */ + +// Supports reading and finding out how many bytes are remaining +class Data_Reader { +public: + + // Reads min(*n,remain()) bytes and sets *n to this number, thus trying to read more + // tham remain() bytes doesn't result in error, just *n being set to remain(). + blargg_err_t read_avail( void* p, int* n ); + blargg_err_t read_avail( void* p, long* n ); + + // Reads exactly n bytes, or returns error if they couldn't ALL be read. + // Reading past end of file results in blargg_err_file_eof. + blargg_err_t read( void* p, int n ); + + // Number of bytes remaining until end of file + int remain() const { return remain_; } + + // Reads and discards n bytes. Skipping past end of file results in blargg_err_file_eof. + blargg_err_t skip( int n ); + + virtual ~Data_Reader() { } + +private: + // noncopyable + Data_Reader( const Data_Reader& ); + Data_Reader& operator = ( const Data_Reader& ); + +// Derived interface +protected: + Data_Reader() : remain_( 0 ) { } + + // Sets remain + void set_remain( int n ) { assert( n >= 0 ); remain_ = n; } + + // Do same as read(). Guaranteed that 0 < n <= remain(). Value of remain() is updated + // AFTER this call succeeds, not before. set_remain() should NOT be called from this. + virtual blargg_err_t read_v( void*, int n ) BLARGG_PURE( { (void)n; return blargg_ok; } ) + + // Do same as skip(). Guaranteed that 0 < n <= remain(). Default just reads data + // and discards it. Value of remain() is updated AFTER this call succeeds, not + // before. set_remain() should NOT be called from this. + virtual blargg_err_t skip_v( int n ); + +// Implementation +public: + BLARGG_DISABLE_NOTHROW + +private: + int remain_; +}; + + +// Supports seeking in addition to Data_Reader operations +class File_Reader : public Data_Reader { +public: + + // Size of file + int size() const { return size_; } + + // Current position in file + int tell() const { return size_ - remain(); } + + // Goes to new position + blargg_err_t seek( int ); + +// Derived interface +protected: + // Sets size and resets position + void set_size( int n ) { size_ = n; Data_Reader::set_remain( n ); } + void set_size( long n ) { set_size( STATIC_CAST(int, n) ); } + + // Sets reported position + void set_tell( int i ) { assert( 0 <= i && i <= size_ ); Data_Reader::set_remain( size_ - i ); } + + // Do same as seek(). Guaranteed that 0 <= n <= size(). Value of tell() is updated + // AFTER this call succeeds, not before. set_* functions should NOT be called from this. + virtual blargg_err_t seek_v( int n ) BLARGG_PURE( { (void)n; return blargg_ok; } ) + +// Implementation +protected: + File_Reader() : size_( 0 ) { } + + virtual blargg_err_t skip_v( int ); + +private: + int size_; + + void set_remain(); // avoid accidental use of set_remain +}; + + +// Reads from file on disk +class Std_File_Reader : public File_Reader { +public: + + // Opens file + blargg_err_t open( const char path [] ); + + // Closes file if one was open + void close(); + + // Switches to unbuffered mode. Useful if buffering is already being + // done at a higher level. + void make_unbuffered(); + +// Implementation +public: + Std_File_Reader(); + virtual ~Std_File_Reader(); + +protected: + virtual blargg_err_t read_v( void*, int ); + virtual blargg_err_t seek_v( int ); + +private: + void* file_; +}; + + +// Treats range of memory as a file +class Mem_File_Reader : public File_Reader { +public: + + Mem_File_Reader( const void* begin, long size ); + +// Implementation +protected: + virtual blargg_err_t read_v( void*, int ); + virtual blargg_err_t seek_v( int ); + +private: + const char* const begin; +}; + + +// Allows only count bytes to be read from reader passed +class Subset_Reader : public Data_Reader { +public: + + Subset_Reader( Data_Reader*, int count ); + +// Implementation +protected: + virtual blargg_err_t read_v( void*, int ); + +private: + Data_Reader* const in; +}; + + +// Joins already-read header and remaining data into original file. +// Meant for cases where you've already read header and don't want +// to seek and re-read data (for efficiency). +class Remaining_Reader : public Data_Reader { +public: + + Remaining_Reader( void const* header, int header_size, Data_Reader* ); + +// Implementation +protected: + virtual blargg_err_t read_v( void*, int ); + +private: + Data_Reader* const in; + void const* header; + int header_remain; +}; + + +// Invokes callback function to read data +extern "C" { // necessary to be usable from C + typedef const char* (*callback_reader_func_t)( + void* user_data, // Same value passed to constructor + void* out, // Buffer to place data into + int count // Number of bytes to read + ); +} +class Callback_Reader : public Data_Reader { +public: + typedef callback_reader_func_t callback_t; + Callback_Reader( callback_t, long size, void* user_data ); + +// Implementation +protected: + virtual blargg_err_t read_v( void*, int ); + +private: + callback_t const callback; + void* const user_data; +}; + + +// Invokes callback function to read data +extern "C" { // necessary to be usable from C + typedef const char* (*callback_file_reader_func_t)( + void* user_data, // Same value passed to constructor + void* out, // Buffer to place data into + int count, // Number of bytes to read + int pos // Position in file to read from + ); +} +class Callback_File_Reader : public File_Reader { +public: + typedef callback_file_reader_func_t callback_t; + Callback_File_Reader( callback_t, long size, void* user_data ); + +// Implementation +protected: + virtual blargg_err_t read_v( void*, int ); + virtual blargg_err_t seek_v( int ); + +private: + callback_t const callback; + void* const user_data; +}; + + +#ifdef HAVE_ZLIB_H + +// Reads file compressed with gzip (or uncompressed) +class Gzip_File_Reader : public File_Reader { +public: + + // Opens possibly gzipped file + blargg_err_t open( const char path [] ); + + // Closes file if one was open + void close(); + +// Implementation +public: + Gzip_File_Reader(); + ~Gzip_File_Reader(); + +protected: + virtual blargg_err_t read_v( void*, int ); + virtual blargg_err_t seek_v( int ); + +private: + // void* so "zlib.h" doesn't have to be included here + void* file_; +}; +#endif + +char* blargg_to_utf8( const wchar_t* ); +wchar_t* blargg_to_wide( const char* ); + +#endif diff --git a/fex/fex/File_Extractor.cpp b/fex/fex/File_Extractor.cpp index 4b27d921..e060e095 100644 --- a/fex/fex/File_Extractor.cpp +++ b/fex/fex/File_Extractor.cpp @@ -1,341 +1,341 @@ -// File_Extractor 1.0.0. http://www.slack.net/~ant/ - -#include "File_Extractor.h" - -/* Copyright (C) 2005-2009 Shay Green. This module is free software; you -can redistribute it and/or modify it under the terms of the GNU Lesser -General Public License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. This -module 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 Lesser General Public License for more -details. You should have received a copy of the GNU Lesser General Public -License along with this module; if not, write to the Free Software Foundation, -Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ - -#include "blargg_source.h" - -File_Extractor::fex_t( fex_type_t t ) : - type_( t ) -{ - own_file_ = NULL; - - close_(); -} - -// Open - -blargg_err_t File_Extractor::set_path( const char* path ) -{ - if ( !path ) - path = ""; - - RETURN_ERR( path_.resize( strlen( path ) + 1 ) ); - memcpy( path_.begin(), path, path_.size() ); - return blargg_ok; -} - -blargg_err_t File_Extractor::open( const char path [] ) -{ - close(); - - RETURN_ERR( set_path( path ) ); - - blargg_err_t err = open_path_v(); - if ( err ) - close(); - else - opened_ = true; - - return err; -} - -blargg_err_t File_Extractor::open_path_v() -{ - RETURN_ERR( open_arc_file() ); - - return open_v(); -} - -inline -static void make_unbuffered( Std_File_Reader* r ) -{ - r->make_unbuffered(); -} - -inline -static void make_unbuffered( void* ) -{ } - -blargg_err_t File_Extractor::open_arc_file( bool unbuffered ) -{ - if ( reader_ ) - return blargg_ok; - - FEX_FILE_READER* in = BLARGG_NEW FEX_FILE_READER; - CHECK_ALLOC( in ); - - blargg_err_t err = in->open( arc_path() ); - if ( err ) - { - delete in; - } - else - { - reader_ = in; - own_file(); - if ( unbuffered ) - make_unbuffered( in ); - } - - return err; -} - -blargg_err_t File_Extractor::open( File_Reader* input, const char* path ) -{ - close(); - - RETURN_ERR( set_path( path ) ); - - RETURN_ERR( input->seek( 0 ) ); - - reader_ = input; - blargg_err_t err = open_v(); - if ( err ) - close(); - else - opened_ = true; - - return err; -} - -// Close - -void File_Extractor::close() -{ - close_v(); - close_(); -} - -void File_Extractor::close_() -{ - delete own_file_; - own_file_ = NULL; - - tell_ = 0; - reader_ = NULL; - opened_ = false; - - path_.clear(); - clear_file(); -} - -File_Extractor::~fex_t() -{ - check( !opened() ); // fails if derived destructor didn't call close() - - delete own_file_; -} - -// Scanning - -void File_Extractor::clear_file() -{ - name_ = NULL; - wname_ = NULL; - done_ = true; - stat_called = false; - data_ptr_ = NULL; - - set_info( 0 ); - own_data_.clear(); - clear_file_v(); -} - -void File_Extractor::set_name( const char new_name [], const wchar_t* new_wname ) -{ - name_ = new_name; - wname_ = new_wname; - done_ = false; -} - -void File_Extractor::set_info( int new_size, unsigned date, unsigned crc ) -{ - size_ = new_size; - date_ = (date != 0xFFFFFFFF ? date : 0); - crc32_ = crc; - set_remain( new_size ); -} - -blargg_err_t File_Extractor::next_() -{ - tell_++; - clear_file(); - - blargg_err_t err = next_v(); - if ( err ) - clear_file(); - - return err; -} - -blargg_err_t File_Extractor::next() -{ - assert( !done() ); - return next_(); -} - -blargg_err_t File_Extractor::rewind() -{ - assert( opened() ); - - tell_ = 0; - clear_file(); - - blargg_err_t err = rewind_v(); - if ( err ) - clear_file(); - - return err; -} - -blargg_err_t File_Extractor::stat() -{ - assert( !done() ); - - if ( !stat_called ) - { - RETURN_ERR( stat_v() ); - stat_called = true; - } - return blargg_ok; -} - -// Tell/seek - -int const pos_offset = 1; - -fex_pos_t File_Extractor::tell_arc() const -{ - assert( opened() ); - - fex_pos_t pos = tell_arc_v(); - assert( pos >= 0 ); - - return pos + pos_offset; -} - -blargg_err_t File_Extractor::seek_arc( fex_pos_t pos ) -{ - assert( opened() ); - assert( pos != 0 ); - - clear_file(); - - blargg_err_t err = seek_arc_v( pos - pos_offset ); - if ( err ) - clear_file(); - - return err; -} - -fex_pos_t File_Extractor::tell_arc_v() const -{ - return tell_; -} - -blargg_err_t File_Extractor::seek_arc_v( fex_pos_t pos ) -{ - // >= because seeking to current file should always reset read pointer etc. - if ( tell_ >= pos ) - RETURN_ERR( rewind() ); - - while ( tell_ < pos ) - { - RETURN_ERR( next_() ); - - if ( done() ) - { - assert( false ); - return blargg_err_caller; - } - } - - assert( tell_ == pos ); - - return blargg_ok; -} - -// Extraction - -blargg_err_t File_Extractor::rewind_file() -{ - RETURN_ERR( stat() ); - - if ( tell() > 0 ) - { - if ( data_ptr_ ) - { - set_remain( size() ); - } - else - { - RETURN_ERR( seek_arc( tell_arc() ) ); - RETURN_ERR( stat() ); - } - } - - return blargg_ok; -} - -blargg_err_t File_Extractor::data( const void** data_out ) -{ - assert( !done() ); - - *data_out = NULL; - if ( !data_ptr_ ) - { - int old_tell = tell(); - - RETURN_ERR( rewind_file() ); - - void const* ptr; - RETURN_ERR( data_v( &ptr ) ); - data_ptr_ = ptr; - - // Now that data is in memory, we can seek by simply setting remain - set_remain( size() - old_tell ); - } - - *data_out = data_ptr_; - return blargg_ok; -} - -blargg_err_t File_Extractor::data_v( void const** out ) -{ - RETURN_ERR( own_data_.resize( size() ) ); - *out = own_data_.begin(); - - blargg_err_t err = extract_v( own_data_.begin(), own_data_.size() ); - if ( err ) - own_data_.clear(); - - return err; -} - -blargg_err_t File_Extractor::extract_v( void* out, int count ) -{ - void const* p; - RETURN_ERR( data( &p ) ); - memcpy( out, STATIC_CAST(char const*,p) + (size() - remain()), count ); - - return blargg_ok; -} - -blargg_err_t File_Extractor::read_v( void* out, int count ) -{ - if ( data_ptr_ ) - return File_Extractor::extract_v( out, count ); - - return extract_v( out, count ); -} +// File_Extractor 1.0.0. http://www.slack.net/~ant/ + +#include "File_Extractor.h" + +/* Copyright (C) 2005-2009 Shay Green. This module is free software; you +can redistribute it and/or modify it under the terms of the GNU Lesser +General Public License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. This +module 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 Lesser General Public License for more +details. You should have received a copy of the GNU Lesser General Public +License along with this module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +File_Extractor::fex_t( fex_type_t t ) : + type_( t ) +{ + own_file_ = NULL; + + close_(); +} + +// Open + +blargg_err_t File_Extractor::set_path( const char* path ) +{ + if ( !path ) + path = ""; + + RETURN_ERR( path_.resize( strlen( path ) + 1 ) ); + memcpy( path_.begin(), path, path_.size() ); + return blargg_ok; +} + +blargg_err_t File_Extractor::open( const char path [] ) +{ + close(); + + RETURN_ERR( set_path( path ) ); + + blargg_err_t err = open_path_v(); + if ( err ) + close(); + else + opened_ = true; + + return err; +} + +blargg_err_t File_Extractor::open_path_v() +{ + RETURN_ERR( open_arc_file() ); + + return open_v(); +} + +inline +static void make_unbuffered( Std_File_Reader* r ) +{ + r->make_unbuffered(); +} + +inline +static void make_unbuffered( void* ) +{ } + +blargg_err_t File_Extractor::open_arc_file( bool unbuffered ) +{ + if ( reader_ ) + return blargg_ok; + + FEX_FILE_READER* in = BLARGG_NEW FEX_FILE_READER; + CHECK_ALLOC( in ); + + blargg_err_t err = in->open( arc_path() ); + if ( err ) + { + delete in; + } + else + { + reader_ = in; + own_file(); + if ( unbuffered ) + make_unbuffered( in ); + } + + return err; +} + +blargg_err_t File_Extractor::open( File_Reader* input, const char* path ) +{ + close(); + + RETURN_ERR( set_path( path ) ); + + RETURN_ERR( input->seek( 0 ) ); + + reader_ = input; + blargg_err_t err = open_v(); + if ( err ) + close(); + else + opened_ = true; + + return err; +} + +// Close + +void File_Extractor::close() +{ + close_v(); + close_(); +} + +void File_Extractor::close_() +{ + delete own_file_; + own_file_ = NULL; + + tell_ = 0; + reader_ = NULL; + opened_ = false; + + path_.clear(); + clear_file(); +} + +File_Extractor::~fex_t() +{ + check( !opened() ); // fails if derived destructor didn't call close() + + delete own_file_; +} + +// Scanning + +void File_Extractor::clear_file() +{ + name_ = NULL; + wname_ = NULL; + done_ = true; + stat_called = false; + data_ptr_ = NULL; + + set_info( 0 ); + own_data_.clear(); + clear_file_v(); +} + +void File_Extractor::set_name( const char new_name [], const wchar_t* new_wname ) +{ + name_ = new_name; + wname_ = new_wname; + done_ = false; +} + +void File_Extractor::set_info( int new_size, unsigned date, unsigned crc ) +{ + size_ = new_size; + date_ = (date != 0xFFFFFFFF ? date : 0); + crc32_ = crc; + set_remain( new_size ); +} + +blargg_err_t File_Extractor::next_() +{ + tell_++; + clear_file(); + + blargg_err_t err = next_v(); + if ( err ) + clear_file(); + + return err; +} + +blargg_err_t File_Extractor::next() +{ + assert( !done() ); + return next_(); +} + +blargg_err_t File_Extractor::rewind() +{ + assert( opened() ); + + tell_ = 0; + clear_file(); + + blargg_err_t err = rewind_v(); + if ( err ) + clear_file(); + + return err; +} + +blargg_err_t File_Extractor::stat() +{ + assert( !done() ); + + if ( !stat_called ) + { + RETURN_ERR( stat_v() ); + stat_called = true; + } + return blargg_ok; +} + +// Tell/seek + +int const pos_offset = 1; + +fex_pos_t File_Extractor::tell_arc() const +{ + assert( opened() ); + + fex_pos_t pos = tell_arc_v(); + assert( pos >= 0 ); + + return pos + pos_offset; +} + +blargg_err_t File_Extractor::seek_arc( fex_pos_t pos ) +{ + assert( opened() ); + assert( pos != 0 ); + + clear_file(); + + blargg_err_t err = seek_arc_v( pos - pos_offset ); + if ( err ) + clear_file(); + + return err; +} + +fex_pos_t File_Extractor::tell_arc_v() const +{ + return tell_; +} + +blargg_err_t File_Extractor::seek_arc_v( fex_pos_t pos ) +{ + // >= because seeking to current file should always reset read pointer etc. + if ( tell_ >= pos ) + RETURN_ERR( rewind() ); + + while ( tell_ < pos ) + { + RETURN_ERR( next_() ); + + if ( done() ) + { + assert( false ); + return blargg_err_caller; + } + } + + assert( tell_ == pos ); + + return blargg_ok; +} + +// Extraction + +blargg_err_t File_Extractor::rewind_file() +{ + RETURN_ERR( stat() ); + + if ( tell() > 0 ) + { + if ( data_ptr_ ) + { + set_remain( size() ); + } + else + { + RETURN_ERR( seek_arc( tell_arc() ) ); + RETURN_ERR( stat() ); + } + } + + return blargg_ok; +} + +blargg_err_t File_Extractor::data( const void** data_out ) +{ + assert( !done() ); + + *data_out = NULL; + if ( !data_ptr_ ) + { + int old_tell = tell(); + + RETURN_ERR( rewind_file() ); + + void const* ptr; + RETURN_ERR( data_v( &ptr ) ); + data_ptr_ = ptr; + + // Now that data is in memory, we can seek by simply setting remain + set_remain( size() - old_tell ); + } + + *data_out = data_ptr_; + return blargg_ok; +} + +blargg_err_t File_Extractor::data_v( void const** out ) +{ + RETURN_ERR( own_data_.resize( size() ) ); + *out = own_data_.begin(); + + blargg_err_t err = extract_v( own_data_.begin(), own_data_.size() ); + if ( err ) + own_data_.clear(); + + return err; +} + +blargg_err_t File_Extractor::extract_v( void* out, int count ) +{ + void const* p; + RETURN_ERR( data( &p ) ); + memcpy( out, STATIC_CAST(char const*,p) + (size() - remain()), count ); + + return blargg_ok; +} + +blargg_err_t File_Extractor::read_v( void* out, int count ) +{ + if ( data_ptr_ ) + return File_Extractor::extract_v( out, count ); + + return extract_v( out, count ); +} diff --git a/fex/fex/File_Extractor.h b/fex/fex/File_Extractor.h index 44080ca2..ad25d5f8 100644 --- a/fex/fex/File_Extractor.h +++ b/fex/fex/File_Extractor.h @@ -1,191 +1,191 @@ -// Compressed file archive interface - -// File_Extractor 1.0.0 -#ifndef FILE_EXTRACTOR_H -#define FILE_EXTRACTOR_H - -#include "blargg_common.h" -#include "Data_Reader.h" -#include "fex.h" - -struct fex_t : private Data_Reader { -public: - virtual ~fex_t(); - -// Open/close - - // Opens archive from custom data source. Keeps pointer until close(). - blargg_err_t open( File_Reader* input, const char* path = NULL ); - - // Takes ownership of File_Reader* passed to open(), so that close() - // will delete it. - void own_file() { own_file_ = reader_; } - - // See fex.h - blargg_err_t open( const char path [] ); - fex_type_t type() const { return type_; } - void close(); - -// Scanning - - // See fex.h - bool done() const { return done_; } - blargg_err_t next(); - blargg_err_t rewind(); - fex_pos_t tell_arc() const; - blargg_err_t seek_arc( fex_pos_t ); - -// Info - - // See fex.h - const char* name() const { return name_; } - const wchar_t* wname() const { return wname_; } - blargg_err_t stat(); - int size() const { assert( stat_called ); return size_; } - unsigned int dos_date() const { return date_; } - unsigned int crc32() const { return crc32_; } - -// Extraction - - // Data_Reader to current file's data, so standard Data_Reader interface can - // be used, rather than having to treat archives specially. stat() must have - // been called. - Data_Reader& reader() { assert( stat_called ); return *this; } - - // See fex.h - blargg_err_t data( const void** data_out ); - int tell() const { return size_ - remain(); } - -// Derived interface -protected: - - // Sets type of object - fex_t( fex_type_t ); - - // Path to archive file, or "" if none supplied - const char* arc_path() const { return path_.begin(); } - - // Opens archive file if it's not already. If unbuffered is true, opens file - // without any buffering. - blargg_err_t open_arc_file( bool unbuffered = false ); - - // Archive file - File_Reader& arc() const { return *reader_; } - - // Sets current file name - void set_name( const char name [], const wchar_t* wname = NULL ); - - // Sets current file information - void set_info( int size, unsigned date = 0, unsigned crc = 0 ); - -// User overrides - - // Overrides must do indicated task. Non-pure functions have reasonable default - // implementation. Overrides should avoid calling public functions like - // next() and rewind(). - - // Open archive using file_path(). OK to delay actual file opening until later. - // Default just calls open_arc_file(), then open_v(). - virtual blargg_err_t open_path_v(); - - // Open archive using file() for source data. If unsupported, return error. - virtual blargg_err_t open_v() BLARGG_PURE( ; ) - - // Go to next file in archive and call set_name() and optionally set_info() - virtual blargg_err_t next_v() BLARGG_PURE( ; ) - - // Go back to first file in archive - virtual blargg_err_t rewind_v() BLARGG_PURE( ; ) - - // Close archive. Called even if open_path_v() or open_v() return unsuccessfully. - virtual void close_v() BLARGG_PURE( ; ) - - // Clear any fields related to current file - virtual void clear_file_v() { } - - // Call set_info() if not already called by next_v() - virtual blargg_err_t stat_v() { return blargg_ok; } - - // Return value that allows later return to this file. Result must be >= 0. - virtual fex_pos_t tell_arc_v() const; - - // Return to previously saved position - virtual blargg_err_t seek_arc_v( fex_pos_t ); - - // One or both of the following must be overridden - - // Provide pointer to data for current file in archive - virtual blargg_err_t data_v( const void** out ); - - // Extract next n bytes - virtual blargg_err_t extract_v( void* out, int n ); - -// Implementation -public: - BLARGG_DISABLE_NOTHROW - -private: - fex_type_t const type_; - - // Archive file - blargg_vector path_; - File_Reader* reader_; - File_Reader* own_file_; - bool opened_; - - // Position in archive - fex_pos_t tell_; // only used by default implementation of tell/seek - bool done_; - - // Info for current file in archive - const char* name_; - const wchar_t* wname_; - unsigned date_; - unsigned crc32_; - int size_; - bool stat_called; - - // Current file contents - void const* data_ptr_; // NULL if not read into memory - blargg_vector own_data_; - - bool opened() const { return opened_; } - void clear_file(); - void close_(); - blargg_err_t set_path( const char* path ); - blargg_err_t rewind_file(); - blargg_err_t next_(); - - // Data_Reader overrides - // TODO: override skip_v? - virtual blargg_err_t read_v( void* out, int n ); -}; - -struct fex_type_t_ -{ - const char* extension; - File_Extractor* (*new_fex)(); - const char* name; - blargg_err_t (*init)(); // Called by fex_init(). Can be NULL. -}; - -extern const fex_type_t_ - fex_7z_type [1], - fex_gz_type [1], - fex_rar_type [1], - fex_zip_type [1], - fex_bin_type [1]; - -inline blargg_err_t File_Extractor::open_v() { return blargg_ok; } -inline blargg_err_t File_Extractor::next_v() { return blargg_ok; } -inline blargg_err_t File_Extractor::rewind_v() { return blargg_ok; } -inline void File_Extractor::close_v() { } - -// Default to Std_File_Reader for archive access -#ifndef FEX_FILE_READER - #define FEX_FILE_READER Std_File_Reader -#elif defined (FEX_FILE_READER_INCLUDE) - #include FEX_FILE_READER_INCLUDE -#endif - -#endif +// Compressed file archive interface + +// File_Extractor 1.0.0 +#ifndef FILE_EXTRACTOR_H +#define FILE_EXTRACTOR_H + +#include "blargg_common.h" +#include "Data_Reader.h" +#include "fex.h" + +struct fex_t : private Data_Reader { +public: + virtual ~fex_t(); + +// Open/close + + // Opens archive from custom data source. Keeps pointer until close(). + blargg_err_t open( File_Reader* input, const char* path = NULL ); + + // Takes ownership of File_Reader* passed to open(), so that close() + // will delete it. + void own_file() { own_file_ = reader_; } + + // See fex.h + blargg_err_t open( const char path [] ); + fex_type_t type() const { return type_; } + void close(); + +// Scanning + + // See fex.h + bool done() const { return done_; } + blargg_err_t next(); + blargg_err_t rewind(); + fex_pos_t tell_arc() const; + blargg_err_t seek_arc( fex_pos_t ); + +// Info + + // See fex.h + const char* name() const { return name_; } + const wchar_t* wname() const { return wname_; } + blargg_err_t stat(); + int size() const { assert( stat_called ); return size_; } + unsigned int dos_date() const { return date_; } + unsigned int crc32() const { return crc32_; } + +// Extraction + + // Data_Reader to current file's data, so standard Data_Reader interface can + // be used, rather than having to treat archives specially. stat() must have + // been called. + Data_Reader& reader() { assert( stat_called ); return *this; } + + // See fex.h + blargg_err_t data( const void** data_out ); + int tell() const { return size_ - remain(); } + +// Derived interface +protected: + + // Sets type of object + fex_t( fex_type_t ); + + // Path to archive file, or "" if none supplied + const char* arc_path() const { return path_.begin(); } + + // Opens archive file if it's not already. If unbuffered is true, opens file + // without any buffering. + blargg_err_t open_arc_file( bool unbuffered = false ); + + // Archive file + File_Reader& arc() const { return *reader_; } + + // Sets current file name + void set_name( const char name [], const wchar_t* wname = NULL ); + + // Sets current file information + void set_info( int size, unsigned date = 0, unsigned crc = 0 ); + +// User overrides + + // Overrides must do indicated task. Non-pure functions have reasonable default + // implementation. Overrides should avoid calling public functions like + // next() and rewind(). + + // Open archive using file_path(). OK to delay actual file opening until later. + // Default just calls open_arc_file(), then open_v(). + virtual blargg_err_t open_path_v(); + + // Open archive using file() for source data. If unsupported, return error. + virtual blargg_err_t open_v() BLARGG_PURE( ; ) + + // Go to next file in archive and call set_name() and optionally set_info() + virtual blargg_err_t next_v() BLARGG_PURE( ; ) + + // Go back to first file in archive + virtual blargg_err_t rewind_v() BLARGG_PURE( ; ) + + // Close archive. Called even if open_path_v() or open_v() return unsuccessfully. + virtual void close_v() BLARGG_PURE( ; ) + + // Clear any fields related to current file + virtual void clear_file_v() { } + + // Call set_info() if not already called by next_v() + virtual blargg_err_t stat_v() { return blargg_ok; } + + // Return value that allows later return to this file. Result must be >= 0. + virtual fex_pos_t tell_arc_v() const; + + // Return to previously saved position + virtual blargg_err_t seek_arc_v( fex_pos_t ); + + // One or both of the following must be overridden + + // Provide pointer to data for current file in archive + virtual blargg_err_t data_v( const void** out ); + + // Extract next n bytes + virtual blargg_err_t extract_v( void* out, int n ); + +// Implementation +public: + BLARGG_DISABLE_NOTHROW + +private: + fex_type_t const type_; + + // Archive file + blargg_vector path_; + File_Reader* reader_; + File_Reader* own_file_; + bool opened_; + + // Position in archive + fex_pos_t tell_; // only used by default implementation of tell/seek + bool done_; + + // Info for current file in archive + const char* name_; + const wchar_t* wname_; + unsigned date_; + unsigned crc32_; + int size_; + bool stat_called; + + // Current file contents + void const* data_ptr_; // NULL if not read into memory + blargg_vector own_data_; + + bool opened() const { return opened_; } + void clear_file(); + void close_(); + blargg_err_t set_path( const char* path ); + blargg_err_t rewind_file(); + blargg_err_t next_(); + + // Data_Reader overrides + // TODO: override skip_v? + virtual blargg_err_t read_v( void* out, int n ); +}; + +struct fex_type_t_ +{ + const char* extension; + File_Extractor* (*new_fex)(); + const char* name; + blargg_err_t (*init)(); // Called by fex_init(). Can be NULL. +}; + +extern const fex_type_t_ + fex_7z_type [1], + fex_gz_type [1], + fex_rar_type [1], + fex_zip_type [1], + fex_bin_type [1]; + +inline blargg_err_t File_Extractor::open_v() { return blargg_ok; } +inline blargg_err_t File_Extractor::next_v() { return blargg_ok; } +inline blargg_err_t File_Extractor::rewind_v() { return blargg_ok; } +inline void File_Extractor::close_v() { } + +// Default to Std_File_Reader for archive access +#ifndef FEX_FILE_READER + #define FEX_FILE_READER Std_File_Reader +#elif defined (FEX_FILE_READER_INCLUDE) + #include FEX_FILE_READER_INCLUDE +#endif + +#endif diff --git a/fex/fex/Gzip_Extractor.cpp b/fex/fex/Gzip_Extractor.cpp index 6946de27..f169fed9 100644 --- a/fex/fex/Gzip_Extractor.cpp +++ b/fex/fex/Gzip_Extractor.cpp @@ -1,98 +1,98 @@ -// File_Extractor 1.0.0. http://www.slack.net/~ant/ - -#include "Gzip_Extractor.h" - -/* Copyright (C) 2005-2009 Shay Green. This module is free software; you -can redistribute it and/or modify it under the terms of the GNU Lesser -General Public License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. This -module 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 Lesser General Public License for more -details. You should have received a copy of the GNU Lesser General Public -License along with this module; if not, write to the Free Software Foundation, -Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ - -#include "blargg_source.h" - -// TODO: could close file once data has been read into memory - -static blargg_err_t init_gzip_file() -{ - get_crc_table(); // initialize zlib's CRC-32 tables - return blargg_ok; -} - -static File_Extractor* new_gzip() -{ - return BLARGG_NEW Gzip_Extractor; -} - -fex_type_t_ const fex_gz_type [1] = {{ - ".gz", - &new_gzip, - "gzipped file", - &init_gzip_file -}}; - -Gzip_Extractor::Gzip_Extractor() : - File_Extractor( fex_gz_type ) -{ } - -Gzip_Extractor::~Gzip_Extractor() -{ - close(); -} - -blargg_err_t Gzip_Extractor::open_path_v() -{ - // skip opening file - return open_v(); -} - -blargg_err_t Gzip_Extractor::stat_v() -{ - RETURN_ERR( open_arc_file( true ) ); - if ( !gr.opened() || gr.tell() != 0 ) - RETURN_ERR( gr.open( &arc() ) ); - - set_info( gr.remain(), 0, gr.crc32() ); - return blargg_ok; -} - -blargg_err_t Gzip_Extractor::open_v() -{ - // Remove .gz suffix - size_t len = strlen( arc_path() ); - if ( fex_has_extension( arc_path(), ".gz" ) ) - len -= 3; - - RETURN_ERR( name.resize( len + 1 ) ); - memcpy( name.begin(), arc_path(), name.size() ); - name [name.size() - 1] = '\0'; - - set_name( name.begin() ); - return blargg_ok; -} - -void Gzip_Extractor::close_v() -{ - name.clear(); - gr.close(); -} - -blargg_err_t Gzip_Extractor::next_v() -{ - return blargg_ok; -} - -blargg_err_t Gzip_Extractor::rewind_v() -{ - set_name( name.begin() ); - return blargg_ok; -} - -blargg_err_t Gzip_Extractor::extract_v( void* p, int n ) -{ - return gr.read( p, n ); -} +// File_Extractor 1.0.0. http://www.slack.net/~ant/ + +#include "Gzip_Extractor.h" + +/* Copyright (C) 2005-2009 Shay Green. This module is free software; you +can redistribute it and/or modify it under the terms of the GNU Lesser +General Public License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. This +module 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 Lesser General Public License for more +details. You should have received a copy of the GNU Lesser General Public +License along with this module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +// TODO: could close file once data has been read into memory + +static blargg_err_t init_gzip_file() +{ + get_crc_table(); // initialize zlib's CRC-32 tables + return blargg_ok; +} + +static File_Extractor* new_gzip() +{ + return BLARGG_NEW Gzip_Extractor; +} + +fex_type_t_ const fex_gz_type [1] = {{ + ".gz", + &new_gzip, + "gzipped file", + &init_gzip_file +}}; + +Gzip_Extractor::Gzip_Extractor() : + File_Extractor( fex_gz_type ) +{ } + +Gzip_Extractor::~Gzip_Extractor() +{ + close(); +} + +blargg_err_t Gzip_Extractor::open_path_v() +{ + // skip opening file + return open_v(); +} + +blargg_err_t Gzip_Extractor::stat_v() +{ + RETURN_ERR( open_arc_file( true ) ); + if ( !gr.opened() || gr.tell() != 0 ) + RETURN_ERR( gr.open( &arc() ) ); + + set_info( gr.remain(), 0, gr.crc32() ); + return blargg_ok; +} + +blargg_err_t Gzip_Extractor::open_v() +{ + // Remove .gz suffix + size_t len = strlen( arc_path() ); + if ( fex_has_extension( arc_path(), ".gz" ) ) + len -= 3; + + RETURN_ERR( name.resize( len + 1 ) ); + memcpy( name.begin(), arc_path(), name.size() ); + name [name.size() - 1] = '\0'; + + set_name( name.begin() ); + return blargg_ok; +} + +void Gzip_Extractor::close_v() +{ + name.clear(); + gr.close(); +} + +blargg_err_t Gzip_Extractor::next_v() +{ + return blargg_ok; +} + +blargg_err_t Gzip_Extractor::rewind_v() +{ + set_name( name.begin() ); + return blargg_ok; +} + +blargg_err_t Gzip_Extractor::extract_v( void* p, int n ) +{ + return gr.read( p, n ); +} diff --git a/fex/fex/Gzip_Extractor.h b/fex/fex/Gzip_Extractor.h index 02044d0c..814dc9b3 100644 --- a/fex/fex/Gzip_Extractor.h +++ b/fex/fex/Gzip_Extractor.h @@ -1,34 +1,34 @@ -// Presents a gzipped file as an "archive" of just that file. -// Also handles non-gzipped files. - -// File_Extractor 1.0.0 -#ifndef GZIP_EXTRACTOR_H -#define GZIP_EXTRACTOR_H - -#include "File_Extractor.h" -#include "Gzip_Reader.h" - -class Gzip_Extractor : public File_Extractor { -public: - Gzip_Extractor(); - virtual ~Gzip_Extractor(); - -protected: - virtual blargg_err_t open_path_v(); - virtual blargg_err_t open_v(); - virtual void close_v(); - - virtual blargg_err_t next_v(); - virtual blargg_err_t rewind_v(); - - virtual blargg_err_t stat_v(); - virtual blargg_err_t extract_v( void*, int ); - -private: - Gzip_Reader gr; - blargg_vector name; - - void set_info_(); -}; - -#endif +// Presents a gzipped file as an "archive" of just that file. +// Also handles non-gzipped files. + +// File_Extractor 1.0.0 +#ifndef GZIP_EXTRACTOR_H +#define GZIP_EXTRACTOR_H + +#include "File_Extractor.h" +#include "Gzip_Reader.h" + +class Gzip_Extractor : public File_Extractor { +public: + Gzip_Extractor(); + virtual ~Gzip_Extractor(); + +protected: + virtual blargg_err_t open_path_v(); + virtual blargg_err_t open_v(); + virtual void close_v(); + + virtual blargg_err_t next_v(); + virtual blargg_err_t rewind_v(); + + virtual blargg_err_t stat_v(); + virtual blargg_err_t extract_v( void*, int ); + +private: + Gzip_Reader gr; + blargg_vector name; + + void set_info_(); +}; + +#endif diff --git a/fex/fex/Gzip_Reader.cpp b/fex/fex/Gzip_Reader.cpp index 464beee7..2aad302c 100644 --- a/fex/fex/Gzip_Reader.cpp +++ b/fex/fex/Gzip_Reader.cpp @@ -1,85 +1,85 @@ -// File_Extractor 1.0.0. http://www.slack.net/~ant/ - -#include "Gzip_Reader.h" - -#include "blargg_endian.h" - -/* Copyright (C) 2006-2009 Shay Green. This module is free software; you -can redistribute it and/or modify it under the terms of the GNU Lesser -General Public License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. This -module 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 Lesser General Public License for more -details. You should have received a copy of the GNU Lesser General Public -License along with this module; if not, write to the Free Software Foundation, -Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ - -#include "blargg_source.h" - -Gzip_Reader::Gzip_Reader() -{ - close(); -} - -Gzip_Reader::~Gzip_Reader() -{ } - -static blargg_err_t gzip_reader_read( void* file, void* out, int* count ) -{ - return STATIC_CAST(File_Reader*,file)->read_avail( out, count ); -} - -blargg_err_t Gzip_Reader::calc_size() -{ - size_ = in->size(); - crc32_ = 0; - if ( inflater.deflated() ) - { - byte trailer [8]; - int old_pos = in->tell(); - RETURN_ERR( in->seek( size_ - sizeof trailer ) ); - RETURN_ERR( in->read( trailer, sizeof trailer ) ); - RETURN_ERR( in->seek( old_pos ) ); - crc32_ = get_le32( trailer + 0 ); - - unsigned n = get_le32( trailer + 4 ); - if ( n > INT_MAX ) - return BLARGG_ERR( BLARGG_ERR_FILE_FEATURE, "gzip larger than 2GB" ); - - size_ = n; - } - return blargg_ok; -} - -blargg_err_t Gzip_Reader::open( File_Reader* new_in ) -{ - close(); - - in = new_in; - RETURN_ERR( in->seek( 0 ) ); - RETURN_ERR( inflater.begin( gzip_reader_read, new_in ) ); - RETURN_ERR( inflater.set_mode( inflater.mode_auto ) ); - RETURN_ERR( calc_size() ); - set_remain( size_ ); - - return blargg_ok; -} - -void Gzip_Reader::close() -{ - in = NULL; - inflater.end(); -} - -blargg_err_t Gzip_Reader::read_v( void* out, int count ) -{ - assert( in ); - int actual = count; - RETURN_ERR( inflater.read( out, &actual ) ); - - if ( actual != count ) - return blargg_err_file_corrupt; - - return blargg_ok; -} +// File_Extractor 1.0.0. http://www.slack.net/~ant/ + +#include "Gzip_Reader.h" + +#include "blargg_endian.h" + +/* Copyright (C) 2006-2009 Shay Green. This module is free software; you +can redistribute it and/or modify it under the terms of the GNU Lesser +General Public License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. This +module 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 Lesser General Public License for more +details. You should have received a copy of the GNU Lesser General Public +License along with this module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +Gzip_Reader::Gzip_Reader() +{ + close(); +} + +Gzip_Reader::~Gzip_Reader() +{ } + +static blargg_err_t gzip_reader_read( void* file, void* out, int* count ) +{ + return STATIC_CAST(File_Reader*,file)->read_avail( out, count ); +} + +blargg_err_t Gzip_Reader::calc_size() +{ + size_ = in->size(); + crc32_ = 0; + if ( inflater.deflated() ) + { + byte trailer [8]; + int old_pos = in->tell(); + RETURN_ERR( in->seek( size_ - sizeof trailer ) ); + RETURN_ERR( in->read( trailer, sizeof trailer ) ); + RETURN_ERR( in->seek( old_pos ) ); + crc32_ = get_le32( trailer + 0 ); + + unsigned n = get_le32( trailer + 4 ); + if ( n > INT_MAX ) + return BLARGG_ERR( BLARGG_ERR_FILE_FEATURE, "gzip larger than 2GB" ); + + size_ = n; + } + return blargg_ok; +} + +blargg_err_t Gzip_Reader::open( File_Reader* new_in ) +{ + close(); + + in = new_in; + RETURN_ERR( in->seek( 0 ) ); + RETURN_ERR( inflater.begin( gzip_reader_read, new_in ) ); + RETURN_ERR( inflater.set_mode( inflater.mode_auto ) ); + RETURN_ERR( calc_size() ); + set_remain( size_ ); + + return blargg_ok; +} + +void Gzip_Reader::close() +{ + in = NULL; + inflater.end(); +} + +blargg_err_t Gzip_Reader::read_v( void* out, int count ) +{ + assert( in ); + int actual = count; + RETURN_ERR( inflater.read( out, &actual ) ); + + if ( actual != count ) + return blargg_err_file_corrupt; + + return blargg_ok; +} diff --git a/fex/fex/Gzip_Reader.h b/fex/fex/Gzip_Reader.h index 72123ead..a9b2d6a9 100644 --- a/fex/fex/Gzip_Reader.h +++ b/fex/fex/Gzip_Reader.h @@ -1,46 +1,46 @@ -// Transparently decompresses gzip files, as well as uncompressed - -// File_Extractor 1.0.0 -#ifndef GZIP_READER_H -#define GZIP_READER_H - -#include "Data_Reader.h" -#include "Zlib_Inflater.h" - -class Gzip_Reader : public Data_Reader { -public: - // Keeps pointer to reader until close(). If - blargg_err_t open( File_Reader* ); - - // True if file is open - bool opened() const { return in != NULL; } - - // Frees memory - void close(); - - // True if file is compressed - bool deflated() const { return inflater.deflated(); } - - // CRC-32 of data, of 0 if unavailable - unsigned int crc32() const { return crc32_; } - - // Number of bytes read since opening - int tell() const { return size_ - remain(); } - -public: - Gzip_Reader(); - virtual ~Gzip_Reader(); - -protected: - virtual blargg_err_t read_v( void*, int ); - -private: - File_Reader* in; - unsigned crc32_; - int size_; - Zlib_Inflater inflater; - - blargg_err_t calc_size(); -}; - -#endif +// Transparently decompresses gzip files, as well as uncompressed + +// File_Extractor 1.0.0 +#ifndef GZIP_READER_H +#define GZIP_READER_H + +#include "Data_Reader.h" +#include "Zlib_Inflater.h" + +class Gzip_Reader : public Data_Reader { +public: + // Keeps pointer to reader until close(). If + blargg_err_t open( File_Reader* ); + + // True if file is open + bool opened() const { return in != NULL; } + + // Frees memory + void close(); + + // True if file is compressed + bool deflated() const { return inflater.deflated(); } + + // CRC-32 of data, of 0 if unavailable + unsigned int crc32() const { return crc32_; } + + // Number of bytes read since opening + int tell() const { return size_ - remain(); } + +public: + Gzip_Reader(); + virtual ~Gzip_Reader(); + +protected: + virtual blargg_err_t read_v( void*, int ); + +private: + File_Reader* in; + unsigned crc32_; + int size_; + Zlib_Inflater inflater; + + blargg_err_t calc_size(); +}; + +#endif diff --git a/fex/fex/Rar_Extractor.cpp b/fex/fex/Rar_Extractor.cpp index 41058646..afade7fa 100644 --- a/fex/fex/Rar_Extractor.cpp +++ b/fex/fex/Rar_Extractor.cpp @@ -1,197 +1,197 @@ -// File_Extractor 1.0.0. http://www.slack.net/~ant/ - -#include "blargg_common.h" - -#if FEX_ENABLE_RAR - -#include "Rar_Extractor.h" - -/* Copyright (C) 2009 Shay Green. This module is free software; you -can redistribute it and/or modify it under the terms of the GNU Lesser -General Public License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. This -module 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 Lesser General Public License for more -details. You should have received a copy of the GNU Lesser General Public -License along with this module; if not, write to the Free Software Foundation, -Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ - -#include "blargg_source.h" - -static blargg_err_t init_rar() -{ - unrar_init(); - return blargg_ok; -} - -static File_Extractor* new_rar() -{ - return BLARGG_NEW Rar_Extractor; -} - -fex_type_t_ const fex_rar_type [1] = {{ - ".rar", - &new_rar, - "RAR archive", - &init_rar -}}; - -blargg_err_t Rar_Extractor::convert_err( unrar_err_t err ) -{ - blargg_err_t reader_err = reader.err; - reader.err = blargg_ok; - if ( reader_err ) - check( err == unrar_next_err ); - - switch ( err ) - { - case unrar_ok: return blargg_ok; - case unrar_err_memory: return blargg_err_memory; - case unrar_err_open: return blargg_err_file_read; - case unrar_err_not_arc: return blargg_err_file_type; - case unrar_err_corrupt: return blargg_err_file_corrupt; - case unrar_err_io: return blargg_err_file_io; - case unrar_err_arc_eof: return blargg_err_internal; - case unrar_err_encrypted: return BLARGG_ERR( BLARGG_ERR_FILE_FEATURE, "RAR encryption not supported" ); - case unrar_err_segmented: return BLARGG_ERR( BLARGG_ERR_FILE_FEATURE, "RAR segmentation not supported" ); - case unrar_err_huge: return BLARGG_ERR( BLARGG_ERR_FILE_FEATURE, "Huge RAR files not supported" ); - case unrar_err_old_algo: return BLARGG_ERR( BLARGG_ERR_FILE_FEATURE, "Old RAR compression not supported" ); - case unrar_err_new_algo: return BLARGG_ERR( BLARGG_ERR_FILE_FEATURE, "RAR uses unknown newer compression" ); - case unrar_next_err: break; - default: - check( false ); // unhandled RAR error - } - - if ( reader_err ) - return reader_err; - - check( false ); - return BLARGG_ERR( BLARGG_ERR_INTERNAL, "RAR archive" ); -} - -static inline unrar_err_t handle_err( Rar_Extractor::read_callback_t* h, blargg_err_t err ) -{ - if ( !err ) - return unrar_ok; - - h->err = err; - return unrar_next_err; -} - -extern "C" -{ - static unrar_err_t my_unrar_read( void* data, void* out, int* count, unrar_pos_t pos ) - { - // TODO: 64-bit file support - - Rar_Extractor::read_callback_t* h = STATIC_CAST(Rar_Extractor::read_callback_t*,data); - if ( h->pos != pos ) - { - blargg_err_t err = h->in->seek( pos ); - if ( err ) - return handle_err( h, err ); - - h->pos = pos; - } - - blargg_err_t err = h->in->read_avail( out, count ); - if ( err ) - return handle_err( h, err ); - - h->pos += *count; - - return unrar_ok; - } -} - -Rar_Extractor::Rar_Extractor() : - File_Extractor( fex_rar_type ) -{ - unrar = NULL; -} - -Rar_Extractor::~Rar_Extractor() -{ - close(); -} - -blargg_err_t Rar_Extractor::open_v() -{ - reader.pos = 0; - reader.in = &arc(); - reader.err = blargg_ok; - - RETURN_ERR( arc().seek( 0 ) ); - RETURN_ERR( convert_err( unrar_open_custom( &unrar, &my_unrar_read, &reader ) ) ); - return skip_unextractables(); -} - -void Rar_Extractor::close_v() -{ - unrar_close( unrar ); - - unrar = NULL; - reader.in = NULL; -} - -blargg_err_t Rar_Extractor::skip_unextractables() -{ - while ( !unrar_done( unrar ) && unrar_try_extract( unrar ) ) - RETURN_ERR( next_raw() ); - - if ( !unrar_done( unrar ) ) - { - unrar_info_t const* info = unrar_info( unrar ); - - set_name( info->name, (info->name_w && *info->name_w) ? info->name_w : NULL ); - set_info( info->size, info->dos_date, (info->is_crc32 ? info->crc : 0) ); - } - - return blargg_ok; -} - -blargg_err_t Rar_Extractor::next_raw() -{ - return convert_err( unrar_next( unrar ) ); -} - -blargg_err_t Rar_Extractor::next_v() -{ - RETURN_ERR( next_raw() ); - return skip_unextractables(); -} - -blargg_err_t Rar_Extractor::rewind_v() -{ - RETURN_ERR( convert_err( unrar_rewind( unrar ) ) ); - return skip_unextractables(); -} - -fex_pos_t Rar_Extractor::tell_arc_v() const -{ - return unrar_tell( unrar ); -} - -blargg_err_t Rar_Extractor::seek_arc_v( fex_pos_t pos ) -{ - RETURN_ERR( convert_err( unrar_seek( unrar, pos ) ) ); - return skip_unextractables(); -} - -blargg_err_t Rar_Extractor::data_v( void const** out ) -{ - return convert_err( unrar_extract_mem( unrar, out ) ); -} - -blargg_err_t Rar_Extractor::extract_v( void* out, int count ) -{ - // We can read entire file directly into user buffer - if ( count == size() ) - return convert_err( unrar_extract( unrar, out, count ) ); - - // This will call data_v() and copy from that buffer for us - return File_Extractor::extract_v( out, count ); -} - -#endif +// File_Extractor 1.0.0. http://www.slack.net/~ant/ + +#include "blargg_common.h" + +#if FEX_ENABLE_RAR + +#include "Rar_Extractor.h" + +/* Copyright (C) 2009 Shay Green. This module is free software; you +can redistribute it and/or modify it under the terms of the GNU Lesser +General Public License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. This +module 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 Lesser General Public License for more +details. You should have received a copy of the GNU Lesser General Public +License along with this module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +static blargg_err_t init_rar() +{ + unrar_init(); + return blargg_ok; +} + +static File_Extractor* new_rar() +{ + return BLARGG_NEW Rar_Extractor; +} + +fex_type_t_ const fex_rar_type [1] = {{ + ".rar", + &new_rar, + "RAR archive", + &init_rar +}}; + +blargg_err_t Rar_Extractor::convert_err( unrar_err_t err ) +{ + blargg_err_t reader_err = reader.err; + reader.err = blargg_ok; + if ( reader_err ) + check( err == unrar_next_err ); + + switch ( err ) + { + case unrar_ok: return blargg_ok; + case unrar_err_memory: return blargg_err_memory; + case unrar_err_open: return blargg_err_file_read; + case unrar_err_not_arc: return blargg_err_file_type; + case unrar_err_corrupt: return blargg_err_file_corrupt; + case unrar_err_io: return blargg_err_file_io; + case unrar_err_arc_eof: return blargg_err_internal; + case unrar_err_encrypted: return BLARGG_ERR( BLARGG_ERR_FILE_FEATURE, "RAR encryption not supported" ); + case unrar_err_segmented: return BLARGG_ERR( BLARGG_ERR_FILE_FEATURE, "RAR segmentation not supported" ); + case unrar_err_huge: return BLARGG_ERR( BLARGG_ERR_FILE_FEATURE, "Huge RAR files not supported" ); + case unrar_err_old_algo: return BLARGG_ERR( BLARGG_ERR_FILE_FEATURE, "Old RAR compression not supported" ); + case unrar_err_new_algo: return BLARGG_ERR( BLARGG_ERR_FILE_FEATURE, "RAR uses unknown newer compression" ); + case unrar_next_err: break; + default: + check( false ); // unhandled RAR error + } + + if ( reader_err ) + return reader_err; + + check( false ); + return BLARGG_ERR( BLARGG_ERR_INTERNAL, "RAR archive" ); +} + +static inline unrar_err_t handle_err( Rar_Extractor::read_callback_t* h, blargg_err_t err ) +{ + if ( !err ) + return unrar_ok; + + h->err = err; + return unrar_next_err; +} + +extern "C" +{ + static unrar_err_t my_unrar_read( void* data, void* out, int* count, unrar_pos_t pos ) + { + // TODO: 64-bit file support + + Rar_Extractor::read_callback_t* h = STATIC_CAST(Rar_Extractor::read_callback_t*,data); + if ( h->pos != pos ) + { + blargg_err_t err = h->in->seek( pos ); + if ( err ) + return handle_err( h, err ); + + h->pos = pos; + } + + blargg_err_t err = h->in->read_avail( out, count ); + if ( err ) + return handle_err( h, err ); + + h->pos += *count; + + return unrar_ok; + } +} + +Rar_Extractor::Rar_Extractor() : + File_Extractor( fex_rar_type ) +{ + unrar = NULL; +} + +Rar_Extractor::~Rar_Extractor() +{ + close(); +} + +blargg_err_t Rar_Extractor::open_v() +{ + reader.pos = 0; + reader.in = &arc(); + reader.err = blargg_ok; + + RETURN_ERR( arc().seek( 0 ) ); + RETURN_ERR( convert_err( unrar_open_custom( &unrar, &my_unrar_read, &reader ) ) ); + return skip_unextractables(); +} + +void Rar_Extractor::close_v() +{ + unrar_close( unrar ); + + unrar = NULL; + reader.in = NULL; +} + +blargg_err_t Rar_Extractor::skip_unextractables() +{ + while ( !unrar_done( unrar ) && unrar_try_extract( unrar ) ) + RETURN_ERR( next_raw() ); + + if ( !unrar_done( unrar ) ) + { + unrar_info_t const* info = unrar_info( unrar ); + + set_name( info->name, (info->name_w && *info->name_w) ? info->name_w : NULL ); + set_info( info->size, info->dos_date, (info->is_crc32 ? info->crc : 0) ); + } + + return blargg_ok; +} + +blargg_err_t Rar_Extractor::next_raw() +{ + return convert_err( unrar_next( unrar ) ); +} + +blargg_err_t Rar_Extractor::next_v() +{ + RETURN_ERR( next_raw() ); + return skip_unextractables(); +} + +blargg_err_t Rar_Extractor::rewind_v() +{ + RETURN_ERR( convert_err( unrar_rewind( unrar ) ) ); + return skip_unextractables(); +} + +fex_pos_t Rar_Extractor::tell_arc_v() const +{ + return unrar_tell( unrar ); +} + +blargg_err_t Rar_Extractor::seek_arc_v( fex_pos_t pos ) +{ + RETURN_ERR( convert_err( unrar_seek( unrar, pos ) ) ); + return skip_unextractables(); +} + +blargg_err_t Rar_Extractor::data_v( void const** out ) +{ + return convert_err( unrar_extract_mem( unrar, out ) ); +} + +blargg_err_t Rar_Extractor::extract_v( void* out, int count ) +{ + // We can read entire file directly into user buffer + if ( count == size() ) + return convert_err( unrar_extract( unrar, out, count ) ); + + // This will call data_v() and copy from that buffer for us + return File_Extractor::extract_v( out, count ); +} + +#endif diff --git a/fex/fex/Rar_Extractor.h b/fex/fex/Rar_Extractor.h index 0f0174d1..9a74dea3 100644 --- a/fex/fex/Rar_Extractor.h +++ b/fex/fex/Rar_Extractor.h @@ -1,43 +1,43 @@ -// RAR archive extractor - -// File_Extractor 1.0.0 -#ifndef RAR_EXTRACTOR_H -#define RAR_EXTRACTOR_H - -#include "File_Extractor.h" -#include "unrar/unrar.h" - -class Rar_Extractor : public File_Extractor { -public: - Rar_Extractor(); - virtual ~Rar_Extractor(); - - struct read_callback_t - { - const char* err; - int pos; - File_Reader* in; - }; - -protected: - virtual blargg_err_t open_v(); - virtual void close_v(); - - virtual blargg_err_t next_v(); - virtual blargg_err_t rewind_v(); - virtual fex_pos_t tell_arc_v() const; - virtual blargg_err_t seek_arc_v( fex_pos_t ); - - virtual blargg_err_t data_v( void const** ); - virtual blargg_err_t extract_v( void*, int ); - -private: - unrar_t* unrar; - read_callback_t reader; - - blargg_err_t convert_err( unrar_err_t ); - blargg_err_t skip_unextractables(); - blargg_err_t next_raw(); -}; - -#endif +// RAR archive extractor + +// File_Extractor 1.0.0 +#ifndef RAR_EXTRACTOR_H +#define RAR_EXTRACTOR_H + +#include "File_Extractor.h" +#include "unrar/unrar.h" + +class Rar_Extractor : public File_Extractor { +public: + Rar_Extractor(); + virtual ~Rar_Extractor(); + + struct read_callback_t + { + const char* err; + int pos; + File_Reader* in; + }; + +protected: + virtual blargg_err_t open_v(); + virtual void close_v(); + + virtual blargg_err_t next_v(); + virtual blargg_err_t rewind_v(); + virtual fex_pos_t tell_arc_v() const; + virtual blargg_err_t seek_arc_v( fex_pos_t ); + + virtual blargg_err_t data_v( void const** ); + virtual blargg_err_t extract_v( void*, int ); + +private: + unrar_t* unrar; + read_callback_t reader; + + blargg_err_t convert_err( unrar_err_t ); + blargg_err_t skip_unextractables(); + blargg_err_t next_raw(); +}; + +#endif diff --git a/fex/fex/Zip7_Extractor.cpp b/fex/fex/Zip7_Extractor.cpp index 41710251..de638063 100644 --- a/fex/fex/Zip7_Extractor.cpp +++ b/fex/fex/Zip7_Extractor.cpp @@ -1,285 +1,285 @@ -// File_Extractor 1.0.0. http://www.slack.net/~ant/ - -#include "Zip7_Extractor.h" - -extern "C" { -#include "7z_C/7z.h" -#include "7z_C/7zAlloc.h" -#include "7z_C/7zCrc.h" -} - -#include - -/* Copyright (C) 2005-2009 Shay Green. This module is free software; you -can redistribute it and/or modify it under the terms of the GNU Lesser -General Public License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. This -module 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 Lesser General Public License for more -details. You should have received a copy of the GNU Lesser General Public -License along with this module; if not, write to the Free Software Foundation, -Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ - -#include "blargg_source.h" - -static ISzAlloc zip7_alloc = { SzAlloc, SzFree }; -static ISzAlloc zip7_alloc_temp = { SzAllocTemp, SzFreeTemp }; - -struct Zip7_Extractor_Impl : - ISeekInStream -{ - CLookToRead look; - CSzArEx db; - - // SzExtract state - UInt32 block_index; - Byte* buf; - size_t buf_size; - - File_Reader* in; - const char* in_err; -}; - -extern "C" -{ - // 7-zip callbacks pass an ISeekInStream* for data, so we must cast it - // back to ISeekInStream* FIRST, then cast to our Impl structure - - static SRes zip7_read_( void* vstream, void* out, size_t* size ) - { - assert( out && size ); - ISeekInStream* stream = STATIC_CAST(ISeekInStream*,vstream); - Zip7_Extractor_Impl* impl = STATIC_CAST(Zip7_Extractor_Impl*,stream); - - long lsize = *size; - blargg_err_t err = impl->in->read_avail( out, &lsize ); - if ( err ) - { - *size = 0; - impl->in_err = err; - return SZ_ERROR_READ; - } - - *size = lsize; - return SZ_OK; - } - - static SRes zip7_seek_( void* vstream, Int64* pos, ESzSeek mode ) - { - ISeekInStream* stream = STATIC_CAST(ISeekInStream*,vstream); - Zip7_Extractor_Impl* impl = STATIC_CAST(Zip7_Extractor_Impl*,stream); - - // assert( mode != SZ_SEEK_CUR ); // never used - - if ( mode == SZ_SEEK_END ) - { - assert( *pos == 0 ); // only used to find file length - *pos = impl->in->size(); - return SZ_OK; - } - - // assert( mode == SZ_SEEK_SET ); - blargg_err_t err = impl->in->seek( *pos ); - if ( err ) - { - // don't set in_err in this case, since it might be benign - if ( err == blargg_err_file_eof ) - return SZ_ERROR_INPUT_EOF; - - impl->in_err = err; - return SZ_ERROR_READ; - } - - return SZ_OK; - } -} - -blargg_err_t Zip7_Extractor::zip7_err( int err ) -{ - // TODO: ignore in_err in some cases? unsure about which error to use - blargg_err_t in_err = impl->in_err; - impl->in_err = NULL; - if ( in_err ) - { - check( err != SZ_OK ); - return in_err; - } - - switch ( err ) - { - case SZ_OK: return blargg_ok; - case SZ_ERROR_MEM: return blargg_err_memory; - case SZ_ERROR_READ: return blargg_err_file_io; - case SZ_ERROR_CRC: - case SZ_ERROR_DATA: - case SZ_ERROR_INPUT_EOF: - case SZ_ERROR_ARCHIVE: return blargg_err_file_corrupt; - case SZ_ERROR_UNSUPPORTED: return blargg_err_file_feature; - case SZ_ERROR_NO_ARCHIVE: return blargg_err_file_type; - } - - return blargg_err_generic; -} - -static blargg_err_t init_7z() -{ - static bool inited; - if ( !inited ) - { - inited = true; - CrcGenerateTable(); - } - return blargg_ok; -} - -static File_Extractor* new_7z() -{ - return BLARGG_NEW Zip7_Extractor; -} - -fex_type_t_ const fex_7z_type [1] = {{ - ".7z", - &new_7z, - "7-zip archive", - &init_7z -}}; - -Zip7_Extractor::Zip7_Extractor() : - File_Extractor( fex_7z_type ) -{ - impl = NULL; -} - -Zip7_Extractor::~Zip7_Extractor() -{ - close(); -} - -blargg_err_t Zip7_Extractor::open_v() -{ - RETURN_ERR( init_7z() ); - - if ( !impl ) - { - impl = (Zip7_Extractor_Impl*) malloc( sizeof *impl ); - CHECK_ALLOC( impl ); - } - - impl->in = &arc(); - impl->block_index = (UInt32) -1; - impl->buf = NULL; - impl->buf_size = 0; - - LookToRead_CreateVTable( &impl->look, false ); - impl->ISeekInStream::Read = zip7_read_; - impl->ISeekInStream::Seek = zip7_seek_; - impl->look.realStream = impl; - LookToRead_Init( &impl->look ); - - SzArEx_Init( &impl->db ); - - impl->in_err = NULL; - RETURN_ERR( zip7_err( SzArEx_Open( &impl->db, &impl->look.s, - &zip7_alloc, &zip7_alloc_temp ) ) ); - - return seek_arc_v( 0 ); -} - -void Zip7_Extractor::close_v() -{ - if ( impl ) - { - if ( impl->in ) - { - impl->in = NULL; - SzArEx_Free( &impl->db, &zip7_alloc ); - } - IAlloc_Free( &zip7_alloc, impl->buf ); - free( impl ); - impl = NULL; - } -} - -blargg_err_t Zip7_Extractor::next_v() -{ - while ( ++index < (int) impl->db.db.NumFiles ) - { - CSzFileItem const& item = impl->db.db.Files [index]; - if ( !item.IsDir ) - { - unsigned long date = 0; - if ( item.MTimeDefined ) - { - const UInt64 epoch = ((UInt64)0x019db1de << 32) + 0xd53e8000; - /* 0x019db1ded53e8000ULL: 1970-01-01 00:00:00 (UTC) */ - struct tm tm; - - UInt64 time = ((UInt64)item.MTime.High << 32) + item.MTime.Low - epoch; - time /= 1000000; - - time_t _time = time; - - #ifdef _WIN32 - localtime_s( &tm, &_time ); - #else - localtime_r( &_time, &tm ); - #endif - - date = ( tm.tm_sec >> 1 ) & 0x1F | - (( tm.tm_min & 0x3F ) << 5 ) | - (( tm.tm_hour & 0x1F ) << 11 ) | - (( tm.tm_mday & 0x1F ) << 16 ) | - (( ( tm.tm_mon + 1 ) & 0x0F ) << 21 ) | - (( ( tm.tm_year - 80 ) & 0x7F ) << 25 ); - } - - size_t name_length = SzArEx_GetFileNameUtf16( &impl->db, index, 0 ); - name16.resize( name_length ); - SzArEx_GetFileNameUtf16( &impl->db, index, ( UInt16 * ) name16.begin() ); - char * temp = blargg_to_utf8( name16.begin() ); - if ( !temp ) temp = ""; - size_t utf8_length = strlen( temp ); - name8.resize( utf8_length + 1 ); - memcpy( name8.begin(), temp, utf8_length + 1 ); - free( temp ); - set_name( name8.begin(), name16.begin() ); - set_info( item.Size, 0, (item.CrcDefined ? item.Crc : 0) ); - break; - } - } - - return blargg_ok; -} - -blargg_err_t Zip7_Extractor::rewind_v() -{ - return seek_arc_v( 0 ); -} - -fex_pos_t Zip7_Extractor::tell_arc_v() const -{ - return index; -} - -blargg_err_t Zip7_Extractor::seek_arc_v( fex_pos_t pos ) -{ - assert( 0 <= pos && pos <= (int) impl->db.db.NumFiles ); - - index = pos - 1; - return next_v(); -} - -blargg_err_t Zip7_Extractor::data_v( void const** out ) -{ - impl->in_err = NULL; - size_t offset = 0; - size_t count = 0; - RETURN_ERR( zip7_err( SzArEx_Extract( &impl->db, &impl->look.s, index, - &impl->block_index, &impl->buf, &impl->buf_size, - &offset, &count, &zip7_alloc, &zip7_alloc_temp ) ) ); - assert( count == (size_t) size() ); - - *out = impl->buf + offset; - return blargg_ok; -} +// File_Extractor 1.0.0. http://www.slack.net/~ant/ + +#include "Zip7_Extractor.h" + +extern "C" { +#include "7z_C/7z.h" +#include "7z_C/7zAlloc.h" +#include "7z_C/7zCrc.h" +} + +#include + +/* Copyright (C) 2005-2009 Shay Green. This module is free software; you +can redistribute it and/or modify it under the terms of the GNU Lesser +General Public License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. This +module 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 Lesser General Public License for more +details. You should have received a copy of the GNU Lesser General Public +License along with this module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +static ISzAlloc zip7_alloc = { SzAlloc, SzFree }; +static ISzAlloc zip7_alloc_temp = { SzAllocTemp, SzFreeTemp }; + +struct Zip7_Extractor_Impl : + ISeekInStream +{ + CLookToRead look; + CSzArEx db; + + // SzExtract state + UInt32 block_index; + Byte* buf; + size_t buf_size; + + File_Reader* in; + const char* in_err; +}; + +extern "C" +{ + // 7-zip callbacks pass an ISeekInStream* for data, so we must cast it + // back to ISeekInStream* FIRST, then cast to our Impl structure + + static SRes zip7_read_( void* vstream, void* out, size_t* size ) + { + assert( out && size ); + ISeekInStream* stream = STATIC_CAST(ISeekInStream*,vstream); + Zip7_Extractor_Impl* impl = STATIC_CAST(Zip7_Extractor_Impl*,stream); + + long lsize = *size; + blargg_err_t err = impl->in->read_avail( out, &lsize ); + if ( err ) + { + *size = 0; + impl->in_err = err; + return SZ_ERROR_READ; + } + + *size = lsize; + return SZ_OK; + } + + static SRes zip7_seek_( void* vstream, Int64* pos, ESzSeek mode ) + { + ISeekInStream* stream = STATIC_CAST(ISeekInStream*,vstream); + Zip7_Extractor_Impl* impl = STATIC_CAST(Zip7_Extractor_Impl*,stream); + + // assert( mode != SZ_SEEK_CUR ); // never used + + if ( mode == SZ_SEEK_END ) + { + assert( *pos == 0 ); // only used to find file length + *pos = impl->in->size(); + return SZ_OK; + } + + // assert( mode == SZ_SEEK_SET ); + blargg_err_t err = impl->in->seek( *pos ); + if ( err ) + { + // don't set in_err in this case, since it might be benign + if ( err == blargg_err_file_eof ) + return SZ_ERROR_INPUT_EOF; + + impl->in_err = err; + return SZ_ERROR_READ; + } + + return SZ_OK; + } +} + +blargg_err_t Zip7_Extractor::zip7_err( int err ) +{ + // TODO: ignore in_err in some cases? unsure about which error to use + blargg_err_t in_err = impl->in_err; + impl->in_err = NULL; + if ( in_err ) + { + check( err != SZ_OK ); + return in_err; + } + + switch ( err ) + { + case SZ_OK: return blargg_ok; + case SZ_ERROR_MEM: return blargg_err_memory; + case SZ_ERROR_READ: return blargg_err_file_io; + case SZ_ERROR_CRC: + case SZ_ERROR_DATA: + case SZ_ERROR_INPUT_EOF: + case SZ_ERROR_ARCHIVE: return blargg_err_file_corrupt; + case SZ_ERROR_UNSUPPORTED: return blargg_err_file_feature; + case SZ_ERROR_NO_ARCHIVE: return blargg_err_file_type; + } + + return blargg_err_generic; +} + +static blargg_err_t init_7z() +{ + static bool inited; + if ( !inited ) + { + inited = true; + CrcGenerateTable(); + } + return blargg_ok; +} + +static File_Extractor* new_7z() +{ + return BLARGG_NEW Zip7_Extractor; +} + +fex_type_t_ const fex_7z_type [1] = {{ + ".7z", + &new_7z, + "7-zip archive", + &init_7z +}}; + +Zip7_Extractor::Zip7_Extractor() : + File_Extractor( fex_7z_type ) +{ + impl = NULL; +} + +Zip7_Extractor::~Zip7_Extractor() +{ + close(); +} + +blargg_err_t Zip7_Extractor::open_v() +{ + RETURN_ERR( init_7z() ); + + if ( !impl ) + { + impl = (Zip7_Extractor_Impl*) malloc( sizeof *impl ); + CHECK_ALLOC( impl ); + } + + impl->in = &arc(); + impl->block_index = (UInt32) -1; + impl->buf = NULL; + impl->buf_size = 0; + + LookToRead_CreateVTable( &impl->look, false ); + impl->ISeekInStream::Read = zip7_read_; + impl->ISeekInStream::Seek = zip7_seek_; + impl->look.realStream = impl; + LookToRead_Init( &impl->look ); + + SzArEx_Init( &impl->db ); + + impl->in_err = NULL; + RETURN_ERR( zip7_err( SzArEx_Open( &impl->db, &impl->look.s, + &zip7_alloc, &zip7_alloc_temp ) ) ); + + return seek_arc_v( 0 ); +} + +void Zip7_Extractor::close_v() +{ + if ( impl ) + { + if ( impl->in ) + { + impl->in = NULL; + SzArEx_Free( &impl->db, &zip7_alloc ); + } + IAlloc_Free( &zip7_alloc, impl->buf ); + free( impl ); + impl = NULL; + } +} + +blargg_err_t Zip7_Extractor::next_v() +{ + while ( ++index < (int) impl->db.db.NumFiles ) + { + CSzFileItem const& item = impl->db.db.Files [index]; + if ( !item.IsDir ) + { + unsigned long date = 0; + if ( item.MTimeDefined ) + { + const UInt64 epoch = ((UInt64)0x019db1de << 32) + 0xd53e8000; + /* 0x019db1ded53e8000ULL: 1970-01-01 00:00:00 (UTC) */ + struct tm tm; + + UInt64 time = ((UInt64)item.MTime.High << 32) + item.MTime.Low - epoch; + time /= 1000000; + + time_t _time = time; + + #ifdef _WIN32 + localtime_s( &tm, &_time ); + #else + localtime_r( &_time, &tm ); + #endif + + date = ( tm.tm_sec >> 1 ) & 0x1F | + (( tm.tm_min & 0x3F ) << 5 ) | + (( tm.tm_hour & 0x1F ) << 11 ) | + (( tm.tm_mday & 0x1F ) << 16 ) | + (( ( tm.tm_mon + 1 ) & 0x0F ) << 21 ) | + (( ( tm.tm_year - 80 ) & 0x7F ) << 25 ); + } + + size_t name_length = SzArEx_GetFileNameUtf16( &impl->db, index, 0 ); + name16.resize( name_length ); + SzArEx_GetFileNameUtf16( &impl->db, index, ( UInt16 * ) name16.begin() ); + char * temp = blargg_to_utf8( name16.begin() ); + if ( !temp ) temp = ""; + size_t utf8_length = strlen( temp ); + name8.resize( utf8_length + 1 ); + memcpy( name8.begin(), temp, utf8_length + 1 ); + free( temp ); + set_name( name8.begin(), name16.begin() ); + set_info( item.Size, 0, (item.CrcDefined ? item.Crc : 0) ); + break; + } + } + + return blargg_ok; +} + +blargg_err_t Zip7_Extractor::rewind_v() +{ + return seek_arc_v( 0 ); +} + +fex_pos_t Zip7_Extractor::tell_arc_v() const +{ + return index; +} + +blargg_err_t Zip7_Extractor::seek_arc_v( fex_pos_t pos ) +{ + assert( 0 <= pos && pos <= (int) impl->db.db.NumFiles ); + + index = pos - 1; + return next_v(); +} + +blargg_err_t Zip7_Extractor::data_v( void const** out ) +{ + impl->in_err = NULL; + size_t offset = 0; + size_t count = 0; + RETURN_ERR( zip7_err( SzArEx_Extract( &impl->db, &impl->look.s, index, + &impl->block_index, &impl->buf, &impl->buf_size, + &offset, &count, &zip7_alloc, &zip7_alloc_temp ) ) ); + assert( count == (size_t) size() ); + + *out = impl->buf + offset; + return blargg_ok; +} diff --git a/fex/fex/Zip7_Extractor.h b/fex/fex/Zip7_Extractor.h index 8a48c960..d166b4eb 100644 --- a/fex/fex/Zip7_Extractor.h +++ b/fex/fex/Zip7_Extractor.h @@ -1,36 +1,36 @@ -// 7-zip archive extractor - -// File_Extractor 1.0.0 -#ifndef ZIP7_EXTRACTOR_H -#define ZIP7_EXTRACTOR_H - -#include "File_Extractor.h" - -struct Zip7_Extractor_Impl; - -class Zip7_Extractor : public File_Extractor { -public: - Zip7_Extractor(); - virtual ~Zip7_Extractor(); - -protected: - virtual blargg_err_t open_v(); - virtual void close_v(); - - virtual blargg_err_t next_v(); - virtual blargg_err_t rewind_v(); - virtual fex_pos_t tell_arc_v() const; - virtual blargg_err_t seek_arc_v( fex_pos_t ); - - virtual blargg_err_t data_v( void const** out ); - -private: - Zip7_Extractor_Impl* impl; - int index; - blargg_vector name8; - blargg_vector name16; - - blargg_err_t zip7_err( int err ); -}; - -#endif +// 7-zip archive extractor + +// File_Extractor 1.0.0 +#ifndef ZIP7_EXTRACTOR_H +#define ZIP7_EXTRACTOR_H + +#include "File_Extractor.h" + +struct Zip7_Extractor_Impl; + +class Zip7_Extractor : public File_Extractor { +public: + Zip7_Extractor(); + virtual ~Zip7_Extractor(); + +protected: + virtual blargg_err_t open_v(); + virtual void close_v(); + + virtual blargg_err_t next_v(); + virtual blargg_err_t rewind_v(); + virtual fex_pos_t tell_arc_v() const; + virtual blargg_err_t seek_arc_v( fex_pos_t ); + + virtual blargg_err_t data_v( void const** out ); + +private: + Zip7_Extractor_Impl* impl; + int index; + blargg_vector name8; + blargg_vector name16; + + blargg_err_t zip7_err( int err ); +}; + +#endif diff --git a/fex/fex/Zip_Extractor.cpp b/fex/fex/Zip_Extractor.cpp index baeb8e32..8bcc61c3 100644 --- a/fex/fex/Zip_Extractor.cpp +++ b/fex/fex/Zip_Extractor.cpp @@ -1,390 +1,390 @@ -// File_Extractor 1.0.0. http://www.slack.net/~ant/ - -#include "Zip_Extractor.h" - -#include "blargg_endian.h" - -/* Copyright (C) 2005-2009 Shay Green. This module is free software; you -can redistribute it and/or modify it under the terms of the GNU Lesser -General Public License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. This -module 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 Lesser General Public License for more -details. You should have received a copy of the GNU Lesser General Public -License along with this module; if not, write to the Free Software Foundation, -Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ - -/* To avoid copying filename string from catalog, I terminate it by modifying -catalog data. This potentially requires moving the first byte of the type -of the next entry elsewhere; I move it to the first byte of made_by. Kind -of hacky, but I'd rather not have to allocate memory for a copy of it. */ - -#include "blargg_source.h" - -/* Reads this much from end of file when first opening. Only this much is -searched for the end catalog entry. If whole catalog is within this data, -nothing more needs to be read on open. */ -int const end_read_size = 8 * 1024; - -/* Reads are are made using file offset that's a multiple of this, -increasing performance. */ -int const disk_block_size = 4 * 1024; - -// Read buffer used for extracting file data -int const read_buf_size = 16 * 1024; - -struct header_t -{ - char type [4]; - byte vers [2]; - byte flags [2]; - byte method [2]; - byte date [4]; - byte crc [4]; - byte raw_size [4]; - byte size [4]; - byte filename_len [2]; - byte extra_len [2]; - char filename [2]; // [filename_len] - //char extra [extra_len]; -}; -int const header_size = 30; - -struct entry_t -{ - char type [4]; - byte made_by [2]; - byte vers [2]; - byte flags [2]; - byte method [2]; - byte date [4]; - byte crc [4]; - byte raw_size [4]; - byte size [4]; - byte filename_len [2]; - byte extra_len [2]; - byte comment_len [2]; - byte disk [2]; - byte int_attrib [2]; - byte ext_attrib [4]; - byte file_offset [4]; - char filename [2]; // [filename_len] - //char extra [extra_len]; - //char comment [comment_len]; -}; -int const entry_size = 46; - -struct end_entry_t -{ - char type [4]; - byte disk [2]; - byte first_disk [2]; - byte disk_entry_count [2]; - byte entry_count [2]; - byte dir_size [4]; - byte dir_offset [4]; - byte comment_len [2]; - char comment [2]; // [comment_len] -}; -int const end_entry_size = 22; - -static blargg_err_t init_zip() -{ - get_crc_table(); // initialize zlib's CRC-32 tables - return blargg_ok; -} - -static File_Extractor* new_zip() -{ - return BLARGG_NEW Zip_Extractor; -} - -fex_type_t_ const fex_zip_type [1] = {{ - ".zip", - &new_zip, - "ZIP archive", - &init_zip -}}; - -Zip_Extractor::Zip_Extractor() : - File_Extractor( fex_zip_type ) -{ - Zip_Extractor::clear_file_v(); - - // If these fail, structures had extra padding inserted by compiler - assert( offsetof (header_t,filename) == header_size ); - assert( offsetof (entry_t,filename) == entry_size ); - assert( offsetof (end_entry_t,comment) == end_entry_size ); -} - -Zip_Extractor::~Zip_Extractor() -{ - close(); -} - -blargg_err_t Zip_Extractor::open_path_v() -{ - RETURN_ERR( open_arc_file( true ) ); - return File_Extractor::open_path_v(); -} - -inline -void Zip_Extractor::reorder_entry_header( int offset ) -{ - catalog [offset + 0] = 0; - catalog [offset + 4] = 'P'; -} - -blargg_err_t Zip_Extractor::open_v() -{ - if ( arc().size() < end_entry_size ) - return blargg_err_file_type; - - // Read final end_read_size bytes of file - int file_pos = max( 0, arc().size() - end_read_size ); - file_pos -= file_pos % disk_block_size; - RETURN_ERR( catalog.resize( arc().size() - file_pos ) ); - RETURN_ERR( arc().seek( file_pos ) ); - RETURN_ERR( arc().read( catalog.begin(), catalog.size() ) ); - - // Find end-of-catalog entry - int end_pos = catalog.size() - end_entry_size; - while ( end_pos >= 0 && memcmp( &catalog [end_pos], "PK\5\6", 4 ) ) - end_pos--; - if ( end_pos < 0 ) - return blargg_err_file_type; - end_entry_t const& end_entry = (end_entry_t&) catalog [end_pos]; - end_pos += file_pos; - - // some idiotic zip compressors add data to end of zip without setting comment len -// check( arc().size() == end_pos + end_entry_size + get_le16( end_entry.comment_len ) ); - - // Find file offset of beginning of catalog - catalog_begin = get_le32( end_entry.dir_offset ); - int catalog_size = end_pos - catalog_begin; - if ( catalog_size < 0 ) - return blargg_err_file_corrupt; - catalog_size += end_entry_size; - - // See if catalog is entirely contained in bytes already read - int begin_offset = catalog_begin - file_pos; - if ( begin_offset >= 0 ) - memmove( catalog.begin(), &catalog [begin_offset], catalog_size ); - - RETURN_ERR( catalog.resize( catalog_size ) ); - if ( begin_offset < 0 ) - { - // Catalog begins before bytes read, so it needs to be read - RETURN_ERR( arc().seek( catalog_begin ) ); - RETURN_ERR( arc().read( catalog.begin(), catalog.size() ) ); - } - - // First entry in catalog should be a file or end of archive - if ( memcmp( catalog.begin(), "PK\1\2", 4 ) && memcmp( catalog.begin(), "PK\5\6", 4 ) ) - return blargg_err_file_type; - - reorder_entry_header( 0 ); - return rewind_v(); -} - -void Zip_Extractor::close_v() -{ - catalog.clear(); -} - -// Scanning - -inline -static bool is_normal_file( entry_t const& e, unsigned len ) -{ - int last_char = (len ? e.filename [len - 1] : '/'); - bool is_dir = (last_char == '/' || last_char == '\\'); - if ( is_dir && get_le32( e.size ) == 0 ) - return false; - check( !is_dir ); - - // Mac OS X puts meta-information in separate files with normal extensions, - // so they must be filtered out or caller will mistake them for normal files. - if ( e.made_by[1] == 3 ) - { - const char* dir = strrchr( e.filename, '/' ); - if ( dir ) - dir++; - else - dir = e.filename; - - if ( *dir == '.' ) - return false; - - if ( !strcmp( dir, "Icon\x0D" ) ) - return false; - } - - return true; -} - -blargg_err_t Zip_Extractor::update_info( bool advance_first ) -{ - while ( 1 ) - { - entry_t& e = (entry_t&) catalog [catalog_pos]; - - if ( memcmp( e.type, "\0K\1\2P", 5 ) && memcmp( e.type, "PK\1\2", 4 ) ) - { - check( !memcmp( e.type, "\0K\5\6P", 5 ) ); - break; - } - - unsigned len = get_le16( e.filename_len ); - int next_offset = catalog_pos + entry_size + len + get_le16( e.extra_len ) + - get_le16( e.comment_len ); - if ( (unsigned) next_offset > catalog.size() - end_entry_size ) - return blargg_err_file_corrupt; - - if ( catalog [next_offset] == 'P' ) - reorder_entry_header( next_offset ); - - if ( !advance_first ) - { - e.filename [len] = 0; // terminate name - - if ( is_normal_file( e, len ) ) - { - set_name( e.filename ); - set_info( get_le32( e.size ), get_le32( e.date ), get_le32( e.crc ) ); - break; - } - } - - catalog_pos = next_offset; - advance_first = false; - } - - return blargg_ok; -} - -blargg_err_t Zip_Extractor::next_v() -{ - return update_info( true ); -} - -blargg_err_t Zip_Extractor::rewind_v() -{ - return seek_arc_v( 0 ); -} - -fex_pos_t Zip_Extractor::tell_arc_v() const -{ - return catalog_pos; -} - -blargg_err_t Zip_Extractor::seek_arc_v( fex_pos_t pos ) -{ - assert( 0 <= pos && (size_t) pos <= catalog.size() - end_entry_size ); - - catalog_pos = pos; - return update_info( false ); -} - -// Reading - -void Zip_Extractor::clear_file_v() -{ - buf.end(); -} - -blargg_err_t Zip_Extractor::inflater_read( void* data, void* out, int* count ) -{ - Zip_Extractor& self = *STATIC_CAST(Zip_Extractor*,data); - - if ( *count > self.raw_remain ) - *count = self.raw_remain; - - self.raw_remain -= *count; - - return self.arc().read( out, *count ); -} - -blargg_err_t Zip_Extractor::fill_buf( int offset, int buf_size, int initial_read ) -{ - raw_remain = arc().size() - offset; - RETURN_ERR( arc().seek( offset ) ); - return buf.begin( inflater_read, this, buf_size, initial_read ); -} - -blargg_err_t Zip_Extractor::first_read( int count ) -{ - entry_t const& e = (entry_t&) catalog [catalog_pos]; - - // Determine compression - { - int method = get_le16( e.method ); - if ( (method && method != Z_DEFLATED) || get_le16( e.vers ) > 20 ) - return BLARGG_ERR( BLARGG_ERR_FILE_FEATURE, "compression method" ); - file_deflated = (method != 0); - } - - int raw_size = get_le32( e.raw_size ); - - int file_offset = get_le32( e.file_offset ); - int align = file_offset % disk_block_size; - { - // read header - int buf_size = 3 * disk_block_size - 1 + raw_size; // space for all raw data - buf_size -= buf_size % disk_block_size; - int initial_read = buf_size; - if ( !file_deflated || count < size() ) - { - buf_size = read_buf_size; - initial_read = disk_block_size * 2; - } - // TODO: avoid re-reading if buffer already has data we want? - RETURN_ERR( fill_buf( file_offset - align, buf_size, initial_read ) ); - } - header_t const& h = (header_t&) buf.data() [align]; - if ( buf.filled() < align + header_size || memcmp( h.type, "PK\3\4", 4 ) ) - return blargg_err_file_corrupt; - - // CRCs of header and file data - correct_crc = get_le32( h.crc ); - if ( !correct_crc ) - correct_crc = get_le32( e.crc ); - check( correct_crc == get_le32( e.crc ) ); // catalog CRC should match - crc = ::crc32( 0, NULL, 0 ); - - // Data offset - int data_offset = file_offset + header_size + - get_le16( h.filename_len ) + get_le16( h.extra_len ); - if ( data_offset + raw_size > catalog_begin ) - return blargg_err_file_corrupt; - - // Refill buffer if there's lots of extra data after header - int buf_offset = data_offset - file_offset + align; - if ( buf_offset > buf.filled() ) - { - // TODO: this will almost never occur, making it a good place for bugs - buf_offset = data_offset % disk_block_size; - RETURN_ERR( fill_buf( data_offset - buf_offset, read_buf_size, disk_block_size ) ); - } - - raw_remain = raw_size - (buf.filled() - buf_offset); - return buf.set_mode( (file_deflated ? buf.mode_raw_deflate : buf.mode_copy), buf_offset ); -} - -blargg_err_t Zip_Extractor::extract_v( void* out, int count ) -{ - if ( tell() == 0 ) - RETURN_ERR( first_read( count ) ); - - int actual = count; - RETURN_ERR( buf.read( out, &actual ) ); - if ( actual < count ) - return blargg_err_file_corrupt; - - crc = ::crc32( crc, (byte const*) out, count ); - if ( count == reader().remain() && crc != correct_crc ) - return blargg_err_file_corrupt; - - return blargg_ok; -} +// File_Extractor 1.0.0. http://www.slack.net/~ant/ + +#include "Zip_Extractor.h" + +#include "blargg_endian.h" + +/* Copyright (C) 2005-2009 Shay Green. This module is free software; you +can redistribute it and/or modify it under the terms of the GNU Lesser +General Public License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. This +module 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 Lesser General Public License for more +details. You should have received a copy of the GNU Lesser General Public +License along with this module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +/* To avoid copying filename string from catalog, I terminate it by modifying +catalog data. This potentially requires moving the first byte of the type +of the next entry elsewhere; I move it to the first byte of made_by. Kind +of hacky, but I'd rather not have to allocate memory for a copy of it. */ + +#include "blargg_source.h" + +/* Reads this much from end of file when first opening. Only this much is +searched for the end catalog entry. If whole catalog is within this data, +nothing more needs to be read on open. */ +int const end_read_size = 8 * 1024; + +/* Reads are are made using file offset that's a multiple of this, +increasing performance. */ +int const disk_block_size = 4 * 1024; + +// Read buffer used for extracting file data +int const read_buf_size = 16 * 1024; + +struct header_t +{ + char type [4]; + byte vers [2]; + byte flags [2]; + byte method [2]; + byte date [4]; + byte crc [4]; + byte raw_size [4]; + byte size [4]; + byte filename_len [2]; + byte extra_len [2]; + char filename [2]; // [filename_len] + //char extra [extra_len]; +}; +int const header_size = 30; + +struct entry_t +{ + char type [4]; + byte made_by [2]; + byte vers [2]; + byte flags [2]; + byte method [2]; + byte date [4]; + byte crc [4]; + byte raw_size [4]; + byte size [4]; + byte filename_len [2]; + byte extra_len [2]; + byte comment_len [2]; + byte disk [2]; + byte int_attrib [2]; + byte ext_attrib [4]; + byte file_offset [4]; + char filename [2]; // [filename_len] + //char extra [extra_len]; + //char comment [comment_len]; +}; +int const entry_size = 46; + +struct end_entry_t +{ + char type [4]; + byte disk [2]; + byte first_disk [2]; + byte disk_entry_count [2]; + byte entry_count [2]; + byte dir_size [4]; + byte dir_offset [4]; + byte comment_len [2]; + char comment [2]; // [comment_len] +}; +int const end_entry_size = 22; + +static blargg_err_t init_zip() +{ + get_crc_table(); // initialize zlib's CRC-32 tables + return blargg_ok; +} + +static File_Extractor* new_zip() +{ + return BLARGG_NEW Zip_Extractor; +} + +fex_type_t_ const fex_zip_type [1] = {{ + ".zip", + &new_zip, + "ZIP archive", + &init_zip +}}; + +Zip_Extractor::Zip_Extractor() : + File_Extractor( fex_zip_type ) +{ + Zip_Extractor::clear_file_v(); + + // If these fail, structures had extra padding inserted by compiler + assert( offsetof (header_t,filename) == header_size ); + assert( offsetof (entry_t,filename) == entry_size ); + assert( offsetof (end_entry_t,comment) == end_entry_size ); +} + +Zip_Extractor::~Zip_Extractor() +{ + close(); +} + +blargg_err_t Zip_Extractor::open_path_v() +{ + RETURN_ERR( open_arc_file( true ) ); + return File_Extractor::open_path_v(); +} + +inline +void Zip_Extractor::reorder_entry_header( int offset ) +{ + catalog [offset + 0] = 0; + catalog [offset + 4] = 'P'; +} + +blargg_err_t Zip_Extractor::open_v() +{ + if ( arc().size() < end_entry_size ) + return blargg_err_file_type; + + // Read final end_read_size bytes of file + int file_pos = max( 0, arc().size() - end_read_size ); + file_pos -= file_pos % disk_block_size; + RETURN_ERR( catalog.resize( arc().size() - file_pos ) ); + RETURN_ERR( arc().seek( file_pos ) ); + RETURN_ERR( arc().read( catalog.begin(), catalog.size() ) ); + + // Find end-of-catalog entry + int end_pos = catalog.size() - end_entry_size; + while ( end_pos >= 0 && memcmp( &catalog [end_pos], "PK\5\6", 4 ) ) + end_pos--; + if ( end_pos < 0 ) + return blargg_err_file_type; + end_entry_t const& end_entry = (end_entry_t&) catalog [end_pos]; + end_pos += file_pos; + + // some idiotic zip compressors add data to end of zip without setting comment len +// check( arc().size() == end_pos + end_entry_size + get_le16( end_entry.comment_len ) ); + + // Find file offset of beginning of catalog + catalog_begin = get_le32( end_entry.dir_offset ); + int catalog_size = end_pos - catalog_begin; + if ( catalog_size < 0 ) + return blargg_err_file_corrupt; + catalog_size += end_entry_size; + + // See if catalog is entirely contained in bytes already read + int begin_offset = catalog_begin - file_pos; + if ( begin_offset >= 0 ) + memmove( catalog.begin(), &catalog [begin_offset], catalog_size ); + + RETURN_ERR( catalog.resize( catalog_size ) ); + if ( begin_offset < 0 ) + { + // Catalog begins before bytes read, so it needs to be read + RETURN_ERR( arc().seek( catalog_begin ) ); + RETURN_ERR( arc().read( catalog.begin(), catalog.size() ) ); + } + + // First entry in catalog should be a file or end of archive + if ( memcmp( catalog.begin(), "PK\1\2", 4 ) && memcmp( catalog.begin(), "PK\5\6", 4 ) ) + return blargg_err_file_type; + + reorder_entry_header( 0 ); + return rewind_v(); +} + +void Zip_Extractor::close_v() +{ + catalog.clear(); +} + +// Scanning + +inline +static bool is_normal_file( entry_t const& e, unsigned len ) +{ + int last_char = (len ? e.filename [len - 1] : '/'); + bool is_dir = (last_char == '/' || last_char == '\\'); + if ( is_dir && get_le32( e.size ) == 0 ) + return false; + check( !is_dir ); + + // Mac OS X puts meta-information in separate files with normal extensions, + // so they must be filtered out or caller will mistake them for normal files. + if ( e.made_by[1] == 3 ) + { + const char* dir = strrchr( e.filename, '/' ); + if ( dir ) + dir++; + else + dir = e.filename; + + if ( *dir == '.' ) + return false; + + if ( !strcmp( dir, "Icon\x0D" ) ) + return false; + } + + return true; +} + +blargg_err_t Zip_Extractor::update_info( bool advance_first ) +{ + while ( 1 ) + { + entry_t& e = (entry_t&) catalog [catalog_pos]; + + if ( memcmp( e.type, "\0K\1\2P", 5 ) && memcmp( e.type, "PK\1\2", 4 ) ) + { + check( !memcmp( e.type, "\0K\5\6P", 5 ) ); + break; + } + + unsigned len = get_le16( e.filename_len ); + int next_offset = catalog_pos + entry_size + len + get_le16( e.extra_len ) + + get_le16( e.comment_len ); + if ( (unsigned) next_offset > catalog.size() - end_entry_size ) + return blargg_err_file_corrupt; + + if ( catalog [next_offset] == 'P' ) + reorder_entry_header( next_offset ); + + if ( !advance_first ) + { + e.filename [len] = 0; // terminate name + + if ( is_normal_file( e, len ) ) + { + set_name( e.filename ); + set_info( get_le32( e.size ), get_le32( e.date ), get_le32( e.crc ) ); + break; + } + } + + catalog_pos = next_offset; + advance_first = false; + } + + return blargg_ok; +} + +blargg_err_t Zip_Extractor::next_v() +{ + return update_info( true ); +} + +blargg_err_t Zip_Extractor::rewind_v() +{ + return seek_arc_v( 0 ); +} + +fex_pos_t Zip_Extractor::tell_arc_v() const +{ + return catalog_pos; +} + +blargg_err_t Zip_Extractor::seek_arc_v( fex_pos_t pos ) +{ + assert( 0 <= pos && (size_t) pos <= catalog.size() - end_entry_size ); + + catalog_pos = pos; + return update_info( false ); +} + +// Reading + +void Zip_Extractor::clear_file_v() +{ + buf.end(); +} + +blargg_err_t Zip_Extractor::inflater_read( void* data, void* out, int* count ) +{ + Zip_Extractor& self = *STATIC_CAST(Zip_Extractor*,data); + + if ( *count > self.raw_remain ) + *count = self.raw_remain; + + self.raw_remain -= *count; + + return self.arc().read( out, *count ); +} + +blargg_err_t Zip_Extractor::fill_buf( int offset, int buf_size, int initial_read ) +{ + raw_remain = arc().size() - offset; + RETURN_ERR( arc().seek( offset ) ); + return buf.begin( inflater_read, this, buf_size, initial_read ); +} + +blargg_err_t Zip_Extractor::first_read( int count ) +{ + entry_t const& e = (entry_t&) catalog [catalog_pos]; + + // Determine compression + { + int method = get_le16( e.method ); + if ( (method && method != Z_DEFLATED) || get_le16( e.vers ) > 20 ) + return BLARGG_ERR( BLARGG_ERR_FILE_FEATURE, "compression method" ); + file_deflated = (method != 0); + } + + int raw_size = get_le32( e.raw_size ); + + int file_offset = get_le32( e.file_offset ); + int align = file_offset % disk_block_size; + { + // read header + int buf_size = 3 * disk_block_size - 1 + raw_size; // space for all raw data + buf_size -= buf_size % disk_block_size; + int initial_read = buf_size; + if ( !file_deflated || count < size() ) + { + buf_size = read_buf_size; + initial_read = disk_block_size * 2; + } + // TODO: avoid re-reading if buffer already has data we want? + RETURN_ERR( fill_buf( file_offset - align, buf_size, initial_read ) ); + } + header_t const& h = (header_t&) buf.data() [align]; + if ( buf.filled() < align + header_size || memcmp( h.type, "PK\3\4", 4 ) ) + return blargg_err_file_corrupt; + + // CRCs of header and file data + correct_crc = get_le32( h.crc ); + if ( !correct_crc ) + correct_crc = get_le32( e.crc ); + check( correct_crc == get_le32( e.crc ) ); // catalog CRC should match + crc = ::crc32( 0, NULL, 0 ); + + // Data offset + int data_offset = file_offset + header_size + + get_le16( h.filename_len ) + get_le16( h.extra_len ); + if ( data_offset + raw_size > catalog_begin ) + return blargg_err_file_corrupt; + + // Refill buffer if there's lots of extra data after header + int buf_offset = data_offset - file_offset + align; + if ( buf_offset > buf.filled() ) + { + // TODO: this will almost never occur, making it a good place for bugs + buf_offset = data_offset % disk_block_size; + RETURN_ERR( fill_buf( data_offset - buf_offset, read_buf_size, disk_block_size ) ); + } + + raw_remain = raw_size - (buf.filled() - buf_offset); + return buf.set_mode( (file_deflated ? buf.mode_raw_deflate : buf.mode_copy), buf_offset ); +} + +blargg_err_t Zip_Extractor::extract_v( void* out, int count ) +{ + if ( tell() == 0 ) + RETURN_ERR( first_read( count ) ); + + int actual = count; + RETURN_ERR( buf.read( out, &actual ) ); + if ( actual < count ) + return blargg_err_file_corrupt; + + crc = ::crc32( crc, (byte const*) out, count ); + if ( count == reader().remain() && crc != correct_crc ) + return blargg_err_file_corrupt; + + return blargg_ok; +} diff --git a/fex/fex/Zip_Extractor.h b/fex/fex/Zip_Extractor.h index fc7c85cb..9742df99 100644 --- a/fex/fex/Zip_Extractor.h +++ b/fex/fex/Zip_Extractor.h @@ -1,45 +1,45 @@ -// ZIP archive extractor. Only supports deflation and store (no compression). - -// File_Extractor 1.0.0 -#ifndef ZIP_EXTRACTOR_H -#define ZIP_EXTRACTOR_H - -#include "File_Extractor.h" -#include "Zlib_Inflater.h" - -class Zip_Extractor : public File_Extractor { -public: - Zip_Extractor(); - virtual ~Zip_Extractor(); - -protected: - virtual blargg_err_t open_path_v(); - virtual blargg_err_t open_v(); - virtual void close_v(); - - virtual void clear_file_v(); - virtual blargg_err_t next_v(); - virtual blargg_err_t rewind_v(); - virtual fex_pos_t tell_arc_v() const; - virtual blargg_err_t seek_arc_v( fex_pos_t ); - - virtual blargg_err_t extract_v( void*, int ); - -private: - blargg_vector catalog; - int catalog_begin; // offset of first catalog entry in file (to detect corruption) - int catalog_pos; // position of current entry in catalog - int raw_remain; // bytes remaining to be read from zip file for current file - unsigned crc; // ongoing CRC of extracted bytes - unsigned correct_crc; - bool file_deflated; - Zlib_Inflater buf; - - blargg_err_t fill_buf( int offset, int buf_size, int initial_read ); - blargg_err_t update_info( bool advance_first ); - blargg_err_t first_read( int count ); - void reorder_entry_header( int offset ); - static blargg_err_t inflater_read( void* data, void* out, int* count ); -}; - -#endif +// ZIP archive extractor. Only supports deflation and store (no compression). + +// File_Extractor 1.0.0 +#ifndef ZIP_EXTRACTOR_H +#define ZIP_EXTRACTOR_H + +#include "File_Extractor.h" +#include "Zlib_Inflater.h" + +class Zip_Extractor : public File_Extractor { +public: + Zip_Extractor(); + virtual ~Zip_Extractor(); + +protected: + virtual blargg_err_t open_path_v(); + virtual blargg_err_t open_v(); + virtual void close_v(); + + virtual void clear_file_v(); + virtual blargg_err_t next_v(); + virtual blargg_err_t rewind_v(); + virtual fex_pos_t tell_arc_v() const; + virtual blargg_err_t seek_arc_v( fex_pos_t ); + + virtual blargg_err_t extract_v( void*, int ); + +private: + blargg_vector catalog; + int catalog_begin; // offset of first catalog entry in file (to detect corruption) + int catalog_pos; // position of current entry in catalog + int raw_remain; // bytes remaining to be read from zip file for current file + unsigned crc; // ongoing CRC of extracted bytes + unsigned correct_crc; + bool file_deflated; + Zlib_Inflater buf; + + blargg_err_t fill_buf( int offset, int buf_size, int initial_read ); + blargg_err_t update_info( bool advance_first ); + blargg_err_t first_read( int count ); + void reorder_entry_header( int offset ); + static blargg_err_t inflater_read( void* data, void* out, int* count ); +}; + +#endif diff --git a/fex/fex/Zlib_Inflater.cpp b/fex/fex/Zlib_Inflater.cpp index ac77ca51..8d31b514 100644 --- a/fex/fex/Zlib_Inflater.cpp +++ b/fex/fex/Zlib_Inflater.cpp @@ -1,257 +1,257 @@ -// File_Extractor 1.0.0. http://www.slack.net/~ant/ - -#include "Zlib_Inflater.h" - -/* Copyright (C) 2006-2009 Shay Green. This module is free software; you -can redistribute it and/or modify it under the terms of the GNU Lesser -General Public License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. This -module 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 Lesser General Public License for more -details. You should have received a copy of the GNU Lesser General Public -License along with this module; if not, write to the Free Software Foundation, -Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ - -#include "blargg_source.h" - -int const block_size = 4096; - -static const char* get_zlib_err( int code ) -{ - assert( code != Z_OK ); - switch ( code ) - { - case Z_MEM_ERROR: return blargg_err_memory; - case Z_DATA_ERROR: return blargg_err_file_corrupt; - // TODO: handle more error codes - } - - const char* str = zError( code ); - if ( !str ) - str = BLARGG_ERR( BLARGG_ERR_GENERIC, "problem unzipping data" ); - - return str; -} - -void Zlib_Inflater::end() -{ - if ( deflated_ ) - { - deflated_ = false; - if ( inflateEnd( &zbuf ) ) - check( false ); - } - buf.clear(); - - static z_stream const empty = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; - memcpy( &zbuf, &empty, sizeof zbuf ); -} - -Zlib_Inflater::Zlib_Inflater() -{ - deflated_ = false; - end(); // initialize things -} - -Zlib_Inflater::~Zlib_Inflater() -{ - end(); -} - -blargg_err_t Zlib_Inflater::fill_buf( int count ) -{ - byte* out = buf.end() - count; - RETURN_ERR( callback( user_data, out, &count ) ); - zbuf.avail_in = count; - zbuf.next_in = out; - return blargg_ok; -} - -blargg_err_t Zlib_Inflater::begin( callback_t new_callback, void* new_user_data, - int new_buf_size, int initial_read ) -{ - callback = new_callback; - user_data = new_user_data; - - end(); - - // TODO: decide whether using different size on alloc failure is a good idea - //RETURN_ERR( buf.resize( new_buf_size ? new_buf_size : 4 * block_size ) ); - if ( new_buf_size && buf.resize( new_buf_size ) ) - { - ACK_FAILURE(); - new_buf_size = 0; - } - - if ( !new_buf_size ) - { - RETURN_ERR( buf.resize( 4 * block_size ) ); - initial_read = 0; - } - - // Fill buffer with some data, less than normal buffer size since caller might - // just be examining beginning of file. - return fill_buf( initial_read ? initial_read : block_size ); -} - -blargg_err_t Zlib_Inflater::set_mode( mode_t mode, int data_offset ) -{ - zbuf.next_in += data_offset; - zbuf.avail_in -= data_offset; - - if ( mode == mode_auto ) - { - // examine buffer for gzip header - mode = mode_copy; - unsigned const min_gzip_size = 2 + 8 + 8; - if ( zbuf.avail_in >= min_gzip_size && - zbuf.next_in [0] == 0x1F && zbuf.next_in [1] == 0x8B ) - mode = mode_ungz; - } - - if ( mode != mode_copy ) - { - int wb = MAX_WBITS + 16; // have zlib handle gzip header - if ( mode == mode_raw_deflate ) - wb = -MAX_WBITS; - - int zerr = inflateInit2( &zbuf, wb ); - if ( zerr ) - { - zbuf.next_in = NULL; - return get_zlib_err( zerr ); - } - - deflated_ = true; - } - return blargg_ok; -} - -/* -// Reads/inflates entire stream. All input must be in buffer, and count must be total -// of all output. -blargg_err_t read_all( void* out, int count ); - - -// zlib automatically applies this optimization (uses inflateFast) -// TODO: remove -blargg_err_t Zlib_Inflater::read_all( void* out, int count ) -{ - if ( deflated_ ) - { - zbuf.next_out = (Bytef*) out; - zbuf.avail_out = count; - - int err = inflate( &zbuf, Z_FINISH ); - - if ( zbuf.avail_out || err != Z_STREAM_END ) - return blargg_err_file_corrupt; - } - else - { - if ( zbuf.avail_in < count ) - return blargg_err_file_corrupt; - - memcpy( out, zbuf.next_in, count ); - - zbuf.next_in += count; - zbuf.avail_in -= count; - } - - return blargg_ok; -} -*/ - -blargg_err_t Zlib_Inflater::read( void* out, int* count_io ) -{ - int remain = *count_io; - if ( remain && zbuf.next_in ) - { - if ( deflated_ ) - { - zbuf.next_out = (Bytef*) out; - zbuf.avail_out = remain; - - while ( 1 ) - { - uInt old_avail_in = zbuf.avail_in; - int err = inflate( &zbuf, Z_NO_FLUSH ); - if ( err == Z_STREAM_END ) - { - remain = zbuf.avail_out; - end(); - break; // no more data to inflate - } - - if ( err && (err != Z_BUF_ERROR || old_avail_in) ) - return get_zlib_err( err ); - - if ( !zbuf.avail_out ) - { - remain = 0; - break; // requested number of bytes inflated - } - - if ( zbuf.avail_in ) - { - // inflate() should never leave input if there's still space for output - check( false ); - return blargg_err_file_corrupt; - } - - RETURN_ERR( fill_buf( buf.size() ) ); - if ( !zbuf.avail_in ) - return blargg_err_file_corrupt; // stream didn't end but there's no more data - } - } - else - { - while ( 1 ) - { - // copy buffered data - if ( zbuf.avail_in ) - { - long count = zbuf.avail_in; - if ( count > remain ) - count = remain; - memcpy( out, zbuf.next_in, count ); - zbuf.total_out += count; - out = (char*) out + count; - remain -= count; - zbuf.next_in += count; - zbuf.avail_in -= count; - } - - if ( !zbuf.avail_in && zbuf.next_in < buf.end() ) - { - end(); - break; - } - - // read large request directly - if ( remain + zbuf.total_out % block_size >= buf.size() ) - { - int count = remain; - RETURN_ERR( callback( user_data, out, &count ) ); - zbuf.total_out += count; - out = (char*) out + count; - remain -= count; - - if ( remain ) - { - end(); - break; - } - } - - if ( !remain ) - break; - - RETURN_ERR( fill_buf( buf.size() - zbuf.total_out % block_size ) ); - } - } - } - *count_io -= remain; - return blargg_ok; -} +// File_Extractor 1.0.0. http://www.slack.net/~ant/ + +#include "Zlib_Inflater.h" + +/* Copyright (C) 2006-2009 Shay Green. This module is free software; you +can redistribute it and/or modify it under the terms of the GNU Lesser +General Public License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. This +module 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 Lesser General Public License for more +details. You should have received a copy of the GNU Lesser General Public +License along with this module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +int const block_size = 4096; + +static const char* get_zlib_err( int code ) +{ + assert( code != Z_OK ); + switch ( code ) + { + case Z_MEM_ERROR: return blargg_err_memory; + case Z_DATA_ERROR: return blargg_err_file_corrupt; + // TODO: handle more error codes + } + + const char* str = zError( code ); + if ( !str ) + str = BLARGG_ERR( BLARGG_ERR_GENERIC, "problem unzipping data" ); + + return str; +} + +void Zlib_Inflater::end() +{ + if ( deflated_ ) + { + deflated_ = false; + if ( inflateEnd( &zbuf ) ) + check( false ); + } + buf.clear(); + + static z_stream const empty = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + memcpy( &zbuf, &empty, sizeof zbuf ); +} + +Zlib_Inflater::Zlib_Inflater() +{ + deflated_ = false; + end(); // initialize things +} + +Zlib_Inflater::~Zlib_Inflater() +{ + end(); +} + +blargg_err_t Zlib_Inflater::fill_buf( int count ) +{ + byte* out = buf.end() - count; + RETURN_ERR( callback( user_data, out, &count ) ); + zbuf.avail_in = count; + zbuf.next_in = out; + return blargg_ok; +} + +blargg_err_t Zlib_Inflater::begin( callback_t new_callback, void* new_user_data, + int new_buf_size, int initial_read ) +{ + callback = new_callback; + user_data = new_user_data; + + end(); + + // TODO: decide whether using different size on alloc failure is a good idea + //RETURN_ERR( buf.resize( new_buf_size ? new_buf_size : 4 * block_size ) ); + if ( new_buf_size && buf.resize( new_buf_size ) ) + { + ACK_FAILURE(); + new_buf_size = 0; + } + + if ( !new_buf_size ) + { + RETURN_ERR( buf.resize( 4 * block_size ) ); + initial_read = 0; + } + + // Fill buffer with some data, less than normal buffer size since caller might + // just be examining beginning of file. + return fill_buf( initial_read ? initial_read : block_size ); +} + +blargg_err_t Zlib_Inflater::set_mode( mode_t mode, int data_offset ) +{ + zbuf.next_in += data_offset; + zbuf.avail_in -= data_offset; + + if ( mode == mode_auto ) + { + // examine buffer for gzip header + mode = mode_copy; + unsigned const min_gzip_size = 2 + 8 + 8; + if ( zbuf.avail_in >= min_gzip_size && + zbuf.next_in [0] == 0x1F && zbuf.next_in [1] == 0x8B ) + mode = mode_ungz; + } + + if ( mode != mode_copy ) + { + int wb = MAX_WBITS + 16; // have zlib handle gzip header + if ( mode == mode_raw_deflate ) + wb = -MAX_WBITS; + + int zerr = inflateInit2( &zbuf, wb ); + if ( zerr ) + { + zbuf.next_in = NULL; + return get_zlib_err( zerr ); + } + + deflated_ = true; + } + return blargg_ok; +} + +/* +// Reads/inflates entire stream. All input must be in buffer, and count must be total +// of all output. +blargg_err_t read_all( void* out, int count ); + + +// zlib automatically applies this optimization (uses inflateFast) +// TODO: remove +blargg_err_t Zlib_Inflater::read_all( void* out, int count ) +{ + if ( deflated_ ) + { + zbuf.next_out = (Bytef*) out; + zbuf.avail_out = count; + + int err = inflate( &zbuf, Z_FINISH ); + + if ( zbuf.avail_out || err != Z_STREAM_END ) + return blargg_err_file_corrupt; + } + else + { + if ( zbuf.avail_in < count ) + return blargg_err_file_corrupt; + + memcpy( out, zbuf.next_in, count ); + + zbuf.next_in += count; + zbuf.avail_in -= count; + } + + return blargg_ok; +} +*/ + +blargg_err_t Zlib_Inflater::read( void* out, int* count_io ) +{ + int remain = *count_io; + if ( remain && zbuf.next_in ) + { + if ( deflated_ ) + { + zbuf.next_out = (Bytef*) out; + zbuf.avail_out = remain; + + while ( 1 ) + { + uInt old_avail_in = zbuf.avail_in; + int err = inflate( &zbuf, Z_NO_FLUSH ); + if ( err == Z_STREAM_END ) + { + remain = zbuf.avail_out; + end(); + break; // no more data to inflate + } + + if ( err && (err != Z_BUF_ERROR || old_avail_in) ) + return get_zlib_err( err ); + + if ( !zbuf.avail_out ) + { + remain = 0; + break; // requested number of bytes inflated + } + + if ( zbuf.avail_in ) + { + // inflate() should never leave input if there's still space for output + check( false ); + return blargg_err_file_corrupt; + } + + RETURN_ERR( fill_buf( buf.size() ) ); + if ( !zbuf.avail_in ) + return blargg_err_file_corrupt; // stream didn't end but there's no more data + } + } + else + { + while ( 1 ) + { + // copy buffered data + if ( zbuf.avail_in ) + { + long count = zbuf.avail_in; + if ( count > remain ) + count = remain; + memcpy( out, zbuf.next_in, count ); + zbuf.total_out += count; + out = (char*) out + count; + remain -= count; + zbuf.next_in += count; + zbuf.avail_in -= count; + } + + if ( !zbuf.avail_in && zbuf.next_in < buf.end() ) + { + end(); + break; + } + + // read large request directly + if ( remain + zbuf.total_out % block_size >= buf.size() ) + { + int count = remain; + RETURN_ERR( callback( user_data, out, &count ) ); + zbuf.total_out += count; + out = (char*) out + count; + remain -= count; + + if ( remain ) + { + end(); + break; + } + } + + if ( !remain ) + break; + + RETURN_ERR( fill_buf( buf.size() - zbuf.total_out % block_size ) ); + } + } + } + *count_io -= remain; + return blargg_ok; +} diff --git a/fex/fex/Zlib_Inflater.h b/fex/fex/Zlib_Inflater.h index 8f0434fd..a585b080 100644 --- a/fex/fex/Zlib_Inflater.h +++ b/fex/fex/Zlib_Inflater.h @@ -1,70 +1,70 @@ -// Simplifies use of zlib for inflating data - -// File_Extractor 1.0.0 -#ifndef ZLIB_INFLATER_H -#define ZLIB_INFLATER_H - -#include "blargg_common.h" -#include "Data_Reader.h" -#include "zlib.h" - -class Zlib_Inflater { -public: - - // Reads at most min(*count,bytes_until_eof()) bytes into *out and set *count - // to that number, or returns error if that many can't be read. - typedef blargg_err_t (*callback_t)( void* user_data, void* out, int* count ); - - // Begins by setting callback and filling buffer. Default buffer is 16K and - // filled to 4K, or specify buf_size and initial_read for custom buffer size - // and how much to read initially. - blargg_err_t begin( callback_t, void* user_data, - int buf_size = 0, int initial_read = 0 ); - - // Data read into buffer by begin() - const unsigned char* data() const { return zbuf.next_in; } - int filled() const { return zbuf.avail_in; } - - // Begins inflation using specified mode. Using mode_auto selects between - // mode_copy and mode_ungz by examining first two bytes of buffer. Use - // buf_offset to specify where data begins in buffer, in case there is - // header data that should be skipped. - enum mode_t { mode_copy, mode_ungz, mode_raw_deflate, mode_auto }; - blargg_err_t set_mode( mode_t, int buf_offset = 0 ); - - // True if set_mode() has been called with mode_ungz or mode_raw_deflate - bool deflated() const { return deflated_; } - - // Reads/inflates at most *count_io bytes into *out and sets *count_io to actual - // number of bytes read (less than requested if end of data was reached). - // Buffers source data internally, even in copy mode, so input file can be - // unbuffered without sacrificing performance. - blargg_err_t read( void* out, int* count_io ); - - // Total number of bytes read since begin() - int tell() const { return zbuf.total_out; } - - // Ends inflation and frees memory - void end(); - -private: - // noncopyable - Zlib_Inflater( const Zlib_Inflater& ); - Zlib_Inflater& operator = ( const Zlib_Inflater& ); - -// Implementation -public: - Zlib_Inflater(); - ~Zlib_Inflater(); - -private: - z_stream_s zbuf; - blargg_vector buf; - bool deflated_; - callback_t callback; - void* user_data; - - blargg_err_t fill_buf( int count ); -}; - -#endif +// Simplifies use of zlib for inflating data + +// File_Extractor 1.0.0 +#ifndef ZLIB_INFLATER_H +#define ZLIB_INFLATER_H + +#include "blargg_common.h" +#include "Data_Reader.h" +#include "zlib.h" + +class Zlib_Inflater { +public: + + // Reads at most min(*count,bytes_until_eof()) bytes into *out and set *count + // to that number, or returns error if that many can't be read. + typedef blargg_err_t (*callback_t)( void* user_data, void* out, int* count ); + + // Begins by setting callback and filling buffer. Default buffer is 16K and + // filled to 4K, or specify buf_size and initial_read for custom buffer size + // and how much to read initially. + blargg_err_t begin( callback_t, void* user_data, + int buf_size = 0, int initial_read = 0 ); + + // Data read into buffer by begin() + const unsigned char* data() const { return zbuf.next_in; } + int filled() const { return zbuf.avail_in; } + + // Begins inflation using specified mode. Using mode_auto selects between + // mode_copy and mode_ungz by examining first two bytes of buffer. Use + // buf_offset to specify where data begins in buffer, in case there is + // header data that should be skipped. + enum mode_t { mode_copy, mode_ungz, mode_raw_deflate, mode_auto }; + blargg_err_t set_mode( mode_t, int buf_offset = 0 ); + + // True if set_mode() has been called with mode_ungz or mode_raw_deflate + bool deflated() const { return deflated_; } + + // Reads/inflates at most *count_io bytes into *out and sets *count_io to actual + // number of bytes read (less than requested if end of data was reached). + // Buffers source data internally, even in copy mode, so input file can be + // unbuffered without sacrificing performance. + blargg_err_t read( void* out, int* count_io ); + + // Total number of bytes read since begin() + int tell() const { return zbuf.total_out; } + + // Ends inflation and frees memory + void end(); + +private: + // noncopyable + Zlib_Inflater( const Zlib_Inflater& ); + Zlib_Inflater& operator = ( const Zlib_Inflater& ); + +// Implementation +public: + Zlib_Inflater(); + ~Zlib_Inflater(); + +private: + z_stream_s zbuf; + blargg_vector buf; + bool deflated_; + callback_t callback; + void* user_data; + + blargg_err_t fill_buf( int count ); +}; + +#endif diff --git a/fex/fex/blargg_common.cpp b/fex/fex/blargg_common.cpp index ab86e645..9f3e9ebd 100644 --- a/fex/fex/blargg_common.cpp +++ b/fex/fex/blargg_common.cpp @@ -1,51 +1,51 @@ -// File_Extractor 1.0.0. http://www.slack.net/~ant/ - -#include "blargg_common.h" - -/* Copyright (C) 2008-2009 Shay Green. This module is free software; you -can redistribute it and/or modify it under the terms of the GNU Lesser -General Public License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. This -module 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 Lesser General Public License for more -details. You should have received a copy of the GNU Lesser General Public -License along with this module; if not, write to the Free Software Foundation, -Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ - -#include "blargg_source.h" - -void blargg_vector_::init() -{ - begin_ = NULL; - size_ = 0; -} - -void blargg_vector_::clear() -{ - void* p = begin_; - begin_ = NULL; - size_ = 0; - free( p ); -} - -blargg_err_t blargg_vector_::resize_( size_t n, size_t elem_size ) -{ - if ( n != size_ ) - { - if ( n == 0 ) - { - // Simpler to handle explicitly. Realloc will handle a size of 0, - // but then we have to avoid raising an error for a NULL return. - clear(); - } - else - { - void* p = realloc( begin_, n * elem_size ); - CHECK_ALLOC( p ); - begin_ = p; - size_ = n; - } - } - return blargg_ok; -} +// File_Extractor 1.0.0. http://www.slack.net/~ant/ + +#include "blargg_common.h" + +/* Copyright (C) 2008-2009 Shay Green. This module is free software; you +can redistribute it and/or modify it under the terms of the GNU Lesser +General Public License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. This +module 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 Lesser General Public License for more +details. You should have received a copy of the GNU Lesser General Public +License along with this module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +void blargg_vector_::init() +{ + begin_ = NULL; + size_ = 0; +} + +void blargg_vector_::clear() +{ + void* p = begin_; + begin_ = NULL; + size_ = 0; + free( p ); +} + +blargg_err_t blargg_vector_::resize_( size_t n, size_t elem_size ) +{ + if ( n != size_ ) + { + if ( n == 0 ) + { + // Simpler to handle explicitly. Realloc will handle a size of 0, + // but then we have to avoid raising an error for a NULL return. + clear(); + } + else + { + void* p = realloc( begin_, n * elem_size ); + CHECK_ALLOC( p ); + begin_ = p; + size_ = n; + } + } + return blargg_ok; +} diff --git a/fex/fex/blargg_common.h b/fex/fex/blargg_common.h index ca7a96ea..8c22fef9 100644 --- a/fex/fex/blargg_common.h +++ b/fex/fex/blargg_common.h @@ -1,201 +1,201 @@ -// Sets up common environment for Shay Green's libraries. -// To change configuration options, modify blargg_config.h, not this file. - -// File_Extractor 1.0.0 -#ifndef BLARGG_COMMON_H -#define BLARGG_COMMON_H - -#include -#include -#include - -typedef const char* blargg_err_t; // 0 on success, otherwise error string - -// Success; no error -int const blargg_ok = 0; - -// BLARGG_RESTRICT: equivalent to C99's restrict, where supported -#if __GNUC__ >= 3 || _MSC_VER >= 1100 - #define BLARGG_RESTRICT __restrict -#else - #define BLARGG_RESTRICT -#endif - -#if __cplusplus >= 199711 - #define BLARGG_MUTABLE mutable -#else - #define BLARGG_MUTABLE -#endif - -/* BLARGG_4CHAR('a','b','c','d') = 'abcd' (four character integer constant). -I don't just use 'abcd' because that's implementation-dependent. */ -#define BLARGG_4CHAR( a, b, c, d ) \ - ((a&0xFF)*0x1000000 + (b&0xFF)*0x10000 + (c&0xFF)*0x100 + (d&0xFF)) - -/* BLARGG_STATIC_ASSERT( expr ): Generates compile error if expr is 0. -Can be used at file, function, or class scope. */ -#ifdef _MSC_VER - // MSVC6 (_MSC_VER < 1300) __LINE__ fails when /Zl is specified - #define BLARGG_STATIC_ASSERT( expr ) \ - void blargg_failed_( int (*arg) [2 / (int) !!(expr) - 1] ) -#else - // Others fail when declaring same function multiple times in class, - // so differentiate them by line - #define BLARGG_STATIC_ASSERT( expr ) \ - void blargg_failed_( int (*arg) [2 / !!(expr) - 1] [__LINE__] ) -#endif - -/* Pure virtual functions cause a vtable entry to a "called pure virtual" -error handler, requiring linkage to the C++ runtime library. This macro is -used in place of the "= 0", and simply expands to its argument. During -development, it expands to "= 0", allowing detection of missing overrides. */ -#define BLARGG_PURE( def ) def - -/* My code depends on ASCII anywhere a character or string constant is -compared with data read from a file, and anywhere file data is read and -treated as a string. */ -#if '\n'!=0x0A || ' '!=0x20 || '0'!=0x30 || 'A'!=0x41 || 'a'!=0x61 - #error "ASCII character set required" -#endif - -/* My code depends on int being at least 32 bits. Almost everything these days -uses at least 32-bit ints, so it's hard to even find a system with 16-bit ints -to test with. The issue can't be gotten around by using a suitable blargg_int -everywhere either, because int is often converted to implicitly when doing -arithmetic on smaller types. */ -#if UINT_MAX < 0xFFFFFFFF - #error "int must be at least 32 bits" -#endif - -// In case compiler doesn't support these properly. Used rarely. -#define STATIC_CAST(T,expr) static_cast (expr) -#define CONST_CAST( T,expr) const_cast (expr) - -// User configuration can override the above macros if necessary -#include "blargg_config.h" - -/* BLARGG_DEPRECATED [_TEXT] for any declarations/text to be removed in a -future version. In GCC, we can let the compiler warn. In other compilers, -we strip it out unless BLARGG_LEGACY is true. */ -#if BLARGG_LEGACY - // Allow old client code to work without warnings - #define BLARGG_DEPRECATED_TEXT( text ) text - #define BLARGG_DEPRECATED( text ) text -#elif __GNUC__ >= 4 - // In GCC, we can mark declarations and let the compiler warn - #define BLARGG_DEPRECATED_TEXT( text ) text - #define BLARGG_DEPRECATED( text ) __attribute__ ((deprecated)) text -#else - // By default, deprecated items are removed, to avoid use in new code - #define BLARGG_DEPRECATED_TEXT( text ) - #define BLARGG_DEPRECATED( text ) -#endif - -/* BOOST::int8_t, BOOST::int32_t, etc. -I used BOOST since I originally was going to allow use of the boost library -for prividing the definitions. If I'm defining them, they must be scoped or -else they could conflict with the standard ones at global scope. Even if -HAVE_STDINT_H isn't defined, I can't assume the typedefs won't exist at -global scope already. */ -#if defined (HAVE_STDINT_H) || \ - UCHAR_MAX != 0xFF || USHRT_MAX != 0xFFFF || UINT_MAX != 0xFFFFFFFF - #include - #define BOOST -#else - struct BOOST - { - typedef signed char int8_t; - typedef unsigned char uint8_t; - typedef short int16_t; - typedef unsigned short uint16_t; - typedef int int32_t; - typedef unsigned int uint32_t; - }; -#endif - -/* My code is not written with exceptions in mind, so either uses new (nothrow) -OR overrides operator new in my classes. The former is best since clients -creating objects will get standard exceptions on failure, but that causes it -to require the standard C++ library. So, when the client is using the C -interface, I override operator new to use malloc. */ - -// BLARGG_DISABLE_NOTHROW is put inside classes -#ifndef BLARGG_DISABLE_NOTHROW - // throw spec mandatory in ISO C++ if NULL can be returned - #if __cplusplus >= 199711 || __GNUC__ >= 3 || _MSC_VER >= 1300 - #define BLARGG_THROWS_NOTHING throw () - #else - #define BLARGG_THROWS_NOTHING - #endif - - #define BLARGG_DISABLE_NOTHROW \ - void* operator new ( size_t s ) BLARGG_THROWS_NOTHING { return malloc( s ); }\ - void operator delete( void* p ) BLARGG_THROWS_NOTHING { free( p ); } - - #define BLARGG_NEW new -#else - // BLARGG_NEW is used in place of new in library code - #include - #define BLARGG_NEW new (std::nothrow) -#endif - - class blargg_vector_ { - protected: - void* begin_; - size_t size_; - void init(); - blargg_err_t resize_( size_t n, size_t elem_size ); - public: - size_t size() const { return size_; } - void clear(); - }; - -// Very lightweight vector for POD types (no constructor/destructor) -template -class blargg_vector : public blargg_vector_ { - union T_must_be_pod { T t; }; // fails if T is not POD -public: - blargg_vector() { init(); } - ~blargg_vector() { clear(); } - - blargg_err_t resize( size_t n ) { return resize_( n, sizeof (T) ); } - - T* begin() { return static_cast (begin_); } - const T* begin() const { return static_cast (begin_); } - - T* end() { return static_cast (begin_) + size_; } - const T* end() const { return static_cast (begin_) + size_; } - - T& operator [] ( size_t n ) - { - assert( n < size_ ); - return static_cast (begin_) [n]; - } - - const T& operator [] ( size_t n ) const - { - assert( n < size_ ); - return static_cast (begin_) [n]; - } -}; - -// Callback function with user data. -// blargg_callback set_callback; // for user, this acts like... -// void set_callback( T func, void* user_data = NULL ); // ...this -// To call function, do set_callback.f( .. set_callback.data ... ); -template -struct blargg_callback -{ - T f; - void* data; - blargg_callback() { f = NULL; } - void operator () ( T callback, void* user_data = NULL ) { f = callback; data = user_data; } -}; - -BLARGG_DEPRECATED( typedef signed int blargg_long; ) -BLARGG_DEPRECATED( typedef unsigned int blargg_ulong; ) -#if BLARGG_LEGACY - #define BOOST_STATIC_ASSERT BLARGG_STATIC_ASSERT -#endif - -#endif +// Sets up common environment for Shay Green's libraries. +// To change configuration options, modify blargg_config.h, not this file. + +// File_Extractor 1.0.0 +#ifndef BLARGG_COMMON_H +#define BLARGG_COMMON_H + +#include +#include +#include + +typedef const char* blargg_err_t; // 0 on success, otherwise error string + +// Success; no error +int const blargg_ok = 0; + +// BLARGG_RESTRICT: equivalent to C99's restrict, where supported +#if __GNUC__ >= 3 || _MSC_VER >= 1100 + #define BLARGG_RESTRICT __restrict +#else + #define BLARGG_RESTRICT +#endif + +#if __cplusplus >= 199711 + #define BLARGG_MUTABLE mutable +#else + #define BLARGG_MUTABLE +#endif + +/* BLARGG_4CHAR('a','b','c','d') = 'abcd' (four character integer constant). +I don't just use 'abcd' because that's implementation-dependent. */ +#define BLARGG_4CHAR( a, b, c, d ) \ + ((a&0xFF)*0x1000000 + (b&0xFF)*0x10000 + (c&0xFF)*0x100 + (d&0xFF)) + +/* BLARGG_STATIC_ASSERT( expr ): Generates compile error if expr is 0. +Can be used at file, function, or class scope. */ +#ifdef _MSC_VER + // MSVC6 (_MSC_VER < 1300) __LINE__ fails when /Zl is specified + #define BLARGG_STATIC_ASSERT( expr ) \ + void blargg_failed_( int (*arg) [2 / (int) !!(expr) - 1] ) +#else + // Others fail when declaring same function multiple times in class, + // so differentiate them by line + #define BLARGG_STATIC_ASSERT( expr ) \ + void blargg_failed_( int (*arg) [2 / !!(expr) - 1] [__LINE__] ) +#endif + +/* Pure virtual functions cause a vtable entry to a "called pure virtual" +error handler, requiring linkage to the C++ runtime library. This macro is +used in place of the "= 0", and simply expands to its argument. During +development, it expands to "= 0", allowing detection of missing overrides. */ +#define BLARGG_PURE( def ) def + +/* My code depends on ASCII anywhere a character or string constant is +compared with data read from a file, and anywhere file data is read and +treated as a string. */ +#if '\n'!=0x0A || ' '!=0x20 || '0'!=0x30 || 'A'!=0x41 || 'a'!=0x61 + #error "ASCII character set required" +#endif + +/* My code depends on int being at least 32 bits. Almost everything these days +uses at least 32-bit ints, so it's hard to even find a system with 16-bit ints +to test with. The issue can't be gotten around by using a suitable blargg_int +everywhere either, because int is often converted to implicitly when doing +arithmetic on smaller types. */ +#if UINT_MAX < 0xFFFFFFFF + #error "int must be at least 32 bits" +#endif + +// In case compiler doesn't support these properly. Used rarely. +#define STATIC_CAST(T,expr) static_cast (expr) +#define CONST_CAST( T,expr) const_cast (expr) + +// User configuration can override the above macros if necessary +#include "blargg_config.h" + +/* BLARGG_DEPRECATED [_TEXT] for any declarations/text to be removed in a +future version. In GCC, we can let the compiler warn. In other compilers, +we strip it out unless BLARGG_LEGACY is true. */ +#if BLARGG_LEGACY + // Allow old client code to work without warnings + #define BLARGG_DEPRECATED_TEXT( text ) text + #define BLARGG_DEPRECATED( text ) text +#elif __GNUC__ >= 4 + // In GCC, we can mark declarations and let the compiler warn + #define BLARGG_DEPRECATED_TEXT( text ) text + #define BLARGG_DEPRECATED( text ) __attribute__ ((deprecated)) text +#else + // By default, deprecated items are removed, to avoid use in new code + #define BLARGG_DEPRECATED_TEXT( text ) + #define BLARGG_DEPRECATED( text ) +#endif + +/* BOOST::int8_t, BOOST::int32_t, etc. +I used BOOST since I originally was going to allow use of the boost library +for prividing the definitions. If I'm defining them, they must be scoped or +else they could conflict with the standard ones at global scope. Even if +HAVE_STDINT_H isn't defined, I can't assume the typedefs won't exist at +global scope already. */ +#if defined (HAVE_STDINT_H) || \ + UCHAR_MAX != 0xFF || USHRT_MAX != 0xFFFF || UINT_MAX != 0xFFFFFFFF + #include + #define BOOST +#else + struct BOOST + { + typedef signed char int8_t; + typedef unsigned char uint8_t; + typedef short int16_t; + typedef unsigned short uint16_t; + typedef int int32_t; + typedef unsigned int uint32_t; + }; +#endif + +/* My code is not written with exceptions in mind, so either uses new (nothrow) +OR overrides operator new in my classes. The former is best since clients +creating objects will get standard exceptions on failure, but that causes it +to require the standard C++ library. So, when the client is using the C +interface, I override operator new to use malloc. */ + +// BLARGG_DISABLE_NOTHROW is put inside classes +#ifndef BLARGG_DISABLE_NOTHROW + // throw spec mandatory in ISO C++ if NULL can be returned + #if __cplusplus >= 199711 || __GNUC__ >= 3 || _MSC_VER >= 1300 + #define BLARGG_THROWS_NOTHING throw () + #else + #define BLARGG_THROWS_NOTHING + #endif + + #define BLARGG_DISABLE_NOTHROW \ + void* operator new ( size_t s ) BLARGG_THROWS_NOTHING { return malloc( s ); }\ + void operator delete( void* p ) BLARGG_THROWS_NOTHING { free( p ); } + + #define BLARGG_NEW new +#else + // BLARGG_NEW is used in place of new in library code + #include + #define BLARGG_NEW new (std::nothrow) +#endif + + class blargg_vector_ { + protected: + void* begin_; + size_t size_; + void init(); + blargg_err_t resize_( size_t n, size_t elem_size ); + public: + size_t size() const { return size_; } + void clear(); + }; + +// Very lightweight vector for POD types (no constructor/destructor) +template +class blargg_vector : public blargg_vector_ { + union T_must_be_pod { T t; }; // fails if T is not POD +public: + blargg_vector() { init(); } + ~blargg_vector() { clear(); } + + blargg_err_t resize( size_t n ) { return resize_( n, sizeof (T) ); } + + T* begin() { return static_cast (begin_); } + const T* begin() const { return static_cast (begin_); } + + T* end() { return static_cast (begin_) + size_; } + const T* end() const { return static_cast (begin_) + size_; } + + T& operator [] ( size_t n ) + { + assert( n < size_ ); + return static_cast (begin_) [n]; + } + + const T& operator [] ( size_t n ) const + { + assert( n < size_ ); + return static_cast (begin_) [n]; + } +}; + +// Callback function with user data. +// blargg_callback set_callback; // for user, this acts like... +// void set_callback( T func, void* user_data = NULL ); // ...this +// To call function, do set_callback.f( .. set_callback.data ... ); +template +struct blargg_callback +{ + T f; + void* data; + blargg_callback() { f = NULL; } + void operator () ( T callback, void* user_data = NULL ) { f = callback; data = user_data; } +}; + +BLARGG_DEPRECATED( typedef signed int blargg_long; ) +BLARGG_DEPRECATED( typedef unsigned int blargg_ulong; ) +#if BLARGG_LEGACY + #define BOOST_STATIC_ASSERT BLARGG_STATIC_ASSERT +#endif + +#endif diff --git a/fex/fex/blargg_config.h b/fex/fex/blargg_config.h index 5eb5d451..2282f1db 100644 --- a/fex/fex/blargg_config.h +++ b/fex/fex/blargg_config.h @@ -1,31 +1,31 @@ -// Library configuration. Modify this file as necessary. - -// File_Extractor 1.0.0 -#ifndef BLARGG_CONFIG_H -#define BLARGG_CONFIG_H - -// Uncomment a #define line below to have effect described. - -// Enable RAR archive support. Doing so adds extra licensing restrictions -// to this library (see unrar/readme.txt for more information). -//#define FEX_ENABLE_RAR 1 - -// Accept file paths encoded as UTF-8. Currently only affects Windows, -// as Unix/Linux/Mac OS X already use UTF-8 paths. -//#define BLARGG_UTF8_PATHS 1 - -// Enable support for as building DLL on Windows. -//#define BLARGG_BUILD_DLL 1 - -// Support only the listed archive types. Remove any you don't need. -#define FEX_TYPE_LIST \ - fex_7z_type,\ - fex_gz_type,\ - fex_zip_type, - -// Use standard config.h if present -#ifdef HAVE_CONFIG_H - #include "config.h" -#endif - -#endif +// Library configuration. Modify this file as necessary. + +// File_Extractor 1.0.0 +#ifndef BLARGG_CONFIG_H +#define BLARGG_CONFIG_H + +// Uncomment a #define line below to have effect described. + +// Enable RAR archive support. Doing so adds extra licensing restrictions +// to this library (see unrar/readme.txt for more information). +//#define FEX_ENABLE_RAR 1 + +// Accept file paths encoded as UTF-8. Currently only affects Windows, +// as Unix/Linux/Mac OS X already use UTF-8 paths. +//#define BLARGG_UTF8_PATHS 1 + +// Enable support for as building DLL on Windows. +//#define BLARGG_BUILD_DLL 1 + +// Support only the listed archive types. Remove any you don't need. +#define FEX_TYPE_LIST \ + fex_7z_type,\ + fex_gz_type,\ + fex_zip_type, + +// Use standard config.h if present +#ifdef HAVE_CONFIG_H + #include "config.h" +#endif + +#endif diff --git a/fex/fex/blargg_endian.h b/fex/fex/blargg_endian.h index d012871f..c32c12f5 100644 --- a/fex/fex/blargg_endian.h +++ b/fex/fex/blargg_endian.h @@ -1,185 +1,185 @@ -// CPU Byte Order Utilities - -// File_Extractor 1.0.0 -#ifndef BLARGG_ENDIAN_H -#define BLARGG_ENDIAN_H - -#include "blargg_common.h" - -// BLARGG_CPU_CISC: Defined if CPU has very few general-purpose registers (< 16) -#if defined (__i386__) || defined (__x86_64__) || defined (_M_IX86) || defined (_M_X64) - #define BLARGG_CPU_X86 1 - #define BLARGG_CPU_CISC 1 -#endif - -#if defined (__powerpc__) || defined (__ppc__) || defined (__ppc64__) || \ - defined (__POWERPC__) || defined (__powerc) - #define BLARGG_CPU_POWERPC 1 - #define BLARGG_CPU_RISC 1 -#endif - -// BLARGG_BIG_ENDIAN, BLARGG_LITTLE_ENDIAN: Determined automatically, otherwise only -// one may be #defined to 1. Only needed if something actually depends on byte order. -#if !defined (BLARGG_BIG_ENDIAN) && !defined (BLARGG_LITTLE_ENDIAN) -#ifdef __GLIBC__ - // GCC handles this for us - #include - #if __BYTE_ORDER == __LITTLE_ENDIAN - #define BLARGG_LITTLE_ENDIAN 1 - #elif __BYTE_ORDER == __BIG_ENDIAN - #define BLARGG_BIG_ENDIAN 1 - #endif -#else - -#if defined (LSB_FIRST) || defined (__LITTLE_ENDIAN__) || BLARGG_CPU_X86 || \ - (defined (LITTLE_ENDIAN) && LITTLE_ENDIAN+0 != 1234) - #define BLARGG_LITTLE_ENDIAN 1 -#endif - -#if defined (MSB_FIRST) || defined (__BIG_ENDIAN__) || defined (WORDS_BIGENDIAN) || \ - defined (__sparc__) || BLARGG_CPU_POWERPC || \ - (defined (BIG_ENDIAN) && BIG_ENDIAN+0 != 4321) - #define BLARGG_BIG_ENDIAN 1 -#elif !defined (__mips__) - // No endian specified; assume little-endian, since it's most common - #define BLARGG_LITTLE_ENDIAN 1 -#endif -#endif -#endif - -#if BLARGG_LITTLE_ENDIAN && BLARGG_BIG_ENDIAN - #undef BLARGG_LITTLE_ENDIAN - #undef BLARGG_BIG_ENDIAN -#endif - -inline void blargg_verify_byte_order() -{ - #ifndef NDEBUG - #if BLARGG_BIG_ENDIAN - volatile int i = 1; - assert( *(volatile char*) &i == 0 ); - #elif BLARGG_LITTLE_ENDIAN - volatile int i = 1; - assert( *(volatile char*) &i != 0 ); - #endif - #endif -} - -inline unsigned get_le16( void const* p ) -{ - return (unsigned) ((unsigned char const*) p) [1] << 8 | - (unsigned) ((unsigned char const*) p) [0]; -} - -inline unsigned get_be16( void const* p ) -{ - return (unsigned) ((unsigned char const*) p) [0] << 8 | - (unsigned) ((unsigned char const*) p) [1]; -} - -inline unsigned get_le32( void const* p ) -{ - return (unsigned) ((unsigned char const*) p) [3] << 24 | - (unsigned) ((unsigned char const*) p) [2] << 16 | - (unsigned) ((unsigned char const*) p) [1] << 8 | - (unsigned) ((unsigned char const*) p) [0]; -} - -inline unsigned get_be32( void const* p ) -{ - return (unsigned) ((unsigned char const*) p) [0] << 24 | - (unsigned) ((unsigned char const*) p) [1] << 16 | - (unsigned) ((unsigned char const*) p) [2] << 8 | - (unsigned) ((unsigned char const*) p) [3]; -} - -inline void set_le16( void* p, unsigned n ) -{ - ((unsigned char*) p) [1] = (unsigned char) (n >> 8); - ((unsigned char*) p) [0] = (unsigned char) n; -} - -inline void set_be16( void* p, unsigned n ) -{ - ((unsigned char*) p) [0] = (unsigned char) (n >> 8); - ((unsigned char*) p) [1] = (unsigned char) n; -} - -inline void set_le32( void* p, unsigned n ) -{ - ((unsigned char*) p) [0] = (unsigned char) n; - ((unsigned char*) p) [1] = (unsigned char) (n >> 8); - ((unsigned char*) p) [2] = (unsigned char) (n >> 16); - ((unsigned char*) p) [3] = (unsigned char) (n >> 24); -} - -inline void set_be32( void* p, unsigned n ) -{ - ((unsigned char*) p) [3] = (unsigned char) n; - ((unsigned char*) p) [2] = (unsigned char) (n >> 8); - ((unsigned char*) p) [1] = (unsigned char) (n >> 16); - ((unsigned char*) p) [0] = (unsigned char) (n >> 24); -} - -#if BLARGG_NONPORTABLE - // Optimized implementation if byte order is known - #if BLARGG_LITTLE_ENDIAN - #define GET_LE16( addr ) (*(BOOST::uint16_t const*) (addr)) - #define GET_LE32( addr ) (*(BOOST::uint32_t const*) (addr)) - #define SET_LE16( addr, data ) (void) (*(BOOST::uint16_t*) (addr) = (data)) - #define SET_LE32( addr, data ) (void) (*(BOOST::uint32_t*) (addr) = (data)) - #elif BLARGG_BIG_ENDIAN - #define GET_BE16( addr ) (*(BOOST::uint16_t const*) (addr)) - #define GET_BE32( addr ) (*(BOOST::uint32_t const*) (addr)) - #define SET_BE16( addr, data ) (void) (*(BOOST::uint16_t*) (addr) = (data)) - #define SET_BE32( addr, data ) (void) (*(BOOST::uint32_t*) (addr) = (data)) - - #if BLARGG_CPU_POWERPC - // PowerPC has special byte-reversed instructions - #if defined (__MWERKS__) - #define GET_LE16( addr ) (__lhbrx( addr, 0 )) - #define GET_LE32( addr ) (__lwbrx( addr, 0 )) - #define SET_LE16( addr, in ) (__sthbrx( in, addr, 0 )) - #define SET_LE32( addr, in ) (__stwbrx( in, addr, 0 )) - #elif defined (__GNUC__) - #define GET_LE16( addr ) ({unsigned short ppc_lhbrx_; __asm__ volatile( "lhbrx %0,0,%1" : "=r" (ppc_lhbrx_) : "r" (addr) : "memory" ); ppc_lhbrx_;}) - #define GET_LE32( addr ) ({unsigned short ppc_lwbrx_; __asm__ volatile( "lwbrx %0,0,%1" : "=r" (ppc_lwbrx_) : "r" (addr) : "memory" ); ppc_lwbrx_;}) - #define SET_LE16( addr, in ) ({__asm__ volatile( "sthbrx %0,0,%1" : : "r" (in), "r" (addr) : "memory" );}) - #define SET_LE32( addr, in ) ({__asm__ volatile( "stwbrx %0,0,%1" : : "r" (in), "r" (addr) : "memory" );}) - #endif - #endif - #endif -#endif - -#ifndef GET_LE16 - #define GET_LE16( addr ) get_le16( addr ) - #define SET_LE16( addr, data ) set_le16( addr, data ) -#endif - -#ifndef GET_LE32 - #define GET_LE32( addr ) get_le32( addr ) - #define SET_LE32( addr, data ) set_le32( addr, data ) -#endif - -#ifndef GET_BE16 - #define GET_BE16( addr ) get_be16( addr ) - #define SET_BE16( addr, data ) set_be16( addr, data ) -#endif - -#ifndef GET_BE32 - #define GET_BE32( addr ) get_be32( addr ) - #define SET_BE32( addr, data ) set_be32( addr, data ) -#endif - -// auto-selecting versions - -inline void set_le( BOOST::uint16_t* p, unsigned n ) { SET_LE16( p, n ); } -inline void set_le( BOOST::uint32_t* p, unsigned n ) { SET_LE32( p, n ); } -inline void set_be( BOOST::uint16_t* p, unsigned n ) { SET_BE16( p, n ); } -inline void set_be( BOOST::uint32_t* p, unsigned n ) { SET_BE32( p, n ); } -inline unsigned get_le( BOOST::uint16_t const* p ) { return GET_LE16( p ); } -inline unsigned get_le( BOOST::uint32_t const* p ) { return GET_LE32( p ); } -inline unsigned get_be( BOOST::uint16_t const* p ) { return GET_BE16( p ); } -inline unsigned get_be( BOOST::uint32_t const* p ) { return GET_BE32( p ); } - -#endif +// CPU Byte Order Utilities + +// File_Extractor 1.0.0 +#ifndef BLARGG_ENDIAN_H +#define BLARGG_ENDIAN_H + +#include "blargg_common.h" + +// BLARGG_CPU_CISC: Defined if CPU has very few general-purpose registers (< 16) +#if defined (__i386__) || defined (__x86_64__) || defined (_M_IX86) || defined (_M_X64) + #define BLARGG_CPU_X86 1 + #define BLARGG_CPU_CISC 1 +#endif + +#if defined (__powerpc__) || defined (__ppc__) || defined (__ppc64__) || \ + defined (__POWERPC__) || defined (__powerc) + #define BLARGG_CPU_POWERPC 1 + #define BLARGG_CPU_RISC 1 +#endif + +// BLARGG_BIG_ENDIAN, BLARGG_LITTLE_ENDIAN: Determined automatically, otherwise only +// one may be #defined to 1. Only needed if something actually depends on byte order. +#if !defined (BLARGG_BIG_ENDIAN) && !defined (BLARGG_LITTLE_ENDIAN) +#ifdef __GLIBC__ + // GCC handles this for us + #include + #if __BYTE_ORDER == __LITTLE_ENDIAN + #define BLARGG_LITTLE_ENDIAN 1 + #elif __BYTE_ORDER == __BIG_ENDIAN + #define BLARGG_BIG_ENDIAN 1 + #endif +#else + +#if defined (LSB_FIRST) || defined (__LITTLE_ENDIAN__) || BLARGG_CPU_X86 || \ + (defined (LITTLE_ENDIAN) && LITTLE_ENDIAN+0 != 1234) + #define BLARGG_LITTLE_ENDIAN 1 +#endif + +#if defined (MSB_FIRST) || defined (__BIG_ENDIAN__) || defined (WORDS_BIGENDIAN) || \ + defined (__sparc__) || BLARGG_CPU_POWERPC || \ + (defined (BIG_ENDIAN) && BIG_ENDIAN+0 != 4321) + #define BLARGG_BIG_ENDIAN 1 +#elif !defined (__mips__) + // No endian specified; assume little-endian, since it's most common + #define BLARGG_LITTLE_ENDIAN 1 +#endif +#endif +#endif + +#if BLARGG_LITTLE_ENDIAN && BLARGG_BIG_ENDIAN + #undef BLARGG_LITTLE_ENDIAN + #undef BLARGG_BIG_ENDIAN +#endif + +inline void blargg_verify_byte_order() +{ + #ifndef NDEBUG + #if BLARGG_BIG_ENDIAN + volatile int i = 1; + assert( *(volatile char*) &i == 0 ); + #elif BLARGG_LITTLE_ENDIAN + volatile int i = 1; + assert( *(volatile char*) &i != 0 ); + #endif + #endif +} + +inline unsigned get_le16( void const* p ) +{ + return (unsigned) ((unsigned char const*) p) [1] << 8 | + (unsigned) ((unsigned char const*) p) [0]; +} + +inline unsigned get_be16( void const* p ) +{ + return (unsigned) ((unsigned char const*) p) [0] << 8 | + (unsigned) ((unsigned char const*) p) [1]; +} + +inline unsigned get_le32( void const* p ) +{ + return (unsigned) ((unsigned char const*) p) [3] << 24 | + (unsigned) ((unsigned char const*) p) [2] << 16 | + (unsigned) ((unsigned char const*) p) [1] << 8 | + (unsigned) ((unsigned char const*) p) [0]; +} + +inline unsigned get_be32( void const* p ) +{ + return (unsigned) ((unsigned char const*) p) [0] << 24 | + (unsigned) ((unsigned char const*) p) [1] << 16 | + (unsigned) ((unsigned char const*) p) [2] << 8 | + (unsigned) ((unsigned char const*) p) [3]; +} + +inline void set_le16( void* p, unsigned n ) +{ + ((unsigned char*) p) [1] = (unsigned char) (n >> 8); + ((unsigned char*) p) [0] = (unsigned char) n; +} + +inline void set_be16( void* p, unsigned n ) +{ + ((unsigned char*) p) [0] = (unsigned char) (n >> 8); + ((unsigned char*) p) [1] = (unsigned char) n; +} + +inline void set_le32( void* p, unsigned n ) +{ + ((unsigned char*) p) [0] = (unsigned char) n; + ((unsigned char*) p) [1] = (unsigned char) (n >> 8); + ((unsigned char*) p) [2] = (unsigned char) (n >> 16); + ((unsigned char*) p) [3] = (unsigned char) (n >> 24); +} + +inline void set_be32( void* p, unsigned n ) +{ + ((unsigned char*) p) [3] = (unsigned char) n; + ((unsigned char*) p) [2] = (unsigned char) (n >> 8); + ((unsigned char*) p) [1] = (unsigned char) (n >> 16); + ((unsigned char*) p) [0] = (unsigned char) (n >> 24); +} + +#if BLARGG_NONPORTABLE + // Optimized implementation if byte order is known + #if BLARGG_LITTLE_ENDIAN + #define GET_LE16( addr ) (*(BOOST::uint16_t const*) (addr)) + #define GET_LE32( addr ) (*(BOOST::uint32_t const*) (addr)) + #define SET_LE16( addr, data ) (void) (*(BOOST::uint16_t*) (addr) = (data)) + #define SET_LE32( addr, data ) (void) (*(BOOST::uint32_t*) (addr) = (data)) + #elif BLARGG_BIG_ENDIAN + #define GET_BE16( addr ) (*(BOOST::uint16_t const*) (addr)) + #define GET_BE32( addr ) (*(BOOST::uint32_t const*) (addr)) + #define SET_BE16( addr, data ) (void) (*(BOOST::uint16_t*) (addr) = (data)) + #define SET_BE32( addr, data ) (void) (*(BOOST::uint32_t*) (addr) = (data)) + + #if BLARGG_CPU_POWERPC + // PowerPC has special byte-reversed instructions + #if defined (__MWERKS__) + #define GET_LE16( addr ) (__lhbrx( addr, 0 )) + #define GET_LE32( addr ) (__lwbrx( addr, 0 )) + #define SET_LE16( addr, in ) (__sthbrx( in, addr, 0 )) + #define SET_LE32( addr, in ) (__stwbrx( in, addr, 0 )) + #elif defined (__GNUC__) + #define GET_LE16( addr ) ({unsigned short ppc_lhbrx_; __asm__ volatile( "lhbrx %0,0,%1" : "=r" (ppc_lhbrx_) : "r" (addr) : "memory" ); ppc_lhbrx_;}) + #define GET_LE32( addr ) ({unsigned short ppc_lwbrx_; __asm__ volatile( "lwbrx %0,0,%1" : "=r" (ppc_lwbrx_) : "r" (addr) : "memory" ); ppc_lwbrx_;}) + #define SET_LE16( addr, in ) ({__asm__ volatile( "sthbrx %0,0,%1" : : "r" (in), "r" (addr) : "memory" );}) + #define SET_LE32( addr, in ) ({__asm__ volatile( "stwbrx %0,0,%1" : : "r" (in), "r" (addr) : "memory" );}) + #endif + #endif + #endif +#endif + +#ifndef GET_LE16 + #define GET_LE16( addr ) get_le16( addr ) + #define SET_LE16( addr, data ) set_le16( addr, data ) +#endif + +#ifndef GET_LE32 + #define GET_LE32( addr ) get_le32( addr ) + #define SET_LE32( addr, data ) set_le32( addr, data ) +#endif + +#ifndef GET_BE16 + #define GET_BE16( addr ) get_be16( addr ) + #define SET_BE16( addr, data ) set_be16( addr, data ) +#endif + +#ifndef GET_BE32 + #define GET_BE32( addr ) get_be32( addr ) + #define SET_BE32( addr, data ) set_be32( addr, data ) +#endif + +// auto-selecting versions + +inline void set_le( BOOST::uint16_t* p, unsigned n ) { SET_LE16( p, n ); } +inline void set_le( BOOST::uint32_t* p, unsigned n ) { SET_LE32( p, n ); } +inline void set_be( BOOST::uint16_t* p, unsigned n ) { SET_BE16( p, n ); } +inline void set_be( BOOST::uint32_t* p, unsigned n ) { SET_BE32( p, n ); } +inline unsigned get_le( BOOST::uint16_t const* p ) { return GET_LE16( p ); } +inline unsigned get_le( BOOST::uint32_t const* p ) { return GET_LE32( p ); } +inline unsigned get_be( BOOST::uint16_t const* p ) { return GET_BE16( p ); } +inline unsigned get_be( BOOST::uint32_t const* p ) { return GET_BE32( p ); } + +#endif diff --git a/fex/fex/blargg_errors.cpp b/fex/fex/blargg_errors.cpp index 025ea500..14076cdb 100644 --- a/fex/fex/blargg_errors.cpp +++ b/fex/fex/blargg_errors.cpp @@ -1,113 +1,113 @@ -// File_Extractor 1.0.0. http://www.slack.net/~ant/ - -#include "blargg_errors.h" - -/* Copyright (C) 2009 Shay Green. This module is free software; you -can redistribute it and/or modify it under the terms of the GNU Lesser -General Public License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. This -module 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 Lesser General Public License for more -details. You should have received a copy of the GNU Lesser General Public -License along with this module; if not, write to the Free Software Foundation, -Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ - -#include "blargg_source.h" - -blargg_err_def_t blargg_err_generic = BLARGG_ERR_GENERIC; -blargg_err_def_t blargg_err_memory = BLARGG_ERR_MEMORY; -blargg_err_def_t blargg_err_caller = BLARGG_ERR_CALLER; -blargg_err_def_t blargg_err_internal = BLARGG_ERR_INTERNAL; -blargg_err_def_t blargg_err_limitation = BLARGG_ERR_LIMITATION; - -blargg_err_def_t blargg_err_file_missing = BLARGG_ERR_FILE_MISSING; -blargg_err_def_t blargg_err_file_read = BLARGG_ERR_FILE_READ; -blargg_err_def_t blargg_err_file_write = BLARGG_ERR_FILE_WRITE; -blargg_err_def_t blargg_err_file_io = BLARGG_ERR_FILE_IO; -blargg_err_def_t blargg_err_file_full = BLARGG_ERR_FILE_FULL; -blargg_err_def_t blargg_err_file_eof = BLARGG_ERR_FILE_EOF; - -blargg_err_def_t blargg_err_file_type = BLARGG_ERR_FILE_TYPE; -blargg_err_def_t blargg_err_file_feature = BLARGG_ERR_FILE_FEATURE; -blargg_err_def_t blargg_err_file_corrupt = BLARGG_ERR_FILE_CORRUPT; - -const char* blargg_err_str( blargg_err_t err ) -{ - if ( !err ) - return ""; - - if ( *err == BLARGG_ERR_TYPE("")[0] ) - return err + 1; - - return err; -} - -bool blargg_is_err_type( blargg_err_t err, const char type [] ) -{ - if ( err ) - { - // True if first strlen(type) characters of err match type - char const* p = err; - while ( *type && *type == *p ) - { - type++; - p++; - } - - if ( !*type ) - return true; - } - - return false; -} - -const char* blargg_err_details( blargg_err_t err ) -{ - const char* p = err; - if ( !p ) - { - p = ""; - } - else if ( *p == BLARGG_ERR_TYPE("")[0] ) - { - while ( *p && *p != ';' ) - p++; - - // Skip ; and space after it - if ( *p ) - { - p++; - - check( *p == ' ' ); - if ( *p ) - p++; - } - } - return p; -} - -int blargg_err_to_code( blargg_err_t err, blargg_err_to_code_t const codes [] ) -{ - if ( !err ) - return 0; - - while ( codes->str && !blargg_is_err_type( err, codes->str ) ) - codes++; - - return codes->code; -} - -blargg_err_t blargg_code_to_err( int code, blargg_err_to_code_t const codes [] ) -{ - if ( !code ) - return blargg_ok; - - while ( codes->str && codes->code != code ) - codes++; - - if ( !codes->str ) - return blargg_err_generic; - - return codes->str; -} +// File_Extractor 1.0.0. http://www.slack.net/~ant/ + +#include "blargg_errors.h" + +/* Copyright (C) 2009 Shay Green. This module is free software; you +can redistribute it and/or modify it under the terms of the GNU Lesser +General Public License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. This +module 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 Lesser General Public License for more +details. You should have received a copy of the GNU Lesser General Public +License along with this module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +blargg_err_def_t blargg_err_generic = BLARGG_ERR_GENERIC; +blargg_err_def_t blargg_err_memory = BLARGG_ERR_MEMORY; +blargg_err_def_t blargg_err_caller = BLARGG_ERR_CALLER; +blargg_err_def_t blargg_err_internal = BLARGG_ERR_INTERNAL; +blargg_err_def_t blargg_err_limitation = BLARGG_ERR_LIMITATION; + +blargg_err_def_t blargg_err_file_missing = BLARGG_ERR_FILE_MISSING; +blargg_err_def_t blargg_err_file_read = BLARGG_ERR_FILE_READ; +blargg_err_def_t blargg_err_file_write = BLARGG_ERR_FILE_WRITE; +blargg_err_def_t blargg_err_file_io = BLARGG_ERR_FILE_IO; +blargg_err_def_t blargg_err_file_full = BLARGG_ERR_FILE_FULL; +blargg_err_def_t blargg_err_file_eof = BLARGG_ERR_FILE_EOF; + +blargg_err_def_t blargg_err_file_type = BLARGG_ERR_FILE_TYPE; +blargg_err_def_t blargg_err_file_feature = BLARGG_ERR_FILE_FEATURE; +blargg_err_def_t blargg_err_file_corrupt = BLARGG_ERR_FILE_CORRUPT; + +const char* blargg_err_str( blargg_err_t err ) +{ + if ( !err ) + return ""; + + if ( *err == BLARGG_ERR_TYPE("")[0] ) + return err + 1; + + return err; +} + +bool blargg_is_err_type( blargg_err_t err, const char type [] ) +{ + if ( err ) + { + // True if first strlen(type) characters of err match type + char const* p = err; + while ( *type && *type == *p ) + { + type++; + p++; + } + + if ( !*type ) + return true; + } + + return false; +} + +const char* blargg_err_details( blargg_err_t err ) +{ + const char* p = err; + if ( !p ) + { + p = ""; + } + else if ( *p == BLARGG_ERR_TYPE("")[0] ) + { + while ( *p && *p != ';' ) + p++; + + // Skip ; and space after it + if ( *p ) + { + p++; + + check( *p == ' ' ); + if ( *p ) + p++; + } + } + return p; +} + +int blargg_err_to_code( blargg_err_t err, blargg_err_to_code_t const codes [] ) +{ + if ( !err ) + return 0; + + while ( codes->str && !blargg_is_err_type( err, codes->str ) ) + codes++; + + return codes->code; +} + +blargg_err_t blargg_code_to_err( int code, blargg_err_to_code_t const codes [] ) +{ + if ( !code ) + return blargg_ok; + + while ( codes->str && codes->code != code ) + codes++; + + if ( !codes->str ) + return blargg_err_generic; + + return codes->str; +} diff --git a/fex/fex/blargg_errors.h b/fex/fex/blargg_errors.h index 2b6dd3c0..9c5206d5 100644 --- a/fex/fex/blargg_errors.h +++ b/fex/fex/blargg_errors.h @@ -1,80 +1,80 @@ -// Error strings and conversion functions - -// File_Extractor 1.0.0 -#ifndef BLARGG_ERRORS_H -#define BLARGG_ERRORS_H - -#ifndef BLARGG_COMMON_H - #include "blargg_common.h" -#endif - -typedef const char blargg_err_def_t []; - -// Basic errors -extern blargg_err_def_t blargg_err_generic; -extern blargg_err_def_t blargg_err_memory; -extern blargg_err_def_t blargg_err_caller; -extern blargg_err_def_t blargg_err_internal; -extern blargg_err_def_t blargg_err_limitation; - -// File low-level -extern blargg_err_def_t blargg_err_file_missing; // not found -extern blargg_err_def_t blargg_err_file_read; -extern blargg_err_def_t blargg_err_file_write; -extern blargg_err_def_t blargg_err_file_io; -extern blargg_err_def_t blargg_err_file_full; -extern blargg_err_def_t blargg_err_file_eof; - -// File high-level -extern blargg_err_def_t blargg_err_file_type; // wrong file type -extern blargg_err_def_t blargg_err_file_feature; -extern blargg_err_def_t blargg_err_file_corrupt; - -// C string describing error, or "" if err == NULL -const char* blargg_err_str( blargg_err_t err ); - -// True iff error is of given type, or false if err == NULL -bool blargg_is_err_type( blargg_err_t, const char type [] ); - -// Details of error without describing main cause, or "" if err == NULL -const char* blargg_err_details( blargg_err_t err ); - -// Converts error string to integer code using mapping table. Calls blargg_is_err_type() -// for each str and returns code on first match. Returns 0 if err == NULL. -struct blargg_err_to_code_t { - const char* str; - int code; -}; -int blargg_err_to_code( blargg_err_t err, blargg_err_to_code_t const [] ); - -// Converts error code back to string. If code == 0, returns NULL. If not in table, -// returns blargg_err_generic. -blargg_err_t blargg_code_to_err( int code, blargg_err_to_code_t const [] ); - -// Generates error string literal with details of cause -#define BLARGG_ERR( type, str ) (type "; " str) - -// Extra space to make it clear when blargg_err_str() isn't called to get -// printable version of error. At some point, I might prefix error strings -// with a code, to speed conversion to a code. -#define BLARGG_ERR_TYPE( str ) " " str - -// Error types to pass to BLARGG_ERR macro -#define BLARGG_ERR_GENERIC BLARGG_ERR_TYPE( "operation failed" ) -#define BLARGG_ERR_MEMORY BLARGG_ERR_TYPE( "out of memory" ) -#define BLARGG_ERR_CALLER BLARGG_ERR_TYPE( "internal usage bug" ) -#define BLARGG_ERR_INTERNAL BLARGG_ERR_TYPE( "internal bug" ) -#define BLARGG_ERR_LIMITATION BLARGG_ERR_TYPE( "exceeded limitation" ) - -#define BLARGG_ERR_FILE_MISSING BLARGG_ERR_TYPE( "file not found" ) -#define BLARGG_ERR_FILE_READ BLARGG_ERR_TYPE( "couldn't open file" ) -#define BLARGG_ERR_FILE_WRITE BLARGG_ERR_TYPE( "couldn't modify file" ) -#define BLARGG_ERR_FILE_IO BLARGG_ERR_TYPE( "read/write error" ) -#define BLARGG_ERR_FILE_FULL BLARGG_ERR_TYPE( "disk full" ) -#define BLARGG_ERR_FILE_EOF BLARGG_ERR_TYPE( "truncated file" ) - -#define BLARGG_ERR_FILE_TYPE BLARGG_ERR_TYPE( "wrong file type" ) -#define BLARGG_ERR_FILE_FEATURE BLARGG_ERR_TYPE( "unsupported file feature" ) -#define BLARGG_ERR_FILE_CORRUPT BLARGG_ERR_TYPE( "corrupt file" ) - -#endif +// Error strings and conversion functions + +// File_Extractor 1.0.0 +#ifndef BLARGG_ERRORS_H +#define BLARGG_ERRORS_H + +#ifndef BLARGG_COMMON_H + #include "blargg_common.h" +#endif + +typedef const char blargg_err_def_t []; + +// Basic errors +extern blargg_err_def_t blargg_err_generic; +extern blargg_err_def_t blargg_err_memory; +extern blargg_err_def_t blargg_err_caller; +extern blargg_err_def_t blargg_err_internal; +extern blargg_err_def_t blargg_err_limitation; + +// File low-level +extern blargg_err_def_t blargg_err_file_missing; // not found +extern blargg_err_def_t blargg_err_file_read; +extern blargg_err_def_t blargg_err_file_write; +extern blargg_err_def_t blargg_err_file_io; +extern blargg_err_def_t blargg_err_file_full; +extern blargg_err_def_t blargg_err_file_eof; + +// File high-level +extern blargg_err_def_t blargg_err_file_type; // wrong file type +extern blargg_err_def_t blargg_err_file_feature; +extern blargg_err_def_t blargg_err_file_corrupt; + +// C string describing error, or "" if err == NULL +const char* blargg_err_str( blargg_err_t err ); + +// True iff error is of given type, or false if err == NULL +bool blargg_is_err_type( blargg_err_t, const char type [] ); + +// Details of error without describing main cause, or "" if err == NULL +const char* blargg_err_details( blargg_err_t err ); + +// Converts error string to integer code using mapping table. Calls blargg_is_err_type() +// for each str and returns code on first match. Returns 0 if err == NULL. +struct blargg_err_to_code_t { + const char* str; + int code; +}; +int blargg_err_to_code( blargg_err_t err, blargg_err_to_code_t const [] ); + +// Converts error code back to string. If code == 0, returns NULL. If not in table, +// returns blargg_err_generic. +blargg_err_t blargg_code_to_err( int code, blargg_err_to_code_t const [] ); + +// Generates error string literal with details of cause +#define BLARGG_ERR( type, str ) (type "; " str) + +// Extra space to make it clear when blargg_err_str() isn't called to get +// printable version of error. At some point, I might prefix error strings +// with a code, to speed conversion to a code. +#define BLARGG_ERR_TYPE( str ) " " str + +// Error types to pass to BLARGG_ERR macro +#define BLARGG_ERR_GENERIC BLARGG_ERR_TYPE( "operation failed" ) +#define BLARGG_ERR_MEMORY BLARGG_ERR_TYPE( "out of memory" ) +#define BLARGG_ERR_CALLER BLARGG_ERR_TYPE( "internal usage bug" ) +#define BLARGG_ERR_INTERNAL BLARGG_ERR_TYPE( "internal bug" ) +#define BLARGG_ERR_LIMITATION BLARGG_ERR_TYPE( "exceeded limitation" ) + +#define BLARGG_ERR_FILE_MISSING BLARGG_ERR_TYPE( "file not found" ) +#define BLARGG_ERR_FILE_READ BLARGG_ERR_TYPE( "couldn't open file" ) +#define BLARGG_ERR_FILE_WRITE BLARGG_ERR_TYPE( "couldn't modify file" ) +#define BLARGG_ERR_FILE_IO BLARGG_ERR_TYPE( "read/write error" ) +#define BLARGG_ERR_FILE_FULL BLARGG_ERR_TYPE( "disk full" ) +#define BLARGG_ERR_FILE_EOF BLARGG_ERR_TYPE( "truncated file" ) + +#define BLARGG_ERR_FILE_TYPE BLARGG_ERR_TYPE( "wrong file type" ) +#define BLARGG_ERR_FILE_FEATURE BLARGG_ERR_TYPE( "unsupported file feature" ) +#define BLARGG_ERR_FILE_CORRUPT BLARGG_ERR_TYPE( "corrupt file" ) + +#endif diff --git a/fex/fex/fex.cpp b/fex/fex/fex.cpp index 07a0ec07..ab3f153e 100644 --- a/fex/fex/fex.cpp +++ b/fex/fex/fex.cpp @@ -1,321 +1,321 @@ -// File_Extractor 1.0.0. http://www.slack.net/~ant/ - -#include "fex.h" - -#include "File_Extractor.h" -#include "blargg_endian.h" -#include -#include - -/* Copyright (C) 2005-2009 Shay Green. This module is free software; you -can redistribute it and/or modify it under the terms of the GNU Lesser -General Public License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. This -module 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 Lesser General Public License for more -details. You should have received a copy of the GNU Lesser General Public -License along with this module; if not, write to the Free Software Foundation, -Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ - -#include "blargg_source.h" - - -//// Types - -BLARGG_EXPORT const fex_type_t* fex_type_list( void ) -{ - static fex_type_t const fex_type_list_ [] = - { - #ifdef FEX_TYPE_LIST - FEX_TYPE_LIST - #else - // Modify blargg_config.h to change type list, NOT this file - fex_7z_type, - fex_gz_type, - #if FEX_ENABLE_RAR - fex_rar_type, - #endif - fex_zip_type, - #endif - fex_bin_type, - NULL - }; - - return fex_type_list_; -} - -BLARGG_EXPORT fex_err_t fex_init( void ) -{ - static bool inited; - if ( !inited ) - { - for ( fex_type_t const* t = fex_type_list(); *t != NULL; ++t ) - { - if ( (*t)->init ) - RETURN_ERR( (*t)->init() ); - } - inited = true; - } - return blargg_ok; -} - -BLARGG_EXPORT const char* fex_identify_header( void const* header ) -{ - unsigned four = get_be32( header ); - switch ( four ) - { - case 0x52457E5E: - case 0x52617221: return ".rar"; - - case 0x377ABCAF: return ".7z"; - - case 0x504B0304: - case 0x504B0506: return ".zip"; - - case 0x53495421: return ".sit"; - case 0x41724301: return ".arc"; - case 0x4D534346: return ".cab"; - case 0x5A4F4F20: return ".zoo"; - } - - unsigned three = four >> 8; - switch ( three ) - { - case 0x425A68: return ".bz2"; - } - - unsigned two = four >> 16; - switch ( two ) - { - case 0x1F8B: return ".gz"; - case 0x60EA: return ".arj"; - } - - unsigned skip_first_two = four & 0xFFFF; - if ( skip_first_two == 0x2D6C ) - return ".lha"; - - return ""; -} - -static int fex_has_extension_( const char str [], const char suffix [], size_t str_len ) -{ - size_t suffix_len = strlen( suffix ); - if ( str_len >= suffix_len ) - { - str += str_len - suffix_len; - while ( *str && tolower( (unsigned char) *str ) == *suffix ) - { - str++; - suffix++; - } - } - return *suffix == 0; -} - -BLARGG_EXPORT int fex_has_extension( const char str [], const char suffix [] ) -{ - return fex_has_extension_( str, suffix, strlen( str ) ); -} - -static int is_archive_extension( const char str [] ) -{ - static const char exts [] [6] = { - ".7z", - ".arc", - ".arj", - ".bz2", - ".cab", - ".dmg", - ".gz", - ".lha", - ".lz", - ".lzh", - ".lzma", - ".lzo", - ".lzx", - ".pea", - ".rar", - ".sit", - ".sitx", - ".tgz", - ".tlz", - ".z", - ".zip", - ".zoo", - "" - }; - - size_t str_len = strlen( str ); - const char (*ext) [6] = exts; - for ( ; **ext; ext++ ) - { - if ( fex_has_extension_( str, *ext, str_len ) ) - return 1; - } - return 0; -} - -BLARGG_EXPORT fex_type_t fex_identify_extension( const char str [] ) -{ - size_t str_len = strlen( str ); - for ( fex_type_t const* types = fex_type_list(); *types; types++ ) - { - if ( fex_has_extension_( str, (*types)->extension, str_len ) ) - { - // Avoid treating known archive type as binary - if ( *(*types)->extension || !is_archive_extension( str ) ) - return *types; - } - } - return NULL; -} - -BLARGG_EXPORT fex_err_t fex_identify_file( fex_type_t* type_out, const char path [] ) -{ - *type_out = NULL; - - fex_type_t type = fex_identify_extension( path ); - - // Unsupported extension? - if ( !type ) - return blargg_ok; // reject - - // Unknown/no extension? - if ( !*(type->extension) ) - { - // Examine header - FEX_FILE_READER in; - RETURN_ERR( in.open( path ) ); - if ( in.remain() >= fex_identify_header_size ) - { - char h [fex_identify_header_size]; - RETURN_ERR( in.read( h, sizeof h ) ); - - type = fex_identify_extension( fex_identify_header( h ) ); - } - } - - *type_out = type; - return blargg_ok; -} - -BLARGG_EXPORT fex_err_t fex_open_type( fex_t** fe_out, const char path [], fex_type_t type ) -{ - *fe_out = NULL; - - if ( !type ) - return blargg_err_file_type; - - fex_t* fe = type->new_fex(); - CHECK_ALLOC( fe ); - - fex_err_t err = fe->open( path ); - if ( err ) - { - delete fe; - return err; - } - - *fe_out = fe; - return blargg_ok; -} - -BLARGG_EXPORT fex_err_t fex_open( fex_t** fe_out, const char path [] ) -{ - *fe_out = NULL; - - fex_type_t type; - RETURN_ERR( fex_identify_file( &type, path ) ); - - return fex_open_type( fe_out, path, type ); -} - - -//// Wide paths - -char* fex_wide_to_path( const wchar_t* wide ) -{ - return blargg_to_utf8( wide ); -} - -void fex_free_path( char* path ) -{ - free( path ); -} - - -//// Errors - -#define ENTRY( name ) { blargg_err_##name, fex_err_##name } -static blargg_err_to_code_t const fex_codes [] = -{ - ENTRY( generic ), - ENTRY( memory ), - ENTRY( caller ), - ENTRY( internal ), - ENTRY( limitation ), - - ENTRY( file_missing ), - ENTRY( file_read ), - ENTRY( file_io ), - ENTRY( file_eof ), - - ENTRY( file_type ), - ENTRY( file_feature ), - ENTRY( file_corrupt ), - - { 0, -1 } -}; -#undef ENTRY - -static int err_code( fex_err_t err ) -{ - return blargg_err_to_code( err, fex_codes ); -} - -BLARGG_EXPORT int fex_err_code( fex_err_t err ) -{ - int code = err_code( err ); - return (code >= 0 ? code : fex_err_generic); -} - -BLARGG_EXPORT fex_err_t fex_code_to_err( int code ) -{ - return blargg_code_to_err( code, fex_codes ); -} - -BLARGG_EXPORT const char* fex_err_details( fex_err_t err ) -{ - // If we don't have error code assigned, return entire string - return (err_code( err ) >= 0 ? blargg_err_details( err ) : blargg_err_str( err )); -} - - -//// Wrappers - -BLARGG_EXPORT fex_err_t fex_read( fex_t* fe, void* out, int count ) -{ - RETURN_ERR( fe->stat() ); - return fe->reader().read( out, count ); -} - -BLARGG_EXPORT void fex_close ( fex_t* fe ) { delete fe; } -BLARGG_EXPORT fex_type_t fex_type ( const fex_t* fe ) { return fe->type(); } -BLARGG_EXPORT int fex_done ( const fex_t* fe ) { return fe->done(); } -BLARGG_EXPORT const char* fex_name ( const fex_t* fe ) { return fe->name(); } -BLARGG_EXPORT const wchar_t* fex_wname ( const fex_t* fe ) { return fe->wname(); } -BLARGG_EXPORT int fex_size ( const fex_t* fe ) { return fe->size(); } -BLARGG_EXPORT unsigned fex_dos_date ( const fex_t* fe ) { return fe->dos_date(); } -BLARGG_EXPORT unsigned fex_crc32 ( const fex_t* fe ) { return fe->crc32(); } -BLARGG_EXPORT fex_err_t fex_stat ( fex_t* fe ) { return fe->stat(); } -BLARGG_EXPORT fex_err_t fex_next ( fex_t* fe ) { return fe->next(); } -BLARGG_EXPORT fex_err_t fex_rewind ( fex_t* fe ) { return fe->rewind(); } -BLARGG_EXPORT int fex_tell ( const fex_t* fe ) { return fe->tell(); } -BLARGG_EXPORT fex_pos_t fex_tell_arc ( const fex_t* fe ) { return fe->tell_arc(); } -BLARGG_EXPORT fex_err_t fex_seek_arc ( fex_t* fe, fex_pos_t pos ) { return fe->seek_arc( pos ); } -BLARGG_EXPORT const char* fex_type_extension ( fex_type_t t ) { return t->extension; } -BLARGG_EXPORT const char* fex_type_name ( fex_type_t t ) { return t->name; } -BLARGG_EXPORT fex_err_t fex_data ( fex_t* fe, const void** data_out ) { return fe->data( data_out ); } -BLARGG_EXPORT const char* fex_err_str ( fex_err_t err ) { return blargg_err_str( err ); } +// File_Extractor 1.0.0. http://www.slack.net/~ant/ + +#include "fex.h" + +#include "File_Extractor.h" +#include "blargg_endian.h" +#include +#include + +/* Copyright (C) 2005-2009 Shay Green. This module is free software; you +can redistribute it and/or modify it under the terms of the GNU Lesser +General Public License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. This +module 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 Lesser General Public License for more +details. You should have received a copy of the GNU Lesser General Public +License along with this module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + + +//// Types + +BLARGG_EXPORT const fex_type_t* fex_type_list( void ) +{ + static fex_type_t const fex_type_list_ [] = + { + #ifdef FEX_TYPE_LIST + FEX_TYPE_LIST + #else + // Modify blargg_config.h to change type list, NOT this file + fex_7z_type, + fex_gz_type, + #if FEX_ENABLE_RAR + fex_rar_type, + #endif + fex_zip_type, + #endif + fex_bin_type, + NULL + }; + + return fex_type_list_; +} + +BLARGG_EXPORT fex_err_t fex_init( void ) +{ + static bool inited; + if ( !inited ) + { + for ( fex_type_t const* t = fex_type_list(); *t != NULL; ++t ) + { + if ( (*t)->init ) + RETURN_ERR( (*t)->init() ); + } + inited = true; + } + return blargg_ok; +} + +BLARGG_EXPORT const char* fex_identify_header( void const* header ) +{ + unsigned four = get_be32( header ); + switch ( four ) + { + case 0x52457E5E: + case 0x52617221: return ".rar"; + + case 0x377ABCAF: return ".7z"; + + case 0x504B0304: + case 0x504B0506: return ".zip"; + + case 0x53495421: return ".sit"; + case 0x41724301: return ".arc"; + case 0x4D534346: return ".cab"; + case 0x5A4F4F20: return ".zoo"; + } + + unsigned three = four >> 8; + switch ( three ) + { + case 0x425A68: return ".bz2"; + } + + unsigned two = four >> 16; + switch ( two ) + { + case 0x1F8B: return ".gz"; + case 0x60EA: return ".arj"; + } + + unsigned skip_first_two = four & 0xFFFF; + if ( skip_first_two == 0x2D6C ) + return ".lha"; + + return ""; +} + +static int fex_has_extension_( const char str [], const char suffix [], size_t str_len ) +{ + size_t suffix_len = strlen( suffix ); + if ( str_len >= suffix_len ) + { + str += str_len - suffix_len; + while ( *str && tolower( (unsigned char) *str ) == *suffix ) + { + str++; + suffix++; + } + } + return *suffix == 0; +} + +BLARGG_EXPORT int fex_has_extension( const char str [], const char suffix [] ) +{ + return fex_has_extension_( str, suffix, strlen( str ) ); +} + +static int is_archive_extension( const char str [] ) +{ + static const char exts [] [6] = { + ".7z", + ".arc", + ".arj", + ".bz2", + ".cab", + ".dmg", + ".gz", + ".lha", + ".lz", + ".lzh", + ".lzma", + ".lzo", + ".lzx", + ".pea", + ".rar", + ".sit", + ".sitx", + ".tgz", + ".tlz", + ".z", + ".zip", + ".zoo", + "" + }; + + size_t str_len = strlen( str ); + const char (*ext) [6] = exts; + for ( ; **ext; ext++ ) + { + if ( fex_has_extension_( str, *ext, str_len ) ) + return 1; + } + return 0; +} + +BLARGG_EXPORT fex_type_t fex_identify_extension( const char str [] ) +{ + size_t str_len = strlen( str ); + for ( fex_type_t const* types = fex_type_list(); *types; types++ ) + { + if ( fex_has_extension_( str, (*types)->extension, str_len ) ) + { + // Avoid treating known archive type as binary + if ( *(*types)->extension || !is_archive_extension( str ) ) + return *types; + } + } + return NULL; +} + +BLARGG_EXPORT fex_err_t fex_identify_file( fex_type_t* type_out, const char path [] ) +{ + *type_out = NULL; + + fex_type_t type = fex_identify_extension( path ); + + // Unsupported extension? + if ( !type ) + return blargg_ok; // reject + + // Unknown/no extension? + if ( !*(type->extension) ) + { + // Examine header + FEX_FILE_READER in; + RETURN_ERR( in.open( path ) ); + if ( in.remain() >= fex_identify_header_size ) + { + char h [fex_identify_header_size]; + RETURN_ERR( in.read( h, sizeof h ) ); + + type = fex_identify_extension( fex_identify_header( h ) ); + } + } + + *type_out = type; + return blargg_ok; +} + +BLARGG_EXPORT fex_err_t fex_open_type( fex_t** fe_out, const char path [], fex_type_t type ) +{ + *fe_out = NULL; + + if ( !type ) + return blargg_err_file_type; + + fex_t* fe = type->new_fex(); + CHECK_ALLOC( fe ); + + fex_err_t err = fe->open( path ); + if ( err ) + { + delete fe; + return err; + } + + *fe_out = fe; + return blargg_ok; +} + +BLARGG_EXPORT fex_err_t fex_open( fex_t** fe_out, const char path [] ) +{ + *fe_out = NULL; + + fex_type_t type; + RETURN_ERR( fex_identify_file( &type, path ) ); + + return fex_open_type( fe_out, path, type ); +} + + +//// Wide paths + +char* fex_wide_to_path( const wchar_t* wide ) +{ + return blargg_to_utf8( wide ); +} + +void fex_free_path( char* path ) +{ + free( path ); +} + + +//// Errors + +#define ENTRY( name ) { blargg_err_##name, fex_err_##name } +static blargg_err_to_code_t const fex_codes [] = +{ + ENTRY( generic ), + ENTRY( memory ), + ENTRY( caller ), + ENTRY( internal ), + ENTRY( limitation ), + + ENTRY( file_missing ), + ENTRY( file_read ), + ENTRY( file_io ), + ENTRY( file_eof ), + + ENTRY( file_type ), + ENTRY( file_feature ), + ENTRY( file_corrupt ), + + { 0, -1 } +}; +#undef ENTRY + +static int err_code( fex_err_t err ) +{ + return blargg_err_to_code( err, fex_codes ); +} + +BLARGG_EXPORT int fex_err_code( fex_err_t err ) +{ + int code = err_code( err ); + return (code >= 0 ? code : fex_err_generic); +} + +BLARGG_EXPORT fex_err_t fex_code_to_err( int code ) +{ + return blargg_code_to_err( code, fex_codes ); +} + +BLARGG_EXPORT const char* fex_err_details( fex_err_t err ) +{ + // If we don't have error code assigned, return entire string + return (err_code( err ) >= 0 ? blargg_err_details( err ) : blargg_err_str( err )); +} + + +//// Wrappers + +BLARGG_EXPORT fex_err_t fex_read( fex_t* fe, void* out, int count ) +{ + RETURN_ERR( fe->stat() ); + return fe->reader().read( out, count ); +} + +BLARGG_EXPORT void fex_close ( fex_t* fe ) { delete fe; } +BLARGG_EXPORT fex_type_t fex_type ( const fex_t* fe ) { return fe->type(); } +BLARGG_EXPORT int fex_done ( const fex_t* fe ) { return fe->done(); } +BLARGG_EXPORT const char* fex_name ( const fex_t* fe ) { return fe->name(); } +BLARGG_EXPORT const wchar_t* fex_wname ( const fex_t* fe ) { return fe->wname(); } +BLARGG_EXPORT int fex_size ( const fex_t* fe ) { return fe->size(); } +BLARGG_EXPORT unsigned fex_dos_date ( const fex_t* fe ) { return fe->dos_date(); } +BLARGG_EXPORT unsigned fex_crc32 ( const fex_t* fe ) { return fe->crc32(); } +BLARGG_EXPORT fex_err_t fex_stat ( fex_t* fe ) { return fe->stat(); } +BLARGG_EXPORT fex_err_t fex_next ( fex_t* fe ) { return fe->next(); } +BLARGG_EXPORT fex_err_t fex_rewind ( fex_t* fe ) { return fe->rewind(); } +BLARGG_EXPORT int fex_tell ( const fex_t* fe ) { return fe->tell(); } +BLARGG_EXPORT fex_pos_t fex_tell_arc ( const fex_t* fe ) { return fe->tell_arc(); } +BLARGG_EXPORT fex_err_t fex_seek_arc ( fex_t* fe, fex_pos_t pos ) { return fe->seek_arc( pos ); } +BLARGG_EXPORT const char* fex_type_extension ( fex_type_t t ) { return t->extension; } +BLARGG_EXPORT const char* fex_type_name ( fex_type_t t ) { return t->name; } +BLARGG_EXPORT fex_err_t fex_data ( fex_t* fe, const void** data_out ) { return fe->data( data_out ); } +BLARGG_EXPORT const char* fex_err_str ( fex_err_t err ) { return blargg_err_str( err ); } diff --git a/fex/fex/fex.h b/fex/fex/fex.h index 503a495a..8b6173ae 100644 --- a/fex/fex/fex.h +++ b/fex/fex/fex.h @@ -1,204 +1,204 @@ -/** Uniform access to zip, gzip, 7-zip, and RAR compressed archives \file */ - -/* File_Extractor 1.0.0 */ -#ifndef FEX_H -#define FEX_H - -#include - -#ifdef __cplusplus - extern "C" { -#endif - - -/** First parameter of most functions is fex_t*, or const fex_t* if nothing is -changed. Once one of these functions returns an error, the archive should not -be used any further, other than to close it. One exception is -fex_error_file_eof; the archive may still be used after this. */ -typedef struct fex_t fex_t; - -/** Pointer to error, or NULL if function was successful. See error functions -below. */ -#ifndef fex_err_t /* (#ifndef allows better testing of library) */ - typedef const char* fex_err_t; -#endif - - -/**** File types ****/ - -/** Archive file type identifier. Can also hold NULL. */ -typedef const struct fex_type_t_* fex_type_t; - -/** Array of supported types, with NULL at end */ -const fex_type_t* fex_type_list( void ); - -/** Name of this archive type, e.g. "ZIP archive", "file" */ -const char* fex_type_name( fex_type_t ); - -/** Usual file extension for type, e.g. ".zip", ".7z". For binary file type, -returns "", since it can open any file. */ -const char* fex_type_extension( fex_type_t ); - - -/**** Wide-character file paths ****/ - -/** Converts wide-character path to form suitable for use with fex functions. */ -char* fex_wide_to_path( const wchar_t* wide ); - -/** Frees converted path. OK to pass NULL. */ -void fex_free_path( char* ); - - -/**** Identification ****/ - -/** True if str ends in extension. If extension is "", always returns true. -Converts str to lowercase before comparison, so extension should ALREADY be -lowercase (i.e. pass ".zip", NOT ".ZIP"). */ -int fex_has_extension( const char str [], const char extension [] ); - -/** Determines type based on first fex_identify_header_size bytes of file. -Returns usual file extension this should have (e.g. ".zip", ".gz", etc.). -Returns "" if file header is not recognized. */ -const char* fex_identify_header( const void* header ); -enum { fex_identify_header_size = 16 }; - -/** Determines type based on extension of a file path, or just a lone extension -(must include '.', e.g. ".zip", NOT just "zip"). Returns NULL if extension is -for an unsupported type (e.g. ".lzh"). */ -fex_type_t fex_identify_extension( const char path_or_extension [] ); - -/** Determines type based on filename extension and/or file header. Sets *out -to determined type, or NULL if type is not supported. */ -fex_err_t fex_identify_file( fex_type_t* out, const char path [] ); - -/** Type of an already-opened archive */ -fex_type_t fex_type( const fex_t* ); - - -/**** Open/close ****/ - -/** Initializes static tables used by library. Automatically called by -fex_open(). OK to call more than once. */ -fex_err_t fex_init( void ); - -/** Opens archive and points *out at it. If error, sets *out to NULL. */ -fex_err_t fex_open( fex_t** out, const char path [] ); - -/** Opens archive of specified type and sets *out. Returns error if file is not -of that archive type. If error, sets *out to NULL. */ -fex_err_t fex_open_type( fex_t** out, const char path [], fex_type_t ); - -/** Closes archive and frees memory. OK to pass NULL. */ -void fex_close( fex_t* ); - - -/**** Scanning ****/ - -/** True if at end of archive. Must be called after fex_open() or fex_rewind(), -as an archive might contain no files. */ -int fex_done( const fex_t* ); - -/** Goes to next file in archive. If there are no more files, fex_done() will -now return true. */ -fex_err_t fex_next( fex_t* ); - -/** Goes back to first file in archive, as if it were just opened with -fex_open() */ -fex_err_t fex_rewind( fex_t* ); - -/** Saved position in archive. Can also store zero. */ -typedef int fex_pos_t; - -/** Position of current file in archive. Never returns zero. */ -fex_pos_t fex_tell_arc( const fex_t* ); - -/** Returns to file at previously-saved position */ -fex_err_t fex_seek_arc( fex_t*, fex_pos_t ); - - -/**** Info ****/ - -/** Name of current file */ -const char* fex_name( const fex_t* ); - -/** Wide-character name of current file, or NULL if unavailable */ -const wchar_t* fex_wname( const fex_t* ); - -/** Makes further information available for file */ -fex_err_t fex_stat( fex_t* ); - -/** Size of current file. fex_stat() or fex_data() must have been called. */ -int fex_size( const fex_t* ); - -/** Modification date of current file (MS-DOS format), or 0 if unavailable. -fex_stat() must have been called. */ -unsigned int fex_dos_date( const fex_t* ); - -/** CRC-32 checksum of current file's contents, or 0 if unavailable. Doesn't -require calculation; simply gets it from file's header. fex_stat() must have -been called. */ -unsigned int fex_crc32( const fex_t* ); - - -/**** Extraction ****/ - -/** Reads n bytes from current file. Reading past end of file results in -fex_err_file_eof. */ -fex_err_t fex_read( fex_t*, void* out, int n ); - -/** Number of bytes read from current file */ -int fex_tell( const fex_t* ); - -/** Points *out at current file's data in memory. Pointer is valid until -fex_next(), fex_rewind(), fex_seek_arc(), or fex_close() is called. Pointer -must NOT be freed(); library frees it automatically. If error, sets *out to -NULL. */ -fex_err_t fex_data( fex_t*, const void** out ); - - -/**** Errors ****/ - -/** Error string associated with err. Returns "" if err is NULL. Returns err -unchanged if it isn't a fex_err_t returned by library. */ -const char* fex_err_str( fex_err_t err ); - -/** Details of error beyond main cause, or "" if none or err is NULL. Returns -err unchanged if it isn't a fex_err_t returned by library. */ -const char* fex_err_details( fex_err_t err ); - -/** Numeric code corresponding to err. Returns fex_ok if err is NULL. Returns -fex_err_generic if err isn't a fex_err_t returned by library. */ -int fex_err_code( fex_err_t err ); - -enum { - fex_ok = 0,/**< Successful call. Guaranteed to be zero. */ - fex_err_generic = 0x01,/**< Error of unspecified type */ - fex_err_memory = 0x02,/**< Out of memory */ - fex_err_caller = 0x03,/**< Caller called function with bad args */ - fex_err_internal = 0x04,/**< Internal problem, bug, etc. */ - fex_err_limitation = 0x05,/**< Exceeded program limit */ - - fex_err_file_missing = 0x20,/**< File not found at specified path */ - fex_err_file_read = 0x21,/**< Couldn't open file for reading */ - fex_err_file_io = 0x23,/**< Read/write error */ - fex_err_file_eof = 0x25,/**< Tried to read past end of file */ - - fex_err_file_type = 0x30,/**< File is of wrong type */ - fex_err_file_feature = 0x32,/**< File requires unsupported feature */ - fex_err_file_corrupt = 0x33 /**< File is corrupt */ -}; - -/** fex_err_t corresponding to numeric code. Note that this might not recover -the original fex_err_t before it was converted to a numeric code; in -particular, fex_err_details(fex_code_to_err(code)) will be "" in most cases. */ -fex_err_t fex_code_to_err( int code ); - - -/* Deprecated */ -typedef fex_t File_Extractor; - -#ifdef __cplusplus - } -#endif - -#endif +/** Uniform access to zip, gzip, 7-zip, and RAR compressed archives \file */ + +/* File_Extractor 1.0.0 */ +#ifndef FEX_H +#define FEX_H + +#include + +#ifdef __cplusplus + extern "C" { +#endif + + +/** First parameter of most functions is fex_t*, or const fex_t* if nothing is +changed. Once one of these functions returns an error, the archive should not +be used any further, other than to close it. One exception is +fex_error_file_eof; the archive may still be used after this. */ +typedef struct fex_t fex_t; + +/** Pointer to error, or NULL if function was successful. See error functions +below. */ +#ifndef fex_err_t /* (#ifndef allows better testing of library) */ + typedef const char* fex_err_t; +#endif + + +/**** File types ****/ + +/** Archive file type identifier. Can also hold NULL. */ +typedef const struct fex_type_t_* fex_type_t; + +/** Array of supported types, with NULL at end */ +const fex_type_t* fex_type_list( void ); + +/** Name of this archive type, e.g. "ZIP archive", "file" */ +const char* fex_type_name( fex_type_t ); + +/** Usual file extension for type, e.g. ".zip", ".7z". For binary file type, +returns "", since it can open any file. */ +const char* fex_type_extension( fex_type_t ); + + +/**** Wide-character file paths ****/ + +/** Converts wide-character path to form suitable for use with fex functions. */ +char* fex_wide_to_path( const wchar_t* wide ); + +/** Frees converted path. OK to pass NULL. */ +void fex_free_path( char* ); + + +/**** Identification ****/ + +/** True if str ends in extension. If extension is "", always returns true. +Converts str to lowercase before comparison, so extension should ALREADY be +lowercase (i.e. pass ".zip", NOT ".ZIP"). */ +int fex_has_extension( const char str [], const char extension [] ); + +/** Determines type based on first fex_identify_header_size bytes of file. +Returns usual file extension this should have (e.g. ".zip", ".gz", etc.). +Returns "" if file header is not recognized. */ +const char* fex_identify_header( const void* header ); +enum { fex_identify_header_size = 16 }; + +/** Determines type based on extension of a file path, or just a lone extension +(must include '.', e.g. ".zip", NOT just "zip"). Returns NULL if extension is +for an unsupported type (e.g. ".lzh"). */ +fex_type_t fex_identify_extension( const char path_or_extension [] ); + +/** Determines type based on filename extension and/or file header. Sets *out +to determined type, or NULL if type is not supported. */ +fex_err_t fex_identify_file( fex_type_t* out, const char path [] ); + +/** Type of an already-opened archive */ +fex_type_t fex_type( const fex_t* ); + + +/**** Open/close ****/ + +/** Initializes static tables used by library. Automatically called by +fex_open(). OK to call more than once. */ +fex_err_t fex_init( void ); + +/** Opens archive and points *out at it. If error, sets *out to NULL. */ +fex_err_t fex_open( fex_t** out, const char path [] ); + +/** Opens archive of specified type and sets *out. Returns error if file is not +of that archive type. If error, sets *out to NULL. */ +fex_err_t fex_open_type( fex_t** out, const char path [], fex_type_t ); + +/** Closes archive and frees memory. OK to pass NULL. */ +void fex_close( fex_t* ); + + +/**** Scanning ****/ + +/** True if at end of archive. Must be called after fex_open() or fex_rewind(), +as an archive might contain no files. */ +int fex_done( const fex_t* ); + +/** Goes to next file in archive. If there are no more files, fex_done() will +now return true. */ +fex_err_t fex_next( fex_t* ); + +/** Goes back to first file in archive, as if it were just opened with +fex_open() */ +fex_err_t fex_rewind( fex_t* ); + +/** Saved position in archive. Can also store zero. */ +typedef int fex_pos_t; + +/** Position of current file in archive. Never returns zero. */ +fex_pos_t fex_tell_arc( const fex_t* ); + +/** Returns to file at previously-saved position */ +fex_err_t fex_seek_arc( fex_t*, fex_pos_t ); + + +/**** Info ****/ + +/** Name of current file */ +const char* fex_name( const fex_t* ); + +/** Wide-character name of current file, or NULL if unavailable */ +const wchar_t* fex_wname( const fex_t* ); + +/** Makes further information available for file */ +fex_err_t fex_stat( fex_t* ); + +/** Size of current file. fex_stat() or fex_data() must have been called. */ +int fex_size( const fex_t* ); + +/** Modification date of current file (MS-DOS format), or 0 if unavailable. +fex_stat() must have been called. */ +unsigned int fex_dos_date( const fex_t* ); + +/** CRC-32 checksum of current file's contents, or 0 if unavailable. Doesn't +require calculation; simply gets it from file's header. fex_stat() must have +been called. */ +unsigned int fex_crc32( const fex_t* ); + + +/**** Extraction ****/ + +/** Reads n bytes from current file. Reading past end of file results in +fex_err_file_eof. */ +fex_err_t fex_read( fex_t*, void* out, int n ); + +/** Number of bytes read from current file */ +int fex_tell( const fex_t* ); + +/** Points *out at current file's data in memory. Pointer is valid until +fex_next(), fex_rewind(), fex_seek_arc(), or fex_close() is called. Pointer +must NOT be freed(); library frees it automatically. If error, sets *out to +NULL. */ +fex_err_t fex_data( fex_t*, const void** out ); + + +/**** Errors ****/ + +/** Error string associated with err. Returns "" if err is NULL. Returns err +unchanged if it isn't a fex_err_t returned by library. */ +const char* fex_err_str( fex_err_t err ); + +/** Details of error beyond main cause, or "" if none or err is NULL. Returns +err unchanged if it isn't a fex_err_t returned by library. */ +const char* fex_err_details( fex_err_t err ); + +/** Numeric code corresponding to err. Returns fex_ok if err is NULL. Returns +fex_err_generic if err isn't a fex_err_t returned by library. */ +int fex_err_code( fex_err_t err ); + +enum { + fex_ok = 0,/**< Successful call. Guaranteed to be zero. */ + fex_err_generic = 0x01,/**< Error of unspecified type */ + fex_err_memory = 0x02,/**< Out of memory */ + fex_err_caller = 0x03,/**< Caller called function with bad args */ + fex_err_internal = 0x04,/**< Internal problem, bug, etc. */ + fex_err_limitation = 0x05,/**< Exceeded program limit */ + + fex_err_file_missing = 0x20,/**< File not found at specified path */ + fex_err_file_read = 0x21,/**< Couldn't open file for reading */ + fex_err_file_io = 0x23,/**< Read/write error */ + fex_err_file_eof = 0x25,/**< Tried to read past end of file */ + + fex_err_file_type = 0x30,/**< File is of wrong type */ + fex_err_file_feature = 0x32,/**< File requires unsupported feature */ + fex_err_file_corrupt = 0x33 /**< File is corrupt */ +}; + +/** fex_err_t corresponding to numeric code. Note that this might not recover +the original fex_err_t before it was converted to a numeric code; in +particular, fex_err_details(fex_code_to_err(code)) will be "" in most cases. */ +fex_err_t fex_code_to_err( int code ); + + +/* Deprecated */ +typedef fex_t File_Extractor; + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/project/vc2008_mfc/svnrev_template.h b/project/vc2008_mfc/svnrev_template.h index 24d8c760..db42e183 100644 --- a/project/vc2008_mfc/svnrev_template.h +++ b/project/vc2008_mfc/svnrev_template.h @@ -1,2 +1,2 @@ -#define SVN_REV $WCREV$ -#define SVN_REV_STR "$WCREV$" +#define SVN_REV $WCREV$ +#define SVN_REV_STR "$WCREV$" diff --git a/project/vs2010_mfc/svnrev_template.h b/project/vs2010_mfc/svnrev_template.h index 24d8c760..db42e183 100644 --- a/project/vs2010_mfc/svnrev_template.h +++ b/project/vs2010_mfc/svnrev_template.h @@ -1,2 +1,2 @@ -#define SVN_REV $WCREV$ -#define SVN_REV_STR "$WCREV$" +#define SVN_REV $WCREV$ +#define SVN_REV_STR "$WCREV$" diff --git a/src/System.h b/src/System.h index 7fee5290..9c1b384a 100644 --- a/src/System.h +++ b/src/System.h @@ -1,92 +1,92 @@ -#ifndef SYSTEM_H -#define SYSTEM_H - -#include "common/Types.h" - -#include - -class SoundDriver; - -struct EmulatedSystem { - // main emulation function - void (*emuMain)(int); - // reset emulator - void (*emuReset)(); - // clean up memory - void (*emuCleanUp)(); - // load battery file - bool (*emuReadBattery)(const char *); - // write battery file - bool (*emuWriteBattery)(const char *); - // load state - bool (*emuReadState)(const char *); - // save state - bool (*emuWriteState)(const char *); - // load memory state (rewind) - bool (*emuReadMemState)(char *, int); - // write memory state (rewind) - bool (*emuWriteMemState)(char *, int); - // write PNG file - bool (*emuWritePNG)(const char *); - // write BMP file - bool (*emuWriteBMP)(const char *); - // emulator update CPSR (ARM only) - void (*emuUpdateCPSR)(); - // emulator has debugger - bool emuHasDebugger; - // clock ticks to emulate - int emuCount; -}; - -extern void log(const char *,...); - -extern bool systemPauseOnFrame(); -extern void systemGbPrint(u8 *,int,int,int,int); -extern void systemScreenCapture(int); -extern void systemDrawScreen(); -// updates the joystick data -extern bool systemReadJoypads(); -// return information about the given joystick, -1 for default joystick -extern u32 systemReadJoypad(int); -extern u32 systemGetClock(); -extern void systemMessage(int, const char *, ...); -extern void systemSetTitle(const char *); -extern SoundDriver * systemSoundInit(); -extern void systemOnWriteDataToSoundBuffer(const u16 * finalWave, int length); -extern void systemOnSoundShutdown(); -extern void systemScreenMessage(const char *); -extern void systemUpdateMotionSensor(); -extern int systemGetSensorX(); -extern int systemGetSensorY(); -extern bool systemCanChangeSoundQuality(); -extern void systemShowSpeed(int); -extern void system10Frames(int); -extern void systemFrame(); -extern void systemGbBorderOn(); - -extern void Sm60FPS_Init(); -extern bool Sm60FPS_CanSkipFrame(); -extern void Sm60FPS_Sleep(); -extern void DbgMsg(const char *msg, ...); -extern void winlog(const char *,...); - -extern void (*dbgOutput)(const char *s, u32 addr); -extern void (*dbgSignal)(int sig,int number); - -extern u16 systemColorMap16[0x10000]; -extern u32 systemColorMap32[0x10000]; -extern u16 systemGbPalette[24]; -extern int systemRedShift; -extern int systemGreenShift; -extern int systemBlueShift; -extern int systemColorDepth; -extern int systemDebug; -extern int systemVerbose; -extern int systemFrameSkip; -extern int systemSaveUpdateCounter; -extern int systemSpeed; - -#define SYSTEM_SAVE_UPDATED 30 -#define SYSTEM_SAVE_NOT_UPDATED 0 - -#endif // SYSTEM_H +#ifndef SYSTEM_H +#define SYSTEM_H + +#include "common/Types.h" + +#include + +class SoundDriver; + +struct EmulatedSystem { + // main emulation function + void (*emuMain)(int); + // reset emulator + void (*emuReset)(); + // clean up memory + void (*emuCleanUp)(); + // load battery file + bool (*emuReadBattery)(const char *); + // write battery file + bool (*emuWriteBattery)(const char *); + // load state + bool (*emuReadState)(const char *); + // save state + bool (*emuWriteState)(const char *); + // load memory state (rewind) + bool (*emuReadMemState)(char *, int); + // write memory state (rewind) + bool (*emuWriteMemState)(char *, int); + // write PNG file + bool (*emuWritePNG)(const char *); + // write BMP file + bool (*emuWriteBMP)(const char *); + // emulator update CPSR (ARM only) + void (*emuUpdateCPSR)(); + // emulator has debugger + bool emuHasDebugger; + // clock ticks to emulate + int emuCount; +}; + +extern void log(const char *,...); + +extern bool systemPauseOnFrame(); +extern void systemGbPrint(u8 *,int,int,int,int); +extern void systemScreenCapture(int); +extern void systemDrawScreen(); +// updates the joystick data +extern bool systemReadJoypads(); +// return information about the given joystick, -1 for default joystick +extern u32 systemReadJoypad(int); +extern u32 systemGetClock(); +extern void systemMessage(int, const char *, ...); +extern void systemSetTitle(const char *); +extern SoundDriver * systemSoundInit(); +extern void systemOnWriteDataToSoundBuffer(const u16 * finalWave, int length); +extern void systemOnSoundShutdown(); +extern void systemScreenMessage(const char *); +extern void systemUpdateMotionSensor(); +extern int systemGetSensorX(); +extern int systemGetSensorY(); +extern bool systemCanChangeSoundQuality(); +extern void systemShowSpeed(int); +extern void system10Frames(int); +extern void systemFrame(); +extern void systemGbBorderOn(); + +extern void Sm60FPS_Init(); +extern bool Sm60FPS_CanSkipFrame(); +extern void Sm60FPS_Sleep(); +extern void DbgMsg(const char *msg, ...); +extern void winlog(const char *,...); + +extern void (*dbgOutput)(const char *s, u32 addr); +extern void (*dbgSignal)(int sig,int number); + +extern u16 systemColorMap16[0x10000]; +extern u32 systemColorMap32[0x10000]; +extern u16 systemGbPalette[24]; +extern int systemRedShift; +extern int systemGreenShift; +extern int systemBlueShift; +extern int systemColorDepth; +extern int systemDebug; +extern int systemVerbose; +extern int systemFrameSkip; +extern int systemSaveUpdateCounter; +extern int systemSpeed; + +#define SYSTEM_SAVE_UPDATED 30 +#define SYSTEM_SAVE_NOT_UPDATED 0 + +#endif // SYSTEM_H diff --git a/src/common/Patch.cpp b/src/common/Patch.cpp index 07daabfb..cdc48545 100644 --- a/src/common/Patch.cpp +++ b/src/common/Patch.cpp @@ -1,459 +1,459 @@ -#include -#include -#include -#include - -#include "Patch.h" - - -#ifdef __GNUC__ -#if defined(__APPLE__) || defined (BSD) -typedef off_t __off64_t; /* off_t is 64 bits on BSD. */ -#define fseeko64 fseeko -#define ftello64 ftello -#else -typedef off64_t __off64_t; -#endif /* __APPLE__ || BSD */ -#endif /* __GNUC__ */ - -#ifndef _MSC_VER -#define _stricmp strcasecmp -#endif // ! _MSC_VER - -#ifdef _MSC_VER -#define fseeko64 _fseeki64 -#define ftello64 _ftelli64 -typedef __int64 __off64_t; -#endif - -static int readInt2(FILE *f) -{ - int res = 0; - int c = fgetc(f); - if(c == EOF) - return -1; - res = c; - c = fgetc(f); - if(c == EOF) - return -1; - return c + (res<<8); -} - -static int readInt3(FILE *f) -{ - int res = 0; - int c = fgetc(f); - if(c == EOF) - return -1; - res = c; - c = fgetc(f); - if(c == EOF) - return -1; - res = c + (res<<8); - c = fgetc(f); - if(c == EOF) - return -1; - return c + (res<<8); -} - -static s64 readInt4(FILE *f) -{ - s64 tmp, res = 0; - int c; - - for (int i = 0; i < 4; i++) { - c = fgetc(f); - if (c == EOF) - return -1; - tmp = c; - res = res + (tmp << (i*8)); - } - - return res; -} - -static s64 readInt8(FILE *f) -{ - s64 tmp, res = 0; - int c; - - for (int i = 0; i < 8; i++) { - c = fgetc(f); - if (c == EOF) - return -1; - tmp = c; - res = res + (tmp << (i*8)); - } - - return res; -} - -static s64 readVarPtr(FILE *f) -{ - s64 offset = 0, shift = 1; - for (;;) { - int c = fgetc(f); - if (c == EOF) return 0; - offset += (c & 0x7F) * shift; - if (c & 0x80) break; - shift <<= 7; - offset += shift; - } - return offset; -} - -#ifndef MIN -#define MIN(a,b) (((a)<(b))?(a):(b)) -#endif - -static uLong computePatchCRC(FILE *f, unsigned int size) -{ - Bytef buf[4096]; - long readed; - - uLong crc = crc32(0L, Z_NULL, 0); - do { - readed = fread(buf, 1, MIN(size, sizeof(buf)), f); - crc = crc32(crc, buf, readed); - size -= readed; - } while (readed > 0); - return crc; -} - -static bool patchApplyIPS(const char *patchname, u8 **r, int *s) -{ - // from the IPS spec at http://zerosoft.zophar.net/ips.htm - FILE *f = fopen(patchname, "rb"); - if(!f) - return false; - - bool result = false; - - u8 *rom = *r; - int size = *s; - if(fgetc(f) == 'P' && - fgetc(f) == 'A' && - fgetc(f) == 'T' && - fgetc(f) == 'C' && - fgetc(f) == 'H') { - int b; - int offset; - int len; - - result = true; - - for(;;) { - // read offset - offset = readInt3(f); - // if offset == EOF, end of patch - if(offset == 0x454f46 || offset == -1) - break; - // read length - len = readInt2(f); - if(!len) { - // len == 0, RLE block - len = readInt2(f); - // byte to fill - int c = fgetc(f); - if(c == -1) - break; - b = (u8)c; - } else - b= -1; - // check if we need to reallocate our ROM - if((offset + len) >= size) { - size *= 2; - rom = (u8 *)realloc(rom, size); - *r = rom; - *s = size; - } - if(b == -1) { - // normal block, just read the data - if(fread(&rom[offset], 1, len, f) != (size_t)len) - break; - } else { - // fill the region with the given byte - while(len--) { - rom[offset++] = b; - } - } - } - } - // close the file - fclose(f); - - return result; -} - -static bool patchApplyUPS(const char *patchname, u8 **rom, int *size) -{ - s64 srcCRC, dstCRC, patchCRC; - - FILE *f = fopen(patchname, "rb"); - if (!f) - return false; - - fseeko64(f, 0, SEEK_END); - __off64_t patchSize = ftello64(f); - if (patchSize < 20) { - fclose(f); - return false; - } - - fseeko64(f, 0, SEEK_SET); - if(fgetc(f) != 'U' || fgetc(f) != 'P' || fgetc(f) != 'S' || fgetc(f) != '1') { - fclose(f); - return false; - } - - fseeko64(f, -12, SEEK_END); - srcCRC = readInt4(f); - dstCRC = readInt4(f); - patchCRC = readInt4(f); - if (srcCRC == -1 || dstCRC == -1 || patchCRC == -1) { - fclose(f); - return false; - } - - fseeko64(f, 0, SEEK_SET); - u32 crc = computePatchCRC(f, patchSize-4); - - if (crc != patchCRC) { - fclose(f); - return false; - } - - crc = crc32(0L, Z_NULL, 0); - crc = crc32(crc, *rom, *size); - - fseeko64(f, 4, SEEK_SET); - s64 dataSize; - s64 srcSize = readVarPtr(f); - s64 dstSize = readVarPtr(f); - - if (crc == srcCRC) { - if (srcSize != *size) { - fclose(f); - return false; - } - dataSize = dstSize; - } else if (crc == dstCRC) { - if (dstSize != *size) { - fclose(f); - return false; - } - dataSize = srcSize; - } else { - fclose(f); - return false; - } - if (dataSize > *size) { - *rom = (u8*)realloc(*rom, dataSize); - memset(*rom + *size, 0, dataSize - *size); - *size = dataSize; - } - - s64 relative = 0; - u8 *mem; - while(ftello64(f) < patchSize - 12) { - relative += readVarPtr(f); - if (relative > dataSize) continue; - mem = *rom + relative; - for(s64 i = relative; i < dataSize; i++) { - int x = fgetc(f); - relative++; - if (!x) break; - if (i < dataSize) { - *mem++ ^= x; - } - } - } - - fclose(f); - return true; -} - -static int ppfVersion(FILE *f) -{ - fseeko64(f, 0, SEEK_SET); - if (fgetc(f) != 'P' || fgetc(f) != 'P' || fgetc(f) != 'F') - return 0; - switch(fgetc(f)){ - case '1': return 1; - case '2': return 2; - case '3': return 3; - default: return 0; - } -} - -static int ppfFileIdLen(FILE *f, int version) -{ - if (version == 2) { - fseeko64(f, -8, SEEK_END); - } else { - fseeko64(f, -6, SEEK_END); - } - - if (fgetc(f) != '.' || fgetc(f) != 'D' || fgetc(f) != 'I' || fgetc(f) != 'Z') - return 0; - - return (version == 2) ? readInt4(f) : readInt2(f); -} - -static bool patchApplyPPF1(FILE *f, u8 **rom, int *size) -{ - fseek(f, 0, SEEK_END); - int count = ftell(f); - if (count < 56) - return false; - count -= 56; - - fseek(f, 56, SEEK_SET); - - u8 *mem = *rom; - - while (count > 0) { - int offset = readInt4(f); - if (offset == -1) - break; - int len = fgetc(f); - if (len == EOF) - break; - if (offset+len > *size) - break; - if (fread(&mem[offset], 1, len, f) != (size_t)len) - break; - count -= 4 + 1 + len; - } - - return (count == 0); -} - -static bool patchApplyPPF2(FILE *f, u8 **rom, int *size) -{ - fseek(f, 0, SEEK_END); - int count = ftell(f); - if (count < 56+4+1024) - return false; - count -= 56+4+1024; - - fseek(f, 56, SEEK_SET); - - int datalen = readInt4(f); - if (datalen != *size) - return false; - - u8 *mem = *rom; - - u8 block[1024]; - fread(&block, 1, 1024, f); - if (memcmp(&mem[0x9320], &block, 1024) != 0) - return false; - - int idlen = ppfFileIdLen(f, 2); - if (idlen > 0) - count -= 16 + 16 + idlen; - - fseek(f, 56+4+1024, SEEK_SET); - - while (count > 0) { - int offset = readInt4(f); - if (offset == -1) - break; - int len = fgetc(f); - if (len == EOF) - break; - if (offset+len > *size) - break; - if (fread(&mem[offset], 1, len, f) != (size_t)len) - break; - count -= 4 + 1 + len; - } - - return (count == 0); -} - -static bool patchApplyPPF3(FILE *f, u8 **rom, int *size) -{ - fseek(f, 0, SEEK_END); - int count = ftell(f); - if (count < 56+4+1024) - return false; - count -= 56+4; - - fseek(f, 56, SEEK_SET); - - int imagetype = fgetc(f); - int blockcheck = fgetc(f); - int undo = fgetc(f); - fgetc(f); - - u8 *mem = *rom; - - if (blockcheck) { - u8 block[1024]; - fread(&block, 1, 1024, f); - if (memcmp(&mem[(imagetype == 0) ? 0x9320 : 0x80A0], &block, 1024) != 0) - return false; - count -= 1024; - } - - int idlen = ppfFileIdLen(f, 2); - if (idlen > 0) - count -= 16 + 16 + idlen; - - fseek(f, 56+4+(blockcheck ? 1024 : 0), SEEK_SET); - - while (count > 0) { - __off64_t offset = readInt8(f); - if (offset == -1) - break; - int len = fgetc(f); - if (len == EOF) - break; - if (offset+len > *size) - break; - if (fread(&mem[offset], 1, len, f) != (size_t)len) - break; - if (undo) fseeko64(f, len, SEEK_CUR); - count -= 8 + 1 + len; - if (undo) count -= len; - } - - return (count == 0); -} - -static bool patchApplyPPF(const char *patchname, u8 **rom, int *size) -{ - FILE *f = fopen(patchname, "rb"); - if (!f) - return false; - - bool res = false; - - int version = ppfVersion(f); - switch (version) { - case 1: res = patchApplyPPF1(f, rom, size); break; - case 2: res = patchApplyPPF2(f, rom, size); break; - case 3: res = patchApplyPPF3(f, rom, size); break; - } - - fclose(f); - return res; -} - -bool applyPatch(const char *patchname, u8 **rom, int *size) -{ - if (strlen(patchname) < 5) - return false; - const char * p = strrchr(patchname, '.'); - if (p == NULL) - return false; - if (_stricmp(p, ".ips") == 0) - return patchApplyIPS(patchname, rom, size); - if (_stricmp(p, ".ups") == 0) - return patchApplyUPS(patchname, rom, size); - if (_stricmp(p, ".ppf") == 0) - return patchApplyPPF(patchname, rom, size); - return false; -} +#include +#include +#include +#include + +#include "Patch.h" + + +#ifdef __GNUC__ +#if defined(__APPLE__) || defined (BSD) +typedef off_t __off64_t; /* off_t is 64 bits on BSD. */ +#define fseeko64 fseeko +#define ftello64 ftello +#else +typedef off64_t __off64_t; +#endif /* __APPLE__ || BSD */ +#endif /* __GNUC__ */ + +#ifndef _MSC_VER +#define _stricmp strcasecmp +#endif // ! _MSC_VER + +#ifdef _MSC_VER +#define fseeko64 _fseeki64 +#define ftello64 _ftelli64 +typedef __int64 __off64_t; +#endif + +static int readInt2(FILE *f) +{ + int res = 0; + int c = fgetc(f); + if(c == EOF) + return -1; + res = c; + c = fgetc(f); + if(c == EOF) + return -1; + return c + (res<<8); +} + +static int readInt3(FILE *f) +{ + int res = 0; + int c = fgetc(f); + if(c == EOF) + return -1; + res = c; + c = fgetc(f); + if(c == EOF) + return -1; + res = c + (res<<8); + c = fgetc(f); + if(c == EOF) + return -1; + return c + (res<<8); +} + +static s64 readInt4(FILE *f) +{ + s64 tmp, res = 0; + int c; + + for (int i = 0; i < 4; i++) { + c = fgetc(f); + if (c == EOF) + return -1; + tmp = c; + res = res + (tmp << (i*8)); + } + + return res; +} + +static s64 readInt8(FILE *f) +{ + s64 tmp, res = 0; + int c; + + for (int i = 0; i < 8; i++) { + c = fgetc(f); + if (c == EOF) + return -1; + tmp = c; + res = res + (tmp << (i*8)); + } + + return res; +} + +static s64 readVarPtr(FILE *f) +{ + s64 offset = 0, shift = 1; + for (;;) { + int c = fgetc(f); + if (c == EOF) return 0; + offset += (c & 0x7F) * shift; + if (c & 0x80) break; + shift <<= 7; + offset += shift; + } + return offset; +} + +#ifndef MIN +#define MIN(a,b) (((a)<(b))?(a):(b)) +#endif + +static uLong computePatchCRC(FILE *f, unsigned int size) +{ + Bytef buf[4096]; + long readed; + + uLong crc = crc32(0L, Z_NULL, 0); + do { + readed = fread(buf, 1, MIN(size, sizeof(buf)), f); + crc = crc32(crc, buf, readed); + size -= readed; + } while (readed > 0); + return crc; +} + +static bool patchApplyIPS(const char *patchname, u8 **r, int *s) +{ + // from the IPS spec at http://zerosoft.zophar.net/ips.htm + FILE *f = fopen(patchname, "rb"); + if(!f) + return false; + + bool result = false; + + u8 *rom = *r; + int size = *s; + if(fgetc(f) == 'P' && + fgetc(f) == 'A' && + fgetc(f) == 'T' && + fgetc(f) == 'C' && + fgetc(f) == 'H') { + int b; + int offset; + int len; + + result = true; + + for(;;) { + // read offset + offset = readInt3(f); + // if offset == EOF, end of patch + if(offset == 0x454f46 || offset == -1) + break; + // read length + len = readInt2(f); + if(!len) { + // len == 0, RLE block + len = readInt2(f); + // byte to fill + int c = fgetc(f); + if(c == -1) + break; + b = (u8)c; + } else + b= -1; + // check if we need to reallocate our ROM + if((offset + len) >= size) { + size *= 2; + rom = (u8 *)realloc(rom, size); + *r = rom; + *s = size; + } + if(b == -1) { + // normal block, just read the data + if(fread(&rom[offset], 1, len, f) != (size_t)len) + break; + } else { + // fill the region with the given byte + while(len--) { + rom[offset++] = b; + } + } + } + } + // close the file + fclose(f); + + return result; +} + +static bool patchApplyUPS(const char *patchname, u8 **rom, int *size) +{ + s64 srcCRC, dstCRC, patchCRC; + + FILE *f = fopen(patchname, "rb"); + if (!f) + return false; + + fseeko64(f, 0, SEEK_END); + __off64_t patchSize = ftello64(f); + if (patchSize < 20) { + fclose(f); + return false; + } + + fseeko64(f, 0, SEEK_SET); + if(fgetc(f) != 'U' || fgetc(f) != 'P' || fgetc(f) != 'S' || fgetc(f) != '1') { + fclose(f); + return false; + } + + fseeko64(f, -12, SEEK_END); + srcCRC = readInt4(f); + dstCRC = readInt4(f); + patchCRC = readInt4(f); + if (srcCRC == -1 || dstCRC == -1 || patchCRC == -1) { + fclose(f); + return false; + } + + fseeko64(f, 0, SEEK_SET); + u32 crc = computePatchCRC(f, patchSize-4); + + if (crc != patchCRC) { + fclose(f); + return false; + } + + crc = crc32(0L, Z_NULL, 0); + crc = crc32(crc, *rom, *size); + + fseeko64(f, 4, SEEK_SET); + s64 dataSize; + s64 srcSize = readVarPtr(f); + s64 dstSize = readVarPtr(f); + + if (crc == srcCRC) { + if (srcSize != *size) { + fclose(f); + return false; + } + dataSize = dstSize; + } else if (crc == dstCRC) { + if (dstSize != *size) { + fclose(f); + return false; + } + dataSize = srcSize; + } else { + fclose(f); + return false; + } + if (dataSize > *size) { + *rom = (u8*)realloc(*rom, dataSize); + memset(*rom + *size, 0, dataSize - *size); + *size = dataSize; + } + + s64 relative = 0; + u8 *mem; + while(ftello64(f) < patchSize - 12) { + relative += readVarPtr(f); + if (relative > dataSize) continue; + mem = *rom + relative; + for(s64 i = relative; i < dataSize; i++) { + int x = fgetc(f); + relative++; + if (!x) break; + if (i < dataSize) { + *mem++ ^= x; + } + } + } + + fclose(f); + return true; +} + +static int ppfVersion(FILE *f) +{ + fseeko64(f, 0, SEEK_SET); + if (fgetc(f) != 'P' || fgetc(f) != 'P' || fgetc(f) != 'F') + return 0; + switch(fgetc(f)){ + case '1': return 1; + case '2': return 2; + case '3': return 3; + default: return 0; + } +} + +static int ppfFileIdLen(FILE *f, int version) +{ + if (version == 2) { + fseeko64(f, -8, SEEK_END); + } else { + fseeko64(f, -6, SEEK_END); + } + + if (fgetc(f) != '.' || fgetc(f) != 'D' || fgetc(f) != 'I' || fgetc(f) != 'Z') + return 0; + + return (version == 2) ? readInt4(f) : readInt2(f); +} + +static bool patchApplyPPF1(FILE *f, u8 **rom, int *size) +{ + fseek(f, 0, SEEK_END); + int count = ftell(f); + if (count < 56) + return false; + count -= 56; + + fseek(f, 56, SEEK_SET); + + u8 *mem = *rom; + + while (count > 0) { + int offset = readInt4(f); + if (offset == -1) + break; + int len = fgetc(f); + if (len == EOF) + break; + if (offset+len > *size) + break; + if (fread(&mem[offset], 1, len, f) != (size_t)len) + break; + count -= 4 + 1 + len; + } + + return (count == 0); +} + +static bool patchApplyPPF2(FILE *f, u8 **rom, int *size) +{ + fseek(f, 0, SEEK_END); + int count = ftell(f); + if (count < 56+4+1024) + return false; + count -= 56+4+1024; + + fseek(f, 56, SEEK_SET); + + int datalen = readInt4(f); + if (datalen != *size) + return false; + + u8 *mem = *rom; + + u8 block[1024]; + fread(&block, 1, 1024, f); + if (memcmp(&mem[0x9320], &block, 1024) != 0) + return false; + + int idlen = ppfFileIdLen(f, 2); + if (idlen > 0) + count -= 16 + 16 + idlen; + + fseek(f, 56+4+1024, SEEK_SET); + + while (count > 0) { + int offset = readInt4(f); + if (offset == -1) + break; + int len = fgetc(f); + if (len == EOF) + break; + if (offset+len > *size) + break; + if (fread(&mem[offset], 1, len, f) != (size_t)len) + break; + count -= 4 + 1 + len; + } + + return (count == 0); +} + +static bool patchApplyPPF3(FILE *f, u8 **rom, int *size) +{ + fseek(f, 0, SEEK_END); + int count = ftell(f); + if (count < 56+4+1024) + return false; + count -= 56+4; + + fseek(f, 56, SEEK_SET); + + int imagetype = fgetc(f); + int blockcheck = fgetc(f); + int undo = fgetc(f); + fgetc(f); + + u8 *mem = *rom; + + if (blockcheck) { + u8 block[1024]; + fread(&block, 1, 1024, f); + if (memcmp(&mem[(imagetype == 0) ? 0x9320 : 0x80A0], &block, 1024) != 0) + return false; + count -= 1024; + } + + int idlen = ppfFileIdLen(f, 2); + if (idlen > 0) + count -= 16 + 16 + idlen; + + fseek(f, 56+4+(blockcheck ? 1024 : 0), SEEK_SET); + + while (count > 0) { + __off64_t offset = readInt8(f); + if (offset == -1) + break; + int len = fgetc(f); + if (len == EOF) + break; + if (offset+len > *size) + break; + if (fread(&mem[offset], 1, len, f) != (size_t)len) + break; + if (undo) fseeko64(f, len, SEEK_CUR); + count -= 8 + 1 + len; + if (undo) count -= len; + } + + return (count == 0); +} + +static bool patchApplyPPF(const char *patchname, u8 **rom, int *size) +{ + FILE *f = fopen(patchname, "rb"); + if (!f) + return false; + + bool res = false; + + int version = ppfVersion(f); + switch (version) { + case 1: res = patchApplyPPF1(f, rom, size); break; + case 2: res = patchApplyPPF2(f, rom, size); break; + case 3: res = patchApplyPPF3(f, rom, size); break; + } + + fclose(f); + return res; +} + +bool applyPatch(const char *patchname, u8 **rom, int *size) +{ + if (strlen(patchname) < 5) + return false; + const char * p = strrchr(patchname, '.'); + if (p == NULL) + return false; + if (_stricmp(p, ".ips") == 0) + return patchApplyIPS(patchname, rom, size); + if (_stricmp(p, ".ups") == 0) + return patchApplyUPS(patchname, rom, size); + if (_stricmp(p, ".ppf") == 0) + return patchApplyPPF(patchname, rom, size); + return false; +} diff --git a/src/common/Patch.h b/src/common/Patch.h index 0e69fee2..5757a63d 100644 --- a/src/common/Patch.h +++ b/src/common/Patch.h @@ -1,8 +1,8 @@ -#ifndef PATCH_H -#define PATCH_H - -#include "Types.h" - -bool applyPatch(const char *patchname, u8 **rom, int *size); - -#endif // PATCH_H +#ifndef PATCH_H +#define PATCH_H + +#include "Types.h" + +bool applyPatch(const char *patchname, u8 **rom, int *size); + +#endif // PATCH_H diff --git a/src/common/memgzio.c b/src/common/memgzio.c index cb38cb83..4d0ebb32 100644 --- a/src/common/memgzio.c +++ b/src/common/memgzio.c @@ -9,7 +9,7 @@ * Adapted from original gzio.c from zlib library by Forgotten */ -/* @(#) $Id: memgzio.c,v 1.5 2006/06/06 21:04:20 spacy51 Exp $ */ +/* @(#) $Id$ */ #include #include diff --git a/src/gb/GB.cpp b/src/gb/GB.cpp index 1bdfe7f1..68f3f097 100644 --- a/src/gb/GB.cpp +++ b/src/gb/GB.cpp @@ -1,5467 +1,5467 @@ -//#include "../win32/stdafx.h" // would fix LNK2005 linker errors for MSVC -#include -#include -#include -#include -#include - -#include "../System.h" -#include "../NLS.h" -#include "gb.h" -#include "gbCheats.h" -#include "gbGlobals.h" -#include "gbMemory.h" -#include "gbSGB.h" -#include "gbSound.h" -#include "../Util.h" - -#ifdef __GNUC__ -#define _stricmp strcasecmp -#endif - -extern u8 *pix; -extern bool speedup; -bool gbUpdateSizes(); -bool inBios = false; - -// debugging -bool memorydebug = false; -char gbBuffer[2048]; - -extern u16 gbLineMix[160]; - -// mappers -void (*mapper)(u16,u8) = NULL; -void (*mapperRAM)(u16,u8) = NULL; -u8 (*mapperReadRAM)(u16) = NULL; -void (*mapperUpdateClock)() = NULL; - -// registers -gbRegister PC; -gbRegister SP; -gbRegister AF; -gbRegister BC; -gbRegister DE; -gbRegister HL; -u16 IFF = 0; -// 0xff04 -u8 register_DIV = 0; -// 0xff05 -u8 register_TIMA = 0; -// 0xff06 -u8 register_TMA = 0; -// 0xff07 -u8 register_TAC = 0; -// 0xff0f -u8 register_IF = 0; -// 0xff40 -u8 register_LCDC = 0; -// 0xff41 -u8 register_STAT = 0; -// 0xff42 -u8 register_SCY = 0; -// 0xff43 -u8 register_SCX = 0; -// 0xff44 -u8 register_LY = 0; -// 0xff45 -u8 register_LYC = 0; -// 0xff46 -u8 register_DMA = 0; -// 0xff4a -u8 register_WY = 0; -// 0xff4b -u8 register_WX = 0; -// 0xff4f -u8 register_VBK = 0; -// 0xff51 -u8 register_HDMA1 = 0; -// 0xff52 -u8 register_HDMA2 = 0; -// 0xff53 -u8 register_HDMA3 = 0; -// 0xff54 -u8 register_HDMA4 = 0; -// 0xff55 -u8 register_HDMA5 = 0; -// 0xff70 -u8 register_SVBK = 0; -// 0xffff -u8 register_IE = 0; - -// ticks definition -int GBDIV_CLOCK_TICKS = 64; -int GBLCD_MODE_0_CLOCK_TICKS = 51; -int GBLCD_MODE_1_CLOCK_TICKS = 1140; -int GBLCD_MODE_2_CLOCK_TICKS = 20; -int GBLCD_MODE_3_CLOCK_TICKS = 43; -int GBLY_INCREMENT_CLOCK_TICKS = 114; -int GBTIMER_MODE_0_CLOCK_TICKS = 256; -int GBTIMER_MODE_1_CLOCK_TICKS = 4; -int GBTIMER_MODE_2_CLOCK_TICKS = 16; -int GBTIMER_MODE_3_CLOCK_TICKS = 64; -int GBSERIAL_CLOCK_TICKS = 128; -int GBSYNCHRONIZE_CLOCK_TICKS = 52920; - -// state variables - -// general -int clockTicks = 0; -bool gbSystemMessage = false; -int gbGBCColorType = 0; -int gbHardware = 0; -int gbRomType = 0; -int gbRemainingClockTicks = 0; -int gbOldClockTicks = 0; -int gbIntBreak = 0; -int gbInterruptLaunched = 0; -u8 gbCheatingDevice = 0; // 1 = GS, 2 = GG -// breakpoint -bool breakpoint = false; -// interrupt -int gbInt48Signal = 0; -int gbInterruptWait = 0; -// serial -int gbSerialOn = 0; -int gbSerialTicks = 0; -int gbSerialBits = 0; -// timer -bool gbTimerOn = false; -int gbTimerTicks = GBTIMER_MODE_0_CLOCK_TICKS; -int gbTimerClockTicks = GBTIMER_MODE_0_CLOCK_TICKS; -int gbTimerMode = 0; -bool gbIncreased = false; -// The internal timer is always active, and it is -// not reset by writing to register_TIMA/TMA, but by -// writing to register_DIV... -int gbInternalTimer = 0x55; -const u8 gbTimerMask [4] = {0xff, 0x3, 0xf, 0x3f}; -const u8 gbTimerBug [8] = {0x80, 0x80, 0x02, 0x02, 0x0, 0xff, 0x0, 0xff}; -bool gbTimerModeChange = false; -bool gbTimerOnChange = false; -// lcd -bool gbScreenOn = true; -int gbLcdMode = 2; -int gbLcdModeDelayed = 2; -int gbLcdTicks = GBLCD_MODE_2_CLOCK_TICKS-1; -int gbLcdTicksDelayed = GBLCD_MODE_2_CLOCK_TICKS; -int gbLcdLYIncrementTicks = 114; -int gbLcdLYIncrementTicksDelayed = 115; -int gbScreenTicks = 0; -u8 gbSCYLine[300]; -u8 gbSCXLine[300]; -u8 gbBgpLine[300]; -u8 gbObp0Line [300]; -u8 gbObp1Line [300]; -u8 gbSpritesTicks [300]; -u8 oldRegister_WY; -bool gbLYChangeHappened = false; -bool gbLCDChangeHappened = false; -int gbLine99Ticks = 1; -int gbRegisterLYLCDCOffOn = 0; -int inUseRegister_WY = 0; - -// Used to keep track of the line that ellapse -// when screen is off -int gbWhiteScreen = 0; -bool gbBlackScreen = false; -int register_LCDCBusy = 0; - -// div -int gbDivTicks = GBDIV_CLOCK_TICKS; -// cgb -int gbVramBank = 0; -int gbWramBank = 1; -//sgb -bool gbSgbResetFlag = false; -// gbHdmaDestination is 0x99d0 on startup (tested on HW) -// but I'm not sure what gbHdmaSource is... -int gbHdmaSource = 0x99d0; -int gbHdmaDestination = 0x99d0; -int gbHdmaBytes = 0x0000; -int gbHdmaOn = 0; -int gbSpeed = 0; -// frame counting -int gbFrameCount = 0; -int gbFrameSkip = 0; -int gbFrameSkipCount = 0; -// timing -u32 gbLastTime = 0; -u32 gbElapsedTime = 0; -u32 gbTimeNow = 0; -int gbSynchronizeTicks = GBSYNCHRONIZE_CLOCK_TICKS; -// emulator features -int gbBattery = 0; -bool gbBatteryError = false; -int gbCaptureNumber = 0; -bool gbCapture = false; -bool gbCapturePrevious = false; -int gbJoymask[4] = { 0, 0, 0, 0 }; - -u8 gbRamFill = 0xff; - -int gbRomSizes[] = { 0x00008000, // 32K - 0x00010000, // 64K - 0x00020000, // 128K - 0x00040000, // 256K - 0x00080000, // 512K - 0x00100000, // 1024K - 0x00200000, // 2048K - 0x00400000, // 4096K - 0x00800000 // 8192K -}; -int gbRomSizesMasks[] = { 0x00007fff, - 0x0000ffff, - 0x0001ffff, - 0x0003ffff, - 0x0007ffff, - 0x000fffff, - 0x001fffff, - 0x003fffff, - 0x007fffff -}; - -int gbRamSizes[6] = { 0x00000000, // 0K - 0x00002000, // 2K // Changed to 2000 to avoid problems with gbMemoryMap... - 0x00002000, // 8K - 0x00008000, // 32K - 0x00020000, // 128K - 0x00010000 // 64K -}; - -int gbRamSizesMasks[6] = { 0x00000000, - 0x000007ff, - 0x00001fff, - 0x00007fff, - 0x0001ffff, - 0x0000ffff -}; - -int gbCycles[] = { -// 0 1 2 3 4 5 6 7 8 9 a b c d e f - 1, 3, 2, 2, 1, 1, 2, 1, 5, 2, 2, 2, 1, 1, 2, 1, // 0 - 1, 3, 2, 2, 1, 1, 2, 1, 3, 2, 2, 2, 1, 1, 2, 1, // 1 - 2, 3, 2, 2, 1, 1, 2, 1, 2, 2, 2, 2, 1, 1, 2, 1, // 2 - 2, 3, 2, 2, 3, 3, 3, 1, 2, 2, 2, 2, 1, 1, 2, 1, // 3 - 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, // 4 - 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, // 5 - 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, // 6 - 2, 2, 2, 2, 2, 2, 1, 2, 1, 1, 1, 1, 1, 1, 2, 1, // 7 - 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, // 8 - 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, // 9 - 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, // a - 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, // b - 2, 3, 3, 4, 3, 4, 2, 4, 2, 4, 3, 2, 3, 6, 2, 4, // c - 2, 3, 3, 1, 3, 4, 2, 4, 2, 4, 3, 1, 3, 1, 2, 4, // d - 3, 3, 2, 1, 1, 4, 2, 4, 4, 1, 4, 1, 1, 1, 2, 4, // e - 3, 3, 2, 1, 1, 4, 2, 4, 3, 2, 4, 1, 0, 1, 2, 4 // f -}; - -int gbCyclesCB[] = { -// 0 1 2 3 4 5 6 7 8 9 a b c d e f - 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, // 0 - 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, // 1 - 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, // 2 - 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, // 3 - 2, 2, 2, 2, 2, 2, 3, 2, 2, 2, 2, 2, 2, 2, 3, 2, // 4 - 2, 2, 2, 2, 2, 2, 3, 2, 2, 2, 2, 2, 2, 2, 3, 2, // 5 - 2, 2, 2, 2, 2, 2, 3, 2, 2, 2, 2, 2, 2, 2, 3, 2, // 6 - 2, 2, 2, 2, 2, 2, 3, 2, 2, 2, 2, 2, 2, 2, 3, 2, // 7 - 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, // 8 - 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, // 9 - 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, // a - 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, // b - 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, // c - 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, // d - 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, // e - 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2 // f -}; - -u16 DAATable[] = { - 0x0080,0x0100,0x0200,0x0300,0x0400,0x0500,0x0600,0x0700, - 0x0800,0x0900,0x1000,0x1100,0x1200,0x1300,0x1400,0x1500, - 0x1000,0x1100,0x1200,0x1300,0x1400,0x1500,0x1600,0x1700, - 0x1800,0x1900,0x2000,0x2100,0x2200,0x2300,0x2400,0x2500, - 0x2000,0x2100,0x2200,0x2300,0x2400,0x2500,0x2600,0x2700, - 0x2800,0x2900,0x3000,0x3100,0x3200,0x3300,0x3400,0x3500, - 0x3000,0x3100,0x3200,0x3300,0x3400,0x3500,0x3600,0x3700, - 0x3800,0x3900,0x4000,0x4100,0x4200,0x4300,0x4400,0x4500, - 0x4000,0x4100,0x4200,0x4300,0x4400,0x4500,0x4600,0x4700, - 0x4800,0x4900,0x5000,0x5100,0x5200,0x5300,0x5400,0x5500, - 0x5000,0x5100,0x5200,0x5300,0x5400,0x5500,0x5600,0x5700, - 0x5800,0x5900,0x6000,0x6100,0x6200,0x6300,0x6400,0x6500, - 0x6000,0x6100,0x6200,0x6300,0x6400,0x6500,0x6600,0x6700, - 0x6800,0x6900,0x7000,0x7100,0x7200,0x7300,0x7400,0x7500, - 0x7000,0x7100,0x7200,0x7300,0x7400,0x7500,0x7600,0x7700, - 0x7800,0x7900,0x8000,0x8100,0x8200,0x8300,0x8400,0x8500, - 0x8000,0x8100,0x8200,0x8300,0x8400,0x8500,0x8600,0x8700, - 0x8800,0x8900,0x9000,0x9100,0x9200,0x9300,0x9400,0x9500, - 0x9000,0x9100,0x9200,0x9300,0x9400,0x9500,0x9600,0x9700, - 0x9800,0x9900,0x0090,0x0110,0x0210,0x0310,0x0410,0x0510, - 0x0090,0x0110,0x0210,0x0310,0x0410,0x0510,0x0610,0x0710, - 0x0810,0x0910,0x1010,0x1110,0x1210,0x1310,0x1410,0x1510, - 0x1010,0x1110,0x1210,0x1310,0x1410,0x1510,0x1610,0x1710, - 0x1810,0x1910,0x2010,0x2110,0x2210,0x2310,0x2410,0x2510, - 0x2010,0x2110,0x2210,0x2310,0x2410,0x2510,0x2610,0x2710, - 0x2810,0x2910,0x3010,0x3110,0x3210,0x3310,0x3410,0x3510, - 0x3010,0x3110,0x3210,0x3310,0x3410,0x3510,0x3610,0x3710, - 0x3810,0x3910,0x4010,0x4110,0x4210,0x4310,0x4410,0x4510, - 0x4010,0x4110,0x4210,0x4310,0x4410,0x4510,0x4610,0x4710, - 0x4810,0x4910,0x5010,0x5110,0x5210,0x5310,0x5410,0x5510, - 0x5010,0x5110,0x5210,0x5310,0x5410,0x5510,0x5610,0x5710, - 0x5810,0x5910,0x6010,0x6110,0x6210,0x6310,0x6410,0x6510, - 0x6010,0x6110,0x6210,0x6310,0x6410,0x6510,0x6610,0x6710, - 0x6810,0x6910,0x7010,0x7110,0x7210,0x7310,0x7410,0x7510, - 0x7010,0x7110,0x7210,0x7310,0x7410,0x7510,0x7610,0x7710, - 0x7810,0x7910,0x8010,0x8110,0x8210,0x8310,0x8410,0x8510, - 0x8010,0x8110,0x8210,0x8310,0x8410,0x8510,0x8610,0x8710, - 0x8810,0x8910,0x9010,0x9110,0x9210,0x9310,0x9410,0x9510, - 0x9010,0x9110,0x9210,0x9310,0x9410,0x9510,0x9610,0x9710, - 0x9810,0x9910,0xA010,0xA110,0xA210,0xA310,0xA410,0xA510, - 0xA010,0xA110,0xA210,0xA310,0xA410,0xA510,0xA610,0xA710, - 0xA810,0xA910,0xB010,0xB110,0xB210,0xB310,0xB410,0xB510, - 0xB010,0xB110,0xB210,0xB310,0xB410,0xB510,0xB610,0xB710, - 0xB810,0xB910,0xC010,0xC110,0xC210,0xC310,0xC410,0xC510, - 0xC010,0xC110,0xC210,0xC310,0xC410,0xC510,0xC610,0xC710, - 0xC810,0xC910,0xD010,0xD110,0xD210,0xD310,0xD410,0xD510, - 0xD010,0xD110,0xD210,0xD310,0xD410,0xD510,0xD610,0xD710, - 0xD810,0xD910,0xE010,0xE110,0xE210,0xE310,0xE410,0xE510, - 0xE010,0xE110,0xE210,0xE310,0xE410,0xE510,0xE610,0xE710, - 0xE810,0xE910,0xF010,0xF110,0xF210,0xF310,0xF410,0xF510, - 0xF010,0xF110,0xF210,0xF310,0xF410,0xF510,0xF610,0xF710, - 0xF810,0xF910,0x0090,0x0110,0x0210,0x0310,0x0410,0x0510, - 0x0090,0x0110,0x0210,0x0310,0x0410,0x0510,0x0610,0x0710, - 0x0810,0x0910,0x1010,0x1110,0x1210,0x1310,0x1410,0x1510, - 0x1010,0x1110,0x1210,0x1310,0x1410,0x1510,0x1610,0x1710, - 0x1810,0x1910,0x2010,0x2110,0x2210,0x2310,0x2410,0x2510, - 0x2010,0x2110,0x2210,0x2310,0x2410,0x2510,0x2610,0x2710, - 0x2810,0x2910,0x3010,0x3110,0x3210,0x3310,0x3410,0x3510, - 0x3010,0x3110,0x3210,0x3310,0x3410,0x3510,0x3610,0x3710, - 0x3810,0x3910,0x4010,0x4110,0x4210,0x4310,0x4410,0x4510, - 0x4010,0x4110,0x4210,0x4310,0x4410,0x4510,0x4610,0x4710, - 0x4810,0x4910,0x5010,0x5110,0x5210,0x5310,0x5410,0x5510, - 0x5010,0x5110,0x5210,0x5310,0x5410,0x5510,0x5610,0x5710, - 0x5810,0x5910,0x6010,0x6110,0x6210,0x6310,0x6410,0x6510, - 0x0600,0x0700,0x0800,0x0900,0x0A00,0x0B00,0x0C00,0x0D00, - 0x0E00,0x0F00,0x1000,0x1100,0x1200,0x1300,0x1400,0x1500, - 0x1600,0x1700,0x1800,0x1900,0x1A00,0x1B00,0x1C00,0x1D00, - 0x1E00,0x1F00,0x2000,0x2100,0x2200,0x2300,0x2400,0x2500, - 0x2600,0x2700,0x2800,0x2900,0x2A00,0x2B00,0x2C00,0x2D00, - 0x2E00,0x2F00,0x3000,0x3100,0x3200,0x3300,0x3400,0x3500, - 0x3600,0x3700,0x3800,0x3900,0x3A00,0x3B00,0x3C00,0x3D00, - 0x3E00,0x3F00,0x4000,0x4100,0x4200,0x4300,0x4400,0x4500, - 0x4600,0x4700,0x4800,0x4900,0x4A00,0x4B00,0x4C00,0x4D00, - 0x4E00,0x4F00,0x5000,0x5100,0x5200,0x5300,0x5400,0x5500, - 0x5600,0x5700,0x5800,0x5900,0x5A00,0x5B00,0x5C00,0x5D00, - 0x5E00,0x5F00,0x6000,0x6100,0x6200,0x6300,0x6400,0x6500, - 0x6600,0x6700,0x6800,0x6900,0x6A00,0x6B00,0x6C00,0x6D00, - 0x6E00,0x6F00,0x7000,0x7100,0x7200,0x7300,0x7400,0x7500, - 0x7600,0x7700,0x7800,0x7900,0x7A00,0x7B00,0x7C00,0x7D00, - 0x7E00,0x7F00,0x8000,0x8100,0x8200,0x8300,0x8400,0x8500, - 0x8600,0x8700,0x8800,0x8900,0x8A00,0x8B00,0x8C00,0x8D00, - 0x8E00,0x8F00,0x9000,0x9100,0x9200,0x9300,0x9400,0x9500, - 0x9600,0x9700,0x9800,0x9900,0x9A00,0x9B00,0x9C00,0x9D00, - 0x9E00,0x9F00,0x0090,0x0110,0x0210,0x0310,0x0410,0x0510, - 0x0610,0x0710,0x0810,0x0910,0x0A10,0x0B10,0x0C10,0x0D10, - 0x0E10,0x0F10,0x1010,0x1110,0x1210,0x1310,0x1410,0x1510, - 0x1610,0x1710,0x1810,0x1910,0x1A10,0x1B10,0x1C10,0x1D10, - 0x1E10,0x1F10,0x2010,0x2110,0x2210,0x2310,0x2410,0x2510, - 0x2610,0x2710,0x2810,0x2910,0x2A10,0x2B10,0x2C10,0x2D10, - 0x2E10,0x2F10,0x3010,0x3110,0x3210,0x3310,0x3410,0x3510, - 0x3610,0x3710,0x3810,0x3910,0x3A10,0x3B10,0x3C10,0x3D10, - 0x3E10,0x3F10,0x4010,0x4110,0x4210,0x4310,0x4410,0x4510, - 0x4610,0x4710,0x4810,0x4910,0x4A10,0x4B10,0x4C10,0x4D10, - 0x4E10,0x4F10,0x5010,0x5110,0x5210,0x5310,0x5410,0x5510, - 0x5610,0x5710,0x5810,0x5910,0x5A10,0x5B10,0x5C10,0x5D10, - 0x5E10,0x5F10,0x6010,0x6110,0x6210,0x6310,0x6410,0x6510, - 0x6610,0x6710,0x6810,0x6910,0x6A10,0x6B10,0x6C10,0x6D10, - 0x6E10,0x6F10,0x7010,0x7110,0x7210,0x7310,0x7410,0x7510, - 0x7610,0x7710,0x7810,0x7910,0x7A10,0x7B10,0x7C10,0x7D10, - 0x7E10,0x7F10,0x8010,0x8110,0x8210,0x8310,0x8410,0x8510, - 0x8610,0x8710,0x8810,0x8910,0x8A10,0x8B10,0x8C10,0x8D10, - 0x8E10,0x8F10,0x9010,0x9110,0x9210,0x9310,0x9410,0x9510, - 0x9610,0x9710,0x9810,0x9910,0x9A10,0x9B10,0x9C10,0x9D10, - 0x9E10,0x9F10,0xA010,0xA110,0xA210,0xA310,0xA410,0xA510, - 0xA610,0xA710,0xA810,0xA910,0xAA10,0xAB10,0xAC10,0xAD10, - 0xAE10,0xAF10,0xB010,0xB110,0xB210,0xB310,0xB410,0xB510, - 0xB610,0xB710,0xB810,0xB910,0xBA10,0xBB10,0xBC10,0xBD10, - 0xBE10,0xBF10,0xC010,0xC110,0xC210,0xC310,0xC410,0xC510, - 0xC610,0xC710,0xC810,0xC910,0xCA10,0xCB10,0xCC10,0xCD10, - 0xCE10,0xCF10,0xD010,0xD110,0xD210,0xD310,0xD410,0xD510, - 0xD610,0xD710,0xD810,0xD910,0xDA10,0xDB10,0xDC10,0xDD10, - 0xDE10,0xDF10,0xE010,0xE110,0xE210,0xE310,0xE410,0xE510, - 0xE610,0xE710,0xE810,0xE910,0xEA10,0xEB10,0xEC10,0xED10, - 0xEE10,0xEF10,0xF010,0xF110,0xF210,0xF310,0xF410,0xF510, - 0xF610,0xF710,0xF810,0xF910,0xFA10,0xFB10,0xFC10,0xFD10, - 0xFE10,0xFF10,0x0090,0x0110,0x0210,0x0310,0x0410,0x0510, - 0x0610,0x0710,0x0810,0x0910,0x0A10,0x0B10,0x0C10,0x0D10, - 0x0E10,0x0F10,0x1010,0x1110,0x1210,0x1310,0x1410,0x1510, - 0x1610,0x1710,0x1810,0x1910,0x1A10,0x1B10,0x1C10,0x1D10, - 0x1E10,0x1F10,0x2010,0x2110,0x2210,0x2310,0x2410,0x2510, - 0x2610,0x2710,0x2810,0x2910,0x2A10,0x2B10,0x2C10,0x2D10, - 0x2E10,0x2F10,0x3010,0x3110,0x3210,0x3310,0x3410,0x3510, - 0x3610,0x3710,0x3810,0x3910,0x3A10,0x3B10,0x3C10,0x3D10, - 0x3E10,0x3F10,0x4010,0x4110,0x4210,0x4310,0x4410,0x4510, - 0x4610,0x4710,0x4810,0x4910,0x4A10,0x4B10,0x4C10,0x4D10, - 0x4E10,0x4F10,0x5010,0x5110,0x5210,0x5310,0x5410,0x5510, - 0x5610,0x5710,0x5810,0x5910,0x5A10,0x5B10,0x5C10,0x5D10, - 0x5E10,0x5F10,0x6010,0x6110,0x6210,0x6310,0x6410,0x6510, - 0x00C0,0x0140,0x0240,0x0340,0x0440,0x0540,0x0640,0x0740, - 0x0840,0x0940,0x0A40,0x0B40,0x0C40,0x0D40,0x0E40,0x0F40, - 0x1040,0x1140,0x1240,0x1340,0x1440,0x1540,0x1640,0x1740, - 0x1840,0x1940,0x1A40,0x1B40,0x1C40,0x1D40,0x1E40,0x1F40, - 0x2040,0x2140,0x2240,0x2340,0x2440,0x2540,0x2640,0x2740, - 0x2840,0x2940,0x2A40,0x2B40,0x2C40,0x2D40,0x2E40,0x2F40, - 0x3040,0x3140,0x3240,0x3340,0x3440,0x3540,0x3640,0x3740, - 0x3840,0x3940,0x3A40,0x3B40,0x3C40,0x3D40,0x3E40,0x3F40, - 0x4040,0x4140,0x4240,0x4340,0x4440,0x4540,0x4640,0x4740, - 0x4840,0x4940,0x4A40,0x4B40,0x4C40,0x4D40,0x4E40,0x4F40, - 0x5040,0x5140,0x5240,0x5340,0x5440,0x5540,0x5640,0x5740, - 0x5840,0x5940,0x5A40,0x5B40,0x5C40,0x5D40,0x5E40,0x5F40, - 0x6040,0x6140,0x6240,0x6340,0x6440,0x6540,0x6640,0x6740, - 0x6840,0x6940,0x6A40,0x6B40,0x6C40,0x6D40,0x6E40,0x6F40, - 0x7040,0x7140,0x7240,0x7340,0x7440,0x7540,0x7640,0x7740, - 0x7840,0x7940,0x7A40,0x7B40,0x7C40,0x7D40,0x7E40,0x7F40, - 0x8040,0x8140,0x8240,0x8340,0x8440,0x8540,0x8640,0x8740, - 0x8840,0x8940,0x8A40,0x8B40,0x8C40,0x8D40,0x8E40,0x8F40, - 0x9040,0x9140,0x9240,0x9340,0x9440,0x9540,0x9640,0x9740, - 0x9840,0x9940,0x9A40,0x9B40,0x9C40,0x9D40,0x9E40,0x9F40, - 0xA040,0xA140,0xA240,0xA340,0xA440,0xA540,0xA640,0xA740, - 0xA840,0xA940,0xAA40,0xAB40,0xAC40,0xAD40,0xAE40,0xAF40, - 0xB040,0xB140,0xB240,0xB340,0xB440,0xB540,0xB640,0xB740, - 0xB840,0xB940,0xBA40,0xBB40,0xBC40,0xBD40,0xBE40,0xBF40, - 0xC040,0xC140,0xC240,0xC340,0xC440,0xC540,0xC640,0xC740, - 0xC840,0xC940,0xCA40,0xCB40,0xCC40,0xCD40,0xCE40,0xCF40, - 0xD040,0xD140,0xD240,0xD340,0xD440,0xD540,0xD640,0xD740, - 0xD840,0xD940,0xDA40,0xDB40,0xDC40,0xDD40,0xDE40,0xDF40, - 0xE040,0xE140,0xE240,0xE340,0xE440,0xE540,0xE640,0xE740, - 0xE840,0xE940,0xEA40,0xEB40,0xEC40,0xED40,0xEE40,0xEF40, - 0xF040,0xF140,0xF240,0xF340,0xF440,0xF540,0xF640,0xF740, - 0xF840,0xF940,0xFA40,0xFB40,0xFC40,0xFD40,0xFE40,0xFF40, - 0xA050,0xA150,0xA250,0xA350,0xA450,0xA550,0xA650,0xA750, - 0xA850,0xA950,0xAA50,0xAB50,0xAC50,0xAD50,0xAE50,0xAF50, - 0xB050,0xB150,0xB250,0xB350,0xB450,0xB550,0xB650,0xB750, - 0xB850,0xB950,0xBA50,0xBB50,0xBC50,0xBD50,0xBE50,0xBF50, - 0xC050,0xC150,0xC250,0xC350,0xC450,0xC550,0xC650,0xC750, - 0xC850,0xC950,0xCA50,0xCB50,0xCC50,0xCD50,0xCE50,0xCF50, - 0xD050,0xD150,0xD250,0xD350,0xD450,0xD550,0xD650,0xD750, - 0xD850,0xD950,0xDA50,0xDB50,0xDC50,0xDD50,0xDE50,0xDF50, - 0xE050,0xE150,0xE250,0xE350,0xE450,0xE550,0xE650,0xE750, - 0xE850,0xE950,0xEA50,0xEB50,0xEC50,0xED50,0xEE50,0xEF50, - 0xF050,0xF150,0xF250,0xF350,0xF450,0xF550,0xF650,0xF750, - 0xF850,0xF950,0xFA50,0xFB50,0xFC50,0xFD50,0xFE50,0xFF50, - 0x00D0,0x0150,0x0250,0x0350,0x0450,0x0550,0x0650,0x0750, - 0x0850,0x0950,0x0A50,0x0B50,0x0C50,0x0D50,0x0E50,0x0F50, - 0x1050,0x1150,0x1250,0x1350,0x1450,0x1550,0x1650,0x1750, - 0x1850,0x1950,0x1A50,0x1B50,0x1C50,0x1D50,0x1E50,0x1F50, - 0x2050,0x2150,0x2250,0x2350,0x2450,0x2550,0x2650,0x2750, - 0x2850,0x2950,0x2A50,0x2B50,0x2C50,0x2D50,0x2E50,0x2F50, - 0x3050,0x3150,0x3250,0x3350,0x3450,0x3550,0x3650,0x3750, - 0x3850,0x3950,0x3A50,0x3B50,0x3C50,0x3D50,0x3E50,0x3F50, - 0x4050,0x4150,0x4250,0x4350,0x4450,0x4550,0x4650,0x4750, - 0x4850,0x4950,0x4A50,0x4B50,0x4C50,0x4D50,0x4E50,0x4F50, - 0x5050,0x5150,0x5250,0x5350,0x5450,0x5550,0x5650,0x5750, - 0x5850,0x5950,0x5A50,0x5B50,0x5C50,0x5D50,0x5E50,0x5F50, - 0x6050,0x6150,0x6250,0x6350,0x6450,0x6550,0x6650,0x6750, - 0x6850,0x6950,0x6A50,0x6B50,0x6C50,0x6D50,0x6E50,0x6F50, - 0x7050,0x7150,0x7250,0x7350,0x7450,0x7550,0x7650,0x7750, - 0x7850,0x7950,0x7A50,0x7B50,0x7C50,0x7D50,0x7E50,0x7F50, - 0x8050,0x8150,0x8250,0x8350,0x8450,0x8550,0x8650,0x8750, - 0x8850,0x8950,0x8A50,0x8B50,0x8C50,0x8D50,0x8E50,0x8F50, - 0x9050,0x9150,0x9250,0x9350,0x9450,0x9550,0x9650,0x9750, - 0x9850,0x9950,0x9A50,0x9B50,0x9C50,0x9D50,0x9E50,0x9F50, - 0xFA40,0xFB40,0xFC40,0xFD40,0xFE40,0xFF40,0x00C0,0x0140, - 0x0240,0x0340,0x0440,0x0540,0x0640,0x0740,0x0840,0x0940, - 0x0A40,0x0B40,0x0C40,0x0D40,0x0E40,0x0F40,0x1040,0x1140, - 0x1240,0x1340,0x1440,0x1540,0x1640,0x1740,0x1840,0x1940, - 0x1A40,0x1B40,0x1C40,0x1D40,0x1E40,0x1F40,0x2040,0x2140, - 0x2240,0x2340,0x2440,0x2540,0x2640,0x2740,0x2840,0x2940, - 0x2A40,0x2B40,0x2C40,0x2D40,0x2E40,0x2F40,0x3040,0x3140, - 0x3240,0x3340,0x3440,0x3540,0x3640,0x3740,0x3840,0x3940, - 0x3A40,0x3B40,0x3C40,0x3D40,0x3E40,0x3F40,0x4040,0x4140, - 0x4240,0x4340,0x4440,0x4540,0x4640,0x4740,0x4840,0x4940, - 0x4A40,0x4B40,0x4C40,0x4D40,0x4E40,0x4F40,0x5040,0x5140, - 0x5240,0x5340,0x5440,0x5540,0x5640,0x5740,0x5840,0x5940, - 0x5A40,0x5B40,0x5C40,0x5D40,0x5E40,0x5F40,0x6040,0x6140, - 0x6240,0x6340,0x6440,0x6540,0x6640,0x6740,0x6840,0x6940, - 0x6A40,0x6B40,0x6C40,0x6D40,0x6E40,0x6F40,0x7040,0x7140, - 0x7240,0x7340,0x7440,0x7540,0x7640,0x7740,0x7840,0x7940, - 0x7A40,0x7B40,0x7C40,0x7D40,0x7E40,0x7F40,0x8040,0x8140, - 0x8240,0x8340,0x8440,0x8540,0x8640,0x8740,0x8840,0x8940, - 0x8A40,0x8B40,0x8C40,0x8D40,0x8E40,0x8F40,0x9040,0x9140, - 0x9240,0x9340,0x9440,0x9540,0x9640,0x9740,0x9840,0x9940, - 0x9A40,0x9B40,0x9C40,0x9D40,0x9E40,0x9F40,0xA040,0xA140, - 0xA240,0xA340,0xA440,0xA540,0xA640,0xA740,0xA840,0xA940, - 0xAA40,0xAB40,0xAC40,0xAD40,0xAE40,0xAF40,0xB040,0xB140, - 0xB240,0xB340,0xB440,0xB540,0xB640,0xB740,0xB840,0xB940, - 0xBA40,0xBB40,0xBC40,0xBD40,0xBE40,0xBF40,0xC040,0xC140, - 0xC240,0xC340,0xC440,0xC540,0xC640,0xC740,0xC840,0xC940, - 0xCA40,0xCB40,0xCC40,0xCD40,0xCE40,0xCF40,0xD040,0xD140, - 0xD240,0xD340,0xD440,0xD540,0xD640,0xD740,0xD840,0xD940, - 0xDA40,0xDB40,0xDC40,0xDD40,0xDE40,0xDF40,0xE040,0xE140, - 0xE240,0xE340,0xE440,0xE540,0xE640,0xE740,0xE840,0xE940, - 0xEA40,0xEB40,0xEC40,0xED40,0xEE40,0xEF40,0xF040,0xF140, - 0xF240,0xF340,0xF440,0xF540,0xF640,0xF740,0xF840,0xF940, - 0x9A50,0x9B50,0x9C50,0x9D50,0x9E50,0x9F50,0xA050,0xA150, - 0xA250,0xA350,0xA450,0xA550,0xA650,0xA750,0xA850,0xA950, - 0xAA50,0xAB50,0xAC50,0xAD50,0xAE50,0xAF50,0xB050,0xB150, - 0xB250,0xB350,0xB450,0xB550,0xB650,0xB750,0xB850,0xB950, - 0xBA50,0xBB50,0xBC50,0xBD50,0xBE50,0xBF50,0xC050,0xC150, - 0xC250,0xC350,0xC450,0xC550,0xC650,0xC750,0xC850,0xC950, - 0xCA50,0xCB50,0xCC50,0xCD50,0xCE50,0xCF50,0xD050,0xD150, - 0xD250,0xD350,0xD450,0xD550,0xD650,0xD750,0xD850,0xD950, - 0xDA50,0xDB50,0xDC50,0xDD50,0xDE50,0xDF50,0xE050,0xE150, - 0xE250,0xE350,0xE450,0xE550,0xE650,0xE750,0xE850,0xE950, - 0xEA50,0xEB50,0xEC50,0xED50,0xEE50,0xEF50,0xF050,0xF150, - 0xF250,0xF350,0xF450,0xF550,0xF650,0xF750,0xF850,0xF950, - 0xFA50,0xFB50,0xFC50,0xFD50,0xFE50,0xFF50,0x00D0,0x0150, - 0x0250,0x0350,0x0450,0x0550,0x0650,0x0750,0x0850,0x0950, - 0x0A50,0x0B50,0x0C50,0x0D50,0x0E50,0x0F50,0x1050,0x1150, - 0x1250,0x1350,0x1450,0x1550,0x1650,0x1750,0x1850,0x1950, - 0x1A50,0x1B50,0x1C50,0x1D50,0x1E50,0x1F50,0x2050,0x2150, - 0x2250,0x2350,0x2450,0x2550,0x2650,0x2750,0x2850,0x2950, - 0x2A50,0x2B50,0x2C50,0x2D50,0x2E50,0x2F50,0x3050,0x3150, - 0x3250,0x3350,0x3450,0x3550,0x3650,0x3750,0x3850,0x3950, - 0x3A50,0x3B50,0x3C50,0x3D50,0x3E50,0x3F50,0x4050,0x4150, - 0x4250,0x4350,0x4450,0x4550,0x4650,0x4750,0x4850,0x4950, - 0x4A50,0x4B50,0x4C50,0x4D50,0x4E50,0x4F50,0x5050,0x5150, - 0x5250,0x5350,0x5450,0x5550,0x5650,0x5750,0x5850,0x5950, - 0x5A50,0x5B50,0x5C50,0x5D50,0x5E50,0x5F50,0x6050,0x6150, - 0x6250,0x6350,0x6450,0x6550,0x6650,0x6750,0x6850,0x6950, - 0x6A50,0x6B50,0x6C50,0x6D50,0x6E50,0x6F50,0x7050,0x7150, - 0x7250,0x7350,0x7450,0x7550,0x7650,0x7750,0x7850,0x7950, - 0x7A50,0x7B50,0x7C50,0x7D50,0x7E50,0x7F50,0x8050,0x8150, - 0x8250,0x8350,0x8450,0x8550,0x8650,0x8750,0x8850,0x8950, - 0x8A50,0x8B50,0x8C50,0x8D50,0x8E50,0x8F50,0x9050,0x9150, - 0x9250,0x9350,0x9450,0x9550,0x9650,0x9750,0x9850,0x9950, -}; - -u8 ZeroTable[256] = { - 0x80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0 -}; - -#define GBSAVE_GAME_VERSION_1 1 -#define GBSAVE_GAME_VERSION_2 2 -#define GBSAVE_GAME_VERSION_3 3 -#define GBSAVE_GAME_VERSION_4 4 -#define GBSAVE_GAME_VERSION_5 5 -#define GBSAVE_GAME_VERSION_6 6 -#define GBSAVE_GAME_VERSION_7 7 -#define GBSAVE_GAME_VERSION_8 8 -#define GBSAVE_GAME_VERSION_9 9 -#define GBSAVE_GAME_VERSION_10 10 -#define GBSAVE_GAME_VERSION_11 11 -#define GBSAVE_GAME_VERSION_12 12 -#define GBSAVE_GAME_VERSION GBSAVE_GAME_VERSION_12 - -int inline gbGetValue(int min,int max,int v) -{ - return (int)(min+(float)(max-min)*(2.0*(v/31.0)-(v/31.0)*(v/31.0))); -} - -void gbGenFilter() -{ - for (int r=0;r<32;r++) { - for (int g=0;g<32;g++) { - for (int b=0;b<32;b++) { - int nr=gbGetValue(gbGetValue(4,14,g), - gbGetValue(24,29,g),r)-4; - int ng=gbGetValue(gbGetValue(4+gbGetValue(0,5,r), - 14+gbGetValue(0,3,r),b), - gbGetValue(24+gbGetValue(0,3,r), - 29+gbGetValue(0,1,r),b),g)-4; - int nb=gbGetValue(gbGetValue(4+gbGetValue(0,5,r), - 14+gbGetValue(0,3,r),g), - gbGetValue(24+gbGetValue(0,3,r), - 29+gbGetValue(0,1,r),g),b)-4; - gbColorFilter[(b<<10)|(g<<5)|r]=(nb<<10)|(ng<<5)|nr; - } - } - } -} - -bool gbIsGameboyRom(char * file) -{ - if(strlen(file) > 4) { - char * p = strrchr(file,'.'); - - if(p != NULL) { - if(_stricmp(p, ".gb") == 0) - return true; - if(_stricmp(p, ".gbc") == 0) - return true; - if(_stricmp(p, ".cgb") == 0) - return true; - if(_stricmp(p, ".sgb") == 0) - return true; - } - } - - return false; -} - -void gbCopyMemory(u16 d, u16 s, int count) -{ - while(count) { - gbMemoryMap[d>>12][d & 0x0fff] = gbMemoryMap[s>>12][s & 0x0fff]; - s++; - d++; - count--; - } -} - -void gbDoHdma() -{ - - gbCopyMemory((gbHdmaDestination & 0x1ff0) | 0x8000, - gbHdmaSource & 0xfff0, - 0x10); - - gbHdmaDestination += 0x10; - if (gbHdmaDestination == 0xa000) - gbHdmaDestination = 0x8000; - - gbHdmaSource += 0x10; - if (gbHdmaSource == 0x8000) - gbHdmaSource = 0xa000; - - register_HDMA2 = gbHdmaSource & 0xff; - register_HDMA1 = gbHdmaSource>>8; - - register_HDMA4 = gbHdmaDestination & 0xff; - register_HDMA3 = gbHdmaDestination>>8; - - gbHdmaBytes -= 0x10; - gbMemory[0xff55] = --register_HDMA5; - if(register_HDMA5 == 0xff) - gbHdmaOn = 0; - -// We need to add the dmaClockticks for HDMA ! - if(gbSpeed) - gbDmaTicks = 17; - else - gbDmaTicks = 9; - - if (IFF & 0x80) - gbDmaTicks++; - -} - -// fix for Harley and Lego Racers -void gbCompareLYToLYC() -{ - if(register_LCDC & 0x80) { - if(register_LY == register_LYC) { - - // mark that we have a match - register_STAT |= 4; - - // check if we need an interrupt - if (register_STAT & 0x40) - { - // send LCD interrupt only if no interrupt 48h signal... - if (!gbInt48Signal) - { - register_IF |=2; - } - gbInt48Signal |= 8; - } - } - else // no match - { - register_STAT &= 0xfb; - gbInt48Signal &=~8; - } - } -} - -void gbWriteMemory(register u16 address, register u8 value) -{ - - if(address < 0x8000) { -#ifndef FINAL_VERSION - if(memorydebug && (address>0x3fff || address < 0x2000)) { - log("Memory register write %04x=%02x PC=%04x\n", - address, - value, - PC.W); - } - -#endif - if(mapper) - (*mapper)(address, value); - return; - - } - - if(address < 0xa000) { - // No access to Vram during mode 3 - // (used to emulate the gfx differences between GB & GBC-GBA/SP in Stunt Racer) - if ((gbLcdModeDelayed !=3) || - // This part is used to emulate a small difference between hardwares - // (check 8-in-1's arrow on GBA/GBC to verify it) - ((register_LY == 0) && ((gbHardware & 0xa) && (gbScreenOn==false) && - (register_LCDC & 0x80)) && - (gbLcdLYIncrementTicksDelayed ==(GBLY_INCREMENT_CLOCK_TICKS-GBLCD_MODE_2_CLOCK_TICKS)))) - gbMemoryMap[address>>12][address&0x0fff] = value; - return; - } - - // Used for the mirroring of 0xC000 in 0xE000 - if ((address >= 0xe000) && (address < 0xfe00)) - address &= ~0x2000; - - if(address < 0xc000) { -#ifndef FINAL_VERSION - if(memorydebug) { - log("Memory register write %04x=%02x PC=%04x\n", - address, - value, - PC.W); - } -#endif - - // Is that a correct fix ??? (it used to be 'if (mapper)')... - if(mapperRAM) - (*mapperRAM)(address, value); - return; - } - - - if(address < 0xfe00) { - gbMemoryMap[address>>12][address & 0x0fff] = value; - return; - } - - // OAM not accessible during mode 2 & 3. - if(address < 0xfea0) - { - if (((gbHardware & 0xa) && ((gbLcdMode | gbLcdModeDelayed) &2)) || - ((gbHardware & 5) && (((gbLcdModeDelayed == 2) && - (gbLcdTicksDelayed<=GBLCD_MODE_2_CLOCK_TICKS)) || - (gbLcdModeDelayed == 3)))) - return; - else - { - gbMemory[address] = value; - return; - } - } - - - - if((address > 0xfea0) && (address < 0xff00)){ // GBC allows reading/writing to that area - gbMemory[address] = value; - return; - } - - switch(address & 0x00ff) { - - case 0x00: { - gbMemory[0xff00] = ((gbMemory[0xff00] & 0xcf) | - (value & 0x30) | 0xc0); - if(gbSgbMode) { - gbSgbDoBitTransfer(value); - } - return; - } - - case 0x01: { - gbMemory[0xff01] = value; - return; - } - - // serial control - case 0x02: { - gbSerialOn = (value & 0x80); - gbMemory[0xff02] = value; - if(gbSerialOn) { - gbSerialTicks = GBSERIAL_CLOCK_TICKS; -#ifdef OLD_GB_LINK - if(linkConnected) { - if(value & 1) { - linkSendByte(0x100|gbMemory[0xFF01]); - Sleep(5); - } - } -#endif - } - - gbSerialBits = 0; - return; - } - - case 0x04: { - // DIV register resets on any write - // (not totally perfect, but better than nothing) - gbMemory[0xff04] = register_DIV = 0; - gbDivTicks = GBDIV_CLOCK_TICKS; - // Another weird timer 'bug' : - // Writing to DIV register resets the internal timer, - // and can also increase TIMA/trigger an interrupt - // in some cases... - if (gbTimerOn && !(gbInternalTimer & (gbTimerClockTicks>>1))) - { - gbMemory[0xff05] = ++register_TIMA; - if(register_TIMA == 0) { - // timer overflow! - - // reload timer modulo - gbMemory[0xff05] = register_TIMA = register_TMA; - - // flag interrupt - gbMemory[0xff0f] = register_IF |= 4; - } - } - gbInternalTimer = 0xff; - return; - } - case 0x05: - gbMemory[0xff05] = register_TIMA = value; - return; - - case 0x06: - gbMemory[0xff06] = register_TMA = value; - return; - - // TIMER control - case 0x07: { - - gbTimerModeChange = (((value & 3) != (register_TAC&3)) && (value & register_TAC & 4)) ? true : false; - gbTimerOnChange = (((value ^ register_TAC) & 4) == 4) ? true : false; - - gbTimerOn = (value & 4) ? true : false; - - if (gbTimerOnChange || gbTimerModeChange) - { - gbTimerMode = value & 3; - - switch(gbTimerMode) { - case 0: - gbTimerClockTicks = GBTIMER_MODE_0_CLOCK_TICKS; - break; - case 1: - gbTimerClockTicks = GBTIMER_MODE_1_CLOCK_TICKS; - break; - case 2: - gbTimerClockTicks = GBTIMER_MODE_2_CLOCK_TICKS; - break; - case 3: - gbTimerClockTicks = GBTIMER_MODE_3_CLOCK_TICKS; - break; - } - } - - - // This looks weird, but this emulates a bug in which register_TIMA - // is increased when writing to register_TAC - // (This fixes Korodice's long-delay between menus bug). - - if (gbTimerOnChange || gbTimerModeChange) - { - bool temp = false; - - if ((gbTimerOn && !gbTimerModeChange) && (gbTimerMode & 2) && - !(gbInternalTimer & 0x80) && (gbInternalTimer & (gbTimerClockTicks>>1)) && - !(gbInternalTimer & (gbTimerClockTicks>>5))) - temp = true; - else if ((!gbTimerOn && !gbTimerModeChange && gbTimerOnChange ) && ((gbTimerTicks-1) < (gbTimerClockTicks>>1))) - temp = true; - else if (gbTimerOn && gbTimerModeChange && !gbTimerOnChange) - { - switch(gbTimerMode & 3) - { - case 0x00: - temp = false; - break; - case 0x01: - if (((gbInternalTimer & 0x82) == 2) && (gbTimerTicks>(clockTicks+1))) - temp = true; - break; - case 0x02: - if (((gbInternalTimer & 0x88) == 0x8) && (gbTimerTicks>(clockTicks+1))) - temp = true; - break; - case 0x03: - if (((gbInternalTimer & 0xA0) == 0x20) && (gbTimerTicks>(clockTicks+1))) - temp = true; - break; - } - } - - if (temp) - { - gbMemory[0xff05] = ++register_TIMA; - if((register_TIMA & 0xff) == 0) { - // timer overflow! - - // reload timer modulo - gbMemory[0xff05] = register_TIMA = register_TMA; - - // flag interrupt - gbMemory[0xff0f] = register_IF |= 4; - } - } - } - gbMemory[0xff07] = register_TAC = value; - return; - } - - case 0x0f: { - gbMemory[0xff0f] = register_IF = value; - //gbMemory[0xff0f] = register_IE |= value; - return; - } - - case 0x10: - case 0x11: - case 0x12: - case 0x13: - case 0x14: - case 0x16: - case 0x17: - case 0x18: - case 0x19: - case 0x1a: - case 0x1b: - case 0x1c: - case 0x1d: - case 0x1e: - case 0x20: - case 0x21: - case 0x22: - case 0x23: - case 0x24: - case 0x25: { - if (gbMemory[NR52] & 0x80) { - SOUND_EVENT(address,value); - return; - } - } - - case 0x26: { - SOUND_EVENT(address,value); - return; - } - - case 0x30: - case 0x31: - case 0x32: - case 0x33: - case 0x34: - case 0x35: - case 0x36: - case 0x37: - case 0x38: - case 0x39: - case 0x3a: - case 0x3b: - case 0x3c: - case 0x3d: - case 0x3e: - case 0x3f: { - SOUND_EVENT(address,value); - //gbMemory[address] = value; - return; - } - - case 0x40: { - int lcdChange = (register_LCDC & 0x80) ^ (value & 0x80); - - // don't draw the window if it was not enabled and not being drawn before - if(!(register_LCDC & 0x20) && (value & 0x20) && gbWindowLine == -1 && - register_LY > register_WY) - gbWindowLine = 146; - // 007 fix : don't draw the first window's 1st line if it's enable 'too late' - // (ie. if register_LY == register_WY when enabling it) - // and move it to the next line - else if (!(register_LCDC & 0x20) && (value & 0x20) && (register_LY == register_WY)) - gbWindowLine = -2; - - - gbMemory[0xff40] = register_LCDC = value; - - - if(lcdChange) { - if((value & 0x80) && (!register_LCDCBusy)) { - - // if (!gbWhiteScreen && !gbSgbMask) - - // systemDrawScreen(); - - - - gbRegisterLYLCDCOffOn = (register_LY + 144) % 154; - - gbLcdTicks = GBLCD_MODE_2_CLOCK_TICKS - (gbSpeed ? 2 : 1); - gbLcdTicksDelayed = GBLCD_MODE_2_CLOCK_TICKS - (gbSpeed ? 1 : 0); - gbLcdLYIncrementTicks = GBLY_INCREMENT_CLOCK_TICKS - (gbSpeed ? 2 : 1); - gbLcdLYIncrementTicksDelayed = GBLY_INCREMENT_CLOCK_TICKS - (gbSpeed ? 1 : 0); - gbLcdMode = 2; - gbLcdModeDelayed = 2; - gbMemory[0xff41] = register_STAT = (register_STAT & 0xfc) | 2; - gbMemory[0xff44] = register_LY = 0x00; - gbInt48Signal = 0; - gbLYChangeHappened = false; - gbLCDChangeHappened = false; - gbWindowLine = 146; - oldRegister_WY = 146; - - // Fix for Namco Gallery Vol.2 - // (along with updating register_LCDC at the start of 'case 0x40') : - if(register_STAT & 0x20) - { - // send LCD interrupt only if no interrupt 48h signal... - if (!gbInt48Signal) - { - gbMemory[0xff0f] = register_IF |= 2; - } - gbInt48Signal |= 4; - } - gbCompareLYToLYC(); - - } else { - - register_LCDCBusy = clockTicks+6; - - //used to update the screen with white lines when it's off. - //(it looks strange, but it's pretty accurate) - - gbWhiteScreen = 0; - - gbScreenTicks = ((150-register_LY)*GBLY_INCREMENT_CLOCK_TICKS + - (49<<(gbSpeed ? 1 : 0))); - - // disable the screen rendering - gbScreenOn = false; - gbLcdTicks = 0; - gbLcdMode = 0; - gbLcdModeDelayed = 0; - gbMemory[0xff41] = register_STAT &= 0xfc; - gbInt48Signal = 0; - } - } - return; - } - - // STAT - case 0x41: { - //register_STAT = (register_STAT & 0x87) | - // (value & 0x7c); - gbMemory[0xff41] = register_STAT = (value & 0xf8) | (register_STAT & 0x07); // fix ? - // GB bug from Devrs FAQ - // proper fix - gbInt48Signal &= ((register_STAT>>3) & 0xF); - - if((register_LCDC & 0x80)) { - if ((register_STAT & 0x08) && (gbLcdMode == 0)) - { - if (!gbInt48Signal) - { - gbMemory[0xff0f] = register_IF |=2; - } - gbInt48Signal |= 1; - } - if ((register_STAT & 0x10) && (gbLcdMode == 1)) - { - if (!gbInt48Signal) - { - gbMemory[0xff0f] = register_IF |=2; - } - gbInt48Signal |= 2; - } - if ((register_STAT & 0x20) && (gbLcdMode == 2)) - { - if (!gbInt48Signal) - { - gbMemory[0xff0f] = register_IF |=2; - } - gbInt48Signal |= 4; - } - gbCompareLYToLYC(); - - gbMemory[0xff0f] = register_IF; - gbMemory[0xff41] = register_STAT; - - } - return; - } - - // SCY - case 0x42: { - int temp = -1; - - if ((gbLcdMode == 3) || (gbLcdModeDelayed == 3)) - temp = ((GBLY_INCREMENT_CLOCK_TICKS-GBLCD_MODE_2_CLOCK_TICKS) - - gbLcdLYIncrementTicks); - - if (temp >=0) - { - for (int i=temp<<(gbSpeed ? 1 : 2);i<300;i++) - if (temp < 300) - gbSCYLine[i] = value; - } - - else - memset(gbSCYLine, value, sizeof(gbSCYLine)); - - gbMemory[0xff42] = register_SCY = value; - return; - } - - // SCX - case 0x43: { - int temp = -1; - - if (gbLcdModeDelayed == 3) - temp = ((GBLY_INCREMENT_CLOCK_TICKS-GBLCD_MODE_2_CLOCK_TICKS) - - gbLcdLYIncrementTicksDelayed); - - if (temp >=0) - { - for (int i=temp<<(gbSpeed ? 1 : 2);i<300;i++) - if (temp < 300) - gbSCXLine[i] = value; - } - - else - memset(gbSCXLine, value, sizeof(gbSCXLine)); - - gbMemory[0xff43] = register_SCX = value; - return; - } - - // LY - case 0x44: { - // read only - return; - } - - // LYC - case 0x45: { - if (register_LYC != value) - { - gbMemory[0xff45] = register_LYC = value; - if(register_LCDC & 0x80) { - gbCompareLYToLYC(); - } - } - return; - } - - // DMA! - case 0x46: { - int source = value * 0x0100; - - gbCopyMemory(0xfe00, - source, - 0xa0); - gbMemory[0xff46] = register_DMA = value; - return; - } - - // BGP - case 0x47: { - - int temp = -1; - - gbMemory[0xff47] = value; - - if (gbLcdModeDelayed == 3) - temp = ((GBLY_INCREMENT_CLOCK_TICKS-GBLCD_MODE_2_CLOCK_TICKS) - - gbLcdLYIncrementTicksDelayed); - - if (temp >=0) - { - for (int i=temp<<(gbSpeed ? 1 : 2);i<300;i++) - if (temp < 300) - gbBgpLine[i] = value; - } - else - memset(gbBgpLine,value,sizeof(gbBgpLine)); - - gbBgp[0] = value & 0x03; - gbBgp[1] = (value & 0x0c)>>2; - gbBgp[2] = (value & 0x30)>>4; - gbBgp[3] = (value & 0xc0)>>6; - break; - } - - // OBP0 - case 0x48: { - int temp = -1; - - gbMemory[0xff48] = value; - - if (gbLcdModeDelayed == 3) - temp = ((GBLY_INCREMENT_CLOCK_TICKS-GBLCD_MODE_2_CLOCK_TICKS) - - gbLcdLYIncrementTicksDelayed); - - if (temp >=0) - { - for (int i=temp<<(gbSpeed ? 1 : 2);i<300;i++) - if (temp < 300) - gbObp0Line[i] = value; - } - else - memset(gbObp0Line,value,sizeof(gbObp0Line)); - - gbObp0[0] = value & 0x03; - gbObp0[1] = (value & 0x0c)>>2; - gbObp0[2] = (value & 0x30)>>4; - gbObp0[3] = (value & 0xc0)>>6; - break; - } - - // OBP1 - case 0x49: { - int temp = -1; - - gbMemory[0xff49] = value; - - if (gbLcdModeDelayed == 3) - temp = ((GBLY_INCREMENT_CLOCK_TICKS-GBLCD_MODE_2_CLOCK_TICKS) - - gbLcdLYIncrementTicksDelayed); - - if (temp >=0) - { - for (int i=temp<<(gbSpeed ? 1 : 2);i<300;i++) - if (temp < 300) - gbObp1Line[i] = value; - } - else - memset(gbObp1Line,value,sizeof(gbObp1Line)); - - gbObp1[0] = value & 0x03; - gbObp1[1] = (value & 0x0c)>>2; - gbObp1[2] = (value & 0x30)>>4; - gbObp1[3] = (value & 0xc0)>>6; - break; - } - - // WY - case 0x4a: - gbMemory[0xff4a] = register_WY = value; - if ((register_LY <= register_WY) && ((gbWindowLine < 0) || (gbWindowLine>=144))) - { - gbWindowLine = -1; - oldRegister_WY = register_WY; - } - return; - - // WX - case 0x4b: - gbMemory[0xff4b] = register_WX = value; - return; - - // KEY1 - case 0x4d: { - if(gbCgbMode) { - gbMemory[0xff4d] = (gbMemory[0xff4d] & 0x80) | (value & 1) | 0x7e; - return; - } - } - break; - - // VBK - case 0x4f: { - if(gbCgbMode) { - value = value & 1; - if(value == gbVramBank) - return; - - int vramAddress = value * 0x2000; - gbMemoryMap[0x08] = &gbVram[vramAddress]; - gbMemoryMap[0x09] = &gbVram[vramAddress + 0x1000]; - - gbVramBank = value; - register_VBK = value; - } - return; - } - break; - - // BOOTROM disable register (also gbCgbMode enabler if value & 0x10 ?) - case 0x50 : - { - if (useBios && inBios && !skipBios && (value & 1)) - { - gbMemoryMap[0x00] = &gbRom[0x0000]; - memcpy ((u8 *)(gbRom+0x100), (u8 *)(gbMemory + 0x100), 0xF00); - inBios = false; - } - } - - // HDMA1 - case 0x51: { - if(gbCgbMode) { - if(value > 0x7f && value < 0xa0) - value = 0; - - gbHdmaSource = (value << 8) | (gbHdmaSource & 0xf0); - - register_HDMA1 = value; - return; - } - } - break; - - // HDMA2 - case 0x52: { - if(gbCgbMode) { - value = value & 0xf0; - - gbHdmaSource = (gbHdmaSource & 0xff00) | (value); - - register_HDMA2 = value; - return; - } - } - break; - - // HDMA3 - case 0x53: { - if(gbCgbMode) { - value = value & 0x1f; - gbHdmaDestination = (value << 8) | (gbHdmaDestination & 0xf0); - gbHdmaDestination |= 0x8000; - register_HDMA3 = value; - return; - } - } - break; - - // HDMA4 - case 0x54: { - if(gbCgbMode) { - value = value & 0xf0; - gbHdmaDestination = (gbHdmaDestination & 0x1f00) | value; - gbHdmaDestination |= 0x8000; - register_HDMA4 = value; - return; - } - } - break; - - // HDMA5 - case 0x55: { - - if(gbCgbMode) { - gbHdmaBytes = 16 + (value & 0x7f) * 16; - if(gbHdmaOn) { - if(value & 0x80) { - gbMemory[0xff55] = register_HDMA5 = (value & 0x7f); - } else { - register_HDMA5 = 0xff; - gbHdmaOn = 0; - } - } else { - if(value & 0x80) { - gbHdmaOn = 1; - gbMemory[0xff55] = register_HDMA5 = value & 0x7f; - if(gbLcdModeDelayed == 0) - gbDoHdma(); - } else { - // we need to take the time it takes to complete the transfer into - // account... according to GB DEV FAQs, the setup time is the same - // for single and double speed, but the actual transfer takes the - // same time - if(gbSpeed) - gbDmaTicks = 2+16 * ((value & 0x7f) +1); - else - gbDmaTicks = 1+8 * ((value & 0x7f)+1); - - gbCopyMemory((gbHdmaDestination & 0x1ff0) | 0x8000, - gbHdmaSource & 0xfff0, - gbHdmaBytes); - gbHdmaDestination += gbHdmaBytes; - gbHdmaSource += gbHdmaBytes; - - gbMemory[0xff51] = register_HDMA1 = 0xff;// = (gbHdmaSource >> 8) & 0xff; - gbMemory[0xff52] = register_HDMA2 = 0xff;// = gbHdmaSource & 0xf0; - gbMemory[0xff53] = register_HDMA3 = 0xff;// = ((gbHdmaDestination - 0x8000) >> 8) & 0x1f; - gbMemory[0xff54] = register_HDMA4 = 0xff;// = gbHdmaDestination & 0xf0; - gbMemory[0xff55] = register_HDMA5 = 0xff; - } - } - return; - } - } - break; - - // BCPS - case 0x68: { - if(gbCgbMode) { - int paletteIndex = (value & 0x3f) >> 1; - int paletteHiLo = (value & 0x01); - - gbMemory[0xff68] = value; - - gbMemory[0xff69] = (paletteHiLo ? - (gbPalette[paletteIndex] >> 8) : - (gbPalette[paletteIndex] & 0x00ff)); - return; - } - } - break; - - // BCPD - case 0x69: { - if(gbCgbMode) { - int v = gbMemory[0xff68]; - int paletteIndex = (v & 0x3f) >> 1; - int paletteHiLo = (v & 0x01); - - // No access to gbPalette during mode 3 (Color Panel Demo) - if (((gbLcdModeDelayed != 3) && (!((gbLcdMode == 0) && (gbLcdTicks>=(GBLCD_MODE_0_CLOCK_TICKS-gbSpritesTicks[299]-1)))) && (!gbSpeed)) || - (gbSpeed && ((gbLcdMode == 1) || (gbLcdMode == 2) || - ((gbLcdMode == 3) && (gbLcdTicks>(GBLCD_MODE_3_CLOCK_TICKS-2))) || - ((gbLcdMode == 0) && (gbLcdTicks<=(GBLCD_MODE_0_CLOCK_TICKS-gbSpritesTicks[299]-2)))))) - { - gbMemory[0xff69] = value; - gbPalette[paletteIndex] = (paletteHiLo ? - ((value << 8) | (gbPalette[paletteIndex] & 0xff)) : - ((gbPalette[paletteIndex] & 0xff00) | (value))) & 0x7fff; - } - - - if(gbMemory[0xff68] & 0x80) { - int index = ((gbMemory[0xff68] & 0x3f) + 1) & 0x3f; - - gbMemory[0xff68] = (gbMemory[0xff68] & 0x80) | index; - gbMemory[0xff69] = (index & 1 ? - (gbPalette[index>>1] >> 8) : - (gbPalette[index>>1] & 0x00ff)); - } - return; - } - } - break; - - // OCPS - case 0x6a: { - if(gbCgbMode) { - int paletteIndex = (value & 0x3f) >> 1; - int paletteHiLo = (value & 0x01); - - paletteIndex += 32; - - gbMemory[0xff6a] = value; - - gbMemory[0xff6b] = (paletteHiLo ? - (gbPalette[paletteIndex] >> 8) : - (gbPalette[paletteIndex] & 0x00ff)); - return; - } - } - break; - - // OCPD - case 0x6b: { - - if(gbCgbMode) { - int v = gbMemory[0xff6a]; - int paletteIndex = (v & 0x3f) >> 1; - int paletteHiLo = (v & 0x01); - - paletteIndex += 32; - - // No access to gbPalette during mode 3 (Color Panel Demo) - if (((gbLcdModeDelayed != 3) && (!((gbLcdMode == 0) && (gbLcdTicks>=(GBLCD_MODE_0_CLOCK_TICKS-gbSpritesTicks[299]-1)))) && (!gbSpeed)) || - (gbSpeed && ((gbLcdMode == 1) || (gbLcdMode == 2) || - ((gbLcdMode == 3) && (gbLcdTicks>(GBLCD_MODE_3_CLOCK_TICKS-2))) || - ((gbLcdMode == 0) && (gbLcdTicks<=(GBLCD_MODE_0_CLOCK_TICKS-gbSpritesTicks[299]-2)))))) - { - gbMemory[0xff6b] = value; - gbPalette[paletteIndex] = (paletteHiLo ? - ((value << 8) | (gbPalette[paletteIndex] & 0xff)) : - ((gbPalette[paletteIndex] & 0xff00) | (value))) & 0x7fff; - } - - if(gbMemory[0xff6a] & 0x80) { - int index = ((gbMemory[0xff6a] & 0x3f) + 1) & 0x3f; - - gbMemory[0xff6a] = (gbMemory[0xff6a] & 0x80) | index; - - gbMemory[0xff6b] = (index & 1 ? - (gbPalette[(index>>1) + 32] >> 8) : - (gbPalette[(index>>1) + 32] & 0x00ff)); - } - return; - } - } - break; - - case 0x6c: { - gbMemory[0xff6c] = 0xfe | value; - return; - } - - - // SVBK - case 0x70: { - if(gbCgbMode) { - value = value & 7; - - int bank = value; - if(value == 0) - bank = 1; - - if(bank == gbWramBank) - return; - - int wramAddress = bank * 0x1000; - gbMemoryMap[0x0d] = &gbWram[wramAddress]; - - gbWramBank = bank; - gbMemory[0xff70] = register_SVBK = value; - return; - } - } - - case 0x75:{ - gbMemory[0xff75] = 0x8f | value; - return; - } - - case 0xff: { - gbMemory[0xffff] = register_IE = value; - return; - } - } - - if(address < 0xff80) - { - gbMemory[address] = value; - return; - } - - gbMemory[address] = value; -} - -u8 gbReadOpcode(register u16 address) -{ - if(gbCheatMap[address]) - return gbCheatRead(address); - - if(address < 0x8000) - return gbMemoryMap[address>>12][address&0x0fff]; - - if (address < 0xa000) - { - // A lot of 'ugly' checks... But only way to emulate this particular behaviour... - if ( - ( - (gbHardware & 0xa) && - ( - (gbLcdModeDelayed != 3) || - ( - ((register_LY == 0) && (gbScreenOn == false) && (register_LCDC & 0x80)) && - (gbLcdLYIncrementTicksDelayed == (GBLY_INCREMENT_CLOCK_TICKS - GBLCD_MODE_2_CLOCK_TICKS)) - ) - ) - ) - || - ( - (gbHardware & 0x5) && - (gbLcdModeDelayed != 3) && - ( - (gbLcdMode != 3) || - ((register_LY == 0) && ((gbScreenOn == false) && (register_LCDC & 0x80)) && - (gbLcdLYIncrementTicks == (GBLY_INCREMENT_CLOCK_TICKS - GBLCD_MODE_2_CLOCK_TICKS))) - ) - ) - ) - return gbMemoryMap[address >> 12][address & 0x0fff]; - - return 0xff; - } - - // Used for the mirroring of 0xC000 in 0xE000 - if ((address >= 0xe000) && (address < 0xfe00)) - address &= ~0x2000; - - switch(address & 0xf000) { - case 0x0a: - case 0x0b: - if(mapperReadRAM) - return mapperReadRAM(address); - break; - case 0x0f: - if(address > 0xff00) { - switch(address & 0x00ff) { - case 0x02: - return (gbMemory[0xff02]); - case 0x03: - return (0xff); - case 0x04: - return register_DIV; - case 0x05: - return register_TIMA; - case 0x06: - return register_TMA; - case 0x07: - return (0xf8 | register_TAC); - case 0x08: - case 0x09: - case 0x0a: - case 0x0b: - case 0x0c: - case 0x0d: - case 0x0e: - return (0xff); - case 0x0f: - return (0xe0 | gbMemory[0xff0f]); - case 0x40: - return register_LCDC; - case 0x41: - // This is a GB/C only bug (ie. not GBA/SP). - if ((gbHardware & 7) && (gbLcdMode == 2) && (gbLcdModeDelayed == 1) && (!gbSpeed)) - return (0x80 | (gbMemory[0xff41] & 0xFC)); - else - return (0x80 | gbMemory[0xff41]); - case 0x42: - return register_SCY; - case 0x43: - return register_SCX; - case 0x44: - if (((gbHardware & 7) && ((gbLcdMode == 1) && (gbLcdTicks == 0x71))) || - (!(register_LCDC && 0x80))) - return 0; - else - return register_LY; - case 0x45: - return register_LYC; - case 0x46: - return register_DMA; - case 0x4a: - return register_WY; - case 0x4b: - return register_WX; - case 0x4c: - return 0xff; - case 0x4f: - return (0xfe | register_VBK); - case 0x51: - return register_HDMA1; - case 0x52: - return register_HDMA2; - case 0x53: - return register_HDMA3; - case 0x54: - return register_HDMA4; - case 0x55: - return register_HDMA5; - case 0x68: - case 0x6a: - if (gbCgbMode) - return (0x40 | gbMemory[address]); - else - return 0xc0; - case 0x69: - case 0x6b: - if (gbCgbMode) - { - // No access to gbPalette during mode 3 (Color Panel Demo) - if (((gbLcdModeDelayed != 3) && (!((gbLcdMode == 0) && (gbLcdTicks>=(GBLCD_MODE_0_CLOCK_TICKS-gbSpritesTicks[299]-1)))) && (!gbSpeed)) || - (gbSpeed && ((gbLcdMode == 1) || (gbLcdMode == 2) || - ((gbLcdMode == 3) && (gbLcdTicks>(GBLCD_MODE_3_CLOCK_TICKS-2))) || - ((gbLcdMode == 0) && (gbLcdTicks<=(GBLCD_MODE_0_CLOCK_TICKS-gbSpritesTicks[299]-2)))))) - return (gbMemory[address]); - else - return 0xff; - } - else - return 0xff; - case 0x70: - if (gbCgbMode) - return (0xf8 | register_SVBK); - else - return 0xff; - case 0xff: - return register_IE; - } - } - // OAM not accessible during mode 2 & 3. - if(((address >= 0xfe00) && (address<0xfea0)) && - ((gbLcdMode | gbLcdModeDelayed) &2)) - return 0xff; - break; - } - - if ((address >= 0xfea0) && (address < 0xff00)) - { - if (gbHardware & 1) - return ((((address + ((address >> 4) - 0xfea)) >> 2) & 1) ? 0x00 : 0xff ); - else if (gbHardware & 2) - return gbMemoryMap[address>>12][address & 0x0fff]; - else if (gbHardware & 4) - return ((((address + ((address >> 4) - 0xfea)) >> 2) & 1) ? 0xff : 0x00 ); - else if (gbHardware & 8) - return ((address & 0xf0) |((address & 0xf0)>>4)); - } - - return gbMemoryMap[address>>12][address & 0x0fff]; -} - -u8 gbReadMemory(register u16 address) -{ - if(gbCheatMap[address]) - return gbCheatRead(address); - - - if(address < 0x8000) - return gbMemoryMap[address>>12][address&0x0fff]; - - - if (address < 0xa000) - { - // A lot of 'ugly' checks... But only way to emulate this particular behaviour... - if ( - ( - (gbHardware & 0xa) && - ( - (gbLcdModeDelayed != 3) || - ( - ((register_LY == 0) && (gbScreenOn == false) && (register_LCDC & 0x80)) && - (gbLcdLYIncrementTicksDelayed == (GBLY_INCREMENT_CLOCK_TICKS - GBLCD_MODE_2_CLOCK_TICKS)) - ) - ) - ) - || - ( - (gbHardware & 0x5) && - (gbLcdModeDelayed != 3) && - ( - (gbLcdMode != 3) || - ((register_LY == 0) && ((gbScreenOn == false) && (register_LCDC & 0x80)) && - (gbLcdLYIncrementTicks == (GBLY_INCREMENT_CLOCK_TICKS - GBLCD_MODE_2_CLOCK_TICKS))) - ) - ) - ) - return gbMemoryMap[address >> 12][address & 0x0fff]; - - return 0xff; - } - - if ((address >= 0xe000) && (address < 0xfe00)) - address &= ~0x2000; - - if(address < 0xc000) { -#ifndef FINAL_VERSION - if(memorydebug) { - log("Memory register read %04x PC=%04x\n", - address, - PC.W); - } -#endif - - // for the 2kb ram limit (fixes crash in shawu's story - // but now its sram test fails, as the it expects 8kb and not 2kb... - // So use the 'genericflashcard' option to fix it). - if (address<=(0xa000+gbRamSizeMask)) - { - if(mapperReadRAM) - return mapperReadRAM(address); - return gbMemoryMap[address>>12][address & 0x0fff]; - } - return 0xff; - } - - if(address >= 0xff00) { - if ( address >= 0xFF10 && address <= 0xFF3F ) - return gbSoundRead( address ); - - switch(address & 0x00ff) { - case 0x00: - { - if(gbSgbMode) { - gbSgbReadingController |= 4; - gbSgbResetPacketState(); - } - - int b = gbMemory[0xff00]; - - if((b & 0x30) == 0x20) { - b &= 0xf0; - - int joy = 0; - if(gbSgbMode && gbSgbMultiplayer) { - switch(gbSgbNextController) { - case 0x0f: - joy = 0; - break; - case 0x0e: - joy = 1; - break; - case 0x0d: - joy = 2; - break; - case 0x0c: - joy = 3; - break; - default: - joy = 0; - } - } - int joystate = gbJoymask[joy]; - if(!(joystate & 128)) - b |= 0x08; - if(!(joystate & 64)) - b |= 0x04; - if(!(joystate & 32)) - b |= 0x02; - if(!(joystate & 16)) - b |= 0x01; - - gbMemory[0xff00] = b; - } else if((b & 0x30) == 0x10) { - b &= 0xf0; - - int joy = 0; - if(gbSgbMode && gbSgbMultiplayer) { - switch(gbSgbNextController) { - case 0x0f: - joy = 0; - break; - case 0x0e: - joy = 1; - break; - case 0x0d: - joy = 2; - break; - case 0x0c: - joy = 3; - break; - default: - joy = 0; - } - } - int joystate = gbJoymask[joy]; - if(!(joystate & 8)) - b |= 0x08; - if(!(joystate & 4)) - b |= 0x04; - if(!(joystate & 2)) - b |= 0x02; - if(!(joystate & 1)) - b |= 0x01; - - gbMemory[0xff00] = b; - } else { - if(gbSgbMode && gbSgbMultiplayer) { - gbMemory[0xff00] = 0xf0 | gbSgbNextController; - } else { - gbMemory[0xff00] = 0xff; - } - } - } - return gbMemory[0xff00]; - break; - case 0x01: - return gbMemory[0xff01]; - case 0x02: - return (gbMemory[0xff02]); - case 0x04: - return register_DIV; - case 0x05: - return register_TIMA; - case 0x06: - return register_TMA; - case 0x07: - return (0xf8 | register_TAC); - case 0x0f: - return (0xe0 | gbMemory[0xff0f]); - case 0x30: - case 0x31: - case 0x32: - case 0x33: - case 0x34: - case 0x35: - case 0x36: - case 0x37: - case 0x38: - case 0x39: - case 0x3A: - case 0x3B: - case 0x3C: - case 0x3D: - case 0x3E: - case 0x3F: - if ((gbMemory[NR30] & 0x80) && (gbMemory[NR34] & 0x80)) - return 0xFF; - else - return gbMemoryMap[address>>12][address & 0x0fff]; - case 0x40: - return register_LCDC; - case 0x41: - // This is a GB/C only bug (ie. not GBA/SP). - if ((gbHardware & 7) && (gbLcdMode == 2) && (gbLcdModeDelayed == 1) && (!gbSpeed)) - return (0x80 | (gbMemory[0xff41] & 0xFC)); - else - return (0x80 | gbMemory[0xff41]); - case 0x42: - return register_SCY; - case 0x43: - return register_SCX; - case 0x44: - if (((gbHardware & 7) && ((gbLcdMode == 1) && (gbLcdTicks == 0x71))) || - (!(register_LCDC && 0x80))) - return (0); - else - return register_LY; - case 0x45: - return register_LYC; - case 0x46: - return register_DMA; - case 0x4a: - return register_WY; - case 0x4b: - return register_WX; - case 0x4f: - return (0xfe | register_VBK); - case 0x51: - return register_HDMA1; - case 0x52: - return register_HDMA2; - case 0x53: - return register_HDMA3; - case 0x54: - return register_HDMA4; - case 0x55: - return register_HDMA5; - case 0x68: - case 0x6a: - if (gbCgbMode) - return (0x40 | gbMemory[address]); - else - return 0xc0; - case 0x69: - case 0x6b: - if (gbCgbMode) - { - // No access to gbPalette during mode 3 (Color Panel Demo) - if (((gbLcdModeDelayed != 3) && (!((gbLcdMode == 0) && (gbLcdTicks>=(GBLCD_MODE_0_CLOCK_TICKS-gbSpritesTicks[299]-1)))) && (!gbSpeed)) || - (gbSpeed && ((gbLcdMode == 1) || (gbLcdMode == 2) || - ((gbLcdMode == 3) && (gbLcdTicks>(GBLCD_MODE_3_CLOCK_TICKS-2))) || - ((gbLcdMode == 0) && (gbLcdTicks<=(GBLCD_MODE_0_CLOCK_TICKS-gbSpritesTicks[299]-2)))))) - return (gbMemory[address]); - else - return 0xff; - } - else - return 0xff; - case 0x70: - if (gbCgbMode) - return (0xf8 | register_SVBK); - else - return 0xff; - case 0xff: - return register_IE; - } - } - // OAM not accessible during mode 2 & 3. - if(((address >= 0xfe00) && (address<0xfea0)) && - ((((gbLcdMode | gbLcdModeDelayed) & 2) && - (!(gbSpeed && (gbHardware & 0x2) && !(gbLcdModeDelayed & 2) && (gbLcdMode == 2)))) || - (gbSpeed && (gbHardware & 0x2) && (gbLcdModeDelayed == 0) && (gbLcdTicksDelayed == (GBLCD_MODE_0_CLOCK_TICKS-gbSpritesTicks[299]))))) - return 0xff; - - if ((address >= 0xfea0) && (address < 0xff00)) - { - if (gbHardware & 1) - return ((((address + ((address >> 4) - 0xfea)) >> 2) & 1) ? 0x00 : 0xff ); - else if (gbHardware & 2) - return gbMemoryMap[address>>12][address & 0x0fff]; - else if (gbHardware & 4) - return ((((address + ((address >> 4) - 0xfea)) >> 2) & 1) ? 0xff : 0x00 ); - else if (gbHardware & 8) - return ((address & 0xf0) |((address & 0xf0)>>4)); - } - - return gbMemoryMap[address>>12][address & 0x0fff]; -} - -void gbVblank_interrupt() -{ - gbCheatWrite(false); // Emulates GS codes. - gbMemory[0xff0f] = register_IF &= 0xfe; - gbWriteMemory(--SP.W, PC.B.B1); - gbWriteMemory(--SP.W, PC.B.B0); - PC.W = 0x40; -} - -void gbLcd_interrupt() -{ - gbMemory[0xff0f] = register_IF &= 0xfd; - gbWriteMemory(--SP.W, PC.B.B1); - gbWriteMemory(--SP.W, PC.B.B0); - PC.W = 0x48; -} - -void gbTimer_interrupt() -{ - gbMemory[0xff0f] = register_IF &= 0xfb; - gbWriteMemory(--SP.W, PC.B.B1); - gbWriteMemory(--SP.W, PC.B.B0); - PC.W = 0x50; -} - -void gbSerial_interrupt() -{ - gbMemory[0xff0f] = register_IF &= 0xf7; - gbWriteMemory(--SP.W, PC.B.B1); - gbWriteMemory(--SP.W, PC.B.B0); - PC.W = 0x58; -} - -void gbJoypad_interrupt() -{ - gbMemory[0xff0f] = register_IF &= 0xef; - gbWriteMemory(--SP.W, PC.B.B1); - gbWriteMemory(--SP.W, PC.B.B0); - PC.W = 0x60; -} - -void gbSpeedSwitch() -{ - gbBlackScreen = true; - if(gbSpeed == 0) { - gbSpeed = 1; - GBLCD_MODE_0_CLOCK_TICKS = 51 * 2; - GBLCD_MODE_1_CLOCK_TICKS = 1140 * 2; - GBLCD_MODE_2_CLOCK_TICKS = 20 * 2; - GBLCD_MODE_3_CLOCK_TICKS = 43 * 2; - GBLY_INCREMENT_CLOCK_TICKS = 114 * 2; - GBDIV_CLOCK_TICKS = 64; - GBTIMER_MODE_0_CLOCK_TICKS = 256; - GBTIMER_MODE_1_CLOCK_TICKS = 4; - GBTIMER_MODE_2_CLOCK_TICKS = 16; - GBTIMER_MODE_3_CLOCK_TICKS = 64; - GBSERIAL_CLOCK_TICKS = 128 * 2; - gbLcdTicks *= 2; - gbLcdTicksDelayed *=2; - gbLcdTicksDelayed--; - gbLcdLYIncrementTicks *= 2; - gbLcdLYIncrementTicksDelayed *= 2; - gbLcdLYIncrementTicksDelayed--; - gbSerialTicks *= 2; - //SOUND_CLOCK_TICKS = soundQuality * 24 * 2; - //soundTicks *= 2; - gbLine99Ticks = 3; - } else { - gbSpeed = 0; - GBLCD_MODE_0_CLOCK_TICKS = 51; - GBLCD_MODE_1_CLOCK_TICKS = 1140; - GBLCD_MODE_2_CLOCK_TICKS = 20; - GBLCD_MODE_3_CLOCK_TICKS = 43; - GBLY_INCREMENT_CLOCK_TICKS = 114; - GBDIV_CLOCK_TICKS = 64; - GBTIMER_MODE_0_CLOCK_TICKS = 256; - GBTIMER_MODE_1_CLOCK_TICKS = 4; - GBTIMER_MODE_2_CLOCK_TICKS = 16; - GBTIMER_MODE_3_CLOCK_TICKS = 64; - GBSERIAL_CLOCK_TICKS = 128; - gbLcdTicks >>= 1; - gbLcdTicksDelayed++; - gbLcdTicksDelayed >>=1; - gbLcdLYIncrementTicks >>= 1; - gbLcdLYIncrementTicksDelayed++; - gbLcdLYIncrementTicksDelayed >>= 1; - gbSerialTicks /= 2; - //SOUND_CLOCK_TICKS = soundQuality * 24; - //soundTicks /= 2; - gbLine99Ticks = 1; - if (gbHardware & 8) - gbLine99Ticks++; - } - gbDmaTicks += (134)*GBLY_INCREMENT_CLOCK_TICKS + (37<<(gbSpeed ? 1 : 0)); -} - -bool CPUIsGBBios(const char * file) -{ - if(strlen(file) > 4) { - const char * p = strrchr(file,'.'); - - if(p != NULL) { - if(_stricmp(p, ".gb") == 0) - return true; - if(_stricmp(p, ".bin") == 0) - return true; - if(_stricmp(p, ".bios") == 0) - return true; - if(_stricmp(p, ".rom") == 0) - return true; - } - } - - return false; -} - -void gbCPUInit(const char *biosFileName, bool useBiosFile) -{ - useBios = false; - if (useBiosFile) - { - int size = 0x100; - if(utilLoad(biosFileName, - CPUIsGBBios, - bios, - size)) { - if(size == 0x100) - useBios = true; - else - systemMessage(MSG_INVALID_BIOS_FILE_SIZE, N_("Invalid BOOTROM file size")); - } - } -} - -void gbGetHardwareType() -{ - gbCgbMode = 0; - gbSgbMode = 0; - if(gbRom[0x143] & 0x80) { - if((gbEmulatorType == 0) || - gbEmulatorType == 1 || - gbEmulatorType == 4) { - gbCgbMode = 1; - } - } - - if((gbCgbMode == 0 ) && (gbRom[0x146] == 0x03)) { - if(gbEmulatorType == 0 || - gbEmulatorType == 2 || - gbEmulatorType == 5) - gbSgbMode = 1; - } - - gbHardware = 1; // GB - if (((gbCgbMode == 1) && (gbEmulatorType == 0)) || (gbEmulatorType == 1)) - gbHardware = 2; // GBC - else if (((gbSgbMode == 1) && (gbEmulatorType == 0)) || (gbEmulatorType == 2) || (gbEmulatorType == 5)) - gbHardware = 4; // SGB(2) - else if (gbEmulatorType == 4) - gbHardware = 8; // GBA - - gbGBCColorType = 0; - if (gbHardware & 8) // If GBA is selected, choose the GBA default settings. - gbGBCColorType = 2; // (0 = GBC, 1 = GBA, 2 = GBASP) -} - -void gbReset() -{ - gbGetHardwareType(); - - oldRegister_WY = 146; - gbInterruptLaunched = 0; - - if(gbCgbMode == 1) { - if (gbVram == NULL) - gbVram = (u8 *)malloc(0x4000); - if (gbWram == NULL) - gbWram = (u8 *)malloc(0x8000); - memset(gbVram,0,0x4000); - memset(gbPalette,0, 2*128); - } - else - { - if(gbVram != NULL) { - free(gbVram); - gbVram = NULL; - } - if(gbWram != NULL) { - free(gbWram); - gbWram = NULL; - } - } - - gbLYChangeHappened = false; - gbLCDChangeHappened = false; - gbBlackScreen = false; - gbInterruptWait = 0; - gbDmaTicks = 0; - clockTicks = 0; - - if(gbSpeed) { - gbSpeedSwitch(); - gbMemory[0xff4d] = 0; - } - - // clean Wram - // This kinda emulates the startup state of Wram on GB/C (not very accurate, - // but way closer to the reality than filling it with 00es or FFes). - // On GBA/GBASP, it's kinda filled with random data. - // In all cases, most of the 2nd bank is filled with 00s. - // The starting data are important for some 'buggy' games, like Buster Brothers or - // Karamuchou ha Oosawagi!. - if (gbMemory != NULL) - { - memset(gbMemory,0xff, 65536); - for (int temp = 0xC000; temp < 0xE000; temp++) - if ((temp & 0x8) ^((temp & 0x800)>>8)) - { - if ((gbHardware & 0x02) && (gbGBCColorType == 0)) - gbMemory[temp] = 0x0; - else - gbMemory[temp] = 0x0f; - } - - else - gbMemory[temp] = 0xff; - } - - // GB bios set this memory area to 0 - // Fixes Pitman (J) title screen - if (gbHardware & 0x1) { - memset(&gbMemory[0x8000], 0x0, 0x2000); - } - - // clean LineBuffer - if (gbLineBuffer != NULL) - memset(gbLineBuffer, 0, sizeof(gbLineBuffer)); - // clean Pix - if (pix != NULL) - memset(pix, 0, sizeof(pix)); - // clean Vram - if (gbVram != NULL) - memset(gbVram, 0, 0x4000); - // clean Wram 2 - // This kinda emulates the startup state of Wram on GBC (not very accurate, - // but way closer to the reality than filling it with 00es or FFes). - // On GBA/GBASP, it's kinda filled with random data. - // In all cases, most of the 2nd bank is filled with 00s. - // The starting data are important for some 'buggy' games, like Buster Brothers or - // Karamuchou ha Oosawagi! - if (gbWram != NULL) - { - for (int i = 0; i<8; i++) - if (i != 2) - memcpy ((u16 *)(gbWram+i*0x1000), (u16 *)(gbMemory+0xC000), 0x1000); - } - - memset(gbSCYLine,0,sizeof(gbSCYLine)); - memset(gbSCXLine,0,sizeof(gbSCXLine)); - memset(gbBgpLine,0xfc,sizeof(gbBgpLine)); - if (gbHardware & 5) - { - memset(gbObp0Line,0xff,sizeof(gbObp0Line)); - memset(gbObp1Line,0xff,sizeof(gbObp1Line)); - } - else - { - memset(gbObp0Line,0x0,sizeof(gbObp0Line)); - memset(gbObp1Line,0x0,sizeof(gbObp1Line)); - } - memset(gbSpritesTicks,0x0,sizeof(gbSpritesTicks)); - - SP.W = 0xfffe; - AF.W = 0x01b0; - BC.W = 0x0013; - DE.W = 0x00d8; - HL.W = 0x014d; - PC.W = 0x0100; - IFF = 0; - gbInt48Signal = 0; - - register_TIMA = 0; - register_TMA = 0; - register_TAC = 0; - gbMemory[0xff0f] = register_IF = 1; - gbMemory[0xff40] = register_LCDC = 0x91; - gbMemory[0xff47] = 0xfc; - - if (gbCgbMode) - gbMemory[0xff4d] = 0x7e; - else - gbMemory[0xff4d] = 0xff; - - if (!gbCgbMode) - gbMemory[0xff70] = gbMemory[0xff74] = 0xff; - - if (gbCgbMode) - gbMemory[0xff56] = 0x3e; - else - gbMemory[0xff56] = 0xff; - - register_SCY = 0; - register_SCX = 0; - register_LYC = 0; - register_DMA = 0xff; - register_WY = 0; - register_WX = 0; - register_VBK = 0; - register_HDMA1 = 0xff; - register_HDMA2 = 0xff; - register_HDMA3 = 0xff; - register_HDMA4 = 0xff; - register_HDMA5 = 0xff; - register_SVBK = 0; - register_IE = 0; - - if (gbCgbMode) - gbMemory[0xff02] = 0x7c; - else - gbMemory[0xff02] = 0x7e; - - gbMemory[0xff03] = 0xff; - int i; - for (i = 0x8; i<0xf; i++) - gbMemory[0xff00+i] = 0xff; - - gbMemory[0xff13] = 0xff; - gbMemory[0xff15] = 0xff; - gbMemory[0xff18] = 0xff; - gbMemory[0xff1d] = 0xff; - gbMemory[0xff1f] = 0xff; - - for (i = 0x27; i<0x30; i++) - gbMemory[0xff00+i] = 0xff; - - gbMemory[0xff4c] = 0xff; - gbMemory[0xff4e] = 0xff; - gbMemory[0xff50] = 0xff; - - for (i = 0x57; i<0x68; i++) - gbMemory[0xff00+i] = 0xff; - - for (i = 0x5d; i<0x70; i++) - gbMemory[0xff00+i] = 0xff; - - gbMemory[0xff71] = 0xff; - - for (i = 0x78; i<0x80; i++) - gbMemory[0xff00+i] = 0xff; - - if (gbHardware & 0xa) - { - - if (gbHardware & 2) - { - AF.W = 0x1180; - BC.W = 0x0000; - } - else - { - AF.W = 0x1100; - BC.W = 0x0100; // GBA/SP have B = 0x01 (which means GBC & GBA/SP bootrom are different !) - } - - gbMemory[0xff26] = 0xf1; - if (gbCgbMode) - { - - gbMemory[0xff31] = 0xff; - gbMemory[0xff33] = 0xff; - gbMemory[0xff35] = 0xff; - gbMemory[0xff37] = 0xff; - gbMemory[0xff39] = 0xff; - gbMemory[0xff3b] = 0xff; - gbMemory[0xff3d] = 0xff; - - gbMemory[0xff44] = register_LY = 0x90; - gbDivTicks = 0x19 + ((gbHardware & 2) >> 1); - gbInternalTimer = 0x58 + ((gbHardware & 2) >> 1); - gbLcdTicks = GBLCD_MODE_1_CLOCK_TICKS - - (register_LY-0x8F)*GBLY_INCREMENT_CLOCK_TICKS + 72 + ((gbHardware & 2) >> 1); - gbLcdLYIncrementTicks = 72 + ((gbHardware & 2) >> 1); - gbMemory[0xff04] = register_DIV = 0x1E; - } - else - { - gbMemory[0xff44] = register_LY = 0x94; - gbDivTicks = 0x22 + ((gbHardware & 2) >> 1); - gbInternalTimer = 0x61 + ((gbHardware & 2) >> 1); - gbLcdTicks = GBLCD_MODE_1_CLOCK_TICKS - - (register_LY-0x8F)*GBLY_INCREMENT_CLOCK_TICKS + 25 + ((gbHardware & 2) >> 1); - gbLcdLYIncrementTicks = 25 + ((gbHardware & 2) >> 1); - gbMemory[0xff04] = register_DIV = 0x26; - } - - - DE.W = 0xff56; - HL.W = 0x000d; - - register_HDMA5 = 0xff; - gbMemory[0xff68] = 0xc0; - gbMemory[0xff6a] = 0xc0; - - - gbMemory[0xff41] = register_STAT = 0x81; - gbLcdMode = 1; - } - else - { - if (gbHardware & 4) - { - if(gbEmulatorType == 5) - AF.W = 0xffb0; - else - AF.W = 0x01b0; - BC.W = 0x0013; - DE.W = 0x00d8; - HL.W = 0x014d; - } - gbDivTicks = 14; - gbInternalTimer = gbDivTicks--; - gbMemory[0xff04] = register_DIV = 0xAB; - gbMemory[0xff41] = register_STAT = 0x85; - gbMemory[0xff44] = register_LY = 0x00; - gbLcdTicks = 15; - gbLcdLYIncrementTicks = 114+gbLcdTicks; - gbLcdMode = 1; - - // used for the handling of the gb Boot Rom - if ((gbHardware & 5) && (bios != NULL) && useBios && !skipBios) - { - memcpy ((u8 *)(gbMemory), (u8 *)(gbRom), 0x1000); - memcpy ((u8 *)(gbMemory), (u8 *)(bios), 0x100); - gbWhiteScreen = 0; - - gbInternalTimer = 0x3e; - gbDivTicks = 0x3f; - gbMemory[0xff04] = register_DIV = 0x00; - PC.W = 0x0000; - register_LCDC = 0x11; - gbScreenOn = false; - gbLcdTicks = 0; - gbLcdMode = 0; - gbLcdModeDelayed = 0; - gbMemory[0xff41] = register_STAT &= 0xfc; - gbInt48Signal = 0; - gbLcdLYIncrementTicks = GBLY_INCREMENT_CLOCK_TICKS; - } - } - - gbLine99Ticks = 1; - if (gbHardware & 8) - gbLine99Ticks++; - - gbLcdModeDelayed = gbLcdMode; - gbLcdTicksDelayed = gbLcdTicks+1; - gbLcdLYIncrementTicksDelayed = gbLcdLYIncrementTicks+1; - - - gbTimerModeChange = false; - gbTimerOnChange = false; - gbTimerOn = false; - - if(gbCgbMode) { - for (i = 0; i<0x20; i++) - gbPalette[i] = 0x7fff; - - // This is just to show that the starting values of the OBJ palettes are different - // between the 3 consoles, and that they 'kinda' stay the same at each reset - // (they can slightly change, somehow (randomly?)). - // You can check the effects of gbGBCColorType on the "Vila Caldan Color" gbc demo. - // Note that you could also check the Div register to check on which system the game - // is running (GB,GBC and GBA(SP) have different startup values). - // Unfortunatly, I don't have any SGB system, so I can't get their starting values. - - if (gbGBCColorType == 0) // GBC Hardware - { - gbPalette[0x20] = 0x0600; - gbPalette[0x21] = 0xfdf3; - gbPalette[0x22] = 0x041c; - gbPalette[0x23] = 0xf5db; - gbPalette[0x24] = 0x4419; - gbPalette[0x25] = 0x57ea; - gbPalette[0x26] = 0x2808; - gbPalette[0x27] = 0x9b75; - gbPalette[0x28] = 0x129b; - gbPalette[0x29] = 0xfce0; - gbPalette[0x2a] = 0x22da; - gbPalette[0x2b] = 0x4ac5; - gbPalette[0x2c] = 0x2d71; - gbPalette[0x2d] = 0xf0c2; - gbPalette[0x2e] = 0x5137; - gbPalette[0x2f] = 0x2d41; - gbPalette[0x30] = 0x6b2d; - gbPalette[0x31] = 0x2215; - gbPalette[0x32] = 0xbe0a; - gbPalette[0x33] = 0xc053; - gbPalette[0x34] = 0xfe5f; - gbPalette[0x35] = 0xe000; - gbPalette[0x36] = 0xbe10; - gbPalette[0x37] = 0x914d; - gbPalette[0x38] = 0x7f91; - gbPalette[0x39] = 0x02b5; - gbPalette[0x3a] = 0x77ac; - gbPalette[0x3b] = 0x14e5; - gbPalette[0x3c] = 0xcf89; - gbPalette[0x3d] = 0xa03d; - gbPalette[0x3e] = 0xfd50; - gbPalette[0x3f] = 0x91ff; - } - else if (gbGBCColorType == 1) // GBA Hardware - { - gbPalette[0x20] = 0xbe00; - gbPalette[0x21] = 0xfdfd; - gbPalette[0x22] = 0xbd69; - gbPalette[0x23] = 0x7baf; - gbPalette[0x24] = 0xf5ff; - gbPalette[0x25] = 0x3f8f; - gbPalette[0x26] = 0xcee5; - gbPalette[0x27] = 0x5bf7; - gbPalette[0x28] = 0xb35b; - gbPalette[0x29] = 0xef97; - gbPalette[0x2a] = 0xef9f; - gbPalette[0x2b] = 0x97f7; - gbPalette[0x2c] = 0x82bf; - gbPalette[0x2d] = 0x9f3d; - gbPalette[0x2e] = 0xddde; - gbPalette[0x2f] = 0xbad5; - gbPalette[0x30] = 0x3cba; - gbPalette[0x31] = 0xdfd7; - gbPalette[0x32] = 0xedea; - gbPalette[0x33] = 0xfeda; - gbPalette[0x34] = 0xf7f9; - gbPalette[0x35] = 0xfdee; - gbPalette[0x36] = 0x6d2f; - gbPalette[0x37] = 0xf0e6; - gbPalette[0x38] = 0xf7f0; - gbPalette[0x39] = 0xf296; - gbPalette[0x3a] = 0x3bf1; - gbPalette[0x3b] = 0xe211; - gbPalette[0x3c] = 0x69ba; - gbPalette[0x3d] = 0x3d0d; - gbPalette[0x3e] = 0xdfd3; - gbPalette[0x3f] = 0xa6ba; - } - else if (gbGBCColorType == 2) // GBASP Hardware - { - gbPalette[0x20] = 0x9c00; - gbPalette[0x21] = 0x6340; - gbPalette[0x22] = 0x10c6; - gbPalette[0x23] = 0xdb97; - gbPalette[0x24] = 0x7622; - gbPalette[0x25] = 0x3e57; - gbPalette[0x26] = 0x2e12; - gbPalette[0x27] = 0x95c3; - gbPalette[0x28] = 0x1095; - gbPalette[0x29] = 0x488c; - gbPalette[0x2a] = 0x8241; - gbPalette[0x2b] = 0xde8c; - gbPalette[0x2c] = 0xfabc; - gbPalette[0x2d] = 0x0e81; - gbPalette[0x2e] = 0x7675; - gbPalette[0x2f] = 0xfdec; - gbPalette[0x30] = 0xddfd; - gbPalette[0x31] = 0x5995; - gbPalette[0x32] = 0x066a; - gbPalette[0x33] = 0xed1e; - gbPalette[0x34] = 0x1e84; - gbPalette[0x35] = 0x1d14; - gbPalette[0x36] = 0x11c3; - gbPalette[0x37] = 0x2749; - gbPalette[0x38] = 0xa727; - gbPalette[0x39] = 0x6266; - gbPalette[0x3a] = 0xe27b; - gbPalette[0x3b] = 0xe3fc; - gbPalette[0x3c] = 0x1f76; - gbPalette[0x3d] = 0xf158; - gbPalette[0x3e] = 0x468e; - gbPalette[0x3f] = 0xa540; - } - } else { - if(gbSgbMode) { - for(i = 0; i < 8; i++) - gbPalette[i] = systemGbPalette[gbPaletteOption*8+i]; - - } - for(i = 0; i < 8; i++) - gbPalette[i] = systemGbPalette[gbPaletteOption*8+i]; - } - - GBTIMER_MODE_0_CLOCK_TICKS = 256; - GBTIMER_MODE_1_CLOCK_TICKS = 4; - GBTIMER_MODE_2_CLOCK_TICKS = 16; - GBTIMER_MODE_3_CLOCK_TICKS = 64; - - GBLY_INCREMENT_CLOCK_TICKS = 114; - gbTimerTicks = GBTIMER_MODE_0_CLOCK_TICKS; - gbTimerClockTicks = GBTIMER_MODE_0_CLOCK_TICKS; - gbSerialTicks = 0; - gbSerialBits = 0; - gbSerialOn = 0; - gbWindowLine = -1; - gbTimerOn = false; - gbTimerMode = 0; - gbSpeed = 0; - gbJoymask[0] = gbJoymask[1] = gbJoymask[2] = gbJoymask[3] = 0; - - if(gbCgbMode) { - gbSpeed = 0; - gbHdmaOn = 0; - gbHdmaSource = 0x99d0; - gbHdmaDestination = 0x99d0; - gbVramBank = 0; - gbWramBank = 1; - - } - - // used to clean the borders - if (gbSgbMode) - { - gbSgbResetFlag = true; - gbSgbReset(); - if (gbBorderOn) - gbSgbRenderBorder(); - gbSgbResetFlag = false; - } - - for(i = 0; i < 4; i++) - gbBgp[i] = gbObp0[i] = gbObp1[i] = i; - - memset(&gbDataMBC1,0, sizeof(gbDataMBC1)); - gbDataMBC1.mapperROMBank = 1; - - gbDataMBC2.mapperRAMEnable = 0; - gbDataMBC2.mapperROMBank = 1; - - memset(&gbDataMBC3,0, 6 * sizeof(int)); - gbDataMBC3.mapperROMBank = 1; - - memset(&gbDataMBC5, 0, sizeof(gbDataMBC5)); - gbDataMBC5.mapperROMBank = 1; - - memset(&gbDataHuC1, 0, sizeof(gbDataHuC1)); - gbDataHuC1.mapperROMBank = 1; - - memset(&gbDataHuC3, 0, sizeof(gbDataHuC3)); - gbDataHuC3.mapperROMBank = 1; - - memset(&gbDataTAMA5,0, 26*sizeof(int)); - gbDataTAMA5.mapperROMBank = 1; - - memset(&gbDataMMM01,0, sizeof(gbDataMMM01)); - gbDataMMM01.mapperROMBank = 1; - - if (useBios && !skipBios && (gbHardware & 5)) - { - gbMemoryMap[0x00] = &gbMemory[0x0000]; - inBios = true; - } - else - { - gbMemoryMap[0x00] = &gbRom[0x0000]; - inBios = false; - } - - gbMemoryMap[0x01] = &gbRom[0x1000]; - gbMemoryMap[0x02] = &gbRom[0x2000]; - gbMemoryMap[0x03] = &gbRom[0x3000]; - gbMemoryMap[0x04] = &gbRom[0x4000]; - gbMemoryMap[0x05] = &gbRom[0x5000]; - gbMemoryMap[0x06] = &gbRom[0x6000]; - gbMemoryMap[0x07] = &gbRom[0x7000]; - if(gbCgbMode) { - gbMemoryMap[0x08] = &gbVram[0x0000]; - gbMemoryMap[0x09] = &gbVram[0x1000]; - gbMemoryMap[0x0a] = &gbMemory[0xa000]; - gbMemoryMap[0x0b] = &gbMemory[0xb000]; - gbMemoryMap[0x0c] = &gbMemory[0xc000]; - gbMemoryMap[0x0d] = &gbWram[0x1000]; - gbMemoryMap[0x0e] = &gbMemory[0xe000]; - gbMemoryMap[0x0f] = &gbMemory[0xf000]; - } else { - gbMemoryMap[0x08] = &gbMemory[0x8000]; - gbMemoryMap[0x09] = &gbMemory[0x9000]; - gbMemoryMap[0x0a] = &gbMemory[0xa000]; - gbMemoryMap[0x0b] = &gbMemory[0xb000]; - gbMemoryMap[0x0c] = &gbMemory[0xc000]; - gbMemoryMap[0x0d] = &gbMemory[0xd000]; - gbMemoryMap[0x0e] = &gbMemory[0xe000]; - gbMemoryMap[0x0f] = &gbMemory[0xf000]; - } - - if(gbRam) { - gbMemoryMap[0x0a] = &gbRam[0x0000]; - gbMemoryMap[0x0b] = &gbRam[0x1000]; - } - - gbSoundReset(); - - systemSaveUpdateCounter = SYSTEM_SAVE_NOT_UPDATED; - - gbLastTime = systemGetClock(); - gbFrameCount = 0; - - gbScreenOn = true; - gbSystemMessage = false; - - gbCheatWrite(true); // Emulates GS codes. - -} - -void gbWriteSaveMBC1(const char * name) -{ - if (gbRam) - { - FILE *gzFile = fopen(name,"wb"); - - if(gzFile == NULL) { - systemMessage(MSG_ERROR_CREATING_FILE, N_("Error creating file %s"), name); - return; - } - - fwrite(gbRam, - 1, - (gbRamSizeMask+1), - gzFile); - - fclose(gzFile); - } -} - -void gbWriteSaveMBC2(const char * name) -{ - if (gbRam) - { - FILE *file = fopen(name, "wb"); - - if(file == NULL) { - systemMessage(MSG_ERROR_CREATING_FILE, N_("Error creating file %s"), name); - return; - } - - fwrite(gbMemoryMap[0x0a], - 1, - 512, - file); - - fclose(file); - } -} - -void gbWriteSaveMBC3(const char * name, bool extendedSave) -{ - if (gbRam || extendedSave) - { - FILE *gzFile = fopen(name,"wb"); - if (gbRam) - { - - if(gzFile == NULL) { - systemMessage(MSG_ERROR_CREATING_FILE, N_("Error creating file %s"), name); - return; - } - - fwrite(gbRam, - 1, - (gbRamSizeMask+1), - gzFile); - } - - if(extendedSave) - fwrite(&gbDataMBC3.mapperSeconds, - 1, - 10*sizeof(int) + sizeof(time_t), - gzFile); - - fclose(gzFile); - } -} - -void gbWriteSaveMBC5(const char * name) -{ - if (gbRam) - { - FILE *gzFile = fopen(name,"wb"); - - if(gzFile == NULL) { - systemMessage(MSG_ERROR_CREATING_FILE, N_("Error creating file %s"), name); - return; - } - - fwrite(gbRam, - 1, - (gbRamSizeMask+1), - gzFile); - - fclose(gzFile); - } -} - -void gbWriteSaveMBC7(const char * name) -{ - if (gbRam) - { - FILE *file = fopen(name, "wb"); - - if(file == NULL) { - systemMessage(MSG_ERROR_CREATING_FILE, N_("Error creating file %s"), name); - return; - } - - fwrite(&gbMemory[0xa000], - 1, - 256, - file); - - fclose(file); - } -} - -void gbWriteSaveTAMA5(const char * name, bool extendedSave) -{ - FILE *gzFile = fopen(name,"wb"); - - if(gzFile == NULL) { - systemMessage(MSG_ERROR_CREATING_FILE, N_("Error creating file %s"), name); - return; - } - if (gbRam) - fwrite(gbRam, - 1, - (gbRamSizeMask+1), - gzFile); - - fwrite(gbTAMA5ram, - 1, - (gbTAMA5ramSize), - gzFile); - - if(extendedSave) - fwrite(&gbDataTAMA5.mapperSeconds, - 1, - 14*sizeof(int) + sizeof(time_t), - gzFile); - - fclose(gzFile); -} - -void gbWriteSaveMMM01(const char * name) -{ - if (gbRam) - { - FILE *gzFile = fopen(name,"wb"); - - if(gzFile == NULL) { - systemMessage(MSG_ERROR_CREATING_FILE, N_("Error creating file %s"), name); - return; - } - - fwrite(gbRam, - 1, - (gbRamSizeMask+1), - gzFile); - - fclose(gzFile); - } -} - - -bool gbReadSaveMBC1(const char * name) -{ - if (gbRam) - { - gzFile gzFile = gzopen(name, "rb"); - - if(gzFile == NULL) { - return false; - } - - int read = gzread(gzFile, - gbRam, - (gbRamSizeMask+1)); - - if(read != (gbRamSizeMask+1)) { - systemMessage(MSG_FAILED_TO_READ_SGM, - N_("Battery file's size incompatible with the rom settings %s (%d).\nWarning : save of the battery file is now disabled !"), name, read); - gzclose(gzFile); - gbBatteryError = true; - return false; - } - - // Also checks if the battery file it bigger than gbRamSizeMask+1 ! - u8 data[1]; - data[0] = 0; - - read = gzread(gzFile, - data, - 1); - if(read >0) { - systemMessage(MSG_FAILED_TO_READ_SGM, - N_("Battery file's size incompatible with the rom settings %s (%d).\nWarning : save of the battery file is now disabled !"), name, read); - gzclose(gzFile); - gbBatteryError = true; - return false; - } - - gzclose(gzFile); - return true; - } - else - return false; -} - - -bool gbReadSaveMBC2(const char * name) -{ - if (gbRam) - { - FILE *file = fopen(name, "rb"); - - if(file == NULL) { - return false; - } - - size_t read = fread(gbMemoryMap[0x0a], - 1, - 512, - file); - - if(read != 512) { - systemMessage(MSG_FAILED_TO_READ_SGM, - N_("Battery file's size incompatible with the rom settings %s (%d).\nWarning : save of the battery file is now disabled !"), name, read); - fclose(file); - gbBatteryError = true; - return false; - } - - // Also checks if the battery file it bigger than gbRamSizeMask+1 ! - u8 data[1]; - data[0] = 0; - - read = fread(&data[0], - 1, - 1, - file); - if(read > 0) { - systemMessage(MSG_FAILED_TO_READ_SGM, - N_("Battery file's size incompatible with the rom settings %s (%d).\nWarning : save of the battery file is now disabled !"), name, read); - fclose(file); - gbBatteryError = true; - return false; - } - - fclose(file); - return true; - } - else - return false; -} - -bool gbReadSaveMBC3(const char * name) -{ - gzFile gzFile = gzopen(name, "rb"); - - if(gzFile == NULL) { - return false; - } - - int read = 0; - - if (gbRam) - read = gzread(gzFile, - gbRam, - (gbRamSizeMask+1)); - else - read = (gbRamSizeMask+1); - - - bool res = true; - - if(read != (gbRamSizeMask+1)) { - systemMessage(MSG_FAILED_TO_READ_SGM, - N_("Battery file's size incompatible with the rom settings %s (%d).\nWarning : save of the battery file is now disabled !"), name, read); - gbBatteryError = true; - res = false; - } else if ((gbRomType == 0xf) || (gbRomType == 0x10)){ - read = gzread(gzFile, - &gbDataMBC3.mapperSeconds, - sizeof(int)*10 + sizeof(time_t)); - - if(read != (sizeof(int)*10 + sizeof(time_t)) && read != 0) { - systemMessage(MSG_FAILED_TO_READ_RTC,N_("Failed to read RTC from save game %s (continuing)"), - name); - res = false; - } - else if (read == 0) - { - systemMessage(MSG_FAILED_TO_READ_RTC,N_("Failed to read RTC from save game %s (continuing)"), - name); - res = false; - } - else - { - // Also checks if the battery file it bigger than gbRamSizeMask+1+RTC ! - u8 data[1]; - data[0] = 0; - - read = gzread(gzFile, - data, - 1); - if(read >0) { - systemMessage(MSG_FAILED_TO_READ_SGM, - N_("Battery file's size incompatible with the rom settings %s (%d).\nWarning : save of the battery file is now disabled !"), name, read); - gbBatteryError = true; - res = false; - } - } - } - - gzclose(gzFile); - return res; -} - -bool gbReadSaveMBC5(const char * name) -{ - if (gbRam) - { - gzFile gzFile = gzopen(name, "rb"); - - if(gzFile == NULL) { - return false; - } - - int read = gzread(gzFile, - gbRam, - (gbRamSizeMask+1)); - - if(read != (gbRamSizeMask+1)) { - systemMessage(MSG_FAILED_TO_READ_SGM, - N_("Battery file's size incompatible with the rom settings %s (%d).\nWarning : save of the battery file is now disabled !"), name, read); - gzclose(gzFile); - gbBatteryError = true; - return false; - } - - - // Also checks if the battery file it bigger than gbRamSizeMask+1 ! - u8 data[1]; - data[0] = 0; - - read = gzread(gzFile, - data, - 1); - if(read >0) { - systemMessage(MSG_FAILED_TO_READ_SGM, - N_("Battery file's size incompatible with the rom settings %s (%d).\nWarning : save of the battery file is now disabled !"), name, read); - gzclose(gzFile); - gbBatteryError = true; - return false; - } - - gzclose(gzFile); - return true; - } - else - return false; -} - -bool gbReadSaveMBC7(const char * name) -{ - if (gbRam) - { - FILE *file = fopen(name, "rb"); - - if(file == NULL) { - return false; - } - - size_t read = fread(&gbMemory[0xa000], - 1, - 256, - file); - - if(read != 256) { - systemMessage(MSG_FAILED_TO_READ_SGM, - N_("Battery file's size incompatible with the rom settings %s (%d).\nWarning : save of the battery file is now disabled !"), name, read); - fclose(file); - gbBatteryError = true; - return false; - } - - // Also checks if the battery file it bigger than gbRamSizeMask+1 ! - u8 data[1]; - data[0] = 0; - - read = fread(&data[0], - 1, - 1, - file); - if(read > 0) { - systemMessage(MSG_FAILED_TO_READ_SGM, - N_("Battery file's size incompatible with the rom settings %s (%d).\nWarning : save of the battery file is now disabled !"), name, read); - fclose(file); - gbBatteryError = true; - return false; - } - - fclose(file); - return true; - } - else - return false; -} - -bool gbReadSaveTAMA5(const char * name) -{ - gzFile gzFile = gzopen(name, "rb"); - - if(gzFile == NULL) { - return false; - } - - int read = 0; - - if (gbRam) - read = gzread(gzFile, - gbRam, - (gbRamSizeMask+1)); - else - read = gbRamSizeMask; - - read += gzread(gzFile, - gbTAMA5ram, - gbTAMA5ramSize); - - bool res = true; - - if(read != (gbRamSizeMask+gbTAMA5ramSize+1)) { - systemMessage(MSG_FAILED_TO_READ_SGM, - N_("Battery file's size incompatible with the rom settings %s (%d).\nWarning : save of the battery file is now disabled !"), name, read); - gbBatteryError = true; - res = false; - } else { - read = gzread(gzFile, - &gbDataTAMA5.mapperSeconds, - sizeof(int)*14 + sizeof(time_t)); - - if(read != (sizeof(int)*14 + sizeof(time_t)) && read != 0) { - systemMessage(MSG_FAILED_TO_READ_RTC,N_("Failed to read RTC from save game %s (continuing)"), - name); - res = false; - } - else if (read == 0) - { - systemMessage(MSG_FAILED_TO_READ_RTC,N_("Failed to read RTC from save game %s (continuing)"), - name); - res = false; - } - else - { - // Also checks if the battery file it bigger than gbRamSizeMask+1+RTC ! - u8 data[1]; - data[0] = 0; - - read = gzread(gzFile, - data, - 1); - if(read >0) { - systemMessage(MSG_FAILED_TO_READ_SGM, - N_("Battery file's size incompatible with the rom settings %s (%d).\nWarning : save of the battery file is now disabled !"), name, read); - gbBatteryError = true; - res = false; - } - } - } - - gzclose(gzFile); - return res; -} - - -bool gbReadSaveMMM01(const char * name) -{ - if (gbRam) - { - gzFile gzFile = gzopen(name, "rb"); - - if(gzFile == NULL) { - return false; - } - - int read = gzread(gzFile, - gbRam, - (gbRamSizeMask+1)); - - if(read != (gbRamSizeMask+1)) { - systemMessage(MSG_FAILED_TO_READ_SGM, - N_("Battery file's size incompatible with the rom settings %s (%d).\nWarning : save of the battery file is now disabled !"), name, read); - gzclose(gzFile); - gbBatteryError = true; - return false; - } - - // Also checks if the battery file it bigger than gbRamSizeMask+1 ! - u8 data[1]; - data[0] = 0; - - read = gzread(gzFile, - data, - 1); - if(read >0) { - systemMessage(MSG_FAILED_TO_READ_SGM, - N_("Battery file's size incompatible with the rom settings %s (%d).\nWarning : save of the battery file is now disabled !"), name, read); - gzclose(gzFile); - gbBatteryError = true; - return false; - } - - gzclose(gzFile); - return true; - } - else - return false; -} - -void gbInit() -{ - gbGenFilter(); - gbSgbInit(); - - gbMemory = (u8 *)malloc(65536); - - pix = (u8 *)calloc(1,4*257*226); - - gbLineBuffer = (u16 *)malloc(160 * sizeof(u16)); -} - -bool gbWriteBatteryFile(const char *file, bool extendedSave) -{ - if(gbBattery) { - switch(gbRomType) { - case 0x03: - gbWriteSaveMBC1(file); - break; - case 0x06: - gbWriteSaveMBC2(file); - break; - case 0x0d: - gbWriteSaveMMM01(file); - break; - case 0x0f: - case 0x10: - gbWriteSaveMBC3(file, extendedSave); - break; - case 0x13: - case 0xfc: - gbWriteSaveMBC3(file, false); - case 0x1b: - case 0x1e: - gbWriteSaveMBC5(file); - break; - case 0x22: - gbWriteSaveMBC7(file); - break; - case 0xfd: - gbWriteSaveTAMA5(file, extendedSave); - break; - case 0xff: - gbWriteSaveMBC1(file); - break; - } - } - return true; -} - -bool gbWriteBatteryFile(const char *file) -{ - if (!gbBatteryError) - { - gbWriteBatteryFile(file, true); - return true; - } - else return false; -} - -bool gbReadBatteryFile(const char *file) -{ - bool res = false; - if(gbBattery) { - switch(gbRomType) { - case 0x03: - res = gbReadSaveMBC1(file); - break; - case 0x06: - res = gbReadSaveMBC2(file); - break; - case 0x0d: - res = gbReadSaveMMM01(file); - break; - case 0x0f: - case 0x10: - if(!gbReadSaveMBC3(file)) { - time(&gbDataMBC3.mapperLastTime); - struct tm *lt; - lt = localtime(&gbDataMBC3.mapperLastTime); - gbDataMBC3.mapperSeconds = lt->tm_sec; - gbDataMBC3.mapperMinutes = lt->tm_min; - gbDataMBC3.mapperHours = lt->tm_hour; - gbDataMBC3.mapperDays = lt->tm_yday & 255; - gbDataMBC3.mapperControl = (gbDataMBC3.mapperControl & 0xfe) | - (lt->tm_yday > 255 ? 1: 0); - res = false; - break; - } - res = true; - break; - case 0x13: - case 0xfc: - res = gbReadSaveMBC3(file); - break; - case 0x1b: - case 0x1e: - res = gbReadSaveMBC5(file); - break; - case 0x22: - res = gbReadSaveMBC7(file); - break; - case 0xfd: - if(!gbReadSaveTAMA5(file)) { - u8 gbDaysinMonth [12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; - time(&gbDataTAMA5.mapperLastTime); - struct tm *lt; - lt = localtime(&gbDataTAMA5.mapperLastTime); - gbDataTAMA5.mapperSeconds = lt->tm_sec; - gbDataTAMA5.mapperMinutes = lt->tm_min; - gbDataTAMA5.mapperHours = lt->tm_hour; - gbDataTAMA5.mapperDays = 1; - gbDataTAMA5.mapperMonths = 1; - gbDataTAMA5.mapperYears = 1970; - int days = lt->tm_yday+365*3; - while (days) - { - gbDataTAMA5.mapperDays++; - days--; - if (gbDataTAMA5.mapperDays>gbDaysinMonth[gbDataTAMA5.mapperMonths-1]) - { - gbDataTAMA5.mapperDays = 1; - gbDataTAMA5.mapperMonths++; - if (gbDataTAMA5.mapperMonths>12) - { - gbDataTAMA5.mapperMonths = 1; - gbDataTAMA5.mapperYears++; - if ((gbDataTAMA5.mapperYears & 3) == 0) - gbDaysinMonth[1] = 29; - else - gbDaysinMonth[1] = 28; - } - } - } - gbDataTAMA5.mapperControl = (gbDataTAMA5.mapperControl & 0xfe) | - (lt->tm_yday > 255 ? 1: 0); - res = false; - break; - } - res = true; - break; - case 0xff: - res = gbReadSaveMBC1(file); - break; - } - } - systemSaveUpdateCounter = SYSTEM_SAVE_NOT_UPDATED; - return res; -} - -bool gbReadGSASnapshot(const char *fileName) -{ - FILE *file = fopen(fileName, "rb"); - - if(!file) { - systemMessage(MSG_CANNOT_OPEN_FILE, N_("Cannot open file %s"), fileName); - return false; - } - - fseek(file, 0x4, SEEK_SET); - char buffer[16]; - char buffer2[16]; - fread(buffer, 1, 15, file); - buffer[15] = 0; - memcpy(buffer2, &gbRom[0x134], 15); - buffer2[15] = 0; - if(memcmp(buffer, buffer2, 15)) { - systemMessage(MSG_CANNOT_IMPORT_SNAPSHOT_FOR, - N_("Cannot import snapshot for %s. Current game is %s"), - buffer, - buffer2); - fclose(file); - return false; - } - fseek(file, 0x13, SEEK_SET); - size_t read = 0; - int toRead = 0; - switch(gbRomType) { - case 0x03: - case 0x0f: - case 0x10: - case 0x13: - case 0x1b: - case 0x1e: - case 0xff: - read = fread(gbRam, 1, (gbRamSizeMask+1), file); - toRead = (gbRamSizeMask+1); - break; - case 0x06: - case 0x22: - read = fread(&gbMemory[0xa000],1,256,file); - toRead = 256; - break; - default: - systemMessage(MSG_UNSUPPORTED_SNAPSHOT_FILE, - N_("Unsupported snapshot file %s"), - fileName); - fclose(file); - return false; - } - fclose(file); - gbReset(); - return true; -} - -variable_desc gbSaveGameStruct[] = { - { &PC.W, sizeof(u16) }, - { &SP.W, sizeof(u16) }, - { &AF.W, sizeof(u16) }, - { &BC.W, sizeof(u16) }, - { &DE.W, sizeof(u16) }, - { &HL.W, sizeof(u16) }, - { &IFF, sizeof(u8) }, - { &GBLCD_MODE_0_CLOCK_TICKS, sizeof(int) }, - { &GBLCD_MODE_1_CLOCK_TICKS, sizeof(int) }, - { &GBLCD_MODE_2_CLOCK_TICKS, sizeof(int) }, - { &GBLCD_MODE_3_CLOCK_TICKS, sizeof(int) }, - { &GBDIV_CLOCK_TICKS, sizeof(int) }, - { &GBLY_INCREMENT_CLOCK_TICKS, sizeof(int) }, - { &GBTIMER_MODE_0_CLOCK_TICKS, sizeof(int) }, - { &GBTIMER_MODE_1_CLOCK_TICKS, sizeof(int) }, - { &GBTIMER_MODE_2_CLOCK_TICKS, sizeof(int) }, - { &GBTIMER_MODE_3_CLOCK_TICKS, sizeof(int) }, - { &GBSERIAL_CLOCK_TICKS, sizeof(int) }, - { &GBSYNCHRONIZE_CLOCK_TICKS, sizeof(int) }, - { &gbDivTicks, sizeof(int) }, - { &gbLcdMode, sizeof(int) }, - { &gbLcdTicks, sizeof(int) }, - { &gbLcdLYIncrementTicks, sizeof(int) }, - { &gbTimerTicks, sizeof(int) }, - { &gbTimerClockTicks, sizeof(int) }, - { &gbSerialTicks, sizeof(int) }, - { &gbSerialBits, sizeof(int) }, - { &gbInt48Signal, sizeof(int) }, - { &gbInterruptWait, sizeof(int) }, - { &gbSynchronizeTicks, sizeof(int) }, - { &gbTimerOn, sizeof(int) }, - { &gbTimerMode, sizeof(int) }, - { &gbSerialOn, sizeof(int) }, - { &gbWindowLine, sizeof(int) }, - { &gbCgbMode, sizeof(int) }, - { &gbVramBank, sizeof(int) }, - { &gbWramBank, sizeof(int) }, - { &gbHdmaSource, sizeof(int) }, - { &gbHdmaDestination, sizeof(int) }, - { &gbHdmaBytes, sizeof(int) }, - { &gbHdmaOn, sizeof(int) }, - { &gbSpeed, sizeof(int) }, - { &gbSgbMode, sizeof(int) }, - { ®ister_DIV, sizeof(u8) }, - { ®ister_TIMA, sizeof(u8) }, - { ®ister_TMA, sizeof(u8) }, - { ®ister_TAC, sizeof(u8) }, - { ®ister_IF, sizeof(u8) }, - { ®ister_LCDC, sizeof(u8) }, - { ®ister_STAT, sizeof(u8) }, - { ®ister_SCY, sizeof(u8) }, - { ®ister_SCX, sizeof(u8) }, - { ®ister_LY, sizeof(u8) }, - { ®ister_LYC, sizeof(u8) }, - { ®ister_DMA, sizeof(u8) }, - { ®ister_WY, sizeof(u8) }, - { ®ister_WX, sizeof(u8) }, - { ®ister_VBK, sizeof(u8) }, - { ®ister_HDMA1, sizeof(u8) }, - { ®ister_HDMA2, sizeof(u8) }, - { ®ister_HDMA3, sizeof(u8) }, - { ®ister_HDMA4, sizeof(u8) }, - { ®ister_HDMA5, sizeof(u8) }, - { ®ister_SVBK, sizeof(u8) }, - { ®ister_IE , sizeof(u8) }, - { &gbBgp[0], sizeof(u8) }, - { &gbBgp[1], sizeof(u8) }, - { &gbBgp[2], sizeof(u8) }, - { &gbBgp[3], sizeof(u8) }, - { &gbObp0[0], sizeof(u8) }, - { &gbObp0[1], sizeof(u8) }, - { &gbObp0[2], sizeof(u8) }, - { &gbObp0[3], sizeof(u8) }, - { &gbObp1[0], sizeof(u8) }, - { &gbObp1[1], sizeof(u8) }, - { &gbObp1[2], sizeof(u8) }, - { &gbObp1[3], sizeof(u8) }, - { NULL, 0 } -}; - - -static bool gbWriteSaveState(gzFile gzFile) -{ - - utilWriteInt(gzFile, GBSAVE_GAME_VERSION); - - utilGzWrite(gzFile, &gbRom[0x134], 15); - - utilWriteInt(gzFile, useBios); - utilWriteInt(gzFile, inBios); - - utilWriteData(gzFile, gbSaveGameStruct); - - utilGzWrite(gzFile, &IFF, 2); - - if(gbSgbMode) { - gbSgbSaveGame(gzFile); - } - - utilGzWrite(gzFile, &gbDataMBC1, sizeof(gbDataMBC1)); - utilGzWrite(gzFile, &gbDataMBC2, sizeof(gbDataMBC2)); - utilGzWrite(gzFile, &gbDataMBC3, sizeof(gbDataMBC3)); - utilGzWrite(gzFile, &gbDataMBC5, sizeof(gbDataMBC5)); - utilGzWrite(gzFile, &gbDataHuC1, sizeof(gbDataHuC1)); - utilGzWrite(gzFile, &gbDataHuC3, sizeof(gbDataHuC3)); - utilGzWrite(gzFile, &gbDataTAMA5, sizeof(gbDataTAMA5)); - if (gbTAMA5ram != NULL) - utilGzWrite(gzFile, gbTAMA5ram, gbTAMA5ramSize); - utilGzWrite(gzFile, &gbDataMMM01, sizeof(gbDataMMM01)); - - utilGzWrite(gzFile, gbPalette, 128 * sizeof(u16)); - - utilGzWrite(gzFile, &gbMemory[0x8000], 0x8000); - - if(gbRamSize && gbRam) { - utilWriteInt(gzFile, gbRamSize); - utilGzWrite(gzFile, gbRam, gbRamSize); - } - - if(gbCgbMode) { - utilGzWrite(gzFile, gbVram, 0x4000); - utilGzWrite(gzFile, gbWram, 0x8000); - } - - gbSoundSaveGame(gzFile); - - gbCheatsSaveGame(gzFile); - - utilWriteInt(gzFile, gbLcdModeDelayed); - utilWriteInt(gzFile, gbLcdTicksDelayed); - utilWriteInt(gzFile, gbLcdLYIncrementTicksDelayed); - utilWriteInt(gzFile, gbSpritesTicks[299]); - utilWriteInt(gzFile, gbTimerModeChange); - utilWriteInt(gzFile, gbTimerOnChange); - utilWriteInt(gzFile, gbHardware); - utilWriteInt(gzFile, gbBlackScreen); - utilWriteInt(gzFile, oldRegister_WY); - utilWriteInt(gzFile, gbWindowLine); - utilWriteInt(gzFile, inUseRegister_WY); - utilWriteInt(gzFile, gbScreenOn); - utilWriteInt(gzFile, 0x12345678); // end marker - return true; -} - -bool gbWriteMemSaveState(char *memory, int available) -{ - gzFile gzFile = utilMemGzOpen(memory, available, "w"); - - if(gzFile == NULL) { - return false; - } - - bool res = gbWriteSaveState(gzFile); - - long pos = utilGzMemTell(gzFile)+8; - - if(pos >= (available)) - res = false; - - utilGzClose(gzFile); - - return res; -} - -bool gbWriteSaveState(const char *name) -{ - gzFile gzFile = utilGzOpen(name,"wb"); - - if(gzFile == NULL) - return false; - - bool res = gbWriteSaveState(gzFile); - - utilGzClose(gzFile); - return res; -} - -static bool gbReadSaveState(gzFile gzFile) -{ - int version = utilReadInt(gzFile); - - if(version > GBSAVE_GAME_VERSION || version < 0) { - systemMessage(MSG_UNSUPPORTED_VB_SGM, - N_("Unsupported VisualBoy save game version %d"), version); - return false; - } - - u8 romname[20]; - - utilGzRead(gzFile, romname, 15); - - if(memcmp(&gbRom[0x134], romname, 15) != 0) { - systemMessage(MSG_CANNOT_LOAD_SGM_FOR, - N_("Cannot load save game for %s. Playing %s"), - romname, &gbRom[0x134]); - return false; - } - - - bool ub = false; - bool ib = false; - - if (version >= 11) - { - ub = utilReadInt(gzFile) ? true : false; - ib = utilReadInt(gzFile) ? true : false; - - if((ub != useBios) && (ib)) { - if(useBios) - systemMessage(MSG_SAVE_GAME_NOT_USING_BIOS, - N_("Save game is not using the BIOS files")); - else - systemMessage(MSG_SAVE_GAME_USING_BIOS, - N_("Save game is using the BIOS file")); - return false; - } - } - - gbReset(); - - inBios = ib; - - utilReadData(gzFile, gbSaveGameStruct); - - - // Correct crash when loading color gameboy save in regular gameboy type. - if (!gbCgbMode) - { - if(gbVram != NULL) { - free(gbVram); - gbVram = NULL; - } - if(gbWram != NULL) { - free(gbWram); - gbWram = NULL; - } - } - else - { - if(gbVram == NULL) - gbVram = (u8 *)malloc(0x4000); - if(gbWram == NULL) - gbWram = (u8 *)malloc(0x8000); - memset(gbVram,0,0x4000); - memset(gbPalette,0, 2*128); - } - - - - if(version >= GBSAVE_GAME_VERSION_7) { - utilGzRead(gzFile, &IFF, 2); - } - - if(gbSgbMode) { - gbSgbReadGame(gzFile, version); - } else { - gbSgbMask = 0; // loading a game at the wrong time causes no display - } - if (version<11) - utilGzRead(gzFile, &gbDataMBC1, sizeof(gbDataMBC1) - sizeof(int)); - else - utilGzRead(gzFile, &gbDataMBC1, sizeof(gbDataMBC1)); - utilGzRead(gzFile, &gbDataMBC2, sizeof(gbDataMBC2)); - if(version < GBSAVE_GAME_VERSION_4) - // prior to version 4, there was no adjustment for the time the game - // was last played, so we have less to read. This needs update if the - // structure changes again. - utilGzRead(gzFile, &gbDataMBC3, sizeof(gbDataMBC3)-sizeof(time_t)); - else - utilGzRead(gzFile, &gbDataMBC3, sizeof(gbDataMBC3)); - utilGzRead(gzFile, &gbDataMBC5, sizeof(gbDataMBC5)); - utilGzRead(gzFile, &gbDataHuC1, sizeof(gbDataHuC1)); - utilGzRead(gzFile, &gbDataHuC3, sizeof(gbDataHuC3)); - if(version>=11) - { - utilGzRead(gzFile, &gbDataTAMA5, sizeof(gbDataTAMA5)); - if(gbTAMA5ram != NULL) { - if(skipSaveGameBattery) { - utilGzSeek(gzFile, gbTAMA5ramSize, SEEK_CUR); - } else { - utilGzRead(gzFile, gbTAMA5ram, gbTAMA5ramSize); - } - } - utilGzRead(gzFile, &gbDataMMM01, sizeof(gbDataMMM01)); - } - - if(version < GBSAVE_GAME_VERSION_5) { - utilGzRead(gzFile, pix, 256*224*sizeof(u16)); - } - memset(pix, 0, 257*226*sizeof(u32)); - - if(version < GBSAVE_GAME_VERSION_6) { - utilGzRead(gzFile, gbPalette, 64 * sizeof(u16)); - } else - utilGzRead(gzFile, gbPalette, 128 * sizeof(u16)); - - if (version < 11) - utilGzRead(gzFile, gbPalette, 128 * sizeof(u16)); - - if(version < GBSAVE_GAME_VERSION_10) { - if(!gbCgbMode && !gbSgbMode) { - for(int i = 0; i < 8; i++) - gbPalette[i] = systemGbPalette[gbPaletteOption*8+i]; - } - } - - utilGzRead(gzFile, &gbMemory[0x8000], 0x8000); - - if(gbRamSize && gbRam) { - if(version < 11) - if(skipSaveGameBattery) { - utilGzSeek(gzFile, gbRamSize, SEEK_CUR); //skip - } else { - utilGzRead(gzFile, gbRam, gbRamSize); //read - } - else - { - int ramSize = utilReadInt(gzFile); - if(skipSaveGameBattery) { - utilGzSeek(gzFile, (gbRamSize>ramSize) ? ramSize : gbRamSize, SEEK_CUR); //skip - } else { - utilGzRead(gzFile, gbRam, (gbRamSize>ramSize) ? ramSize : gbRamSize); //read - } - if(ramSize>gbRamSize) - utilGzSeek(gzFile,ramSize-gbRamSize,SEEK_CUR); - } - } - - memset(gbSCYLine, register_SCY, sizeof(gbSCYLine)); - memset(gbSCXLine, register_SCX, sizeof(gbSCXLine)); - memset(gbBgpLine, (gbBgp[0] | (gbBgp[1]<<2) | (gbBgp[2]<<4) | - (gbBgp[3]<<6)), sizeof(gbBgpLine)); - memset(gbObp0Line, (gbObp0[0] | (gbObp0[1]<<2) | (gbObp0[2]<<4) | - (gbObp0[3]<<6)), sizeof(gbObp0Line)); - memset(gbObp1Line, (gbObp1[0] | (gbObp1[1]<<2) | (gbObp1[2]<<4) | - (gbObp1[3]<<6)), sizeof(gbObp1Line)); - memset(gbSpritesTicks, 0x0, sizeof(gbSpritesTicks)); - - if (inBios) - { - gbMemoryMap[0x00] = &gbMemory[0x0000]; - memcpy ((u8 *)(gbMemory), (u8 *)(gbRom), 0x1000); - memcpy ((u8 *)(gbMemory), (u8 *)(bios), 0x100); - } - else - gbMemoryMap[0x00] = &gbRom[0x0000]; - gbMemoryMap[0x01] = &gbRom[0x1000]; - gbMemoryMap[0x02] = &gbRom[0x2000]; - gbMemoryMap[0x03] = &gbRom[0x3000]; - gbMemoryMap[0x04] = &gbRom[0x4000]; - gbMemoryMap[0x05] = &gbRom[0x5000]; - gbMemoryMap[0x06] = &gbRom[0x6000]; - gbMemoryMap[0x07] = &gbRom[0x7000]; - gbMemoryMap[0x08] = &gbMemory[0x8000]; - gbMemoryMap[0x09] = &gbMemory[0x9000]; - gbMemoryMap[0x0a] = &gbMemory[0xa000]; - gbMemoryMap[0x0b] = &gbMemory[0xb000]; - gbMemoryMap[0x0c] = &gbMemory[0xc000]; - gbMemoryMap[0x0d] = &gbMemory[0xd000]; - gbMemoryMap[0x0e] = &gbMemory[0xe000]; - gbMemoryMap[0x0f] = &gbMemory[0xf000]; - - switch(gbRomType) { - case 0x00: - case 0x01: - case 0x02: - case 0x03: - // MBC 1 - memoryUpdateMapMBC1(); - break; - case 0x05: - case 0x06: - // MBC2 - memoryUpdateMapMBC2(); - break; - case 0x0b: - case 0x0c: - case 0x0d: - // MMM01 - memoryUpdateMapMMM01(); - break; - case 0x0f: - case 0x10: - case 0x11: - case 0x12: - case 0x13: - // MBC 3 - memoryUpdateMapMBC3(); - break; - case 0x19: - case 0x1a: - case 0x1b: - // MBC5 - memoryUpdateMapMBC5(); - break; - case 0x1c: - case 0x1d: - case 0x1e: - // MBC 5 Rumble - memoryUpdateMapMBC5(); - break; - case 0x22: - // MBC 7 - memoryUpdateMapMBC7(); - break; - case 0x56: - // GS3 - memoryUpdateMapGS3(); - break; - case 0xfd: - // TAMA5 - memoryUpdateMapTAMA5(); - break; - case 0xfe: - // HuC3 - memoryUpdateMapHuC3(); - break; - case 0xff: - // HuC1 - memoryUpdateMapHuC1(); - break; - } - - if(gbCgbMode) { - utilGzRead(gzFile, gbVram, 0x4000); - utilGzRead(gzFile, gbWram, 0x8000); - - int value = register_SVBK; - if(value == 0) - value = 1; - - gbMemoryMap[0x08] = &gbVram[register_VBK * 0x2000]; - gbMemoryMap[0x09] = &gbVram[register_VBK * 0x2000 + 0x1000]; - gbMemoryMap[0x0d] = &gbWram[value * 0x1000]; - } - - gbSoundReadGame(version, gzFile); - - if (gbCgbMode && gbSgbMode) { - gbSgbMode = 0; - } - - if(gbBorderOn && !gbSgbMask) { - gbSgbRenderBorder(); - } - - systemDrawScreen(); - - if(version > GBSAVE_GAME_VERSION_1) - { - if( skipSaveGameCheats ) { - gbCheatsReadGameSkip(gzFile, version); - } else { - gbCheatsReadGame(gzFile, version); - } - } - - if (version<11) - { - gbWriteMemory(0xff00, 0); - gbMemory[0xff04] = register_DIV; - gbMemory[0xff05] = register_TIMA; - gbMemory[0xff06] = register_TMA; - gbMemory[0xff07] = register_TAC; - gbMemory[0xff40] = register_LCDC; - gbMemory[0xff42] = register_SCY; - gbMemory[0xff43] = register_SCX; - gbMemory[0xff44] = register_LY; - gbMemory[0xff45] = register_LYC; - gbMemory[0xff46] = register_DMA; - gbMemory[0xff4a] = register_WY; - gbMemory[0xff4b] = register_WX; - gbMemory[0xff4f] = register_VBK; - gbMemory[0xff51] = register_HDMA1; - gbMemory[0xff52] = register_HDMA2; - gbMemory[0xff53] = register_HDMA3; - gbMemory[0xff54] = register_HDMA4; - gbMemory[0xff55] = register_HDMA5; - gbMemory[0xff70] = register_SVBK; - gbMemory[0xffff] = register_IE; - GBDIV_CLOCK_TICKS = 64; - - if (gbSpeed) - gbDivTicks /=2; - - if ((gbLcdMode == 0) && (register_STAT & 8)) - gbInt48Signal |= 1; - if ((gbLcdMode == 1) && (register_STAT & 0x10)) - gbInt48Signal |= 2; - if ((gbLcdMode == 2) && (register_STAT & 0x20)) - gbInt48Signal |= 4; - if ((register_LY==register_LYC) && (register_STAT & 0x40)) - gbInt48Signal |= 8; - - gbLcdLYIncrementTicks = GBLY_INCREMENT_CLOCK_TICKS; - - if (gbLcdMode == 2) - gbLcdLYIncrementTicks-=GBLCD_MODE_2_CLOCK_TICKS-gbLcdTicks; - else if (gbLcdMode == 3) - gbLcdLYIncrementTicks -=GBLCD_MODE_2_CLOCK_TICKS+GBLCD_MODE_3_CLOCK_TICKS-gbLcdTicks; - else if (gbLcdMode == 0) - gbLcdLYIncrementTicks =gbLcdTicks; - else if (gbLcdMode == 1) - { - gbLcdLYIncrementTicks = gbLcdTicks % GBLY_INCREMENT_CLOCK_TICKS; - if (register_LY == 0x99) - gbLcdLYIncrementTicks =gbLine99Ticks; - else if (register_LY == 0) - gbLcdLYIncrementTicks += GBLY_INCREMENT_CLOCK_TICKS; - } - - gbLcdModeDelayed = gbLcdMode; - gbLcdTicksDelayed = gbLcdTicks--; - gbLcdLYIncrementTicksDelayed = gbLcdLYIncrementTicks--; - gbInterruptWait = 0; - memset(gbSpritesTicks,0,sizeof(gbSpritesTicks)); - } - else - { - gbLcdModeDelayed = utilReadInt(gzFile); - gbLcdTicksDelayed = utilReadInt(gzFile); - gbLcdLYIncrementTicksDelayed = utilReadInt(gzFile); - gbSpritesTicks[299] = utilReadInt(gzFile) & 0xff; - gbTimerModeChange = (utilReadInt(gzFile) ? true : false); - gbTimerOnChange = (utilReadInt(gzFile) ? true : false); - gbHardware = utilReadInt(gzFile); - gbBlackScreen = (utilReadInt(gzFile) ? true : false); - oldRegister_WY = utilReadInt(gzFile); - gbWindowLine = utilReadInt(gzFile); - inUseRegister_WY = utilReadInt(gzFile); - gbScreenOn = (utilReadInt(gzFile) ? true : false); - } - - if (gbSpeed) - gbLine99Ticks *= 2; - - systemSaveUpdateCounter = SYSTEM_SAVE_NOT_UPDATED; - - if ( version >= 12 && utilReadInt( gzFile ) != 0x12345678 ) - assert( false ); // fails if something read too much/little from file - - return true; -} - -bool gbReadMemSaveState(char *memory, int available) -{ - gzFile gzFile = utilMemGzOpen(memory, available, "r"); - - bool res = gbReadSaveState(gzFile); - - utilGzClose(gzFile); - - return res; -} - -bool gbReadSaveState(const char *name) -{ - gzFile gzFile = utilGzOpen(name,"rb"); - - if(gzFile == NULL) { - return false; - } - - bool res = gbReadSaveState(gzFile); - - utilGzClose(gzFile); - - return res; -} - -bool gbWritePNGFile(const char *fileName) -{ - if(gbBorderOn) - return utilWritePNGFile(fileName, 256, 224, pix); - return utilWritePNGFile(fileName, 160, 144, pix); -} - -bool gbWriteBMPFile(const char *fileName) -{ - if(gbBorderOn) - return utilWriteBMPFile(fileName, 256, 224, pix); - return utilWriteBMPFile(fileName, 160, 144, pix); -} - -void gbCleanUp() -{ - if(gbRam != NULL) { - free(gbRam); - gbRam = NULL; - } - - if(gbRom != NULL) { - free(gbRom); - gbRom = NULL; - } - - if(bios != NULL) { - free(bios); - bios = NULL; - } - - if(gbMemory != NULL) { - free(gbMemory); - gbMemory = NULL; - } - - if(gbLineBuffer != NULL) { - free(gbLineBuffer); - gbLineBuffer = NULL; - } - - if(pix != NULL) { - free(pix); - pix = NULL; - } - - gbSgbShutdown(); - - if(gbVram != NULL) { - free(gbVram); - gbVram = NULL; - } - - if(gbWram != NULL) { - free(gbWram); - gbWram = NULL; - } - - if(gbTAMA5ram != NULL) { - free(gbTAMA5ram); - gbTAMA5ram = NULL; - } - - systemSaveUpdateCounter = SYSTEM_SAVE_NOT_UPDATED; -} - -bool gbLoadRom(const char *szFile) -{ - int size = 0; - - if(gbRom != NULL) { - gbCleanUp(); - } - - systemSaveUpdateCounter = SYSTEM_SAVE_NOT_UPDATED; - - gbRom = utilLoad(szFile, - utilIsGBImage, - NULL, - size); - if(!gbRom) - return false; - - gbRomSize = size; - - gbBatteryError = false; - - if(bios != NULL) { - free(bios); - bios = NULL; - } - bios = (u8 *)calloc(1,0x100); - - return gbUpdateSizes(); -} - -bool gbUpdateSizes() -{ - if(gbRom[0x148] > 8) { - systemMessage(MSG_UNSUPPORTED_ROM_SIZE, - N_("Unsupported rom size %02x"), gbRom[0x148]); - return false; - } - - if(gbRomSize < gbRomSizes[gbRom[0x148]]) { - u8 *gbRomNew = (u8 *)realloc(gbRom, gbRomSizes[gbRom[0x148]]); - if( !gbRomNew ) { assert( false ); return false; }; - gbRom = gbRomNew; - for (int i = gbRomSize; igbRomSizes[gbRom[0x148]]) && (genericflashcardEnable)) - { - gbRomSize = gbRomSize>>16; - gbRom[0x148] = 0; - if (gbRomSize) - { - while (!((gbRomSize & 1) || (gbRom[0x148] == 7))) - { - gbRom[0x148]++; - gbRomSize>>=1; - } - gbRom[0x148]++; - } - u8 *gbRomNew = (u8 *)realloc(gbRom, gbRomSizes[gbRom[0x148]]); - if( !gbRomNew ) { assert( false ); return false; }; - gbRom = gbRomNew; - } - gbRomSize = gbRomSizes[gbRom[0x148]]; - gbRomSizeMask = gbRomSizesMasks[gbRom[0x148]]; - - - // The 'genericflashcard' option allows some PD to work. - // However, the setting is dangerous (if you let in enabled - // and play a normal game, it might just break everything). - // That's why it is not saved in the emulator options. - // Also I added some checks in VBA to make sure your saves will not be - // overwritten if you wrongly enable this option for a game - // you already played (and vice-versa, ie. if you forgot to - // enable the option for a game you played with it enabled, like Shawu Story). - u8 ramsize = genericflashcardEnable ? 5 : gbRom[0x149]; - gbRom[0x149] = ramsize; - - if ((gbRom[2] == 0x6D) && (gbRom[5] == 0x47) && (gbRom[6] == 0x65) && (gbRom[7] == 0x6E) && - (gbRom[8] == 0x69) && (gbRom[9] == 0x65) && (gbRom[0xA] == 0x28) && (gbRom[0xB] == 0x54)) - { - gbCheatingDevice = 1; // GameGenie - for (int i = 0; i < 0x20; i++) // Cleans GG hardware registers - gbRom[0x4000+i] = 0; - } - else if (((gbRom[0x104] == 0x44) && (gbRom[0x156] == 0xEA) && (gbRom[0x158] == 0x7F) && - (gbRom[0x159] == 0xEA) && (gbRom[0x15B] == 0x7F)) || ((gbRom[0x165] == 0x3E) && - (gbRom[0x166] == 0xD9) && (gbRom[0x16D] == 0xE1) && (gbRom[0x16E] == 0x7F))) - gbCheatingDevice = 2; // GameShark - else gbCheatingDevice = 0; - - if(ramsize > 5) { - systemMessage(MSG_UNSUPPORTED_RAM_SIZE, - N_("Unsupported ram size %02x"), gbRom[0x149]); - return false; - } - - gbRamSize = gbRamSizes[ramsize]; - gbRamSizeMask = gbRamSizesMasks[ramsize]; - - gbRomType = gbRom[0x147]; - if (genericflashcardEnable) - { - /*if (gbRomType<2) - gbRomType =3; - else if ((gbRomType == 0xc) || (gbRomType == 0xf) || (gbRomType == 0x12) || - (gbRomType == 0x16) || (gbRomType == 0x1a) || (gbRomType == 0x1d)) - gbRomType++; - else if ((gbRomType == 0xb) || (gbRomType == 0x11) || (gbRomType == 0x15) || - (gbRomType == 0x19) || (gbRomType == 0x1c)) - gbRomType+=2; - else if ((gbRomType == 0x5) || (gbRomType == 0x6)) - gbRomType = 0x1a;*/ - gbRomType = 0x1b; - } - else if (gbCheatingDevice == 1) - gbRomType = 0x55; - else if (gbCheatingDevice == 2) - gbRomType = 0x56; - - gbRom[0x147] = gbRomType; - - mapperReadRAM = NULL; - - switch(gbRomType) { - case 0x00: - case 0x01: - case 0x02: - case 0x03: - case 0x08: - case 0x09: - // MBC 1 - mapper = mapperMBC1ROM; - mapperRAM = mapperMBC1RAM; - mapperReadRAM = mapperMBC1ReadRAM; - break; - case 0x05: - case 0x06: - // MBC2 - mapper = mapperMBC2ROM; - mapperRAM = mapperMBC2RAM; - gbRamSize = 0x200; - gbRamSizeMask = 0x1ff; - break; - case 0x0b: - case 0x0c: - case 0x0d: - // MMM01 - mapper = mapperMMM01ROM; - mapperRAM = mapperMMM01RAM; - break; - case 0x0f: - case 0x10: - case 0x11: - case 0x12: - case 0x13: - case 0xfc: - // MBC 3 - mapper = mapperMBC3ROM; - mapperRAM = mapperMBC3RAM; - mapperReadRAM = mapperMBC3ReadRAM; - break; - case 0x19: - case 0x1a: - case 0x1b: - // MBC5 - mapper = mapperMBC5ROM; - mapperRAM = mapperMBC5RAM; - mapperReadRAM = mapperMBC5ReadRAM; - break; - case 0x1c: - case 0x1d: - case 0x1e: - // MBC 5 Rumble - mapper = mapperMBC5ROM; - mapperRAM = mapperMBC5RAM; - mapperReadRAM = mapperMBC5ReadRAM; - break; - case 0x22: - // MBC 7 - mapper = mapperMBC7ROM; - mapperRAM = mapperMBC7RAM; - mapperReadRAM = mapperMBC7ReadRAM; - gbRamSize = 0x200; - gbRamSizeMask = 0x1ff; - break; - // GG (GameGenie) - case 0x55: - mapper = mapperGGROM; - break; - case 0x56: - // GS (GameShark) - mapper = mapperGS3ROM; - break; - case 0xfd: - // TAMA5 - if (gbRam!= NULL) - { - free(gbRam); - gbRam = NULL; - } - - ramsize = 3; - gbRamSize = gbRamSizes[3]; - gbRamSizeMask = gbRamSizesMasks[3]; - gbRamFill = 0x0; - - gbTAMA5ramSize = 0x100; - - if (gbTAMA5ram == NULL) - gbTAMA5ram = (u8 *)malloc(gbTAMA5ramSize); - memset(gbTAMA5ram, 0x0, gbTAMA5ramSize); - - mapperRAM = mapperTAMA5RAM; - mapperReadRAM = mapperTAMA5ReadRAM; - mapperUpdateClock = memoryUpdateTAMA5Clock; - break; - case 0xfe: - // HuC3 - mapper = mapperHuC3ROM; - mapperRAM = mapperHuC3RAM; - mapperReadRAM = mapperHuC3ReadRAM; - break; - case 0xff: - // HuC1 - mapper = mapperHuC1ROM; - mapperRAM = mapperHuC1RAM; - break; - default: - systemMessage(MSG_UNKNOWN_CARTRIDGE_TYPE, - N_("Unknown cartridge type %02x"), gbRomType); - return false; - } - - if(gbRamSize) { - gbRam = (u8 *)malloc(gbRamSize); - memset(gbRam, gbRamFill, gbRamSize); - } - - switch(gbRomType) { - case 0x03: - case 0x06: - case 0x0f: - case 0x10: - case 0x13: - case 0x1b: - case 0x1d: - case 0x1e: - case 0x22: - case 0xfd: - case 0xff: - gbBattery = 1; - break; - } - - gbInit(); - - //gbReset(); - - switch(gbRomType) { - case 0x1c: - case 0x1d: - case 0x1e: - gbDataMBC5.isRumbleCartridge = 1; - } - - return true; -} - -int gbGetNextEvent (int _clockTicks) -{ - if (register_LCDC & 0x80) - { - if(gbLcdTicks < _clockTicks) - _clockTicks = gbLcdTicks; - - if(gbLcdTicksDelayed < _clockTicks) - _clockTicks = gbLcdTicksDelayed; - - if(gbLcdLYIncrementTicksDelayed < _clockTicks) - _clockTicks = gbLcdLYIncrementTicksDelayed; - } - - if(gbLcdLYIncrementTicks < _clockTicks) - _clockTicks = gbLcdLYIncrementTicks; - - if(gbSerialOn && (gbSerialTicks < _clockTicks)) - _clockTicks = gbSerialTicks; - - if(gbTimerOn && (((gbInternalTimer) & gbTimerMask[gbTimerMode])+1 < _clockTicks)) - _clockTicks = ((gbInternalTimer) & gbTimerMask[gbTimerMode])+1; - - //if(soundTicks && (soundTicks < _clockTicks)) - // _clockTicks = soundTicks; - - if ((_clockTicks<=0) || (gbInterruptWait)) - _clockTicks = 1; - - return _clockTicks; -} - -void gbDrawLine() -{ - switch(systemColorDepth) { - case 16: - { - u16 * dest = (u16 *)pix + - (gbBorderLineSkip+2) * (register_LY + gbBorderRowSkip+1) - + gbBorderColumnSkip; - for(int x = 0; x < 160; ) { - *dest++ = systemColorMap16[gbLineMix[x++]]; - *dest++ = systemColorMap16[gbLineMix[x++]]; - *dest++ = systemColorMap16[gbLineMix[x++]]; - *dest++ = systemColorMap16[gbLineMix[x++]]; - - *dest++ = systemColorMap16[gbLineMix[x++]]; - *dest++ = systemColorMap16[gbLineMix[x++]]; - *dest++ = systemColorMap16[gbLineMix[x++]]; - *dest++ = systemColorMap16[gbLineMix[x++]]; - - *dest++ = systemColorMap16[gbLineMix[x++]]; - *dest++ = systemColorMap16[gbLineMix[x++]]; - *dest++ = systemColorMap16[gbLineMix[x++]]; - *dest++ = systemColorMap16[gbLineMix[x++]]; - - *dest++ = systemColorMap16[gbLineMix[x++]]; - *dest++ = systemColorMap16[gbLineMix[x++]]; - *dest++ = systemColorMap16[gbLineMix[x++]]; - *dest++ = systemColorMap16[gbLineMix[x++]]; - } - if(gbBorderOn) - dest += gbBorderColumnSkip; - *dest++ = 0; // for filters that read one pixel more - } - break; - - case 24: - { - u8 *dest = (u8 *)pix + - 3*(gbBorderLineSkip * (register_LY + gbBorderRowSkip) + - gbBorderColumnSkip); - for(int x = 0; x < 160;) { - *((u32 *)dest) = systemColorMap32[gbLineMix[x++]]; - dest+= 3; - *((u32 *)dest) = systemColorMap32[gbLineMix[x++]]; - dest+= 3; - *((u32 *)dest) = systemColorMap32[gbLineMix[x++]]; - dest+= 3; - *((u32 *)dest) = systemColorMap32[gbLineMix[x++]]; - dest+= 3; - - *((u32 *)dest) = systemColorMap32[gbLineMix[x++]]; - dest+= 3; - *((u32 *)dest) = systemColorMap32[gbLineMix[x++]]; - dest+= 3; - *((u32 *)dest) = systemColorMap32[gbLineMix[x++]]; - dest+= 3; - *((u32 *)dest) = systemColorMap32[gbLineMix[x++]]; - dest+= 3; - - *((u32 *)dest) = systemColorMap32[gbLineMix[x++]]; - dest+= 3; - *((u32 *)dest) = systemColorMap32[gbLineMix[x++]]; - dest+= 3; - *((u32 *)dest) = systemColorMap32[gbLineMix[x++]]; - dest+= 3; - *((u32 *)dest) = systemColorMap32[gbLineMix[x++]]; - dest+= 3; - - *((u32 *)dest) = systemColorMap32[gbLineMix[x++]]; - dest+= 3; - *((u32 *)dest) = systemColorMap32[gbLineMix[x++]]; - dest+= 3; - *((u32 *)dest) = systemColorMap32[gbLineMix[x++]]; - dest+= 3; - *((u32 *)dest) = systemColorMap32[gbLineMix[x++]]; - dest+= 3; - } - } - break; - - case 32: - { - u32 * dest = (u32 *)pix + - (gbBorderLineSkip+1) * (register_LY + gbBorderRowSkip+1) - + gbBorderColumnSkip; - for(int x = 0; x < 160;) { - *dest++ = systemColorMap32[gbLineMix[x++]]; - *dest++ = systemColorMap32[gbLineMix[x++]]; - *dest++ = systemColorMap32[gbLineMix[x++]]; - *dest++ = systemColorMap32[gbLineMix[x++]]; - - *dest++ = systemColorMap32[gbLineMix[x++]]; - *dest++ = systemColorMap32[gbLineMix[x++]]; - *dest++ = systemColorMap32[gbLineMix[x++]]; - *dest++ = systemColorMap32[gbLineMix[x++]]; - - *dest++ = systemColorMap32[gbLineMix[x++]]; - *dest++ = systemColorMap32[gbLineMix[x++]]; - *dest++ = systemColorMap32[gbLineMix[x++]]; - *dest++ = systemColorMap32[gbLineMix[x++]]; - - *dest++ = systemColorMap32[gbLineMix[x++]]; - *dest++ = systemColorMap32[gbLineMix[x++]]; - *dest++ = systemColorMap32[gbLineMix[x++]]; - *dest++ = systemColorMap32[gbLineMix[x++]]; - } - } - break; - } -} - -void gbEmulate(int ticksToStop) -{ - gbRegister tempRegister; - u8 tempValue; - s8 offset; - - clockTicks = 0; - gbDmaTicks = 0; - - register int opcode = 0; - - int opcode1 = 0; - int opcode2 = 0; - bool execute = false; - - while(1) { -#ifndef FINAL_VERSION - if(systemDebug) { - if(!(IFF & 0x80)) { - if(systemDebug > 1) { - sprintf(gbBuffer,"PC=%04x AF=%04x BC=%04x DE=%04x HL=%04x SP=%04x I=%04x\n", - PC.W, AF.W, BC.W, DE.W,HL.W,SP.W,IFF); - } else { - sprintf(gbBuffer,"PC=%04x I=%02x\n", PC.W, IFF); - } - log(gbBuffer); - } - } -#endif - - u16 oldPCW = PC.W; - - if(IFF & 0x80) { - if(register_LCDC & 0x80) { - clockTicks = gbLcdTicks; - } else - clockTicks = 1000; - - clockTicks = gbGetNextEvent(clockTicks); - - /*if(gbLcdTicksDelayed < clockTicks) - clockTicks = gbLcdTicksDelayed; - - if(gbLcdLYIncrementTicksDelayed < clockTicks) - clockTicks = gbLcdLYIncrementTicksDelayed; - - if(gbLcdLYIncrementTicks < clockTicks) - clockTicks = gbLcdLYIncrementTicks; - - if(gbSerialOn && (gbSerialTicks < clockTicks)) - clockTicks = gbSerialTicks; - - if(gbTimerOn && (((gbInternalTimer) & gbTimerMask[gbTimerMode])+1 < clockTicks)) - clockTicks = ((gbInternalTimer) & gbTimerMask[gbTimerMode])+1; - - if(soundTicks && (soundTicks < clockTicks)) - clockTicks = soundTicks; - - if ((clockTicks<=0) || (gbInterruptWait)) - clockTicks = 1;*/ - - } else { - - // First we apply the clockTicks, then we execute the opcodes. - opcode1 = 0; - opcode2 = 0; - execute = true; - - opcode2 = opcode1 = opcode = gbReadOpcode(PC.W++); - - // If HALT state was launched while IME = 0 and (register_IF & register_IE & 0x1F), - // PC.W is not incremented for the first byte of the next instruction. - if (IFF & 2) - { - PC.W--; - IFF &= ~2; - } - - clockTicks = gbCycles[opcode]; - - switch(opcode) { - case 0xCB: - // extended opcode - opcode2 = opcode = gbReadOpcode(PC.W++); - clockTicks = gbCyclesCB[opcode]; - break; - } - gbOldClockTicks = clockTicks-1; - gbIntBreak = 1; - } - - - if(!emulating) - return; - - // For 'breakpoint' support (opcode 0xFC is considered as a breakpoint) - if ((clockTicks==0) && execute) - { - PC.W = oldPCW; - return; - } - - - if (!(IFF & 0x80)) - clockTicks = 1; - - gbRedoLoop: - - - - if (gbInterruptWait) - gbInterruptWait = 0; - else - gbInterruptLaunched = 0; - - - // Used for the EI/DI instruction's delay. - if (IFF & 0x38) - { - int tempIFF = (IFF >> 4) & 3; - - if (tempIFF <=clockTicks) - { - tempIFF = 0; - IFF |=1; - } - else - tempIFF -= clockTicks; - IFF = (IFF & 0xCF) | (tempIFF <<4); - - if (IFF & 0x08) - IFF &= 0x82; - } - - - if (register_LCDCBusy) - { - register_LCDCBusy-=clockTicks; - if (register_LCDCBusy<0) - register_LCDCBusy = 0; - } - - - if(gbSgbMode) { - if(gbSgbPacketTimeout) { - gbSgbPacketTimeout -= clockTicks; - - if(gbSgbPacketTimeout <= 0) - gbSgbResetPacketState(); - } - } - - ticksToStop -= clockTicks; - - // DIV register emulation - gbDivTicks -= clockTicks; - while(gbDivTicks <= 0) { - gbMemory[0xff04] = ++register_DIV; - gbDivTicks += GBDIV_CLOCK_TICKS; - } - - if(register_LCDC & 0x80) { - // LCD stuff - - gbLcdTicks -= clockTicks; - gbLcdTicksDelayed -= clockTicks; - gbLcdLYIncrementTicks -= clockTicks; - gbLcdLYIncrementTicksDelayed -= clockTicks; - - - // our counters are off, see what we need to do - - // This looks (and kinda is) complicated, however this - // is the only way I found to emulate properly the way - // the real hardware operates... - while(((gbLcdTicks <= 0) && (gbLCDChangeHappened == false)) || - ((gbLcdTicksDelayed <= 0) && (gbLCDChangeHappened == true)) || - ((gbLcdLYIncrementTicks <= 0) && (gbLYChangeHappened == false)) || - ((gbLcdLYIncrementTicksDelayed<=0) && (gbLYChangeHappened == true))) - { - - if ((gbLcdLYIncrementTicks <= 0) && (!gbLYChangeHappened)) - { - gbLYChangeHappened = true; - gbMemory[0xff44] = register_LY = (register_LY + 1) % 154; - - if (register_LY == 0x91) - { - /* if (IFF & 0x80) - gbScreenOn = !gbScreenOn; - else*/ if (register_LCDC & 0x80) - gbScreenOn = true; - } - - gbLcdLYIncrementTicks += GBLY_INCREMENT_CLOCK_TICKS; - - if (gbLcdMode == 1) - { - - if(register_LY == 153) - gbLcdLYIncrementTicks -= GBLY_INCREMENT_CLOCK_TICKS - gbLine99Ticks; - else if(register_LY == 0) - gbLcdLYIncrementTicks += GBLY_INCREMENT_CLOCK_TICKS - gbLine99Ticks; - } - - // GB only 'bug' : Halt state is broken one tick before LY==LYC interrupt - // is reflected in the registers. - if ((gbHardware & 5) && (IFF & 0x80) && (register_LY == register_LYC) - && (register_STAT & 0x40) && (register_LY != 0)) - { - if (!((gbLcdModeDelayed != 1) && (register_LY==0))) - { - gbInt48Signal &= ~9; - gbCompareLYToLYC(); - gbLYChangeHappened = false; - gbMemory[0xff41] = register_STAT; - gbMemory[0xff0f] = register_IF; - } - - gbLcdLYIncrementTicksDelayed += GBLY_INCREMENT_CLOCK_TICKS - gbLine99Ticks+1; - } - } - - - if ((gbLcdTicks <= 0) && (!gbLCDChangeHappened)) - { - gbLCDChangeHappened = true; - - switch(gbLcdMode) - { - case 0: - { - // H-Blank - // check if we reached the V-Blank period - if(register_LY == 144) { - // Yes, V-Blank - // set the LY increment counter - if (gbHardware & 0x5) - { - register_IF |= 1; // V-Blank interrupt - } - - gbInt48Signal &= ~6; - if(register_STAT & 0x10) - { - // send LCD interrupt only if no interrupt 48h signal... - if ((!(gbInt48Signal & 1)) && ((!(gbInt48Signal & 8)) || (gbHardware & 0x0a))) - { - register_IF |=2; - gbInterruptLaunched |= 2; - if (gbHardware & 0xa) - gbInterruptWait = 1; - } - gbInt48Signal |= 2; - } - gbInt48Signal &= ~1; - - gbLcdTicks += GBLCD_MODE_1_CLOCK_TICKS; - gbLcdMode = 1; - - } else { - // go the the OAM being accessed mode - gbLcdTicks += GBLCD_MODE_2_CLOCK_TICKS; - gbLcdMode = 2; - - gbInt48Signal &= ~6; - if(register_STAT & 0x20) - { - // send LCD interrupt only if no interrupt 48h signal... - if (!gbInt48Signal) - { - register_IF |= 2; - gbInterruptLaunched |= 2; - } - gbInt48Signal |= 4; - } - gbInt48Signal &= ~1; - } - } - break; - case 1: - { - // V-Blank - // next mode is OAM being accessed mode - gbInt48Signal &= ~5; - if(register_STAT & 0x20) - { - // send LCD interrupt only if no interrupt 48h signal... - if (!gbInt48Signal) - { - register_IF |= 2; - gbInterruptLaunched |= 2; - if ((gbHardware & 0xa) && (IFF & 0x80)) - gbInterruptWait = 1; - } - gbInt48Signal |= 4; - } - gbInt48Signal &= ~2; - - gbLcdTicks += GBLCD_MODE_2_CLOCK_TICKS; - - gbLcdMode = 2; - register_LY = 0x00; - - } - break; - case 2: - { - - // OAM being accessed mode - // next mode is OAM and VRAM in use - if ((gbScreenOn) && (register_LCDC & 0x80)) - { - gbDrawSprites(false); - // Used to add a one tick delay when a window line is drawn. - //(fixes a part of Carmaggedon problem) - if((register_LCDC & 0x01 || gbCgbMode) && (register_LCDC & 0x20) && - (gbWindowLine != -2)) { - - int inUseRegister_WY = 0; - int tempgbWindowLine = gbWindowLine; - - if ((tempgbWindowLine == -1) || (tempgbWindowLine>144)) - { - inUseRegister_WY = oldRegister_WY; - if (register_LY>oldRegister_WY) - tempgbWindowLine = 146; - } - - if(register_LY >= inUseRegister_WY) { - - if (tempgbWindowLine == -1) - tempgbWindowLine = 0; - - int wx = register_WX; - wx -= 7; - if (wx<0) - wx = 0; - - if((wx <= 159) && (tempgbWindowLine <= 143)) - for (int i = wx; i<300; i++) - if (gbSpeed) - gbSpritesTicks[i]+=3; - else - gbSpritesTicks[i]+=1; - } - } - } - - gbInt48Signal &= ~7; - - gbLcdTicks += GBLCD_MODE_3_CLOCK_TICKS+gbSpritesTicks[299]; - gbLcdMode = 3; - } - break; - case 3: - { - // OAM and VRAM in use - // next mode is H-Blank - - - gbInt48Signal &= ~7; - if(register_STAT & 0x08) - { - // send LCD interrupt only if no interrupt 48h signal... - if (!(gbInt48Signal & 8)) - { - register_IF |= 2; - if ((gbHardware & 0xa) && (IFF & 0x80)) - gbInterruptWait = 1; - } - gbInt48Signal |= 1; - } - - gbLcdTicks += GBLCD_MODE_0_CLOCK_TICKS-gbSpritesTicks[299]; - - gbLcdMode = 0; - - // No HDMA during HALT ! - if(gbHdmaOn && (!(IFF & 0x80) || (register_IE & register_IF & 0x1f))) { - gbDoHdma(); - } - - } - break; - } - } - - - if ((gbLcdTicksDelayed <= 0) && (gbLCDChangeHappened)) { - int framesToSkip = systemFrameSkip; - if(speedup) - framesToSkip = 9; // try 6 FPS during speedup - //gbLcdTicksDelayed = gbLcdTicks+1; - gbLCDChangeHappened = false; - switch(gbLcdModeDelayed) { - case 0: - { - // H-Blank - - memset(gbSCYLine,gbSCYLine[299],sizeof(gbSCYLine)); - memset(gbSCXLine,gbSCXLine[299],sizeof(gbSCXLine)); - memset(gbBgpLine,gbBgpLine[299],sizeof(gbBgpLine)); - memset(gbObp0Line,gbObp0Line[299],sizeof(gbObp0Line)); - memset(gbObp1Line,gbObp1Line[299],sizeof(gbObp1Line)); - memset(gbSpritesTicks,gbSpritesTicks[299],sizeof(gbSpritesTicks)); - - if (gbWindowLine <0) - oldRegister_WY = register_WY; - // check if we reached the V-Blank period - if(register_LY == 144) { - // Yes, V-Blank - // set the LY increment counter - - if(register_LCDC & 0x80) { - if (gbHardware & 0xa) - { - - register_IF |= 1; // V-Blank interrupt - gbInterruptLaunched |=1; - } - - - } - - gbLcdTicksDelayed += GBLCD_MODE_1_CLOCK_TICKS; - gbLcdModeDelayed = 1; - - gbFrameCount++; - systemFrame(); - - if((gbFrameCount % 10) == 0) - system10Frames(60); - - if(gbFrameCount >= 60) { - u32 currentTime = systemGetClock(); - if(currentTime != gbLastTime) - systemShowSpeed(100000/(currentTime - gbLastTime)); - else - systemShowSpeed(0); - gbLastTime = currentTime; - gbFrameCount = 0; - } - - if(systemReadJoypads()) { - // read joystick - if(gbSgbMode && gbSgbMultiplayer) { - if(gbSgbFourPlayers) { - gbJoymask[0] = systemReadJoypad(0); - gbJoymask[1] = systemReadJoypad(1); - gbJoymask[2] = systemReadJoypad(2); - gbJoymask[3] = systemReadJoypad(3); - } else { - gbJoymask[0] = systemReadJoypad(0); - gbJoymask[1] = systemReadJoypad(1); - } - } else { - gbJoymask[0] = systemReadJoypad(-1); - } - } - int newmask = gbJoymask[0] & 255; - - if(gbRomType == 0x22) { - systemUpdateMotionSensor(); - } - - if(newmask) - { - gbMemory[0xff0f] = register_IF |= 16; - } - - - newmask = (gbJoymask[0] >> 10); - - speedup = (newmask & 1) ? true : false; - gbCapture = (newmask & 2) ? true : false; - - if(gbCapture && !gbCapturePrevious) { - gbCaptureNumber++; - systemScreenCapture(gbCaptureNumber); - } - gbCapturePrevious = gbCapture; - - if(gbFrameSkipCount >= framesToSkip) { - - if(!gbSgbMask) - { - if (gbBorderOn) - gbSgbRenderBorder(); - //if (gbScreenOn) - systemDrawScreen(); - } - gbFrameSkipCount = 0; - } else - gbFrameSkipCount++; - - } else { - // go the the OAM being accessed mode - gbLcdTicksDelayed += GBLCD_MODE_2_CLOCK_TICKS; - gbLcdModeDelayed = 2; - gbInt48Signal &= ~3; - } - } - break; - case 1: - { - // V-Blank - // next mode is OAM being accessed mode - - // gbScreenOn = true; - - oldRegister_WY = register_WY; - - gbLcdTicksDelayed += GBLCD_MODE_2_CLOCK_TICKS; - gbLcdModeDelayed = 2; - - // reset the window line - gbWindowLine = -1; - } - break; - case 2: - { - // OAM being accessed mode - // next mode is OAM and VRAM in use - gbLcdTicksDelayed += GBLCD_MODE_3_CLOCK_TICKS+gbSpritesTicks[299]; - gbLcdModeDelayed = 3; - } - break; - case 3: - { - - // OAM and VRAM in use - // next mode is H-Blank - if((register_LY < 144) && (register_LCDC & 0x80) && gbScreenOn) { - if(!gbSgbMask) { - if(gbFrameSkipCount >= framesToSkip) { - if (!gbBlackScreen) - { - gbRenderLine(); - gbDrawSprites(true); - } - else if (gbBlackScreen) - { - u16 color = gbColorOption ? gbColorFilter[0] : 0; - if (!gbCgbMode) - color = gbColorOption ? gbColorFilter[gbPalette[3] & 0x7FFF] : - gbPalette[3] & 0x7FFF; - for(int i = 0; i < 160; i++) - { - gbLineMix[i] = color; - gbLineBuffer[i] = 0; - } - } - gbDrawLine(); - } - } - } - gbLcdTicksDelayed += GBLCD_MODE_0_CLOCK_TICKS-gbSpritesTicks[299]; - gbLcdModeDelayed = 0; - } - break; - } - } - - if ((gbLcdLYIncrementTicksDelayed <= 0) && (gbLYChangeHappened == true)) - { - - gbLYChangeHappened = false; - - if (!((gbLcdMode != 1) && (register_LY==0))) - { - { - gbInt48Signal &= ~8; - gbCompareLYToLYC(); - if ((gbInt48Signal == 8) && (!((register_LY == 0) && (gbHardware & 1)))) - gbInterruptLaunched |= 2; - if ((gbHardware & (gbSpeed ? 8 : 2)) && (register_LY==0) && ((register_STAT & 0x44) == 0x44) && (gbLcdLYIncrementTicksDelayed==0)) - { - gbInterruptWait = 1; - - } - } - } - gbLcdLYIncrementTicksDelayed += GBLY_INCREMENT_CLOCK_TICKS; - - if (gbLcdModeDelayed == 1) - { - - if(register_LY == 153) - gbLcdLYIncrementTicksDelayed -= GBLY_INCREMENT_CLOCK_TICKS - gbLine99Ticks; - else if(register_LY == 0) - gbLcdLYIncrementTicksDelayed += GBLY_INCREMENT_CLOCK_TICKS - gbLine99Ticks; - } - gbMemory[0xff0f] = register_IF; - gbMemory[0xff41] = register_STAT; - } - } - gbMemory[0xff0f] = register_IF; - gbMemory[0xff41] = register_STAT = (register_STAT & 0xfc) | gbLcdModeDelayed; - } - else - { - - // Used to update the screen with white lines when it's off. - // (it looks strange, but it's kinda accurate :p) - // You can try the Mario Demo Vx.x for exemple - // (check the bottom 2 lines while moving) - if (!gbWhiteScreen) - { - gbScreenTicks -= clockTicks; - gbLcdLYIncrementTicks -= clockTicks; - while (gbLcdLYIncrementTicks <=0) - { - register_LY = ((register_LY+1)%154); - gbLcdLYIncrementTicks+=GBLY_INCREMENT_CLOCK_TICKS; - } - if (gbScreenTicks <= 0) - { - gbWhiteScreen = 1; - u8 register_LYLcdOff = ((register_LY+154)%154); - for (register_LY=0;register_LY <= 0x90;register_LY++) - { - u16 color = gbColorOption ? gbColorFilter[0x7FFF] : - 0x7FFF; - if (!gbCgbMode) - color = gbColorOption ? gbColorFilter[gbPalette[0] & 0x7FFF] : - gbPalette[0] & 0x7FFF; - for(int i = 0; i < 160; i++) - { - gbLineMix[i] = color; - gbLineBuffer[i] = 0; - } - gbDrawLine(); - } - register_LY = register_LYLcdOff; - } - } - - if (gbWhiteScreen) - { - gbLcdLYIncrementTicks -= clockTicks; - - while (gbLcdLYIncrementTicks <=0) - { - register_LY = ((register_LY+1)%154); - gbLcdLYIncrementTicks+=GBLY_INCREMENT_CLOCK_TICKS; - if (register_LY<144) - { - - u16 color = gbColorOption ? gbColorFilter[0x7FFF] : - 0x7FFF; - if (!gbCgbMode) - color = gbColorOption ? gbColorFilter[gbPalette[0] & 0x7FFF] : - gbPalette[0] & 0x7FFF; - for(int i = 0; i < 160; i++) - { - gbLineMix[i] = color; - gbLineBuffer[i] = 0; - } - gbDrawLine(); - } - else if ((register_LY==144) && (!systemFrameSkip)) - { - int framesToSkip = systemFrameSkip; - if(speedup) - framesToSkip = 9; // try 6 FPS during speedup - if((gbFrameSkipCount >= framesToSkip) || (gbWhiteScreen == 1)) { - gbWhiteScreen = 2; - - if(!gbSgbMask) - { - if (gbBorderOn) - gbSgbRenderBorder(); - //if (gbScreenOn) - systemDrawScreen(); - } - } - if(systemReadJoypads()) { - // read joystick - if(gbSgbMode && gbSgbMultiplayer) { - if(gbSgbFourPlayers) { - gbJoymask[0] = systemReadJoypad(0); - gbJoymask[1] = systemReadJoypad(1); - gbJoymask[2] = systemReadJoypad(2); - gbJoymask[3] = systemReadJoypad(3); - } else { - gbJoymask[0] = systemReadJoypad(0); - gbJoymask[1] = systemReadJoypad(1); - } - } else { - gbJoymask[0] = systemReadJoypad(-1); - } - } - gbFrameCount++; - - systemFrame(); - - if((gbFrameCount % 10) == 0) - system10Frames(60); - - if(gbFrameCount >= 60) { - u32 currentTime = systemGetClock(); - if(currentTime != gbLastTime) - systemShowSpeed(100000/(currentTime - gbLastTime)); - else - systemShowSpeed(0); - gbLastTime = currentTime; - gbFrameCount = 0; - } - } - } - } - } - - gbMemory[0xff41] = register_STAT; - - // serial emulation - if(gbSerialOn) { -#ifdef OLD_GB_LINK - if(linkConnected) { - gbSerialTicks -= clockTicks; - - while(gbSerialTicks <= 0) { - // increment number of shifted bits - gbSerialBits++; - linkProc(); - if(gbSerialOn && (gbMemory[0xff02] & 1)) { - if(gbSerialBits == 8) { - gbSerialBits = 0; - gbMemory[0xff01] = 0xff; - gbMemory[0xff02] &= 0x7f; - gbSerialOn = 0; - gbMemory[0xff0f] = register_IF |= 8; - gbSerialTicks = 0; - } - } - gbSerialTicks += GBSERIAL_CLOCK_TICKS; - } - } else { -#endif - if(gbMemory[0xff02] & 1) { - gbSerialTicks -= clockTicks; - - // overflow - while(gbSerialTicks <= 0) { - // shift serial byte to right and put a 1 bit in its place - // gbMemory[0xff01] = 0x80 | (gbMemory[0xff01]>>1); - // increment number of shifted bits - gbSerialBits++; - if(gbSerialBits == 8) { - // end of transmission - if(gbSerialFunction) // external device - gbMemory[0xff01] = gbSerialFunction(gbMemory[0xff01]); - else - gbMemory[0xff01] = 0xff; - gbSerialTicks = 0; - gbMemory[0xff02] &= 0x7f; - gbSerialOn = 0; - gbMemory[0xff0f] = register_IF |= 8; - gbSerialBits = 0; - } else - gbSerialTicks += GBSERIAL_CLOCK_TICKS; - } - } -#ifdef OLD_GB_LINK - } -#endif - } - - - soundTicks -= clockTicks; - if ( !gbSpeed ) - soundTicks -= clockTicks; - - while(soundTicks < 0) { - soundTicks += SOUND_CLOCK_TICKS; - - gbSoundTick(); - } - - - // timer emulation - - if(gbTimerOn) { - gbTimerTicks= ((gbInternalTimer) & gbTimerMask[gbTimerMode])+1-clockTicks; - - while(gbTimerTicks <= 0) { - register_TIMA++; - // timer overflow! - if((register_TIMA & 0xff) == 0) { - // reload timer modulo - register_TIMA = register_TMA; - // flag interrupt - gbMemory[0xff0f] = register_IF |= 4; - } - gbTimerTicks += gbTimerClockTicks; - } - gbTimerOnChange = false; - gbTimerModeChange = false; - - gbMemory[0xff05] = register_TIMA; - - } - - gbInternalTimer -= clockTicks; - while (gbInternalTimer<0) - gbInternalTimer+=0x100; - - clockTicks = 0; - - if (gbIntBreak == 1) - { - gbIntBreak = 0; - if ((register_IE & register_IF & gbInterruptLaunched & 0x3) && - ((IFF & 0x81) == 1) && (!gbInterruptWait) && (execute)) - { - gbIntBreak = 2; - PC.W = oldPCW; - execute = false; - gbOldClockTicks = 0; - } - if (gbOldClockTicks) - { - clockTicks = gbOldClockTicks; - gbOldClockTicks = 0; - goto gbRedoLoop; - } - } - - // Executes the opcode(s), and apply the instruction's remaining clockTicks (if any). - if (execute) - { - switch(opcode1) { - case 0xCB: - // extended opcode - switch(opcode2) { -#include "gbCodesCB.h" - } - break; -#include "gbCodes.h" - } - execute = false; - - if (clockTicks) - { - gbDmaTicks += clockTicks; - clockTicks = 0; - } - } - - if (gbDmaTicks) - { - clockTicks = gbGetNextEvent(gbDmaTicks); - - if (clockTicks<=gbDmaTicks) - gbDmaTicks -= clockTicks; - else - { - clockTicks = gbDmaTicks; - gbDmaTicks = 0; - } - - goto gbRedoLoop; - } - - - // Remove the 'if an IE is pending' flag if IE has finished being executed. - if ((IFF & 0x40) && !(IFF & 0x30)) - IFF &= 0x81; - - - - if ((register_IE & register_IF & 0x1f) && (IFF & 0x81) && (!gbInterruptWait)) - { - - if (IFF & 1) - { - // Add 5 ticks for the interrupt execution time - gbDmaTicks += 5; - - if (gbIntBreak == 2) - { - gbDmaTicks--; - gbIntBreak = 0; - } - - - if(register_IF & register_IE & 1) - gbVblank_interrupt(); - else if(register_IF & register_IE & 2) - gbLcd_interrupt(); - else if(register_IF & register_IE & 4) - gbTimer_interrupt(); - else if(register_IF & register_IE & 8) - gbSerial_interrupt(); - else if(register_IF & register_IE & 16) - gbJoypad_interrupt(); - } - - IFF &= ~0x81; - } - - if (IFF & 0x08) - IFF &=~0x79; - - // Used to apply the interrupt's execution time. - if (gbDmaTicks) - { - clockTicks = gbGetNextEvent(gbDmaTicks); - - if (clockTicks<=gbDmaTicks) - gbDmaTicks -= clockTicks; - else - { - clockTicks = gbDmaTicks; - gbDmaTicks = 0; - } - goto gbRedoLoop; - } - - - gbBlackScreen = false; - - if((ticksToStop <= 0)) { - if(!(register_LCDC & 0x80)) { - if(systemReadJoypads()) { - // read joystick - if(gbSgbMode && gbSgbMultiplayer) { - if(gbSgbFourPlayers) { - gbJoymask[0] = systemReadJoypad(0); - gbJoymask[1] = systemReadJoypad(1); - gbJoymask[2] = systemReadJoypad(2); - gbJoymask[3] = systemReadJoypad(3); - } else { - gbJoymask[0] = systemReadJoypad(0); - gbJoymask[1] = systemReadJoypad(1); - } - } else { - gbJoymask[0] = systemReadJoypad(-1); - } - } - } - return; - } - } -} - -struct EmulatedSystem GBSystem = { - // emuMain - gbEmulate, - // emuReset - gbReset, - // emuCleanUp - gbCleanUp, - // emuReadBattery - gbReadBatteryFile, - // emuWriteBattery - gbWriteBatteryFile, - // emuReadState - gbReadSaveState, - // emuWriteState - gbWriteSaveState, - // emuReadMemState - gbReadMemSaveState, - // emuWriteMemState - gbWriteMemSaveState, - // emuWritePNG - gbWritePNGFile, - // emuWriteBMP - gbWriteBMPFile, - // emuUpdateCPSR - NULL, - // emuHasDebugger - false, - // emuCount -#ifdef FINAL_VERSION - 70000/4, -#else - 1000, -#endif -}; +//#include "../win32/stdafx.h" // would fix LNK2005 linker errors for MSVC +#include +#include +#include +#include +#include + +#include "../System.h" +#include "../NLS.h" +#include "gb.h" +#include "gbCheats.h" +#include "gbGlobals.h" +#include "gbMemory.h" +#include "gbSGB.h" +#include "gbSound.h" +#include "../Util.h" + +#ifdef __GNUC__ +#define _stricmp strcasecmp +#endif + +extern u8 *pix; +extern bool speedup; +bool gbUpdateSizes(); +bool inBios = false; + +// debugging +bool memorydebug = false; +char gbBuffer[2048]; + +extern u16 gbLineMix[160]; + +// mappers +void (*mapper)(u16,u8) = NULL; +void (*mapperRAM)(u16,u8) = NULL; +u8 (*mapperReadRAM)(u16) = NULL; +void (*mapperUpdateClock)() = NULL; + +// registers +gbRegister PC; +gbRegister SP; +gbRegister AF; +gbRegister BC; +gbRegister DE; +gbRegister HL; +u16 IFF = 0; +// 0xff04 +u8 register_DIV = 0; +// 0xff05 +u8 register_TIMA = 0; +// 0xff06 +u8 register_TMA = 0; +// 0xff07 +u8 register_TAC = 0; +// 0xff0f +u8 register_IF = 0; +// 0xff40 +u8 register_LCDC = 0; +// 0xff41 +u8 register_STAT = 0; +// 0xff42 +u8 register_SCY = 0; +// 0xff43 +u8 register_SCX = 0; +// 0xff44 +u8 register_LY = 0; +// 0xff45 +u8 register_LYC = 0; +// 0xff46 +u8 register_DMA = 0; +// 0xff4a +u8 register_WY = 0; +// 0xff4b +u8 register_WX = 0; +// 0xff4f +u8 register_VBK = 0; +// 0xff51 +u8 register_HDMA1 = 0; +// 0xff52 +u8 register_HDMA2 = 0; +// 0xff53 +u8 register_HDMA3 = 0; +// 0xff54 +u8 register_HDMA4 = 0; +// 0xff55 +u8 register_HDMA5 = 0; +// 0xff70 +u8 register_SVBK = 0; +// 0xffff +u8 register_IE = 0; + +// ticks definition +int GBDIV_CLOCK_TICKS = 64; +int GBLCD_MODE_0_CLOCK_TICKS = 51; +int GBLCD_MODE_1_CLOCK_TICKS = 1140; +int GBLCD_MODE_2_CLOCK_TICKS = 20; +int GBLCD_MODE_3_CLOCK_TICKS = 43; +int GBLY_INCREMENT_CLOCK_TICKS = 114; +int GBTIMER_MODE_0_CLOCK_TICKS = 256; +int GBTIMER_MODE_1_CLOCK_TICKS = 4; +int GBTIMER_MODE_2_CLOCK_TICKS = 16; +int GBTIMER_MODE_3_CLOCK_TICKS = 64; +int GBSERIAL_CLOCK_TICKS = 128; +int GBSYNCHRONIZE_CLOCK_TICKS = 52920; + +// state variables + +// general +int clockTicks = 0; +bool gbSystemMessage = false; +int gbGBCColorType = 0; +int gbHardware = 0; +int gbRomType = 0; +int gbRemainingClockTicks = 0; +int gbOldClockTicks = 0; +int gbIntBreak = 0; +int gbInterruptLaunched = 0; +u8 gbCheatingDevice = 0; // 1 = GS, 2 = GG +// breakpoint +bool breakpoint = false; +// interrupt +int gbInt48Signal = 0; +int gbInterruptWait = 0; +// serial +int gbSerialOn = 0; +int gbSerialTicks = 0; +int gbSerialBits = 0; +// timer +bool gbTimerOn = false; +int gbTimerTicks = GBTIMER_MODE_0_CLOCK_TICKS; +int gbTimerClockTicks = GBTIMER_MODE_0_CLOCK_TICKS; +int gbTimerMode = 0; +bool gbIncreased = false; +// The internal timer is always active, and it is +// not reset by writing to register_TIMA/TMA, but by +// writing to register_DIV... +int gbInternalTimer = 0x55; +const u8 gbTimerMask [4] = {0xff, 0x3, 0xf, 0x3f}; +const u8 gbTimerBug [8] = {0x80, 0x80, 0x02, 0x02, 0x0, 0xff, 0x0, 0xff}; +bool gbTimerModeChange = false; +bool gbTimerOnChange = false; +// lcd +bool gbScreenOn = true; +int gbLcdMode = 2; +int gbLcdModeDelayed = 2; +int gbLcdTicks = GBLCD_MODE_2_CLOCK_TICKS-1; +int gbLcdTicksDelayed = GBLCD_MODE_2_CLOCK_TICKS; +int gbLcdLYIncrementTicks = 114; +int gbLcdLYIncrementTicksDelayed = 115; +int gbScreenTicks = 0; +u8 gbSCYLine[300]; +u8 gbSCXLine[300]; +u8 gbBgpLine[300]; +u8 gbObp0Line [300]; +u8 gbObp1Line [300]; +u8 gbSpritesTicks [300]; +u8 oldRegister_WY; +bool gbLYChangeHappened = false; +bool gbLCDChangeHappened = false; +int gbLine99Ticks = 1; +int gbRegisterLYLCDCOffOn = 0; +int inUseRegister_WY = 0; + +// Used to keep track of the line that ellapse +// when screen is off +int gbWhiteScreen = 0; +bool gbBlackScreen = false; +int register_LCDCBusy = 0; + +// div +int gbDivTicks = GBDIV_CLOCK_TICKS; +// cgb +int gbVramBank = 0; +int gbWramBank = 1; +//sgb +bool gbSgbResetFlag = false; +// gbHdmaDestination is 0x99d0 on startup (tested on HW) +// but I'm not sure what gbHdmaSource is... +int gbHdmaSource = 0x99d0; +int gbHdmaDestination = 0x99d0; +int gbHdmaBytes = 0x0000; +int gbHdmaOn = 0; +int gbSpeed = 0; +// frame counting +int gbFrameCount = 0; +int gbFrameSkip = 0; +int gbFrameSkipCount = 0; +// timing +u32 gbLastTime = 0; +u32 gbElapsedTime = 0; +u32 gbTimeNow = 0; +int gbSynchronizeTicks = GBSYNCHRONIZE_CLOCK_TICKS; +// emulator features +int gbBattery = 0; +bool gbBatteryError = false; +int gbCaptureNumber = 0; +bool gbCapture = false; +bool gbCapturePrevious = false; +int gbJoymask[4] = { 0, 0, 0, 0 }; + +u8 gbRamFill = 0xff; + +int gbRomSizes[] = { 0x00008000, // 32K + 0x00010000, // 64K + 0x00020000, // 128K + 0x00040000, // 256K + 0x00080000, // 512K + 0x00100000, // 1024K + 0x00200000, // 2048K + 0x00400000, // 4096K + 0x00800000 // 8192K +}; +int gbRomSizesMasks[] = { 0x00007fff, + 0x0000ffff, + 0x0001ffff, + 0x0003ffff, + 0x0007ffff, + 0x000fffff, + 0x001fffff, + 0x003fffff, + 0x007fffff +}; + +int gbRamSizes[6] = { 0x00000000, // 0K + 0x00002000, // 2K // Changed to 2000 to avoid problems with gbMemoryMap... + 0x00002000, // 8K + 0x00008000, // 32K + 0x00020000, // 128K + 0x00010000 // 64K +}; + +int gbRamSizesMasks[6] = { 0x00000000, + 0x000007ff, + 0x00001fff, + 0x00007fff, + 0x0001ffff, + 0x0000ffff +}; + +int gbCycles[] = { +// 0 1 2 3 4 5 6 7 8 9 a b c d e f + 1, 3, 2, 2, 1, 1, 2, 1, 5, 2, 2, 2, 1, 1, 2, 1, // 0 + 1, 3, 2, 2, 1, 1, 2, 1, 3, 2, 2, 2, 1, 1, 2, 1, // 1 + 2, 3, 2, 2, 1, 1, 2, 1, 2, 2, 2, 2, 1, 1, 2, 1, // 2 + 2, 3, 2, 2, 3, 3, 3, 1, 2, 2, 2, 2, 1, 1, 2, 1, // 3 + 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, // 4 + 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, // 5 + 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, // 6 + 2, 2, 2, 2, 2, 2, 1, 2, 1, 1, 1, 1, 1, 1, 2, 1, // 7 + 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, // 8 + 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, // 9 + 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, // a + 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, // b + 2, 3, 3, 4, 3, 4, 2, 4, 2, 4, 3, 2, 3, 6, 2, 4, // c + 2, 3, 3, 1, 3, 4, 2, 4, 2, 4, 3, 1, 3, 1, 2, 4, // d + 3, 3, 2, 1, 1, 4, 2, 4, 4, 1, 4, 1, 1, 1, 2, 4, // e + 3, 3, 2, 1, 1, 4, 2, 4, 3, 2, 4, 1, 0, 1, 2, 4 // f +}; + +int gbCyclesCB[] = { +// 0 1 2 3 4 5 6 7 8 9 a b c d e f + 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, // 0 + 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, // 1 + 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, // 2 + 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, // 3 + 2, 2, 2, 2, 2, 2, 3, 2, 2, 2, 2, 2, 2, 2, 3, 2, // 4 + 2, 2, 2, 2, 2, 2, 3, 2, 2, 2, 2, 2, 2, 2, 3, 2, // 5 + 2, 2, 2, 2, 2, 2, 3, 2, 2, 2, 2, 2, 2, 2, 3, 2, // 6 + 2, 2, 2, 2, 2, 2, 3, 2, 2, 2, 2, 2, 2, 2, 3, 2, // 7 + 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, // 8 + 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, // 9 + 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, // a + 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, // b + 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, // c + 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, // d + 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, // e + 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2 // f +}; + +u16 DAATable[] = { + 0x0080,0x0100,0x0200,0x0300,0x0400,0x0500,0x0600,0x0700, + 0x0800,0x0900,0x1000,0x1100,0x1200,0x1300,0x1400,0x1500, + 0x1000,0x1100,0x1200,0x1300,0x1400,0x1500,0x1600,0x1700, + 0x1800,0x1900,0x2000,0x2100,0x2200,0x2300,0x2400,0x2500, + 0x2000,0x2100,0x2200,0x2300,0x2400,0x2500,0x2600,0x2700, + 0x2800,0x2900,0x3000,0x3100,0x3200,0x3300,0x3400,0x3500, + 0x3000,0x3100,0x3200,0x3300,0x3400,0x3500,0x3600,0x3700, + 0x3800,0x3900,0x4000,0x4100,0x4200,0x4300,0x4400,0x4500, + 0x4000,0x4100,0x4200,0x4300,0x4400,0x4500,0x4600,0x4700, + 0x4800,0x4900,0x5000,0x5100,0x5200,0x5300,0x5400,0x5500, + 0x5000,0x5100,0x5200,0x5300,0x5400,0x5500,0x5600,0x5700, + 0x5800,0x5900,0x6000,0x6100,0x6200,0x6300,0x6400,0x6500, + 0x6000,0x6100,0x6200,0x6300,0x6400,0x6500,0x6600,0x6700, + 0x6800,0x6900,0x7000,0x7100,0x7200,0x7300,0x7400,0x7500, + 0x7000,0x7100,0x7200,0x7300,0x7400,0x7500,0x7600,0x7700, + 0x7800,0x7900,0x8000,0x8100,0x8200,0x8300,0x8400,0x8500, + 0x8000,0x8100,0x8200,0x8300,0x8400,0x8500,0x8600,0x8700, + 0x8800,0x8900,0x9000,0x9100,0x9200,0x9300,0x9400,0x9500, + 0x9000,0x9100,0x9200,0x9300,0x9400,0x9500,0x9600,0x9700, + 0x9800,0x9900,0x0090,0x0110,0x0210,0x0310,0x0410,0x0510, + 0x0090,0x0110,0x0210,0x0310,0x0410,0x0510,0x0610,0x0710, + 0x0810,0x0910,0x1010,0x1110,0x1210,0x1310,0x1410,0x1510, + 0x1010,0x1110,0x1210,0x1310,0x1410,0x1510,0x1610,0x1710, + 0x1810,0x1910,0x2010,0x2110,0x2210,0x2310,0x2410,0x2510, + 0x2010,0x2110,0x2210,0x2310,0x2410,0x2510,0x2610,0x2710, + 0x2810,0x2910,0x3010,0x3110,0x3210,0x3310,0x3410,0x3510, + 0x3010,0x3110,0x3210,0x3310,0x3410,0x3510,0x3610,0x3710, + 0x3810,0x3910,0x4010,0x4110,0x4210,0x4310,0x4410,0x4510, + 0x4010,0x4110,0x4210,0x4310,0x4410,0x4510,0x4610,0x4710, + 0x4810,0x4910,0x5010,0x5110,0x5210,0x5310,0x5410,0x5510, + 0x5010,0x5110,0x5210,0x5310,0x5410,0x5510,0x5610,0x5710, + 0x5810,0x5910,0x6010,0x6110,0x6210,0x6310,0x6410,0x6510, + 0x6010,0x6110,0x6210,0x6310,0x6410,0x6510,0x6610,0x6710, + 0x6810,0x6910,0x7010,0x7110,0x7210,0x7310,0x7410,0x7510, + 0x7010,0x7110,0x7210,0x7310,0x7410,0x7510,0x7610,0x7710, + 0x7810,0x7910,0x8010,0x8110,0x8210,0x8310,0x8410,0x8510, + 0x8010,0x8110,0x8210,0x8310,0x8410,0x8510,0x8610,0x8710, + 0x8810,0x8910,0x9010,0x9110,0x9210,0x9310,0x9410,0x9510, + 0x9010,0x9110,0x9210,0x9310,0x9410,0x9510,0x9610,0x9710, + 0x9810,0x9910,0xA010,0xA110,0xA210,0xA310,0xA410,0xA510, + 0xA010,0xA110,0xA210,0xA310,0xA410,0xA510,0xA610,0xA710, + 0xA810,0xA910,0xB010,0xB110,0xB210,0xB310,0xB410,0xB510, + 0xB010,0xB110,0xB210,0xB310,0xB410,0xB510,0xB610,0xB710, + 0xB810,0xB910,0xC010,0xC110,0xC210,0xC310,0xC410,0xC510, + 0xC010,0xC110,0xC210,0xC310,0xC410,0xC510,0xC610,0xC710, + 0xC810,0xC910,0xD010,0xD110,0xD210,0xD310,0xD410,0xD510, + 0xD010,0xD110,0xD210,0xD310,0xD410,0xD510,0xD610,0xD710, + 0xD810,0xD910,0xE010,0xE110,0xE210,0xE310,0xE410,0xE510, + 0xE010,0xE110,0xE210,0xE310,0xE410,0xE510,0xE610,0xE710, + 0xE810,0xE910,0xF010,0xF110,0xF210,0xF310,0xF410,0xF510, + 0xF010,0xF110,0xF210,0xF310,0xF410,0xF510,0xF610,0xF710, + 0xF810,0xF910,0x0090,0x0110,0x0210,0x0310,0x0410,0x0510, + 0x0090,0x0110,0x0210,0x0310,0x0410,0x0510,0x0610,0x0710, + 0x0810,0x0910,0x1010,0x1110,0x1210,0x1310,0x1410,0x1510, + 0x1010,0x1110,0x1210,0x1310,0x1410,0x1510,0x1610,0x1710, + 0x1810,0x1910,0x2010,0x2110,0x2210,0x2310,0x2410,0x2510, + 0x2010,0x2110,0x2210,0x2310,0x2410,0x2510,0x2610,0x2710, + 0x2810,0x2910,0x3010,0x3110,0x3210,0x3310,0x3410,0x3510, + 0x3010,0x3110,0x3210,0x3310,0x3410,0x3510,0x3610,0x3710, + 0x3810,0x3910,0x4010,0x4110,0x4210,0x4310,0x4410,0x4510, + 0x4010,0x4110,0x4210,0x4310,0x4410,0x4510,0x4610,0x4710, + 0x4810,0x4910,0x5010,0x5110,0x5210,0x5310,0x5410,0x5510, + 0x5010,0x5110,0x5210,0x5310,0x5410,0x5510,0x5610,0x5710, + 0x5810,0x5910,0x6010,0x6110,0x6210,0x6310,0x6410,0x6510, + 0x0600,0x0700,0x0800,0x0900,0x0A00,0x0B00,0x0C00,0x0D00, + 0x0E00,0x0F00,0x1000,0x1100,0x1200,0x1300,0x1400,0x1500, + 0x1600,0x1700,0x1800,0x1900,0x1A00,0x1B00,0x1C00,0x1D00, + 0x1E00,0x1F00,0x2000,0x2100,0x2200,0x2300,0x2400,0x2500, + 0x2600,0x2700,0x2800,0x2900,0x2A00,0x2B00,0x2C00,0x2D00, + 0x2E00,0x2F00,0x3000,0x3100,0x3200,0x3300,0x3400,0x3500, + 0x3600,0x3700,0x3800,0x3900,0x3A00,0x3B00,0x3C00,0x3D00, + 0x3E00,0x3F00,0x4000,0x4100,0x4200,0x4300,0x4400,0x4500, + 0x4600,0x4700,0x4800,0x4900,0x4A00,0x4B00,0x4C00,0x4D00, + 0x4E00,0x4F00,0x5000,0x5100,0x5200,0x5300,0x5400,0x5500, + 0x5600,0x5700,0x5800,0x5900,0x5A00,0x5B00,0x5C00,0x5D00, + 0x5E00,0x5F00,0x6000,0x6100,0x6200,0x6300,0x6400,0x6500, + 0x6600,0x6700,0x6800,0x6900,0x6A00,0x6B00,0x6C00,0x6D00, + 0x6E00,0x6F00,0x7000,0x7100,0x7200,0x7300,0x7400,0x7500, + 0x7600,0x7700,0x7800,0x7900,0x7A00,0x7B00,0x7C00,0x7D00, + 0x7E00,0x7F00,0x8000,0x8100,0x8200,0x8300,0x8400,0x8500, + 0x8600,0x8700,0x8800,0x8900,0x8A00,0x8B00,0x8C00,0x8D00, + 0x8E00,0x8F00,0x9000,0x9100,0x9200,0x9300,0x9400,0x9500, + 0x9600,0x9700,0x9800,0x9900,0x9A00,0x9B00,0x9C00,0x9D00, + 0x9E00,0x9F00,0x0090,0x0110,0x0210,0x0310,0x0410,0x0510, + 0x0610,0x0710,0x0810,0x0910,0x0A10,0x0B10,0x0C10,0x0D10, + 0x0E10,0x0F10,0x1010,0x1110,0x1210,0x1310,0x1410,0x1510, + 0x1610,0x1710,0x1810,0x1910,0x1A10,0x1B10,0x1C10,0x1D10, + 0x1E10,0x1F10,0x2010,0x2110,0x2210,0x2310,0x2410,0x2510, + 0x2610,0x2710,0x2810,0x2910,0x2A10,0x2B10,0x2C10,0x2D10, + 0x2E10,0x2F10,0x3010,0x3110,0x3210,0x3310,0x3410,0x3510, + 0x3610,0x3710,0x3810,0x3910,0x3A10,0x3B10,0x3C10,0x3D10, + 0x3E10,0x3F10,0x4010,0x4110,0x4210,0x4310,0x4410,0x4510, + 0x4610,0x4710,0x4810,0x4910,0x4A10,0x4B10,0x4C10,0x4D10, + 0x4E10,0x4F10,0x5010,0x5110,0x5210,0x5310,0x5410,0x5510, + 0x5610,0x5710,0x5810,0x5910,0x5A10,0x5B10,0x5C10,0x5D10, + 0x5E10,0x5F10,0x6010,0x6110,0x6210,0x6310,0x6410,0x6510, + 0x6610,0x6710,0x6810,0x6910,0x6A10,0x6B10,0x6C10,0x6D10, + 0x6E10,0x6F10,0x7010,0x7110,0x7210,0x7310,0x7410,0x7510, + 0x7610,0x7710,0x7810,0x7910,0x7A10,0x7B10,0x7C10,0x7D10, + 0x7E10,0x7F10,0x8010,0x8110,0x8210,0x8310,0x8410,0x8510, + 0x8610,0x8710,0x8810,0x8910,0x8A10,0x8B10,0x8C10,0x8D10, + 0x8E10,0x8F10,0x9010,0x9110,0x9210,0x9310,0x9410,0x9510, + 0x9610,0x9710,0x9810,0x9910,0x9A10,0x9B10,0x9C10,0x9D10, + 0x9E10,0x9F10,0xA010,0xA110,0xA210,0xA310,0xA410,0xA510, + 0xA610,0xA710,0xA810,0xA910,0xAA10,0xAB10,0xAC10,0xAD10, + 0xAE10,0xAF10,0xB010,0xB110,0xB210,0xB310,0xB410,0xB510, + 0xB610,0xB710,0xB810,0xB910,0xBA10,0xBB10,0xBC10,0xBD10, + 0xBE10,0xBF10,0xC010,0xC110,0xC210,0xC310,0xC410,0xC510, + 0xC610,0xC710,0xC810,0xC910,0xCA10,0xCB10,0xCC10,0xCD10, + 0xCE10,0xCF10,0xD010,0xD110,0xD210,0xD310,0xD410,0xD510, + 0xD610,0xD710,0xD810,0xD910,0xDA10,0xDB10,0xDC10,0xDD10, + 0xDE10,0xDF10,0xE010,0xE110,0xE210,0xE310,0xE410,0xE510, + 0xE610,0xE710,0xE810,0xE910,0xEA10,0xEB10,0xEC10,0xED10, + 0xEE10,0xEF10,0xF010,0xF110,0xF210,0xF310,0xF410,0xF510, + 0xF610,0xF710,0xF810,0xF910,0xFA10,0xFB10,0xFC10,0xFD10, + 0xFE10,0xFF10,0x0090,0x0110,0x0210,0x0310,0x0410,0x0510, + 0x0610,0x0710,0x0810,0x0910,0x0A10,0x0B10,0x0C10,0x0D10, + 0x0E10,0x0F10,0x1010,0x1110,0x1210,0x1310,0x1410,0x1510, + 0x1610,0x1710,0x1810,0x1910,0x1A10,0x1B10,0x1C10,0x1D10, + 0x1E10,0x1F10,0x2010,0x2110,0x2210,0x2310,0x2410,0x2510, + 0x2610,0x2710,0x2810,0x2910,0x2A10,0x2B10,0x2C10,0x2D10, + 0x2E10,0x2F10,0x3010,0x3110,0x3210,0x3310,0x3410,0x3510, + 0x3610,0x3710,0x3810,0x3910,0x3A10,0x3B10,0x3C10,0x3D10, + 0x3E10,0x3F10,0x4010,0x4110,0x4210,0x4310,0x4410,0x4510, + 0x4610,0x4710,0x4810,0x4910,0x4A10,0x4B10,0x4C10,0x4D10, + 0x4E10,0x4F10,0x5010,0x5110,0x5210,0x5310,0x5410,0x5510, + 0x5610,0x5710,0x5810,0x5910,0x5A10,0x5B10,0x5C10,0x5D10, + 0x5E10,0x5F10,0x6010,0x6110,0x6210,0x6310,0x6410,0x6510, + 0x00C0,0x0140,0x0240,0x0340,0x0440,0x0540,0x0640,0x0740, + 0x0840,0x0940,0x0A40,0x0B40,0x0C40,0x0D40,0x0E40,0x0F40, + 0x1040,0x1140,0x1240,0x1340,0x1440,0x1540,0x1640,0x1740, + 0x1840,0x1940,0x1A40,0x1B40,0x1C40,0x1D40,0x1E40,0x1F40, + 0x2040,0x2140,0x2240,0x2340,0x2440,0x2540,0x2640,0x2740, + 0x2840,0x2940,0x2A40,0x2B40,0x2C40,0x2D40,0x2E40,0x2F40, + 0x3040,0x3140,0x3240,0x3340,0x3440,0x3540,0x3640,0x3740, + 0x3840,0x3940,0x3A40,0x3B40,0x3C40,0x3D40,0x3E40,0x3F40, + 0x4040,0x4140,0x4240,0x4340,0x4440,0x4540,0x4640,0x4740, + 0x4840,0x4940,0x4A40,0x4B40,0x4C40,0x4D40,0x4E40,0x4F40, + 0x5040,0x5140,0x5240,0x5340,0x5440,0x5540,0x5640,0x5740, + 0x5840,0x5940,0x5A40,0x5B40,0x5C40,0x5D40,0x5E40,0x5F40, + 0x6040,0x6140,0x6240,0x6340,0x6440,0x6540,0x6640,0x6740, + 0x6840,0x6940,0x6A40,0x6B40,0x6C40,0x6D40,0x6E40,0x6F40, + 0x7040,0x7140,0x7240,0x7340,0x7440,0x7540,0x7640,0x7740, + 0x7840,0x7940,0x7A40,0x7B40,0x7C40,0x7D40,0x7E40,0x7F40, + 0x8040,0x8140,0x8240,0x8340,0x8440,0x8540,0x8640,0x8740, + 0x8840,0x8940,0x8A40,0x8B40,0x8C40,0x8D40,0x8E40,0x8F40, + 0x9040,0x9140,0x9240,0x9340,0x9440,0x9540,0x9640,0x9740, + 0x9840,0x9940,0x9A40,0x9B40,0x9C40,0x9D40,0x9E40,0x9F40, + 0xA040,0xA140,0xA240,0xA340,0xA440,0xA540,0xA640,0xA740, + 0xA840,0xA940,0xAA40,0xAB40,0xAC40,0xAD40,0xAE40,0xAF40, + 0xB040,0xB140,0xB240,0xB340,0xB440,0xB540,0xB640,0xB740, + 0xB840,0xB940,0xBA40,0xBB40,0xBC40,0xBD40,0xBE40,0xBF40, + 0xC040,0xC140,0xC240,0xC340,0xC440,0xC540,0xC640,0xC740, + 0xC840,0xC940,0xCA40,0xCB40,0xCC40,0xCD40,0xCE40,0xCF40, + 0xD040,0xD140,0xD240,0xD340,0xD440,0xD540,0xD640,0xD740, + 0xD840,0xD940,0xDA40,0xDB40,0xDC40,0xDD40,0xDE40,0xDF40, + 0xE040,0xE140,0xE240,0xE340,0xE440,0xE540,0xE640,0xE740, + 0xE840,0xE940,0xEA40,0xEB40,0xEC40,0xED40,0xEE40,0xEF40, + 0xF040,0xF140,0xF240,0xF340,0xF440,0xF540,0xF640,0xF740, + 0xF840,0xF940,0xFA40,0xFB40,0xFC40,0xFD40,0xFE40,0xFF40, + 0xA050,0xA150,0xA250,0xA350,0xA450,0xA550,0xA650,0xA750, + 0xA850,0xA950,0xAA50,0xAB50,0xAC50,0xAD50,0xAE50,0xAF50, + 0xB050,0xB150,0xB250,0xB350,0xB450,0xB550,0xB650,0xB750, + 0xB850,0xB950,0xBA50,0xBB50,0xBC50,0xBD50,0xBE50,0xBF50, + 0xC050,0xC150,0xC250,0xC350,0xC450,0xC550,0xC650,0xC750, + 0xC850,0xC950,0xCA50,0xCB50,0xCC50,0xCD50,0xCE50,0xCF50, + 0xD050,0xD150,0xD250,0xD350,0xD450,0xD550,0xD650,0xD750, + 0xD850,0xD950,0xDA50,0xDB50,0xDC50,0xDD50,0xDE50,0xDF50, + 0xE050,0xE150,0xE250,0xE350,0xE450,0xE550,0xE650,0xE750, + 0xE850,0xE950,0xEA50,0xEB50,0xEC50,0xED50,0xEE50,0xEF50, + 0xF050,0xF150,0xF250,0xF350,0xF450,0xF550,0xF650,0xF750, + 0xF850,0xF950,0xFA50,0xFB50,0xFC50,0xFD50,0xFE50,0xFF50, + 0x00D0,0x0150,0x0250,0x0350,0x0450,0x0550,0x0650,0x0750, + 0x0850,0x0950,0x0A50,0x0B50,0x0C50,0x0D50,0x0E50,0x0F50, + 0x1050,0x1150,0x1250,0x1350,0x1450,0x1550,0x1650,0x1750, + 0x1850,0x1950,0x1A50,0x1B50,0x1C50,0x1D50,0x1E50,0x1F50, + 0x2050,0x2150,0x2250,0x2350,0x2450,0x2550,0x2650,0x2750, + 0x2850,0x2950,0x2A50,0x2B50,0x2C50,0x2D50,0x2E50,0x2F50, + 0x3050,0x3150,0x3250,0x3350,0x3450,0x3550,0x3650,0x3750, + 0x3850,0x3950,0x3A50,0x3B50,0x3C50,0x3D50,0x3E50,0x3F50, + 0x4050,0x4150,0x4250,0x4350,0x4450,0x4550,0x4650,0x4750, + 0x4850,0x4950,0x4A50,0x4B50,0x4C50,0x4D50,0x4E50,0x4F50, + 0x5050,0x5150,0x5250,0x5350,0x5450,0x5550,0x5650,0x5750, + 0x5850,0x5950,0x5A50,0x5B50,0x5C50,0x5D50,0x5E50,0x5F50, + 0x6050,0x6150,0x6250,0x6350,0x6450,0x6550,0x6650,0x6750, + 0x6850,0x6950,0x6A50,0x6B50,0x6C50,0x6D50,0x6E50,0x6F50, + 0x7050,0x7150,0x7250,0x7350,0x7450,0x7550,0x7650,0x7750, + 0x7850,0x7950,0x7A50,0x7B50,0x7C50,0x7D50,0x7E50,0x7F50, + 0x8050,0x8150,0x8250,0x8350,0x8450,0x8550,0x8650,0x8750, + 0x8850,0x8950,0x8A50,0x8B50,0x8C50,0x8D50,0x8E50,0x8F50, + 0x9050,0x9150,0x9250,0x9350,0x9450,0x9550,0x9650,0x9750, + 0x9850,0x9950,0x9A50,0x9B50,0x9C50,0x9D50,0x9E50,0x9F50, + 0xFA40,0xFB40,0xFC40,0xFD40,0xFE40,0xFF40,0x00C0,0x0140, + 0x0240,0x0340,0x0440,0x0540,0x0640,0x0740,0x0840,0x0940, + 0x0A40,0x0B40,0x0C40,0x0D40,0x0E40,0x0F40,0x1040,0x1140, + 0x1240,0x1340,0x1440,0x1540,0x1640,0x1740,0x1840,0x1940, + 0x1A40,0x1B40,0x1C40,0x1D40,0x1E40,0x1F40,0x2040,0x2140, + 0x2240,0x2340,0x2440,0x2540,0x2640,0x2740,0x2840,0x2940, + 0x2A40,0x2B40,0x2C40,0x2D40,0x2E40,0x2F40,0x3040,0x3140, + 0x3240,0x3340,0x3440,0x3540,0x3640,0x3740,0x3840,0x3940, + 0x3A40,0x3B40,0x3C40,0x3D40,0x3E40,0x3F40,0x4040,0x4140, + 0x4240,0x4340,0x4440,0x4540,0x4640,0x4740,0x4840,0x4940, + 0x4A40,0x4B40,0x4C40,0x4D40,0x4E40,0x4F40,0x5040,0x5140, + 0x5240,0x5340,0x5440,0x5540,0x5640,0x5740,0x5840,0x5940, + 0x5A40,0x5B40,0x5C40,0x5D40,0x5E40,0x5F40,0x6040,0x6140, + 0x6240,0x6340,0x6440,0x6540,0x6640,0x6740,0x6840,0x6940, + 0x6A40,0x6B40,0x6C40,0x6D40,0x6E40,0x6F40,0x7040,0x7140, + 0x7240,0x7340,0x7440,0x7540,0x7640,0x7740,0x7840,0x7940, + 0x7A40,0x7B40,0x7C40,0x7D40,0x7E40,0x7F40,0x8040,0x8140, + 0x8240,0x8340,0x8440,0x8540,0x8640,0x8740,0x8840,0x8940, + 0x8A40,0x8B40,0x8C40,0x8D40,0x8E40,0x8F40,0x9040,0x9140, + 0x9240,0x9340,0x9440,0x9540,0x9640,0x9740,0x9840,0x9940, + 0x9A40,0x9B40,0x9C40,0x9D40,0x9E40,0x9F40,0xA040,0xA140, + 0xA240,0xA340,0xA440,0xA540,0xA640,0xA740,0xA840,0xA940, + 0xAA40,0xAB40,0xAC40,0xAD40,0xAE40,0xAF40,0xB040,0xB140, + 0xB240,0xB340,0xB440,0xB540,0xB640,0xB740,0xB840,0xB940, + 0xBA40,0xBB40,0xBC40,0xBD40,0xBE40,0xBF40,0xC040,0xC140, + 0xC240,0xC340,0xC440,0xC540,0xC640,0xC740,0xC840,0xC940, + 0xCA40,0xCB40,0xCC40,0xCD40,0xCE40,0xCF40,0xD040,0xD140, + 0xD240,0xD340,0xD440,0xD540,0xD640,0xD740,0xD840,0xD940, + 0xDA40,0xDB40,0xDC40,0xDD40,0xDE40,0xDF40,0xE040,0xE140, + 0xE240,0xE340,0xE440,0xE540,0xE640,0xE740,0xE840,0xE940, + 0xEA40,0xEB40,0xEC40,0xED40,0xEE40,0xEF40,0xF040,0xF140, + 0xF240,0xF340,0xF440,0xF540,0xF640,0xF740,0xF840,0xF940, + 0x9A50,0x9B50,0x9C50,0x9D50,0x9E50,0x9F50,0xA050,0xA150, + 0xA250,0xA350,0xA450,0xA550,0xA650,0xA750,0xA850,0xA950, + 0xAA50,0xAB50,0xAC50,0xAD50,0xAE50,0xAF50,0xB050,0xB150, + 0xB250,0xB350,0xB450,0xB550,0xB650,0xB750,0xB850,0xB950, + 0xBA50,0xBB50,0xBC50,0xBD50,0xBE50,0xBF50,0xC050,0xC150, + 0xC250,0xC350,0xC450,0xC550,0xC650,0xC750,0xC850,0xC950, + 0xCA50,0xCB50,0xCC50,0xCD50,0xCE50,0xCF50,0xD050,0xD150, + 0xD250,0xD350,0xD450,0xD550,0xD650,0xD750,0xD850,0xD950, + 0xDA50,0xDB50,0xDC50,0xDD50,0xDE50,0xDF50,0xE050,0xE150, + 0xE250,0xE350,0xE450,0xE550,0xE650,0xE750,0xE850,0xE950, + 0xEA50,0xEB50,0xEC50,0xED50,0xEE50,0xEF50,0xF050,0xF150, + 0xF250,0xF350,0xF450,0xF550,0xF650,0xF750,0xF850,0xF950, + 0xFA50,0xFB50,0xFC50,0xFD50,0xFE50,0xFF50,0x00D0,0x0150, + 0x0250,0x0350,0x0450,0x0550,0x0650,0x0750,0x0850,0x0950, + 0x0A50,0x0B50,0x0C50,0x0D50,0x0E50,0x0F50,0x1050,0x1150, + 0x1250,0x1350,0x1450,0x1550,0x1650,0x1750,0x1850,0x1950, + 0x1A50,0x1B50,0x1C50,0x1D50,0x1E50,0x1F50,0x2050,0x2150, + 0x2250,0x2350,0x2450,0x2550,0x2650,0x2750,0x2850,0x2950, + 0x2A50,0x2B50,0x2C50,0x2D50,0x2E50,0x2F50,0x3050,0x3150, + 0x3250,0x3350,0x3450,0x3550,0x3650,0x3750,0x3850,0x3950, + 0x3A50,0x3B50,0x3C50,0x3D50,0x3E50,0x3F50,0x4050,0x4150, + 0x4250,0x4350,0x4450,0x4550,0x4650,0x4750,0x4850,0x4950, + 0x4A50,0x4B50,0x4C50,0x4D50,0x4E50,0x4F50,0x5050,0x5150, + 0x5250,0x5350,0x5450,0x5550,0x5650,0x5750,0x5850,0x5950, + 0x5A50,0x5B50,0x5C50,0x5D50,0x5E50,0x5F50,0x6050,0x6150, + 0x6250,0x6350,0x6450,0x6550,0x6650,0x6750,0x6850,0x6950, + 0x6A50,0x6B50,0x6C50,0x6D50,0x6E50,0x6F50,0x7050,0x7150, + 0x7250,0x7350,0x7450,0x7550,0x7650,0x7750,0x7850,0x7950, + 0x7A50,0x7B50,0x7C50,0x7D50,0x7E50,0x7F50,0x8050,0x8150, + 0x8250,0x8350,0x8450,0x8550,0x8650,0x8750,0x8850,0x8950, + 0x8A50,0x8B50,0x8C50,0x8D50,0x8E50,0x8F50,0x9050,0x9150, + 0x9250,0x9350,0x9450,0x9550,0x9650,0x9750,0x9850,0x9950, +}; + +u8 ZeroTable[256] = { + 0x80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0 +}; + +#define GBSAVE_GAME_VERSION_1 1 +#define GBSAVE_GAME_VERSION_2 2 +#define GBSAVE_GAME_VERSION_3 3 +#define GBSAVE_GAME_VERSION_4 4 +#define GBSAVE_GAME_VERSION_5 5 +#define GBSAVE_GAME_VERSION_6 6 +#define GBSAVE_GAME_VERSION_7 7 +#define GBSAVE_GAME_VERSION_8 8 +#define GBSAVE_GAME_VERSION_9 9 +#define GBSAVE_GAME_VERSION_10 10 +#define GBSAVE_GAME_VERSION_11 11 +#define GBSAVE_GAME_VERSION_12 12 +#define GBSAVE_GAME_VERSION GBSAVE_GAME_VERSION_12 + +int inline gbGetValue(int min,int max,int v) +{ + return (int)(min+(float)(max-min)*(2.0*(v/31.0)-(v/31.0)*(v/31.0))); +} + +void gbGenFilter() +{ + for (int r=0;r<32;r++) { + for (int g=0;g<32;g++) { + for (int b=0;b<32;b++) { + int nr=gbGetValue(gbGetValue(4,14,g), + gbGetValue(24,29,g),r)-4; + int ng=gbGetValue(gbGetValue(4+gbGetValue(0,5,r), + 14+gbGetValue(0,3,r),b), + gbGetValue(24+gbGetValue(0,3,r), + 29+gbGetValue(0,1,r),b),g)-4; + int nb=gbGetValue(gbGetValue(4+gbGetValue(0,5,r), + 14+gbGetValue(0,3,r),g), + gbGetValue(24+gbGetValue(0,3,r), + 29+gbGetValue(0,1,r),g),b)-4; + gbColorFilter[(b<<10)|(g<<5)|r]=(nb<<10)|(ng<<5)|nr; + } + } + } +} + +bool gbIsGameboyRom(char * file) +{ + if(strlen(file) > 4) { + char * p = strrchr(file,'.'); + + if(p != NULL) { + if(_stricmp(p, ".gb") == 0) + return true; + if(_stricmp(p, ".gbc") == 0) + return true; + if(_stricmp(p, ".cgb") == 0) + return true; + if(_stricmp(p, ".sgb") == 0) + return true; + } + } + + return false; +} + +void gbCopyMemory(u16 d, u16 s, int count) +{ + while(count) { + gbMemoryMap[d>>12][d & 0x0fff] = gbMemoryMap[s>>12][s & 0x0fff]; + s++; + d++; + count--; + } +} + +void gbDoHdma() +{ + + gbCopyMemory((gbHdmaDestination & 0x1ff0) | 0x8000, + gbHdmaSource & 0xfff0, + 0x10); + + gbHdmaDestination += 0x10; + if (gbHdmaDestination == 0xa000) + gbHdmaDestination = 0x8000; + + gbHdmaSource += 0x10; + if (gbHdmaSource == 0x8000) + gbHdmaSource = 0xa000; + + register_HDMA2 = gbHdmaSource & 0xff; + register_HDMA1 = gbHdmaSource>>8; + + register_HDMA4 = gbHdmaDestination & 0xff; + register_HDMA3 = gbHdmaDestination>>8; + + gbHdmaBytes -= 0x10; + gbMemory[0xff55] = --register_HDMA5; + if(register_HDMA5 == 0xff) + gbHdmaOn = 0; + +// We need to add the dmaClockticks for HDMA ! + if(gbSpeed) + gbDmaTicks = 17; + else + gbDmaTicks = 9; + + if (IFF & 0x80) + gbDmaTicks++; + +} + +// fix for Harley and Lego Racers +void gbCompareLYToLYC() +{ + if(register_LCDC & 0x80) { + if(register_LY == register_LYC) { + + // mark that we have a match + register_STAT |= 4; + + // check if we need an interrupt + if (register_STAT & 0x40) + { + // send LCD interrupt only if no interrupt 48h signal... + if (!gbInt48Signal) + { + register_IF |=2; + } + gbInt48Signal |= 8; + } + } + else // no match + { + register_STAT &= 0xfb; + gbInt48Signal &=~8; + } + } +} + +void gbWriteMemory(register u16 address, register u8 value) +{ + + if(address < 0x8000) { +#ifndef FINAL_VERSION + if(memorydebug && (address>0x3fff || address < 0x2000)) { + log("Memory register write %04x=%02x PC=%04x\n", + address, + value, + PC.W); + } + +#endif + if(mapper) + (*mapper)(address, value); + return; + + } + + if(address < 0xa000) { + // No access to Vram during mode 3 + // (used to emulate the gfx differences between GB & GBC-GBA/SP in Stunt Racer) + if ((gbLcdModeDelayed !=3) || + // This part is used to emulate a small difference between hardwares + // (check 8-in-1's arrow on GBA/GBC to verify it) + ((register_LY == 0) && ((gbHardware & 0xa) && (gbScreenOn==false) && + (register_LCDC & 0x80)) && + (gbLcdLYIncrementTicksDelayed ==(GBLY_INCREMENT_CLOCK_TICKS-GBLCD_MODE_2_CLOCK_TICKS)))) + gbMemoryMap[address>>12][address&0x0fff] = value; + return; + } + + // Used for the mirroring of 0xC000 in 0xE000 + if ((address >= 0xe000) && (address < 0xfe00)) + address &= ~0x2000; + + if(address < 0xc000) { +#ifndef FINAL_VERSION + if(memorydebug) { + log("Memory register write %04x=%02x PC=%04x\n", + address, + value, + PC.W); + } +#endif + + // Is that a correct fix ??? (it used to be 'if (mapper)')... + if(mapperRAM) + (*mapperRAM)(address, value); + return; + } + + + if(address < 0xfe00) { + gbMemoryMap[address>>12][address & 0x0fff] = value; + return; + } + + // OAM not accessible during mode 2 & 3. + if(address < 0xfea0) + { + if (((gbHardware & 0xa) && ((gbLcdMode | gbLcdModeDelayed) &2)) || + ((gbHardware & 5) && (((gbLcdModeDelayed == 2) && + (gbLcdTicksDelayed<=GBLCD_MODE_2_CLOCK_TICKS)) || + (gbLcdModeDelayed == 3)))) + return; + else + { + gbMemory[address] = value; + return; + } + } + + + + if((address > 0xfea0) && (address < 0xff00)){ // GBC allows reading/writing to that area + gbMemory[address] = value; + return; + } + + switch(address & 0x00ff) { + + case 0x00: { + gbMemory[0xff00] = ((gbMemory[0xff00] & 0xcf) | + (value & 0x30) | 0xc0); + if(gbSgbMode) { + gbSgbDoBitTransfer(value); + } + return; + } + + case 0x01: { + gbMemory[0xff01] = value; + return; + } + + // serial control + case 0x02: { + gbSerialOn = (value & 0x80); + gbMemory[0xff02] = value; + if(gbSerialOn) { + gbSerialTicks = GBSERIAL_CLOCK_TICKS; +#ifdef OLD_GB_LINK + if(linkConnected) { + if(value & 1) { + linkSendByte(0x100|gbMemory[0xFF01]); + Sleep(5); + } + } +#endif + } + + gbSerialBits = 0; + return; + } + + case 0x04: { + // DIV register resets on any write + // (not totally perfect, but better than nothing) + gbMemory[0xff04] = register_DIV = 0; + gbDivTicks = GBDIV_CLOCK_TICKS; + // Another weird timer 'bug' : + // Writing to DIV register resets the internal timer, + // and can also increase TIMA/trigger an interrupt + // in some cases... + if (gbTimerOn && !(gbInternalTimer & (gbTimerClockTicks>>1))) + { + gbMemory[0xff05] = ++register_TIMA; + if(register_TIMA == 0) { + // timer overflow! + + // reload timer modulo + gbMemory[0xff05] = register_TIMA = register_TMA; + + // flag interrupt + gbMemory[0xff0f] = register_IF |= 4; + } + } + gbInternalTimer = 0xff; + return; + } + case 0x05: + gbMemory[0xff05] = register_TIMA = value; + return; + + case 0x06: + gbMemory[0xff06] = register_TMA = value; + return; + + // TIMER control + case 0x07: { + + gbTimerModeChange = (((value & 3) != (register_TAC&3)) && (value & register_TAC & 4)) ? true : false; + gbTimerOnChange = (((value ^ register_TAC) & 4) == 4) ? true : false; + + gbTimerOn = (value & 4) ? true : false; + + if (gbTimerOnChange || gbTimerModeChange) + { + gbTimerMode = value & 3; + + switch(gbTimerMode) { + case 0: + gbTimerClockTicks = GBTIMER_MODE_0_CLOCK_TICKS; + break; + case 1: + gbTimerClockTicks = GBTIMER_MODE_1_CLOCK_TICKS; + break; + case 2: + gbTimerClockTicks = GBTIMER_MODE_2_CLOCK_TICKS; + break; + case 3: + gbTimerClockTicks = GBTIMER_MODE_3_CLOCK_TICKS; + break; + } + } + + + // This looks weird, but this emulates a bug in which register_TIMA + // is increased when writing to register_TAC + // (This fixes Korodice's long-delay between menus bug). + + if (gbTimerOnChange || gbTimerModeChange) + { + bool temp = false; + + if ((gbTimerOn && !gbTimerModeChange) && (gbTimerMode & 2) && + !(gbInternalTimer & 0x80) && (gbInternalTimer & (gbTimerClockTicks>>1)) && + !(gbInternalTimer & (gbTimerClockTicks>>5))) + temp = true; + else if ((!gbTimerOn && !gbTimerModeChange && gbTimerOnChange ) && ((gbTimerTicks-1) < (gbTimerClockTicks>>1))) + temp = true; + else if (gbTimerOn && gbTimerModeChange && !gbTimerOnChange) + { + switch(gbTimerMode & 3) + { + case 0x00: + temp = false; + break; + case 0x01: + if (((gbInternalTimer & 0x82) == 2) && (gbTimerTicks>(clockTicks+1))) + temp = true; + break; + case 0x02: + if (((gbInternalTimer & 0x88) == 0x8) && (gbTimerTicks>(clockTicks+1))) + temp = true; + break; + case 0x03: + if (((gbInternalTimer & 0xA0) == 0x20) && (gbTimerTicks>(clockTicks+1))) + temp = true; + break; + } + } + + if (temp) + { + gbMemory[0xff05] = ++register_TIMA; + if((register_TIMA & 0xff) == 0) { + // timer overflow! + + // reload timer modulo + gbMemory[0xff05] = register_TIMA = register_TMA; + + // flag interrupt + gbMemory[0xff0f] = register_IF |= 4; + } + } + } + gbMemory[0xff07] = register_TAC = value; + return; + } + + case 0x0f: { + gbMemory[0xff0f] = register_IF = value; + //gbMemory[0xff0f] = register_IE |= value; + return; + } + + case 0x10: + case 0x11: + case 0x12: + case 0x13: + case 0x14: + case 0x16: + case 0x17: + case 0x18: + case 0x19: + case 0x1a: + case 0x1b: + case 0x1c: + case 0x1d: + case 0x1e: + case 0x20: + case 0x21: + case 0x22: + case 0x23: + case 0x24: + case 0x25: { + if (gbMemory[NR52] & 0x80) { + SOUND_EVENT(address,value); + return; + } + } + + case 0x26: { + SOUND_EVENT(address,value); + return; + } + + case 0x30: + case 0x31: + case 0x32: + case 0x33: + case 0x34: + case 0x35: + case 0x36: + case 0x37: + case 0x38: + case 0x39: + case 0x3a: + case 0x3b: + case 0x3c: + case 0x3d: + case 0x3e: + case 0x3f: { + SOUND_EVENT(address,value); + //gbMemory[address] = value; + return; + } + + case 0x40: { + int lcdChange = (register_LCDC & 0x80) ^ (value & 0x80); + + // don't draw the window if it was not enabled and not being drawn before + if(!(register_LCDC & 0x20) && (value & 0x20) && gbWindowLine == -1 && + register_LY > register_WY) + gbWindowLine = 146; + // 007 fix : don't draw the first window's 1st line if it's enable 'too late' + // (ie. if register_LY == register_WY when enabling it) + // and move it to the next line + else if (!(register_LCDC & 0x20) && (value & 0x20) && (register_LY == register_WY)) + gbWindowLine = -2; + + + gbMemory[0xff40] = register_LCDC = value; + + + if(lcdChange) { + if((value & 0x80) && (!register_LCDCBusy)) { + + // if (!gbWhiteScreen && !gbSgbMask) + + // systemDrawScreen(); + + + + gbRegisterLYLCDCOffOn = (register_LY + 144) % 154; + + gbLcdTicks = GBLCD_MODE_2_CLOCK_TICKS - (gbSpeed ? 2 : 1); + gbLcdTicksDelayed = GBLCD_MODE_2_CLOCK_TICKS - (gbSpeed ? 1 : 0); + gbLcdLYIncrementTicks = GBLY_INCREMENT_CLOCK_TICKS - (gbSpeed ? 2 : 1); + gbLcdLYIncrementTicksDelayed = GBLY_INCREMENT_CLOCK_TICKS - (gbSpeed ? 1 : 0); + gbLcdMode = 2; + gbLcdModeDelayed = 2; + gbMemory[0xff41] = register_STAT = (register_STAT & 0xfc) | 2; + gbMemory[0xff44] = register_LY = 0x00; + gbInt48Signal = 0; + gbLYChangeHappened = false; + gbLCDChangeHappened = false; + gbWindowLine = 146; + oldRegister_WY = 146; + + // Fix for Namco Gallery Vol.2 + // (along with updating register_LCDC at the start of 'case 0x40') : + if(register_STAT & 0x20) + { + // send LCD interrupt only if no interrupt 48h signal... + if (!gbInt48Signal) + { + gbMemory[0xff0f] = register_IF |= 2; + } + gbInt48Signal |= 4; + } + gbCompareLYToLYC(); + + } else { + + register_LCDCBusy = clockTicks+6; + + //used to update the screen with white lines when it's off. + //(it looks strange, but it's pretty accurate) + + gbWhiteScreen = 0; + + gbScreenTicks = ((150-register_LY)*GBLY_INCREMENT_CLOCK_TICKS + + (49<<(gbSpeed ? 1 : 0))); + + // disable the screen rendering + gbScreenOn = false; + gbLcdTicks = 0; + gbLcdMode = 0; + gbLcdModeDelayed = 0; + gbMemory[0xff41] = register_STAT &= 0xfc; + gbInt48Signal = 0; + } + } + return; + } + + // STAT + case 0x41: { + //register_STAT = (register_STAT & 0x87) | + // (value & 0x7c); + gbMemory[0xff41] = register_STAT = (value & 0xf8) | (register_STAT & 0x07); // fix ? + // GB bug from Devrs FAQ + // proper fix + gbInt48Signal &= ((register_STAT>>3) & 0xF); + + if((register_LCDC & 0x80)) { + if ((register_STAT & 0x08) && (gbLcdMode == 0)) + { + if (!gbInt48Signal) + { + gbMemory[0xff0f] = register_IF |=2; + } + gbInt48Signal |= 1; + } + if ((register_STAT & 0x10) && (gbLcdMode == 1)) + { + if (!gbInt48Signal) + { + gbMemory[0xff0f] = register_IF |=2; + } + gbInt48Signal |= 2; + } + if ((register_STAT & 0x20) && (gbLcdMode == 2)) + { + if (!gbInt48Signal) + { + gbMemory[0xff0f] = register_IF |=2; + } + gbInt48Signal |= 4; + } + gbCompareLYToLYC(); + + gbMemory[0xff0f] = register_IF; + gbMemory[0xff41] = register_STAT; + + } + return; + } + + // SCY + case 0x42: { + int temp = -1; + + if ((gbLcdMode == 3) || (gbLcdModeDelayed == 3)) + temp = ((GBLY_INCREMENT_CLOCK_TICKS-GBLCD_MODE_2_CLOCK_TICKS) - + gbLcdLYIncrementTicks); + + if (temp >=0) + { + for (int i=temp<<(gbSpeed ? 1 : 2);i<300;i++) + if (temp < 300) + gbSCYLine[i] = value; + } + + else + memset(gbSCYLine, value, sizeof(gbSCYLine)); + + gbMemory[0xff42] = register_SCY = value; + return; + } + + // SCX + case 0x43: { + int temp = -1; + + if (gbLcdModeDelayed == 3) + temp = ((GBLY_INCREMENT_CLOCK_TICKS-GBLCD_MODE_2_CLOCK_TICKS) - + gbLcdLYIncrementTicksDelayed); + + if (temp >=0) + { + for (int i=temp<<(gbSpeed ? 1 : 2);i<300;i++) + if (temp < 300) + gbSCXLine[i] = value; + } + + else + memset(gbSCXLine, value, sizeof(gbSCXLine)); + + gbMemory[0xff43] = register_SCX = value; + return; + } + + // LY + case 0x44: { + // read only + return; + } + + // LYC + case 0x45: { + if (register_LYC != value) + { + gbMemory[0xff45] = register_LYC = value; + if(register_LCDC & 0x80) { + gbCompareLYToLYC(); + } + } + return; + } + + // DMA! + case 0x46: { + int source = value * 0x0100; + + gbCopyMemory(0xfe00, + source, + 0xa0); + gbMemory[0xff46] = register_DMA = value; + return; + } + + // BGP + case 0x47: { + + int temp = -1; + + gbMemory[0xff47] = value; + + if (gbLcdModeDelayed == 3) + temp = ((GBLY_INCREMENT_CLOCK_TICKS-GBLCD_MODE_2_CLOCK_TICKS) - + gbLcdLYIncrementTicksDelayed); + + if (temp >=0) + { + for (int i=temp<<(gbSpeed ? 1 : 2);i<300;i++) + if (temp < 300) + gbBgpLine[i] = value; + } + else + memset(gbBgpLine,value,sizeof(gbBgpLine)); + + gbBgp[0] = value & 0x03; + gbBgp[1] = (value & 0x0c)>>2; + gbBgp[2] = (value & 0x30)>>4; + gbBgp[3] = (value & 0xc0)>>6; + break; + } + + // OBP0 + case 0x48: { + int temp = -1; + + gbMemory[0xff48] = value; + + if (gbLcdModeDelayed == 3) + temp = ((GBLY_INCREMENT_CLOCK_TICKS-GBLCD_MODE_2_CLOCK_TICKS) - + gbLcdLYIncrementTicksDelayed); + + if (temp >=0) + { + for (int i=temp<<(gbSpeed ? 1 : 2);i<300;i++) + if (temp < 300) + gbObp0Line[i] = value; + } + else + memset(gbObp0Line,value,sizeof(gbObp0Line)); + + gbObp0[0] = value & 0x03; + gbObp0[1] = (value & 0x0c)>>2; + gbObp0[2] = (value & 0x30)>>4; + gbObp0[3] = (value & 0xc0)>>6; + break; + } + + // OBP1 + case 0x49: { + int temp = -1; + + gbMemory[0xff49] = value; + + if (gbLcdModeDelayed == 3) + temp = ((GBLY_INCREMENT_CLOCK_TICKS-GBLCD_MODE_2_CLOCK_TICKS) - + gbLcdLYIncrementTicksDelayed); + + if (temp >=0) + { + for (int i=temp<<(gbSpeed ? 1 : 2);i<300;i++) + if (temp < 300) + gbObp1Line[i] = value; + } + else + memset(gbObp1Line,value,sizeof(gbObp1Line)); + + gbObp1[0] = value & 0x03; + gbObp1[1] = (value & 0x0c)>>2; + gbObp1[2] = (value & 0x30)>>4; + gbObp1[3] = (value & 0xc0)>>6; + break; + } + + // WY + case 0x4a: + gbMemory[0xff4a] = register_WY = value; + if ((register_LY <= register_WY) && ((gbWindowLine < 0) || (gbWindowLine>=144))) + { + gbWindowLine = -1; + oldRegister_WY = register_WY; + } + return; + + // WX + case 0x4b: + gbMemory[0xff4b] = register_WX = value; + return; + + // KEY1 + case 0x4d: { + if(gbCgbMode) { + gbMemory[0xff4d] = (gbMemory[0xff4d] & 0x80) | (value & 1) | 0x7e; + return; + } + } + break; + + // VBK + case 0x4f: { + if(gbCgbMode) { + value = value & 1; + if(value == gbVramBank) + return; + + int vramAddress = value * 0x2000; + gbMemoryMap[0x08] = &gbVram[vramAddress]; + gbMemoryMap[0x09] = &gbVram[vramAddress + 0x1000]; + + gbVramBank = value; + register_VBK = value; + } + return; + } + break; + + // BOOTROM disable register (also gbCgbMode enabler if value & 0x10 ?) + case 0x50 : + { + if (useBios && inBios && !skipBios && (value & 1)) + { + gbMemoryMap[0x00] = &gbRom[0x0000]; + memcpy ((u8 *)(gbRom+0x100), (u8 *)(gbMemory + 0x100), 0xF00); + inBios = false; + } + } + + // HDMA1 + case 0x51: { + if(gbCgbMode) { + if(value > 0x7f && value < 0xa0) + value = 0; + + gbHdmaSource = (value << 8) | (gbHdmaSource & 0xf0); + + register_HDMA1 = value; + return; + } + } + break; + + // HDMA2 + case 0x52: { + if(gbCgbMode) { + value = value & 0xf0; + + gbHdmaSource = (gbHdmaSource & 0xff00) | (value); + + register_HDMA2 = value; + return; + } + } + break; + + // HDMA3 + case 0x53: { + if(gbCgbMode) { + value = value & 0x1f; + gbHdmaDestination = (value << 8) | (gbHdmaDestination & 0xf0); + gbHdmaDestination |= 0x8000; + register_HDMA3 = value; + return; + } + } + break; + + // HDMA4 + case 0x54: { + if(gbCgbMode) { + value = value & 0xf0; + gbHdmaDestination = (gbHdmaDestination & 0x1f00) | value; + gbHdmaDestination |= 0x8000; + register_HDMA4 = value; + return; + } + } + break; + + // HDMA5 + case 0x55: { + + if(gbCgbMode) { + gbHdmaBytes = 16 + (value & 0x7f) * 16; + if(gbHdmaOn) { + if(value & 0x80) { + gbMemory[0xff55] = register_HDMA5 = (value & 0x7f); + } else { + register_HDMA5 = 0xff; + gbHdmaOn = 0; + } + } else { + if(value & 0x80) { + gbHdmaOn = 1; + gbMemory[0xff55] = register_HDMA5 = value & 0x7f; + if(gbLcdModeDelayed == 0) + gbDoHdma(); + } else { + // we need to take the time it takes to complete the transfer into + // account... according to GB DEV FAQs, the setup time is the same + // for single and double speed, but the actual transfer takes the + // same time + if(gbSpeed) + gbDmaTicks = 2+16 * ((value & 0x7f) +1); + else + gbDmaTicks = 1+8 * ((value & 0x7f)+1); + + gbCopyMemory((gbHdmaDestination & 0x1ff0) | 0x8000, + gbHdmaSource & 0xfff0, + gbHdmaBytes); + gbHdmaDestination += gbHdmaBytes; + gbHdmaSource += gbHdmaBytes; + + gbMemory[0xff51] = register_HDMA1 = 0xff;// = (gbHdmaSource >> 8) & 0xff; + gbMemory[0xff52] = register_HDMA2 = 0xff;// = gbHdmaSource & 0xf0; + gbMemory[0xff53] = register_HDMA3 = 0xff;// = ((gbHdmaDestination - 0x8000) >> 8) & 0x1f; + gbMemory[0xff54] = register_HDMA4 = 0xff;// = gbHdmaDestination & 0xf0; + gbMemory[0xff55] = register_HDMA5 = 0xff; + } + } + return; + } + } + break; + + // BCPS + case 0x68: { + if(gbCgbMode) { + int paletteIndex = (value & 0x3f) >> 1; + int paletteHiLo = (value & 0x01); + + gbMemory[0xff68] = value; + + gbMemory[0xff69] = (paletteHiLo ? + (gbPalette[paletteIndex] >> 8) : + (gbPalette[paletteIndex] & 0x00ff)); + return; + } + } + break; + + // BCPD + case 0x69: { + if(gbCgbMode) { + int v = gbMemory[0xff68]; + int paletteIndex = (v & 0x3f) >> 1; + int paletteHiLo = (v & 0x01); + + // No access to gbPalette during mode 3 (Color Panel Demo) + if (((gbLcdModeDelayed != 3) && (!((gbLcdMode == 0) && (gbLcdTicks>=(GBLCD_MODE_0_CLOCK_TICKS-gbSpritesTicks[299]-1)))) && (!gbSpeed)) || + (gbSpeed && ((gbLcdMode == 1) || (gbLcdMode == 2) || + ((gbLcdMode == 3) && (gbLcdTicks>(GBLCD_MODE_3_CLOCK_TICKS-2))) || + ((gbLcdMode == 0) && (gbLcdTicks<=(GBLCD_MODE_0_CLOCK_TICKS-gbSpritesTicks[299]-2)))))) + { + gbMemory[0xff69] = value; + gbPalette[paletteIndex] = (paletteHiLo ? + ((value << 8) | (gbPalette[paletteIndex] & 0xff)) : + ((gbPalette[paletteIndex] & 0xff00) | (value))) & 0x7fff; + } + + + if(gbMemory[0xff68] & 0x80) { + int index = ((gbMemory[0xff68] & 0x3f) + 1) & 0x3f; + + gbMemory[0xff68] = (gbMemory[0xff68] & 0x80) | index; + gbMemory[0xff69] = (index & 1 ? + (gbPalette[index>>1] >> 8) : + (gbPalette[index>>1] & 0x00ff)); + } + return; + } + } + break; + + // OCPS + case 0x6a: { + if(gbCgbMode) { + int paletteIndex = (value & 0x3f) >> 1; + int paletteHiLo = (value & 0x01); + + paletteIndex += 32; + + gbMemory[0xff6a] = value; + + gbMemory[0xff6b] = (paletteHiLo ? + (gbPalette[paletteIndex] >> 8) : + (gbPalette[paletteIndex] & 0x00ff)); + return; + } + } + break; + + // OCPD + case 0x6b: { + + if(gbCgbMode) { + int v = gbMemory[0xff6a]; + int paletteIndex = (v & 0x3f) >> 1; + int paletteHiLo = (v & 0x01); + + paletteIndex += 32; + + // No access to gbPalette during mode 3 (Color Panel Demo) + if (((gbLcdModeDelayed != 3) && (!((gbLcdMode == 0) && (gbLcdTicks>=(GBLCD_MODE_0_CLOCK_TICKS-gbSpritesTicks[299]-1)))) && (!gbSpeed)) || + (gbSpeed && ((gbLcdMode == 1) || (gbLcdMode == 2) || + ((gbLcdMode == 3) && (gbLcdTicks>(GBLCD_MODE_3_CLOCK_TICKS-2))) || + ((gbLcdMode == 0) && (gbLcdTicks<=(GBLCD_MODE_0_CLOCK_TICKS-gbSpritesTicks[299]-2)))))) + { + gbMemory[0xff6b] = value; + gbPalette[paletteIndex] = (paletteHiLo ? + ((value << 8) | (gbPalette[paletteIndex] & 0xff)) : + ((gbPalette[paletteIndex] & 0xff00) | (value))) & 0x7fff; + } + + if(gbMemory[0xff6a] & 0x80) { + int index = ((gbMemory[0xff6a] & 0x3f) + 1) & 0x3f; + + gbMemory[0xff6a] = (gbMemory[0xff6a] & 0x80) | index; + + gbMemory[0xff6b] = (index & 1 ? + (gbPalette[(index>>1) + 32] >> 8) : + (gbPalette[(index>>1) + 32] & 0x00ff)); + } + return; + } + } + break; + + case 0x6c: { + gbMemory[0xff6c] = 0xfe | value; + return; + } + + + // SVBK + case 0x70: { + if(gbCgbMode) { + value = value & 7; + + int bank = value; + if(value == 0) + bank = 1; + + if(bank == gbWramBank) + return; + + int wramAddress = bank * 0x1000; + gbMemoryMap[0x0d] = &gbWram[wramAddress]; + + gbWramBank = bank; + gbMemory[0xff70] = register_SVBK = value; + return; + } + } + + case 0x75:{ + gbMemory[0xff75] = 0x8f | value; + return; + } + + case 0xff: { + gbMemory[0xffff] = register_IE = value; + return; + } + } + + if(address < 0xff80) + { + gbMemory[address] = value; + return; + } + + gbMemory[address] = value; +} + +u8 gbReadOpcode(register u16 address) +{ + if(gbCheatMap[address]) + return gbCheatRead(address); + + if(address < 0x8000) + return gbMemoryMap[address>>12][address&0x0fff]; + + if (address < 0xa000) + { + // A lot of 'ugly' checks... But only way to emulate this particular behaviour... + if ( + ( + (gbHardware & 0xa) && + ( + (gbLcdModeDelayed != 3) || + ( + ((register_LY == 0) && (gbScreenOn == false) && (register_LCDC & 0x80)) && + (gbLcdLYIncrementTicksDelayed == (GBLY_INCREMENT_CLOCK_TICKS - GBLCD_MODE_2_CLOCK_TICKS)) + ) + ) + ) + || + ( + (gbHardware & 0x5) && + (gbLcdModeDelayed != 3) && + ( + (gbLcdMode != 3) || + ((register_LY == 0) && ((gbScreenOn == false) && (register_LCDC & 0x80)) && + (gbLcdLYIncrementTicks == (GBLY_INCREMENT_CLOCK_TICKS - GBLCD_MODE_2_CLOCK_TICKS))) + ) + ) + ) + return gbMemoryMap[address >> 12][address & 0x0fff]; + + return 0xff; + } + + // Used for the mirroring of 0xC000 in 0xE000 + if ((address >= 0xe000) && (address < 0xfe00)) + address &= ~0x2000; + + switch(address & 0xf000) { + case 0x0a: + case 0x0b: + if(mapperReadRAM) + return mapperReadRAM(address); + break; + case 0x0f: + if(address > 0xff00) { + switch(address & 0x00ff) { + case 0x02: + return (gbMemory[0xff02]); + case 0x03: + return (0xff); + case 0x04: + return register_DIV; + case 0x05: + return register_TIMA; + case 0x06: + return register_TMA; + case 0x07: + return (0xf8 | register_TAC); + case 0x08: + case 0x09: + case 0x0a: + case 0x0b: + case 0x0c: + case 0x0d: + case 0x0e: + return (0xff); + case 0x0f: + return (0xe0 | gbMemory[0xff0f]); + case 0x40: + return register_LCDC; + case 0x41: + // This is a GB/C only bug (ie. not GBA/SP). + if ((gbHardware & 7) && (gbLcdMode == 2) && (gbLcdModeDelayed == 1) && (!gbSpeed)) + return (0x80 | (gbMemory[0xff41] & 0xFC)); + else + return (0x80 | gbMemory[0xff41]); + case 0x42: + return register_SCY; + case 0x43: + return register_SCX; + case 0x44: + if (((gbHardware & 7) && ((gbLcdMode == 1) && (gbLcdTicks == 0x71))) || + (!(register_LCDC && 0x80))) + return 0; + else + return register_LY; + case 0x45: + return register_LYC; + case 0x46: + return register_DMA; + case 0x4a: + return register_WY; + case 0x4b: + return register_WX; + case 0x4c: + return 0xff; + case 0x4f: + return (0xfe | register_VBK); + case 0x51: + return register_HDMA1; + case 0x52: + return register_HDMA2; + case 0x53: + return register_HDMA3; + case 0x54: + return register_HDMA4; + case 0x55: + return register_HDMA5; + case 0x68: + case 0x6a: + if (gbCgbMode) + return (0x40 | gbMemory[address]); + else + return 0xc0; + case 0x69: + case 0x6b: + if (gbCgbMode) + { + // No access to gbPalette during mode 3 (Color Panel Demo) + if (((gbLcdModeDelayed != 3) && (!((gbLcdMode == 0) && (gbLcdTicks>=(GBLCD_MODE_0_CLOCK_TICKS-gbSpritesTicks[299]-1)))) && (!gbSpeed)) || + (gbSpeed && ((gbLcdMode == 1) || (gbLcdMode == 2) || + ((gbLcdMode == 3) && (gbLcdTicks>(GBLCD_MODE_3_CLOCK_TICKS-2))) || + ((gbLcdMode == 0) && (gbLcdTicks<=(GBLCD_MODE_0_CLOCK_TICKS-gbSpritesTicks[299]-2)))))) + return (gbMemory[address]); + else + return 0xff; + } + else + return 0xff; + case 0x70: + if (gbCgbMode) + return (0xf8 | register_SVBK); + else + return 0xff; + case 0xff: + return register_IE; + } + } + // OAM not accessible during mode 2 & 3. + if(((address >= 0xfe00) && (address<0xfea0)) && + ((gbLcdMode | gbLcdModeDelayed) &2)) + return 0xff; + break; + } + + if ((address >= 0xfea0) && (address < 0xff00)) + { + if (gbHardware & 1) + return ((((address + ((address >> 4) - 0xfea)) >> 2) & 1) ? 0x00 : 0xff ); + else if (gbHardware & 2) + return gbMemoryMap[address>>12][address & 0x0fff]; + else if (gbHardware & 4) + return ((((address + ((address >> 4) - 0xfea)) >> 2) & 1) ? 0xff : 0x00 ); + else if (gbHardware & 8) + return ((address & 0xf0) |((address & 0xf0)>>4)); + } + + return gbMemoryMap[address>>12][address & 0x0fff]; +} + +u8 gbReadMemory(register u16 address) +{ + if(gbCheatMap[address]) + return gbCheatRead(address); + + + if(address < 0x8000) + return gbMemoryMap[address>>12][address&0x0fff]; + + + if (address < 0xa000) + { + // A lot of 'ugly' checks... But only way to emulate this particular behaviour... + if ( + ( + (gbHardware & 0xa) && + ( + (gbLcdModeDelayed != 3) || + ( + ((register_LY == 0) && (gbScreenOn == false) && (register_LCDC & 0x80)) && + (gbLcdLYIncrementTicksDelayed == (GBLY_INCREMENT_CLOCK_TICKS - GBLCD_MODE_2_CLOCK_TICKS)) + ) + ) + ) + || + ( + (gbHardware & 0x5) && + (gbLcdModeDelayed != 3) && + ( + (gbLcdMode != 3) || + ((register_LY == 0) && ((gbScreenOn == false) && (register_LCDC & 0x80)) && + (gbLcdLYIncrementTicks == (GBLY_INCREMENT_CLOCK_TICKS - GBLCD_MODE_2_CLOCK_TICKS))) + ) + ) + ) + return gbMemoryMap[address >> 12][address & 0x0fff]; + + return 0xff; + } + + if ((address >= 0xe000) && (address < 0xfe00)) + address &= ~0x2000; + + if(address < 0xc000) { +#ifndef FINAL_VERSION + if(memorydebug) { + log("Memory register read %04x PC=%04x\n", + address, + PC.W); + } +#endif + + // for the 2kb ram limit (fixes crash in shawu's story + // but now its sram test fails, as the it expects 8kb and not 2kb... + // So use the 'genericflashcard' option to fix it). + if (address<=(0xa000+gbRamSizeMask)) + { + if(mapperReadRAM) + return mapperReadRAM(address); + return gbMemoryMap[address>>12][address & 0x0fff]; + } + return 0xff; + } + + if(address >= 0xff00) { + if ( address >= 0xFF10 && address <= 0xFF3F ) + return gbSoundRead( address ); + + switch(address & 0x00ff) { + case 0x00: + { + if(gbSgbMode) { + gbSgbReadingController |= 4; + gbSgbResetPacketState(); + } + + int b = gbMemory[0xff00]; + + if((b & 0x30) == 0x20) { + b &= 0xf0; + + int joy = 0; + if(gbSgbMode && gbSgbMultiplayer) { + switch(gbSgbNextController) { + case 0x0f: + joy = 0; + break; + case 0x0e: + joy = 1; + break; + case 0x0d: + joy = 2; + break; + case 0x0c: + joy = 3; + break; + default: + joy = 0; + } + } + int joystate = gbJoymask[joy]; + if(!(joystate & 128)) + b |= 0x08; + if(!(joystate & 64)) + b |= 0x04; + if(!(joystate & 32)) + b |= 0x02; + if(!(joystate & 16)) + b |= 0x01; + + gbMemory[0xff00] = b; + } else if((b & 0x30) == 0x10) { + b &= 0xf0; + + int joy = 0; + if(gbSgbMode && gbSgbMultiplayer) { + switch(gbSgbNextController) { + case 0x0f: + joy = 0; + break; + case 0x0e: + joy = 1; + break; + case 0x0d: + joy = 2; + break; + case 0x0c: + joy = 3; + break; + default: + joy = 0; + } + } + int joystate = gbJoymask[joy]; + if(!(joystate & 8)) + b |= 0x08; + if(!(joystate & 4)) + b |= 0x04; + if(!(joystate & 2)) + b |= 0x02; + if(!(joystate & 1)) + b |= 0x01; + + gbMemory[0xff00] = b; + } else { + if(gbSgbMode && gbSgbMultiplayer) { + gbMemory[0xff00] = 0xf0 | gbSgbNextController; + } else { + gbMemory[0xff00] = 0xff; + } + } + } + return gbMemory[0xff00]; + break; + case 0x01: + return gbMemory[0xff01]; + case 0x02: + return (gbMemory[0xff02]); + case 0x04: + return register_DIV; + case 0x05: + return register_TIMA; + case 0x06: + return register_TMA; + case 0x07: + return (0xf8 | register_TAC); + case 0x0f: + return (0xe0 | gbMemory[0xff0f]); + case 0x30: + case 0x31: + case 0x32: + case 0x33: + case 0x34: + case 0x35: + case 0x36: + case 0x37: + case 0x38: + case 0x39: + case 0x3A: + case 0x3B: + case 0x3C: + case 0x3D: + case 0x3E: + case 0x3F: + if ((gbMemory[NR30] & 0x80) && (gbMemory[NR34] & 0x80)) + return 0xFF; + else + return gbMemoryMap[address>>12][address & 0x0fff]; + case 0x40: + return register_LCDC; + case 0x41: + // This is a GB/C only bug (ie. not GBA/SP). + if ((gbHardware & 7) && (gbLcdMode == 2) && (gbLcdModeDelayed == 1) && (!gbSpeed)) + return (0x80 | (gbMemory[0xff41] & 0xFC)); + else + return (0x80 | gbMemory[0xff41]); + case 0x42: + return register_SCY; + case 0x43: + return register_SCX; + case 0x44: + if (((gbHardware & 7) && ((gbLcdMode == 1) && (gbLcdTicks == 0x71))) || + (!(register_LCDC && 0x80))) + return (0); + else + return register_LY; + case 0x45: + return register_LYC; + case 0x46: + return register_DMA; + case 0x4a: + return register_WY; + case 0x4b: + return register_WX; + case 0x4f: + return (0xfe | register_VBK); + case 0x51: + return register_HDMA1; + case 0x52: + return register_HDMA2; + case 0x53: + return register_HDMA3; + case 0x54: + return register_HDMA4; + case 0x55: + return register_HDMA5; + case 0x68: + case 0x6a: + if (gbCgbMode) + return (0x40 | gbMemory[address]); + else + return 0xc0; + case 0x69: + case 0x6b: + if (gbCgbMode) + { + // No access to gbPalette during mode 3 (Color Panel Demo) + if (((gbLcdModeDelayed != 3) && (!((gbLcdMode == 0) && (gbLcdTicks>=(GBLCD_MODE_0_CLOCK_TICKS-gbSpritesTicks[299]-1)))) && (!gbSpeed)) || + (gbSpeed && ((gbLcdMode == 1) || (gbLcdMode == 2) || + ((gbLcdMode == 3) && (gbLcdTicks>(GBLCD_MODE_3_CLOCK_TICKS-2))) || + ((gbLcdMode == 0) && (gbLcdTicks<=(GBLCD_MODE_0_CLOCK_TICKS-gbSpritesTicks[299]-2)))))) + return (gbMemory[address]); + else + return 0xff; + } + else + return 0xff; + case 0x70: + if (gbCgbMode) + return (0xf8 | register_SVBK); + else + return 0xff; + case 0xff: + return register_IE; + } + } + // OAM not accessible during mode 2 & 3. + if(((address >= 0xfe00) && (address<0xfea0)) && + ((((gbLcdMode | gbLcdModeDelayed) & 2) && + (!(gbSpeed && (gbHardware & 0x2) && !(gbLcdModeDelayed & 2) && (gbLcdMode == 2)))) || + (gbSpeed && (gbHardware & 0x2) && (gbLcdModeDelayed == 0) && (gbLcdTicksDelayed == (GBLCD_MODE_0_CLOCK_TICKS-gbSpritesTicks[299]))))) + return 0xff; + + if ((address >= 0xfea0) && (address < 0xff00)) + { + if (gbHardware & 1) + return ((((address + ((address >> 4) - 0xfea)) >> 2) & 1) ? 0x00 : 0xff ); + else if (gbHardware & 2) + return gbMemoryMap[address>>12][address & 0x0fff]; + else if (gbHardware & 4) + return ((((address + ((address >> 4) - 0xfea)) >> 2) & 1) ? 0xff : 0x00 ); + else if (gbHardware & 8) + return ((address & 0xf0) |((address & 0xf0)>>4)); + } + + return gbMemoryMap[address>>12][address & 0x0fff]; +} + +void gbVblank_interrupt() +{ + gbCheatWrite(false); // Emulates GS codes. + gbMemory[0xff0f] = register_IF &= 0xfe; + gbWriteMemory(--SP.W, PC.B.B1); + gbWriteMemory(--SP.W, PC.B.B0); + PC.W = 0x40; +} + +void gbLcd_interrupt() +{ + gbMemory[0xff0f] = register_IF &= 0xfd; + gbWriteMemory(--SP.W, PC.B.B1); + gbWriteMemory(--SP.W, PC.B.B0); + PC.W = 0x48; +} + +void gbTimer_interrupt() +{ + gbMemory[0xff0f] = register_IF &= 0xfb; + gbWriteMemory(--SP.W, PC.B.B1); + gbWriteMemory(--SP.W, PC.B.B0); + PC.W = 0x50; +} + +void gbSerial_interrupt() +{ + gbMemory[0xff0f] = register_IF &= 0xf7; + gbWriteMemory(--SP.W, PC.B.B1); + gbWriteMemory(--SP.W, PC.B.B0); + PC.W = 0x58; +} + +void gbJoypad_interrupt() +{ + gbMemory[0xff0f] = register_IF &= 0xef; + gbWriteMemory(--SP.W, PC.B.B1); + gbWriteMemory(--SP.W, PC.B.B0); + PC.W = 0x60; +} + +void gbSpeedSwitch() +{ + gbBlackScreen = true; + if(gbSpeed == 0) { + gbSpeed = 1; + GBLCD_MODE_0_CLOCK_TICKS = 51 * 2; + GBLCD_MODE_1_CLOCK_TICKS = 1140 * 2; + GBLCD_MODE_2_CLOCK_TICKS = 20 * 2; + GBLCD_MODE_3_CLOCK_TICKS = 43 * 2; + GBLY_INCREMENT_CLOCK_TICKS = 114 * 2; + GBDIV_CLOCK_TICKS = 64; + GBTIMER_MODE_0_CLOCK_TICKS = 256; + GBTIMER_MODE_1_CLOCK_TICKS = 4; + GBTIMER_MODE_2_CLOCK_TICKS = 16; + GBTIMER_MODE_3_CLOCK_TICKS = 64; + GBSERIAL_CLOCK_TICKS = 128 * 2; + gbLcdTicks *= 2; + gbLcdTicksDelayed *=2; + gbLcdTicksDelayed--; + gbLcdLYIncrementTicks *= 2; + gbLcdLYIncrementTicksDelayed *= 2; + gbLcdLYIncrementTicksDelayed--; + gbSerialTicks *= 2; + //SOUND_CLOCK_TICKS = soundQuality * 24 * 2; + //soundTicks *= 2; + gbLine99Ticks = 3; + } else { + gbSpeed = 0; + GBLCD_MODE_0_CLOCK_TICKS = 51; + GBLCD_MODE_1_CLOCK_TICKS = 1140; + GBLCD_MODE_2_CLOCK_TICKS = 20; + GBLCD_MODE_3_CLOCK_TICKS = 43; + GBLY_INCREMENT_CLOCK_TICKS = 114; + GBDIV_CLOCK_TICKS = 64; + GBTIMER_MODE_0_CLOCK_TICKS = 256; + GBTIMER_MODE_1_CLOCK_TICKS = 4; + GBTIMER_MODE_2_CLOCK_TICKS = 16; + GBTIMER_MODE_3_CLOCK_TICKS = 64; + GBSERIAL_CLOCK_TICKS = 128; + gbLcdTicks >>= 1; + gbLcdTicksDelayed++; + gbLcdTicksDelayed >>=1; + gbLcdLYIncrementTicks >>= 1; + gbLcdLYIncrementTicksDelayed++; + gbLcdLYIncrementTicksDelayed >>= 1; + gbSerialTicks /= 2; + //SOUND_CLOCK_TICKS = soundQuality * 24; + //soundTicks /= 2; + gbLine99Ticks = 1; + if (gbHardware & 8) + gbLine99Ticks++; + } + gbDmaTicks += (134)*GBLY_INCREMENT_CLOCK_TICKS + (37<<(gbSpeed ? 1 : 0)); +} + +bool CPUIsGBBios(const char * file) +{ + if(strlen(file) > 4) { + const char * p = strrchr(file,'.'); + + if(p != NULL) { + if(_stricmp(p, ".gb") == 0) + return true; + if(_stricmp(p, ".bin") == 0) + return true; + if(_stricmp(p, ".bios") == 0) + return true; + if(_stricmp(p, ".rom") == 0) + return true; + } + } + + return false; +} + +void gbCPUInit(const char *biosFileName, bool useBiosFile) +{ + useBios = false; + if (useBiosFile) + { + int size = 0x100; + if(utilLoad(biosFileName, + CPUIsGBBios, + bios, + size)) { + if(size == 0x100) + useBios = true; + else + systemMessage(MSG_INVALID_BIOS_FILE_SIZE, N_("Invalid BOOTROM file size")); + } + } +} + +void gbGetHardwareType() +{ + gbCgbMode = 0; + gbSgbMode = 0; + if(gbRom[0x143] & 0x80) { + if((gbEmulatorType == 0) || + gbEmulatorType == 1 || + gbEmulatorType == 4) { + gbCgbMode = 1; + } + } + + if((gbCgbMode == 0 ) && (gbRom[0x146] == 0x03)) { + if(gbEmulatorType == 0 || + gbEmulatorType == 2 || + gbEmulatorType == 5) + gbSgbMode = 1; + } + + gbHardware = 1; // GB + if (((gbCgbMode == 1) && (gbEmulatorType == 0)) || (gbEmulatorType == 1)) + gbHardware = 2; // GBC + else if (((gbSgbMode == 1) && (gbEmulatorType == 0)) || (gbEmulatorType == 2) || (gbEmulatorType == 5)) + gbHardware = 4; // SGB(2) + else if (gbEmulatorType == 4) + gbHardware = 8; // GBA + + gbGBCColorType = 0; + if (gbHardware & 8) // If GBA is selected, choose the GBA default settings. + gbGBCColorType = 2; // (0 = GBC, 1 = GBA, 2 = GBASP) +} + +void gbReset() +{ + gbGetHardwareType(); + + oldRegister_WY = 146; + gbInterruptLaunched = 0; + + if(gbCgbMode == 1) { + if (gbVram == NULL) + gbVram = (u8 *)malloc(0x4000); + if (gbWram == NULL) + gbWram = (u8 *)malloc(0x8000); + memset(gbVram,0,0x4000); + memset(gbPalette,0, 2*128); + } + else + { + if(gbVram != NULL) { + free(gbVram); + gbVram = NULL; + } + if(gbWram != NULL) { + free(gbWram); + gbWram = NULL; + } + } + + gbLYChangeHappened = false; + gbLCDChangeHappened = false; + gbBlackScreen = false; + gbInterruptWait = 0; + gbDmaTicks = 0; + clockTicks = 0; + + if(gbSpeed) { + gbSpeedSwitch(); + gbMemory[0xff4d] = 0; + } + + // clean Wram + // This kinda emulates the startup state of Wram on GB/C (not very accurate, + // but way closer to the reality than filling it with 00es or FFes). + // On GBA/GBASP, it's kinda filled with random data. + // In all cases, most of the 2nd bank is filled with 00s. + // The starting data are important for some 'buggy' games, like Buster Brothers or + // Karamuchou ha Oosawagi!. + if (gbMemory != NULL) + { + memset(gbMemory,0xff, 65536); + for (int temp = 0xC000; temp < 0xE000; temp++) + if ((temp & 0x8) ^((temp & 0x800)>>8)) + { + if ((gbHardware & 0x02) && (gbGBCColorType == 0)) + gbMemory[temp] = 0x0; + else + gbMemory[temp] = 0x0f; + } + + else + gbMemory[temp] = 0xff; + } + + // GB bios set this memory area to 0 + // Fixes Pitman (J) title screen + if (gbHardware & 0x1) { + memset(&gbMemory[0x8000], 0x0, 0x2000); + } + + // clean LineBuffer + if (gbLineBuffer != NULL) + memset(gbLineBuffer, 0, sizeof(gbLineBuffer)); + // clean Pix + if (pix != NULL) + memset(pix, 0, sizeof(pix)); + // clean Vram + if (gbVram != NULL) + memset(gbVram, 0, 0x4000); + // clean Wram 2 + // This kinda emulates the startup state of Wram on GBC (not very accurate, + // but way closer to the reality than filling it with 00es or FFes). + // On GBA/GBASP, it's kinda filled with random data. + // In all cases, most of the 2nd bank is filled with 00s. + // The starting data are important for some 'buggy' games, like Buster Brothers or + // Karamuchou ha Oosawagi! + if (gbWram != NULL) + { + for (int i = 0; i<8; i++) + if (i != 2) + memcpy ((u16 *)(gbWram+i*0x1000), (u16 *)(gbMemory+0xC000), 0x1000); + } + + memset(gbSCYLine,0,sizeof(gbSCYLine)); + memset(gbSCXLine,0,sizeof(gbSCXLine)); + memset(gbBgpLine,0xfc,sizeof(gbBgpLine)); + if (gbHardware & 5) + { + memset(gbObp0Line,0xff,sizeof(gbObp0Line)); + memset(gbObp1Line,0xff,sizeof(gbObp1Line)); + } + else + { + memset(gbObp0Line,0x0,sizeof(gbObp0Line)); + memset(gbObp1Line,0x0,sizeof(gbObp1Line)); + } + memset(gbSpritesTicks,0x0,sizeof(gbSpritesTicks)); + + SP.W = 0xfffe; + AF.W = 0x01b0; + BC.W = 0x0013; + DE.W = 0x00d8; + HL.W = 0x014d; + PC.W = 0x0100; + IFF = 0; + gbInt48Signal = 0; + + register_TIMA = 0; + register_TMA = 0; + register_TAC = 0; + gbMemory[0xff0f] = register_IF = 1; + gbMemory[0xff40] = register_LCDC = 0x91; + gbMemory[0xff47] = 0xfc; + + if (gbCgbMode) + gbMemory[0xff4d] = 0x7e; + else + gbMemory[0xff4d] = 0xff; + + if (!gbCgbMode) + gbMemory[0xff70] = gbMemory[0xff74] = 0xff; + + if (gbCgbMode) + gbMemory[0xff56] = 0x3e; + else + gbMemory[0xff56] = 0xff; + + register_SCY = 0; + register_SCX = 0; + register_LYC = 0; + register_DMA = 0xff; + register_WY = 0; + register_WX = 0; + register_VBK = 0; + register_HDMA1 = 0xff; + register_HDMA2 = 0xff; + register_HDMA3 = 0xff; + register_HDMA4 = 0xff; + register_HDMA5 = 0xff; + register_SVBK = 0; + register_IE = 0; + + if (gbCgbMode) + gbMemory[0xff02] = 0x7c; + else + gbMemory[0xff02] = 0x7e; + + gbMemory[0xff03] = 0xff; + int i; + for (i = 0x8; i<0xf; i++) + gbMemory[0xff00+i] = 0xff; + + gbMemory[0xff13] = 0xff; + gbMemory[0xff15] = 0xff; + gbMemory[0xff18] = 0xff; + gbMemory[0xff1d] = 0xff; + gbMemory[0xff1f] = 0xff; + + for (i = 0x27; i<0x30; i++) + gbMemory[0xff00+i] = 0xff; + + gbMemory[0xff4c] = 0xff; + gbMemory[0xff4e] = 0xff; + gbMemory[0xff50] = 0xff; + + for (i = 0x57; i<0x68; i++) + gbMemory[0xff00+i] = 0xff; + + for (i = 0x5d; i<0x70; i++) + gbMemory[0xff00+i] = 0xff; + + gbMemory[0xff71] = 0xff; + + for (i = 0x78; i<0x80; i++) + gbMemory[0xff00+i] = 0xff; + + if (gbHardware & 0xa) + { + + if (gbHardware & 2) + { + AF.W = 0x1180; + BC.W = 0x0000; + } + else + { + AF.W = 0x1100; + BC.W = 0x0100; // GBA/SP have B = 0x01 (which means GBC & GBA/SP bootrom are different !) + } + + gbMemory[0xff26] = 0xf1; + if (gbCgbMode) + { + + gbMemory[0xff31] = 0xff; + gbMemory[0xff33] = 0xff; + gbMemory[0xff35] = 0xff; + gbMemory[0xff37] = 0xff; + gbMemory[0xff39] = 0xff; + gbMemory[0xff3b] = 0xff; + gbMemory[0xff3d] = 0xff; + + gbMemory[0xff44] = register_LY = 0x90; + gbDivTicks = 0x19 + ((gbHardware & 2) >> 1); + gbInternalTimer = 0x58 + ((gbHardware & 2) >> 1); + gbLcdTicks = GBLCD_MODE_1_CLOCK_TICKS - + (register_LY-0x8F)*GBLY_INCREMENT_CLOCK_TICKS + 72 + ((gbHardware & 2) >> 1); + gbLcdLYIncrementTicks = 72 + ((gbHardware & 2) >> 1); + gbMemory[0xff04] = register_DIV = 0x1E; + } + else + { + gbMemory[0xff44] = register_LY = 0x94; + gbDivTicks = 0x22 + ((gbHardware & 2) >> 1); + gbInternalTimer = 0x61 + ((gbHardware & 2) >> 1); + gbLcdTicks = GBLCD_MODE_1_CLOCK_TICKS - + (register_LY-0x8F)*GBLY_INCREMENT_CLOCK_TICKS + 25 + ((gbHardware & 2) >> 1); + gbLcdLYIncrementTicks = 25 + ((gbHardware & 2) >> 1); + gbMemory[0xff04] = register_DIV = 0x26; + } + + + DE.W = 0xff56; + HL.W = 0x000d; + + register_HDMA5 = 0xff; + gbMemory[0xff68] = 0xc0; + gbMemory[0xff6a] = 0xc0; + + + gbMemory[0xff41] = register_STAT = 0x81; + gbLcdMode = 1; + } + else + { + if (gbHardware & 4) + { + if(gbEmulatorType == 5) + AF.W = 0xffb0; + else + AF.W = 0x01b0; + BC.W = 0x0013; + DE.W = 0x00d8; + HL.W = 0x014d; + } + gbDivTicks = 14; + gbInternalTimer = gbDivTicks--; + gbMemory[0xff04] = register_DIV = 0xAB; + gbMemory[0xff41] = register_STAT = 0x85; + gbMemory[0xff44] = register_LY = 0x00; + gbLcdTicks = 15; + gbLcdLYIncrementTicks = 114+gbLcdTicks; + gbLcdMode = 1; + + // used for the handling of the gb Boot Rom + if ((gbHardware & 5) && (bios != NULL) && useBios && !skipBios) + { + memcpy ((u8 *)(gbMemory), (u8 *)(gbRom), 0x1000); + memcpy ((u8 *)(gbMemory), (u8 *)(bios), 0x100); + gbWhiteScreen = 0; + + gbInternalTimer = 0x3e; + gbDivTicks = 0x3f; + gbMemory[0xff04] = register_DIV = 0x00; + PC.W = 0x0000; + register_LCDC = 0x11; + gbScreenOn = false; + gbLcdTicks = 0; + gbLcdMode = 0; + gbLcdModeDelayed = 0; + gbMemory[0xff41] = register_STAT &= 0xfc; + gbInt48Signal = 0; + gbLcdLYIncrementTicks = GBLY_INCREMENT_CLOCK_TICKS; + } + } + + gbLine99Ticks = 1; + if (gbHardware & 8) + gbLine99Ticks++; + + gbLcdModeDelayed = gbLcdMode; + gbLcdTicksDelayed = gbLcdTicks+1; + gbLcdLYIncrementTicksDelayed = gbLcdLYIncrementTicks+1; + + + gbTimerModeChange = false; + gbTimerOnChange = false; + gbTimerOn = false; + + if(gbCgbMode) { + for (i = 0; i<0x20; i++) + gbPalette[i] = 0x7fff; + + // This is just to show that the starting values of the OBJ palettes are different + // between the 3 consoles, and that they 'kinda' stay the same at each reset + // (they can slightly change, somehow (randomly?)). + // You can check the effects of gbGBCColorType on the "Vila Caldan Color" gbc demo. + // Note that you could also check the Div register to check on which system the game + // is running (GB,GBC and GBA(SP) have different startup values). + // Unfortunatly, I don't have any SGB system, so I can't get their starting values. + + if (gbGBCColorType == 0) // GBC Hardware + { + gbPalette[0x20] = 0x0600; + gbPalette[0x21] = 0xfdf3; + gbPalette[0x22] = 0x041c; + gbPalette[0x23] = 0xf5db; + gbPalette[0x24] = 0x4419; + gbPalette[0x25] = 0x57ea; + gbPalette[0x26] = 0x2808; + gbPalette[0x27] = 0x9b75; + gbPalette[0x28] = 0x129b; + gbPalette[0x29] = 0xfce0; + gbPalette[0x2a] = 0x22da; + gbPalette[0x2b] = 0x4ac5; + gbPalette[0x2c] = 0x2d71; + gbPalette[0x2d] = 0xf0c2; + gbPalette[0x2e] = 0x5137; + gbPalette[0x2f] = 0x2d41; + gbPalette[0x30] = 0x6b2d; + gbPalette[0x31] = 0x2215; + gbPalette[0x32] = 0xbe0a; + gbPalette[0x33] = 0xc053; + gbPalette[0x34] = 0xfe5f; + gbPalette[0x35] = 0xe000; + gbPalette[0x36] = 0xbe10; + gbPalette[0x37] = 0x914d; + gbPalette[0x38] = 0x7f91; + gbPalette[0x39] = 0x02b5; + gbPalette[0x3a] = 0x77ac; + gbPalette[0x3b] = 0x14e5; + gbPalette[0x3c] = 0xcf89; + gbPalette[0x3d] = 0xa03d; + gbPalette[0x3e] = 0xfd50; + gbPalette[0x3f] = 0x91ff; + } + else if (gbGBCColorType == 1) // GBA Hardware + { + gbPalette[0x20] = 0xbe00; + gbPalette[0x21] = 0xfdfd; + gbPalette[0x22] = 0xbd69; + gbPalette[0x23] = 0x7baf; + gbPalette[0x24] = 0xf5ff; + gbPalette[0x25] = 0x3f8f; + gbPalette[0x26] = 0xcee5; + gbPalette[0x27] = 0x5bf7; + gbPalette[0x28] = 0xb35b; + gbPalette[0x29] = 0xef97; + gbPalette[0x2a] = 0xef9f; + gbPalette[0x2b] = 0x97f7; + gbPalette[0x2c] = 0x82bf; + gbPalette[0x2d] = 0x9f3d; + gbPalette[0x2e] = 0xddde; + gbPalette[0x2f] = 0xbad5; + gbPalette[0x30] = 0x3cba; + gbPalette[0x31] = 0xdfd7; + gbPalette[0x32] = 0xedea; + gbPalette[0x33] = 0xfeda; + gbPalette[0x34] = 0xf7f9; + gbPalette[0x35] = 0xfdee; + gbPalette[0x36] = 0x6d2f; + gbPalette[0x37] = 0xf0e6; + gbPalette[0x38] = 0xf7f0; + gbPalette[0x39] = 0xf296; + gbPalette[0x3a] = 0x3bf1; + gbPalette[0x3b] = 0xe211; + gbPalette[0x3c] = 0x69ba; + gbPalette[0x3d] = 0x3d0d; + gbPalette[0x3e] = 0xdfd3; + gbPalette[0x3f] = 0xa6ba; + } + else if (gbGBCColorType == 2) // GBASP Hardware + { + gbPalette[0x20] = 0x9c00; + gbPalette[0x21] = 0x6340; + gbPalette[0x22] = 0x10c6; + gbPalette[0x23] = 0xdb97; + gbPalette[0x24] = 0x7622; + gbPalette[0x25] = 0x3e57; + gbPalette[0x26] = 0x2e12; + gbPalette[0x27] = 0x95c3; + gbPalette[0x28] = 0x1095; + gbPalette[0x29] = 0x488c; + gbPalette[0x2a] = 0x8241; + gbPalette[0x2b] = 0xde8c; + gbPalette[0x2c] = 0xfabc; + gbPalette[0x2d] = 0x0e81; + gbPalette[0x2e] = 0x7675; + gbPalette[0x2f] = 0xfdec; + gbPalette[0x30] = 0xddfd; + gbPalette[0x31] = 0x5995; + gbPalette[0x32] = 0x066a; + gbPalette[0x33] = 0xed1e; + gbPalette[0x34] = 0x1e84; + gbPalette[0x35] = 0x1d14; + gbPalette[0x36] = 0x11c3; + gbPalette[0x37] = 0x2749; + gbPalette[0x38] = 0xa727; + gbPalette[0x39] = 0x6266; + gbPalette[0x3a] = 0xe27b; + gbPalette[0x3b] = 0xe3fc; + gbPalette[0x3c] = 0x1f76; + gbPalette[0x3d] = 0xf158; + gbPalette[0x3e] = 0x468e; + gbPalette[0x3f] = 0xa540; + } + } else { + if(gbSgbMode) { + for(i = 0; i < 8; i++) + gbPalette[i] = systemGbPalette[gbPaletteOption*8+i]; + + } + for(i = 0; i < 8; i++) + gbPalette[i] = systemGbPalette[gbPaletteOption*8+i]; + } + + GBTIMER_MODE_0_CLOCK_TICKS = 256; + GBTIMER_MODE_1_CLOCK_TICKS = 4; + GBTIMER_MODE_2_CLOCK_TICKS = 16; + GBTIMER_MODE_3_CLOCK_TICKS = 64; + + GBLY_INCREMENT_CLOCK_TICKS = 114; + gbTimerTicks = GBTIMER_MODE_0_CLOCK_TICKS; + gbTimerClockTicks = GBTIMER_MODE_0_CLOCK_TICKS; + gbSerialTicks = 0; + gbSerialBits = 0; + gbSerialOn = 0; + gbWindowLine = -1; + gbTimerOn = false; + gbTimerMode = 0; + gbSpeed = 0; + gbJoymask[0] = gbJoymask[1] = gbJoymask[2] = gbJoymask[3] = 0; + + if(gbCgbMode) { + gbSpeed = 0; + gbHdmaOn = 0; + gbHdmaSource = 0x99d0; + gbHdmaDestination = 0x99d0; + gbVramBank = 0; + gbWramBank = 1; + + } + + // used to clean the borders + if (gbSgbMode) + { + gbSgbResetFlag = true; + gbSgbReset(); + if (gbBorderOn) + gbSgbRenderBorder(); + gbSgbResetFlag = false; + } + + for(i = 0; i < 4; i++) + gbBgp[i] = gbObp0[i] = gbObp1[i] = i; + + memset(&gbDataMBC1,0, sizeof(gbDataMBC1)); + gbDataMBC1.mapperROMBank = 1; + + gbDataMBC2.mapperRAMEnable = 0; + gbDataMBC2.mapperROMBank = 1; + + memset(&gbDataMBC3,0, 6 * sizeof(int)); + gbDataMBC3.mapperROMBank = 1; + + memset(&gbDataMBC5, 0, sizeof(gbDataMBC5)); + gbDataMBC5.mapperROMBank = 1; + + memset(&gbDataHuC1, 0, sizeof(gbDataHuC1)); + gbDataHuC1.mapperROMBank = 1; + + memset(&gbDataHuC3, 0, sizeof(gbDataHuC3)); + gbDataHuC3.mapperROMBank = 1; + + memset(&gbDataTAMA5,0, 26*sizeof(int)); + gbDataTAMA5.mapperROMBank = 1; + + memset(&gbDataMMM01,0, sizeof(gbDataMMM01)); + gbDataMMM01.mapperROMBank = 1; + + if (useBios && !skipBios && (gbHardware & 5)) + { + gbMemoryMap[0x00] = &gbMemory[0x0000]; + inBios = true; + } + else + { + gbMemoryMap[0x00] = &gbRom[0x0000]; + inBios = false; + } + + gbMemoryMap[0x01] = &gbRom[0x1000]; + gbMemoryMap[0x02] = &gbRom[0x2000]; + gbMemoryMap[0x03] = &gbRom[0x3000]; + gbMemoryMap[0x04] = &gbRom[0x4000]; + gbMemoryMap[0x05] = &gbRom[0x5000]; + gbMemoryMap[0x06] = &gbRom[0x6000]; + gbMemoryMap[0x07] = &gbRom[0x7000]; + if(gbCgbMode) { + gbMemoryMap[0x08] = &gbVram[0x0000]; + gbMemoryMap[0x09] = &gbVram[0x1000]; + gbMemoryMap[0x0a] = &gbMemory[0xa000]; + gbMemoryMap[0x0b] = &gbMemory[0xb000]; + gbMemoryMap[0x0c] = &gbMemory[0xc000]; + gbMemoryMap[0x0d] = &gbWram[0x1000]; + gbMemoryMap[0x0e] = &gbMemory[0xe000]; + gbMemoryMap[0x0f] = &gbMemory[0xf000]; + } else { + gbMemoryMap[0x08] = &gbMemory[0x8000]; + gbMemoryMap[0x09] = &gbMemory[0x9000]; + gbMemoryMap[0x0a] = &gbMemory[0xa000]; + gbMemoryMap[0x0b] = &gbMemory[0xb000]; + gbMemoryMap[0x0c] = &gbMemory[0xc000]; + gbMemoryMap[0x0d] = &gbMemory[0xd000]; + gbMemoryMap[0x0e] = &gbMemory[0xe000]; + gbMemoryMap[0x0f] = &gbMemory[0xf000]; + } + + if(gbRam) { + gbMemoryMap[0x0a] = &gbRam[0x0000]; + gbMemoryMap[0x0b] = &gbRam[0x1000]; + } + + gbSoundReset(); + + systemSaveUpdateCounter = SYSTEM_SAVE_NOT_UPDATED; + + gbLastTime = systemGetClock(); + gbFrameCount = 0; + + gbScreenOn = true; + gbSystemMessage = false; + + gbCheatWrite(true); // Emulates GS codes. + +} + +void gbWriteSaveMBC1(const char * name) +{ + if (gbRam) + { + FILE *gzFile = fopen(name,"wb"); + + if(gzFile == NULL) { + systemMessage(MSG_ERROR_CREATING_FILE, N_("Error creating file %s"), name); + return; + } + + fwrite(gbRam, + 1, + (gbRamSizeMask+1), + gzFile); + + fclose(gzFile); + } +} + +void gbWriteSaveMBC2(const char * name) +{ + if (gbRam) + { + FILE *file = fopen(name, "wb"); + + if(file == NULL) { + systemMessage(MSG_ERROR_CREATING_FILE, N_("Error creating file %s"), name); + return; + } + + fwrite(gbMemoryMap[0x0a], + 1, + 512, + file); + + fclose(file); + } +} + +void gbWriteSaveMBC3(const char * name, bool extendedSave) +{ + if (gbRam || extendedSave) + { + FILE *gzFile = fopen(name,"wb"); + if (gbRam) + { + + if(gzFile == NULL) { + systemMessage(MSG_ERROR_CREATING_FILE, N_("Error creating file %s"), name); + return; + } + + fwrite(gbRam, + 1, + (gbRamSizeMask+1), + gzFile); + } + + if(extendedSave) + fwrite(&gbDataMBC3.mapperSeconds, + 1, + 10*sizeof(int) + sizeof(time_t), + gzFile); + + fclose(gzFile); + } +} + +void gbWriteSaveMBC5(const char * name) +{ + if (gbRam) + { + FILE *gzFile = fopen(name,"wb"); + + if(gzFile == NULL) { + systemMessage(MSG_ERROR_CREATING_FILE, N_("Error creating file %s"), name); + return; + } + + fwrite(gbRam, + 1, + (gbRamSizeMask+1), + gzFile); + + fclose(gzFile); + } +} + +void gbWriteSaveMBC7(const char * name) +{ + if (gbRam) + { + FILE *file = fopen(name, "wb"); + + if(file == NULL) { + systemMessage(MSG_ERROR_CREATING_FILE, N_("Error creating file %s"), name); + return; + } + + fwrite(&gbMemory[0xa000], + 1, + 256, + file); + + fclose(file); + } +} + +void gbWriteSaveTAMA5(const char * name, bool extendedSave) +{ + FILE *gzFile = fopen(name,"wb"); + + if(gzFile == NULL) { + systemMessage(MSG_ERROR_CREATING_FILE, N_("Error creating file %s"), name); + return; + } + if (gbRam) + fwrite(gbRam, + 1, + (gbRamSizeMask+1), + gzFile); + + fwrite(gbTAMA5ram, + 1, + (gbTAMA5ramSize), + gzFile); + + if(extendedSave) + fwrite(&gbDataTAMA5.mapperSeconds, + 1, + 14*sizeof(int) + sizeof(time_t), + gzFile); + + fclose(gzFile); +} + +void gbWriteSaveMMM01(const char * name) +{ + if (gbRam) + { + FILE *gzFile = fopen(name,"wb"); + + if(gzFile == NULL) { + systemMessage(MSG_ERROR_CREATING_FILE, N_("Error creating file %s"), name); + return; + } + + fwrite(gbRam, + 1, + (gbRamSizeMask+1), + gzFile); + + fclose(gzFile); + } +} + + +bool gbReadSaveMBC1(const char * name) +{ + if (gbRam) + { + gzFile gzFile = gzopen(name, "rb"); + + if(gzFile == NULL) { + return false; + } + + int read = gzread(gzFile, + gbRam, + (gbRamSizeMask+1)); + + if(read != (gbRamSizeMask+1)) { + systemMessage(MSG_FAILED_TO_READ_SGM, + N_("Battery file's size incompatible with the rom settings %s (%d).\nWarning : save of the battery file is now disabled !"), name, read); + gzclose(gzFile); + gbBatteryError = true; + return false; + } + + // Also checks if the battery file it bigger than gbRamSizeMask+1 ! + u8 data[1]; + data[0] = 0; + + read = gzread(gzFile, + data, + 1); + if(read >0) { + systemMessage(MSG_FAILED_TO_READ_SGM, + N_("Battery file's size incompatible with the rom settings %s (%d).\nWarning : save of the battery file is now disabled !"), name, read); + gzclose(gzFile); + gbBatteryError = true; + return false; + } + + gzclose(gzFile); + return true; + } + else + return false; +} + + +bool gbReadSaveMBC2(const char * name) +{ + if (gbRam) + { + FILE *file = fopen(name, "rb"); + + if(file == NULL) { + return false; + } + + size_t read = fread(gbMemoryMap[0x0a], + 1, + 512, + file); + + if(read != 512) { + systemMessage(MSG_FAILED_TO_READ_SGM, + N_("Battery file's size incompatible with the rom settings %s (%d).\nWarning : save of the battery file is now disabled !"), name, read); + fclose(file); + gbBatteryError = true; + return false; + } + + // Also checks if the battery file it bigger than gbRamSizeMask+1 ! + u8 data[1]; + data[0] = 0; + + read = fread(&data[0], + 1, + 1, + file); + if(read > 0) { + systemMessage(MSG_FAILED_TO_READ_SGM, + N_("Battery file's size incompatible with the rom settings %s (%d).\nWarning : save of the battery file is now disabled !"), name, read); + fclose(file); + gbBatteryError = true; + return false; + } + + fclose(file); + return true; + } + else + return false; +} + +bool gbReadSaveMBC3(const char * name) +{ + gzFile gzFile = gzopen(name, "rb"); + + if(gzFile == NULL) { + return false; + } + + int read = 0; + + if (gbRam) + read = gzread(gzFile, + gbRam, + (gbRamSizeMask+1)); + else + read = (gbRamSizeMask+1); + + + bool res = true; + + if(read != (gbRamSizeMask+1)) { + systemMessage(MSG_FAILED_TO_READ_SGM, + N_("Battery file's size incompatible with the rom settings %s (%d).\nWarning : save of the battery file is now disabled !"), name, read); + gbBatteryError = true; + res = false; + } else if ((gbRomType == 0xf) || (gbRomType == 0x10)){ + read = gzread(gzFile, + &gbDataMBC3.mapperSeconds, + sizeof(int)*10 + sizeof(time_t)); + + if(read != (sizeof(int)*10 + sizeof(time_t)) && read != 0) { + systemMessage(MSG_FAILED_TO_READ_RTC,N_("Failed to read RTC from save game %s (continuing)"), + name); + res = false; + } + else if (read == 0) + { + systemMessage(MSG_FAILED_TO_READ_RTC,N_("Failed to read RTC from save game %s (continuing)"), + name); + res = false; + } + else + { + // Also checks if the battery file it bigger than gbRamSizeMask+1+RTC ! + u8 data[1]; + data[0] = 0; + + read = gzread(gzFile, + data, + 1); + if(read >0) { + systemMessage(MSG_FAILED_TO_READ_SGM, + N_("Battery file's size incompatible with the rom settings %s (%d).\nWarning : save of the battery file is now disabled !"), name, read); + gbBatteryError = true; + res = false; + } + } + } + + gzclose(gzFile); + return res; +} + +bool gbReadSaveMBC5(const char * name) +{ + if (gbRam) + { + gzFile gzFile = gzopen(name, "rb"); + + if(gzFile == NULL) { + return false; + } + + int read = gzread(gzFile, + gbRam, + (gbRamSizeMask+1)); + + if(read != (gbRamSizeMask+1)) { + systemMessage(MSG_FAILED_TO_READ_SGM, + N_("Battery file's size incompatible with the rom settings %s (%d).\nWarning : save of the battery file is now disabled !"), name, read); + gzclose(gzFile); + gbBatteryError = true; + return false; + } + + + // Also checks if the battery file it bigger than gbRamSizeMask+1 ! + u8 data[1]; + data[0] = 0; + + read = gzread(gzFile, + data, + 1); + if(read >0) { + systemMessage(MSG_FAILED_TO_READ_SGM, + N_("Battery file's size incompatible with the rom settings %s (%d).\nWarning : save of the battery file is now disabled !"), name, read); + gzclose(gzFile); + gbBatteryError = true; + return false; + } + + gzclose(gzFile); + return true; + } + else + return false; +} + +bool gbReadSaveMBC7(const char * name) +{ + if (gbRam) + { + FILE *file = fopen(name, "rb"); + + if(file == NULL) { + return false; + } + + size_t read = fread(&gbMemory[0xa000], + 1, + 256, + file); + + if(read != 256) { + systemMessage(MSG_FAILED_TO_READ_SGM, + N_("Battery file's size incompatible with the rom settings %s (%d).\nWarning : save of the battery file is now disabled !"), name, read); + fclose(file); + gbBatteryError = true; + return false; + } + + // Also checks if the battery file it bigger than gbRamSizeMask+1 ! + u8 data[1]; + data[0] = 0; + + read = fread(&data[0], + 1, + 1, + file); + if(read > 0) { + systemMessage(MSG_FAILED_TO_READ_SGM, + N_("Battery file's size incompatible with the rom settings %s (%d).\nWarning : save of the battery file is now disabled !"), name, read); + fclose(file); + gbBatteryError = true; + return false; + } + + fclose(file); + return true; + } + else + return false; +} + +bool gbReadSaveTAMA5(const char * name) +{ + gzFile gzFile = gzopen(name, "rb"); + + if(gzFile == NULL) { + return false; + } + + int read = 0; + + if (gbRam) + read = gzread(gzFile, + gbRam, + (gbRamSizeMask+1)); + else + read = gbRamSizeMask; + + read += gzread(gzFile, + gbTAMA5ram, + gbTAMA5ramSize); + + bool res = true; + + if(read != (gbRamSizeMask+gbTAMA5ramSize+1)) { + systemMessage(MSG_FAILED_TO_READ_SGM, + N_("Battery file's size incompatible with the rom settings %s (%d).\nWarning : save of the battery file is now disabled !"), name, read); + gbBatteryError = true; + res = false; + } else { + read = gzread(gzFile, + &gbDataTAMA5.mapperSeconds, + sizeof(int)*14 + sizeof(time_t)); + + if(read != (sizeof(int)*14 + sizeof(time_t)) && read != 0) { + systemMessage(MSG_FAILED_TO_READ_RTC,N_("Failed to read RTC from save game %s (continuing)"), + name); + res = false; + } + else if (read == 0) + { + systemMessage(MSG_FAILED_TO_READ_RTC,N_("Failed to read RTC from save game %s (continuing)"), + name); + res = false; + } + else + { + // Also checks if the battery file it bigger than gbRamSizeMask+1+RTC ! + u8 data[1]; + data[0] = 0; + + read = gzread(gzFile, + data, + 1); + if(read >0) { + systemMessage(MSG_FAILED_TO_READ_SGM, + N_("Battery file's size incompatible with the rom settings %s (%d).\nWarning : save of the battery file is now disabled !"), name, read); + gbBatteryError = true; + res = false; + } + } + } + + gzclose(gzFile); + return res; +} + + +bool gbReadSaveMMM01(const char * name) +{ + if (gbRam) + { + gzFile gzFile = gzopen(name, "rb"); + + if(gzFile == NULL) { + return false; + } + + int read = gzread(gzFile, + gbRam, + (gbRamSizeMask+1)); + + if(read != (gbRamSizeMask+1)) { + systemMessage(MSG_FAILED_TO_READ_SGM, + N_("Battery file's size incompatible with the rom settings %s (%d).\nWarning : save of the battery file is now disabled !"), name, read); + gzclose(gzFile); + gbBatteryError = true; + return false; + } + + // Also checks if the battery file it bigger than gbRamSizeMask+1 ! + u8 data[1]; + data[0] = 0; + + read = gzread(gzFile, + data, + 1); + if(read >0) { + systemMessage(MSG_FAILED_TO_READ_SGM, + N_("Battery file's size incompatible with the rom settings %s (%d).\nWarning : save of the battery file is now disabled !"), name, read); + gzclose(gzFile); + gbBatteryError = true; + return false; + } + + gzclose(gzFile); + return true; + } + else + return false; +} + +void gbInit() +{ + gbGenFilter(); + gbSgbInit(); + + gbMemory = (u8 *)malloc(65536); + + pix = (u8 *)calloc(1,4*257*226); + + gbLineBuffer = (u16 *)malloc(160 * sizeof(u16)); +} + +bool gbWriteBatteryFile(const char *file, bool extendedSave) +{ + if(gbBattery) { + switch(gbRomType) { + case 0x03: + gbWriteSaveMBC1(file); + break; + case 0x06: + gbWriteSaveMBC2(file); + break; + case 0x0d: + gbWriteSaveMMM01(file); + break; + case 0x0f: + case 0x10: + gbWriteSaveMBC3(file, extendedSave); + break; + case 0x13: + case 0xfc: + gbWriteSaveMBC3(file, false); + case 0x1b: + case 0x1e: + gbWriteSaveMBC5(file); + break; + case 0x22: + gbWriteSaveMBC7(file); + break; + case 0xfd: + gbWriteSaveTAMA5(file, extendedSave); + break; + case 0xff: + gbWriteSaveMBC1(file); + break; + } + } + return true; +} + +bool gbWriteBatteryFile(const char *file) +{ + if (!gbBatteryError) + { + gbWriteBatteryFile(file, true); + return true; + } + else return false; +} + +bool gbReadBatteryFile(const char *file) +{ + bool res = false; + if(gbBattery) { + switch(gbRomType) { + case 0x03: + res = gbReadSaveMBC1(file); + break; + case 0x06: + res = gbReadSaveMBC2(file); + break; + case 0x0d: + res = gbReadSaveMMM01(file); + break; + case 0x0f: + case 0x10: + if(!gbReadSaveMBC3(file)) { + time(&gbDataMBC3.mapperLastTime); + struct tm *lt; + lt = localtime(&gbDataMBC3.mapperLastTime); + gbDataMBC3.mapperSeconds = lt->tm_sec; + gbDataMBC3.mapperMinutes = lt->tm_min; + gbDataMBC3.mapperHours = lt->tm_hour; + gbDataMBC3.mapperDays = lt->tm_yday & 255; + gbDataMBC3.mapperControl = (gbDataMBC3.mapperControl & 0xfe) | + (lt->tm_yday > 255 ? 1: 0); + res = false; + break; + } + res = true; + break; + case 0x13: + case 0xfc: + res = gbReadSaveMBC3(file); + break; + case 0x1b: + case 0x1e: + res = gbReadSaveMBC5(file); + break; + case 0x22: + res = gbReadSaveMBC7(file); + break; + case 0xfd: + if(!gbReadSaveTAMA5(file)) { + u8 gbDaysinMonth [12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; + time(&gbDataTAMA5.mapperLastTime); + struct tm *lt; + lt = localtime(&gbDataTAMA5.mapperLastTime); + gbDataTAMA5.mapperSeconds = lt->tm_sec; + gbDataTAMA5.mapperMinutes = lt->tm_min; + gbDataTAMA5.mapperHours = lt->tm_hour; + gbDataTAMA5.mapperDays = 1; + gbDataTAMA5.mapperMonths = 1; + gbDataTAMA5.mapperYears = 1970; + int days = lt->tm_yday+365*3; + while (days) + { + gbDataTAMA5.mapperDays++; + days--; + if (gbDataTAMA5.mapperDays>gbDaysinMonth[gbDataTAMA5.mapperMonths-1]) + { + gbDataTAMA5.mapperDays = 1; + gbDataTAMA5.mapperMonths++; + if (gbDataTAMA5.mapperMonths>12) + { + gbDataTAMA5.mapperMonths = 1; + gbDataTAMA5.mapperYears++; + if ((gbDataTAMA5.mapperYears & 3) == 0) + gbDaysinMonth[1] = 29; + else + gbDaysinMonth[1] = 28; + } + } + } + gbDataTAMA5.mapperControl = (gbDataTAMA5.mapperControl & 0xfe) | + (lt->tm_yday > 255 ? 1: 0); + res = false; + break; + } + res = true; + break; + case 0xff: + res = gbReadSaveMBC1(file); + break; + } + } + systemSaveUpdateCounter = SYSTEM_SAVE_NOT_UPDATED; + return res; +} + +bool gbReadGSASnapshot(const char *fileName) +{ + FILE *file = fopen(fileName, "rb"); + + if(!file) { + systemMessage(MSG_CANNOT_OPEN_FILE, N_("Cannot open file %s"), fileName); + return false; + } + + fseek(file, 0x4, SEEK_SET); + char buffer[16]; + char buffer2[16]; + fread(buffer, 1, 15, file); + buffer[15] = 0; + memcpy(buffer2, &gbRom[0x134], 15); + buffer2[15] = 0; + if(memcmp(buffer, buffer2, 15)) { + systemMessage(MSG_CANNOT_IMPORT_SNAPSHOT_FOR, + N_("Cannot import snapshot for %s. Current game is %s"), + buffer, + buffer2); + fclose(file); + return false; + } + fseek(file, 0x13, SEEK_SET); + size_t read = 0; + int toRead = 0; + switch(gbRomType) { + case 0x03: + case 0x0f: + case 0x10: + case 0x13: + case 0x1b: + case 0x1e: + case 0xff: + read = fread(gbRam, 1, (gbRamSizeMask+1), file); + toRead = (gbRamSizeMask+1); + break; + case 0x06: + case 0x22: + read = fread(&gbMemory[0xa000],1,256,file); + toRead = 256; + break; + default: + systemMessage(MSG_UNSUPPORTED_SNAPSHOT_FILE, + N_("Unsupported snapshot file %s"), + fileName); + fclose(file); + return false; + } + fclose(file); + gbReset(); + return true; +} + +variable_desc gbSaveGameStruct[] = { + { &PC.W, sizeof(u16) }, + { &SP.W, sizeof(u16) }, + { &AF.W, sizeof(u16) }, + { &BC.W, sizeof(u16) }, + { &DE.W, sizeof(u16) }, + { &HL.W, sizeof(u16) }, + { &IFF, sizeof(u8) }, + { &GBLCD_MODE_0_CLOCK_TICKS, sizeof(int) }, + { &GBLCD_MODE_1_CLOCK_TICKS, sizeof(int) }, + { &GBLCD_MODE_2_CLOCK_TICKS, sizeof(int) }, + { &GBLCD_MODE_3_CLOCK_TICKS, sizeof(int) }, + { &GBDIV_CLOCK_TICKS, sizeof(int) }, + { &GBLY_INCREMENT_CLOCK_TICKS, sizeof(int) }, + { &GBTIMER_MODE_0_CLOCK_TICKS, sizeof(int) }, + { &GBTIMER_MODE_1_CLOCK_TICKS, sizeof(int) }, + { &GBTIMER_MODE_2_CLOCK_TICKS, sizeof(int) }, + { &GBTIMER_MODE_3_CLOCK_TICKS, sizeof(int) }, + { &GBSERIAL_CLOCK_TICKS, sizeof(int) }, + { &GBSYNCHRONIZE_CLOCK_TICKS, sizeof(int) }, + { &gbDivTicks, sizeof(int) }, + { &gbLcdMode, sizeof(int) }, + { &gbLcdTicks, sizeof(int) }, + { &gbLcdLYIncrementTicks, sizeof(int) }, + { &gbTimerTicks, sizeof(int) }, + { &gbTimerClockTicks, sizeof(int) }, + { &gbSerialTicks, sizeof(int) }, + { &gbSerialBits, sizeof(int) }, + { &gbInt48Signal, sizeof(int) }, + { &gbInterruptWait, sizeof(int) }, + { &gbSynchronizeTicks, sizeof(int) }, + { &gbTimerOn, sizeof(int) }, + { &gbTimerMode, sizeof(int) }, + { &gbSerialOn, sizeof(int) }, + { &gbWindowLine, sizeof(int) }, + { &gbCgbMode, sizeof(int) }, + { &gbVramBank, sizeof(int) }, + { &gbWramBank, sizeof(int) }, + { &gbHdmaSource, sizeof(int) }, + { &gbHdmaDestination, sizeof(int) }, + { &gbHdmaBytes, sizeof(int) }, + { &gbHdmaOn, sizeof(int) }, + { &gbSpeed, sizeof(int) }, + { &gbSgbMode, sizeof(int) }, + { ®ister_DIV, sizeof(u8) }, + { ®ister_TIMA, sizeof(u8) }, + { ®ister_TMA, sizeof(u8) }, + { ®ister_TAC, sizeof(u8) }, + { ®ister_IF, sizeof(u8) }, + { ®ister_LCDC, sizeof(u8) }, + { ®ister_STAT, sizeof(u8) }, + { ®ister_SCY, sizeof(u8) }, + { ®ister_SCX, sizeof(u8) }, + { ®ister_LY, sizeof(u8) }, + { ®ister_LYC, sizeof(u8) }, + { ®ister_DMA, sizeof(u8) }, + { ®ister_WY, sizeof(u8) }, + { ®ister_WX, sizeof(u8) }, + { ®ister_VBK, sizeof(u8) }, + { ®ister_HDMA1, sizeof(u8) }, + { ®ister_HDMA2, sizeof(u8) }, + { ®ister_HDMA3, sizeof(u8) }, + { ®ister_HDMA4, sizeof(u8) }, + { ®ister_HDMA5, sizeof(u8) }, + { ®ister_SVBK, sizeof(u8) }, + { ®ister_IE , sizeof(u8) }, + { &gbBgp[0], sizeof(u8) }, + { &gbBgp[1], sizeof(u8) }, + { &gbBgp[2], sizeof(u8) }, + { &gbBgp[3], sizeof(u8) }, + { &gbObp0[0], sizeof(u8) }, + { &gbObp0[1], sizeof(u8) }, + { &gbObp0[2], sizeof(u8) }, + { &gbObp0[3], sizeof(u8) }, + { &gbObp1[0], sizeof(u8) }, + { &gbObp1[1], sizeof(u8) }, + { &gbObp1[2], sizeof(u8) }, + { &gbObp1[3], sizeof(u8) }, + { NULL, 0 } +}; + + +static bool gbWriteSaveState(gzFile gzFile) +{ + + utilWriteInt(gzFile, GBSAVE_GAME_VERSION); + + utilGzWrite(gzFile, &gbRom[0x134], 15); + + utilWriteInt(gzFile, useBios); + utilWriteInt(gzFile, inBios); + + utilWriteData(gzFile, gbSaveGameStruct); + + utilGzWrite(gzFile, &IFF, 2); + + if(gbSgbMode) { + gbSgbSaveGame(gzFile); + } + + utilGzWrite(gzFile, &gbDataMBC1, sizeof(gbDataMBC1)); + utilGzWrite(gzFile, &gbDataMBC2, sizeof(gbDataMBC2)); + utilGzWrite(gzFile, &gbDataMBC3, sizeof(gbDataMBC3)); + utilGzWrite(gzFile, &gbDataMBC5, sizeof(gbDataMBC5)); + utilGzWrite(gzFile, &gbDataHuC1, sizeof(gbDataHuC1)); + utilGzWrite(gzFile, &gbDataHuC3, sizeof(gbDataHuC3)); + utilGzWrite(gzFile, &gbDataTAMA5, sizeof(gbDataTAMA5)); + if (gbTAMA5ram != NULL) + utilGzWrite(gzFile, gbTAMA5ram, gbTAMA5ramSize); + utilGzWrite(gzFile, &gbDataMMM01, sizeof(gbDataMMM01)); + + utilGzWrite(gzFile, gbPalette, 128 * sizeof(u16)); + + utilGzWrite(gzFile, &gbMemory[0x8000], 0x8000); + + if(gbRamSize && gbRam) { + utilWriteInt(gzFile, gbRamSize); + utilGzWrite(gzFile, gbRam, gbRamSize); + } + + if(gbCgbMode) { + utilGzWrite(gzFile, gbVram, 0x4000); + utilGzWrite(gzFile, gbWram, 0x8000); + } + + gbSoundSaveGame(gzFile); + + gbCheatsSaveGame(gzFile); + + utilWriteInt(gzFile, gbLcdModeDelayed); + utilWriteInt(gzFile, gbLcdTicksDelayed); + utilWriteInt(gzFile, gbLcdLYIncrementTicksDelayed); + utilWriteInt(gzFile, gbSpritesTicks[299]); + utilWriteInt(gzFile, gbTimerModeChange); + utilWriteInt(gzFile, gbTimerOnChange); + utilWriteInt(gzFile, gbHardware); + utilWriteInt(gzFile, gbBlackScreen); + utilWriteInt(gzFile, oldRegister_WY); + utilWriteInt(gzFile, gbWindowLine); + utilWriteInt(gzFile, inUseRegister_WY); + utilWriteInt(gzFile, gbScreenOn); + utilWriteInt(gzFile, 0x12345678); // end marker + return true; +} + +bool gbWriteMemSaveState(char *memory, int available) +{ + gzFile gzFile = utilMemGzOpen(memory, available, "w"); + + if(gzFile == NULL) { + return false; + } + + bool res = gbWriteSaveState(gzFile); + + long pos = utilGzMemTell(gzFile)+8; + + if(pos >= (available)) + res = false; + + utilGzClose(gzFile); + + return res; +} + +bool gbWriteSaveState(const char *name) +{ + gzFile gzFile = utilGzOpen(name,"wb"); + + if(gzFile == NULL) + return false; + + bool res = gbWriteSaveState(gzFile); + + utilGzClose(gzFile); + return res; +} + +static bool gbReadSaveState(gzFile gzFile) +{ + int version = utilReadInt(gzFile); + + if(version > GBSAVE_GAME_VERSION || version < 0) { + systemMessage(MSG_UNSUPPORTED_VB_SGM, + N_("Unsupported VisualBoy save game version %d"), version); + return false; + } + + u8 romname[20]; + + utilGzRead(gzFile, romname, 15); + + if(memcmp(&gbRom[0x134], romname, 15) != 0) { + systemMessage(MSG_CANNOT_LOAD_SGM_FOR, + N_("Cannot load save game for %s. Playing %s"), + romname, &gbRom[0x134]); + return false; + } + + + bool ub = false; + bool ib = false; + + if (version >= 11) + { + ub = utilReadInt(gzFile) ? true : false; + ib = utilReadInt(gzFile) ? true : false; + + if((ub != useBios) && (ib)) { + if(useBios) + systemMessage(MSG_SAVE_GAME_NOT_USING_BIOS, + N_("Save game is not using the BIOS files")); + else + systemMessage(MSG_SAVE_GAME_USING_BIOS, + N_("Save game is using the BIOS file")); + return false; + } + } + + gbReset(); + + inBios = ib; + + utilReadData(gzFile, gbSaveGameStruct); + + + // Correct crash when loading color gameboy save in regular gameboy type. + if (!gbCgbMode) + { + if(gbVram != NULL) { + free(gbVram); + gbVram = NULL; + } + if(gbWram != NULL) { + free(gbWram); + gbWram = NULL; + } + } + else + { + if(gbVram == NULL) + gbVram = (u8 *)malloc(0x4000); + if(gbWram == NULL) + gbWram = (u8 *)malloc(0x8000); + memset(gbVram,0,0x4000); + memset(gbPalette,0, 2*128); + } + + + + if(version >= GBSAVE_GAME_VERSION_7) { + utilGzRead(gzFile, &IFF, 2); + } + + if(gbSgbMode) { + gbSgbReadGame(gzFile, version); + } else { + gbSgbMask = 0; // loading a game at the wrong time causes no display + } + if (version<11) + utilGzRead(gzFile, &gbDataMBC1, sizeof(gbDataMBC1) - sizeof(int)); + else + utilGzRead(gzFile, &gbDataMBC1, sizeof(gbDataMBC1)); + utilGzRead(gzFile, &gbDataMBC2, sizeof(gbDataMBC2)); + if(version < GBSAVE_GAME_VERSION_4) + // prior to version 4, there was no adjustment for the time the game + // was last played, so we have less to read. This needs update if the + // structure changes again. + utilGzRead(gzFile, &gbDataMBC3, sizeof(gbDataMBC3)-sizeof(time_t)); + else + utilGzRead(gzFile, &gbDataMBC3, sizeof(gbDataMBC3)); + utilGzRead(gzFile, &gbDataMBC5, sizeof(gbDataMBC5)); + utilGzRead(gzFile, &gbDataHuC1, sizeof(gbDataHuC1)); + utilGzRead(gzFile, &gbDataHuC3, sizeof(gbDataHuC3)); + if(version>=11) + { + utilGzRead(gzFile, &gbDataTAMA5, sizeof(gbDataTAMA5)); + if(gbTAMA5ram != NULL) { + if(skipSaveGameBattery) { + utilGzSeek(gzFile, gbTAMA5ramSize, SEEK_CUR); + } else { + utilGzRead(gzFile, gbTAMA5ram, gbTAMA5ramSize); + } + } + utilGzRead(gzFile, &gbDataMMM01, sizeof(gbDataMMM01)); + } + + if(version < GBSAVE_GAME_VERSION_5) { + utilGzRead(gzFile, pix, 256*224*sizeof(u16)); + } + memset(pix, 0, 257*226*sizeof(u32)); + + if(version < GBSAVE_GAME_VERSION_6) { + utilGzRead(gzFile, gbPalette, 64 * sizeof(u16)); + } else + utilGzRead(gzFile, gbPalette, 128 * sizeof(u16)); + + if (version < 11) + utilGzRead(gzFile, gbPalette, 128 * sizeof(u16)); + + if(version < GBSAVE_GAME_VERSION_10) { + if(!gbCgbMode && !gbSgbMode) { + for(int i = 0; i < 8; i++) + gbPalette[i] = systemGbPalette[gbPaletteOption*8+i]; + } + } + + utilGzRead(gzFile, &gbMemory[0x8000], 0x8000); + + if(gbRamSize && gbRam) { + if(version < 11) + if(skipSaveGameBattery) { + utilGzSeek(gzFile, gbRamSize, SEEK_CUR); //skip + } else { + utilGzRead(gzFile, gbRam, gbRamSize); //read + } + else + { + int ramSize = utilReadInt(gzFile); + if(skipSaveGameBattery) { + utilGzSeek(gzFile, (gbRamSize>ramSize) ? ramSize : gbRamSize, SEEK_CUR); //skip + } else { + utilGzRead(gzFile, gbRam, (gbRamSize>ramSize) ? ramSize : gbRamSize); //read + } + if(ramSize>gbRamSize) + utilGzSeek(gzFile,ramSize-gbRamSize,SEEK_CUR); + } + } + + memset(gbSCYLine, register_SCY, sizeof(gbSCYLine)); + memset(gbSCXLine, register_SCX, sizeof(gbSCXLine)); + memset(gbBgpLine, (gbBgp[0] | (gbBgp[1]<<2) | (gbBgp[2]<<4) | + (gbBgp[3]<<6)), sizeof(gbBgpLine)); + memset(gbObp0Line, (gbObp0[0] | (gbObp0[1]<<2) | (gbObp0[2]<<4) | + (gbObp0[3]<<6)), sizeof(gbObp0Line)); + memset(gbObp1Line, (gbObp1[0] | (gbObp1[1]<<2) | (gbObp1[2]<<4) | + (gbObp1[3]<<6)), sizeof(gbObp1Line)); + memset(gbSpritesTicks, 0x0, sizeof(gbSpritesTicks)); + + if (inBios) + { + gbMemoryMap[0x00] = &gbMemory[0x0000]; + memcpy ((u8 *)(gbMemory), (u8 *)(gbRom), 0x1000); + memcpy ((u8 *)(gbMemory), (u8 *)(bios), 0x100); + } + else + gbMemoryMap[0x00] = &gbRom[0x0000]; + gbMemoryMap[0x01] = &gbRom[0x1000]; + gbMemoryMap[0x02] = &gbRom[0x2000]; + gbMemoryMap[0x03] = &gbRom[0x3000]; + gbMemoryMap[0x04] = &gbRom[0x4000]; + gbMemoryMap[0x05] = &gbRom[0x5000]; + gbMemoryMap[0x06] = &gbRom[0x6000]; + gbMemoryMap[0x07] = &gbRom[0x7000]; + gbMemoryMap[0x08] = &gbMemory[0x8000]; + gbMemoryMap[0x09] = &gbMemory[0x9000]; + gbMemoryMap[0x0a] = &gbMemory[0xa000]; + gbMemoryMap[0x0b] = &gbMemory[0xb000]; + gbMemoryMap[0x0c] = &gbMemory[0xc000]; + gbMemoryMap[0x0d] = &gbMemory[0xd000]; + gbMemoryMap[0x0e] = &gbMemory[0xe000]; + gbMemoryMap[0x0f] = &gbMemory[0xf000]; + + switch(gbRomType) { + case 0x00: + case 0x01: + case 0x02: + case 0x03: + // MBC 1 + memoryUpdateMapMBC1(); + break; + case 0x05: + case 0x06: + // MBC2 + memoryUpdateMapMBC2(); + break; + case 0x0b: + case 0x0c: + case 0x0d: + // MMM01 + memoryUpdateMapMMM01(); + break; + case 0x0f: + case 0x10: + case 0x11: + case 0x12: + case 0x13: + // MBC 3 + memoryUpdateMapMBC3(); + break; + case 0x19: + case 0x1a: + case 0x1b: + // MBC5 + memoryUpdateMapMBC5(); + break; + case 0x1c: + case 0x1d: + case 0x1e: + // MBC 5 Rumble + memoryUpdateMapMBC5(); + break; + case 0x22: + // MBC 7 + memoryUpdateMapMBC7(); + break; + case 0x56: + // GS3 + memoryUpdateMapGS3(); + break; + case 0xfd: + // TAMA5 + memoryUpdateMapTAMA5(); + break; + case 0xfe: + // HuC3 + memoryUpdateMapHuC3(); + break; + case 0xff: + // HuC1 + memoryUpdateMapHuC1(); + break; + } + + if(gbCgbMode) { + utilGzRead(gzFile, gbVram, 0x4000); + utilGzRead(gzFile, gbWram, 0x8000); + + int value = register_SVBK; + if(value == 0) + value = 1; + + gbMemoryMap[0x08] = &gbVram[register_VBK * 0x2000]; + gbMemoryMap[0x09] = &gbVram[register_VBK * 0x2000 + 0x1000]; + gbMemoryMap[0x0d] = &gbWram[value * 0x1000]; + } + + gbSoundReadGame(version, gzFile); + + if (gbCgbMode && gbSgbMode) { + gbSgbMode = 0; + } + + if(gbBorderOn && !gbSgbMask) { + gbSgbRenderBorder(); + } + + systemDrawScreen(); + + if(version > GBSAVE_GAME_VERSION_1) + { + if( skipSaveGameCheats ) { + gbCheatsReadGameSkip(gzFile, version); + } else { + gbCheatsReadGame(gzFile, version); + } + } + + if (version<11) + { + gbWriteMemory(0xff00, 0); + gbMemory[0xff04] = register_DIV; + gbMemory[0xff05] = register_TIMA; + gbMemory[0xff06] = register_TMA; + gbMemory[0xff07] = register_TAC; + gbMemory[0xff40] = register_LCDC; + gbMemory[0xff42] = register_SCY; + gbMemory[0xff43] = register_SCX; + gbMemory[0xff44] = register_LY; + gbMemory[0xff45] = register_LYC; + gbMemory[0xff46] = register_DMA; + gbMemory[0xff4a] = register_WY; + gbMemory[0xff4b] = register_WX; + gbMemory[0xff4f] = register_VBK; + gbMemory[0xff51] = register_HDMA1; + gbMemory[0xff52] = register_HDMA2; + gbMemory[0xff53] = register_HDMA3; + gbMemory[0xff54] = register_HDMA4; + gbMemory[0xff55] = register_HDMA5; + gbMemory[0xff70] = register_SVBK; + gbMemory[0xffff] = register_IE; + GBDIV_CLOCK_TICKS = 64; + + if (gbSpeed) + gbDivTicks /=2; + + if ((gbLcdMode == 0) && (register_STAT & 8)) + gbInt48Signal |= 1; + if ((gbLcdMode == 1) && (register_STAT & 0x10)) + gbInt48Signal |= 2; + if ((gbLcdMode == 2) && (register_STAT & 0x20)) + gbInt48Signal |= 4; + if ((register_LY==register_LYC) && (register_STAT & 0x40)) + gbInt48Signal |= 8; + + gbLcdLYIncrementTicks = GBLY_INCREMENT_CLOCK_TICKS; + + if (gbLcdMode == 2) + gbLcdLYIncrementTicks-=GBLCD_MODE_2_CLOCK_TICKS-gbLcdTicks; + else if (gbLcdMode == 3) + gbLcdLYIncrementTicks -=GBLCD_MODE_2_CLOCK_TICKS+GBLCD_MODE_3_CLOCK_TICKS-gbLcdTicks; + else if (gbLcdMode == 0) + gbLcdLYIncrementTicks =gbLcdTicks; + else if (gbLcdMode == 1) + { + gbLcdLYIncrementTicks = gbLcdTicks % GBLY_INCREMENT_CLOCK_TICKS; + if (register_LY == 0x99) + gbLcdLYIncrementTicks =gbLine99Ticks; + else if (register_LY == 0) + gbLcdLYIncrementTicks += GBLY_INCREMENT_CLOCK_TICKS; + } + + gbLcdModeDelayed = gbLcdMode; + gbLcdTicksDelayed = gbLcdTicks--; + gbLcdLYIncrementTicksDelayed = gbLcdLYIncrementTicks--; + gbInterruptWait = 0; + memset(gbSpritesTicks,0,sizeof(gbSpritesTicks)); + } + else + { + gbLcdModeDelayed = utilReadInt(gzFile); + gbLcdTicksDelayed = utilReadInt(gzFile); + gbLcdLYIncrementTicksDelayed = utilReadInt(gzFile); + gbSpritesTicks[299] = utilReadInt(gzFile) & 0xff; + gbTimerModeChange = (utilReadInt(gzFile) ? true : false); + gbTimerOnChange = (utilReadInt(gzFile) ? true : false); + gbHardware = utilReadInt(gzFile); + gbBlackScreen = (utilReadInt(gzFile) ? true : false); + oldRegister_WY = utilReadInt(gzFile); + gbWindowLine = utilReadInt(gzFile); + inUseRegister_WY = utilReadInt(gzFile); + gbScreenOn = (utilReadInt(gzFile) ? true : false); + } + + if (gbSpeed) + gbLine99Ticks *= 2; + + systemSaveUpdateCounter = SYSTEM_SAVE_NOT_UPDATED; + + if ( version >= 12 && utilReadInt( gzFile ) != 0x12345678 ) + assert( false ); // fails if something read too much/little from file + + return true; +} + +bool gbReadMemSaveState(char *memory, int available) +{ + gzFile gzFile = utilMemGzOpen(memory, available, "r"); + + bool res = gbReadSaveState(gzFile); + + utilGzClose(gzFile); + + return res; +} + +bool gbReadSaveState(const char *name) +{ + gzFile gzFile = utilGzOpen(name,"rb"); + + if(gzFile == NULL) { + return false; + } + + bool res = gbReadSaveState(gzFile); + + utilGzClose(gzFile); + + return res; +} + +bool gbWritePNGFile(const char *fileName) +{ + if(gbBorderOn) + return utilWritePNGFile(fileName, 256, 224, pix); + return utilWritePNGFile(fileName, 160, 144, pix); +} + +bool gbWriteBMPFile(const char *fileName) +{ + if(gbBorderOn) + return utilWriteBMPFile(fileName, 256, 224, pix); + return utilWriteBMPFile(fileName, 160, 144, pix); +} + +void gbCleanUp() +{ + if(gbRam != NULL) { + free(gbRam); + gbRam = NULL; + } + + if(gbRom != NULL) { + free(gbRom); + gbRom = NULL; + } + + if(bios != NULL) { + free(bios); + bios = NULL; + } + + if(gbMemory != NULL) { + free(gbMemory); + gbMemory = NULL; + } + + if(gbLineBuffer != NULL) { + free(gbLineBuffer); + gbLineBuffer = NULL; + } + + if(pix != NULL) { + free(pix); + pix = NULL; + } + + gbSgbShutdown(); + + if(gbVram != NULL) { + free(gbVram); + gbVram = NULL; + } + + if(gbWram != NULL) { + free(gbWram); + gbWram = NULL; + } + + if(gbTAMA5ram != NULL) { + free(gbTAMA5ram); + gbTAMA5ram = NULL; + } + + systemSaveUpdateCounter = SYSTEM_SAVE_NOT_UPDATED; +} + +bool gbLoadRom(const char *szFile) +{ + int size = 0; + + if(gbRom != NULL) { + gbCleanUp(); + } + + systemSaveUpdateCounter = SYSTEM_SAVE_NOT_UPDATED; + + gbRom = utilLoad(szFile, + utilIsGBImage, + NULL, + size); + if(!gbRom) + return false; + + gbRomSize = size; + + gbBatteryError = false; + + if(bios != NULL) { + free(bios); + bios = NULL; + } + bios = (u8 *)calloc(1,0x100); + + return gbUpdateSizes(); +} + +bool gbUpdateSizes() +{ + if(gbRom[0x148] > 8) { + systemMessage(MSG_UNSUPPORTED_ROM_SIZE, + N_("Unsupported rom size %02x"), gbRom[0x148]); + return false; + } + + if(gbRomSize < gbRomSizes[gbRom[0x148]]) { + u8 *gbRomNew = (u8 *)realloc(gbRom, gbRomSizes[gbRom[0x148]]); + if( !gbRomNew ) { assert( false ); return false; }; + gbRom = gbRomNew; + for (int i = gbRomSize; igbRomSizes[gbRom[0x148]]) && (genericflashcardEnable)) + { + gbRomSize = gbRomSize>>16; + gbRom[0x148] = 0; + if (gbRomSize) + { + while (!((gbRomSize & 1) || (gbRom[0x148] == 7))) + { + gbRom[0x148]++; + gbRomSize>>=1; + } + gbRom[0x148]++; + } + u8 *gbRomNew = (u8 *)realloc(gbRom, gbRomSizes[gbRom[0x148]]); + if( !gbRomNew ) { assert( false ); return false; }; + gbRom = gbRomNew; + } + gbRomSize = gbRomSizes[gbRom[0x148]]; + gbRomSizeMask = gbRomSizesMasks[gbRom[0x148]]; + + + // The 'genericflashcard' option allows some PD to work. + // However, the setting is dangerous (if you let in enabled + // and play a normal game, it might just break everything). + // That's why it is not saved in the emulator options. + // Also I added some checks in VBA to make sure your saves will not be + // overwritten if you wrongly enable this option for a game + // you already played (and vice-versa, ie. if you forgot to + // enable the option for a game you played with it enabled, like Shawu Story). + u8 ramsize = genericflashcardEnable ? 5 : gbRom[0x149]; + gbRom[0x149] = ramsize; + + if ((gbRom[2] == 0x6D) && (gbRom[5] == 0x47) && (gbRom[6] == 0x65) && (gbRom[7] == 0x6E) && + (gbRom[8] == 0x69) && (gbRom[9] == 0x65) && (gbRom[0xA] == 0x28) && (gbRom[0xB] == 0x54)) + { + gbCheatingDevice = 1; // GameGenie + for (int i = 0; i < 0x20; i++) // Cleans GG hardware registers + gbRom[0x4000+i] = 0; + } + else if (((gbRom[0x104] == 0x44) && (gbRom[0x156] == 0xEA) && (gbRom[0x158] == 0x7F) && + (gbRom[0x159] == 0xEA) && (gbRom[0x15B] == 0x7F)) || ((gbRom[0x165] == 0x3E) && + (gbRom[0x166] == 0xD9) && (gbRom[0x16D] == 0xE1) && (gbRom[0x16E] == 0x7F))) + gbCheatingDevice = 2; // GameShark + else gbCheatingDevice = 0; + + if(ramsize > 5) { + systemMessage(MSG_UNSUPPORTED_RAM_SIZE, + N_("Unsupported ram size %02x"), gbRom[0x149]); + return false; + } + + gbRamSize = gbRamSizes[ramsize]; + gbRamSizeMask = gbRamSizesMasks[ramsize]; + + gbRomType = gbRom[0x147]; + if (genericflashcardEnable) + { + /*if (gbRomType<2) + gbRomType =3; + else if ((gbRomType == 0xc) || (gbRomType == 0xf) || (gbRomType == 0x12) || + (gbRomType == 0x16) || (gbRomType == 0x1a) || (gbRomType == 0x1d)) + gbRomType++; + else if ((gbRomType == 0xb) || (gbRomType == 0x11) || (gbRomType == 0x15) || + (gbRomType == 0x19) || (gbRomType == 0x1c)) + gbRomType+=2; + else if ((gbRomType == 0x5) || (gbRomType == 0x6)) + gbRomType = 0x1a;*/ + gbRomType = 0x1b; + } + else if (gbCheatingDevice == 1) + gbRomType = 0x55; + else if (gbCheatingDevice == 2) + gbRomType = 0x56; + + gbRom[0x147] = gbRomType; + + mapperReadRAM = NULL; + + switch(gbRomType) { + case 0x00: + case 0x01: + case 0x02: + case 0x03: + case 0x08: + case 0x09: + // MBC 1 + mapper = mapperMBC1ROM; + mapperRAM = mapperMBC1RAM; + mapperReadRAM = mapperMBC1ReadRAM; + break; + case 0x05: + case 0x06: + // MBC2 + mapper = mapperMBC2ROM; + mapperRAM = mapperMBC2RAM; + gbRamSize = 0x200; + gbRamSizeMask = 0x1ff; + break; + case 0x0b: + case 0x0c: + case 0x0d: + // MMM01 + mapper = mapperMMM01ROM; + mapperRAM = mapperMMM01RAM; + break; + case 0x0f: + case 0x10: + case 0x11: + case 0x12: + case 0x13: + case 0xfc: + // MBC 3 + mapper = mapperMBC3ROM; + mapperRAM = mapperMBC3RAM; + mapperReadRAM = mapperMBC3ReadRAM; + break; + case 0x19: + case 0x1a: + case 0x1b: + // MBC5 + mapper = mapperMBC5ROM; + mapperRAM = mapperMBC5RAM; + mapperReadRAM = mapperMBC5ReadRAM; + break; + case 0x1c: + case 0x1d: + case 0x1e: + // MBC 5 Rumble + mapper = mapperMBC5ROM; + mapperRAM = mapperMBC5RAM; + mapperReadRAM = mapperMBC5ReadRAM; + break; + case 0x22: + // MBC 7 + mapper = mapperMBC7ROM; + mapperRAM = mapperMBC7RAM; + mapperReadRAM = mapperMBC7ReadRAM; + gbRamSize = 0x200; + gbRamSizeMask = 0x1ff; + break; + // GG (GameGenie) + case 0x55: + mapper = mapperGGROM; + break; + case 0x56: + // GS (GameShark) + mapper = mapperGS3ROM; + break; + case 0xfd: + // TAMA5 + if (gbRam!= NULL) + { + free(gbRam); + gbRam = NULL; + } + + ramsize = 3; + gbRamSize = gbRamSizes[3]; + gbRamSizeMask = gbRamSizesMasks[3]; + gbRamFill = 0x0; + + gbTAMA5ramSize = 0x100; + + if (gbTAMA5ram == NULL) + gbTAMA5ram = (u8 *)malloc(gbTAMA5ramSize); + memset(gbTAMA5ram, 0x0, gbTAMA5ramSize); + + mapperRAM = mapperTAMA5RAM; + mapperReadRAM = mapperTAMA5ReadRAM; + mapperUpdateClock = memoryUpdateTAMA5Clock; + break; + case 0xfe: + // HuC3 + mapper = mapperHuC3ROM; + mapperRAM = mapperHuC3RAM; + mapperReadRAM = mapperHuC3ReadRAM; + break; + case 0xff: + // HuC1 + mapper = mapperHuC1ROM; + mapperRAM = mapperHuC1RAM; + break; + default: + systemMessage(MSG_UNKNOWN_CARTRIDGE_TYPE, + N_("Unknown cartridge type %02x"), gbRomType); + return false; + } + + if(gbRamSize) { + gbRam = (u8 *)malloc(gbRamSize); + memset(gbRam, gbRamFill, gbRamSize); + } + + switch(gbRomType) { + case 0x03: + case 0x06: + case 0x0f: + case 0x10: + case 0x13: + case 0x1b: + case 0x1d: + case 0x1e: + case 0x22: + case 0xfd: + case 0xff: + gbBattery = 1; + break; + } + + gbInit(); + + //gbReset(); + + switch(gbRomType) { + case 0x1c: + case 0x1d: + case 0x1e: + gbDataMBC5.isRumbleCartridge = 1; + } + + return true; +} + +int gbGetNextEvent (int _clockTicks) +{ + if (register_LCDC & 0x80) + { + if(gbLcdTicks < _clockTicks) + _clockTicks = gbLcdTicks; + + if(gbLcdTicksDelayed < _clockTicks) + _clockTicks = gbLcdTicksDelayed; + + if(gbLcdLYIncrementTicksDelayed < _clockTicks) + _clockTicks = gbLcdLYIncrementTicksDelayed; + } + + if(gbLcdLYIncrementTicks < _clockTicks) + _clockTicks = gbLcdLYIncrementTicks; + + if(gbSerialOn && (gbSerialTicks < _clockTicks)) + _clockTicks = gbSerialTicks; + + if(gbTimerOn && (((gbInternalTimer) & gbTimerMask[gbTimerMode])+1 < _clockTicks)) + _clockTicks = ((gbInternalTimer) & gbTimerMask[gbTimerMode])+1; + + //if(soundTicks && (soundTicks < _clockTicks)) + // _clockTicks = soundTicks; + + if ((_clockTicks<=0) || (gbInterruptWait)) + _clockTicks = 1; + + return _clockTicks; +} + +void gbDrawLine() +{ + switch(systemColorDepth) { + case 16: + { + u16 * dest = (u16 *)pix + + (gbBorderLineSkip+2) * (register_LY + gbBorderRowSkip+1) + + gbBorderColumnSkip; + for(int x = 0; x < 160; ) { + *dest++ = systemColorMap16[gbLineMix[x++]]; + *dest++ = systemColorMap16[gbLineMix[x++]]; + *dest++ = systemColorMap16[gbLineMix[x++]]; + *dest++ = systemColorMap16[gbLineMix[x++]]; + + *dest++ = systemColorMap16[gbLineMix[x++]]; + *dest++ = systemColorMap16[gbLineMix[x++]]; + *dest++ = systemColorMap16[gbLineMix[x++]]; + *dest++ = systemColorMap16[gbLineMix[x++]]; + + *dest++ = systemColorMap16[gbLineMix[x++]]; + *dest++ = systemColorMap16[gbLineMix[x++]]; + *dest++ = systemColorMap16[gbLineMix[x++]]; + *dest++ = systemColorMap16[gbLineMix[x++]]; + + *dest++ = systemColorMap16[gbLineMix[x++]]; + *dest++ = systemColorMap16[gbLineMix[x++]]; + *dest++ = systemColorMap16[gbLineMix[x++]]; + *dest++ = systemColorMap16[gbLineMix[x++]]; + } + if(gbBorderOn) + dest += gbBorderColumnSkip; + *dest++ = 0; // for filters that read one pixel more + } + break; + + case 24: + { + u8 *dest = (u8 *)pix + + 3*(gbBorderLineSkip * (register_LY + gbBorderRowSkip) + + gbBorderColumnSkip); + for(int x = 0; x < 160;) { + *((u32 *)dest) = systemColorMap32[gbLineMix[x++]]; + dest+= 3; + *((u32 *)dest) = systemColorMap32[gbLineMix[x++]]; + dest+= 3; + *((u32 *)dest) = systemColorMap32[gbLineMix[x++]]; + dest+= 3; + *((u32 *)dest) = systemColorMap32[gbLineMix[x++]]; + dest+= 3; + + *((u32 *)dest) = systemColorMap32[gbLineMix[x++]]; + dest+= 3; + *((u32 *)dest) = systemColorMap32[gbLineMix[x++]]; + dest+= 3; + *((u32 *)dest) = systemColorMap32[gbLineMix[x++]]; + dest+= 3; + *((u32 *)dest) = systemColorMap32[gbLineMix[x++]]; + dest+= 3; + + *((u32 *)dest) = systemColorMap32[gbLineMix[x++]]; + dest+= 3; + *((u32 *)dest) = systemColorMap32[gbLineMix[x++]]; + dest+= 3; + *((u32 *)dest) = systemColorMap32[gbLineMix[x++]]; + dest+= 3; + *((u32 *)dest) = systemColorMap32[gbLineMix[x++]]; + dest+= 3; + + *((u32 *)dest) = systemColorMap32[gbLineMix[x++]]; + dest+= 3; + *((u32 *)dest) = systemColorMap32[gbLineMix[x++]]; + dest+= 3; + *((u32 *)dest) = systemColorMap32[gbLineMix[x++]]; + dest+= 3; + *((u32 *)dest) = systemColorMap32[gbLineMix[x++]]; + dest+= 3; + } + } + break; + + case 32: + { + u32 * dest = (u32 *)pix + + (gbBorderLineSkip+1) * (register_LY + gbBorderRowSkip+1) + + gbBorderColumnSkip; + for(int x = 0; x < 160;) { + *dest++ = systemColorMap32[gbLineMix[x++]]; + *dest++ = systemColorMap32[gbLineMix[x++]]; + *dest++ = systemColorMap32[gbLineMix[x++]]; + *dest++ = systemColorMap32[gbLineMix[x++]]; + + *dest++ = systemColorMap32[gbLineMix[x++]]; + *dest++ = systemColorMap32[gbLineMix[x++]]; + *dest++ = systemColorMap32[gbLineMix[x++]]; + *dest++ = systemColorMap32[gbLineMix[x++]]; + + *dest++ = systemColorMap32[gbLineMix[x++]]; + *dest++ = systemColorMap32[gbLineMix[x++]]; + *dest++ = systemColorMap32[gbLineMix[x++]]; + *dest++ = systemColorMap32[gbLineMix[x++]]; + + *dest++ = systemColorMap32[gbLineMix[x++]]; + *dest++ = systemColorMap32[gbLineMix[x++]]; + *dest++ = systemColorMap32[gbLineMix[x++]]; + *dest++ = systemColorMap32[gbLineMix[x++]]; + } + } + break; + } +} + +void gbEmulate(int ticksToStop) +{ + gbRegister tempRegister; + u8 tempValue; + s8 offset; + + clockTicks = 0; + gbDmaTicks = 0; + + register int opcode = 0; + + int opcode1 = 0; + int opcode2 = 0; + bool execute = false; + + while(1) { +#ifndef FINAL_VERSION + if(systemDebug) { + if(!(IFF & 0x80)) { + if(systemDebug > 1) { + sprintf(gbBuffer,"PC=%04x AF=%04x BC=%04x DE=%04x HL=%04x SP=%04x I=%04x\n", + PC.W, AF.W, BC.W, DE.W,HL.W,SP.W,IFF); + } else { + sprintf(gbBuffer,"PC=%04x I=%02x\n", PC.W, IFF); + } + log(gbBuffer); + } + } +#endif + + u16 oldPCW = PC.W; + + if(IFF & 0x80) { + if(register_LCDC & 0x80) { + clockTicks = gbLcdTicks; + } else + clockTicks = 1000; + + clockTicks = gbGetNextEvent(clockTicks); + + /*if(gbLcdTicksDelayed < clockTicks) + clockTicks = gbLcdTicksDelayed; + + if(gbLcdLYIncrementTicksDelayed < clockTicks) + clockTicks = gbLcdLYIncrementTicksDelayed; + + if(gbLcdLYIncrementTicks < clockTicks) + clockTicks = gbLcdLYIncrementTicks; + + if(gbSerialOn && (gbSerialTicks < clockTicks)) + clockTicks = gbSerialTicks; + + if(gbTimerOn && (((gbInternalTimer) & gbTimerMask[gbTimerMode])+1 < clockTicks)) + clockTicks = ((gbInternalTimer) & gbTimerMask[gbTimerMode])+1; + + if(soundTicks && (soundTicks < clockTicks)) + clockTicks = soundTicks; + + if ((clockTicks<=0) || (gbInterruptWait)) + clockTicks = 1;*/ + + } else { + + // First we apply the clockTicks, then we execute the opcodes. + opcode1 = 0; + opcode2 = 0; + execute = true; + + opcode2 = opcode1 = opcode = gbReadOpcode(PC.W++); + + // If HALT state was launched while IME = 0 and (register_IF & register_IE & 0x1F), + // PC.W is not incremented for the first byte of the next instruction. + if (IFF & 2) + { + PC.W--; + IFF &= ~2; + } + + clockTicks = gbCycles[opcode]; + + switch(opcode) { + case 0xCB: + // extended opcode + opcode2 = opcode = gbReadOpcode(PC.W++); + clockTicks = gbCyclesCB[opcode]; + break; + } + gbOldClockTicks = clockTicks-1; + gbIntBreak = 1; + } + + + if(!emulating) + return; + + // For 'breakpoint' support (opcode 0xFC is considered as a breakpoint) + if ((clockTicks==0) && execute) + { + PC.W = oldPCW; + return; + } + + + if (!(IFF & 0x80)) + clockTicks = 1; + + gbRedoLoop: + + + + if (gbInterruptWait) + gbInterruptWait = 0; + else + gbInterruptLaunched = 0; + + + // Used for the EI/DI instruction's delay. + if (IFF & 0x38) + { + int tempIFF = (IFF >> 4) & 3; + + if (tempIFF <=clockTicks) + { + tempIFF = 0; + IFF |=1; + } + else + tempIFF -= clockTicks; + IFF = (IFF & 0xCF) | (tempIFF <<4); + + if (IFF & 0x08) + IFF &= 0x82; + } + + + if (register_LCDCBusy) + { + register_LCDCBusy-=clockTicks; + if (register_LCDCBusy<0) + register_LCDCBusy = 0; + } + + + if(gbSgbMode) { + if(gbSgbPacketTimeout) { + gbSgbPacketTimeout -= clockTicks; + + if(gbSgbPacketTimeout <= 0) + gbSgbResetPacketState(); + } + } + + ticksToStop -= clockTicks; + + // DIV register emulation + gbDivTicks -= clockTicks; + while(gbDivTicks <= 0) { + gbMemory[0xff04] = ++register_DIV; + gbDivTicks += GBDIV_CLOCK_TICKS; + } + + if(register_LCDC & 0x80) { + // LCD stuff + + gbLcdTicks -= clockTicks; + gbLcdTicksDelayed -= clockTicks; + gbLcdLYIncrementTicks -= clockTicks; + gbLcdLYIncrementTicksDelayed -= clockTicks; + + + // our counters are off, see what we need to do + + // This looks (and kinda is) complicated, however this + // is the only way I found to emulate properly the way + // the real hardware operates... + while(((gbLcdTicks <= 0) && (gbLCDChangeHappened == false)) || + ((gbLcdTicksDelayed <= 0) && (gbLCDChangeHappened == true)) || + ((gbLcdLYIncrementTicks <= 0) && (gbLYChangeHappened == false)) || + ((gbLcdLYIncrementTicksDelayed<=0) && (gbLYChangeHappened == true))) + { + + if ((gbLcdLYIncrementTicks <= 0) && (!gbLYChangeHappened)) + { + gbLYChangeHappened = true; + gbMemory[0xff44] = register_LY = (register_LY + 1) % 154; + + if (register_LY == 0x91) + { + /* if (IFF & 0x80) + gbScreenOn = !gbScreenOn; + else*/ if (register_LCDC & 0x80) + gbScreenOn = true; + } + + gbLcdLYIncrementTicks += GBLY_INCREMENT_CLOCK_TICKS; + + if (gbLcdMode == 1) + { + + if(register_LY == 153) + gbLcdLYIncrementTicks -= GBLY_INCREMENT_CLOCK_TICKS - gbLine99Ticks; + else if(register_LY == 0) + gbLcdLYIncrementTicks += GBLY_INCREMENT_CLOCK_TICKS - gbLine99Ticks; + } + + // GB only 'bug' : Halt state is broken one tick before LY==LYC interrupt + // is reflected in the registers. + if ((gbHardware & 5) && (IFF & 0x80) && (register_LY == register_LYC) + && (register_STAT & 0x40) && (register_LY != 0)) + { + if (!((gbLcdModeDelayed != 1) && (register_LY==0))) + { + gbInt48Signal &= ~9; + gbCompareLYToLYC(); + gbLYChangeHappened = false; + gbMemory[0xff41] = register_STAT; + gbMemory[0xff0f] = register_IF; + } + + gbLcdLYIncrementTicksDelayed += GBLY_INCREMENT_CLOCK_TICKS - gbLine99Ticks+1; + } + } + + + if ((gbLcdTicks <= 0) && (!gbLCDChangeHappened)) + { + gbLCDChangeHappened = true; + + switch(gbLcdMode) + { + case 0: + { + // H-Blank + // check if we reached the V-Blank period + if(register_LY == 144) { + // Yes, V-Blank + // set the LY increment counter + if (gbHardware & 0x5) + { + register_IF |= 1; // V-Blank interrupt + } + + gbInt48Signal &= ~6; + if(register_STAT & 0x10) + { + // send LCD interrupt only if no interrupt 48h signal... + if ((!(gbInt48Signal & 1)) && ((!(gbInt48Signal & 8)) || (gbHardware & 0x0a))) + { + register_IF |=2; + gbInterruptLaunched |= 2; + if (gbHardware & 0xa) + gbInterruptWait = 1; + } + gbInt48Signal |= 2; + } + gbInt48Signal &= ~1; + + gbLcdTicks += GBLCD_MODE_1_CLOCK_TICKS; + gbLcdMode = 1; + + } else { + // go the the OAM being accessed mode + gbLcdTicks += GBLCD_MODE_2_CLOCK_TICKS; + gbLcdMode = 2; + + gbInt48Signal &= ~6; + if(register_STAT & 0x20) + { + // send LCD interrupt only if no interrupt 48h signal... + if (!gbInt48Signal) + { + register_IF |= 2; + gbInterruptLaunched |= 2; + } + gbInt48Signal |= 4; + } + gbInt48Signal &= ~1; + } + } + break; + case 1: + { + // V-Blank + // next mode is OAM being accessed mode + gbInt48Signal &= ~5; + if(register_STAT & 0x20) + { + // send LCD interrupt only if no interrupt 48h signal... + if (!gbInt48Signal) + { + register_IF |= 2; + gbInterruptLaunched |= 2; + if ((gbHardware & 0xa) && (IFF & 0x80)) + gbInterruptWait = 1; + } + gbInt48Signal |= 4; + } + gbInt48Signal &= ~2; + + gbLcdTicks += GBLCD_MODE_2_CLOCK_TICKS; + + gbLcdMode = 2; + register_LY = 0x00; + + } + break; + case 2: + { + + // OAM being accessed mode + // next mode is OAM and VRAM in use + if ((gbScreenOn) && (register_LCDC & 0x80)) + { + gbDrawSprites(false); + // Used to add a one tick delay when a window line is drawn. + //(fixes a part of Carmaggedon problem) + if((register_LCDC & 0x01 || gbCgbMode) && (register_LCDC & 0x20) && + (gbWindowLine != -2)) { + + int inUseRegister_WY = 0; + int tempgbWindowLine = gbWindowLine; + + if ((tempgbWindowLine == -1) || (tempgbWindowLine>144)) + { + inUseRegister_WY = oldRegister_WY; + if (register_LY>oldRegister_WY) + tempgbWindowLine = 146; + } + + if(register_LY >= inUseRegister_WY) { + + if (tempgbWindowLine == -1) + tempgbWindowLine = 0; + + int wx = register_WX; + wx -= 7; + if (wx<0) + wx = 0; + + if((wx <= 159) && (tempgbWindowLine <= 143)) + for (int i = wx; i<300; i++) + if (gbSpeed) + gbSpritesTicks[i]+=3; + else + gbSpritesTicks[i]+=1; + } + } + } + + gbInt48Signal &= ~7; + + gbLcdTicks += GBLCD_MODE_3_CLOCK_TICKS+gbSpritesTicks[299]; + gbLcdMode = 3; + } + break; + case 3: + { + // OAM and VRAM in use + // next mode is H-Blank + + + gbInt48Signal &= ~7; + if(register_STAT & 0x08) + { + // send LCD interrupt only if no interrupt 48h signal... + if (!(gbInt48Signal & 8)) + { + register_IF |= 2; + if ((gbHardware & 0xa) && (IFF & 0x80)) + gbInterruptWait = 1; + } + gbInt48Signal |= 1; + } + + gbLcdTicks += GBLCD_MODE_0_CLOCK_TICKS-gbSpritesTicks[299]; + + gbLcdMode = 0; + + // No HDMA during HALT ! + if(gbHdmaOn && (!(IFF & 0x80) || (register_IE & register_IF & 0x1f))) { + gbDoHdma(); + } + + } + break; + } + } + + + if ((gbLcdTicksDelayed <= 0) && (gbLCDChangeHappened)) { + int framesToSkip = systemFrameSkip; + if(speedup) + framesToSkip = 9; // try 6 FPS during speedup + //gbLcdTicksDelayed = gbLcdTicks+1; + gbLCDChangeHappened = false; + switch(gbLcdModeDelayed) { + case 0: + { + // H-Blank + + memset(gbSCYLine,gbSCYLine[299],sizeof(gbSCYLine)); + memset(gbSCXLine,gbSCXLine[299],sizeof(gbSCXLine)); + memset(gbBgpLine,gbBgpLine[299],sizeof(gbBgpLine)); + memset(gbObp0Line,gbObp0Line[299],sizeof(gbObp0Line)); + memset(gbObp1Line,gbObp1Line[299],sizeof(gbObp1Line)); + memset(gbSpritesTicks,gbSpritesTicks[299],sizeof(gbSpritesTicks)); + + if (gbWindowLine <0) + oldRegister_WY = register_WY; + // check if we reached the V-Blank period + if(register_LY == 144) { + // Yes, V-Blank + // set the LY increment counter + + if(register_LCDC & 0x80) { + if (gbHardware & 0xa) + { + + register_IF |= 1; // V-Blank interrupt + gbInterruptLaunched |=1; + } + + + } + + gbLcdTicksDelayed += GBLCD_MODE_1_CLOCK_TICKS; + gbLcdModeDelayed = 1; + + gbFrameCount++; + systemFrame(); + + if((gbFrameCount % 10) == 0) + system10Frames(60); + + if(gbFrameCount >= 60) { + u32 currentTime = systemGetClock(); + if(currentTime != gbLastTime) + systemShowSpeed(100000/(currentTime - gbLastTime)); + else + systemShowSpeed(0); + gbLastTime = currentTime; + gbFrameCount = 0; + } + + if(systemReadJoypads()) { + // read joystick + if(gbSgbMode && gbSgbMultiplayer) { + if(gbSgbFourPlayers) { + gbJoymask[0] = systemReadJoypad(0); + gbJoymask[1] = systemReadJoypad(1); + gbJoymask[2] = systemReadJoypad(2); + gbJoymask[3] = systemReadJoypad(3); + } else { + gbJoymask[0] = systemReadJoypad(0); + gbJoymask[1] = systemReadJoypad(1); + } + } else { + gbJoymask[0] = systemReadJoypad(-1); + } + } + int newmask = gbJoymask[0] & 255; + + if(gbRomType == 0x22) { + systemUpdateMotionSensor(); + } + + if(newmask) + { + gbMemory[0xff0f] = register_IF |= 16; + } + + + newmask = (gbJoymask[0] >> 10); + + speedup = (newmask & 1) ? true : false; + gbCapture = (newmask & 2) ? true : false; + + if(gbCapture && !gbCapturePrevious) { + gbCaptureNumber++; + systemScreenCapture(gbCaptureNumber); + } + gbCapturePrevious = gbCapture; + + if(gbFrameSkipCount >= framesToSkip) { + + if(!gbSgbMask) + { + if (gbBorderOn) + gbSgbRenderBorder(); + //if (gbScreenOn) + systemDrawScreen(); + } + gbFrameSkipCount = 0; + } else + gbFrameSkipCount++; + + } else { + // go the the OAM being accessed mode + gbLcdTicksDelayed += GBLCD_MODE_2_CLOCK_TICKS; + gbLcdModeDelayed = 2; + gbInt48Signal &= ~3; + } + } + break; + case 1: + { + // V-Blank + // next mode is OAM being accessed mode + + // gbScreenOn = true; + + oldRegister_WY = register_WY; + + gbLcdTicksDelayed += GBLCD_MODE_2_CLOCK_TICKS; + gbLcdModeDelayed = 2; + + // reset the window line + gbWindowLine = -1; + } + break; + case 2: + { + // OAM being accessed mode + // next mode is OAM and VRAM in use + gbLcdTicksDelayed += GBLCD_MODE_3_CLOCK_TICKS+gbSpritesTicks[299]; + gbLcdModeDelayed = 3; + } + break; + case 3: + { + + // OAM and VRAM in use + // next mode is H-Blank + if((register_LY < 144) && (register_LCDC & 0x80) && gbScreenOn) { + if(!gbSgbMask) { + if(gbFrameSkipCount >= framesToSkip) { + if (!gbBlackScreen) + { + gbRenderLine(); + gbDrawSprites(true); + } + else if (gbBlackScreen) + { + u16 color = gbColorOption ? gbColorFilter[0] : 0; + if (!gbCgbMode) + color = gbColorOption ? gbColorFilter[gbPalette[3] & 0x7FFF] : + gbPalette[3] & 0x7FFF; + for(int i = 0; i < 160; i++) + { + gbLineMix[i] = color; + gbLineBuffer[i] = 0; + } + } + gbDrawLine(); + } + } + } + gbLcdTicksDelayed += GBLCD_MODE_0_CLOCK_TICKS-gbSpritesTicks[299]; + gbLcdModeDelayed = 0; + } + break; + } + } + + if ((gbLcdLYIncrementTicksDelayed <= 0) && (gbLYChangeHappened == true)) + { + + gbLYChangeHappened = false; + + if (!((gbLcdMode != 1) && (register_LY==0))) + { + { + gbInt48Signal &= ~8; + gbCompareLYToLYC(); + if ((gbInt48Signal == 8) && (!((register_LY == 0) && (gbHardware & 1)))) + gbInterruptLaunched |= 2; + if ((gbHardware & (gbSpeed ? 8 : 2)) && (register_LY==0) && ((register_STAT & 0x44) == 0x44) && (gbLcdLYIncrementTicksDelayed==0)) + { + gbInterruptWait = 1; + + } + } + } + gbLcdLYIncrementTicksDelayed += GBLY_INCREMENT_CLOCK_TICKS; + + if (gbLcdModeDelayed == 1) + { + + if(register_LY == 153) + gbLcdLYIncrementTicksDelayed -= GBLY_INCREMENT_CLOCK_TICKS - gbLine99Ticks; + else if(register_LY == 0) + gbLcdLYIncrementTicksDelayed += GBLY_INCREMENT_CLOCK_TICKS - gbLine99Ticks; + } + gbMemory[0xff0f] = register_IF; + gbMemory[0xff41] = register_STAT; + } + } + gbMemory[0xff0f] = register_IF; + gbMemory[0xff41] = register_STAT = (register_STAT & 0xfc) | gbLcdModeDelayed; + } + else + { + + // Used to update the screen with white lines when it's off. + // (it looks strange, but it's kinda accurate :p) + // You can try the Mario Demo Vx.x for exemple + // (check the bottom 2 lines while moving) + if (!gbWhiteScreen) + { + gbScreenTicks -= clockTicks; + gbLcdLYIncrementTicks -= clockTicks; + while (gbLcdLYIncrementTicks <=0) + { + register_LY = ((register_LY+1)%154); + gbLcdLYIncrementTicks+=GBLY_INCREMENT_CLOCK_TICKS; + } + if (gbScreenTicks <= 0) + { + gbWhiteScreen = 1; + u8 register_LYLcdOff = ((register_LY+154)%154); + for (register_LY=0;register_LY <= 0x90;register_LY++) + { + u16 color = gbColorOption ? gbColorFilter[0x7FFF] : + 0x7FFF; + if (!gbCgbMode) + color = gbColorOption ? gbColorFilter[gbPalette[0] & 0x7FFF] : + gbPalette[0] & 0x7FFF; + for(int i = 0; i < 160; i++) + { + gbLineMix[i] = color; + gbLineBuffer[i] = 0; + } + gbDrawLine(); + } + register_LY = register_LYLcdOff; + } + } + + if (gbWhiteScreen) + { + gbLcdLYIncrementTicks -= clockTicks; + + while (gbLcdLYIncrementTicks <=0) + { + register_LY = ((register_LY+1)%154); + gbLcdLYIncrementTicks+=GBLY_INCREMENT_CLOCK_TICKS; + if (register_LY<144) + { + + u16 color = gbColorOption ? gbColorFilter[0x7FFF] : + 0x7FFF; + if (!gbCgbMode) + color = gbColorOption ? gbColorFilter[gbPalette[0] & 0x7FFF] : + gbPalette[0] & 0x7FFF; + for(int i = 0; i < 160; i++) + { + gbLineMix[i] = color; + gbLineBuffer[i] = 0; + } + gbDrawLine(); + } + else if ((register_LY==144) && (!systemFrameSkip)) + { + int framesToSkip = systemFrameSkip; + if(speedup) + framesToSkip = 9; // try 6 FPS during speedup + if((gbFrameSkipCount >= framesToSkip) || (gbWhiteScreen == 1)) { + gbWhiteScreen = 2; + + if(!gbSgbMask) + { + if (gbBorderOn) + gbSgbRenderBorder(); + //if (gbScreenOn) + systemDrawScreen(); + } + } + if(systemReadJoypads()) { + // read joystick + if(gbSgbMode && gbSgbMultiplayer) { + if(gbSgbFourPlayers) { + gbJoymask[0] = systemReadJoypad(0); + gbJoymask[1] = systemReadJoypad(1); + gbJoymask[2] = systemReadJoypad(2); + gbJoymask[3] = systemReadJoypad(3); + } else { + gbJoymask[0] = systemReadJoypad(0); + gbJoymask[1] = systemReadJoypad(1); + } + } else { + gbJoymask[0] = systemReadJoypad(-1); + } + } + gbFrameCount++; + + systemFrame(); + + if((gbFrameCount % 10) == 0) + system10Frames(60); + + if(gbFrameCount >= 60) { + u32 currentTime = systemGetClock(); + if(currentTime != gbLastTime) + systemShowSpeed(100000/(currentTime - gbLastTime)); + else + systemShowSpeed(0); + gbLastTime = currentTime; + gbFrameCount = 0; + } + } + } + } + } + + gbMemory[0xff41] = register_STAT; + + // serial emulation + if(gbSerialOn) { +#ifdef OLD_GB_LINK + if(linkConnected) { + gbSerialTicks -= clockTicks; + + while(gbSerialTicks <= 0) { + // increment number of shifted bits + gbSerialBits++; + linkProc(); + if(gbSerialOn && (gbMemory[0xff02] & 1)) { + if(gbSerialBits == 8) { + gbSerialBits = 0; + gbMemory[0xff01] = 0xff; + gbMemory[0xff02] &= 0x7f; + gbSerialOn = 0; + gbMemory[0xff0f] = register_IF |= 8; + gbSerialTicks = 0; + } + } + gbSerialTicks += GBSERIAL_CLOCK_TICKS; + } + } else { +#endif + if(gbMemory[0xff02] & 1) { + gbSerialTicks -= clockTicks; + + // overflow + while(gbSerialTicks <= 0) { + // shift serial byte to right and put a 1 bit in its place + // gbMemory[0xff01] = 0x80 | (gbMemory[0xff01]>>1); + // increment number of shifted bits + gbSerialBits++; + if(gbSerialBits == 8) { + // end of transmission + if(gbSerialFunction) // external device + gbMemory[0xff01] = gbSerialFunction(gbMemory[0xff01]); + else + gbMemory[0xff01] = 0xff; + gbSerialTicks = 0; + gbMemory[0xff02] &= 0x7f; + gbSerialOn = 0; + gbMemory[0xff0f] = register_IF |= 8; + gbSerialBits = 0; + } else + gbSerialTicks += GBSERIAL_CLOCK_TICKS; + } + } +#ifdef OLD_GB_LINK + } +#endif + } + + + soundTicks -= clockTicks; + if ( !gbSpeed ) + soundTicks -= clockTicks; + + while(soundTicks < 0) { + soundTicks += SOUND_CLOCK_TICKS; + + gbSoundTick(); + } + + + // timer emulation + + if(gbTimerOn) { + gbTimerTicks= ((gbInternalTimer) & gbTimerMask[gbTimerMode])+1-clockTicks; + + while(gbTimerTicks <= 0) { + register_TIMA++; + // timer overflow! + if((register_TIMA & 0xff) == 0) { + // reload timer modulo + register_TIMA = register_TMA; + // flag interrupt + gbMemory[0xff0f] = register_IF |= 4; + } + gbTimerTicks += gbTimerClockTicks; + } + gbTimerOnChange = false; + gbTimerModeChange = false; + + gbMemory[0xff05] = register_TIMA; + + } + + gbInternalTimer -= clockTicks; + while (gbInternalTimer<0) + gbInternalTimer+=0x100; + + clockTicks = 0; + + if (gbIntBreak == 1) + { + gbIntBreak = 0; + if ((register_IE & register_IF & gbInterruptLaunched & 0x3) && + ((IFF & 0x81) == 1) && (!gbInterruptWait) && (execute)) + { + gbIntBreak = 2; + PC.W = oldPCW; + execute = false; + gbOldClockTicks = 0; + } + if (gbOldClockTicks) + { + clockTicks = gbOldClockTicks; + gbOldClockTicks = 0; + goto gbRedoLoop; + } + } + + // Executes the opcode(s), and apply the instruction's remaining clockTicks (if any). + if (execute) + { + switch(opcode1) { + case 0xCB: + // extended opcode + switch(opcode2) { +#include "gbCodesCB.h" + } + break; +#include "gbCodes.h" + } + execute = false; + + if (clockTicks) + { + gbDmaTicks += clockTicks; + clockTicks = 0; + } + } + + if (gbDmaTicks) + { + clockTicks = gbGetNextEvent(gbDmaTicks); + + if (clockTicks<=gbDmaTicks) + gbDmaTicks -= clockTicks; + else + { + clockTicks = gbDmaTicks; + gbDmaTicks = 0; + } + + goto gbRedoLoop; + } + + + // Remove the 'if an IE is pending' flag if IE has finished being executed. + if ((IFF & 0x40) && !(IFF & 0x30)) + IFF &= 0x81; + + + + if ((register_IE & register_IF & 0x1f) && (IFF & 0x81) && (!gbInterruptWait)) + { + + if (IFF & 1) + { + // Add 5 ticks for the interrupt execution time + gbDmaTicks += 5; + + if (gbIntBreak == 2) + { + gbDmaTicks--; + gbIntBreak = 0; + } + + + if(register_IF & register_IE & 1) + gbVblank_interrupt(); + else if(register_IF & register_IE & 2) + gbLcd_interrupt(); + else if(register_IF & register_IE & 4) + gbTimer_interrupt(); + else if(register_IF & register_IE & 8) + gbSerial_interrupt(); + else if(register_IF & register_IE & 16) + gbJoypad_interrupt(); + } + + IFF &= ~0x81; + } + + if (IFF & 0x08) + IFF &=~0x79; + + // Used to apply the interrupt's execution time. + if (gbDmaTicks) + { + clockTicks = gbGetNextEvent(gbDmaTicks); + + if (clockTicks<=gbDmaTicks) + gbDmaTicks -= clockTicks; + else + { + clockTicks = gbDmaTicks; + gbDmaTicks = 0; + } + goto gbRedoLoop; + } + + + gbBlackScreen = false; + + if((ticksToStop <= 0)) { + if(!(register_LCDC & 0x80)) { + if(systemReadJoypads()) { + // read joystick + if(gbSgbMode && gbSgbMultiplayer) { + if(gbSgbFourPlayers) { + gbJoymask[0] = systemReadJoypad(0); + gbJoymask[1] = systemReadJoypad(1); + gbJoymask[2] = systemReadJoypad(2); + gbJoymask[3] = systemReadJoypad(3); + } else { + gbJoymask[0] = systemReadJoypad(0); + gbJoymask[1] = systemReadJoypad(1); + } + } else { + gbJoymask[0] = systemReadJoypad(-1); + } + } + } + return; + } + } +} + +struct EmulatedSystem GBSystem = { + // emuMain + gbEmulate, + // emuReset + gbReset, + // emuCleanUp + gbCleanUp, + // emuReadBattery + gbReadBatteryFile, + // emuWriteBattery + gbWriteBatteryFile, + // emuReadState + gbReadSaveState, + // emuWriteState + gbWriteSaveState, + // emuReadMemState + gbReadMemSaveState, + // emuWriteMemState + gbWriteMemSaveState, + // emuWritePNG + gbWritePNGFile, + // emuWriteBMP + gbWriteBMPFile, + // emuUpdateCPSR + NULL, + // emuHasDebugger + false, + // emuCount +#ifdef FINAL_VERSION + 70000/4, +#else + 1000, +#endif +}; diff --git a/src/gb/gbMemory.cpp b/src/gb/gbMemory.cpp index dc6d24cb..53a7f69e 100644 --- a/src/gb/gbMemory.cpp +++ b/src/gb/gbMemory.cpp @@ -1,1703 +1,1703 @@ -#include "../System.h" -#include "../common/Port.h" -#include "gbGlobals.h" -#include "gbMemory.h" -#include "gb.h" -u8 gbDaysinMonth [12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; -const u8 gbDisabledRam [8] = {0x80, 0xff, 0xf0, 0x00, 0x30, 0xbf, 0xbf, 0xbf}; -extern int gbGBCColorType; -extern gbRegister PC; - -mapperMBC1 gbDataMBC1 = { - 0, // RAM enable - 1, // ROM bank - 0, // RAM bank - 0, // memory model - 0, // ROM high address - 0, // RAM address - 0 // Rom Bank 0 remapping -}; - -// MBC1 ROM write registers -void mapperMBC1ROM(u16 address, u8 value) -{ - int tmpAddress = 0; - - switch(address & 0x6000) { - case 0x0000: // RAM enable register - gbDataMBC1.mapperRAMEnable = ( ( value & 0x0a) == 0x0a ? 1 : 0); - break; - case 0x2000: // ROM bank select - // value = value & 0x1f; - if ((value == 1) && (address == 0x2100)) - gbDataMBC1.mapperRomBank0Remapping = 1; - - if((value & 0x1f) == 0) - value += 1; - if(value == gbDataMBC1.mapperROMBank) - break; - - tmpAddress = value << 14; - - // check current model - if (gbDataMBC1.mapperRomBank0Remapping == 3) { - tmpAddress = (value & 0xf) << 14; - tmpAddress |= (gbDataMBC1.mapperROMHighAddress & 3) << 18; - } - else - if(gbDataMBC1.mapperMemoryModel == 0) { - // model is 16/8, so we have a high address in use - tmpAddress |= (gbDataMBC1.mapperROMHighAddress & 3) << 19; - } - - tmpAddress &= gbRomSizeMask; - gbDataMBC1.mapperROMBank = value; - gbMemoryMap[0x04] = &gbRom[tmpAddress]; - gbMemoryMap[0x05] = &gbRom[tmpAddress + 0x1000]; - gbMemoryMap[0x06] = &gbRom[tmpAddress + 0x2000]; - gbMemoryMap[0x07] = &gbRom[tmpAddress + 0x3000]; - break; - case 0x4000: // RAM bank select - if(gbDataMBC1.mapperMemoryModel == 1) { - if (!gbRamSize) - { - if (gbDataMBC1.mapperRomBank0Remapping == 3) - { - gbDataMBC1.mapperROMHighAddress = value & 0x03; - tmpAddress = (gbDataMBC1.mapperROMHighAddress) << 18; - tmpAddress &= gbRomSizeMask; - gbMemoryMap[0x00] = &gbRom[tmpAddress]; - gbMemoryMap[0x01] = &gbRom[tmpAddress + 0x1000]; - gbMemoryMap[0x02] = &gbRom[tmpAddress + 0x2000]; - gbMemoryMap[0x03] = &gbRom[tmpAddress + 0x3000]; - gbMemoryMap[0x04] = &gbRom[tmpAddress + 0x4000]; - gbMemoryMap[0x05] = &gbRom[tmpAddress + 0x5000]; - gbMemoryMap[0x06] = &gbRom[tmpAddress + 0x6000]; - gbMemoryMap[0x07] = &gbRom[tmpAddress + 0x7000]; - } - else gbDataMBC1.mapperRomBank0Remapping = 0; - } - // 4/32 model, RAM bank switching provided - value = value & 0x03; - if(value == gbDataMBC1.mapperRAMBank) - break; - tmpAddress = value << 13; - tmpAddress &= gbRamSizeMask; - if(gbRamSize) { - gbMemoryMap[0x0a] = &gbRam[tmpAddress]; - gbMemoryMap[0x0b] = &gbRam[tmpAddress + 0x1000]; - } - gbDataMBC1.mapperRAMBank = value; - gbDataMBC1.mapperRAMAddress = tmpAddress; - - if (gbDataMBC1.mapperRomBank0Remapping != 3) - gbDataMBC1.mapperROMHighAddress = 0; - } else { - // 16/8, set the high address - gbDataMBC1.mapperROMHighAddress = value & 0x03; - tmpAddress = gbDataMBC1.mapperROMBank << 14; - tmpAddress |= (gbDataMBC1.mapperROMHighAddress) << 19; - tmpAddress &= gbRomSizeMask; - gbMemoryMap[0x04] = &gbRom[tmpAddress]; - gbMemoryMap[0x05] = &gbRom[tmpAddress + 0x1000]; - gbMemoryMap[0x06] = &gbRom[tmpAddress + 0x2000]; - gbMemoryMap[0x07] = &gbRom[tmpAddress + 0x3000]; - if(gbRamSize) { - gbMemoryMap[0x0a] = &gbRam[0]; - gbMemoryMap[0x0b] = &gbRam[0x1000]; - } - - gbDataMBC1.mapperRAMBank = 0; - } - break; - case 0x6000: // memory model select - gbDataMBC1.mapperMemoryModel = value & 1; - - if(gbDataMBC1.mapperMemoryModel == 1) { - // 4/32 model, RAM bank switching provided - - value = gbDataMBC1.mapperRAMBank & 0x03; - tmpAddress = value << 13; - tmpAddress &= gbRamSizeMask; - if(gbRamSize) { - gbMemoryMap[0x0a] = &gbRam[gbDataMBC1.mapperRAMAddress]; - gbMemoryMap[0x0b] = &gbRam[gbDataMBC1.mapperRAMAddress + 0x1000]; - gbDataMBC1.mapperRomBank0Remapping = 0; - } - else gbDataMBC1.mapperRomBank0Remapping |=2; - - gbDataMBC1.mapperRAMBank = value; - gbDataMBC1.mapperRAMAddress = tmpAddress; - - tmpAddress = gbDataMBC1.mapperROMBank << 14; - - - tmpAddress &= gbRomSizeMask; - gbMemoryMap[0x04] = &gbRom[tmpAddress]; - gbMemoryMap[0x05] = &gbRom[tmpAddress + 0x1000]; - gbMemoryMap[0x06] = &gbRom[tmpAddress + 0x2000]; - gbMemoryMap[0x07] = &gbRom[tmpAddress + 0x3000]; - - } else { - // 16/8, set the high address - - tmpAddress = gbDataMBC1.mapperROMBank << 14; - tmpAddress |= (gbDataMBC1.mapperROMHighAddress) << 19; - tmpAddress &= gbRomSizeMask; - gbMemoryMap[0x04] = &gbRom[tmpAddress]; - gbMemoryMap[0x05] = &gbRom[tmpAddress + 0x1000]; - gbMemoryMap[0x06] = &gbRom[tmpAddress + 0x2000]; - gbMemoryMap[0x07] = &gbRom[tmpAddress + 0x3000]; - if(gbRamSize) { - gbMemoryMap[0x0a] = &gbRam[0]; - gbMemoryMap[0x0b] = &gbRam[0x1000]; - } - } - break; - } -} - -// MBC1 RAM write -void mapperMBC1RAM(u16 address, u8 value) -{ - if(gbDataMBC1.mapperRAMEnable) { - if(gbRamSize) { - gbMemoryMap[address >> 12][address & 0x0fff] = value; - systemSaveUpdateCounter = SYSTEM_SAVE_UPDATED; - } - } -} - -// MBC1 read RAM -u8 mapperMBC1ReadRAM(u16 address) -{ - - if(gbDataMBC1.mapperRAMEnable) - return gbMemoryMap[address>>12][address & 0x0fff]; - - if (!genericflashcardEnable) - return 0xff; - else - if ((address & 0x1000) >= 0x1000) - { - // The value returned when reading RAM while it's disabled - // is constant, exept for the GBASP hardware. - // (actually, is the address that read is out of the ROM, the returned value if 0xff...) - if (PC.W>=0xff80) - return 0xff; - else - if ((gbHardware & 0x08) && (gbGBCColorType == 2)) - { - if (address & 1) - return 0xfb; - else - return 0x7a; - } - else - return 0x0a; - } - else - return gbDisabledRam[address & 7]; -} - -void memoryUpdateMapMBC1() -{ - int tmpAddress = gbDataMBC1.mapperROMBank << 14; - - // check current model - if (gbDataMBC1.mapperRomBank0Remapping == 3) { - tmpAddress = (gbDataMBC1.mapperROMHighAddress & 3) << 18; - tmpAddress &= gbRomSizeMask; - gbMemoryMap[0x00] = &gbRom[tmpAddress]; - gbMemoryMap[0x01] = &gbRom[tmpAddress + 0x1000]; - gbMemoryMap[0x02] = &gbRom[tmpAddress + 0x2000]; - gbMemoryMap[0x03] = &gbRom[tmpAddress + 0x3000]; - - tmpAddress |= (gbDataMBC1.mapperROMBank & 0xf) << 14; - gbMemoryMap[0x04] = &gbRom[tmpAddress]; - gbMemoryMap[0x05] = &gbRom[tmpAddress + 0x1000]; - gbMemoryMap[0x06] = &gbRom[tmpAddress + 0x2000]; - gbMemoryMap[0x07] = &gbRom[tmpAddress + 0x3000]; - } - else - { - if(gbDataMBC1.mapperMemoryModel == 0) { - // model is 16/8, so we have a high address in use - tmpAddress |= (gbDataMBC1.mapperROMHighAddress & 3) << 19; - } - - tmpAddress &= gbRomSizeMask; - gbMemoryMap[0x04] = &gbRom[tmpAddress]; - gbMemoryMap[0x05] = &gbRom[tmpAddress + 0x1000]; - gbMemoryMap[0x06] = &gbRom[tmpAddress + 0x2000]; - gbMemoryMap[0x07] = &gbRom[tmpAddress + 0x3000]; - } - - if(gbRamSize) { - if(gbDataMBC1.mapperMemoryModel == 1) { - gbMemoryMap[0x0a] = &gbRam[gbDataMBC1.mapperRAMAddress]; - gbMemoryMap[0x0b] = &gbRam[gbDataMBC1.mapperRAMAddress + 0x1000]; - } else { - gbMemoryMap[0x0a] = &gbRam[0]; - gbMemoryMap[0x0b] = &gbRam[0x1000]; - } - } -} - -mapperMBC2 gbDataMBC2 = { - 0, // RAM enable - 1 // ROM bank -}; - -// MBC2 ROM write registers -void mapperMBC2ROM(u16 address, u8 value) -{ - switch(address & 0x6000) { - case 0x0000: // RAM enable - if(!(address & 0x0100)) { - gbDataMBC2.mapperRAMEnable = (value & 0x0f) == 0x0a; - } - break; - case 0x2000: // ROM bank select - if(address & 0x0100) { - value &= 0x0f; - - if(value == 0) - value = 1; - if(gbDataMBC2.mapperROMBank != value) { - gbDataMBC2.mapperROMBank = value; - - int tmpAddress = value << 14; - - tmpAddress &= gbRomSizeMask; - - gbMemoryMap[0x04] = &gbRom[tmpAddress]; - gbMemoryMap[0x05] = &gbRom[tmpAddress + 0x1000]; - gbMemoryMap[0x06] = &gbRom[tmpAddress + 0x2000]; - gbMemoryMap[0x07] = &gbRom[tmpAddress + 0x3000]; - } - } - break; - } -} - -// MBC2 RAM write -void mapperMBC2RAM(u16 address, u8 value) -{ - if(gbDataMBC2.mapperRAMEnable) { - if(gbRamSize && address < 0xa200) { - gbMemoryMap[address >> 12][address & 0x0fff] = value; - systemSaveUpdateCounter = SYSTEM_SAVE_UPDATED; - } - } -} - -void memoryUpdateMapMBC2() -{ - int tmpAddress = gbDataMBC2.mapperROMBank << 14; - - tmpAddress &= gbRomSizeMask; - - gbMemoryMap[0x04] = &gbRom[tmpAddress]; - gbMemoryMap[0x05] = &gbRom[tmpAddress + 0x1000]; - gbMemoryMap[0x06] = &gbRom[tmpAddress + 0x2000]; - gbMemoryMap[0x07] = &gbRom[tmpAddress + 0x3000]; -} - -mapperMBC3 gbDataMBC3 = { - 0, // RAM enable - 1, // ROM bank - 0, // RAM bank - 0, // RAM address - 0, // timer clock latch - 0, // timer clock register - 0, // timer seconds - 0, // timer minutes - 0, // timer hours - 0, // timer days - 0, // timer control - 0, // timer latched seconds - 0, // timer latched minutes - 0, // timer latched hours - 0, // timer latched days - 0, // timer latched control - (time_t)-1 // last time -}; - -void memoryUpdateMBC3Clock() -{ - time_t now = time(NULL); - time_t diff = now - gbDataMBC3.mapperLastTime; - if(diff > 0) { - // update the clock according to the last update time - gbDataMBC3.mapperSeconds += (int)(diff % 60); - if(gbDataMBC3.mapperSeconds > 59) { - gbDataMBC3.mapperSeconds -= 60; - gbDataMBC3.mapperMinutes++; - } - - diff /= 60; - - gbDataMBC3.mapperMinutes += (int)(diff % 60); - if(gbDataMBC3.mapperMinutes > 59) { - gbDataMBC3.mapperMinutes -= 60; - gbDataMBC3.mapperHours++; - } - - diff /= 60; - - gbDataMBC3.mapperHours += (int)(diff % 24); - if(gbDataMBC3.mapperHours > 23) { - gbDataMBC3.mapperHours -= 24; - gbDataMBC3.mapperDays++; - } - diff /= 24; - - gbDataMBC3.mapperDays += (int)(diff & 0xffffffff); - if(gbDataMBC3.mapperDays > 255) { - if(gbDataMBC3.mapperDays > 511) { - gbDataMBC3.mapperDays %= 512; - gbDataMBC3.mapperControl |= 0x80; - } - gbDataMBC3.mapperControl = (gbDataMBC3.mapperControl & 0xfe) | - (gbDataMBC3.mapperDays>255 ? 1 : 0); - } - } - gbDataMBC3.mapperLastTime = now; -} - -// MBC3 ROM write registers -void mapperMBC3ROM(u16 address, u8 value) -{ - int tmpAddress = 0; - - switch(address & 0x6000) { - case 0x0000: // RAM enable register - gbDataMBC3.mapperRAMEnable = ( ( value & 0x0a) == 0x0a ? 1 : 0); - break; - case 0x2000: // ROM bank select - value = value & 0x7f; - if(value == 0) - value = 1; - if(value == gbDataMBC3.mapperROMBank) - break; - - tmpAddress = value << 14; - - tmpAddress &= gbRomSizeMask; - gbDataMBC3.mapperROMBank = value; - gbMemoryMap[0x04] = &gbRom[tmpAddress]; - gbMemoryMap[0x05] = &gbRom[tmpAddress + 0x1000]; - gbMemoryMap[0x06] = &gbRom[tmpAddress + 0x2000]; - gbMemoryMap[0x07] = &gbRom[tmpAddress + 0x3000]; - - break; - case 0x4000: // RAM bank select - if(value < 8) { - if(value == gbDataMBC3.mapperRAMBank) - break; - tmpAddress = value << 13; - tmpAddress &= gbRamSizeMask; - gbMemoryMap[0x0a] = &gbRam[tmpAddress]; - gbMemoryMap[0x0b] = &gbRam[tmpAddress + 0x1000]; - gbDataMBC3.mapperRAMBank = value; - gbDataMBC3.mapperRAMAddress = tmpAddress; - } else { - if(gbDataMBC3.mapperRAMEnable) { - gbDataMBC3.mapperRAMBank = -1; - - gbDataMBC3.mapperClockRegister = value; - } - } - break; - case 0x6000: // clock latch - if(gbDataMBC3.mapperClockLatch == 0 && value == 1) { - memoryUpdateMBC3Clock(); - gbDataMBC3.mapperLSeconds = gbDataMBC3.mapperSeconds; - gbDataMBC3.mapperLMinutes = gbDataMBC3.mapperMinutes; - gbDataMBC3.mapperLHours = gbDataMBC3.mapperHours; - gbDataMBC3.mapperLDays = gbDataMBC3.mapperDays; - gbDataMBC3.mapperLControl = gbDataMBC3.mapperControl; - } - if(value == 0x00 || value == 0x01) - gbDataMBC3.mapperClockLatch = value; - break; - } -} - -// MBC3 RAM write -void mapperMBC3RAM(u16 address, u8 value) -{ - if(gbDataMBC3.mapperRAMEnable) { - if(gbDataMBC3.mapperRAMBank != -1) { - if(gbRamSize) { - gbMemoryMap[address>>12][address & 0x0fff] = value; - systemSaveUpdateCounter = SYSTEM_SAVE_UPDATED; - } - } else { - time(&gbDataMBC3.mapperLastTime); - switch(gbDataMBC3.mapperClockRegister) { - case 0x08: - gbDataMBC3.mapperSeconds = value; - break; - case 0x09: - gbDataMBC3.mapperMinutes = value; - break; - case 0x0a: - gbDataMBC3.mapperHours = value; - break; - case 0x0b: - gbDataMBC3.mapperDays = value; - break; - case 0x0c: - if(gbDataMBC3.mapperControl & 0x80) - gbDataMBC3.mapperControl = 0x80 | value; - else - gbDataMBC3.mapperControl = value; - break; - } - } - } -} - -// MBC3 read RAM -u8 mapperMBC3ReadRAM(u16 address) -{ - if(gbDataMBC3.mapperRAMEnable) { - if(gbDataMBC3.mapperRAMBank != -1) { - return gbMemoryMap[address>>12][address & 0x0fff]; - } - - switch(gbDataMBC3.mapperClockRegister) { - case 0x08: - return gbDataMBC3.mapperLSeconds; - break; - case 0x09: - return gbDataMBC3.mapperLMinutes; - break; - case 0x0a: - return gbDataMBC3.mapperLHours; - break; - case 0x0b: - return gbDataMBC3.mapperLDays; - break; - case 0x0c: - return gbDataMBC3.mapperLControl; - } - } - - if (!genericflashcardEnable) - return 0xff; - else - if ((address & 0x1000) >= 0x1000) - { - // The value returned when reading RAM while it's disabled - // is constant, exept for the GBASP hardware. - // (actually, is the address that read is out of the ROM, the returned value if 0xff...) - if (PC.W>=0xff80) - return 0xff; - else - if ((gbHardware & 0x08) && (gbGBCColorType == 2)) - { - if (address & 1) - return 0xfb; - else - return 0x7a; - } - else - return 0x0a; - } - else - return gbDisabledRam[address & 7]; -} - -void memoryUpdateMapMBC3() -{ - int tmpAddress = gbDataMBC3.mapperROMBank << 14; - - tmpAddress &= gbRomSizeMask; - - gbMemoryMap[0x04] = &gbRom[tmpAddress]; - gbMemoryMap[0x05] = &gbRom[tmpAddress + 0x1000]; - gbMemoryMap[0x06] = &gbRom[tmpAddress + 0x2000]; - gbMemoryMap[0x07] = &gbRom[tmpAddress + 0x3000]; - - if(gbDataMBC3.mapperRAMBank >= 0 && gbRamSize) { - tmpAddress = gbDataMBC3.mapperRAMBank << 13; - tmpAddress &= gbRamSizeMask; - gbMemoryMap[0x0a] = &gbRam[tmpAddress]; - gbMemoryMap[0x0b] = &gbRam[tmpAddress + 0x1000]; - } -} - -mapperMBC5 gbDataMBC5 = { - 0, // RAM enable - 1, // ROM bank - 0, // RAM bank - 0, // ROM high address - 0, // RAM address - 0 // is rumble cartridge? -}; - -// MBC5 ROM write registers -void mapperMBC5ROM(u16 address, u8 value) -{ - int tmpAddress = 0; - - switch(address & 0x6000) { - case 0x0000: // RAM enable register - gbDataMBC5.mapperRAMEnable = ( ( value & 0x0a) == 0x0a ? 1 : 0); - break; - case 0x2000: // ROM bank select - - if(address < 0x3000) { - value = value & 0xff; - if(value == gbDataMBC5.mapperROMBank) - break; - - tmpAddress = (value << 14) | (gbDataMBC5.mapperROMHighAddress << 22) ; - - tmpAddress &= gbRomSizeMask; - gbDataMBC5.mapperROMBank = value; - gbMemoryMap[0x04] = &gbRom[tmpAddress]; - gbMemoryMap[0x05] = &gbRom[tmpAddress + 0x1000]; - gbMemoryMap[0x06] = &gbRom[tmpAddress + 0x2000]; - gbMemoryMap[0x07] = &gbRom[tmpAddress + 0x3000]; - - } else { - value = value & 1; - if(value == gbDataMBC5.mapperROMHighAddress) - break; - - tmpAddress = (gbDataMBC5.mapperROMBank << 14) | (value << 22); - - tmpAddress &= gbRomSizeMask; - gbDataMBC5.mapperROMHighAddress = value; - gbMemoryMap[0x04] = &gbRom[tmpAddress]; - gbMemoryMap[0x05] = &gbRom[tmpAddress + 0x1000]; - gbMemoryMap[0x06] = &gbRom[tmpAddress + 0x2000]; - gbMemoryMap[0x07] = &gbRom[tmpAddress + 0x3000]; - } - break; - case 0x4000: // RAM bank select - if(gbDataMBC5.isRumbleCartridge) - value &= 0x07; - else - value &= 0x0f; - if(value == gbDataMBC5.mapperRAMBank) - break; - tmpAddress = value << 13; - tmpAddress &= gbRamSizeMask; - if(gbRamSize) { - gbMemoryMap[0x0a] = &gbRam[tmpAddress]; - gbMemoryMap[0x0b] = &gbRam[tmpAddress + 0x1000]; - - gbDataMBC5.mapperRAMBank = value; - gbDataMBC5.mapperRAMAddress = tmpAddress; - } - break; - } -} - -// MBC5 RAM write -void mapperMBC5RAM(u16 address, u8 value) -{ - if(gbDataMBC5.mapperRAMEnable) { - if(gbRamSize) { - gbMemoryMap[address >> 12][address & 0x0fff] = value; - systemSaveUpdateCounter = SYSTEM_SAVE_UPDATED; - } - } -} - -// MBC5 read RAM -u8 mapperMBC5ReadRAM(u16 address) -{ - - if(gbDataMBC5.mapperRAMEnable) - return gbMemoryMap[address>>12][address & 0x0fff]; - - if (!genericflashcardEnable) - return 0xff; - else - if ((address & 0x1000) >= 0x1000) - { - // The value returned when reading RAM while it's disabled - // is constant, exept for the GBASP hardware. - // (actually, is the address that read is out of the ROM, the returned value if 0xff...) - if (PC.W>=0xff80) - return 0xff; - else - if ((gbHardware & 0x08) && (gbGBCColorType == 2)) - { - if (address & 1) - return 0xfb; - else - return 0x7a; - } - else - return 0x0a; - } - else - return gbDisabledRam[address & 7]; -} - -void memoryUpdateMapMBC5() -{ - int tmpAddress = (gbDataMBC5.mapperROMBank << 14) | - (gbDataMBC5.mapperROMHighAddress << 22) ; - - tmpAddress &= gbRomSizeMask; - gbMemoryMap[0x04] = &gbRom[tmpAddress]; - gbMemoryMap[0x05] = &gbRom[tmpAddress + 0x1000]; - gbMemoryMap[0x06] = &gbRom[tmpAddress + 0x2000]; - gbMemoryMap[0x07] = &gbRom[tmpAddress + 0x3000]; - - if(gbRamSize) { - tmpAddress = gbDataMBC5.mapperRAMBank << 13; - tmpAddress &= gbRamSizeMask; - gbMemoryMap[0x0a] = &gbRam[tmpAddress]; - gbMemoryMap[0x0b] = &gbRam[tmpAddress + 0x1000]; - } -} - -mapperMBC7 gbDataMBC7 = { - 0, // RAM enable - 1, // ROM bank - 0, // RAM bank - 0, // RAM address - 0, // chip select - 0, // ?? - 0, // mapper state - 0, // buffer for receiving serial data - 0, // idle state - 0, // count of bits received - 0, // command received - 0, // address received - 0, // write enable - 0, // value to return on ram -}; - -// MBC7 ROM write registers -void mapperMBC7ROM(u16 address, u8 value) -{ - int tmpAddress = 0; - - switch(address & 0x6000) { - case 0x0000: - break; - case 0x2000: // ROM bank select - value = value & 0x7f; - if(value == 0) - value = 1; - - if(value == gbDataMBC7.mapperROMBank) - break; - - tmpAddress = (value << 14); - - tmpAddress &= gbRomSizeMask; - gbDataMBC7.mapperROMBank = value; - gbMemoryMap[0x04] = &gbRom[tmpAddress]; - gbMemoryMap[0x05] = &gbRom[tmpAddress + 0x1000]; - gbMemoryMap[0x06] = &gbRom[tmpAddress + 0x2000]; - gbMemoryMap[0x07] = &gbRom[tmpAddress + 0x3000]; - break; - case 0x4000: // RAM bank select/enable - if(value < 8) { - tmpAddress = (value&3) << 13; - tmpAddress &= gbRamSizeMask; - gbMemoryMap[0x0a] = &gbMemory[0xa000]; - gbMemoryMap[0x0b] = &gbMemory[0xb000]; - - gbDataMBC7.mapperRAMBank = value; - gbDataMBC7.mapperRAMAddress = tmpAddress; - gbDataMBC7.mapperRAMEnable = 0; - } else { - gbDataMBC7.mapperRAMEnable = 0; - } - break; - } -} - -// MBC7 read RAM -u8 mapperMBC7ReadRAM(u16 address) -{ - switch(address & 0xa0f0) { - case 0xa000: - case 0xa010: - case 0xa060: - case 0xa070: - return 0; - case 0xa020: - // sensor X low byte - return systemGetSensorX() & 255; - case 0xa030: - // sensor X high byte - return systemGetSensorX() >> 8; - case 0xa040: - // sensor Y low byte - return systemGetSensorY() & 255; - case 0xa050: - // sensor Y high byte - return systemGetSensorY() >> 8; - case 0xa080: - return gbDataMBC7.value; - } - - if (!genericflashcardEnable) - return 0xff; - else - if ((address & 0x1000) >= 0x1000) - { - // The value returned when reading RAM while it's disabled - // is constant, exept for the GBASP hardware. - // (actually, is the address that read is out of the ROM, the returned value if 0xff...) - if (PC.W>=0xff80) - return 0xff; - else - if ((gbHardware & 0x08) && (gbGBCColorType == 2)) - { - if (address & 1) - return 0xfb; - else - return 0x7a; - } - else - return 0x0a; - } - else - return gbDisabledRam[address & 7]; -} - -// MBC7 RAM write -void mapperMBC7RAM(u16 address, u8 value) -{ - if(address == 0xa080) { - // special processing needed - int oldCs = gbDataMBC7.cs,oldSk=gbDataMBC7.sk; - - gbDataMBC7.cs=value>>7; - gbDataMBC7.sk=(value>>6)&1; - - if(!oldCs && gbDataMBC7.cs) { - if(gbDataMBC7.state==5) { - if(gbDataMBC7.writeEnable) { - gbMemory[0xa000+gbDataMBC7.address*2]=gbDataMBC7.buffer>>8; - gbMemory[0xa000+gbDataMBC7.address*2+1]=gbDataMBC7.buffer&0xff; - systemSaveUpdateCounter = SYSTEM_SAVE_UPDATED; - } - gbDataMBC7.state=0; - gbDataMBC7.value=1; - } else { - gbDataMBC7.idle=true; - gbDataMBC7.state=0; - } - } - - if(!oldSk && gbDataMBC7.sk) { - if(gbDataMBC7.idle) { - if(value & 0x02) { - gbDataMBC7.idle=false; - gbDataMBC7.count=0; - gbDataMBC7.state=1; - } - } else { - switch(gbDataMBC7.state) { - case 1: - // receiving command - gbDataMBC7.buffer <<= 1; - gbDataMBC7.buffer |= (value & 0x02)?1:0; - gbDataMBC7.count++; - if(gbDataMBC7.count==2) { - // finished receiving command - gbDataMBC7.state=2; - gbDataMBC7.count=0; - gbDataMBC7.code=gbDataMBC7.buffer & 3; - } - break; - case 2: - // receive address - gbDataMBC7.buffer <<= 1; - gbDataMBC7.buffer |= (value&0x02)?1:0; - gbDataMBC7.count++; - if(gbDataMBC7.count==8) { - // finish receiving - gbDataMBC7.state=3; - gbDataMBC7.count=0; - gbDataMBC7.address=gbDataMBC7.buffer&0xff; - if(gbDataMBC7.code==0) { - if((gbDataMBC7.address>>6)==0) { - gbDataMBC7.writeEnable=0; - gbDataMBC7.state=0; - } else if((gbDataMBC7.address>>6) == 3) { - gbDataMBC7.writeEnable=1; - gbDataMBC7.state=0; - } - } - } - break; - case 3: - gbDataMBC7.buffer <<= 1; - gbDataMBC7.buffer |= (value&0x02)?1:0; - gbDataMBC7.count++; - - switch(gbDataMBC7.code) { - case 0: - if(gbDataMBC7.count==16) { - if((gbDataMBC7.address>>6)==0) { - gbDataMBC7.writeEnable = 0; - gbDataMBC7.state=0; - } else if((gbDataMBC7.address>>6)==1) { - if (gbDataMBC7.writeEnable) { - for(int i=0;i<256;i++) { - gbMemory[0xa000+i*2] = gbDataMBC7.buffer >> 8; - gbMemory[0xa000+i*2+1] = gbDataMBC7.buffer & 0xff; - systemSaveUpdateCounter = SYSTEM_SAVE_UPDATED; - } - } - gbDataMBC7.state=5; - } else if((gbDataMBC7.address>>6) == 2) { - if (gbDataMBC7.writeEnable) { - for(int i=0;i<256;i++) - WRITE16LE((u16 *)&gbMemory[0xa000+i*2], 0xffff); - systemSaveUpdateCounter = SYSTEM_SAVE_UPDATED; - } - gbDataMBC7.state=5; - } else if((gbDataMBC7.address>>6)==3) { - gbDataMBC7.writeEnable = 1; - gbDataMBC7.state=0; - } - gbDataMBC7.count=0; - } - break; - case 1: - if(gbDataMBC7.count==16) { - gbDataMBC7.count=0; - gbDataMBC7.state=5; - gbDataMBC7.value=0; - } - break; - case 2: - if(gbDataMBC7.count==1) { - gbDataMBC7.state=4; - gbDataMBC7.count=0; - gbDataMBC7.buffer = (gbMemory[0xa000+gbDataMBC7.address*2]<<8)| - (gbMemory[0xa000+gbDataMBC7.address*2+1]); - } - break; - case 3: - if(gbDataMBC7.count==16) { - gbDataMBC7.count=0; - gbDataMBC7.state=5; - gbDataMBC7.value=0; - gbDataMBC7.buffer=0xffff; - } - break; - } - break; - } - } - } - - if (oldSk && !gbDataMBC7.sk) { - if (gbDataMBC7.state==4) { - gbDataMBC7.value = (gbDataMBC7.buffer & 0x8000)?1:0; - gbDataMBC7.buffer <<= 1; - gbDataMBC7.count++; - if (gbDataMBC7.count==16) { - gbDataMBC7.count=0; - gbDataMBC7.state=0; - } - } - } - } -} - -void memoryUpdateMapMBC7() -{ - int tmpAddress = (gbDataMBC7.mapperROMBank << 14); - - tmpAddress &= gbRomSizeMask; - gbMemoryMap[0x04] = &gbRom[tmpAddress]; - gbMemoryMap[0x05] = &gbRom[tmpAddress + 0x1000]; - gbMemoryMap[0x06] = &gbRom[tmpAddress + 0x2000]; - gbMemoryMap[0x07] = &gbRom[tmpAddress + 0x3000]; -} - -mapperHuC1 gbDataHuC1 = { - 0, // RAM enable - 1, // ROM bank - 0, // RAM bank - 0, // memory model - 0, // ROM high address - 0 // RAM address -}; - -// HuC1 ROM write registers -void mapperHuC1ROM(u16 address, u8 value) -{ - int tmpAddress = 0; - - switch(address & 0x6000) { - case 0x0000: // RAM enable register - gbDataHuC1.mapperRAMEnable = ( ( value & 0x0a) == 0x0a ? 1 : 0); - break; - case 0x2000: // ROM bank select - value = value & 0x3f; - if(value == 0) - value = 1; - if(value == gbDataHuC1.mapperROMBank) - break; - - tmpAddress = value << 14; - - tmpAddress &= gbRomSizeMask; - gbDataHuC1.mapperROMBank = value; - gbMemoryMap[0x04] = &gbRom[tmpAddress]; - gbMemoryMap[0x05] = &gbRom[tmpAddress + 0x1000]; - gbMemoryMap[0x06] = &gbRom[tmpAddress + 0x2000]; - gbMemoryMap[0x07] = &gbRom[tmpAddress + 0x3000]; - break; - case 0x4000: // RAM bank select - if(gbDataHuC1.mapperMemoryModel == 1) { - // 4/32 model, RAM bank switching provided - value = value & 0x03; - if(value == gbDataHuC1.mapperRAMBank) - break; - tmpAddress = value << 13; - tmpAddress &= gbRamSizeMask; - gbMemoryMap[0x0a] = &gbRam[tmpAddress]; - gbMemoryMap[0x0b] = &gbRam[tmpAddress + 0x1000]; - gbDataHuC1.mapperRAMBank = value; - gbDataHuC1.mapperRAMAddress = tmpAddress; - } else { - // 16/8, set the high address - gbDataHuC1.mapperROMHighAddress = value & 0x03; - tmpAddress = gbDataHuC1.mapperROMBank << 14; - tmpAddress |= (gbDataHuC1.mapperROMHighAddress) << 19; - tmpAddress &= gbRomSizeMask; - gbMemoryMap[0x04] = &gbRom[tmpAddress]; - gbMemoryMap[0x05] = &gbRom[tmpAddress + 0x1000]; - gbMemoryMap[0x06] = &gbRom[tmpAddress + 0x2000]; - gbMemoryMap[0x07] = &gbRom[tmpAddress + 0x3000]; - } - break; - case 0x6000: // memory model select - gbDataHuC1.mapperMemoryModel = value & 1; - break; - } -} - -// HuC1 RAM write -void mapperHuC1RAM(u16 address, u8 value) -{ - if(gbDataHuC1.mapperRAMEnable) { - if(gbRamSize) { - gbMemoryMap[address >> 12][address & 0x0fff] = value; - systemSaveUpdateCounter = SYSTEM_SAVE_UPDATED; - } - } -} - -void memoryUpdateMapHuC1() -{ - int tmpAddress = gbDataHuC1.mapperROMBank << 14; - - tmpAddress &= gbRomSizeMask; - - gbMemoryMap[0x04] = &gbRom[tmpAddress]; - gbMemoryMap[0x05] = &gbRom[tmpAddress + 0x1000]; - gbMemoryMap[0x06] = &gbRom[tmpAddress + 0x2000]; - gbMemoryMap[0x07] = &gbRom[tmpAddress + 0x3000]; - - if(gbRamSize) { - tmpAddress = gbDataHuC1.mapperRAMBank << 13; - tmpAddress &= gbRamSizeMask; - gbMemoryMap[0x0a] = &gbRam[tmpAddress]; - gbMemoryMap[0x0b] = &gbRam[tmpAddress + 0x1000]; - } -} - -mapperHuC3 gbDataHuC3 = { - 0, // RAM enable - 1, // ROM bank - 0, // RAM bank - 0, // RAM address - 0, // RAM flag - 0 // RAM read value -}; - - -// HuC3 ROM write registers -void mapperHuC3ROM(u16 address, u8 value) -{ - int tmpAddress = 0; - - switch(address & 0x6000) { - case 0x0000: // RAM enable register - gbDataHuC3.mapperRAMEnable = ( value == 0x0a ? 1 : 0); - gbDataHuC3.mapperRAMFlag = value; - if(gbDataHuC3.mapperRAMFlag != 0x0a) - gbDataHuC3.mapperRAMBank = -1; - break; - case 0x2000: // ROM bank select - value = value & 0x7f; - if(value == 0) - value = 1; - if(value == gbDataHuC3.mapperROMBank) - break; - - tmpAddress = value << 14; - - tmpAddress &= gbRomSizeMask; - gbDataHuC3.mapperROMBank = value; - gbMemoryMap[0x04] = &gbRom[tmpAddress]; - gbMemoryMap[0x05] = &gbRom[tmpAddress + 0x1000]; - gbMemoryMap[0x06] = &gbRom[tmpAddress + 0x2000]; - gbMemoryMap[0x07] = &gbRom[tmpAddress + 0x3000]; - break; - case 0x4000: // RAM bank select - value = value & 0x03; - if(value == gbDataHuC3.mapperRAMBank) - break; - tmpAddress = value << 13; - tmpAddress &= gbRamSizeMask; - gbMemoryMap[0x0a] = &gbRam[tmpAddress]; - gbMemoryMap[0x0b] = &gbRam[tmpAddress + 0x1000]; - gbDataHuC3.mapperRAMBank = value; - gbDataHuC3.mapperRAMAddress = tmpAddress; - break; - case 0x6000: // nothing to do! - break; - } -} - -// HuC3 read RAM -u8 mapperHuC3ReadRAM(u16 address) -{ - if(gbDataHuC3.mapperRAMFlag > 0x0b && - gbDataHuC3.mapperRAMFlag < 0x0e) { - if(gbDataHuC3.mapperRAMFlag != 0x0c) - return 1; - return gbDataHuC3.mapperRAMValue; - } else - return gbMemoryMap[address >> 12][address & 0x0fff]; -} - -// HuC3 RAM write -void mapperHuC3RAM(u16 address, u8 value) -{ - int *p; - - if(gbDataHuC3.mapperRAMFlag < 0x0b || - gbDataHuC3.mapperRAMFlag > 0x0e) { - if(gbDataHuC3.mapperRAMEnable) { - if(gbRamSize) { - gbMemoryMap[address >> 12][address & 0x0fff] = value; - systemSaveUpdateCounter = SYSTEM_SAVE_UPDATED; - } - } - } else { - if(gbDataHuC3.mapperRAMFlag == 0x0b) { - if(value == 0x62) { - gbDataHuC3.mapperRAMValue = 1; - } else { - switch(value & 0xf0) { - case 0x10: - p = &gbDataHuC3.mapperRegister2; - gbDataHuC3.mapperRAMValue = *(p+gbDataHuC3.mapperRegister1++); - if(gbDataHuC3.mapperRegister1 > 6) - gbDataHuC3.mapperRegister1 = 0; - break; - case 0x30: - p = &gbDataHuC3.mapperRegister2; - *(p+gbDataHuC3.mapperRegister1++) = value & 0x0f; - if(gbDataHuC3.mapperRegister1 > 6) - gbDataHuC3.mapperRegister1 = 0; - gbDataHuC3.mapperAddress = - (gbDataHuC3.mapperRegister6 << 24) | - (gbDataHuC3.mapperRegister5 << 16) | - (gbDataHuC3.mapperRegister4 << 8) | - (gbDataHuC3.mapperRegister3 << 4) | - (gbDataHuC3.mapperRegister2); - break; - case 0x40: - gbDataHuC3.mapperRegister1 = (gbDataHuC3.mapperRegister1 & 0xf0) | - (value & 0x0f); - gbDataHuC3.mapperRegister2 = (gbDataHuC3.mapperAddress & 0x0f); - gbDataHuC3.mapperRegister3 = ((gbDataHuC3.mapperAddress>>4)&0x0f); - gbDataHuC3.mapperRegister4 = ((gbDataHuC3.mapperAddress>>8)&0x0f); - gbDataHuC3.mapperRegister5 = ((gbDataHuC3.mapperAddress>>16)&0x0f); - gbDataHuC3.mapperRegister6 = ((gbDataHuC3.mapperAddress>>24)&0x0f); - gbDataHuC3.mapperRegister7 = 0; - gbDataHuC3.mapperRegister8 = 0; - gbDataHuC3.mapperRAMValue = 0; - break; - case 0x50: - gbDataHuC3.mapperRegister1 = (gbDataHuC3.mapperRegister1 & 0x0f) | - ((value << 4)&0x0f); - break; - default: - gbDataHuC3.mapperRAMValue = 1; - break; - } - } - } - } -} - -void memoryUpdateMapHuC3() -{ - int tmpAddress = gbDataHuC3.mapperROMBank << 14; - - tmpAddress &= gbRomSizeMask; - gbMemoryMap[0x04] = &gbRom[tmpAddress]; - gbMemoryMap[0x05] = &gbRom[tmpAddress + 0x1000]; - gbMemoryMap[0x06] = &gbRom[tmpAddress + 0x2000]; - gbMemoryMap[0x07] = &gbRom[tmpAddress + 0x3000]; - - if(gbRamSize) { - tmpAddress = gbDataHuC3.mapperRAMBank << 13; - tmpAddress &= gbRamSizeMask; - gbMemoryMap[0x0a] = &gbRam[tmpAddress]; - gbMemoryMap[0x0b] = &gbRam[tmpAddress + 0x1000]; - } -} - -// TAMA5 (for Tamagotchi 3 (gb)). -// Very basic (and ugly :p) support, only rom bank switching is actually working... -mapperTAMA5 gbDataTAMA5 = { - 1, // RAM enable - 1, // ROM bank - 0, // RAM bank - 0, // RAM address - 0, // RAM Byte select - 0, // mapper command number - 0, // mapper last command; - { - 0, // commands 0x0 - 0, // commands 0x1 - 0, // commands 0x2 - 0, // commands 0x3 - 0, // commands 0x4 - 0, // commands 0x5 - 0, // commands 0x6 - 0, // commands 0x7 - 0, // commands 0x8 - 0, // commands 0x9 - 0, // commands 0xa - 0, // commands 0xb - 0, // commands 0xc - 0, // commands 0xd - 0, // commands 0xe - 0 // commands 0xf - }, - 0, // register - 0, // timer clock latch - 0, // timer clock register - 0, // timer seconds - 0, // timer minutes - 0, // timer hours - 0, // timer days - 0, // timer months - 0, // timer years - 0, // timer control - 0, // timer latched seconds - 0, // timer latched minutes - 0, // timer latched hours - 0, // timer latched days - 0, // timer latched months - 0, // timer latched years - 0, // timer latched control - (time_t)-1 // last time -}; - - -void memoryUpdateTAMA5Clock() -{ - if ((gbDataTAMA5.mapperYears & 3) == 0) - gbDaysinMonth[1] = 29; - else - gbDaysinMonth[1] = 28; - - time_t now = time(NULL); - time_t diff = now - gbDataTAMA5.mapperLastTime; - if(diff > 0) { - // update the clock according to the last update time - gbDataTAMA5.mapperSeconds += (int)(diff % 60); - if(gbDataTAMA5.mapperSeconds > 59) { - gbDataTAMA5.mapperSeconds -= 60; - gbDataTAMA5.mapperMinutes++; - } - - diff /= 60; - - gbDataTAMA5.mapperMinutes += (int)(diff % 60); - if(gbDataTAMA5.mapperMinutes > 59) { - gbDataTAMA5.mapperMinutes -= 60; - gbDataTAMA5.mapperHours++; - } - - diff /= 60; - - gbDataTAMA5.mapperHours += (int)(diff % 24); - diff /= 24; - if(gbDataTAMA5.mapperHours > 23) { - gbDataTAMA5.mapperHours -= 24; - diff++; - - } - - time_t days = diff; - while (days) - { - gbDataTAMA5.mapperDays++; - days--; - if (gbDataTAMA5.mapperDays>gbDaysinMonth[gbDataTAMA5.mapperMonths-1]) - { - gbDataTAMA5.mapperDays = 1; - gbDataTAMA5.mapperMonths++; - if (gbDataTAMA5.mapperMonths>12) - { - gbDataTAMA5.mapperMonths = 1; - gbDataTAMA5.mapperYears++; - if ((gbDataTAMA5.mapperYears & 3) == 0) - gbDaysinMonth[1] = 29; - else - gbDaysinMonth[1] = 28; - } - } - } - } - gbDataTAMA5.mapperLastTime = now; - -} - - - -// TAMA5 RAM write -void mapperTAMA5RAM(u16 address, u8 value) -{ - if ((address & 0xffff) <= 0xa001) - { - switch (address & 1) - { - case 0: // 'Values' Register - { - value &= 0xf; - gbDataTAMA5.mapperCommands[gbDataTAMA5.mapperCommandNumber] = value; - gbMemoryMap[0xa][0] = value; - -/* int test = gbDataTAMA5.mapperCommands[gbDataTAMA5.mapperCommandNumber & 0x0e] | - (gbDataTAMA5.mapperCommands[(gbDataTAMA5.mapperCommandNumber & 0x0e) +1]<<4);*/ - - if ((gbDataTAMA5.mapperCommandNumber & 0xe) == 0) // Read Command !!! - { - gbDataTAMA5.mapperROMBank = gbDataTAMA5.mapperCommands[0] | - (gbDataTAMA5.mapperCommands[1]<<4); - - int tmpAddress = (gbDataTAMA5.mapperROMBank << 14); - - tmpAddress &= gbRomSizeMask; - gbMemoryMap[0x04] = &gbRom[tmpAddress]; - gbMemoryMap[0x05] = &gbRom[tmpAddress + 0x1000]; - gbMemoryMap[0x06] = &gbRom[tmpAddress + 0x2000]; - gbMemoryMap[0x07] = &gbRom[tmpAddress + 0x3000]; - - gbDataTAMA5.mapperCommands[0x0f] = 0; - } - else if ((gbDataTAMA5.mapperCommandNumber & 0xe) == 4) - { - gbDataTAMA5.mapperCommands[0x0f] = 1; - if (gbDataTAMA5.mapperCommandNumber == 4) - gbDataTAMA5.mapperCommands[5] =0; // correct ? - } - else if ((gbDataTAMA5.mapperCommandNumber & 0xe) == 6) - { - gbDataTAMA5.mapperRamByteSelect = (gbDataTAMA5.mapperCommands[7]<<4) | - (gbDataTAMA5.mapperCommands[6]&0x0f); - - // Write Commands !!! - if (gbDataTAMA5.mapperCommands[0x0f] && (gbDataTAMA5.mapperCommandNumber == 7)) - { - int data = (gbDataTAMA5.mapperCommands[0x04] & 0x0f) | - (gbDataTAMA5.mapperCommands[0x05] <<4); - - // Not sure when the write command should reset... - // but it doesn't seem to matter. - // gbDataTAMA5.mapperCommands[0x0f] = 0; - - if (gbDataTAMA5.mapperRamByteSelect == 0x8) // Timer stuff - { - switch (data & 0xf) - { - case 0x7: - gbDataTAMA5.mapperDays = ((gbDataTAMA5.mapperDays)/10)*10 + (data >> 4); - break; - case 0x8: - gbDataTAMA5.mapperDays = (gbDataTAMA5.mapperDays%10) + (data >>4)*10; - break; - case 0x9: - gbDataTAMA5.mapperMonths = ((gbDataTAMA5.mapperMonths)/10)*10 + (data >> 4); - break; - case 0xa: - gbDataTAMA5.mapperMonths = (gbDataTAMA5.mapperMonths%10) + (data >>4)*10; - break; - case 0xb: - gbDataTAMA5.mapperYears = ((gbDataTAMA5.mapperYears)%1000) + (data >> 4)*1000; - break; - case 0xc: - gbDataTAMA5.mapperYears = (gbDataTAMA5.mapperYears%100) + (gbDataTAMA5.mapperYears/1000)*1000 + - (data >>4)*100; - break; - default : - break; - } - } - else if (gbDataTAMA5.mapperRamByteSelect == 0x18) // Timer stuff again - { - memoryUpdateTAMA5Clock(); - gbDataTAMA5.mapperLSeconds = gbDataTAMA5.mapperSeconds; - gbDataTAMA5.mapperLMinutes = gbDataTAMA5.mapperMinutes; - gbDataTAMA5.mapperLHours = gbDataTAMA5.mapperHours; - gbDataTAMA5.mapperLDays = gbDataTAMA5.mapperDays; - gbDataTAMA5.mapperLMonths = gbDataTAMA5.mapperMonths; - gbDataTAMA5.mapperLYears = gbDataTAMA5.mapperYears; - gbDataTAMA5.mapperLControl = gbDataTAMA5.mapperControl; - - int seconds = (gbDataTAMA5.mapperLSeconds / 10)*16 + gbDataTAMA5.mapperLSeconds %10; - int secondsL = (gbDataTAMA5.mapperLSeconds % 10); - int secondsH = (gbDataTAMA5.mapperLSeconds / 10); - int minutes = (gbDataTAMA5.mapperLMinutes / 10)*16 + gbDataTAMA5.mapperLMinutes %10; - int hours = (gbDataTAMA5.mapperLHours / 10)*16 + gbDataTAMA5.mapperLHours %10; - int DaysL = gbDataTAMA5.mapperLDays % 10; - int DaysH = gbDataTAMA5.mapperLDays /10; - int MonthsL = gbDataTAMA5.mapperLMonths % 10; - int MonthsH = gbDataTAMA5.mapperLMonths / 10; - int Years3 = (gbDataTAMA5.mapperLYears / 100) % 10; - int Years4 = (gbDataTAMA5.mapperLYears / 1000); - - switch (data & 0x0f) - { - // I guess cases 0 and 1 are used for secondsL and secondsH - // so the game would update the timer values on screen when - // the seconds reset to 0... ? - case 0x0: - gbTAMA5ram[gbDataTAMA5.mapperRamByteSelect] = secondsL; - break; - case 0x1: - gbTAMA5ram[gbDataTAMA5.mapperRamByteSelect] = secondsH; - break; - case 0x7: - gbTAMA5ram[gbDataTAMA5.mapperRamByteSelect] = DaysL; // days low - break; - case 0x8: - gbTAMA5ram[gbDataTAMA5.mapperRamByteSelect] = DaysH; // days high - break; - case 0x9: - gbTAMA5ram[gbDataTAMA5.mapperRamByteSelect] = MonthsL; // month low - break; - case 0xa: - gbTAMA5ram[gbDataTAMA5.mapperRamByteSelect] = MonthsH; // month high - break; - case 0xb: - gbTAMA5ram[gbDataTAMA5.mapperRamByteSelect] = Years4; // years 4th digit - break; - case 0xc: - gbTAMA5ram[gbDataTAMA5.mapperRamByteSelect] = Years3; // years 3rd digit - break; - default : - break; - } - - gbTAMA5ram[0x54] = seconds; // incorrect ? (not used by the game) ? - gbTAMA5ram[0x64] = minutes; - gbTAMA5ram[0x74] = hours; - gbTAMA5ram[0x84] = DaysH*16+DaysL; // incorrect ? (not used by the game) ? - gbTAMA5ram[0x94] = MonthsH*16+MonthsL; // incorrect ? (not used by the game) ? - - time(&gbDataTAMA5.mapperLastTime); - - gbMemoryMap[0xa][0] = 1; - } - else if (gbDataTAMA5.mapperRamByteSelect == 0x28) // Timer stuff again - { - if ((data & 0xf) == 0xb) - gbDataTAMA5.mapperYears = ((gbDataTAMA5.mapperYears>>2)<<2) + (data & 3); - } - else if (gbDataTAMA5.mapperRamByteSelect == 0x44) - { - gbDataTAMA5.mapperMinutes = (data/16)*10 + data%16; - } - else if (gbDataTAMA5.mapperRamByteSelect == 0x54) - { - gbDataTAMA5.mapperHours = (data/16)*10 + data%16; - } - else - { - gbTAMA5ram[gbDataTAMA5.mapperRamByteSelect] = data; - } - } - } - } - break; - case 1: // 'Commands' Register - { - gbMemoryMap[0xa][1] = gbDataTAMA5.mapperCommandNumber = value; - - // This should be only a 'is the flashrom ready ?' command. - // However as I couldn't find any 'copy' command - // (that seems to be needed for the saving system to work) - // I put it there... - if (value == 0x0a) - { - for (int i = 0; i<0x10; i++) - for (int j = 0; j<0x10; j++) - if (!(j&2)) - gbTAMA5ram[((i*0x10)+j) | 2] = gbTAMA5ram[(i*0x10)+j]; - // Enable this to see the content of the flashrom in 0xe000 - /*for (int k = 0; k<0x100; k++) - gbMemoryMap[0xe][k] = gbTAMA5ram[k];*/ - - gbMemoryMap[0xa][0] = gbDataTAMA5.mapperRAMEnable = 1; - } - else - { - if ((value & 0x0e) == 0x0c) - { - gbDataTAMA5.mapperRamByteSelect = gbDataTAMA5.mapperCommands[6] | - (gbDataTAMA5.mapperCommands[7]<<4); - - u8 byte = gbTAMA5ram[gbDataTAMA5.mapperRamByteSelect]; - - gbMemoryMap[0xa][0] = (value & 1) ? byte >> 4 : byte & 0x0f; - - gbDataTAMA5.mapperCommands[0x0f] = 0; - } - } - break; - } - } - } - else - { - if(gbDataTAMA5.mapperRAMEnable) { - if(gbDataTAMA5.mapperRAMBank != -1) { - if(gbRamSize) { - gbMemoryMap[address>>12][address & 0x0fff] = value; - systemSaveUpdateCounter = SYSTEM_SAVE_UPDATED; - } - } - } - } -} - - -// TAMA5 read RAM -u8 mapperTAMA5ReadRAM(u16 address) -{ - return gbMemoryMap[address>>12][address & 0xfff]; -} - - -void memoryUpdateMapTAMA5() -{ - int tmpAddress = (gbDataTAMA5.mapperROMBank << 14); - - tmpAddress &= gbRomSizeMask; - gbMemoryMap[0x04] = &gbRom[tmpAddress]; - gbMemoryMap[0x05] = &gbRom[tmpAddress + 0x1000]; - gbMemoryMap[0x06] = &gbRom[tmpAddress + 0x2000]; - gbMemoryMap[0x07] = &gbRom[tmpAddress + 0x3000]; - - if(gbRamSize) { - tmpAddress = 0 << 13; - tmpAddress &= gbRamSizeMask; - gbMemoryMap[0x0a] = &gbRam[tmpAddress]; - gbMemoryMap[0x0b] = &gbRam[tmpAddress + 0x1000]; - } -} - -// MMM01 Used in Momotarou collection (however the rom is corrupted) -mapperMMM01 gbDataMMM01 ={ - 0, // RAM enable - 1, // ROM bank - 0, // RAM bank - 0, // memory model - 0, // ROM high address - 0, // RAM address - 0 // Rom Bank 0 remapping -}; - -// MMM01 ROM write registers -void mapperMMM01ROM(u16 address, u8 value) -{ - int tmpAddress = 0; - - switch(address & 0x6000) { - case 0x0000: // RAM enable register - gbDataMMM01.mapperRAMEnable = ( ( value & 0x0a) == 0x0a ? 1 : 0); - break; - case 0x2000: // ROM bank select - // value = value & 0x1f; - if(value == 0) - value = 1; - if(value == gbDataMMM01.mapperROMBank) - break; - - tmpAddress = value << 14; - - // check current model - if(gbDataMMM01.mapperMemoryModel == 0) { - // model is 16/8, so we have a high address in use - tmpAddress |= (gbDataMMM01.mapperROMHighAddress) << 19; - } - else - tmpAddress |= gbDataMMM01.mapperRomBank0Remapping << 18; - - tmpAddress &= gbRomSizeMask; - gbDataMMM01.mapperROMBank = value; - gbMemoryMap[0x04] = &gbRom[tmpAddress]; - gbMemoryMap[0x05] = &gbRom[tmpAddress + 0x1000]; - gbMemoryMap[0x06] = &gbRom[tmpAddress + 0x2000]; - gbMemoryMap[0x07] = &gbRom[tmpAddress + 0x3000]; - break; - case 0x4000: // RAM bank select - if(gbDataMMM01.mapperMemoryModel == 1) { - // 4/32 model, RAM bank switching provided - value = value & 0x03; - if(value == gbDataMBC1.mapperRAMBank) - break; - tmpAddress = value << 13; - tmpAddress &= gbRamSizeMask; - gbMemoryMap[0x0a] = &gbRam[tmpAddress]; - gbMemoryMap[0x0b] = &gbRam[tmpAddress + 0x1000]; - gbDataMMM01.mapperRAMBank = value; - gbDataMMM01.mapperRAMAddress = tmpAddress; - } else { - // 16/8, set the high address - gbDataMMM01.mapperROMHighAddress = value & 0x03; - tmpAddress = gbDataMMM01.mapperROMBank << 14; - tmpAddress |= (gbDataMMM01.mapperROMHighAddress) << 19; - tmpAddress &= gbRomSizeMask; - gbMemoryMap[0x04] = &gbRom[tmpAddress]; - gbMemoryMap[0x05] = &gbRom[tmpAddress + 0x1000]; - gbMemoryMap[0x06] = &gbRom[tmpAddress + 0x2000]; - gbMemoryMap[0x07] = &gbRom[tmpAddress + 0x3000]; - - gbDataMMM01.mapperRomBank0Remapping = ((value<<1) | (value & 0x40 ? 1 : 0)) & 0xff; - tmpAddress = gbDataMMM01.mapperRomBank0Remapping << 18; - tmpAddress &= gbRomSizeMask; - gbMemoryMap[0x00] = &gbRom[tmpAddress]; - gbMemoryMap[0x01] = &gbRom[tmpAddress + 0x1000]; - gbMemoryMap[0x02] = &gbRom[tmpAddress + 0x2000]; - gbMemoryMap[0x03] = &gbRom[tmpAddress + 0x3000]; - } - break; - case 0x6000: // memory model select - gbDataMMM01.mapperMemoryModel = value & 1; - break; - } -} - -// MMM01 RAM write -void mapperMMM01RAM(u16 address, u8 value) -{ - if(gbDataMMM01.mapperRAMEnable) { - if(gbRamSize) { - gbMemoryMap[address >> 12][address & 0x0fff] = value; - systemSaveUpdateCounter = SYSTEM_SAVE_UPDATED; - } - } -} - -void memoryUpdateMapMMM01() -{ - int tmpAddress = gbDataMMM01.mapperROMBank << 14; - - // check current model - if(gbDataMMM01.mapperMemoryModel == 1) { - // model is 16/8, so we have a high address in use - tmpAddress |= (gbDataMMM01.mapperROMHighAddress) << 19; - } - - tmpAddress &= gbRomSizeMask; - gbMemoryMap[0x04] = &gbRom[tmpAddress]; - gbMemoryMap[0x05] = &gbRom[tmpAddress + 0x1000]; - gbMemoryMap[0x06] = &gbRom[tmpAddress + 0x2000]; - gbMemoryMap[0x07] = &gbRom[tmpAddress + 0x3000]; - - tmpAddress = gbDataMMM01.mapperRomBank0Remapping << 18; - tmpAddress &= gbRomSizeMask; - gbMemoryMap[0x00] = &gbRom[tmpAddress]; - gbMemoryMap[0x01] = &gbRom[tmpAddress + 0x1000]; - gbMemoryMap[0x02] = &gbRom[tmpAddress + 0x2000]; - gbMemoryMap[0x03] = &gbRom[tmpAddress + 0x3000]; - - if(gbRamSize) { - gbMemoryMap[0x0a] = &gbRam[gbDataMMM01.mapperRAMAddress]; - gbMemoryMap[0x0b] = &gbRam[gbDataMMM01.mapperRAMAddress + 0x1000]; - } -} - -// GameGenie ROM write registers -void mapperGGROM(u16 address, u8 value) -{ - switch(address & 0x6000) { - case 0x0000: // RAM enable register - break; - case 0x2000: // GameGenie has only a half bank - break; - case 0x4000: // GameGenie has no RAM - if ((address >=0x4001) && (address <= 0x4020)) // GG Hardware Registers - gbMemoryMap[address >> 12][address & 0x0fff] = value; - break; - case 0x6000: // GameGenie has only a half bank - break; - } -} - - -// GS3 Used to emulate the GS V3.0 rom bank switching -mapperGS3 gbDataGS3 = { 1 }; // ROM bank - -void mapperGS3ROM(u16 address, u8 value) -{ - int tmpAddress = 0; - - switch(address & 0x6000) { - case 0x0000: // GS has no ram - break; - case 0x2000: // GS has no 'classic' ROM bank select - break; - case 0x4000: // GS has no ram - break; - case 0x6000: // 0x6000 area is RW, and used for GS hardware registers - - if (address == 0x7FE1) // This is the (half) ROM bank select register - { - if(value == gbDataGS3.mapperROMBank) - break; - tmpAddress = value << 13; - - tmpAddress &= gbRomSizeMask; - gbDataGS3.mapperROMBank = value; - gbMemoryMap[0x04] = &gbRom[tmpAddress]; - gbMemoryMap[0x05] = &gbRom[tmpAddress + 0x1000]; - } - else - gbMemoryMap[address>>12][address & 0x0fff] = value; - break; - } -} - -void memoryUpdateMapGS3() -{ - int tmpAddress = gbDataGS3.mapperROMBank << 13; - - tmpAddress &= gbRomSizeMask; - // GS can only change a half ROM bank - gbMemoryMap[0x04] = &gbRom[tmpAddress]; - gbMemoryMap[0x05] = &gbRom[tmpAddress + 0x1000]; -} +#include "../System.h" +#include "../common/Port.h" +#include "gbGlobals.h" +#include "gbMemory.h" +#include "gb.h" +u8 gbDaysinMonth [12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; +const u8 gbDisabledRam [8] = {0x80, 0xff, 0xf0, 0x00, 0x30, 0xbf, 0xbf, 0xbf}; +extern int gbGBCColorType; +extern gbRegister PC; + +mapperMBC1 gbDataMBC1 = { + 0, // RAM enable + 1, // ROM bank + 0, // RAM bank + 0, // memory model + 0, // ROM high address + 0, // RAM address + 0 // Rom Bank 0 remapping +}; + +// MBC1 ROM write registers +void mapperMBC1ROM(u16 address, u8 value) +{ + int tmpAddress = 0; + + switch(address & 0x6000) { + case 0x0000: // RAM enable register + gbDataMBC1.mapperRAMEnable = ( ( value & 0x0a) == 0x0a ? 1 : 0); + break; + case 0x2000: // ROM bank select + // value = value & 0x1f; + if ((value == 1) && (address == 0x2100)) + gbDataMBC1.mapperRomBank0Remapping = 1; + + if((value & 0x1f) == 0) + value += 1; + if(value == gbDataMBC1.mapperROMBank) + break; + + tmpAddress = value << 14; + + // check current model + if (gbDataMBC1.mapperRomBank0Remapping == 3) { + tmpAddress = (value & 0xf) << 14; + tmpAddress |= (gbDataMBC1.mapperROMHighAddress & 3) << 18; + } + else + if(gbDataMBC1.mapperMemoryModel == 0) { + // model is 16/8, so we have a high address in use + tmpAddress |= (gbDataMBC1.mapperROMHighAddress & 3) << 19; + } + + tmpAddress &= gbRomSizeMask; + gbDataMBC1.mapperROMBank = value; + gbMemoryMap[0x04] = &gbRom[tmpAddress]; + gbMemoryMap[0x05] = &gbRom[tmpAddress + 0x1000]; + gbMemoryMap[0x06] = &gbRom[tmpAddress + 0x2000]; + gbMemoryMap[0x07] = &gbRom[tmpAddress + 0x3000]; + break; + case 0x4000: // RAM bank select + if(gbDataMBC1.mapperMemoryModel == 1) { + if (!gbRamSize) + { + if (gbDataMBC1.mapperRomBank0Remapping == 3) + { + gbDataMBC1.mapperROMHighAddress = value & 0x03; + tmpAddress = (gbDataMBC1.mapperROMHighAddress) << 18; + tmpAddress &= gbRomSizeMask; + gbMemoryMap[0x00] = &gbRom[tmpAddress]; + gbMemoryMap[0x01] = &gbRom[tmpAddress + 0x1000]; + gbMemoryMap[0x02] = &gbRom[tmpAddress + 0x2000]; + gbMemoryMap[0x03] = &gbRom[tmpAddress + 0x3000]; + gbMemoryMap[0x04] = &gbRom[tmpAddress + 0x4000]; + gbMemoryMap[0x05] = &gbRom[tmpAddress + 0x5000]; + gbMemoryMap[0x06] = &gbRom[tmpAddress + 0x6000]; + gbMemoryMap[0x07] = &gbRom[tmpAddress + 0x7000]; + } + else gbDataMBC1.mapperRomBank0Remapping = 0; + } + // 4/32 model, RAM bank switching provided + value = value & 0x03; + if(value == gbDataMBC1.mapperRAMBank) + break; + tmpAddress = value << 13; + tmpAddress &= gbRamSizeMask; + if(gbRamSize) { + gbMemoryMap[0x0a] = &gbRam[tmpAddress]; + gbMemoryMap[0x0b] = &gbRam[tmpAddress + 0x1000]; + } + gbDataMBC1.mapperRAMBank = value; + gbDataMBC1.mapperRAMAddress = tmpAddress; + + if (gbDataMBC1.mapperRomBank0Remapping != 3) + gbDataMBC1.mapperROMHighAddress = 0; + } else { + // 16/8, set the high address + gbDataMBC1.mapperROMHighAddress = value & 0x03; + tmpAddress = gbDataMBC1.mapperROMBank << 14; + tmpAddress |= (gbDataMBC1.mapperROMHighAddress) << 19; + tmpAddress &= gbRomSizeMask; + gbMemoryMap[0x04] = &gbRom[tmpAddress]; + gbMemoryMap[0x05] = &gbRom[tmpAddress + 0x1000]; + gbMemoryMap[0x06] = &gbRom[tmpAddress + 0x2000]; + gbMemoryMap[0x07] = &gbRom[tmpAddress + 0x3000]; + if(gbRamSize) { + gbMemoryMap[0x0a] = &gbRam[0]; + gbMemoryMap[0x0b] = &gbRam[0x1000]; + } + + gbDataMBC1.mapperRAMBank = 0; + } + break; + case 0x6000: // memory model select + gbDataMBC1.mapperMemoryModel = value & 1; + + if(gbDataMBC1.mapperMemoryModel == 1) { + // 4/32 model, RAM bank switching provided + + value = gbDataMBC1.mapperRAMBank & 0x03; + tmpAddress = value << 13; + tmpAddress &= gbRamSizeMask; + if(gbRamSize) { + gbMemoryMap[0x0a] = &gbRam[gbDataMBC1.mapperRAMAddress]; + gbMemoryMap[0x0b] = &gbRam[gbDataMBC1.mapperRAMAddress + 0x1000]; + gbDataMBC1.mapperRomBank0Remapping = 0; + } + else gbDataMBC1.mapperRomBank0Remapping |=2; + + gbDataMBC1.mapperRAMBank = value; + gbDataMBC1.mapperRAMAddress = tmpAddress; + + tmpAddress = gbDataMBC1.mapperROMBank << 14; + + + tmpAddress &= gbRomSizeMask; + gbMemoryMap[0x04] = &gbRom[tmpAddress]; + gbMemoryMap[0x05] = &gbRom[tmpAddress + 0x1000]; + gbMemoryMap[0x06] = &gbRom[tmpAddress + 0x2000]; + gbMemoryMap[0x07] = &gbRom[tmpAddress + 0x3000]; + + } else { + // 16/8, set the high address + + tmpAddress = gbDataMBC1.mapperROMBank << 14; + tmpAddress |= (gbDataMBC1.mapperROMHighAddress) << 19; + tmpAddress &= gbRomSizeMask; + gbMemoryMap[0x04] = &gbRom[tmpAddress]; + gbMemoryMap[0x05] = &gbRom[tmpAddress + 0x1000]; + gbMemoryMap[0x06] = &gbRom[tmpAddress + 0x2000]; + gbMemoryMap[0x07] = &gbRom[tmpAddress + 0x3000]; + if(gbRamSize) { + gbMemoryMap[0x0a] = &gbRam[0]; + gbMemoryMap[0x0b] = &gbRam[0x1000]; + } + } + break; + } +} + +// MBC1 RAM write +void mapperMBC1RAM(u16 address, u8 value) +{ + if(gbDataMBC1.mapperRAMEnable) { + if(gbRamSize) { + gbMemoryMap[address >> 12][address & 0x0fff] = value; + systemSaveUpdateCounter = SYSTEM_SAVE_UPDATED; + } + } +} + +// MBC1 read RAM +u8 mapperMBC1ReadRAM(u16 address) +{ + + if(gbDataMBC1.mapperRAMEnable) + return gbMemoryMap[address>>12][address & 0x0fff]; + + if (!genericflashcardEnable) + return 0xff; + else + if ((address & 0x1000) >= 0x1000) + { + // The value returned when reading RAM while it's disabled + // is constant, exept for the GBASP hardware. + // (actually, is the address that read is out of the ROM, the returned value if 0xff...) + if (PC.W>=0xff80) + return 0xff; + else + if ((gbHardware & 0x08) && (gbGBCColorType == 2)) + { + if (address & 1) + return 0xfb; + else + return 0x7a; + } + else + return 0x0a; + } + else + return gbDisabledRam[address & 7]; +} + +void memoryUpdateMapMBC1() +{ + int tmpAddress = gbDataMBC1.mapperROMBank << 14; + + // check current model + if (gbDataMBC1.mapperRomBank0Remapping == 3) { + tmpAddress = (gbDataMBC1.mapperROMHighAddress & 3) << 18; + tmpAddress &= gbRomSizeMask; + gbMemoryMap[0x00] = &gbRom[tmpAddress]; + gbMemoryMap[0x01] = &gbRom[tmpAddress + 0x1000]; + gbMemoryMap[0x02] = &gbRom[tmpAddress + 0x2000]; + gbMemoryMap[0x03] = &gbRom[tmpAddress + 0x3000]; + + tmpAddress |= (gbDataMBC1.mapperROMBank & 0xf) << 14; + gbMemoryMap[0x04] = &gbRom[tmpAddress]; + gbMemoryMap[0x05] = &gbRom[tmpAddress + 0x1000]; + gbMemoryMap[0x06] = &gbRom[tmpAddress + 0x2000]; + gbMemoryMap[0x07] = &gbRom[tmpAddress + 0x3000]; + } + else + { + if(gbDataMBC1.mapperMemoryModel == 0) { + // model is 16/8, so we have a high address in use + tmpAddress |= (gbDataMBC1.mapperROMHighAddress & 3) << 19; + } + + tmpAddress &= gbRomSizeMask; + gbMemoryMap[0x04] = &gbRom[tmpAddress]; + gbMemoryMap[0x05] = &gbRom[tmpAddress + 0x1000]; + gbMemoryMap[0x06] = &gbRom[tmpAddress + 0x2000]; + gbMemoryMap[0x07] = &gbRom[tmpAddress + 0x3000]; + } + + if(gbRamSize) { + if(gbDataMBC1.mapperMemoryModel == 1) { + gbMemoryMap[0x0a] = &gbRam[gbDataMBC1.mapperRAMAddress]; + gbMemoryMap[0x0b] = &gbRam[gbDataMBC1.mapperRAMAddress + 0x1000]; + } else { + gbMemoryMap[0x0a] = &gbRam[0]; + gbMemoryMap[0x0b] = &gbRam[0x1000]; + } + } +} + +mapperMBC2 gbDataMBC2 = { + 0, // RAM enable + 1 // ROM bank +}; + +// MBC2 ROM write registers +void mapperMBC2ROM(u16 address, u8 value) +{ + switch(address & 0x6000) { + case 0x0000: // RAM enable + if(!(address & 0x0100)) { + gbDataMBC2.mapperRAMEnable = (value & 0x0f) == 0x0a; + } + break; + case 0x2000: // ROM bank select + if(address & 0x0100) { + value &= 0x0f; + + if(value == 0) + value = 1; + if(gbDataMBC2.mapperROMBank != value) { + gbDataMBC2.mapperROMBank = value; + + int tmpAddress = value << 14; + + tmpAddress &= gbRomSizeMask; + + gbMemoryMap[0x04] = &gbRom[tmpAddress]; + gbMemoryMap[0x05] = &gbRom[tmpAddress + 0x1000]; + gbMemoryMap[0x06] = &gbRom[tmpAddress + 0x2000]; + gbMemoryMap[0x07] = &gbRom[tmpAddress + 0x3000]; + } + } + break; + } +} + +// MBC2 RAM write +void mapperMBC2RAM(u16 address, u8 value) +{ + if(gbDataMBC2.mapperRAMEnable) { + if(gbRamSize && address < 0xa200) { + gbMemoryMap[address >> 12][address & 0x0fff] = value; + systemSaveUpdateCounter = SYSTEM_SAVE_UPDATED; + } + } +} + +void memoryUpdateMapMBC2() +{ + int tmpAddress = gbDataMBC2.mapperROMBank << 14; + + tmpAddress &= gbRomSizeMask; + + gbMemoryMap[0x04] = &gbRom[tmpAddress]; + gbMemoryMap[0x05] = &gbRom[tmpAddress + 0x1000]; + gbMemoryMap[0x06] = &gbRom[tmpAddress + 0x2000]; + gbMemoryMap[0x07] = &gbRom[tmpAddress + 0x3000]; +} + +mapperMBC3 gbDataMBC3 = { + 0, // RAM enable + 1, // ROM bank + 0, // RAM bank + 0, // RAM address + 0, // timer clock latch + 0, // timer clock register + 0, // timer seconds + 0, // timer minutes + 0, // timer hours + 0, // timer days + 0, // timer control + 0, // timer latched seconds + 0, // timer latched minutes + 0, // timer latched hours + 0, // timer latched days + 0, // timer latched control + (time_t)-1 // last time +}; + +void memoryUpdateMBC3Clock() +{ + time_t now = time(NULL); + time_t diff = now - gbDataMBC3.mapperLastTime; + if(diff > 0) { + // update the clock according to the last update time + gbDataMBC3.mapperSeconds += (int)(diff % 60); + if(gbDataMBC3.mapperSeconds > 59) { + gbDataMBC3.mapperSeconds -= 60; + gbDataMBC3.mapperMinutes++; + } + + diff /= 60; + + gbDataMBC3.mapperMinutes += (int)(diff % 60); + if(gbDataMBC3.mapperMinutes > 59) { + gbDataMBC3.mapperMinutes -= 60; + gbDataMBC3.mapperHours++; + } + + diff /= 60; + + gbDataMBC3.mapperHours += (int)(diff % 24); + if(gbDataMBC3.mapperHours > 23) { + gbDataMBC3.mapperHours -= 24; + gbDataMBC3.mapperDays++; + } + diff /= 24; + + gbDataMBC3.mapperDays += (int)(diff & 0xffffffff); + if(gbDataMBC3.mapperDays > 255) { + if(gbDataMBC3.mapperDays > 511) { + gbDataMBC3.mapperDays %= 512; + gbDataMBC3.mapperControl |= 0x80; + } + gbDataMBC3.mapperControl = (gbDataMBC3.mapperControl & 0xfe) | + (gbDataMBC3.mapperDays>255 ? 1 : 0); + } + } + gbDataMBC3.mapperLastTime = now; +} + +// MBC3 ROM write registers +void mapperMBC3ROM(u16 address, u8 value) +{ + int tmpAddress = 0; + + switch(address & 0x6000) { + case 0x0000: // RAM enable register + gbDataMBC3.mapperRAMEnable = ( ( value & 0x0a) == 0x0a ? 1 : 0); + break; + case 0x2000: // ROM bank select + value = value & 0x7f; + if(value == 0) + value = 1; + if(value == gbDataMBC3.mapperROMBank) + break; + + tmpAddress = value << 14; + + tmpAddress &= gbRomSizeMask; + gbDataMBC3.mapperROMBank = value; + gbMemoryMap[0x04] = &gbRom[tmpAddress]; + gbMemoryMap[0x05] = &gbRom[tmpAddress + 0x1000]; + gbMemoryMap[0x06] = &gbRom[tmpAddress + 0x2000]; + gbMemoryMap[0x07] = &gbRom[tmpAddress + 0x3000]; + + break; + case 0x4000: // RAM bank select + if(value < 8) { + if(value == gbDataMBC3.mapperRAMBank) + break; + tmpAddress = value << 13; + tmpAddress &= gbRamSizeMask; + gbMemoryMap[0x0a] = &gbRam[tmpAddress]; + gbMemoryMap[0x0b] = &gbRam[tmpAddress + 0x1000]; + gbDataMBC3.mapperRAMBank = value; + gbDataMBC3.mapperRAMAddress = tmpAddress; + } else { + if(gbDataMBC3.mapperRAMEnable) { + gbDataMBC3.mapperRAMBank = -1; + + gbDataMBC3.mapperClockRegister = value; + } + } + break; + case 0x6000: // clock latch + if(gbDataMBC3.mapperClockLatch == 0 && value == 1) { + memoryUpdateMBC3Clock(); + gbDataMBC3.mapperLSeconds = gbDataMBC3.mapperSeconds; + gbDataMBC3.mapperLMinutes = gbDataMBC3.mapperMinutes; + gbDataMBC3.mapperLHours = gbDataMBC3.mapperHours; + gbDataMBC3.mapperLDays = gbDataMBC3.mapperDays; + gbDataMBC3.mapperLControl = gbDataMBC3.mapperControl; + } + if(value == 0x00 || value == 0x01) + gbDataMBC3.mapperClockLatch = value; + break; + } +} + +// MBC3 RAM write +void mapperMBC3RAM(u16 address, u8 value) +{ + if(gbDataMBC3.mapperRAMEnable) { + if(gbDataMBC3.mapperRAMBank != -1) { + if(gbRamSize) { + gbMemoryMap[address>>12][address & 0x0fff] = value; + systemSaveUpdateCounter = SYSTEM_SAVE_UPDATED; + } + } else { + time(&gbDataMBC3.mapperLastTime); + switch(gbDataMBC3.mapperClockRegister) { + case 0x08: + gbDataMBC3.mapperSeconds = value; + break; + case 0x09: + gbDataMBC3.mapperMinutes = value; + break; + case 0x0a: + gbDataMBC3.mapperHours = value; + break; + case 0x0b: + gbDataMBC3.mapperDays = value; + break; + case 0x0c: + if(gbDataMBC3.mapperControl & 0x80) + gbDataMBC3.mapperControl = 0x80 | value; + else + gbDataMBC3.mapperControl = value; + break; + } + } + } +} + +// MBC3 read RAM +u8 mapperMBC3ReadRAM(u16 address) +{ + if(gbDataMBC3.mapperRAMEnable) { + if(gbDataMBC3.mapperRAMBank != -1) { + return gbMemoryMap[address>>12][address & 0x0fff]; + } + + switch(gbDataMBC3.mapperClockRegister) { + case 0x08: + return gbDataMBC3.mapperLSeconds; + break; + case 0x09: + return gbDataMBC3.mapperLMinutes; + break; + case 0x0a: + return gbDataMBC3.mapperLHours; + break; + case 0x0b: + return gbDataMBC3.mapperLDays; + break; + case 0x0c: + return gbDataMBC3.mapperLControl; + } + } + + if (!genericflashcardEnable) + return 0xff; + else + if ((address & 0x1000) >= 0x1000) + { + // The value returned when reading RAM while it's disabled + // is constant, exept for the GBASP hardware. + // (actually, is the address that read is out of the ROM, the returned value if 0xff...) + if (PC.W>=0xff80) + return 0xff; + else + if ((gbHardware & 0x08) && (gbGBCColorType == 2)) + { + if (address & 1) + return 0xfb; + else + return 0x7a; + } + else + return 0x0a; + } + else + return gbDisabledRam[address & 7]; +} + +void memoryUpdateMapMBC3() +{ + int tmpAddress = gbDataMBC3.mapperROMBank << 14; + + tmpAddress &= gbRomSizeMask; + + gbMemoryMap[0x04] = &gbRom[tmpAddress]; + gbMemoryMap[0x05] = &gbRom[tmpAddress + 0x1000]; + gbMemoryMap[0x06] = &gbRom[tmpAddress + 0x2000]; + gbMemoryMap[0x07] = &gbRom[tmpAddress + 0x3000]; + + if(gbDataMBC3.mapperRAMBank >= 0 && gbRamSize) { + tmpAddress = gbDataMBC3.mapperRAMBank << 13; + tmpAddress &= gbRamSizeMask; + gbMemoryMap[0x0a] = &gbRam[tmpAddress]; + gbMemoryMap[0x0b] = &gbRam[tmpAddress + 0x1000]; + } +} + +mapperMBC5 gbDataMBC5 = { + 0, // RAM enable + 1, // ROM bank + 0, // RAM bank + 0, // ROM high address + 0, // RAM address + 0 // is rumble cartridge? +}; + +// MBC5 ROM write registers +void mapperMBC5ROM(u16 address, u8 value) +{ + int tmpAddress = 0; + + switch(address & 0x6000) { + case 0x0000: // RAM enable register + gbDataMBC5.mapperRAMEnable = ( ( value & 0x0a) == 0x0a ? 1 : 0); + break; + case 0x2000: // ROM bank select + + if(address < 0x3000) { + value = value & 0xff; + if(value == gbDataMBC5.mapperROMBank) + break; + + tmpAddress = (value << 14) | (gbDataMBC5.mapperROMHighAddress << 22) ; + + tmpAddress &= gbRomSizeMask; + gbDataMBC5.mapperROMBank = value; + gbMemoryMap[0x04] = &gbRom[tmpAddress]; + gbMemoryMap[0x05] = &gbRom[tmpAddress + 0x1000]; + gbMemoryMap[0x06] = &gbRom[tmpAddress + 0x2000]; + gbMemoryMap[0x07] = &gbRom[tmpAddress + 0x3000]; + + } else { + value = value & 1; + if(value == gbDataMBC5.mapperROMHighAddress) + break; + + tmpAddress = (gbDataMBC5.mapperROMBank << 14) | (value << 22); + + tmpAddress &= gbRomSizeMask; + gbDataMBC5.mapperROMHighAddress = value; + gbMemoryMap[0x04] = &gbRom[tmpAddress]; + gbMemoryMap[0x05] = &gbRom[tmpAddress + 0x1000]; + gbMemoryMap[0x06] = &gbRom[tmpAddress + 0x2000]; + gbMemoryMap[0x07] = &gbRom[tmpAddress + 0x3000]; + } + break; + case 0x4000: // RAM bank select + if(gbDataMBC5.isRumbleCartridge) + value &= 0x07; + else + value &= 0x0f; + if(value == gbDataMBC5.mapperRAMBank) + break; + tmpAddress = value << 13; + tmpAddress &= gbRamSizeMask; + if(gbRamSize) { + gbMemoryMap[0x0a] = &gbRam[tmpAddress]; + gbMemoryMap[0x0b] = &gbRam[tmpAddress + 0x1000]; + + gbDataMBC5.mapperRAMBank = value; + gbDataMBC5.mapperRAMAddress = tmpAddress; + } + break; + } +} + +// MBC5 RAM write +void mapperMBC5RAM(u16 address, u8 value) +{ + if(gbDataMBC5.mapperRAMEnable) { + if(gbRamSize) { + gbMemoryMap[address >> 12][address & 0x0fff] = value; + systemSaveUpdateCounter = SYSTEM_SAVE_UPDATED; + } + } +} + +// MBC5 read RAM +u8 mapperMBC5ReadRAM(u16 address) +{ + + if(gbDataMBC5.mapperRAMEnable) + return gbMemoryMap[address>>12][address & 0x0fff]; + + if (!genericflashcardEnable) + return 0xff; + else + if ((address & 0x1000) >= 0x1000) + { + // The value returned when reading RAM while it's disabled + // is constant, exept for the GBASP hardware. + // (actually, is the address that read is out of the ROM, the returned value if 0xff...) + if (PC.W>=0xff80) + return 0xff; + else + if ((gbHardware & 0x08) && (gbGBCColorType == 2)) + { + if (address & 1) + return 0xfb; + else + return 0x7a; + } + else + return 0x0a; + } + else + return gbDisabledRam[address & 7]; +} + +void memoryUpdateMapMBC5() +{ + int tmpAddress = (gbDataMBC5.mapperROMBank << 14) | + (gbDataMBC5.mapperROMHighAddress << 22) ; + + tmpAddress &= gbRomSizeMask; + gbMemoryMap[0x04] = &gbRom[tmpAddress]; + gbMemoryMap[0x05] = &gbRom[tmpAddress + 0x1000]; + gbMemoryMap[0x06] = &gbRom[tmpAddress + 0x2000]; + gbMemoryMap[0x07] = &gbRom[tmpAddress + 0x3000]; + + if(gbRamSize) { + tmpAddress = gbDataMBC5.mapperRAMBank << 13; + tmpAddress &= gbRamSizeMask; + gbMemoryMap[0x0a] = &gbRam[tmpAddress]; + gbMemoryMap[0x0b] = &gbRam[tmpAddress + 0x1000]; + } +} + +mapperMBC7 gbDataMBC7 = { + 0, // RAM enable + 1, // ROM bank + 0, // RAM bank + 0, // RAM address + 0, // chip select + 0, // ?? + 0, // mapper state + 0, // buffer for receiving serial data + 0, // idle state + 0, // count of bits received + 0, // command received + 0, // address received + 0, // write enable + 0, // value to return on ram +}; + +// MBC7 ROM write registers +void mapperMBC7ROM(u16 address, u8 value) +{ + int tmpAddress = 0; + + switch(address & 0x6000) { + case 0x0000: + break; + case 0x2000: // ROM bank select + value = value & 0x7f; + if(value == 0) + value = 1; + + if(value == gbDataMBC7.mapperROMBank) + break; + + tmpAddress = (value << 14); + + tmpAddress &= gbRomSizeMask; + gbDataMBC7.mapperROMBank = value; + gbMemoryMap[0x04] = &gbRom[tmpAddress]; + gbMemoryMap[0x05] = &gbRom[tmpAddress + 0x1000]; + gbMemoryMap[0x06] = &gbRom[tmpAddress + 0x2000]; + gbMemoryMap[0x07] = &gbRom[tmpAddress + 0x3000]; + break; + case 0x4000: // RAM bank select/enable + if(value < 8) { + tmpAddress = (value&3) << 13; + tmpAddress &= gbRamSizeMask; + gbMemoryMap[0x0a] = &gbMemory[0xa000]; + gbMemoryMap[0x0b] = &gbMemory[0xb000]; + + gbDataMBC7.mapperRAMBank = value; + gbDataMBC7.mapperRAMAddress = tmpAddress; + gbDataMBC7.mapperRAMEnable = 0; + } else { + gbDataMBC7.mapperRAMEnable = 0; + } + break; + } +} + +// MBC7 read RAM +u8 mapperMBC7ReadRAM(u16 address) +{ + switch(address & 0xa0f0) { + case 0xa000: + case 0xa010: + case 0xa060: + case 0xa070: + return 0; + case 0xa020: + // sensor X low byte + return systemGetSensorX() & 255; + case 0xa030: + // sensor X high byte + return systemGetSensorX() >> 8; + case 0xa040: + // sensor Y low byte + return systemGetSensorY() & 255; + case 0xa050: + // sensor Y high byte + return systemGetSensorY() >> 8; + case 0xa080: + return gbDataMBC7.value; + } + + if (!genericflashcardEnable) + return 0xff; + else + if ((address & 0x1000) >= 0x1000) + { + // The value returned when reading RAM while it's disabled + // is constant, exept for the GBASP hardware. + // (actually, is the address that read is out of the ROM, the returned value if 0xff...) + if (PC.W>=0xff80) + return 0xff; + else + if ((gbHardware & 0x08) && (gbGBCColorType == 2)) + { + if (address & 1) + return 0xfb; + else + return 0x7a; + } + else + return 0x0a; + } + else + return gbDisabledRam[address & 7]; +} + +// MBC7 RAM write +void mapperMBC7RAM(u16 address, u8 value) +{ + if(address == 0xa080) { + // special processing needed + int oldCs = gbDataMBC7.cs,oldSk=gbDataMBC7.sk; + + gbDataMBC7.cs=value>>7; + gbDataMBC7.sk=(value>>6)&1; + + if(!oldCs && gbDataMBC7.cs) { + if(gbDataMBC7.state==5) { + if(gbDataMBC7.writeEnable) { + gbMemory[0xa000+gbDataMBC7.address*2]=gbDataMBC7.buffer>>8; + gbMemory[0xa000+gbDataMBC7.address*2+1]=gbDataMBC7.buffer&0xff; + systemSaveUpdateCounter = SYSTEM_SAVE_UPDATED; + } + gbDataMBC7.state=0; + gbDataMBC7.value=1; + } else { + gbDataMBC7.idle=true; + gbDataMBC7.state=0; + } + } + + if(!oldSk && gbDataMBC7.sk) { + if(gbDataMBC7.idle) { + if(value & 0x02) { + gbDataMBC7.idle=false; + gbDataMBC7.count=0; + gbDataMBC7.state=1; + } + } else { + switch(gbDataMBC7.state) { + case 1: + // receiving command + gbDataMBC7.buffer <<= 1; + gbDataMBC7.buffer |= (value & 0x02)?1:0; + gbDataMBC7.count++; + if(gbDataMBC7.count==2) { + // finished receiving command + gbDataMBC7.state=2; + gbDataMBC7.count=0; + gbDataMBC7.code=gbDataMBC7.buffer & 3; + } + break; + case 2: + // receive address + gbDataMBC7.buffer <<= 1; + gbDataMBC7.buffer |= (value&0x02)?1:0; + gbDataMBC7.count++; + if(gbDataMBC7.count==8) { + // finish receiving + gbDataMBC7.state=3; + gbDataMBC7.count=0; + gbDataMBC7.address=gbDataMBC7.buffer&0xff; + if(gbDataMBC7.code==0) { + if((gbDataMBC7.address>>6)==0) { + gbDataMBC7.writeEnable=0; + gbDataMBC7.state=0; + } else if((gbDataMBC7.address>>6) == 3) { + gbDataMBC7.writeEnable=1; + gbDataMBC7.state=0; + } + } + } + break; + case 3: + gbDataMBC7.buffer <<= 1; + gbDataMBC7.buffer |= (value&0x02)?1:0; + gbDataMBC7.count++; + + switch(gbDataMBC7.code) { + case 0: + if(gbDataMBC7.count==16) { + if((gbDataMBC7.address>>6)==0) { + gbDataMBC7.writeEnable = 0; + gbDataMBC7.state=0; + } else if((gbDataMBC7.address>>6)==1) { + if (gbDataMBC7.writeEnable) { + for(int i=0;i<256;i++) { + gbMemory[0xa000+i*2] = gbDataMBC7.buffer >> 8; + gbMemory[0xa000+i*2+1] = gbDataMBC7.buffer & 0xff; + systemSaveUpdateCounter = SYSTEM_SAVE_UPDATED; + } + } + gbDataMBC7.state=5; + } else if((gbDataMBC7.address>>6) == 2) { + if (gbDataMBC7.writeEnable) { + for(int i=0;i<256;i++) + WRITE16LE((u16 *)&gbMemory[0xa000+i*2], 0xffff); + systemSaveUpdateCounter = SYSTEM_SAVE_UPDATED; + } + gbDataMBC7.state=5; + } else if((gbDataMBC7.address>>6)==3) { + gbDataMBC7.writeEnable = 1; + gbDataMBC7.state=0; + } + gbDataMBC7.count=0; + } + break; + case 1: + if(gbDataMBC7.count==16) { + gbDataMBC7.count=0; + gbDataMBC7.state=5; + gbDataMBC7.value=0; + } + break; + case 2: + if(gbDataMBC7.count==1) { + gbDataMBC7.state=4; + gbDataMBC7.count=0; + gbDataMBC7.buffer = (gbMemory[0xa000+gbDataMBC7.address*2]<<8)| + (gbMemory[0xa000+gbDataMBC7.address*2+1]); + } + break; + case 3: + if(gbDataMBC7.count==16) { + gbDataMBC7.count=0; + gbDataMBC7.state=5; + gbDataMBC7.value=0; + gbDataMBC7.buffer=0xffff; + } + break; + } + break; + } + } + } + + if (oldSk && !gbDataMBC7.sk) { + if (gbDataMBC7.state==4) { + gbDataMBC7.value = (gbDataMBC7.buffer & 0x8000)?1:0; + gbDataMBC7.buffer <<= 1; + gbDataMBC7.count++; + if (gbDataMBC7.count==16) { + gbDataMBC7.count=0; + gbDataMBC7.state=0; + } + } + } + } +} + +void memoryUpdateMapMBC7() +{ + int tmpAddress = (gbDataMBC7.mapperROMBank << 14); + + tmpAddress &= gbRomSizeMask; + gbMemoryMap[0x04] = &gbRom[tmpAddress]; + gbMemoryMap[0x05] = &gbRom[tmpAddress + 0x1000]; + gbMemoryMap[0x06] = &gbRom[tmpAddress + 0x2000]; + gbMemoryMap[0x07] = &gbRom[tmpAddress + 0x3000]; +} + +mapperHuC1 gbDataHuC1 = { + 0, // RAM enable + 1, // ROM bank + 0, // RAM bank + 0, // memory model + 0, // ROM high address + 0 // RAM address +}; + +// HuC1 ROM write registers +void mapperHuC1ROM(u16 address, u8 value) +{ + int tmpAddress = 0; + + switch(address & 0x6000) { + case 0x0000: // RAM enable register + gbDataHuC1.mapperRAMEnable = ( ( value & 0x0a) == 0x0a ? 1 : 0); + break; + case 0x2000: // ROM bank select + value = value & 0x3f; + if(value == 0) + value = 1; + if(value == gbDataHuC1.mapperROMBank) + break; + + tmpAddress = value << 14; + + tmpAddress &= gbRomSizeMask; + gbDataHuC1.mapperROMBank = value; + gbMemoryMap[0x04] = &gbRom[tmpAddress]; + gbMemoryMap[0x05] = &gbRom[tmpAddress + 0x1000]; + gbMemoryMap[0x06] = &gbRom[tmpAddress + 0x2000]; + gbMemoryMap[0x07] = &gbRom[tmpAddress + 0x3000]; + break; + case 0x4000: // RAM bank select + if(gbDataHuC1.mapperMemoryModel == 1) { + // 4/32 model, RAM bank switching provided + value = value & 0x03; + if(value == gbDataHuC1.mapperRAMBank) + break; + tmpAddress = value << 13; + tmpAddress &= gbRamSizeMask; + gbMemoryMap[0x0a] = &gbRam[tmpAddress]; + gbMemoryMap[0x0b] = &gbRam[tmpAddress + 0x1000]; + gbDataHuC1.mapperRAMBank = value; + gbDataHuC1.mapperRAMAddress = tmpAddress; + } else { + // 16/8, set the high address + gbDataHuC1.mapperROMHighAddress = value & 0x03; + tmpAddress = gbDataHuC1.mapperROMBank << 14; + tmpAddress |= (gbDataHuC1.mapperROMHighAddress) << 19; + tmpAddress &= gbRomSizeMask; + gbMemoryMap[0x04] = &gbRom[tmpAddress]; + gbMemoryMap[0x05] = &gbRom[tmpAddress + 0x1000]; + gbMemoryMap[0x06] = &gbRom[tmpAddress + 0x2000]; + gbMemoryMap[0x07] = &gbRom[tmpAddress + 0x3000]; + } + break; + case 0x6000: // memory model select + gbDataHuC1.mapperMemoryModel = value & 1; + break; + } +} + +// HuC1 RAM write +void mapperHuC1RAM(u16 address, u8 value) +{ + if(gbDataHuC1.mapperRAMEnable) { + if(gbRamSize) { + gbMemoryMap[address >> 12][address & 0x0fff] = value; + systemSaveUpdateCounter = SYSTEM_SAVE_UPDATED; + } + } +} + +void memoryUpdateMapHuC1() +{ + int tmpAddress = gbDataHuC1.mapperROMBank << 14; + + tmpAddress &= gbRomSizeMask; + + gbMemoryMap[0x04] = &gbRom[tmpAddress]; + gbMemoryMap[0x05] = &gbRom[tmpAddress + 0x1000]; + gbMemoryMap[0x06] = &gbRom[tmpAddress + 0x2000]; + gbMemoryMap[0x07] = &gbRom[tmpAddress + 0x3000]; + + if(gbRamSize) { + tmpAddress = gbDataHuC1.mapperRAMBank << 13; + tmpAddress &= gbRamSizeMask; + gbMemoryMap[0x0a] = &gbRam[tmpAddress]; + gbMemoryMap[0x0b] = &gbRam[tmpAddress + 0x1000]; + } +} + +mapperHuC3 gbDataHuC3 = { + 0, // RAM enable + 1, // ROM bank + 0, // RAM bank + 0, // RAM address + 0, // RAM flag + 0 // RAM read value +}; + + +// HuC3 ROM write registers +void mapperHuC3ROM(u16 address, u8 value) +{ + int tmpAddress = 0; + + switch(address & 0x6000) { + case 0x0000: // RAM enable register + gbDataHuC3.mapperRAMEnable = ( value == 0x0a ? 1 : 0); + gbDataHuC3.mapperRAMFlag = value; + if(gbDataHuC3.mapperRAMFlag != 0x0a) + gbDataHuC3.mapperRAMBank = -1; + break; + case 0x2000: // ROM bank select + value = value & 0x7f; + if(value == 0) + value = 1; + if(value == gbDataHuC3.mapperROMBank) + break; + + tmpAddress = value << 14; + + tmpAddress &= gbRomSizeMask; + gbDataHuC3.mapperROMBank = value; + gbMemoryMap[0x04] = &gbRom[tmpAddress]; + gbMemoryMap[0x05] = &gbRom[tmpAddress + 0x1000]; + gbMemoryMap[0x06] = &gbRom[tmpAddress + 0x2000]; + gbMemoryMap[0x07] = &gbRom[tmpAddress + 0x3000]; + break; + case 0x4000: // RAM bank select + value = value & 0x03; + if(value == gbDataHuC3.mapperRAMBank) + break; + tmpAddress = value << 13; + tmpAddress &= gbRamSizeMask; + gbMemoryMap[0x0a] = &gbRam[tmpAddress]; + gbMemoryMap[0x0b] = &gbRam[tmpAddress + 0x1000]; + gbDataHuC3.mapperRAMBank = value; + gbDataHuC3.mapperRAMAddress = tmpAddress; + break; + case 0x6000: // nothing to do! + break; + } +} + +// HuC3 read RAM +u8 mapperHuC3ReadRAM(u16 address) +{ + if(gbDataHuC3.mapperRAMFlag > 0x0b && + gbDataHuC3.mapperRAMFlag < 0x0e) { + if(gbDataHuC3.mapperRAMFlag != 0x0c) + return 1; + return gbDataHuC3.mapperRAMValue; + } else + return gbMemoryMap[address >> 12][address & 0x0fff]; +} + +// HuC3 RAM write +void mapperHuC3RAM(u16 address, u8 value) +{ + int *p; + + if(gbDataHuC3.mapperRAMFlag < 0x0b || + gbDataHuC3.mapperRAMFlag > 0x0e) { + if(gbDataHuC3.mapperRAMEnable) { + if(gbRamSize) { + gbMemoryMap[address >> 12][address & 0x0fff] = value; + systemSaveUpdateCounter = SYSTEM_SAVE_UPDATED; + } + } + } else { + if(gbDataHuC3.mapperRAMFlag == 0x0b) { + if(value == 0x62) { + gbDataHuC3.mapperRAMValue = 1; + } else { + switch(value & 0xf0) { + case 0x10: + p = &gbDataHuC3.mapperRegister2; + gbDataHuC3.mapperRAMValue = *(p+gbDataHuC3.mapperRegister1++); + if(gbDataHuC3.mapperRegister1 > 6) + gbDataHuC3.mapperRegister1 = 0; + break; + case 0x30: + p = &gbDataHuC3.mapperRegister2; + *(p+gbDataHuC3.mapperRegister1++) = value & 0x0f; + if(gbDataHuC3.mapperRegister1 > 6) + gbDataHuC3.mapperRegister1 = 0; + gbDataHuC3.mapperAddress = + (gbDataHuC3.mapperRegister6 << 24) | + (gbDataHuC3.mapperRegister5 << 16) | + (gbDataHuC3.mapperRegister4 << 8) | + (gbDataHuC3.mapperRegister3 << 4) | + (gbDataHuC3.mapperRegister2); + break; + case 0x40: + gbDataHuC3.mapperRegister1 = (gbDataHuC3.mapperRegister1 & 0xf0) | + (value & 0x0f); + gbDataHuC3.mapperRegister2 = (gbDataHuC3.mapperAddress & 0x0f); + gbDataHuC3.mapperRegister3 = ((gbDataHuC3.mapperAddress>>4)&0x0f); + gbDataHuC3.mapperRegister4 = ((gbDataHuC3.mapperAddress>>8)&0x0f); + gbDataHuC3.mapperRegister5 = ((gbDataHuC3.mapperAddress>>16)&0x0f); + gbDataHuC3.mapperRegister6 = ((gbDataHuC3.mapperAddress>>24)&0x0f); + gbDataHuC3.mapperRegister7 = 0; + gbDataHuC3.mapperRegister8 = 0; + gbDataHuC3.mapperRAMValue = 0; + break; + case 0x50: + gbDataHuC3.mapperRegister1 = (gbDataHuC3.mapperRegister1 & 0x0f) | + ((value << 4)&0x0f); + break; + default: + gbDataHuC3.mapperRAMValue = 1; + break; + } + } + } + } +} + +void memoryUpdateMapHuC3() +{ + int tmpAddress = gbDataHuC3.mapperROMBank << 14; + + tmpAddress &= gbRomSizeMask; + gbMemoryMap[0x04] = &gbRom[tmpAddress]; + gbMemoryMap[0x05] = &gbRom[tmpAddress + 0x1000]; + gbMemoryMap[0x06] = &gbRom[tmpAddress + 0x2000]; + gbMemoryMap[0x07] = &gbRom[tmpAddress + 0x3000]; + + if(gbRamSize) { + tmpAddress = gbDataHuC3.mapperRAMBank << 13; + tmpAddress &= gbRamSizeMask; + gbMemoryMap[0x0a] = &gbRam[tmpAddress]; + gbMemoryMap[0x0b] = &gbRam[tmpAddress + 0x1000]; + } +} + +// TAMA5 (for Tamagotchi 3 (gb)). +// Very basic (and ugly :p) support, only rom bank switching is actually working... +mapperTAMA5 gbDataTAMA5 = { + 1, // RAM enable + 1, // ROM bank + 0, // RAM bank + 0, // RAM address + 0, // RAM Byte select + 0, // mapper command number + 0, // mapper last command; + { + 0, // commands 0x0 + 0, // commands 0x1 + 0, // commands 0x2 + 0, // commands 0x3 + 0, // commands 0x4 + 0, // commands 0x5 + 0, // commands 0x6 + 0, // commands 0x7 + 0, // commands 0x8 + 0, // commands 0x9 + 0, // commands 0xa + 0, // commands 0xb + 0, // commands 0xc + 0, // commands 0xd + 0, // commands 0xe + 0 // commands 0xf + }, + 0, // register + 0, // timer clock latch + 0, // timer clock register + 0, // timer seconds + 0, // timer minutes + 0, // timer hours + 0, // timer days + 0, // timer months + 0, // timer years + 0, // timer control + 0, // timer latched seconds + 0, // timer latched minutes + 0, // timer latched hours + 0, // timer latched days + 0, // timer latched months + 0, // timer latched years + 0, // timer latched control + (time_t)-1 // last time +}; + + +void memoryUpdateTAMA5Clock() +{ + if ((gbDataTAMA5.mapperYears & 3) == 0) + gbDaysinMonth[1] = 29; + else + gbDaysinMonth[1] = 28; + + time_t now = time(NULL); + time_t diff = now - gbDataTAMA5.mapperLastTime; + if(diff > 0) { + // update the clock according to the last update time + gbDataTAMA5.mapperSeconds += (int)(diff % 60); + if(gbDataTAMA5.mapperSeconds > 59) { + gbDataTAMA5.mapperSeconds -= 60; + gbDataTAMA5.mapperMinutes++; + } + + diff /= 60; + + gbDataTAMA5.mapperMinutes += (int)(diff % 60); + if(gbDataTAMA5.mapperMinutes > 59) { + gbDataTAMA5.mapperMinutes -= 60; + gbDataTAMA5.mapperHours++; + } + + diff /= 60; + + gbDataTAMA5.mapperHours += (int)(diff % 24); + diff /= 24; + if(gbDataTAMA5.mapperHours > 23) { + gbDataTAMA5.mapperHours -= 24; + diff++; + + } + + time_t days = diff; + while (days) + { + gbDataTAMA5.mapperDays++; + days--; + if (gbDataTAMA5.mapperDays>gbDaysinMonth[gbDataTAMA5.mapperMonths-1]) + { + gbDataTAMA5.mapperDays = 1; + gbDataTAMA5.mapperMonths++; + if (gbDataTAMA5.mapperMonths>12) + { + gbDataTAMA5.mapperMonths = 1; + gbDataTAMA5.mapperYears++; + if ((gbDataTAMA5.mapperYears & 3) == 0) + gbDaysinMonth[1] = 29; + else + gbDaysinMonth[1] = 28; + } + } + } + } + gbDataTAMA5.mapperLastTime = now; + +} + + + +// TAMA5 RAM write +void mapperTAMA5RAM(u16 address, u8 value) +{ + if ((address & 0xffff) <= 0xa001) + { + switch (address & 1) + { + case 0: // 'Values' Register + { + value &= 0xf; + gbDataTAMA5.mapperCommands[gbDataTAMA5.mapperCommandNumber] = value; + gbMemoryMap[0xa][0] = value; + +/* int test = gbDataTAMA5.mapperCommands[gbDataTAMA5.mapperCommandNumber & 0x0e] | + (gbDataTAMA5.mapperCommands[(gbDataTAMA5.mapperCommandNumber & 0x0e) +1]<<4);*/ + + if ((gbDataTAMA5.mapperCommandNumber & 0xe) == 0) // Read Command !!! + { + gbDataTAMA5.mapperROMBank = gbDataTAMA5.mapperCommands[0] | + (gbDataTAMA5.mapperCommands[1]<<4); + + int tmpAddress = (gbDataTAMA5.mapperROMBank << 14); + + tmpAddress &= gbRomSizeMask; + gbMemoryMap[0x04] = &gbRom[tmpAddress]; + gbMemoryMap[0x05] = &gbRom[tmpAddress + 0x1000]; + gbMemoryMap[0x06] = &gbRom[tmpAddress + 0x2000]; + gbMemoryMap[0x07] = &gbRom[tmpAddress + 0x3000]; + + gbDataTAMA5.mapperCommands[0x0f] = 0; + } + else if ((gbDataTAMA5.mapperCommandNumber & 0xe) == 4) + { + gbDataTAMA5.mapperCommands[0x0f] = 1; + if (gbDataTAMA5.mapperCommandNumber == 4) + gbDataTAMA5.mapperCommands[5] =0; // correct ? + } + else if ((gbDataTAMA5.mapperCommandNumber & 0xe) == 6) + { + gbDataTAMA5.mapperRamByteSelect = (gbDataTAMA5.mapperCommands[7]<<4) | + (gbDataTAMA5.mapperCommands[6]&0x0f); + + // Write Commands !!! + if (gbDataTAMA5.mapperCommands[0x0f] && (gbDataTAMA5.mapperCommandNumber == 7)) + { + int data = (gbDataTAMA5.mapperCommands[0x04] & 0x0f) | + (gbDataTAMA5.mapperCommands[0x05] <<4); + + // Not sure when the write command should reset... + // but it doesn't seem to matter. + // gbDataTAMA5.mapperCommands[0x0f] = 0; + + if (gbDataTAMA5.mapperRamByteSelect == 0x8) // Timer stuff + { + switch (data & 0xf) + { + case 0x7: + gbDataTAMA5.mapperDays = ((gbDataTAMA5.mapperDays)/10)*10 + (data >> 4); + break; + case 0x8: + gbDataTAMA5.mapperDays = (gbDataTAMA5.mapperDays%10) + (data >>4)*10; + break; + case 0x9: + gbDataTAMA5.mapperMonths = ((gbDataTAMA5.mapperMonths)/10)*10 + (data >> 4); + break; + case 0xa: + gbDataTAMA5.mapperMonths = (gbDataTAMA5.mapperMonths%10) + (data >>4)*10; + break; + case 0xb: + gbDataTAMA5.mapperYears = ((gbDataTAMA5.mapperYears)%1000) + (data >> 4)*1000; + break; + case 0xc: + gbDataTAMA5.mapperYears = (gbDataTAMA5.mapperYears%100) + (gbDataTAMA5.mapperYears/1000)*1000 + + (data >>4)*100; + break; + default : + break; + } + } + else if (gbDataTAMA5.mapperRamByteSelect == 0x18) // Timer stuff again + { + memoryUpdateTAMA5Clock(); + gbDataTAMA5.mapperLSeconds = gbDataTAMA5.mapperSeconds; + gbDataTAMA5.mapperLMinutes = gbDataTAMA5.mapperMinutes; + gbDataTAMA5.mapperLHours = gbDataTAMA5.mapperHours; + gbDataTAMA5.mapperLDays = gbDataTAMA5.mapperDays; + gbDataTAMA5.mapperLMonths = gbDataTAMA5.mapperMonths; + gbDataTAMA5.mapperLYears = gbDataTAMA5.mapperYears; + gbDataTAMA5.mapperLControl = gbDataTAMA5.mapperControl; + + int seconds = (gbDataTAMA5.mapperLSeconds / 10)*16 + gbDataTAMA5.mapperLSeconds %10; + int secondsL = (gbDataTAMA5.mapperLSeconds % 10); + int secondsH = (gbDataTAMA5.mapperLSeconds / 10); + int minutes = (gbDataTAMA5.mapperLMinutes / 10)*16 + gbDataTAMA5.mapperLMinutes %10; + int hours = (gbDataTAMA5.mapperLHours / 10)*16 + gbDataTAMA5.mapperLHours %10; + int DaysL = gbDataTAMA5.mapperLDays % 10; + int DaysH = gbDataTAMA5.mapperLDays /10; + int MonthsL = gbDataTAMA5.mapperLMonths % 10; + int MonthsH = gbDataTAMA5.mapperLMonths / 10; + int Years3 = (gbDataTAMA5.mapperLYears / 100) % 10; + int Years4 = (gbDataTAMA5.mapperLYears / 1000); + + switch (data & 0x0f) + { + // I guess cases 0 and 1 are used for secondsL and secondsH + // so the game would update the timer values on screen when + // the seconds reset to 0... ? + case 0x0: + gbTAMA5ram[gbDataTAMA5.mapperRamByteSelect] = secondsL; + break; + case 0x1: + gbTAMA5ram[gbDataTAMA5.mapperRamByteSelect] = secondsH; + break; + case 0x7: + gbTAMA5ram[gbDataTAMA5.mapperRamByteSelect] = DaysL; // days low + break; + case 0x8: + gbTAMA5ram[gbDataTAMA5.mapperRamByteSelect] = DaysH; // days high + break; + case 0x9: + gbTAMA5ram[gbDataTAMA5.mapperRamByteSelect] = MonthsL; // month low + break; + case 0xa: + gbTAMA5ram[gbDataTAMA5.mapperRamByteSelect] = MonthsH; // month high + break; + case 0xb: + gbTAMA5ram[gbDataTAMA5.mapperRamByteSelect] = Years4; // years 4th digit + break; + case 0xc: + gbTAMA5ram[gbDataTAMA5.mapperRamByteSelect] = Years3; // years 3rd digit + break; + default : + break; + } + + gbTAMA5ram[0x54] = seconds; // incorrect ? (not used by the game) ? + gbTAMA5ram[0x64] = minutes; + gbTAMA5ram[0x74] = hours; + gbTAMA5ram[0x84] = DaysH*16+DaysL; // incorrect ? (not used by the game) ? + gbTAMA5ram[0x94] = MonthsH*16+MonthsL; // incorrect ? (not used by the game) ? + + time(&gbDataTAMA5.mapperLastTime); + + gbMemoryMap[0xa][0] = 1; + } + else if (gbDataTAMA5.mapperRamByteSelect == 0x28) // Timer stuff again + { + if ((data & 0xf) == 0xb) + gbDataTAMA5.mapperYears = ((gbDataTAMA5.mapperYears>>2)<<2) + (data & 3); + } + else if (gbDataTAMA5.mapperRamByteSelect == 0x44) + { + gbDataTAMA5.mapperMinutes = (data/16)*10 + data%16; + } + else if (gbDataTAMA5.mapperRamByteSelect == 0x54) + { + gbDataTAMA5.mapperHours = (data/16)*10 + data%16; + } + else + { + gbTAMA5ram[gbDataTAMA5.mapperRamByteSelect] = data; + } + } + } + } + break; + case 1: // 'Commands' Register + { + gbMemoryMap[0xa][1] = gbDataTAMA5.mapperCommandNumber = value; + + // This should be only a 'is the flashrom ready ?' command. + // However as I couldn't find any 'copy' command + // (that seems to be needed for the saving system to work) + // I put it there... + if (value == 0x0a) + { + for (int i = 0; i<0x10; i++) + for (int j = 0; j<0x10; j++) + if (!(j&2)) + gbTAMA5ram[((i*0x10)+j) | 2] = gbTAMA5ram[(i*0x10)+j]; + // Enable this to see the content of the flashrom in 0xe000 + /*for (int k = 0; k<0x100; k++) + gbMemoryMap[0xe][k] = gbTAMA5ram[k];*/ + + gbMemoryMap[0xa][0] = gbDataTAMA5.mapperRAMEnable = 1; + } + else + { + if ((value & 0x0e) == 0x0c) + { + gbDataTAMA5.mapperRamByteSelect = gbDataTAMA5.mapperCommands[6] | + (gbDataTAMA5.mapperCommands[7]<<4); + + u8 byte = gbTAMA5ram[gbDataTAMA5.mapperRamByteSelect]; + + gbMemoryMap[0xa][0] = (value & 1) ? byte >> 4 : byte & 0x0f; + + gbDataTAMA5.mapperCommands[0x0f] = 0; + } + } + break; + } + } + } + else + { + if(gbDataTAMA5.mapperRAMEnable) { + if(gbDataTAMA5.mapperRAMBank != -1) { + if(gbRamSize) { + gbMemoryMap[address>>12][address & 0x0fff] = value; + systemSaveUpdateCounter = SYSTEM_SAVE_UPDATED; + } + } + } + } +} + + +// TAMA5 read RAM +u8 mapperTAMA5ReadRAM(u16 address) +{ + return gbMemoryMap[address>>12][address & 0xfff]; +} + + +void memoryUpdateMapTAMA5() +{ + int tmpAddress = (gbDataTAMA5.mapperROMBank << 14); + + tmpAddress &= gbRomSizeMask; + gbMemoryMap[0x04] = &gbRom[tmpAddress]; + gbMemoryMap[0x05] = &gbRom[tmpAddress + 0x1000]; + gbMemoryMap[0x06] = &gbRom[tmpAddress + 0x2000]; + gbMemoryMap[0x07] = &gbRom[tmpAddress + 0x3000]; + + if(gbRamSize) { + tmpAddress = 0 << 13; + tmpAddress &= gbRamSizeMask; + gbMemoryMap[0x0a] = &gbRam[tmpAddress]; + gbMemoryMap[0x0b] = &gbRam[tmpAddress + 0x1000]; + } +} + +// MMM01 Used in Momotarou collection (however the rom is corrupted) +mapperMMM01 gbDataMMM01 ={ + 0, // RAM enable + 1, // ROM bank + 0, // RAM bank + 0, // memory model + 0, // ROM high address + 0, // RAM address + 0 // Rom Bank 0 remapping +}; + +// MMM01 ROM write registers +void mapperMMM01ROM(u16 address, u8 value) +{ + int tmpAddress = 0; + + switch(address & 0x6000) { + case 0x0000: // RAM enable register + gbDataMMM01.mapperRAMEnable = ( ( value & 0x0a) == 0x0a ? 1 : 0); + break; + case 0x2000: // ROM bank select + // value = value & 0x1f; + if(value == 0) + value = 1; + if(value == gbDataMMM01.mapperROMBank) + break; + + tmpAddress = value << 14; + + // check current model + if(gbDataMMM01.mapperMemoryModel == 0) { + // model is 16/8, so we have a high address in use + tmpAddress |= (gbDataMMM01.mapperROMHighAddress) << 19; + } + else + tmpAddress |= gbDataMMM01.mapperRomBank0Remapping << 18; + + tmpAddress &= gbRomSizeMask; + gbDataMMM01.mapperROMBank = value; + gbMemoryMap[0x04] = &gbRom[tmpAddress]; + gbMemoryMap[0x05] = &gbRom[tmpAddress + 0x1000]; + gbMemoryMap[0x06] = &gbRom[tmpAddress + 0x2000]; + gbMemoryMap[0x07] = &gbRom[tmpAddress + 0x3000]; + break; + case 0x4000: // RAM bank select + if(gbDataMMM01.mapperMemoryModel == 1) { + // 4/32 model, RAM bank switching provided + value = value & 0x03; + if(value == gbDataMBC1.mapperRAMBank) + break; + tmpAddress = value << 13; + tmpAddress &= gbRamSizeMask; + gbMemoryMap[0x0a] = &gbRam[tmpAddress]; + gbMemoryMap[0x0b] = &gbRam[tmpAddress + 0x1000]; + gbDataMMM01.mapperRAMBank = value; + gbDataMMM01.mapperRAMAddress = tmpAddress; + } else { + // 16/8, set the high address + gbDataMMM01.mapperROMHighAddress = value & 0x03; + tmpAddress = gbDataMMM01.mapperROMBank << 14; + tmpAddress |= (gbDataMMM01.mapperROMHighAddress) << 19; + tmpAddress &= gbRomSizeMask; + gbMemoryMap[0x04] = &gbRom[tmpAddress]; + gbMemoryMap[0x05] = &gbRom[tmpAddress + 0x1000]; + gbMemoryMap[0x06] = &gbRom[tmpAddress + 0x2000]; + gbMemoryMap[0x07] = &gbRom[tmpAddress + 0x3000]; + + gbDataMMM01.mapperRomBank0Remapping = ((value<<1) | (value & 0x40 ? 1 : 0)) & 0xff; + tmpAddress = gbDataMMM01.mapperRomBank0Remapping << 18; + tmpAddress &= gbRomSizeMask; + gbMemoryMap[0x00] = &gbRom[tmpAddress]; + gbMemoryMap[0x01] = &gbRom[tmpAddress + 0x1000]; + gbMemoryMap[0x02] = &gbRom[tmpAddress + 0x2000]; + gbMemoryMap[0x03] = &gbRom[tmpAddress + 0x3000]; + } + break; + case 0x6000: // memory model select + gbDataMMM01.mapperMemoryModel = value & 1; + break; + } +} + +// MMM01 RAM write +void mapperMMM01RAM(u16 address, u8 value) +{ + if(gbDataMMM01.mapperRAMEnable) { + if(gbRamSize) { + gbMemoryMap[address >> 12][address & 0x0fff] = value; + systemSaveUpdateCounter = SYSTEM_SAVE_UPDATED; + } + } +} + +void memoryUpdateMapMMM01() +{ + int tmpAddress = gbDataMMM01.mapperROMBank << 14; + + // check current model + if(gbDataMMM01.mapperMemoryModel == 1) { + // model is 16/8, so we have a high address in use + tmpAddress |= (gbDataMMM01.mapperROMHighAddress) << 19; + } + + tmpAddress &= gbRomSizeMask; + gbMemoryMap[0x04] = &gbRom[tmpAddress]; + gbMemoryMap[0x05] = &gbRom[tmpAddress + 0x1000]; + gbMemoryMap[0x06] = &gbRom[tmpAddress + 0x2000]; + gbMemoryMap[0x07] = &gbRom[tmpAddress + 0x3000]; + + tmpAddress = gbDataMMM01.mapperRomBank0Remapping << 18; + tmpAddress &= gbRomSizeMask; + gbMemoryMap[0x00] = &gbRom[tmpAddress]; + gbMemoryMap[0x01] = &gbRom[tmpAddress + 0x1000]; + gbMemoryMap[0x02] = &gbRom[tmpAddress + 0x2000]; + gbMemoryMap[0x03] = &gbRom[tmpAddress + 0x3000]; + + if(gbRamSize) { + gbMemoryMap[0x0a] = &gbRam[gbDataMMM01.mapperRAMAddress]; + gbMemoryMap[0x0b] = &gbRam[gbDataMMM01.mapperRAMAddress + 0x1000]; + } +} + +// GameGenie ROM write registers +void mapperGGROM(u16 address, u8 value) +{ + switch(address & 0x6000) { + case 0x0000: // RAM enable register + break; + case 0x2000: // GameGenie has only a half bank + break; + case 0x4000: // GameGenie has no RAM + if ((address >=0x4001) && (address <= 0x4020)) // GG Hardware Registers + gbMemoryMap[address >> 12][address & 0x0fff] = value; + break; + case 0x6000: // GameGenie has only a half bank + break; + } +} + + +// GS3 Used to emulate the GS V3.0 rom bank switching +mapperGS3 gbDataGS3 = { 1 }; // ROM bank + +void mapperGS3ROM(u16 address, u8 value) +{ + int tmpAddress = 0; + + switch(address & 0x6000) { + case 0x0000: // GS has no ram + break; + case 0x2000: // GS has no 'classic' ROM bank select + break; + case 0x4000: // GS has no ram + break; + case 0x6000: // 0x6000 area is RW, and used for GS hardware registers + + if (address == 0x7FE1) // This is the (half) ROM bank select register + { + if(value == gbDataGS3.mapperROMBank) + break; + tmpAddress = value << 13; + + tmpAddress &= gbRomSizeMask; + gbDataGS3.mapperROMBank = value; + gbMemoryMap[0x04] = &gbRom[tmpAddress]; + gbMemoryMap[0x05] = &gbRom[tmpAddress + 0x1000]; + } + else + gbMemoryMap[address>>12][address & 0x0fff] = value; + break; + } +} + +void memoryUpdateMapGS3() +{ + int tmpAddress = gbDataGS3.mapperROMBank << 13; + + tmpAddress &= gbRomSizeMask; + // GS can only change a half ROM bank + gbMemoryMap[0x04] = &gbRom[tmpAddress]; + gbMemoryMap[0x05] = &gbRom[tmpAddress + 0x1000]; +} diff --git a/src/gba/GBA.cpp b/src/gba/GBA.cpp index b4512b23..805cadd2 100644 --- a/src/gba/GBA.cpp +++ b/src/gba/GBA.cpp @@ -1,4066 +1,4066 @@ -#include -#include -#include -#include -#include -#include "GBA.h" -#include "GBAcpu.h" -#include "GBAinline.h" -#include "Globals.h" -#include "GBAGfx.h" -#include "EEprom.h" -#include "Flash.h" -#include "Sound.h" -#include "Sram.h" -#include "bios.h" -#include "Cheats.h" -#include "../NLS.h" -#include "elf.h" -#include "../Util.h" -#include "../common/Port.h" -#include "../System.h" -#include "agbprint.h" -#include "GBALink.h" - -#ifdef PROFILING -#include "prof/prof.h" -#endif - -#ifdef __GNUC__ -#define _stricmp strcasecmp -#endif - -extern int emulating; - -int SWITicks = 0; -int IRQTicks = 0; - -u32 mastercode = 0; -int layerEnableDelay = 0; -bool busPrefetch = false; -bool busPrefetchEnable = false; -u32 busPrefetchCount = 0; -int cpuDmaTicksToUpdate = 0; -int cpuDmaCount = 0; -bool cpuDmaHack = false; -u32 cpuDmaLast = 0; -int dummyAddress = 0; - -bool cpuBreakLoop = false; -int cpuNextEvent = 0; - -int gbaSaveType = 0; // used to remember the save type on reset -bool intState = false; -bool stopState = false; -bool holdState = false; -int holdType = 0; -bool cpuSramEnabled = true; -bool cpuFlashEnabled = true; -bool cpuEEPROMEnabled = true; -bool cpuEEPROMSensorEnabled = false; - -u32 cpuPrefetch[2]; - -int cpuTotalTicks = 0; -#ifdef PROFILING -int profilingTicks = 0; -int profilingTicksReload = 0; -static profile_segment *profilSegment = NULL; -#endif - -#ifdef BKPT_SUPPORT -u8 freezeWorkRAM[0x40000]; -u8 freezeInternalRAM[0x8000]; -u8 freezeVRAM[0x18000]; -u8 freezePRAM[0x400]; -u8 freezeOAM[0x400]; -bool debugger_last; -#endif - -int lcdTicks = (useBios && !skipBios) ? 1008 : 208; -u8 timerOnOffDelay = 0; -u16 timer0Value = 0; -bool timer0On = false; -int timer0Ticks = 0; -int timer0Reload = 0; -int timer0ClockReload = 0; -u16 timer1Value = 0; -bool timer1On = false; -int timer1Ticks = 0; -int timer1Reload = 0; -int timer1ClockReload = 0; -u16 timer2Value = 0; -bool timer2On = false; -int timer2Ticks = 0; -int timer2Reload = 0; -int timer2ClockReload = 0; -u16 timer3Value = 0; -bool timer3On = false; -int timer3Ticks = 0; -int timer3Reload = 0; -int timer3ClockReload = 0; -u32 dma0Source = 0; -u32 dma0Dest = 0; -u32 dma1Source = 0; -u32 dma1Dest = 0; -u32 dma2Source = 0; -u32 dma2Dest = 0; -u32 dma3Source = 0; -u32 dma3Dest = 0; -void (*cpuSaveGameFunc)(u32,u8) = flashSaveDecide; -void (*renderLine)() = mode0RenderLine; -bool fxOn = false; -bool windowOn = false; -int frameCount = 0; -char buffer[1024]; -FILE *out = NULL; -u32 lastTime = 0; -int count = 0; - -int capture = 0; -int capturePrevious = 0; -int captureNumber = 0; - -const int TIMER_TICKS[4] = { - 0, - 6, - 8, - 10 -}; - -const u32 objTilesAddress [3] = {0x010000, 0x014000, 0x014000}; -const u8 gamepakRamWaitState[4] = { 4, 3, 2, 8 }; -const u8 gamepakWaitState[4] = { 4, 3, 2, 8 }; -const u8 gamepakWaitState0[2] = { 2, 1 }; -const u8 gamepakWaitState1[2] = { 4, 1 }; -const u8 gamepakWaitState2[2] = { 8, 1 }; -const bool isInRom [16]= - { false, false, false, false, false, false, false, false, - true, true, true, true, true, true, false, false }; - -u8 memoryWait[16] = - { 0, 0, 2, 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 0 }; -u8 memoryWait32[16] = - { 0, 0, 5, 0, 0, 1, 1, 0, 7, 7, 9, 9, 13, 13, 4, 0 }; -u8 memoryWaitSeq[16] = - { 0, 0, 2, 0, 0, 0, 0, 0, 2, 2, 4, 4, 8, 8, 4, 0 }; -u8 memoryWaitSeq32[16] = - { 0, 0, 5, 0, 0, 1, 1, 0, 5, 5, 9, 9, 17, 17, 4, 0 }; - -// The videoMemoryWait constants are used to add some waitstates -// if the opcode access video memory data outside of vblank/hblank -// It seems to happen on only one ticks for each pixel. -// Not used for now (too problematic with current code). -//const u8 videoMemoryWait[16] = -// {0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}; - - -u8 biosProtected[4]; - -#ifdef WORDS_BIGENDIAN -bool cpuBiosSwapped = false; -#endif - -u32 myROM[] = { -0xEA000006, -0xEA000093, -0xEA000006, -0x00000000, -0x00000000, -0x00000000, -0xEA000088, -0x00000000, -0xE3A00302, -0xE1A0F000, -0xE92D5800, -0xE55EC002, -0xE28FB03C, -0xE79BC10C, -0xE14FB000, -0xE92D0800, -0xE20BB080, -0xE38BB01F, -0xE129F00B, -0xE92D4004, -0xE1A0E00F, -0xE12FFF1C, -0xE8BD4004, -0xE3A0C0D3, -0xE129F00C, -0xE8BD0800, -0xE169F00B, -0xE8BD5800, -0xE1B0F00E, -0x0000009C, -0x0000009C, -0x0000009C, -0x0000009C, -0x000001F8, -0x000001F0, -0x000000AC, -0x000000A0, -0x000000FC, -0x00000168, -0xE12FFF1E, -0xE1A03000, -0xE1A00001, -0xE1A01003, -0xE2113102, -0x42611000, -0xE033C040, -0x22600000, -0xE1B02001, -0xE15200A0, -0x91A02082, -0x3AFFFFFC, -0xE1500002, -0xE0A33003, -0x20400002, -0xE1320001, -0x11A020A2, -0x1AFFFFF9, -0xE1A01000, -0xE1A00003, -0xE1B0C08C, -0x22600000, -0x42611000, -0xE12FFF1E, -0xE92D0010, -0xE1A0C000, -0xE3A01001, -0xE1500001, -0x81A000A0, -0x81A01081, -0x8AFFFFFB, -0xE1A0000C, -0xE1A04001, -0xE3A03000, -0xE1A02001, -0xE15200A0, -0x91A02082, -0x3AFFFFFC, -0xE1500002, -0xE0A33003, -0x20400002, -0xE1320001, -0x11A020A2, -0x1AFFFFF9, -0xE0811003, -0xE1B010A1, -0xE1510004, -0x3AFFFFEE, -0xE1A00004, -0xE8BD0010, -0xE12FFF1E, -0xE0010090, -0xE1A01741, -0xE2611000, -0xE3A030A9, -0xE0030391, -0xE1A03743, -0xE2833E39, -0xE0030391, -0xE1A03743, -0xE2833C09, -0xE283301C, -0xE0030391, -0xE1A03743, -0xE2833C0F, -0xE28330B6, -0xE0030391, -0xE1A03743, -0xE2833C16, -0xE28330AA, -0xE0030391, -0xE1A03743, -0xE2833A02, -0xE2833081, -0xE0030391, -0xE1A03743, -0xE2833C36, -0xE2833051, -0xE0030391, -0xE1A03743, -0xE2833CA2, -0xE28330F9, -0xE0000093, -0xE1A00840, -0xE12FFF1E, -0xE3A00001, -0xE3A01001, -0xE92D4010, -0xE3A03000, -0xE3A04001, -0xE3500000, -0x1B000004, -0xE5CC3301, -0xEB000002, -0x0AFFFFFC, -0xE8BD4010, -0xE12FFF1E, -0xE3A0C301, -0xE5CC3208, -0xE15C20B8, -0xE0110002, -0x10222000, -0x114C20B8, -0xE5CC4208, -0xE12FFF1E, -0xE92D500F, -0xE3A00301, -0xE1A0E00F, -0xE510F004, -0xE8BD500F, -0xE25EF004, -0xE59FD044, -0xE92D5000, -0xE14FC000, -0xE10FE000, -0xE92D5000, -0xE3A0C302, -0xE5DCE09C, -0xE35E00A5, -0x1A000004, -0x05DCE0B4, -0x021EE080, -0xE28FE004, -0x159FF018, -0x059FF018, -0xE59FD018, -0xE8BD5000, -0xE169F00C, -0xE8BD5000, -0xE25EF004, -0x03007FF0, -0x09FE2000, -0x09FFC000, -0x03007FE0 -}; - -variable_desc saveGameStruct[] = { - { &DISPCNT , sizeof(u16) }, - { &DISPSTAT , sizeof(u16) }, - { &VCOUNT , sizeof(u16) }, - { &BG0CNT , sizeof(u16) }, - { &BG1CNT , sizeof(u16) }, - { &BG2CNT , sizeof(u16) }, - { &BG3CNT , sizeof(u16) }, - { &BG0HOFS , sizeof(u16) }, - { &BG0VOFS , sizeof(u16) }, - { &BG1HOFS , sizeof(u16) }, - { &BG1VOFS , sizeof(u16) }, - { &BG2HOFS , sizeof(u16) }, - { &BG2VOFS , sizeof(u16) }, - { &BG3HOFS , sizeof(u16) }, - { &BG3VOFS , sizeof(u16) }, - { &BG2PA , sizeof(u16) }, - { &BG2PB , sizeof(u16) }, - { &BG2PC , sizeof(u16) }, - { &BG2PD , sizeof(u16) }, - { &BG2X_L , sizeof(u16) }, - { &BG2X_H , sizeof(u16) }, - { &BG2Y_L , sizeof(u16) }, - { &BG2Y_H , sizeof(u16) }, - { &BG3PA , sizeof(u16) }, - { &BG3PB , sizeof(u16) }, - { &BG3PC , sizeof(u16) }, - { &BG3PD , sizeof(u16) }, - { &BG3X_L , sizeof(u16) }, - { &BG3X_H , sizeof(u16) }, - { &BG3Y_L , sizeof(u16) }, - { &BG3Y_H , sizeof(u16) }, - { &WIN0H , sizeof(u16) }, - { &WIN1H , sizeof(u16) }, - { &WIN0V , sizeof(u16) }, - { &WIN1V , sizeof(u16) }, - { &WININ , sizeof(u16) }, - { &WINOUT , sizeof(u16) }, - { &MOSAIC , sizeof(u16) }, - { &BLDMOD , sizeof(u16) }, - { &COLEV , sizeof(u16) }, - { &COLY , sizeof(u16) }, - { &DM0SAD_L , sizeof(u16) }, - { &DM0SAD_H , sizeof(u16) }, - { &DM0DAD_L , sizeof(u16) }, - { &DM0DAD_H , sizeof(u16) }, - { &DM0CNT_L , sizeof(u16) }, - { &DM0CNT_H , sizeof(u16) }, - { &DM1SAD_L , sizeof(u16) }, - { &DM1SAD_H , sizeof(u16) }, - { &DM1DAD_L , sizeof(u16) }, - { &DM1DAD_H , sizeof(u16) }, - { &DM1CNT_L , sizeof(u16) }, - { &DM1CNT_H , sizeof(u16) }, - { &DM2SAD_L , sizeof(u16) }, - { &DM2SAD_H , sizeof(u16) }, - { &DM2DAD_L , sizeof(u16) }, - { &DM2DAD_H , sizeof(u16) }, - { &DM2CNT_L , sizeof(u16) }, - { &DM2CNT_H , sizeof(u16) }, - { &DM3SAD_L , sizeof(u16) }, - { &DM3SAD_H , sizeof(u16) }, - { &DM3DAD_L , sizeof(u16) }, - { &DM3DAD_H , sizeof(u16) }, - { &DM3CNT_L , sizeof(u16) }, - { &DM3CNT_H , sizeof(u16) }, - { &TM0D , sizeof(u16) }, - { &TM0CNT , sizeof(u16) }, - { &TM1D , sizeof(u16) }, - { &TM1CNT , sizeof(u16) }, - { &TM2D , sizeof(u16) }, - { &TM2CNT , sizeof(u16) }, - { &TM3D , sizeof(u16) }, - { &TM3CNT , sizeof(u16) }, - { &P1 , sizeof(u16) }, - { &IE , sizeof(u16) }, - { &IF , sizeof(u16) }, - { &IME , sizeof(u16) }, - { &holdState, sizeof(bool) }, - { &holdType, sizeof(int) }, - { &lcdTicks, sizeof(int) }, - { &timer0On , sizeof(bool) }, - { &timer0Ticks , sizeof(int) }, - { &timer0Reload , sizeof(int) }, - { &timer0ClockReload , sizeof(int) }, - { &timer1On , sizeof(bool) }, - { &timer1Ticks , sizeof(int) }, - { &timer1Reload , sizeof(int) }, - { &timer1ClockReload , sizeof(int) }, - { &timer2On , sizeof(bool) }, - { &timer2Ticks , sizeof(int) }, - { &timer2Reload , sizeof(int) }, - { &timer2ClockReload , sizeof(int) }, - { &timer3On , sizeof(bool) }, - { &timer3Ticks , sizeof(int) }, - { &timer3Reload , sizeof(int) }, - { &timer3ClockReload , sizeof(int) }, - { &dma0Source , sizeof(u32) }, - { &dma0Dest , sizeof(u32) }, - { &dma1Source , sizeof(u32) }, - { &dma1Dest , sizeof(u32) }, - { &dma2Source , sizeof(u32) }, - { &dma2Dest , sizeof(u32) }, - { &dma3Source , sizeof(u32) }, - { &dma3Dest , sizeof(u32) }, - { &fxOn, sizeof(bool) }, - { &windowOn, sizeof(bool) }, - { &N_FLAG , sizeof(bool) }, - { &C_FLAG , sizeof(bool) }, - { &Z_FLAG , sizeof(bool) }, - { &V_FLAG , sizeof(bool) }, - { &armState , sizeof(bool) }, - { &armIrqEnable , sizeof(bool) }, - { &armNextPC , sizeof(u32) }, - { &armMode , sizeof(int) }, - { &saveType , sizeof(int) }, - { NULL, 0 } -}; - -static int romSize = 0x2000000; - -#ifdef PROFILING -void cpuProfil(profile_segment *seg) -{ - profilSegment = seg; -} - -void cpuEnableProfiling(int hz) -{ - if(hz == 0) - hz = 100; - profilingTicks = profilingTicksReload = 16777216 / hz; - profSetHertz(hz); -} -#endif - - -inline int CPUUpdateTicks() -{ - int cpuLoopTicks = lcdTicks; - - if(soundTicks < cpuLoopTicks) - cpuLoopTicks = soundTicks; - - if(timer0On && (timer0Ticks < cpuLoopTicks)) { - cpuLoopTicks = timer0Ticks; - } - if(timer1On && !(TM1CNT & 4) && (timer1Ticks < cpuLoopTicks)) { - cpuLoopTicks = timer1Ticks; - } - if(timer2On && !(TM2CNT & 4) && (timer2Ticks < cpuLoopTicks)) { - cpuLoopTicks = timer2Ticks; - } - if(timer3On && !(TM3CNT & 4) && (timer3Ticks < cpuLoopTicks)) { - cpuLoopTicks = timer3Ticks; - } -#ifdef PROFILING - if(profilingTicksReload != 0) { - if(profilingTicks < cpuLoopTicks) { - cpuLoopTicks = profilingTicks; - } - } -#endif - - if (SWITicks) { - if (SWITicks < cpuLoopTicks) - cpuLoopTicks = SWITicks; - } - - if (IRQTicks) { - if (IRQTicks < cpuLoopTicks) - cpuLoopTicks = IRQTicks; - } - - return cpuLoopTicks; -} - -void CPUUpdateWindow0() -{ - int x00 = WIN0H>>8; - int x01 = WIN0H & 255; - - if(x00 <= x01) { - for(int i = 0; i < 240; i++) { - gfxInWin0[i] = (i >= x00 && i < x01); - } - } else { - for(int i = 0; i < 240; i++) { - gfxInWin0[i] = (i >= x00 || i < x01); - } - } -} - -void CPUUpdateWindow1() -{ - int x00 = WIN1H>>8; - int x01 = WIN1H & 255; - - if(x00 <= x01) { - for(int i = 0; i < 240; i++) { - gfxInWin1[i] = (i >= x00 && i < x01); - } - } else { - for(int i = 0; i < 240; i++) { - gfxInWin1[i] = (i >= x00 || i < x01); - } - } -} - -extern u32 line0[240]; -extern u32 line1[240]; -extern u32 line2[240]; -extern u32 line3[240]; - -#define CLEAR_ARRAY(a) \ - {\ - u32 *array = (a);\ - for(int i = 0; i < 240; i++) {\ - *array++ = 0x80000000;\ - }\ - }\ - -void CPUUpdateRenderBuffers(bool force) -{ - if(!(layerEnable & 0x0100) || force) { - CLEAR_ARRAY(line0); - } - if(!(layerEnable & 0x0200) || force) { - CLEAR_ARRAY(line1); - } - if(!(layerEnable & 0x0400) || force) { - CLEAR_ARRAY(line2); - } - if(!(layerEnable & 0x0800) || force) { - CLEAR_ARRAY(line3); - } -} - -static bool CPUWriteState(gzFile gzFile) -{ - utilWriteInt(gzFile, SAVE_GAME_VERSION); - - utilGzWrite(gzFile, &rom[0xa0], 16); - - utilWriteInt(gzFile, useBios); - - utilGzWrite(gzFile, ®[0], sizeof(reg)); - - utilWriteData(gzFile, saveGameStruct); - - // new to version 0.7.1 - utilWriteInt(gzFile, stopState); - // new to version 0.8 - utilWriteInt(gzFile, IRQTicks); - - utilGzWrite(gzFile, internalRAM, 0x8000); - utilGzWrite(gzFile, paletteRAM, 0x400); - utilGzWrite(gzFile, workRAM, 0x40000); - utilGzWrite(gzFile, vram, 0x20000); - utilGzWrite(gzFile, oam, 0x400); - utilGzWrite(gzFile, pix, 4*241*162); - utilGzWrite(gzFile, ioMem, 0x400); - - eepromSaveGame(gzFile); - flashSaveGame(gzFile); - soundSaveGame(gzFile); - - cheatsSaveGame(gzFile); - - // version 1.5 - rtcSaveGame(gzFile); - - return true; -} - -bool CPUWriteState(const char *file) -{ - gzFile gzFile = utilGzOpen(file, "wb"); - - if(gzFile == NULL) { - systemMessage(MSG_ERROR_CREATING_FILE, N_("Error creating file %s"), file); - return false; - } - - bool res = CPUWriteState(gzFile); - - utilGzClose(gzFile); - - return res; -} - -bool CPUWriteMemState(char *memory, int available) -{ - gzFile gzFile = utilMemGzOpen(memory, available, "w"); - - if(gzFile == NULL) { - return false; - } - - bool res = CPUWriteState(gzFile); - - long pos = utilGzMemTell(gzFile)+8; - - if(pos >= (available)) - res = false; - - utilGzClose(gzFile); - - return res; -} - -static bool CPUReadState(gzFile gzFile) -{ - int version = utilReadInt(gzFile); - - if(version > SAVE_GAME_VERSION || version < SAVE_GAME_VERSION_1) { - systemMessage(MSG_UNSUPPORTED_VBA_SGM, - N_("Unsupported VisualBoyAdvance save game version %d"), - version); - return false; - } - - u8 romname[17]; - - utilGzRead(gzFile, romname, 16); - - if(memcmp(&rom[0xa0], romname, 16) != 0) { - romname[16]=0; - for(int i = 0; i < 16; i++) - if(romname[i] < 32) - romname[i] = 32; - systemMessage(MSG_CANNOT_LOAD_SGM, N_("Cannot load save game for %s"), romname); - return false; - } - - bool ub = utilReadInt(gzFile) ? true : false; - - if(ub != useBios) { - if(useBios) - systemMessage(MSG_SAVE_GAME_NOT_USING_BIOS, - N_("Save game is not using the BIOS files")); - else - systemMessage(MSG_SAVE_GAME_USING_BIOS, - N_("Save game is using the BIOS file")); - return false; - } - - utilGzRead(gzFile, ®[0], sizeof(reg)); - - utilReadData(gzFile, saveGameStruct); - - if(version < SAVE_GAME_VERSION_3) - stopState = false; - else - stopState = utilReadInt(gzFile) ? true : false; - - if(version < SAVE_GAME_VERSION_4) - { - IRQTicks = 0; - intState = false; - } - else - { - IRQTicks = utilReadInt(gzFile); - if (IRQTicks>0) - intState = true; - else - { - intState = false; - IRQTicks = 0; - } - } - - utilGzRead(gzFile, internalRAM, 0x8000); - utilGzRead(gzFile, paletteRAM, 0x400); - utilGzRead(gzFile, workRAM, 0x40000); - utilGzRead(gzFile, vram, 0x20000); - utilGzRead(gzFile, oam, 0x400); - if(version < SAVE_GAME_VERSION_6) - utilGzRead(gzFile, pix, 4*240*160); - else - utilGzRead(gzFile, pix, 4*241*162); - utilGzRead(gzFile, ioMem, 0x400); - - if(skipSaveGameBattery) { - // skip eeprom data - eepromReadGameSkip(gzFile, version); - // skip flash data - flashReadGameSkip(gzFile, version); - } else { - eepromReadGame(gzFile, version); - flashReadGame(gzFile, version); - } - soundReadGame(gzFile, version); - - if(version > SAVE_GAME_VERSION_1) { - if(skipSaveGameCheats) { - // skip cheats list data - cheatsReadGameSkip(gzFile, version); - } else { - cheatsReadGame(gzFile, version); - } - } - if(version > SAVE_GAME_VERSION_6) { - rtcReadGame(gzFile); - } - - if(version <= SAVE_GAME_VERSION_7) { - u32 temp; -#define SWAP(a,b,c) \ - temp = (a);\ - (a) = (b)<<16|(c);\ - (b) = (temp) >> 16;\ - (c) = (temp) & 0xFFFF; - - SWAP(dma0Source, DM0SAD_H, DM0SAD_L); - SWAP(dma0Dest, DM0DAD_H, DM0DAD_L); - SWAP(dma1Source, DM1SAD_H, DM1SAD_L); - SWAP(dma1Dest, DM1DAD_H, DM1DAD_L); - SWAP(dma2Source, DM2SAD_H, DM2SAD_L); - SWAP(dma2Dest, DM2DAD_H, DM2DAD_L); - SWAP(dma3Source, DM3SAD_H, DM3SAD_L); - SWAP(dma3Dest, DM3DAD_H, DM3DAD_L); - } - - if(version <= SAVE_GAME_VERSION_8) { - timer0ClockReload = TIMER_TICKS[TM0CNT & 3]; - timer1ClockReload = TIMER_TICKS[TM1CNT & 3]; - timer2ClockReload = TIMER_TICKS[TM2CNT & 3]; - timer3ClockReload = TIMER_TICKS[TM3CNT & 3]; - - timer0Ticks = ((0x10000 - TM0D) << timer0ClockReload) - timer0Ticks; - timer1Ticks = ((0x10000 - TM1D) << timer1ClockReload) - timer1Ticks; - timer2Ticks = ((0x10000 - TM2D) << timer2ClockReload) - timer2Ticks; - timer3Ticks = ((0x10000 - TM3D) << timer3ClockReload) - timer3Ticks; - interp_rate(); - } - - // set pointers! - layerEnable = layerSettings & DISPCNT; - - CPUUpdateRender(); - CPUUpdateRenderBuffers(true); - CPUUpdateWindow0(); - CPUUpdateWindow1(); - gbaSaveType = 0; - switch(saveType) { - case 0: - cpuSaveGameFunc = flashSaveDecide; - break; - case 1: - cpuSaveGameFunc = sramWrite; - gbaSaveType = 1; - break; - case 2: - cpuSaveGameFunc = flashWrite; - gbaSaveType = 2; - break; - case 3: - break; - case 5: - gbaSaveType = 5; - break; - default: - systemMessage(MSG_UNSUPPORTED_SAVE_TYPE, - N_("Unsupported save type %d"), saveType); - break; - } - if(eepromInUse) - gbaSaveType = 3; - - systemSaveUpdateCounter = SYSTEM_SAVE_NOT_UPDATED; - if(armState) { - ARM_PREFETCH; - } else { - THUMB_PREFETCH; - } - - CPUUpdateRegister(0x204, CPUReadHalfWordQuick(0x4000204)); - - return true; -} - -bool CPUReadMemState(char *memory, int available) -{ - gzFile gzFile = utilMemGzOpen(memory, available, "r"); - - bool res = CPUReadState(gzFile); - - utilGzClose(gzFile); - - return res; -} - -bool CPUReadState(const char * file) -{ - gzFile gzFile = utilGzOpen(file, "rb"); - - if(gzFile == NULL) - return false; - - bool res = CPUReadState(gzFile); - - utilGzClose(gzFile); - - return res; -} - -bool CPUExportEepromFile(const char *fileName) -{ - if(eepromInUse) { - FILE *file = fopen(fileName, "wb"); - - if(!file) { - systemMessage(MSG_ERROR_CREATING_FILE, N_("Error creating file %s"), - fileName); - return false; - } - - for(int i = 0; i < eepromSize;) { - for(int j = 0; j < 8; j++) { - if(fwrite(&eepromData[i+7-j], 1, 1, file) != 1) { - fclose(file); - return false; - } - } - i += 8; - } - fclose(file); - } - return true; -} - -bool CPUWriteBatteryFile(const char *fileName) -{ - if(gbaSaveType == 0) { - if(eepromInUse) - gbaSaveType = 3; - else switch(saveType) { - case 1: - gbaSaveType = 1; - break; - case 2: - gbaSaveType = 2; - break; - } - } - - if((gbaSaveType) && (gbaSaveType!=5)) { - FILE *file = fopen(fileName, "wb"); - - if(!file) { - systemMessage(MSG_ERROR_CREATING_FILE, N_("Error creating file %s"), - fileName); - return false; - } - - // only save if Flash/Sram in use or EEprom in use - if(gbaSaveType != 3) { - if(gbaSaveType == 2) { - if(fwrite(flashSaveMemory, 1, flashSize, file) != (size_t)flashSize) { - fclose(file); - return false; - } - } else { - if(fwrite(flashSaveMemory, 1, 0x10000, file) != 0x10000) { - fclose(file); - return false; - } - } - } else { - if(fwrite(eepromData, 1, eepromSize, file) != (size_t)eepromSize) { - fclose(file); - return false; - } - } - fclose(file); - } - return true; -} - -bool CPUReadGSASnapshot(const char *fileName) -{ - int i; - FILE *file = fopen(fileName, "rb"); - - if(!file) { - systemMessage(MSG_CANNOT_OPEN_FILE, N_("Cannot open file %s"), fileName); - return false; - } - - // check file size to know what we should read - fseek(file, 0, SEEK_END); - - // long size = ftell(file); - fseek(file, 0x0, SEEK_SET); - fread(&i, 1, 4, file); - fseek(file, i, SEEK_CUR); // Skip SharkPortSave - fseek(file, 4, SEEK_CUR); // skip some sort of flag - fread(&i, 1, 4, file); // name length - fseek(file, i, SEEK_CUR); // skip name - fread(&i, 1, 4, file); // desc length - fseek(file, i, SEEK_CUR); // skip desc - fread(&i, 1, 4, file); // notes length - fseek(file, i, SEEK_CUR); // skip notes - int saveSize; - fread(&saveSize, 1, 4, file); // read length - saveSize -= 0x1c; // remove header size - char buffer[17]; - char buffer2[17]; - fread(buffer, 1, 16, file); - buffer[16] = 0; - for(i = 0; i < 16; i++) - if(buffer[i] < 32) - buffer[i] = 32; - memcpy(buffer2, &rom[0xa0], 16); - buffer2[16] = 0; - for(i = 0; i < 16; i++) - if(buffer2[i] < 32) - buffer2[i] = 32; - if(memcmp(buffer, buffer2, 16)) { - systemMessage(MSG_CANNOT_IMPORT_SNAPSHOT_FOR, - N_("Cannot import snapshot for %s. Current game is %s"), - buffer, - buffer2); - fclose(file); - return false; - } - fseek(file, 12, SEEK_CUR); // skip some flags - if(saveSize >= 65536) { - if(fread(flashSaveMemory, 1, saveSize, file) != (size_t)saveSize) { - fclose(file); - return false; - } - } else { - systemMessage(MSG_UNSUPPORTED_SNAPSHOT_FILE, - N_("Unsupported snapshot file %s"), - fileName); - fclose(file); - return false; - } - fclose(file); - CPUReset(); - return true; -} - -bool CPUReadGSASPSnapshot(const char *fileName) -{ - const char gsvfooter[] = "xV4\x12"; - const size_t namepos=0x0c, namesz=12; - const size_t footerpos=0x42c, footersz=4; - - char footer[footersz+1], romname[namesz+1], savename[namesz+1];; - FILE *file = fopen(fileName, "rb"); - - if(!file) { - systemMessage(MSG_CANNOT_OPEN_FILE, N_("Cannot open file %s"), fileName); - return false; - } - - // read save name - fseek(file, namepos, SEEK_SET); - fread(savename, 1, namesz, file); - savename[namesz] = 0; - - memcpy(romname, &rom[0xa0], namesz); - romname[namesz] = 0; - - if(memcmp(romname, savename, namesz)) { - systemMessage(MSG_CANNOT_IMPORT_SNAPSHOT_FOR, - N_("Cannot import snapshot for %s. Current game is %s"), - savename, - romname); - fclose(file); - return false; - } - - // read footer tag - fseek(file, footerpos, SEEK_SET); - fread(footer, 1, footersz, file); - footer[footersz] = 0; - - if(memcmp(footer, gsvfooter, footersz)) { - systemMessage(0, - N_("Unsupported snapshot file %s. Footer '%s' at %u should be '%s'"), - fileName, - footer, - footerpos, - gsvfooter); - fclose(file); - return false; - } - - // Read up to 128k save - fread(flashSaveMemory, 1, FLASH_128K_SZ, file); - - fclose(file); - CPUReset(); - return true; -} - - -bool CPUWriteGSASnapshot(const char *fileName, - const char *title, - const char *desc, - const char *notes) -{ - FILE *file = fopen(fileName, "wb"); - - if(!file) { - systemMessage(MSG_CANNOT_OPEN_FILE, N_("Cannot open file %s"), fileName); - return false; - } - - u8 buffer[17]; - - utilPutDword(buffer, 0x0d); // SharkPortSave length - fwrite(buffer, 1, 4, file); - fwrite("SharkPortSave", 1, 0x0d, file); - utilPutDword(buffer, 0x000f0000); - fwrite(buffer, 1, 4, file); // save type 0x000f0000 = GBA save - utilPutDword(buffer, (u32)strlen(title)); - fwrite(buffer, 1, 4, file); // title length - fwrite(title, 1, strlen(title), file); - utilPutDword(buffer, (u32)strlen(desc)); - fwrite(buffer, 1, 4, file); // desc length - fwrite(desc, 1, strlen(desc), file); - utilPutDword(buffer, (u32)strlen(notes)); - fwrite(buffer, 1, 4, file); // notes length - fwrite(notes, 1, strlen(notes), file); - int saveSize = 0x10000; - if(gbaSaveType == 2) - saveSize = flashSize; - int totalSize = saveSize + 0x1c; - - utilPutDword(buffer, totalSize); // length of remainder of save - CRC - fwrite(buffer, 1, 4, file); - - char *temp = new char[0x2001c]; - memset(temp, 0, 28); - memcpy(temp, &rom[0xa0], 16); // copy internal name - temp[0x10] = rom[0xbe]; // reserved area (old checksum) - temp[0x11] = rom[0xbf]; // reserved area (old checksum) - temp[0x12] = rom[0xbd]; // complement check - temp[0x13] = rom[0xb0]; // maker - temp[0x14] = 1; // 1 save ? - memcpy(&temp[0x1c], flashSaveMemory, saveSize); // copy save - fwrite(temp, 1, totalSize, file); // write save + header - u32 crc = 0; - - for(int i = 0; i < totalSize; i++) { - crc += ((u32)temp[i] << (crc % 0x18)); - } - - utilPutDword(buffer, crc); - fwrite(buffer, 1, 4, file); // CRC? - - fclose(file); - delete [] temp; - return true; -} - -bool CPUImportEepromFile(const char *fileName) -{ - FILE *file = fopen(fileName, "rb"); - - if(!file) - return false; - - // check file size to know what we should read - fseek(file, 0, SEEK_END); - - long size = ftell(file); - fseek(file, 0, SEEK_SET); - if(size == 512 || size == 0x2000) { - if(fread(eepromData, 1, size, file) != (size_t)size) { - fclose(file); - return false; - } - for(int i = 0; i < size;) { - u8 tmp = eepromData[i]; - eepromData[i] = eepromData[7-i]; - eepromData[7-i] = tmp; - i++; - tmp = eepromData[i]; - eepromData[i] = eepromData[7-i]; - eepromData[7-i] = tmp; - i++; - tmp = eepromData[i]; - eepromData[i] = eepromData[7-i]; - eepromData[7-i] = tmp; - i++; - tmp = eepromData[i]; - eepromData[i] = eepromData[7-i]; - eepromData[7-i] = tmp; - i++; - i += 4; - } - } else { - fclose(file); - return false; - } - fclose(file); - return true; -} - -bool CPUReadBatteryFile(const char *fileName) -{ - FILE *file = fopen(fileName, "rb"); - - if(!file) - return false; - - // check file size to know what we should read - fseek(file, 0, SEEK_END); - - long size = ftell(file); - fseek(file, 0, SEEK_SET); - systemSaveUpdateCounter = SYSTEM_SAVE_NOT_UPDATED; - - if(size == 512 || size == 0x2000) { - if(fread(eepromData, 1, size, file) != (size_t)size) { - fclose(file); - return false; - } - } else { - if(size == 0x20000) { - if(fread(flashSaveMemory, 1, 0x20000, file) != 0x20000) { - fclose(file); - return false; - } - flashSetSize(0x20000); - } else { - if(fread(flashSaveMemory, 1, 0x10000, file) != 0x10000) { - fclose(file); - return false; - } - flashSetSize(0x10000); - } - } - fclose(file); - return true; -} - -bool CPUWritePNGFile(const char *fileName) -{ - return utilWritePNGFile(fileName, 240, 160, pix); -} - -bool CPUWriteBMPFile(const char *fileName) -{ - return utilWriteBMPFile(fileName, 240, 160, pix); -} - -bool CPUIsZipFile(const char * file) -{ - if(strlen(file) > 4) { - const char * p = strrchr(file,'.'); - - if(p != NULL) { - if(_stricmp(p, ".zip") == 0) - return true; - } - } - - return false; -} - -bool CPUIsGBAImage(const char * file) -{ - cpuIsMultiBoot = false; - if(strlen(file) > 4) { - const char * p = strrchr(file,'.'); - - if(p != NULL) { - if(_stricmp(p, ".gba") == 0) - return true; - if(_stricmp(p, ".agb") == 0) - return true; - if(_stricmp(p, ".bin") == 0) - return true; - if(_stricmp(p, ".elf") == 0) - return true; - if(_stricmp(p, ".mb") == 0) { - cpuIsMultiBoot = true; - return true; - } - } - } - - return false; -} - -bool CPUIsGBABios(const char * file) -{ - if(strlen(file) > 4) { - const char * p = strrchr(file,'.'); - - if(p != NULL) { - if(_stricmp(p, ".gba") == 0) - return true; - if(_stricmp(p, ".agb") == 0) - return true; - if(_stricmp(p, ".bin") == 0) - return true; - if(_stricmp(p, ".bios") == 0) - return true; - if(_stricmp(p, ".rom") == 0) - return true; - } - } - - return false; -} - -bool CPUIsELF(const char *file) -{ - if(file == NULL) - return false; - - if(strlen(file) > 4) { - const char * p = strrchr(file,'.'); - - if(p != NULL) { - if(_stricmp(p, ".elf") == 0) - return true; - } - } - return false; -} - -void CPUCleanUp() -{ -#ifdef PROFILING - if(profilingTicksReload) { - profCleanup(); - } -#endif - - if(rom != NULL) { - free(rom); - rom = NULL; - } - - if(vram != NULL) { - free(vram); - vram = NULL; - } - - if(paletteRAM != NULL) { - free(paletteRAM); - paletteRAM = NULL; - } - - if(internalRAM != NULL) { - free(internalRAM); - internalRAM = NULL; - } - - if(workRAM != NULL) { - free(workRAM); - workRAM = NULL; - } - - if(bios != NULL) { - free(bios); - bios = NULL; - } - - if(pix != NULL) { - free(pix); - pix = NULL; - } - - if(oam != NULL) { - free(oam); - oam = NULL; - } - - if(ioMem != NULL) { - free(ioMem); - ioMem = NULL; - } - -#ifndef NO_DEBUGGER - elfCleanUp(); -#endif //NO_DEBUGGER - - systemSaveUpdateCounter = SYSTEM_SAVE_NOT_UPDATED; - - emulating = 0; -} - -int CPULoadRom(const char *szFile) -{ - romSize = 0x2000000; - if(rom != NULL) { - CPUCleanUp(); - } - - systemSaveUpdateCounter = SYSTEM_SAVE_NOT_UPDATED; - - rom = (u8 *)malloc(0x2000000); - if(rom == NULL) { - systemMessage(MSG_OUT_OF_MEMORY, N_("Failed to allocate memory for %s"), - "ROM"); - return 0; - } - workRAM = (u8 *)calloc(1, 0x40000); - if(workRAM == NULL) { - systemMessage(MSG_OUT_OF_MEMORY, N_("Failed to allocate memory for %s"), - "WRAM"); - return 0; - } - - u8 *whereToLoad = cpuIsMultiBoot ? workRAM : rom; - -#ifndef NO_DEBUGGER - if(CPUIsELF(szFile)) { - FILE *f = fopen(szFile, "rb"); - if(!f) { - systemMessage(MSG_ERROR_OPENING_IMAGE, N_("Error opening image %s"), - szFile); - free(rom); - rom = NULL; - free(workRAM); - workRAM = NULL; - return 0; - } - bool res = elfRead(szFile, romSize, f); - if(!res || romSize == 0) { - free(rom); - rom = NULL; - free(workRAM); - workRAM = NULL; - elfCleanUp(); - return 0; - } - } else -#endif //NO_DEBUGGER - if(szFile!=NULL) - { - if(!utilLoad(szFile, - utilIsGBAImage, - whereToLoad, - romSize)) { - free(rom); - rom = NULL; - free(workRAM); - workRAM = NULL; - return 0; - } - } - - u16 *temp = (u16 *)(rom+((romSize+1)&~1)); - int i; - for(i = (romSize+1)&~1; i < 0x2000000; i+=2) { - WRITE16LE(temp, (i >> 1) & 0xFFFF); - temp++; - } - - bios = (u8 *)calloc(1,0x4000); - if(bios == NULL) { - systemMessage(MSG_OUT_OF_MEMORY, N_("Failed to allocate memory for %s"), - "BIOS"); - CPUCleanUp(); - return 0; - } - internalRAM = (u8 *)calloc(1,0x8000); - if(internalRAM == NULL) { - systemMessage(MSG_OUT_OF_MEMORY, N_("Failed to allocate memory for %s"), - "IRAM"); - CPUCleanUp(); - return 0; - } - paletteRAM = (u8 *)calloc(1,0x400); - if(paletteRAM == NULL) { - systemMessage(MSG_OUT_OF_MEMORY, N_("Failed to allocate memory for %s"), - "PRAM"); - CPUCleanUp(); - return 0; - } - vram = (u8 *)calloc(1, 0x20000); - if(vram == NULL) { - systemMessage(MSG_OUT_OF_MEMORY, N_("Failed to allocate memory for %s"), - "VRAM"); - CPUCleanUp(); - return 0; - } - oam = (u8 *)calloc(1, 0x400); - if(oam == NULL) { - systemMessage(MSG_OUT_OF_MEMORY, N_("Failed to allocate memory for %s"), - "OAM"); - CPUCleanUp(); - return 0; - } - pix = (u8 *)calloc(1, 4 * 241 * 162); - if(pix == NULL) { - systemMessage(MSG_OUT_OF_MEMORY, N_("Failed to allocate memory for %s"), - "PIX"); - CPUCleanUp(); - return 0; - } - ioMem = (u8 *)calloc(1, 0x400); - if(ioMem == NULL) { - systemMessage(MSG_OUT_OF_MEMORY, N_("Failed to allocate memory for %s"), - "IO"); - CPUCleanUp(); - return 0; - } - - flashInit(); - eepromInit(); - - CPUUpdateRenderBuffers(true); - - return romSize; -} - -void doMirroring (bool b) -{ - u32 mirroredRomSize = (((romSize)>>20) & 0x3F)<<20; - u32 mirroredRomAddress = romSize; - if ((mirroredRomSize <=0x800000) && (b)) - { - mirroredRomAddress = mirroredRomSize; - if (mirroredRomSize==0) - mirroredRomSize=0x100000; - while (mirroredRomAddress<0x01000000) - { - memcpy ((u16 *)(rom+mirroredRomAddress), (u16 *)(rom), mirroredRomSize); - mirroredRomAddress+=mirroredRomSize; - } - } -} - -void CPUUpdateRender() -{ - switch(DISPCNT & 7) { - case 0: - if((!fxOn && !windowOn && !(layerEnable & 0x8000)) || - cpuDisableSfx) - renderLine = mode0RenderLine; - else if(fxOn && !windowOn && !(layerEnable & 0x8000)) - renderLine = mode0RenderLineNoWindow; - else - renderLine = mode0RenderLineAll; - break; - case 1: - if((!fxOn && !windowOn && !(layerEnable & 0x8000)) || - cpuDisableSfx) - renderLine = mode1RenderLine; - else if(fxOn && !windowOn && !(layerEnable & 0x8000)) - renderLine = mode1RenderLineNoWindow; - else - renderLine = mode1RenderLineAll; - break; - case 2: - if((!fxOn && !windowOn && !(layerEnable & 0x8000)) || - cpuDisableSfx) - renderLine = mode2RenderLine; - else if(fxOn && !windowOn && !(layerEnable & 0x8000)) - renderLine = mode2RenderLineNoWindow; - else - renderLine = mode2RenderLineAll; - break; - case 3: - if((!fxOn && !windowOn && !(layerEnable & 0x8000)) || - cpuDisableSfx) - renderLine = mode3RenderLine; - else if(fxOn && !windowOn && !(layerEnable & 0x8000)) - renderLine = mode3RenderLineNoWindow; - else - renderLine = mode3RenderLineAll; - break; - case 4: - if((!fxOn && !windowOn && !(layerEnable & 0x8000)) || - cpuDisableSfx) - renderLine = mode4RenderLine; - else if(fxOn && !windowOn && !(layerEnable & 0x8000)) - renderLine = mode4RenderLineNoWindow; - else - renderLine = mode4RenderLineAll; - break; - case 5: - if((!fxOn && !windowOn && !(layerEnable & 0x8000)) || - cpuDisableSfx) - renderLine = mode5RenderLine; - else if(fxOn && !windowOn && !(layerEnable & 0x8000)) - renderLine = mode5RenderLineNoWindow; - else - renderLine = mode5RenderLineAll; - default: - break; - } -} - -void CPUUpdateCPSR() -{ - u32 CPSR = reg[16].I & 0x40; - if(N_FLAG) - CPSR |= 0x80000000; - if(Z_FLAG) - CPSR |= 0x40000000; - if(C_FLAG) - CPSR |= 0x20000000; - if(V_FLAG) - CPSR |= 0x10000000; - if(!armState) - CPSR |= 0x00000020; - if(!armIrqEnable) - CPSR |= 0x80; - CPSR |= (armMode & 0x1F); - reg[16].I = CPSR; -} - -void CPUUpdateFlags(bool breakLoop) -{ - u32 CPSR = reg[16].I; - - N_FLAG = (CPSR & 0x80000000) ? true: false; - Z_FLAG = (CPSR & 0x40000000) ? true: false; - C_FLAG = (CPSR & 0x20000000) ? true: false; - V_FLAG = (CPSR & 0x10000000) ? true: false; - armState = (CPSR & 0x20) ? false : true; - armIrqEnable = (CPSR & 0x80) ? false : true; - if(breakLoop) { - if (armIrqEnable && (IF & IE) && (IME & 1)) - cpuNextEvent = cpuTotalTicks; - } -} - -void CPUUpdateFlags() -{ - CPUUpdateFlags(true); -} - -#ifdef WORDS_BIGENDIAN -static void CPUSwap(volatile u32 *a, volatile u32 *b) -{ - volatile u32 c = *b; - *b = *a; - *a = c; -} -#else -static void CPUSwap(u32 *a, u32 *b) -{ - u32 c = *b; - *b = *a; - *a = c; -} -#endif - -void CPUSwitchMode(int mode, bool saveState, bool breakLoop) -{ - // if(armMode == mode) - // return; - - CPUUpdateCPSR(); - - switch(armMode) { - case 0x10: - case 0x1F: - reg[R13_USR].I = reg[13].I; - reg[R14_USR].I = reg[14].I; - reg[17].I = reg[16].I; - break; - case 0x11: - CPUSwap(®[R8_FIQ].I, ®[8].I); - CPUSwap(®[R9_FIQ].I, ®[9].I); - CPUSwap(®[R10_FIQ].I, ®[10].I); - CPUSwap(®[R11_FIQ].I, ®[11].I); - CPUSwap(®[R12_FIQ].I, ®[12].I); - reg[R13_FIQ].I = reg[13].I; - reg[R14_FIQ].I = reg[14].I; - reg[SPSR_FIQ].I = reg[17].I; - break; - case 0x12: - reg[R13_IRQ].I = reg[13].I; - reg[R14_IRQ].I = reg[14].I; - reg[SPSR_IRQ].I = reg[17].I; - break; - case 0x13: - reg[R13_SVC].I = reg[13].I; - reg[R14_SVC].I = reg[14].I; - reg[SPSR_SVC].I = reg[17].I; - break; - case 0x17: - reg[R13_ABT].I = reg[13].I; - reg[R14_ABT].I = reg[14].I; - reg[SPSR_ABT].I = reg[17].I; - break; - case 0x1b: - reg[R13_UND].I = reg[13].I; - reg[R14_UND].I = reg[14].I; - reg[SPSR_UND].I = reg[17].I; - break; - } - - u32 CPSR = reg[16].I; - u32 SPSR = reg[17].I; - - switch(mode) { - case 0x10: - case 0x1F: - reg[13].I = reg[R13_USR].I; - reg[14].I = reg[R14_USR].I; - reg[16].I = SPSR; - break; - case 0x11: - CPUSwap(®[8].I, ®[R8_FIQ].I); - CPUSwap(®[9].I, ®[R9_FIQ].I); - CPUSwap(®[10].I, ®[R10_FIQ].I); - CPUSwap(®[11].I, ®[R11_FIQ].I); - CPUSwap(®[12].I, ®[R12_FIQ].I); - reg[13].I = reg[R13_FIQ].I; - reg[14].I = reg[R14_FIQ].I; - if(saveState) - reg[17].I = CPSR; - else - reg[17].I = reg[SPSR_FIQ].I; - break; - case 0x12: - reg[13].I = reg[R13_IRQ].I; - reg[14].I = reg[R14_IRQ].I; - reg[16].I = SPSR; - if(saveState) - reg[17].I = CPSR; - else - reg[17].I = reg[SPSR_IRQ].I; - break; - case 0x13: - reg[13].I = reg[R13_SVC].I; - reg[14].I = reg[R14_SVC].I; - reg[16].I = SPSR; - if(saveState) - reg[17].I = CPSR; - else - reg[17].I = reg[SPSR_SVC].I; - break; - case 0x17: - reg[13].I = reg[R13_ABT].I; - reg[14].I = reg[R14_ABT].I; - reg[16].I = SPSR; - if(saveState) - reg[17].I = CPSR; - else - reg[17].I = reg[SPSR_ABT].I; - break; - case 0x1b: - reg[13].I = reg[R13_UND].I; - reg[14].I = reg[R14_UND].I; - reg[16].I = SPSR; - if(saveState) - reg[17].I = CPSR; - else - reg[17].I = reg[SPSR_UND].I; - break; - default: - systemMessage(MSG_UNSUPPORTED_ARM_MODE, N_("Unsupported ARM mode %02x"), mode); - break; - } - armMode = mode; - CPUUpdateFlags(breakLoop); - CPUUpdateCPSR(); -} - -void CPUSwitchMode(int mode, bool saveState) -{ - CPUSwitchMode(mode, saveState, true); -} - -void CPUUndefinedException() -{ - u32 PC = reg[15].I; - bool savedArmState = armState; - CPUSwitchMode(0x1b, true, false); - reg[14].I = PC - (savedArmState ? 4 : 2); - reg[15].I = 0x04; - armState = true; - armIrqEnable = false; - armNextPC = 0x04; - ARM_PREFETCH; - reg[15].I += 4; -} - -void CPUSoftwareInterrupt() -{ - u32 PC = reg[15].I; - bool savedArmState = armState; - CPUSwitchMode(0x13, true, false); - reg[14].I = PC - (savedArmState ? 4 : 2); - reg[15].I = 0x08; - armState = true; - armIrqEnable = false; - armNextPC = 0x08; - ARM_PREFETCH; - reg[15].I += 4; -} - -void CPUSoftwareInterrupt(int comment) -{ - static bool disableMessage = false; - if(armState) comment >>= 16; -#ifdef BKPT_SUPPORT - if(comment == 0xff) { - dbgOutput(NULL, reg[0].I); - return; - } -#endif -#ifdef PROFILING - if(comment == 0xfe) { - profStartup(reg[0].I, reg[1].I); - return; - } - if(comment == 0xfd) { - profControl(reg[0].I); - return; - } - if(comment == 0xfc) { - profCleanup(); - return; - } - if(comment == 0xfb) { - profCount(); - return; - } -#endif - if(comment == 0xfa) { - agbPrintFlush(); - return; - } -#ifdef SDL - if(comment == 0xf9) { - emulating = 0; - cpuNextEvent = cpuTotalTicks; - cpuBreakLoop = true; - return; - } -#endif - if(useBios) { -#ifdef GBA_LOGGING - if(systemVerbose & VERBOSE_SWI) { - log("SWI: %08x at %08x (0x%08x,0x%08x,0x%08x,VCOUNT = %2d)\n", comment, - armState ? armNextPC - 4: armNextPC -2, - reg[0].I, - reg[1].I, - reg[2].I, - VCOUNT); - } -#endif - CPUSoftwareInterrupt(); - return; - } - // This would be correct, but it causes problems if uncommented - // else { - // biosProtected = 0xe3a02004; - // } - - switch(comment) { - case 0x00: - BIOS_SoftReset(); - ARM_PREFETCH; - break; - case 0x01: - BIOS_RegisterRamReset(); - break; - case 0x02: -#ifdef GBA_LOGGING - if(systemVerbose & VERBOSE_SWI) { - log("Halt: (VCOUNT = %2d)\n", - VCOUNT); - } -#endif - holdState = true; - holdType = -1; - cpuNextEvent = cpuTotalTicks; - break; - case 0x03: -#ifdef GBA_LOGGING - if(systemVerbose & VERBOSE_SWI) { - log("Stop: (VCOUNT = %2d)\n", - VCOUNT); - } -#endif - holdState = true; - holdType = -1; - stopState = true; - cpuNextEvent = cpuTotalTicks; - break; - case 0x04: -#ifdef GBA_LOGGING - if(systemVerbose & VERBOSE_SWI) { - log("IntrWait: 0x%08x,0x%08x (VCOUNT = %2d)\n", - reg[0].I, - reg[1].I, - VCOUNT); - } -#endif - CPUSoftwareInterrupt(); - break; - case 0x05: -#ifdef GBA_LOGGING - if(systemVerbose & VERBOSE_SWI) { - log("VBlankIntrWait: (VCOUNT = %2d)\n", - VCOUNT); - } -#endif - CPUSoftwareInterrupt(); - break; - case 0x06: - CPUSoftwareInterrupt(); - break; - case 0x07: - CPUSoftwareInterrupt(); - break; - case 0x08: - BIOS_Sqrt(); - break; - case 0x09: - BIOS_ArcTan(); - break; - case 0x0A: - BIOS_ArcTan2(); - break; - case 0x0B: - { - int len = (reg[2].I & 0x1FFFFF) >>1; - if (!(((reg[0].I & 0xe000000) == 0) || - ((reg[0].I + len) & 0xe000000) == 0)) - { - if ((reg[2].I >> 24) & 1) - { - if ((reg[2].I >> 26) & 1) - SWITicks = (7 + memoryWait32[(reg[1].I>>24) & 0xF]) * (len>>1); - else - SWITicks = (8 + memoryWait[(reg[1].I>>24) & 0xF]) * (len); - } - else - { - if ((reg[2].I >> 26) & 1) - SWITicks = (10 + memoryWait32[(reg[0].I>>24) & 0xF] + - memoryWait32[(reg[1].I>>24) & 0xF]) * (len>>1); - else - SWITicks = (11 + memoryWait[(reg[0].I>>24) & 0xF] + - memoryWait[(reg[1].I>>24) & 0xF]) * len; - } - } - } - BIOS_CpuSet(); - break; - case 0x0C: - { - int len = (reg[2].I & 0x1FFFFF) >>5; - if (!(((reg[0].I & 0xe000000) == 0) || - ((reg[0].I + len) & 0xe000000) == 0)) - { - if ((reg[2].I >> 24) & 1) - SWITicks = (6 + memoryWait32[(reg[1].I>>24) & 0xF] + - 7 * (memoryWaitSeq32[(reg[1].I>>24) & 0xF] + 1)) * len; - else - SWITicks = (9 + memoryWait32[(reg[0].I>>24) & 0xF] + - memoryWait32[(reg[1].I>>24) & 0xF] + - 7 * (memoryWaitSeq32[(reg[0].I>>24) & 0xF] + - memoryWaitSeq32[(reg[1].I>>24) & 0xF] + 2)) * len; - } - } - BIOS_CpuFastSet(); - break; - case 0x0D: - BIOS_GetBiosChecksum(); - break; - case 0x0E: - BIOS_BgAffineSet(); - break; - case 0x0F: - BIOS_ObjAffineSet(); - break; - case 0x10: - { - int len = CPUReadHalfWord(reg[2].I); - if (!(((reg[0].I & 0xe000000) == 0) || - ((reg[0].I + len) & 0xe000000) == 0)) - SWITicks = (32 + memoryWait[(reg[0].I>>24) & 0xF]) * len; - } - BIOS_BitUnPack(); - break; - case 0x11: - { - u32 len = CPUReadMemory(reg[0].I) >> 8; - if(!(((reg[0].I & 0xe000000) == 0) || - ((reg[0].I + (len & 0x1fffff)) & 0xe000000) == 0)) - SWITicks = (9 + memoryWait[(reg[1].I>>24) & 0xF]) * len; - } - BIOS_LZ77UnCompWram(); - break; - case 0x12: - { - u32 len = CPUReadMemory(reg[0].I) >> 8; - if(!(((reg[0].I & 0xe000000) == 0) || - ((reg[0].I + (len & 0x1fffff)) & 0xe000000) == 0)) - SWITicks = (19 + memoryWait[(reg[1].I>>24) & 0xF]) * len; - } - BIOS_LZ77UnCompVram(); - break; - case 0x13: - { - u32 len = CPUReadMemory(reg[0].I) >> 8; - if(!(((reg[0].I & 0xe000000) == 0) || - ((reg[0].I + (len & 0x1fffff)) & 0xe000000) == 0)) - SWITicks = (29 + (memoryWait[(reg[0].I>>24) & 0xF]<<1)) * len; - } - BIOS_HuffUnComp(); - break; - case 0x14: - { - u32 len = CPUReadMemory(reg[0].I) >> 8; - if(!(((reg[0].I & 0xe000000) == 0) || - ((reg[0].I + (len & 0x1fffff)) & 0xe000000) == 0)) - SWITicks = (11 + memoryWait[(reg[0].I>>24) & 0xF] + - memoryWait[(reg[1].I>>24) & 0xF]) * len; - } - BIOS_RLUnCompWram(); - break; - case 0x15: - { - u32 len = CPUReadMemory(reg[0].I) >> 9; - if(!(((reg[0].I & 0xe000000) == 0) || - ((reg[0].I + (len & 0x1fffff)) & 0xe000000) == 0)) - SWITicks = (34 + (memoryWait[(reg[0].I>>24) & 0xF] << 1) + - memoryWait[(reg[1].I>>24) & 0xF]) * len; - } - BIOS_RLUnCompVram(); - break; - case 0x16: - { - u32 len = CPUReadMemory(reg[0].I) >> 8; - if(!(((reg[0].I & 0xe000000) == 0) || - ((reg[0].I + (len & 0x1fffff)) & 0xe000000) == 0)) - SWITicks = (13 + memoryWait[(reg[0].I>>24) & 0xF] + - memoryWait[(reg[1].I>>24) & 0xF]) * len; - } - BIOS_Diff8bitUnFilterWram(); - break; - case 0x17: - { - u32 len = CPUReadMemory(reg[0].I) >> 9; - if(!(((reg[0].I & 0xe000000) == 0) || - ((reg[0].I + (len & 0x1fffff)) & 0xe000000) == 0)) - SWITicks = (39 + (memoryWait[(reg[0].I>>24) & 0xF]<<1) + - memoryWait[(reg[1].I>>24) & 0xF]) * len; - } - BIOS_Diff8bitUnFilterVram(); - break; - case 0x18: - { - u32 len = CPUReadMemory(reg[0].I) >> 9; - if(!(((reg[0].I & 0xe000000) == 0) || - ((reg[0].I + (len & 0x1fffff)) & 0xe000000) == 0)) - SWITicks = (13 + memoryWait[(reg[0].I>>24) & 0xF] + - memoryWait[(reg[1].I>>24) & 0xF]) * len; - } - BIOS_Diff16bitUnFilter(); - break; - case 0x19: -#ifdef GBA_LOGGING - if(systemVerbose & VERBOSE_SWI) { - log("SoundBiasSet: 0x%08x (VCOUNT = %2d)\n", - reg[0].I, - VCOUNT); - } -#endif - if(reg[0].I) - soundPause(); - else - soundResume(); - break; - case 0x1F: - BIOS_MidiKey2Freq(); - break; - case 0x2A: - BIOS_SndDriverJmpTableCopy(); - // let it go, because we don't really emulate this function - default: -#ifdef GBA_LOGGING - if(systemVerbose & VERBOSE_SWI) { - log("SWI: %08x at %08x (0x%08x,0x%08x,0x%08x,VCOUNT = %2d)\n", comment, - armState ? armNextPC - 4: armNextPC -2, - reg[0].I, - reg[1].I, - reg[2].I, - VCOUNT); - } -#endif - - if(!disableMessage) { - systemMessage(MSG_UNSUPPORTED_BIOS_FUNCTION, - N_("Unsupported BIOS function %02x called from %08x. A BIOS file is needed in order to get correct behaviour."), - comment, - armMode ? armNextPC - 4: armNextPC - 2); - disableMessage = true; - } - break; - } -} - -void CPUCompareVCOUNT() -{ - if(VCOUNT == (DISPSTAT >> 8)) { - DISPSTAT |= 4; - UPDATE_REG(0x04, DISPSTAT); - - if(DISPSTAT & 0x20) { - IF |= 4; - UPDATE_REG(0x202, IF); - } - } else { - DISPSTAT &= 0xFFFB; - UPDATE_REG(0x4, DISPSTAT); - } - if (layerEnableDelay>0) - { - layerEnableDelay--; - if (layerEnableDelay==1) - layerEnable = layerSettings & DISPCNT; - } - -} - -void doDMA(u32 &s, u32 &d, u32 si, u32 di, u32 c, int transfer32) -{ - int sm = s >> 24; - int dm = d >> 24; - int sw = 0; - int dw = 0; - int sc = c; - - cpuDmaCount = c; - // This is done to get the correct waitstates. - if (sm>15) - sm=15; - if (dm>15) - dm=15; - - //if ((sm>=0x05) && (sm<=0x07) || (dm>=0x05) && (dm <=0x07)) - // blank = (((DISPSTAT | ((DISPSTAT>>1)&1))==1) ? true : false); - - if(transfer32) { - s &= 0xFFFFFFFC; - if(s < 0x02000000 && (reg[15].I >> 24)) { - while(c != 0) { - CPUWriteMemory(d, 0); - d += di; - c--; - } - } else { - while(c != 0) { - cpuDmaLast = CPUReadMemory(s); - CPUWriteMemory(d, cpuDmaLast); - d += di; - s += si; - c--; - } - } - } else { - s &= 0xFFFFFFFE; - si = (int)si >> 1; - di = (int)di >> 1; - if(s < 0x02000000 && (reg[15].I >> 24)) { - while(c != 0) { - CPUWriteHalfWord(d, 0); - d += di; - c--; - } - } else { - while(c != 0) { - cpuDmaLast = CPUReadHalfWord(s); - CPUWriteHalfWord(d, cpuDmaLast); - cpuDmaLast |= (cpuDmaLast<<16); - d += di; - s += si; - c--; - } - } - } - - cpuDmaCount = 0; - - int totalTicks = 0; - - if(transfer32) { - sw =1+memoryWaitSeq32[sm & 15]; - dw =1+memoryWaitSeq32[dm & 15]; - totalTicks = (sw+dw)*(sc-1) + 6 + memoryWait32[sm & 15] + - memoryWaitSeq32[dm & 15]; - } - else - { - sw = 1+memoryWaitSeq[sm & 15]; - dw = 1+memoryWaitSeq[dm & 15]; - totalTicks = (sw+dw)*(sc-1) + 6 + memoryWait[sm & 15] + - memoryWaitSeq[dm & 15]; - } - - cpuDmaTicksToUpdate += totalTicks; - -} - -void CPUCheckDMA(int reason, int dmamask) -{ - // DMA 0 - if((DM0CNT_H & 0x8000) && (dmamask & 1)) { - if(((DM0CNT_H >> 12) & 3) == reason) { - u32 sourceIncrement = 4; - u32 destIncrement = 4; - switch((DM0CNT_H >> 7) & 3) { - case 0: - break; - case 1: - sourceIncrement = (u32)-4; - break; - case 2: - sourceIncrement = 0; - break; - } - switch((DM0CNT_H >> 5) & 3) { - case 0: - break; - case 1: - destIncrement = (u32)-4; - break; - case 2: - destIncrement = 0; - break; - } -#ifdef GBA_LOGGING - if(systemVerbose & VERBOSE_DMA0) { - int count = (DM0CNT_L ? DM0CNT_L : 0x4000) << 1; - if(DM0CNT_H & 0x0400) - count <<= 1; - log("DMA0: s=%08x d=%08x c=%04x count=%08x\n", dma0Source, dma0Dest, - DM0CNT_H, - count); - } -#endif - doDMA(dma0Source, dma0Dest, sourceIncrement, destIncrement, - DM0CNT_L ? DM0CNT_L : 0x4000, - DM0CNT_H & 0x0400); - cpuDmaHack = true; - - if(DM0CNT_H & 0x4000) { - IF |= 0x0100; - UPDATE_REG(0x202, IF); - cpuNextEvent = cpuTotalTicks; - } - - if(((DM0CNT_H >> 5) & 3) == 3) { - dma0Dest = DM0DAD_L | (DM0DAD_H << 16); - } - - if(!(DM0CNT_H & 0x0200) || (reason == 0)) { - DM0CNT_H &= 0x7FFF; - UPDATE_REG(0xBA, DM0CNT_H); - } - } - } - - // DMA 1 - if((DM1CNT_H & 0x8000) && (dmamask & 2)) { - if(((DM1CNT_H >> 12) & 3) == reason) { - u32 sourceIncrement = 4; - u32 destIncrement = 4; - switch((DM1CNT_H >> 7) & 3) { - case 0: - break; - case 1: - sourceIncrement = (u32)-4; - break; - case 2: - sourceIncrement = 0; - break; - } - switch((DM1CNT_H >> 5) & 3) { - case 0: - break; - case 1: - destIncrement = (u32)-4; - break; - case 2: - destIncrement = 0; - break; - } - if(reason == 3) { -#ifdef GBA_LOGGING - if(systemVerbose & VERBOSE_DMA1) { - log("DMA1: s=%08x d=%08x c=%04x count=%08x\n", dma1Source, dma1Dest, - DM1CNT_H, - 16); - } -#endif - doDMA(dma1Source, dma1Dest, sourceIncrement, 0, 4, - 0x0400); - } else { -#ifdef GBA_LOGGING - if(systemVerbose & VERBOSE_DMA1) { - int count = (DM1CNT_L ? DM1CNT_L : 0x4000) << 1; - if(DM1CNT_H & 0x0400) - count <<= 1; - log("DMA1: s=%08x d=%08x c=%04x count=%08x\n", dma1Source, dma1Dest, - DM1CNT_H, - count); - } -#endif - doDMA(dma1Source, dma1Dest, sourceIncrement, destIncrement, - DM1CNT_L ? DM1CNT_L : 0x4000, - DM1CNT_H & 0x0400); - } - cpuDmaHack = true; - - if(DM1CNT_H & 0x4000) { - IF |= 0x0200; - UPDATE_REG(0x202, IF); - cpuNextEvent = cpuTotalTicks; - } - - if(((DM1CNT_H >> 5) & 3) == 3) { - dma1Dest = DM1DAD_L | (DM1DAD_H << 16); - } - - if(!(DM1CNT_H & 0x0200) || (reason == 0)) { - DM1CNT_H &= 0x7FFF; - UPDATE_REG(0xC6, DM1CNT_H); - } - } - } - - // DMA 2 - if((DM2CNT_H & 0x8000) && (dmamask & 4)) { - if(((DM2CNT_H >> 12) & 3) == reason) { - u32 sourceIncrement = 4; - u32 destIncrement = 4; - switch((DM2CNT_H >> 7) & 3) { - case 0: - break; - case 1: - sourceIncrement = (u32)-4; - break; - case 2: - sourceIncrement = 0; - break; - } - switch((DM2CNT_H >> 5) & 3) { - case 0: - break; - case 1: - destIncrement = (u32)-4; - break; - case 2: - destIncrement = 0; - break; - } - if(reason == 3) { -#ifdef GBA_LOGGING - if(systemVerbose & VERBOSE_DMA2) { - int count = (4) << 2; - log("DMA2: s=%08x d=%08x c=%04x count=%08x\n", dma2Source, dma2Dest, - DM2CNT_H, - count); - } -#endif - doDMA(dma2Source, dma2Dest, sourceIncrement, 0, 4, - 0x0400); - } else { -#ifdef GBA_LOGGING - if(systemVerbose & VERBOSE_DMA2) { - int count = (DM2CNT_L ? DM2CNT_L : 0x4000) << 1; - if(DM2CNT_H & 0x0400) - count <<= 1; - log("DMA2: s=%08x d=%08x c=%04x count=%08x\n", dma2Source, dma2Dest, - DM2CNT_H, - count); - } -#endif - doDMA(dma2Source, dma2Dest, sourceIncrement, destIncrement, - DM2CNT_L ? DM2CNT_L : 0x4000, - DM2CNT_H & 0x0400); - } - cpuDmaHack = true; - - if(DM2CNT_H & 0x4000) { - IF |= 0x0400; - UPDATE_REG(0x202, IF); - cpuNextEvent = cpuTotalTicks; - } - - if(((DM2CNT_H >> 5) & 3) == 3) { - dma2Dest = DM2DAD_L | (DM2DAD_H << 16); - } - - if(!(DM2CNT_H & 0x0200) || (reason == 0)) { - DM2CNT_H &= 0x7FFF; - UPDATE_REG(0xD2, DM2CNT_H); - } - } - } - - // DMA 3 - if((DM3CNT_H & 0x8000) && (dmamask & 8)) { - if(((DM3CNT_H >> 12) & 3) == reason) { - u32 sourceIncrement = 4; - u32 destIncrement = 4; - switch((DM3CNT_H >> 7) & 3) { - case 0: - break; - case 1: - sourceIncrement = (u32)-4; - break; - case 2: - sourceIncrement = 0; - break; - } - switch((DM3CNT_H >> 5) & 3) { - case 0: - break; - case 1: - destIncrement = (u32)-4; - break; - case 2: - destIncrement = 0; - break; - } -#ifdef GBA_LOGGING - if(systemVerbose & VERBOSE_DMA3) { - int count = (DM3CNT_L ? DM3CNT_L : 0x10000) << 1; - if(DM3CNT_H & 0x0400) - count <<= 1; - log("DMA3: s=%08x d=%08x c=%04x count=%08x\n", dma3Source, dma3Dest, - DM3CNT_H, - count); - } -#endif - doDMA(dma3Source, dma3Dest, sourceIncrement, destIncrement, - DM3CNT_L ? DM3CNT_L : 0x10000, - DM3CNT_H & 0x0400); - if(DM3CNT_H & 0x4000) { - IF |= 0x0800; - UPDATE_REG(0x202, IF); - cpuNextEvent = cpuTotalTicks; - } - - if(((DM3CNT_H >> 5) & 3) == 3) { - dma3Dest = DM3DAD_L | (DM3DAD_H << 16); - } - - if(!(DM3CNT_H & 0x0200) || (reason == 0)) { - DM3CNT_H &= 0x7FFF; - UPDATE_REG(0xDE, DM3CNT_H); - } - } - } -} - -void CPUUpdateRegister(u32 address, u16 value) -{ - switch(address) - { - case 0x00: - { // we need to place the following code in { } because we declare & initialize variables in a case statement - if((value & 7) > 5) { - // display modes above 0-5 are prohibited - DISPCNT = (value & 7); - } - bool change = (0 != ((DISPCNT ^ value) & 0x80)); - bool changeBG = (0 != ((DISPCNT ^ value) & 0x0F00)); - u16 changeBGon = ((~DISPCNT) & value) & 0x0F00; // these layers are being activated - - DISPCNT = (value & 0xFFF7); // bit 3 can only be accessed by the BIOS to enable GBC mode - UPDATE_REG(0x00, DISPCNT); - - if(changeBGon) { - layerEnableDelay = 4; - layerEnable = layerSettings & value & (~changeBGon); - } else { - layerEnable = layerSettings & value; - // CPUUpdateTicks(); - } - - windowOn = (layerEnable & 0x6000) ? true : false; - if(change && !((value & 0x80))) { - if(!(DISPSTAT & 1)) { - lcdTicks = 1008; - // VCOUNT = 0; - // UPDATE_REG(0x06, VCOUNT); - DISPSTAT &= 0xFFFC; - UPDATE_REG(0x04, DISPSTAT); - CPUCompareVCOUNT(); - } - // (*renderLine)(); - } - CPUUpdateRender(); - // we only care about changes in BG0-BG3 - if(changeBG) { - CPUUpdateRenderBuffers(false); - } - break; - } - case 0x04: - DISPSTAT = (value & 0xFF38) | (DISPSTAT & 7); - UPDATE_REG(0x04, DISPSTAT); - break; - case 0x06: - // not writable - break; - case 0x08: - BG0CNT = (value & 0xDFCF); - UPDATE_REG(0x08, BG0CNT); - break; - case 0x0A: - BG1CNT = (value & 0xDFCF); - UPDATE_REG(0x0A, BG1CNT); - break; - case 0x0C: - BG2CNT = (value & 0xFFCF); - UPDATE_REG(0x0C, BG2CNT); - break; - case 0x0E: - BG3CNT = (value & 0xFFCF); - UPDATE_REG(0x0E, BG3CNT); - break; - case 0x10: - BG0HOFS = value & 511; - UPDATE_REG(0x10, BG0HOFS); - break; - case 0x12: - BG0VOFS = value & 511; - UPDATE_REG(0x12, BG0VOFS); - break; - case 0x14: - BG1HOFS = value & 511; - UPDATE_REG(0x14, BG1HOFS); - break; - case 0x16: - BG1VOFS = value & 511; - UPDATE_REG(0x16, BG1VOFS); - break; - case 0x18: - BG2HOFS = value & 511; - UPDATE_REG(0x18, BG2HOFS); - break; - case 0x1A: - BG2VOFS = value & 511; - UPDATE_REG(0x1A, BG2VOFS); - break; - case 0x1C: - BG3HOFS = value & 511; - UPDATE_REG(0x1C, BG3HOFS); - break; - case 0x1E: - BG3VOFS = value & 511; - UPDATE_REG(0x1E, BG3VOFS); - break; - case 0x20: - BG2PA = value; - UPDATE_REG(0x20, BG2PA); - break; - case 0x22: - BG2PB = value; - UPDATE_REG(0x22, BG2PB); - break; - case 0x24: - BG2PC = value; - UPDATE_REG(0x24, BG2PC); - break; - case 0x26: - BG2PD = value; - UPDATE_REG(0x26, BG2PD); - break; - case 0x28: - BG2X_L = value; - UPDATE_REG(0x28, BG2X_L); - gfxBG2Changed |= 1; - break; - case 0x2A: - BG2X_H = (value & 0xFFF); - UPDATE_REG(0x2A, BG2X_H); - gfxBG2Changed |= 1; - break; - case 0x2C: - BG2Y_L = value; - UPDATE_REG(0x2C, BG2Y_L); - gfxBG2Changed |= 2; - break; - case 0x2E: - BG2Y_H = value & 0xFFF; - UPDATE_REG(0x2E, BG2Y_H); - gfxBG2Changed |= 2; - break; - case 0x30: - BG3PA = value; - UPDATE_REG(0x30, BG3PA); - break; - case 0x32: - BG3PB = value; - UPDATE_REG(0x32, BG3PB); - break; - case 0x34: - BG3PC = value; - UPDATE_REG(0x34, BG3PC); - break; - case 0x36: - BG3PD = value; - UPDATE_REG(0x36, BG3PD); - break; - case 0x38: - BG3X_L = value; - UPDATE_REG(0x38, BG3X_L); - gfxBG3Changed |= 1; - break; - case 0x3A: - BG3X_H = value & 0xFFF; - UPDATE_REG(0x3A, BG3X_H); - gfxBG3Changed |= 1; - break; - case 0x3C: - BG3Y_L = value; - UPDATE_REG(0x3C, BG3Y_L); - gfxBG3Changed |= 2; - break; - case 0x3E: - BG3Y_H = value & 0xFFF; - UPDATE_REG(0x3E, BG3Y_H); - gfxBG3Changed |= 2; - break; - case 0x40: - WIN0H = value; - UPDATE_REG(0x40, WIN0H); - CPUUpdateWindow0(); - break; - case 0x42: - WIN1H = value; - UPDATE_REG(0x42, WIN1H); - CPUUpdateWindow1(); - break; - case 0x44: - WIN0V = value; - UPDATE_REG(0x44, WIN0V); - break; - case 0x46: - WIN1V = value; - UPDATE_REG(0x46, WIN1V); - break; - case 0x48: - WININ = value & 0x3F3F; - UPDATE_REG(0x48, WININ); - break; - case 0x4A: - WINOUT = value & 0x3F3F; - UPDATE_REG(0x4A, WINOUT); - break; - case 0x4C: - MOSAIC = value; - UPDATE_REG(0x4C, MOSAIC); - break; - case 0x50: - BLDMOD = value & 0x3FFF; - UPDATE_REG(0x50, BLDMOD); - fxOn = ((BLDMOD>>6)&3) != 0; - CPUUpdateRender(); - break; - case 0x52: - COLEV = value & 0x1F1F; - UPDATE_REG(0x52, COLEV); - break; - case 0x54: - COLY = value & 0x1F; - UPDATE_REG(0x54, COLY); - break; - case 0x60: - case 0x62: - case 0x64: - case 0x68: - case 0x6c: - case 0x70: - case 0x72: - case 0x74: - case 0x78: - case 0x7c: - case 0x80: - case 0x84: - soundEvent(address&0xFF, (u8)(value & 0xFF)); - soundEvent((address&0xFF)+1, (u8)(value>>8)); - break; - case 0x82: - case 0x88: - case 0xa0: - case 0xa2: - case 0xa4: - case 0xa6: - case 0x90: - case 0x92: - case 0x94: - case 0x96: - case 0x98: - case 0x9a: - case 0x9c: - case 0x9e: - soundEvent(address&0xFF, value); - break; - case 0xB0: - DM0SAD_L = value; - UPDATE_REG(0xB0, DM0SAD_L); - break; - case 0xB2: - DM0SAD_H = value & 0x07FF; - UPDATE_REG(0xB2, DM0SAD_H); - break; - case 0xB4: - DM0DAD_L = value; - UPDATE_REG(0xB4, DM0DAD_L); - break; - case 0xB6: - DM0DAD_H = value & 0x07FF; - UPDATE_REG(0xB6, DM0DAD_H); - break; - case 0xB8: - DM0CNT_L = value & 0x3FFF; - UPDATE_REG(0xB8, 0); - break; - case 0xBA: - { - bool start = ((DM0CNT_H ^ value) & 0x8000) ? true : false; - value &= 0xF7E0; - - DM0CNT_H = value; - UPDATE_REG(0xBA, DM0CNT_H); - - if(start && (value & 0x8000)) { - dma0Source = DM0SAD_L | (DM0SAD_H << 16); - dma0Dest = DM0DAD_L | (DM0DAD_H << 16); - CPUCheckDMA(0, 1); - } - } - break; - case 0xBC: - DM1SAD_L = value; - UPDATE_REG(0xBC, DM1SAD_L); - break; - case 0xBE: - DM1SAD_H = value & 0x0FFF; - UPDATE_REG(0xBE, DM1SAD_H); - break; - case 0xC0: - DM1DAD_L = value; - UPDATE_REG(0xC0, DM1DAD_L); - break; - case 0xC2: - DM1DAD_H = value & 0x07FF; - UPDATE_REG(0xC2, DM1DAD_H); - break; - case 0xC4: - DM1CNT_L = value & 0x3FFF; - UPDATE_REG(0xC4, 0); - break; - case 0xC6: - { - bool start = ((DM1CNT_H ^ value) & 0x8000) ? true : false; - value &= 0xF7E0; - - DM1CNT_H = value; - UPDATE_REG(0xC6, DM1CNT_H); - - if(start && (value & 0x8000)) { - dma1Source = DM1SAD_L | (DM1SAD_H << 16); - dma1Dest = DM1DAD_L | (DM1DAD_H << 16); - CPUCheckDMA(0, 2); - } - } - break; - case 0xC8: - DM2SAD_L = value; - UPDATE_REG(0xC8, DM2SAD_L); - break; - case 0xCA: - DM2SAD_H = value & 0x0FFF; - UPDATE_REG(0xCA, DM2SAD_H); - break; - case 0xCC: - DM2DAD_L = value; - UPDATE_REG(0xCC, DM2DAD_L); - break; - case 0xCE: - DM2DAD_H = value & 0x07FF; - UPDATE_REG(0xCE, DM2DAD_H); - break; - case 0xD0: - DM2CNT_L = value & 0x3FFF; - UPDATE_REG(0xD0, 0); - break; - case 0xD2: - { - bool start = ((DM2CNT_H ^ value) & 0x8000) ? true : false; - - value &= 0xF7E0; - - DM2CNT_H = value; - UPDATE_REG(0xD2, DM2CNT_H); - - if(start && (value & 0x8000)) { - dma2Source = DM2SAD_L | (DM2SAD_H << 16); - dma2Dest = DM2DAD_L | (DM2DAD_H << 16); - - CPUCheckDMA(0, 4); - } - } - break; - case 0xD4: - DM3SAD_L = value; - UPDATE_REG(0xD4, DM3SAD_L); - break; - case 0xD6: - DM3SAD_H = value & 0x0FFF; - UPDATE_REG(0xD6, DM3SAD_H); - break; - case 0xD8: - DM3DAD_L = value; - UPDATE_REG(0xD8, DM3DAD_L); - break; - case 0xDA: - DM3DAD_H = value & 0x0FFF; - UPDATE_REG(0xDA, DM3DAD_H); - break; - case 0xDC: - DM3CNT_L = value; - UPDATE_REG(0xDC, 0); - break; - case 0xDE: - { - bool start = ((DM3CNT_H ^ value) & 0x8000) ? true : false; - - value &= 0xFFE0; - - DM3CNT_H = value; - UPDATE_REG(0xDE, DM3CNT_H); - - if(start && (value & 0x8000)) { - dma3Source = DM3SAD_L | (DM3SAD_H << 16); - dma3Dest = DM3DAD_L | (DM3DAD_H << 16); - CPUCheckDMA(0,8); - } - } - break; - case 0x100: - timer0Reload = value; - interp_rate(); - break; - case 0x102: - timer0Value = value; - timerOnOffDelay|=1; - cpuNextEvent = cpuTotalTicks; - break; - case 0x104: - timer1Reload = value; - interp_rate(); - break; - case 0x106: - timer1Value = value; - timerOnOffDelay|=2; - cpuNextEvent = cpuTotalTicks; - break; - case 0x108: - timer2Reload = value; - break; - case 0x10A: - timer2Value = value; - timerOnOffDelay|=4; - cpuNextEvent = cpuTotalTicks; - break; - case 0x10C: - timer3Reload = value; - break; - case 0x10E: - timer3Value = value; - timerOnOffDelay|=8; - cpuNextEvent = cpuTotalTicks; - break; - - - case COMM_SIOCNT: - StartLink(value); - /* - // old code path for no linking... - { - if (value & 0x80) { - value &= 0xff7f; - if ((value & 1) && (value & 0x4000)) { - UPDATE_REG(COMM_SIODATA8, 0xFF); - IF |= 0x80; - UPDATE_REG(0x202, IF); - value &= 0x7f7f; - } - } - UPDATE_REG(COMM_SIOCNT, value); - } - */ - break; - - case COMM_SIODATA8: - if (gba_link_enabled) - LinkSSend(value); - UPDATE_REG(COMM_RCNT, value); - break; - - case 0x130: - P1 |= (value & 0x3FF); - UPDATE_REG(0x130, P1); - break; - - case 0x132: - UPDATE_REG(0x132, value & 0xC3FF); - break; - - case COMM_RCNT: - StartGPLink(value); - break; - - case COMM_JOYCNT: - { - u16 cur = READ16LE(&ioMem[COMM_JOYCNT]); - - if (value & JOYCNT_RESET) cur &= ~JOYCNT_RESET; - if (value & JOYCNT_RECV_COMPLETE) cur &= ~JOYCNT_RECV_COMPLETE; - if (value & JOYCNT_SEND_COMPLETE) cur &= ~JOYCNT_SEND_COMPLETE; - if (value & JOYCNT_INT_ENABLE) cur |= JOYCNT_INT_ENABLE; - - UPDATE_REG(COMM_JOYCNT, cur); - } - break; - - case COMM_JOY_RECV_L: - UPDATE_REG(COMM_JOY_RECV_L, value); - break; - case COMM_JOY_RECV_H: - UPDATE_REG(COMM_JOY_RECV_H, value); - break; - - case COMM_JOY_TRANS_L: - UPDATE_REG(COMM_JOY_TRANS_L, value); - UPDATE_REG(COMM_JOYSTAT, READ16LE(&ioMem[COMM_JOYSTAT]) | JOYSTAT_SEND); - break; - case COMM_JOY_TRANS_H: - UPDATE_REG(COMM_JOY_TRANS_H, value); - break; - - case COMM_JOYSTAT: - UPDATE_REG(COMM_JOYSTAT, (READ16LE(&ioMem[COMM_JOYSTAT]) & 0xf) | (value & 0xf0)); - break; - - case 0x200: - IE = value & 0x3FFF; - UPDATE_REG(0x200, IE); - if ((IME & 1) && (IF & IE) && armIrqEnable) - cpuNextEvent = cpuTotalTicks; - break; - case 0x202: - IF ^= (value & IF); - UPDATE_REG(0x202, IF); - break; - case 0x204: - { - memoryWait[0x0e] = memoryWaitSeq[0x0e] = gamepakRamWaitState[value & 3]; - - if(!speedHack) { - memoryWait[0x08] = memoryWait[0x09] = gamepakWaitState[(value >> 2) & 3]; - memoryWaitSeq[0x08] = memoryWaitSeq[0x09] = - gamepakWaitState0[(value >> 4) & 1]; - - memoryWait[0x0a] = memoryWait[0x0b] = gamepakWaitState[(value >> 5) & 3]; - memoryWaitSeq[0x0a] = memoryWaitSeq[0x0b] = - gamepakWaitState1[(value >> 7) & 1]; - - memoryWait[0x0c] = memoryWait[0x0d] = gamepakWaitState[(value >> 8) & 3]; - memoryWaitSeq[0x0c] = memoryWaitSeq[0x0d] = - gamepakWaitState2[(value >> 10) & 1]; - } else { - memoryWait[0x08] = memoryWait[0x09] = 3; - memoryWaitSeq[0x08] = memoryWaitSeq[0x09] = 1; - - memoryWait[0x0a] = memoryWait[0x0b] = 3; - memoryWaitSeq[0x0a] = memoryWaitSeq[0x0b] = 1; - - memoryWait[0x0c] = memoryWait[0x0d] = 3; - memoryWaitSeq[0x0c] = memoryWaitSeq[0x0d] = 1; - } - - for(int i = 8; i < 15; i++) { - memoryWait32[i] = memoryWait[i] + memoryWaitSeq[i] + 1; - memoryWaitSeq32[i] = memoryWaitSeq[i]*2 + 1; - } - - if((value & 0x4000) == 0x4000) { - busPrefetchEnable = true; - busPrefetch = false; - busPrefetchCount = 0; - } else { - busPrefetchEnable = false; - busPrefetch = false; - busPrefetchCount = 0; - } - UPDATE_REG(0x204, value & 0x7FFF); - - } - break; - case 0x208: - IME = value & 1; - UPDATE_REG(0x208, IME); - if ((IME & 1) && (IF & IE) && armIrqEnable) - cpuNextEvent = cpuTotalTicks; - break; - case 0x300: - if(value != 0) - value &= 0xFFFE; - UPDATE_REG(0x300, value); - break; - default: - UPDATE_REG(address&0x3FE, value); - break; - } -} - -void applyTimer () -{ - if (timerOnOffDelay & 1) - { - timer0ClockReload = TIMER_TICKS[timer0Value & 3]; - if(!timer0On && (timer0Value & 0x80)) { - // reload the counter - TM0D = timer0Reload; - timer0Ticks = (0x10000 - TM0D) << timer0ClockReload; - UPDATE_REG(0x100, TM0D); - } - timer0On = timer0Value & 0x80 ? true : false; - TM0CNT = timer0Value & 0xC7; - interp_rate(); - UPDATE_REG(0x102, TM0CNT); - // CPUUpdateTicks(); - } - if (timerOnOffDelay & 2) - { - timer1ClockReload = TIMER_TICKS[timer1Value & 3]; - if(!timer1On && (timer1Value & 0x80)) { - // reload the counter - TM1D = timer1Reload; - timer1Ticks = (0x10000 - TM1D) << timer1ClockReload; - UPDATE_REG(0x104, TM1D); - } - timer1On = timer1Value & 0x80 ? true : false; - TM1CNT = timer1Value & 0xC7; - interp_rate(); - UPDATE_REG(0x106, TM1CNT); - } - if (timerOnOffDelay & 4) - { - timer2ClockReload = TIMER_TICKS[timer2Value & 3]; - if(!timer2On && (timer2Value & 0x80)) { - // reload the counter - TM2D = timer2Reload; - timer2Ticks = (0x10000 - TM2D) << timer2ClockReload; - UPDATE_REG(0x108, TM2D); - } - timer2On = timer2Value & 0x80 ? true : false; - TM2CNT = timer2Value & 0xC7; - UPDATE_REG(0x10A, TM2CNT); - } - if (timerOnOffDelay & 8) - { - timer3ClockReload = TIMER_TICKS[timer3Value & 3]; - if(!timer3On && (timer3Value & 0x80)) { - // reload the counter - TM3D = timer3Reload; - timer3Ticks = (0x10000 - TM3D) << timer3ClockReload; - UPDATE_REG(0x10C, TM3D); - } - timer3On = timer3Value & 0x80 ? true : false; - TM3CNT = timer3Value & 0xC7; - UPDATE_REG(0x10E, TM3CNT); - } - cpuNextEvent = CPUUpdateTicks(); - timerOnOffDelay = 0; -} - -u8 cpuBitsSet[256]; -u8 cpuLowestBitSet[256]; - -void CPUInit(const char *biosFileName, bool useBiosFile) -{ -#ifdef WORDS_BIGENDIAN - if(!cpuBiosSwapped) { - for(unsigned int i = 0; i < sizeof(myROM)/4; i++) { - WRITE32LE(&myROM[i], myROM[i]); - } - cpuBiosSwapped = true; - } -#endif - gbaSaveType = 0; - eepromInUse = 0; - saveType = 0; - useBios = false; - - if(useBiosFile) { - int size = 0x4000; - if(utilLoad(biosFileName, - CPUIsGBABios, - bios, - size)) { - if(size == 0x4000) - useBios = true; - else - systemMessage(MSG_INVALID_BIOS_FILE_SIZE, N_("Invalid BIOS file size")); - } - } - - if(!useBios) { - memcpy(bios, myROM, sizeof(myROM)); - } - - int i = 0; - - biosProtected[0] = 0x00; - biosProtected[1] = 0xf0; - biosProtected[2] = 0x29; - biosProtected[3] = 0xe1; - - for(i = 0; i < 256; i++) { - int count = 0; - int j; - for(j = 0; j < 8; j++) - if(i & (1 << j)) - count++; - cpuBitsSet[i] = count; - - for(j = 0; j < 8; j++) - if(i & (1 << j)) - break; - cpuLowestBitSet[i] = j; - } - - for(i = 0; i < 0x400; i++) - ioReadable[i] = true; - for(i = 0x10; i < 0x48; i++) - ioReadable[i] = false; - for(i = 0x4c; i < 0x50; i++) - ioReadable[i] = false; - for(i = 0x54; i < 0x60; i++) - ioReadable[i] = false; - for(i = 0x8c; i < 0x90; i++) - ioReadable[i] = false; - for(i = 0xa0; i < 0xb8; i++) - ioReadable[i] = false; - for(i = 0xbc; i < 0xc4; i++) - ioReadable[i] = false; - for(i = 0xc8; i < 0xd0; i++) - ioReadable[i] = false; - for(i = 0xd4; i < 0xdc; i++) - ioReadable[i] = false; - for(i = 0xe0; i < 0x100; i++) - ioReadable[i] = false; - for(i = 0x110; i < 0x120; i++) - ioReadable[i] = false; - for(i = 0x12c; i < 0x130; i++) - ioReadable[i] = false; - for(i = 0x138; i < 0x140; i++) - ioReadable[i] = false; - for(i = 0x144; i < 0x150; i++) - ioReadable[i] = false; - for(i = 0x15c; i < 0x200; i++) - ioReadable[i] = false; - for(i = 0x20c; i < 0x300; i++) - ioReadable[i] = false; - for(i = 0x304; i < 0x400; i++) - ioReadable[i] = false; - - if(romSize < 0x1fe2000) { - *((u16 *)&rom[0x1fe209c]) = 0xdffa; // SWI 0xFA - *((u16 *)&rom[0x1fe209e]) = 0x4770; // BX LR - } else { - agbPrintEnable(false); - } -} - -void CPUReset() -{ - if(gbaSaveType == 0) { - if(eepromInUse) - gbaSaveType = 3; - else - switch(saveType) { - case 1: - gbaSaveType = 1; - break; - case 2: - gbaSaveType = 2; - break; - } - } - rtcReset(); - // clean registers - memset(®[0], 0, sizeof(reg)); - // clean OAM - memset(oam, 0, 0x400); - // clean palette - memset(paletteRAM, 0, 0x400); - // clean picture - memset(pix, 0, 4*160*240); - // clean vram - memset(vram, 0, 0x20000); - // clean io memory - memset(ioMem, 0, 0x400); - - DISPCNT = 0x0080; - DISPSTAT = 0x0000; - VCOUNT = (useBios && !skipBios) ? 0 :0x007E; - BG0CNT = 0x0000; - BG1CNT = 0x0000; - BG2CNT = 0x0000; - BG3CNT = 0x0000; - BG0HOFS = 0x0000; - BG0VOFS = 0x0000; - BG1HOFS = 0x0000; - BG1VOFS = 0x0000; - BG2HOFS = 0x0000; - BG2VOFS = 0x0000; - BG3HOFS = 0x0000; - BG3VOFS = 0x0000; - BG2PA = 0x0100; - BG2PB = 0x0000; - BG2PC = 0x0000; - BG2PD = 0x0100; - BG2X_L = 0x0000; - BG2X_H = 0x0000; - BG2Y_L = 0x0000; - BG2Y_H = 0x0000; - BG3PA = 0x0100; - BG3PB = 0x0000; - BG3PC = 0x0000; - BG3PD = 0x0100; - BG3X_L = 0x0000; - BG3X_H = 0x0000; - BG3Y_L = 0x0000; - BG3Y_H = 0x0000; - WIN0H = 0x0000; - WIN1H = 0x0000; - WIN0V = 0x0000; - WIN1V = 0x0000; - WININ = 0x0000; - WINOUT = 0x0000; - MOSAIC = 0x0000; - BLDMOD = 0x0000; - COLEV = 0x0000; - COLY = 0x0000; - DM0SAD_L = 0x0000; - DM0SAD_H = 0x0000; - DM0DAD_L = 0x0000; - DM0DAD_H = 0x0000; - DM0CNT_L = 0x0000; - DM0CNT_H = 0x0000; - DM1SAD_L = 0x0000; - DM1SAD_H = 0x0000; - DM1DAD_L = 0x0000; - DM1DAD_H = 0x0000; - DM1CNT_L = 0x0000; - DM1CNT_H = 0x0000; - DM2SAD_L = 0x0000; - DM2SAD_H = 0x0000; - DM2DAD_L = 0x0000; - DM2DAD_H = 0x0000; - DM2CNT_L = 0x0000; - DM2CNT_H = 0x0000; - DM3SAD_L = 0x0000; - DM3SAD_H = 0x0000; - DM3DAD_L = 0x0000; - DM3DAD_H = 0x0000; - DM3CNT_L = 0x0000; - DM3CNT_H = 0x0000; - TM0D = 0x0000; - TM0CNT = 0x0000; - TM1D = 0x0000; - TM1CNT = 0x0000; - TM2D = 0x0000; - TM2CNT = 0x0000; - TM3D = 0x0000; - TM3CNT = 0x0000; - P1 = 0x03FF; - IE = 0x0000; - IF = 0x0000; - IME = 0x0000; - - armMode = 0x1F; - - if(cpuIsMultiBoot) { - reg[13].I = 0x03007F00; - reg[15].I = 0x02000000; - reg[16].I = 0x00000000; - reg[R13_IRQ].I = 0x03007FA0; - reg[R13_SVC].I = 0x03007FE0; - armIrqEnable = true; - } else { - if(useBios && !skipBios) { - reg[15].I = 0x00000000; - armMode = 0x13; - armIrqEnable = false; - } else { - reg[13].I = 0x03007F00; - reg[15].I = 0x08000000; - reg[16].I = 0x00000000; - reg[R13_IRQ].I = 0x03007FA0; - reg[R13_SVC].I = 0x03007FE0; - armIrqEnable = true; - } - } - armState = true; - C_FLAG = V_FLAG = N_FLAG = Z_FLAG = false; - UPDATE_REG(0x00, DISPCNT); - UPDATE_REG(0x06, VCOUNT); - UPDATE_REG(0x20, BG2PA); - UPDATE_REG(0x26, BG2PD); - UPDATE_REG(0x30, BG3PA); - UPDATE_REG(0x36, BG3PD); - UPDATE_REG(0x130, P1); - UPDATE_REG(0x88, 0x200); - - // disable FIQ - reg[16].I |= 0x40; - - CPUUpdateCPSR(); - - armNextPC = reg[15].I; - reg[15].I += 4; - - // reset internal state - holdState = false; - holdType = 0; - - biosProtected[0] = 0x00; - biosProtected[1] = 0xf0; - biosProtected[2] = 0x29; - biosProtected[3] = 0xe1; - - lcdTicks = (useBios && !skipBios) ? 1008 : 208; - timer0On = false; - timer0Ticks = 0; - timer0Reload = 0; - timer0ClockReload = 0; - timer1On = false; - timer1Ticks = 0; - timer1Reload = 0; - timer1ClockReload = 0; - timer2On = false; - timer2Ticks = 0; - timer2Reload = 0; - timer2ClockReload = 0; - timer3On = false; - timer3Ticks = 0; - timer3Reload = 0; - timer3ClockReload = 0; - dma0Source = 0; - dma0Dest = 0; - dma1Source = 0; - dma1Dest = 0; - dma2Source = 0; - dma2Dest = 0; - dma3Source = 0; - dma3Dest = 0; - cpuSaveGameFunc = flashSaveDecide; - renderLine = mode0RenderLine; - fxOn = false; - windowOn = false; - frameCount = 0; - saveType = 0; - layerEnable = DISPCNT & layerSettings; - - CPUUpdateRenderBuffers(true); - - for(int i = 0; i < 256; i++) { - map[i].address = (u8 *)&dummyAddress; - map[i].mask = 0; - } - - map[0].address = bios; - map[0].mask = 0x3FFF; - map[2].address = workRAM; - map[2].mask = 0x3FFFF; - map[3].address = internalRAM; - map[3].mask = 0x7FFF; - map[4].address = ioMem; - map[4].mask = 0x3FF; - map[5].address = paletteRAM; - map[5].mask = 0x3FF; - map[6].address = vram; - map[6].mask = 0x1FFFF; - map[7].address = oam; - map[7].mask = 0x3FF; - map[8].address = rom; - map[8].mask = 0x1FFFFFF; - map[9].address = rom; - map[9].mask = 0x1FFFFFF; - map[10].address = rom; - map[10].mask = 0x1FFFFFF; - map[12].address = rom; - map[12].mask = 0x1FFFFFF; - map[14].address = flashSaveMemory; - map[14].mask = 0xFFFF; - - eepromReset(); - flashReset(); - - soundReset(); - - CPUUpdateWindow0(); - CPUUpdateWindow1(); - - // make sure registers are correctly initialized if not using BIOS - if(!useBios) { - if(cpuIsMultiBoot) - BIOS_RegisterRamReset(0xfe); - else - BIOS_RegisterRamReset(0xff); - } else { - if(cpuIsMultiBoot) - BIOS_RegisterRamReset(0xfe); - } - - switch(cpuSaveType) { - case 0: // automatic - cpuSramEnabled = true; - cpuFlashEnabled = true; - cpuEEPROMEnabled = true; - cpuEEPROMSensorEnabled = false; - saveType = gbaSaveType = 0; - break; - case 1: // EEPROM - cpuSramEnabled = false; - cpuFlashEnabled = false; - cpuEEPROMEnabled = true; - cpuEEPROMSensorEnabled = false; - saveType = gbaSaveType = 3; - // EEPROM usage is automatically detected - break; - case 2: // SRAM - cpuSramEnabled = true; - cpuFlashEnabled = false; - cpuEEPROMEnabled = false; - cpuEEPROMSensorEnabled = false; - cpuSaveGameFunc = sramDelayedWrite; // to insure we detect the write - saveType = gbaSaveType = 1; - break; - case 3: // FLASH - cpuSramEnabled = false; - cpuFlashEnabled = true; - cpuEEPROMEnabled = false; - cpuEEPROMSensorEnabled = false; - cpuSaveGameFunc = flashDelayedWrite; // to insure we detect the write - saveType = gbaSaveType = 2; - break; - case 4: // EEPROM+Sensor - cpuSramEnabled = false; - cpuFlashEnabled = false; - cpuEEPROMEnabled = true; - cpuEEPROMSensorEnabled = true; - // EEPROM usage is automatically detected - saveType = gbaSaveType = 3; - break; - case 5: // NONE - cpuSramEnabled = false; - cpuFlashEnabled = false; - cpuEEPROMEnabled = false; - cpuEEPROMSensorEnabled = false; - // no save at all - saveType = gbaSaveType = 5; - break; - } - - ARM_PREFETCH; - - systemSaveUpdateCounter = SYSTEM_SAVE_NOT_UPDATED; - - cpuDmaHack = false; - - lastTime = systemGetClock(); - - SWITicks = 0; -} - -void CPUInterrupt() -{ - u32 PC = reg[15].I; - bool savedState = armState; - CPUSwitchMode(0x12, true, false); - reg[14].I = PC; - if(!savedState) - reg[14].I += 2; - reg[15].I = 0x18; - armState = true; - armIrqEnable = false; - - armNextPC = reg[15].I; - reg[15].I += 4; - ARM_PREFETCH; - - // if(!holdState) - biosProtected[0] = 0x02; - biosProtected[1] = 0xc0; - biosProtected[2] = 0x5e; - biosProtected[3] = 0xe5; -} - -#ifdef SDL -void log(const char *defaultMsg, ...) -{ - char buffer[2048]; - va_list valist; - - va_start(valist, defaultMsg); - vsprintf(buffer, defaultMsg, valist); - - if(out == NULL) { - out = fopen("trace.log","w"); - } - - fputs(buffer, out); - - va_end(valist); -} -#else -extern void winlog(const char *, ...); -#endif - -void CPULoop(int ticks) -{ - int clockTicks; - int timerOverflow = 0; - // variable used by the CPU core - cpuTotalTicks = 0; - - // shuffle2: what's the purpose? - if(gba_link_enabled) - cpuNextEvent = 1; - - cpuBreakLoop = false; - cpuNextEvent = CPUUpdateTicks(); - if(cpuNextEvent > ticks) - cpuNextEvent = ticks; - - - for(;;) { -#ifndef FINAL_VERSION - if(systemDebug) { - if(systemDebug >= 10 && !holdState) { - CPUUpdateCPSR(); -#ifdef BKPT_SUPPORT - if (debugger_last) - { - sprintf(buffer, "R00=%08x R01=%08x R02=%08x R03=%08x R04=%08x R05=%08x R06=%08x R07=%08x R08=%08x R09=%08x R10=%08x R11=%08x R12=%08x R13=%08x R14=%08x R15=%08x R16=%08x R17=%08x\n", - oldreg[0], oldreg[1], oldreg[2], oldreg[3], oldreg[4], oldreg[5], - oldreg[6], oldreg[7], oldreg[8], oldreg[9], oldreg[10], oldreg[11], - oldreg[12], oldreg[13], oldreg[14], oldreg[15], oldreg[16], - oldreg[17]); - } -#endif - sprintf(buffer, "R00=%08x R01=%08x R02=%08x R03=%08x R04=%08x R05=%08x R06=%08x R07=%08x R08=%08x R09=%08x R10=%08x R11=%08x R12=%08x R13=%08x R14=%08x R15=%08x R16=%08x R17=%08x\n", - reg[0].I, reg[1].I, reg[2].I, reg[3].I, reg[4].I, reg[5].I, - reg[6].I, reg[7].I, reg[8].I, reg[9].I, reg[10].I, reg[11].I, - reg[12].I, reg[13].I, reg[14].I, reg[15].I, reg[16].I, - reg[17].I); -#ifdef SDL - log(buffer); -#else - winlog(buffer); -#endif - } else if(!holdState) { - sprintf(buffer, "PC=%08x\n", armNextPC); -#ifdef SDL - log(buffer); -#else - winlog(buffer); -#endif - } - } -#endif /* FINAL_VERSION */ - - if(!holdState && !SWITicks) { - if(armState) { - if (!armExecute()) - return; - } else { - if (!thumbExecute()) - return; - } - clockTicks = 0; - } else - clockTicks = CPUUpdateTicks(); - - cpuTotalTicks += clockTicks; - - - if(cpuTotalTicks >= cpuNextEvent) { - int remainingTicks = cpuTotalTicks - cpuNextEvent; - - if (SWITicks) - { - SWITicks-=clockTicks; - if (SWITicks<0) - SWITicks = 0; - } - - clockTicks = cpuNextEvent; - cpuTotalTicks = 0; - cpuDmaHack = false; - - updateLoop: - - if (IRQTicks) - { - IRQTicks -= clockTicks; - if (IRQTicks<0) - IRQTicks = 0; - } - - lcdTicks -= clockTicks; - - - if(lcdTicks <= 0) { - if(DISPSTAT & 1) { // V-BLANK - // if in V-Blank mode, keep computing... - if(DISPSTAT & 2) { - lcdTicks += 1008; - VCOUNT++; - UPDATE_REG(0x06, VCOUNT); - DISPSTAT &= 0xFFFD; - UPDATE_REG(0x04, DISPSTAT); - CPUCompareVCOUNT(); - } else { - lcdTicks += 224; - DISPSTAT |= 2; - UPDATE_REG(0x04, DISPSTAT); - if(DISPSTAT & 16) { - IF |= 2; - UPDATE_REG(0x202, IF); - } - } - - if(VCOUNT >= 228) { //Reaching last line - DISPSTAT &= 0xFFFC; - UPDATE_REG(0x04, DISPSTAT); - VCOUNT = 0; - UPDATE_REG(0x06, VCOUNT); - CPUCompareVCOUNT(); - } - } else { - int framesToSkip = systemFrameSkip; - if(speedup) - framesToSkip = 9; // try 6 FPS during speedup - - if(DISPSTAT & 2) { - // if in H-Blank, leave it and move to drawing mode - VCOUNT++; - UPDATE_REG(0x06, VCOUNT); - - lcdTicks += 1008; - DISPSTAT &= 0xFFFD; - if(VCOUNT == 160) { - count++; - systemFrame(); - - if((count % 10) == 0) { - system10Frames(60); - } - if(count == 60) { - u32 time = systemGetClock(); - if(time != lastTime) { - u32 t = 100000/(time - lastTime); - systemShowSpeed(t); - } else - systemShowSpeed(0); - lastTime = time; - count = 0; - } - u32 joy = 0; - // update joystick information - if(systemReadJoypads()) - // read default joystick - joy = systemReadJoypad(-1); - P1 = 0x03FF ^ (joy & 0x3FF); - if(cpuEEPROMSensorEnabled) - systemUpdateMotionSensor(); - UPDATE_REG(0x130, P1); - u16 P1CNT = READ16LE(((u16 *)&ioMem[0x132])); - // this seems wrong, but there are cases where the game - // can enter the stop state without requesting an IRQ from - // the joypad. - if((P1CNT & 0x4000) || stopState) { - u16 p1 = (0x3FF ^ P1) & 0x3FF; - if(P1CNT & 0x8000) { - if(p1 == (P1CNT & 0x3FF)) { - IF |= 0x1000; - UPDATE_REG(0x202, IF); - } - } else { - if(p1 & P1CNT) { - IF |= 0x1000; - UPDATE_REG(0x202, IF); - } - } - } - - u32 ext = (joy >> 10); - // If no (m) code is enabled, apply the cheats at each LCDline - if((cheatsEnabled) && (mastercode==0)) - remainingTicks += cheatsCheckKeys(P1^0x3FF, ext); - speedup = (ext & 1) ? true : false; - capture = (ext & 2) ? true : false; - - if(capture && !capturePrevious) { - captureNumber++; - systemScreenCapture(captureNumber); - } - capturePrevious = capture; - - DISPSTAT |= 1; - DISPSTAT &= 0xFFFD; - UPDATE_REG(0x04, DISPSTAT); - if(DISPSTAT & 0x0008) { - IF |= 1; - UPDATE_REG(0x202, IF); - } - CPUCheckDMA(1, 0x0f); - if(frameCount >= framesToSkip) { - systemDrawScreen(); - frameCount = 0; - } else - frameCount++; - if(systemPauseOnFrame()) - ticks = 0; - } - - UPDATE_REG(0x04, DISPSTAT); - CPUCompareVCOUNT(); - - } else { - if(frameCount >= framesToSkip) - { - (*renderLine)(); - switch(systemColorDepth) { - case 16: - { - u16 *dest = (u16 *)pix + 242 * (VCOUNT+1); - for(int x = 0; x < 240;) { - *dest++ = systemColorMap16[lineMix[x++]&0xFFFF]; - *dest++ = systemColorMap16[lineMix[x++]&0xFFFF]; - *dest++ = systemColorMap16[lineMix[x++]&0xFFFF]; - *dest++ = systemColorMap16[lineMix[x++]&0xFFFF]; - - *dest++ = systemColorMap16[lineMix[x++]&0xFFFF]; - *dest++ = systemColorMap16[lineMix[x++]&0xFFFF]; - *dest++ = systemColorMap16[lineMix[x++]&0xFFFF]; - *dest++ = systemColorMap16[lineMix[x++]&0xFFFF]; - - *dest++ = systemColorMap16[lineMix[x++]&0xFFFF]; - *dest++ = systemColorMap16[lineMix[x++]&0xFFFF]; - *dest++ = systemColorMap16[lineMix[x++]&0xFFFF]; - *dest++ = systemColorMap16[lineMix[x++]&0xFFFF]; - - *dest++ = systemColorMap16[lineMix[x++]&0xFFFF]; - *dest++ = systemColorMap16[lineMix[x++]&0xFFFF]; - *dest++ = systemColorMap16[lineMix[x++]&0xFFFF]; - *dest++ = systemColorMap16[lineMix[x++]&0xFFFF]; - } - // for filters that read past the screen - *dest++ = 0; - } - break; - case 24: - { - u8 *dest = (u8 *)pix + 240 * VCOUNT * 3; - for(int x = 0; x < 240;) { - *((u32 *)dest) = systemColorMap32[lineMix[x++] & 0xFFFF]; - dest += 3; - *((u32 *)dest) = systemColorMap32[lineMix[x++] & 0xFFFF]; - dest += 3; - *((u32 *)dest) = systemColorMap32[lineMix[x++] & 0xFFFF]; - dest += 3; - *((u32 *)dest) = systemColorMap32[lineMix[x++] & 0xFFFF]; - dest += 3; - - *((u32 *)dest) = systemColorMap32[lineMix[x++] & 0xFFFF]; - dest += 3; - *((u32 *)dest) = systemColorMap32[lineMix[x++] & 0xFFFF]; - dest += 3; - *((u32 *)dest) = systemColorMap32[lineMix[x++] & 0xFFFF]; - dest += 3; - *((u32 *)dest) = systemColorMap32[lineMix[x++] & 0xFFFF]; - dest += 3; - - *((u32 *)dest) = systemColorMap32[lineMix[x++] & 0xFFFF]; - dest += 3; - *((u32 *)dest) = systemColorMap32[lineMix[x++] & 0xFFFF]; - dest += 3; - *((u32 *)dest) = systemColorMap32[lineMix[x++] & 0xFFFF]; - dest += 3; - *((u32 *)dest) = systemColorMap32[lineMix[x++] & 0xFFFF]; - dest += 3; - - *((u32 *)dest) = systemColorMap32[lineMix[x++] & 0xFFFF]; - dest += 3; - *((u32 *)dest) = systemColorMap32[lineMix[x++] & 0xFFFF]; - dest += 3; - *((u32 *)dest) = systemColorMap32[lineMix[x++] & 0xFFFF]; - dest += 3; - *((u32 *)dest) = systemColorMap32[lineMix[x++] & 0xFFFF]; - dest += 3; - } - } - break; - case 32: - { - u32 *dest = (u32 *)pix + 241 * (VCOUNT+1); - for(int x = 0; x < 240; ) { - *dest++ = systemColorMap32[lineMix[x++] & 0xFFFF]; - *dest++ = systemColorMap32[lineMix[x++] & 0xFFFF]; - *dest++ = systemColorMap32[lineMix[x++] & 0xFFFF]; - *dest++ = systemColorMap32[lineMix[x++] & 0xFFFF]; - - *dest++ = systemColorMap32[lineMix[x++] & 0xFFFF]; - *dest++ = systemColorMap32[lineMix[x++] & 0xFFFF]; - *dest++ = systemColorMap32[lineMix[x++] & 0xFFFF]; - *dest++ = systemColorMap32[lineMix[x++] & 0xFFFF]; - - *dest++ = systemColorMap32[lineMix[x++] & 0xFFFF]; - *dest++ = systemColorMap32[lineMix[x++] & 0xFFFF]; - *dest++ = systemColorMap32[lineMix[x++] & 0xFFFF]; - *dest++ = systemColorMap32[lineMix[x++] & 0xFFFF]; - - *dest++ = systemColorMap32[lineMix[x++] & 0xFFFF]; - *dest++ = systemColorMap32[lineMix[x++] & 0xFFFF]; - *dest++ = systemColorMap32[lineMix[x++] & 0xFFFF]; - *dest++ = systemColorMap32[lineMix[x++] & 0xFFFF]; - } - } - break; - } - } - // entering H-Blank - DISPSTAT |= 2; - UPDATE_REG(0x04, DISPSTAT); - lcdTicks += 224; - CPUCheckDMA(2, 0x0f); - if(DISPSTAT & 16) { - IF |= 2; - UPDATE_REG(0x202, IF); - } - } - } - } - - // we shouldn't be doing sound in stop state, but we loose synchronization - // if sound is disabled, so in stop state, soundTick will just produce - // mute sound - soundTicks -= clockTicks; - if(soundTicks <= 0) { - psoundTickfn(); - soundTicks += SOUND_CLOCK_TICKS; - } - - if(!stopState) { - if(timer0On) { - timer0Ticks -= clockTicks; - if(timer0Ticks <= 0) { - timer0Ticks += (0x10000 - timer0Reload) << timer0ClockReload; - timerOverflow |= 1; - soundTimerOverflow(0); - if(TM0CNT & 0x40) { - IF |= 0x08; - UPDATE_REG(0x202, IF); - } - } - TM0D = 0xFFFF - (timer0Ticks >> timer0ClockReload); - UPDATE_REG(0x100, TM0D); - } - - if(timer1On) { - if(TM1CNT & 4) { - if(timerOverflow & 1) { - TM1D++; - if(TM1D == 0) { - TM1D += timer1Reload; - timerOverflow |= 2; - soundTimerOverflow(1); - if(TM1CNT & 0x40) { - IF |= 0x10; - UPDATE_REG(0x202, IF); - } - } - UPDATE_REG(0x104, TM1D); - } - } else { - timer1Ticks -= clockTicks; - if(timer1Ticks <= 0) { - timer1Ticks += (0x10000 - timer1Reload) << timer1ClockReload; - timerOverflow |= 2; - soundTimerOverflow(1); - if(TM1CNT & 0x40) { - IF |= 0x10; - UPDATE_REG(0x202, IF); - } - } - TM1D = 0xFFFF - (timer1Ticks >> timer1ClockReload); - UPDATE_REG(0x104, TM1D); - } - } - - if(timer2On) { - if(TM2CNT & 4) { - if(timerOverflow & 2) { - TM2D++; - if(TM2D == 0) { - TM2D += timer2Reload; - timerOverflow |= 4; - if(TM2CNT & 0x40) { - IF |= 0x20; - UPDATE_REG(0x202, IF); - } - } - UPDATE_REG(0x108, TM2D); - } - } else { - timer2Ticks -= clockTicks; - if(timer2Ticks <= 0) { - timer2Ticks += (0x10000 - timer2Reload) << timer2ClockReload; - timerOverflow |= 4; - if(TM2CNT & 0x40) { - IF |= 0x20; - UPDATE_REG(0x202, IF); - } - } - TM2D = 0xFFFF - (timer2Ticks >> timer2ClockReload); - UPDATE_REG(0x108, TM2D); - } - } - - if(timer3On) { - if(TM3CNT & 4) { - if(timerOverflow & 4) { - TM3D++; - if(TM3D == 0) { - TM3D += timer3Reload; - if(TM3CNT & 0x40) { - IF |= 0x40; - UPDATE_REG(0x202, IF); - } - } - UPDATE_REG(0x10C, TM3D); - } - } else { - timer3Ticks -= clockTicks; - if(timer3Ticks <= 0) { - timer3Ticks += (0x10000 - timer3Reload) << timer3ClockReload; - if(TM3CNT & 0x40) { - IF |= 0x40; - UPDATE_REG(0x202, IF); - } - } - TM3D = 0xFFFF - (timer3Ticks >> timer3ClockReload); - UPDATE_REG(0x10C, TM3D); - } - } - } - - timerOverflow = 0; - - - -#ifdef PROFILING - profilingTicks -= clockTicks; - if(profilingTicks <= 0) { - profilingTicks += profilingTicksReload; - if(profilSegment) { - profile_segment *seg = profilSegment; - do { - u16 *b = (u16 *)seg->sbuf; - int pc = ((reg[15].I - seg->s_lowpc) * seg->s_scale)/0x10000; - if(pc >= 0 && pc < seg->ssiz) { - b[pc]++; - break; - } - - seg = seg->next; - } while(seg); - } - } -#endif - - ticks -= clockTicks; - - if (gba_joybus_enabled) - JoyBusUpdate(clockTicks); - - if (gba_link_enabled) - LinkUpdate(clockTicks); - - cpuNextEvent = CPUUpdateTicks(); - - if(cpuDmaTicksToUpdate > 0) { - if(cpuDmaTicksToUpdate > cpuNextEvent) - clockTicks = cpuNextEvent; - else - clockTicks = cpuDmaTicksToUpdate; - cpuDmaTicksToUpdate -= clockTicks; - if(cpuDmaTicksToUpdate < 0) - cpuDmaTicksToUpdate = 0; - cpuDmaHack = true; - goto updateLoop; - } - - // shuffle2: what's the purpose? - if(gba_link_enabled) - cpuNextEvent = 1; - - if(IF && (IME & 1) && armIrqEnable) { - int res = IF & IE; - if(stopState) - res &= 0x3080; - if(res) { - if (intState) - { - if (!IRQTicks) - { - CPUInterrupt(); - intState = false; - holdState = false; - stopState = false; - holdType = 0; - } - } - else - { - if (!holdState) - { - intState = true; - IRQTicks=7; - if (cpuNextEvent> IRQTicks) - cpuNextEvent = IRQTicks; - } - else - { - CPUInterrupt(); - holdState = false; - stopState = false; - holdType = 0; - } - } - - // Stops the SWI Ticks emulation if an IRQ is executed - //(to avoid problems with nested IRQ/SWI) - if (SWITicks) - SWITicks = 0; - } - } - - if(remainingTicks > 0) { - if(remainingTicks > cpuNextEvent) - clockTicks = cpuNextEvent; - else - clockTicks = remainingTicks; - remainingTicks -= clockTicks; - if(remainingTicks < 0) - remainingTicks = 0; - goto updateLoop; - } - - if (timerOnOffDelay) - applyTimer(); - - if(cpuNextEvent > ticks) - cpuNextEvent = ticks; - - if(ticks <= 0 || cpuBreakLoop) - break; - - } - } -} - - - -struct EmulatedSystem GBASystem = { - // emuMain - CPULoop, - // emuReset - CPUReset, - // emuCleanUp - CPUCleanUp, - // emuReadBattery - CPUReadBatteryFile, - // emuWriteBattery - CPUWriteBatteryFile, - // emuReadState - CPUReadState, - // emuWriteState - CPUWriteState, - // emuReadMemState - CPUReadMemState, - // emuWriteMemState - CPUWriteMemState, - // emuWritePNG - CPUWritePNGFile, - // emuWriteBMP - CPUWriteBMPFile, - // emuUpdateCPSR - CPUUpdateCPSR, - // emuHasDebugger - true, - // emuCount -#ifdef FINAL_VERSION - 250000 -#else - 5000 -#endif -}; +#include +#include +#include +#include +#include +#include "GBA.h" +#include "GBAcpu.h" +#include "GBAinline.h" +#include "Globals.h" +#include "GBAGfx.h" +#include "EEprom.h" +#include "Flash.h" +#include "Sound.h" +#include "Sram.h" +#include "bios.h" +#include "Cheats.h" +#include "../NLS.h" +#include "elf.h" +#include "../Util.h" +#include "../common/Port.h" +#include "../System.h" +#include "agbprint.h" +#include "GBALink.h" + +#ifdef PROFILING +#include "prof/prof.h" +#endif + +#ifdef __GNUC__ +#define _stricmp strcasecmp +#endif + +extern int emulating; + +int SWITicks = 0; +int IRQTicks = 0; + +u32 mastercode = 0; +int layerEnableDelay = 0; +bool busPrefetch = false; +bool busPrefetchEnable = false; +u32 busPrefetchCount = 0; +int cpuDmaTicksToUpdate = 0; +int cpuDmaCount = 0; +bool cpuDmaHack = false; +u32 cpuDmaLast = 0; +int dummyAddress = 0; + +bool cpuBreakLoop = false; +int cpuNextEvent = 0; + +int gbaSaveType = 0; // used to remember the save type on reset +bool intState = false; +bool stopState = false; +bool holdState = false; +int holdType = 0; +bool cpuSramEnabled = true; +bool cpuFlashEnabled = true; +bool cpuEEPROMEnabled = true; +bool cpuEEPROMSensorEnabled = false; + +u32 cpuPrefetch[2]; + +int cpuTotalTicks = 0; +#ifdef PROFILING +int profilingTicks = 0; +int profilingTicksReload = 0; +static profile_segment *profilSegment = NULL; +#endif + +#ifdef BKPT_SUPPORT +u8 freezeWorkRAM[0x40000]; +u8 freezeInternalRAM[0x8000]; +u8 freezeVRAM[0x18000]; +u8 freezePRAM[0x400]; +u8 freezeOAM[0x400]; +bool debugger_last; +#endif + +int lcdTicks = (useBios && !skipBios) ? 1008 : 208; +u8 timerOnOffDelay = 0; +u16 timer0Value = 0; +bool timer0On = false; +int timer0Ticks = 0; +int timer0Reload = 0; +int timer0ClockReload = 0; +u16 timer1Value = 0; +bool timer1On = false; +int timer1Ticks = 0; +int timer1Reload = 0; +int timer1ClockReload = 0; +u16 timer2Value = 0; +bool timer2On = false; +int timer2Ticks = 0; +int timer2Reload = 0; +int timer2ClockReload = 0; +u16 timer3Value = 0; +bool timer3On = false; +int timer3Ticks = 0; +int timer3Reload = 0; +int timer3ClockReload = 0; +u32 dma0Source = 0; +u32 dma0Dest = 0; +u32 dma1Source = 0; +u32 dma1Dest = 0; +u32 dma2Source = 0; +u32 dma2Dest = 0; +u32 dma3Source = 0; +u32 dma3Dest = 0; +void (*cpuSaveGameFunc)(u32,u8) = flashSaveDecide; +void (*renderLine)() = mode0RenderLine; +bool fxOn = false; +bool windowOn = false; +int frameCount = 0; +char buffer[1024]; +FILE *out = NULL; +u32 lastTime = 0; +int count = 0; + +int capture = 0; +int capturePrevious = 0; +int captureNumber = 0; + +const int TIMER_TICKS[4] = { + 0, + 6, + 8, + 10 +}; + +const u32 objTilesAddress [3] = {0x010000, 0x014000, 0x014000}; +const u8 gamepakRamWaitState[4] = { 4, 3, 2, 8 }; +const u8 gamepakWaitState[4] = { 4, 3, 2, 8 }; +const u8 gamepakWaitState0[2] = { 2, 1 }; +const u8 gamepakWaitState1[2] = { 4, 1 }; +const u8 gamepakWaitState2[2] = { 8, 1 }; +const bool isInRom [16]= + { false, false, false, false, false, false, false, false, + true, true, true, true, true, true, false, false }; + +u8 memoryWait[16] = + { 0, 0, 2, 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 0 }; +u8 memoryWait32[16] = + { 0, 0, 5, 0, 0, 1, 1, 0, 7, 7, 9, 9, 13, 13, 4, 0 }; +u8 memoryWaitSeq[16] = + { 0, 0, 2, 0, 0, 0, 0, 0, 2, 2, 4, 4, 8, 8, 4, 0 }; +u8 memoryWaitSeq32[16] = + { 0, 0, 5, 0, 0, 1, 1, 0, 5, 5, 9, 9, 17, 17, 4, 0 }; + +// The videoMemoryWait constants are used to add some waitstates +// if the opcode access video memory data outside of vblank/hblank +// It seems to happen on only one ticks for each pixel. +// Not used for now (too problematic with current code). +//const u8 videoMemoryWait[16] = +// {0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + + +u8 biosProtected[4]; + +#ifdef WORDS_BIGENDIAN +bool cpuBiosSwapped = false; +#endif + +u32 myROM[] = { +0xEA000006, +0xEA000093, +0xEA000006, +0x00000000, +0x00000000, +0x00000000, +0xEA000088, +0x00000000, +0xE3A00302, +0xE1A0F000, +0xE92D5800, +0xE55EC002, +0xE28FB03C, +0xE79BC10C, +0xE14FB000, +0xE92D0800, +0xE20BB080, +0xE38BB01F, +0xE129F00B, +0xE92D4004, +0xE1A0E00F, +0xE12FFF1C, +0xE8BD4004, +0xE3A0C0D3, +0xE129F00C, +0xE8BD0800, +0xE169F00B, +0xE8BD5800, +0xE1B0F00E, +0x0000009C, +0x0000009C, +0x0000009C, +0x0000009C, +0x000001F8, +0x000001F0, +0x000000AC, +0x000000A0, +0x000000FC, +0x00000168, +0xE12FFF1E, +0xE1A03000, +0xE1A00001, +0xE1A01003, +0xE2113102, +0x42611000, +0xE033C040, +0x22600000, +0xE1B02001, +0xE15200A0, +0x91A02082, +0x3AFFFFFC, +0xE1500002, +0xE0A33003, +0x20400002, +0xE1320001, +0x11A020A2, +0x1AFFFFF9, +0xE1A01000, +0xE1A00003, +0xE1B0C08C, +0x22600000, +0x42611000, +0xE12FFF1E, +0xE92D0010, +0xE1A0C000, +0xE3A01001, +0xE1500001, +0x81A000A0, +0x81A01081, +0x8AFFFFFB, +0xE1A0000C, +0xE1A04001, +0xE3A03000, +0xE1A02001, +0xE15200A0, +0x91A02082, +0x3AFFFFFC, +0xE1500002, +0xE0A33003, +0x20400002, +0xE1320001, +0x11A020A2, +0x1AFFFFF9, +0xE0811003, +0xE1B010A1, +0xE1510004, +0x3AFFFFEE, +0xE1A00004, +0xE8BD0010, +0xE12FFF1E, +0xE0010090, +0xE1A01741, +0xE2611000, +0xE3A030A9, +0xE0030391, +0xE1A03743, +0xE2833E39, +0xE0030391, +0xE1A03743, +0xE2833C09, +0xE283301C, +0xE0030391, +0xE1A03743, +0xE2833C0F, +0xE28330B6, +0xE0030391, +0xE1A03743, +0xE2833C16, +0xE28330AA, +0xE0030391, +0xE1A03743, +0xE2833A02, +0xE2833081, +0xE0030391, +0xE1A03743, +0xE2833C36, +0xE2833051, +0xE0030391, +0xE1A03743, +0xE2833CA2, +0xE28330F9, +0xE0000093, +0xE1A00840, +0xE12FFF1E, +0xE3A00001, +0xE3A01001, +0xE92D4010, +0xE3A03000, +0xE3A04001, +0xE3500000, +0x1B000004, +0xE5CC3301, +0xEB000002, +0x0AFFFFFC, +0xE8BD4010, +0xE12FFF1E, +0xE3A0C301, +0xE5CC3208, +0xE15C20B8, +0xE0110002, +0x10222000, +0x114C20B8, +0xE5CC4208, +0xE12FFF1E, +0xE92D500F, +0xE3A00301, +0xE1A0E00F, +0xE510F004, +0xE8BD500F, +0xE25EF004, +0xE59FD044, +0xE92D5000, +0xE14FC000, +0xE10FE000, +0xE92D5000, +0xE3A0C302, +0xE5DCE09C, +0xE35E00A5, +0x1A000004, +0x05DCE0B4, +0x021EE080, +0xE28FE004, +0x159FF018, +0x059FF018, +0xE59FD018, +0xE8BD5000, +0xE169F00C, +0xE8BD5000, +0xE25EF004, +0x03007FF0, +0x09FE2000, +0x09FFC000, +0x03007FE0 +}; + +variable_desc saveGameStruct[] = { + { &DISPCNT , sizeof(u16) }, + { &DISPSTAT , sizeof(u16) }, + { &VCOUNT , sizeof(u16) }, + { &BG0CNT , sizeof(u16) }, + { &BG1CNT , sizeof(u16) }, + { &BG2CNT , sizeof(u16) }, + { &BG3CNT , sizeof(u16) }, + { &BG0HOFS , sizeof(u16) }, + { &BG0VOFS , sizeof(u16) }, + { &BG1HOFS , sizeof(u16) }, + { &BG1VOFS , sizeof(u16) }, + { &BG2HOFS , sizeof(u16) }, + { &BG2VOFS , sizeof(u16) }, + { &BG3HOFS , sizeof(u16) }, + { &BG3VOFS , sizeof(u16) }, + { &BG2PA , sizeof(u16) }, + { &BG2PB , sizeof(u16) }, + { &BG2PC , sizeof(u16) }, + { &BG2PD , sizeof(u16) }, + { &BG2X_L , sizeof(u16) }, + { &BG2X_H , sizeof(u16) }, + { &BG2Y_L , sizeof(u16) }, + { &BG2Y_H , sizeof(u16) }, + { &BG3PA , sizeof(u16) }, + { &BG3PB , sizeof(u16) }, + { &BG3PC , sizeof(u16) }, + { &BG3PD , sizeof(u16) }, + { &BG3X_L , sizeof(u16) }, + { &BG3X_H , sizeof(u16) }, + { &BG3Y_L , sizeof(u16) }, + { &BG3Y_H , sizeof(u16) }, + { &WIN0H , sizeof(u16) }, + { &WIN1H , sizeof(u16) }, + { &WIN0V , sizeof(u16) }, + { &WIN1V , sizeof(u16) }, + { &WININ , sizeof(u16) }, + { &WINOUT , sizeof(u16) }, + { &MOSAIC , sizeof(u16) }, + { &BLDMOD , sizeof(u16) }, + { &COLEV , sizeof(u16) }, + { &COLY , sizeof(u16) }, + { &DM0SAD_L , sizeof(u16) }, + { &DM0SAD_H , sizeof(u16) }, + { &DM0DAD_L , sizeof(u16) }, + { &DM0DAD_H , sizeof(u16) }, + { &DM0CNT_L , sizeof(u16) }, + { &DM0CNT_H , sizeof(u16) }, + { &DM1SAD_L , sizeof(u16) }, + { &DM1SAD_H , sizeof(u16) }, + { &DM1DAD_L , sizeof(u16) }, + { &DM1DAD_H , sizeof(u16) }, + { &DM1CNT_L , sizeof(u16) }, + { &DM1CNT_H , sizeof(u16) }, + { &DM2SAD_L , sizeof(u16) }, + { &DM2SAD_H , sizeof(u16) }, + { &DM2DAD_L , sizeof(u16) }, + { &DM2DAD_H , sizeof(u16) }, + { &DM2CNT_L , sizeof(u16) }, + { &DM2CNT_H , sizeof(u16) }, + { &DM3SAD_L , sizeof(u16) }, + { &DM3SAD_H , sizeof(u16) }, + { &DM3DAD_L , sizeof(u16) }, + { &DM3DAD_H , sizeof(u16) }, + { &DM3CNT_L , sizeof(u16) }, + { &DM3CNT_H , sizeof(u16) }, + { &TM0D , sizeof(u16) }, + { &TM0CNT , sizeof(u16) }, + { &TM1D , sizeof(u16) }, + { &TM1CNT , sizeof(u16) }, + { &TM2D , sizeof(u16) }, + { &TM2CNT , sizeof(u16) }, + { &TM3D , sizeof(u16) }, + { &TM3CNT , sizeof(u16) }, + { &P1 , sizeof(u16) }, + { &IE , sizeof(u16) }, + { &IF , sizeof(u16) }, + { &IME , sizeof(u16) }, + { &holdState, sizeof(bool) }, + { &holdType, sizeof(int) }, + { &lcdTicks, sizeof(int) }, + { &timer0On , sizeof(bool) }, + { &timer0Ticks , sizeof(int) }, + { &timer0Reload , sizeof(int) }, + { &timer0ClockReload , sizeof(int) }, + { &timer1On , sizeof(bool) }, + { &timer1Ticks , sizeof(int) }, + { &timer1Reload , sizeof(int) }, + { &timer1ClockReload , sizeof(int) }, + { &timer2On , sizeof(bool) }, + { &timer2Ticks , sizeof(int) }, + { &timer2Reload , sizeof(int) }, + { &timer2ClockReload , sizeof(int) }, + { &timer3On , sizeof(bool) }, + { &timer3Ticks , sizeof(int) }, + { &timer3Reload , sizeof(int) }, + { &timer3ClockReload , sizeof(int) }, + { &dma0Source , sizeof(u32) }, + { &dma0Dest , sizeof(u32) }, + { &dma1Source , sizeof(u32) }, + { &dma1Dest , sizeof(u32) }, + { &dma2Source , sizeof(u32) }, + { &dma2Dest , sizeof(u32) }, + { &dma3Source , sizeof(u32) }, + { &dma3Dest , sizeof(u32) }, + { &fxOn, sizeof(bool) }, + { &windowOn, sizeof(bool) }, + { &N_FLAG , sizeof(bool) }, + { &C_FLAG , sizeof(bool) }, + { &Z_FLAG , sizeof(bool) }, + { &V_FLAG , sizeof(bool) }, + { &armState , sizeof(bool) }, + { &armIrqEnable , sizeof(bool) }, + { &armNextPC , sizeof(u32) }, + { &armMode , sizeof(int) }, + { &saveType , sizeof(int) }, + { NULL, 0 } +}; + +static int romSize = 0x2000000; + +#ifdef PROFILING +void cpuProfil(profile_segment *seg) +{ + profilSegment = seg; +} + +void cpuEnableProfiling(int hz) +{ + if(hz == 0) + hz = 100; + profilingTicks = profilingTicksReload = 16777216 / hz; + profSetHertz(hz); +} +#endif + + +inline int CPUUpdateTicks() +{ + int cpuLoopTicks = lcdTicks; + + if(soundTicks < cpuLoopTicks) + cpuLoopTicks = soundTicks; + + if(timer0On && (timer0Ticks < cpuLoopTicks)) { + cpuLoopTicks = timer0Ticks; + } + if(timer1On && !(TM1CNT & 4) && (timer1Ticks < cpuLoopTicks)) { + cpuLoopTicks = timer1Ticks; + } + if(timer2On && !(TM2CNT & 4) && (timer2Ticks < cpuLoopTicks)) { + cpuLoopTicks = timer2Ticks; + } + if(timer3On && !(TM3CNT & 4) && (timer3Ticks < cpuLoopTicks)) { + cpuLoopTicks = timer3Ticks; + } +#ifdef PROFILING + if(profilingTicksReload != 0) { + if(profilingTicks < cpuLoopTicks) { + cpuLoopTicks = profilingTicks; + } + } +#endif + + if (SWITicks) { + if (SWITicks < cpuLoopTicks) + cpuLoopTicks = SWITicks; + } + + if (IRQTicks) { + if (IRQTicks < cpuLoopTicks) + cpuLoopTicks = IRQTicks; + } + + return cpuLoopTicks; +} + +void CPUUpdateWindow0() +{ + int x00 = WIN0H>>8; + int x01 = WIN0H & 255; + + if(x00 <= x01) { + for(int i = 0; i < 240; i++) { + gfxInWin0[i] = (i >= x00 && i < x01); + } + } else { + for(int i = 0; i < 240; i++) { + gfxInWin0[i] = (i >= x00 || i < x01); + } + } +} + +void CPUUpdateWindow1() +{ + int x00 = WIN1H>>8; + int x01 = WIN1H & 255; + + if(x00 <= x01) { + for(int i = 0; i < 240; i++) { + gfxInWin1[i] = (i >= x00 && i < x01); + } + } else { + for(int i = 0; i < 240; i++) { + gfxInWin1[i] = (i >= x00 || i < x01); + } + } +} + +extern u32 line0[240]; +extern u32 line1[240]; +extern u32 line2[240]; +extern u32 line3[240]; + +#define CLEAR_ARRAY(a) \ + {\ + u32 *array = (a);\ + for(int i = 0; i < 240; i++) {\ + *array++ = 0x80000000;\ + }\ + }\ + +void CPUUpdateRenderBuffers(bool force) +{ + if(!(layerEnable & 0x0100) || force) { + CLEAR_ARRAY(line0); + } + if(!(layerEnable & 0x0200) || force) { + CLEAR_ARRAY(line1); + } + if(!(layerEnable & 0x0400) || force) { + CLEAR_ARRAY(line2); + } + if(!(layerEnable & 0x0800) || force) { + CLEAR_ARRAY(line3); + } +} + +static bool CPUWriteState(gzFile gzFile) +{ + utilWriteInt(gzFile, SAVE_GAME_VERSION); + + utilGzWrite(gzFile, &rom[0xa0], 16); + + utilWriteInt(gzFile, useBios); + + utilGzWrite(gzFile, ®[0], sizeof(reg)); + + utilWriteData(gzFile, saveGameStruct); + + // new to version 0.7.1 + utilWriteInt(gzFile, stopState); + // new to version 0.8 + utilWriteInt(gzFile, IRQTicks); + + utilGzWrite(gzFile, internalRAM, 0x8000); + utilGzWrite(gzFile, paletteRAM, 0x400); + utilGzWrite(gzFile, workRAM, 0x40000); + utilGzWrite(gzFile, vram, 0x20000); + utilGzWrite(gzFile, oam, 0x400); + utilGzWrite(gzFile, pix, 4*241*162); + utilGzWrite(gzFile, ioMem, 0x400); + + eepromSaveGame(gzFile); + flashSaveGame(gzFile); + soundSaveGame(gzFile); + + cheatsSaveGame(gzFile); + + // version 1.5 + rtcSaveGame(gzFile); + + return true; +} + +bool CPUWriteState(const char *file) +{ + gzFile gzFile = utilGzOpen(file, "wb"); + + if(gzFile == NULL) { + systemMessage(MSG_ERROR_CREATING_FILE, N_("Error creating file %s"), file); + return false; + } + + bool res = CPUWriteState(gzFile); + + utilGzClose(gzFile); + + return res; +} + +bool CPUWriteMemState(char *memory, int available) +{ + gzFile gzFile = utilMemGzOpen(memory, available, "w"); + + if(gzFile == NULL) { + return false; + } + + bool res = CPUWriteState(gzFile); + + long pos = utilGzMemTell(gzFile)+8; + + if(pos >= (available)) + res = false; + + utilGzClose(gzFile); + + return res; +} + +static bool CPUReadState(gzFile gzFile) +{ + int version = utilReadInt(gzFile); + + if(version > SAVE_GAME_VERSION || version < SAVE_GAME_VERSION_1) { + systemMessage(MSG_UNSUPPORTED_VBA_SGM, + N_("Unsupported VisualBoyAdvance save game version %d"), + version); + return false; + } + + u8 romname[17]; + + utilGzRead(gzFile, romname, 16); + + if(memcmp(&rom[0xa0], romname, 16) != 0) { + romname[16]=0; + for(int i = 0; i < 16; i++) + if(romname[i] < 32) + romname[i] = 32; + systemMessage(MSG_CANNOT_LOAD_SGM, N_("Cannot load save game for %s"), romname); + return false; + } + + bool ub = utilReadInt(gzFile) ? true : false; + + if(ub != useBios) { + if(useBios) + systemMessage(MSG_SAVE_GAME_NOT_USING_BIOS, + N_("Save game is not using the BIOS files")); + else + systemMessage(MSG_SAVE_GAME_USING_BIOS, + N_("Save game is using the BIOS file")); + return false; + } + + utilGzRead(gzFile, ®[0], sizeof(reg)); + + utilReadData(gzFile, saveGameStruct); + + if(version < SAVE_GAME_VERSION_3) + stopState = false; + else + stopState = utilReadInt(gzFile) ? true : false; + + if(version < SAVE_GAME_VERSION_4) + { + IRQTicks = 0; + intState = false; + } + else + { + IRQTicks = utilReadInt(gzFile); + if (IRQTicks>0) + intState = true; + else + { + intState = false; + IRQTicks = 0; + } + } + + utilGzRead(gzFile, internalRAM, 0x8000); + utilGzRead(gzFile, paletteRAM, 0x400); + utilGzRead(gzFile, workRAM, 0x40000); + utilGzRead(gzFile, vram, 0x20000); + utilGzRead(gzFile, oam, 0x400); + if(version < SAVE_GAME_VERSION_6) + utilGzRead(gzFile, pix, 4*240*160); + else + utilGzRead(gzFile, pix, 4*241*162); + utilGzRead(gzFile, ioMem, 0x400); + + if(skipSaveGameBattery) { + // skip eeprom data + eepromReadGameSkip(gzFile, version); + // skip flash data + flashReadGameSkip(gzFile, version); + } else { + eepromReadGame(gzFile, version); + flashReadGame(gzFile, version); + } + soundReadGame(gzFile, version); + + if(version > SAVE_GAME_VERSION_1) { + if(skipSaveGameCheats) { + // skip cheats list data + cheatsReadGameSkip(gzFile, version); + } else { + cheatsReadGame(gzFile, version); + } + } + if(version > SAVE_GAME_VERSION_6) { + rtcReadGame(gzFile); + } + + if(version <= SAVE_GAME_VERSION_7) { + u32 temp; +#define SWAP(a,b,c) \ + temp = (a);\ + (a) = (b)<<16|(c);\ + (b) = (temp) >> 16;\ + (c) = (temp) & 0xFFFF; + + SWAP(dma0Source, DM0SAD_H, DM0SAD_L); + SWAP(dma0Dest, DM0DAD_H, DM0DAD_L); + SWAP(dma1Source, DM1SAD_H, DM1SAD_L); + SWAP(dma1Dest, DM1DAD_H, DM1DAD_L); + SWAP(dma2Source, DM2SAD_H, DM2SAD_L); + SWAP(dma2Dest, DM2DAD_H, DM2DAD_L); + SWAP(dma3Source, DM3SAD_H, DM3SAD_L); + SWAP(dma3Dest, DM3DAD_H, DM3DAD_L); + } + + if(version <= SAVE_GAME_VERSION_8) { + timer0ClockReload = TIMER_TICKS[TM0CNT & 3]; + timer1ClockReload = TIMER_TICKS[TM1CNT & 3]; + timer2ClockReload = TIMER_TICKS[TM2CNT & 3]; + timer3ClockReload = TIMER_TICKS[TM3CNT & 3]; + + timer0Ticks = ((0x10000 - TM0D) << timer0ClockReload) - timer0Ticks; + timer1Ticks = ((0x10000 - TM1D) << timer1ClockReload) - timer1Ticks; + timer2Ticks = ((0x10000 - TM2D) << timer2ClockReload) - timer2Ticks; + timer3Ticks = ((0x10000 - TM3D) << timer3ClockReload) - timer3Ticks; + interp_rate(); + } + + // set pointers! + layerEnable = layerSettings & DISPCNT; + + CPUUpdateRender(); + CPUUpdateRenderBuffers(true); + CPUUpdateWindow0(); + CPUUpdateWindow1(); + gbaSaveType = 0; + switch(saveType) { + case 0: + cpuSaveGameFunc = flashSaveDecide; + break; + case 1: + cpuSaveGameFunc = sramWrite; + gbaSaveType = 1; + break; + case 2: + cpuSaveGameFunc = flashWrite; + gbaSaveType = 2; + break; + case 3: + break; + case 5: + gbaSaveType = 5; + break; + default: + systemMessage(MSG_UNSUPPORTED_SAVE_TYPE, + N_("Unsupported save type %d"), saveType); + break; + } + if(eepromInUse) + gbaSaveType = 3; + + systemSaveUpdateCounter = SYSTEM_SAVE_NOT_UPDATED; + if(armState) { + ARM_PREFETCH; + } else { + THUMB_PREFETCH; + } + + CPUUpdateRegister(0x204, CPUReadHalfWordQuick(0x4000204)); + + return true; +} + +bool CPUReadMemState(char *memory, int available) +{ + gzFile gzFile = utilMemGzOpen(memory, available, "r"); + + bool res = CPUReadState(gzFile); + + utilGzClose(gzFile); + + return res; +} + +bool CPUReadState(const char * file) +{ + gzFile gzFile = utilGzOpen(file, "rb"); + + if(gzFile == NULL) + return false; + + bool res = CPUReadState(gzFile); + + utilGzClose(gzFile); + + return res; +} + +bool CPUExportEepromFile(const char *fileName) +{ + if(eepromInUse) { + FILE *file = fopen(fileName, "wb"); + + if(!file) { + systemMessage(MSG_ERROR_CREATING_FILE, N_("Error creating file %s"), + fileName); + return false; + } + + for(int i = 0; i < eepromSize;) { + for(int j = 0; j < 8; j++) { + if(fwrite(&eepromData[i+7-j], 1, 1, file) != 1) { + fclose(file); + return false; + } + } + i += 8; + } + fclose(file); + } + return true; +} + +bool CPUWriteBatteryFile(const char *fileName) +{ + if(gbaSaveType == 0) { + if(eepromInUse) + gbaSaveType = 3; + else switch(saveType) { + case 1: + gbaSaveType = 1; + break; + case 2: + gbaSaveType = 2; + break; + } + } + + if((gbaSaveType) && (gbaSaveType!=5)) { + FILE *file = fopen(fileName, "wb"); + + if(!file) { + systemMessage(MSG_ERROR_CREATING_FILE, N_("Error creating file %s"), + fileName); + return false; + } + + // only save if Flash/Sram in use or EEprom in use + if(gbaSaveType != 3) { + if(gbaSaveType == 2) { + if(fwrite(flashSaveMemory, 1, flashSize, file) != (size_t)flashSize) { + fclose(file); + return false; + } + } else { + if(fwrite(flashSaveMemory, 1, 0x10000, file) != 0x10000) { + fclose(file); + return false; + } + } + } else { + if(fwrite(eepromData, 1, eepromSize, file) != (size_t)eepromSize) { + fclose(file); + return false; + } + } + fclose(file); + } + return true; +} + +bool CPUReadGSASnapshot(const char *fileName) +{ + int i; + FILE *file = fopen(fileName, "rb"); + + if(!file) { + systemMessage(MSG_CANNOT_OPEN_FILE, N_("Cannot open file %s"), fileName); + return false; + } + + // check file size to know what we should read + fseek(file, 0, SEEK_END); + + // long size = ftell(file); + fseek(file, 0x0, SEEK_SET); + fread(&i, 1, 4, file); + fseek(file, i, SEEK_CUR); // Skip SharkPortSave + fseek(file, 4, SEEK_CUR); // skip some sort of flag + fread(&i, 1, 4, file); // name length + fseek(file, i, SEEK_CUR); // skip name + fread(&i, 1, 4, file); // desc length + fseek(file, i, SEEK_CUR); // skip desc + fread(&i, 1, 4, file); // notes length + fseek(file, i, SEEK_CUR); // skip notes + int saveSize; + fread(&saveSize, 1, 4, file); // read length + saveSize -= 0x1c; // remove header size + char buffer[17]; + char buffer2[17]; + fread(buffer, 1, 16, file); + buffer[16] = 0; + for(i = 0; i < 16; i++) + if(buffer[i] < 32) + buffer[i] = 32; + memcpy(buffer2, &rom[0xa0], 16); + buffer2[16] = 0; + for(i = 0; i < 16; i++) + if(buffer2[i] < 32) + buffer2[i] = 32; + if(memcmp(buffer, buffer2, 16)) { + systemMessage(MSG_CANNOT_IMPORT_SNAPSHOT_FOR, + N_("Cannot import snapshot for %s. Current game is %s"), + buffer, + buffer2); + fclose(file); + return false; + } + fseek(file, 12, SEEK_CUR); // skip some flags + if(saveSize >= 65536) { + if(fread(flashSaveMemory, 1, saveSize, file) != (size_t)saveSize) { + fclose(file); + return false; + } + } else { + systemMessage(MSG_UNSUPPORTED_SNAPSHOT_FILE, + N_("Unsupported snapshot file %s"), + fileName); + fclose(file); + return false; + } + fclose(file); + CPUReset(); + return true; +} + +bool CPUReadGSASPSnapshot(const char *fileName) +{ + const char gsvfooter[] = "xV4\x12"; + const size_t namepos=0x0c, namesz=12; + const size_t footerpos=0x42c, footersz=4; + + char footer[footersz+1], romname[namesz+1], savename[namesz+1];; + FILE *file = fopen(fileName, "rb"); + + if(!file) { + systemMessage(MSG_CANNOT_OPEN_FILE, N_("Cannot open file %s"), fileName); + return false; + } + + // read save name + fseek(file, namepos, SEEK_SET); + fread(savename, 1, namesz, file); + savename[namesz] = 0; + + memcpy(romname, &rom[0xa0], namesz); + romname[namesz] = 0; + + if(memcmp(romname, savename, namesz)) { + systemMessage(MSG_CANNOT_IMPORT_SNAPSHOT_FOR, + N_("Cannot import snapshot for %s. Current game is %s"), + savename, + romname); + fclose(file); + return false; + } + + // read footer tag + fseek(file, footerpos, SEEK_SET); + fread(footer, 1, footersz, file); + footer[footersz] = 0; + + if(memcmp(footer, gsvfooter, footersz)) { + systemMessage(0, + N_("Unsupported snapshot file %s. Footer '%s' at %u should be '%s'"), + fileName, + footer, + footerpos, + gsvfooter); + fclose(file); + return false; + } + + // Read up to 128k save + fread(flashSaveMemory, 1, FLASH_128K_SZ, file); + + fclose(file); + CPUReset(); + return true; +} + + +bool CPUWriteGSASnapshot(const char *fileName, + const char *title, + const char *desc, + const char *notes) +{ + FILE *file = fopen(fileName, "wb"); + + if(!file) { + systemMessage(MSG_CANNOT_OPEN_FILE, N_("Cannot open file %s"), fileName); + return false; + } + + u8 buffer[17]; + + utilPutDword(buffer, 0x0d); // SharkPortSave length + fwrite(buffer, 1, 4, file); + fwrite("SharkPortSave", 1, 0x0d, file); + utilPutDword(buffer, 0x000f0000); + fwrite(buffer, 1, 4, file); // save type 0x000f0000 = GBA save + utilPutDword(buffer, (u32)strlen(title)); + fwrite(buffer, 1, 4, file); // title length + fwrite(title, 1, strlen(title), file); + utilPutDword(buffer, (u32)strlen(desc)); + fwrite(buffer, 1, 4, file); // desc length + fwrite(desc, 1, strlen(desc), file); + utilPutDword(buffer, (u32)strlen(notes)); + fwrite(buffer, 1, 4, file); // notes length + fwrite(notes, 1, strlen(notes), file); + int saveSize = 0x10000; + if(gbaSaveType == 2) + saveSize = flashSize; + int totalSize = saveSize + 0x1c; + + utilPutDword(buffer, totalSize); // length of remainder of save - CRC + fwrite(buffer, 1, 4, file); + + char *temp = new char[0x2001c]; + memset(temp, 0, 28); + memcpy(temp, &rom[0xa0], 16); // copy internal name + temp[0x10] = rom[0xbe]; // reserved area (old checksum) + temp[0x11] = rom[0xbf]; // reserved area (old checksum) + temp[0x12] = rom[0xbd]; // complement check + temp[0x13] = rom[0xb0]; // maker + temp[0x14] = 1; // 1 save ? + memcpy(&temp[0x1c], flashSaveMemory, saveSize); // copy save + fwrite(temp, 1, totalSize, file); // write save + header + u32 crc = 0; + + for(int i = 0; i < totalSize; i++) { + crc += ((u32)temp[i] << (crc % 0x18)); + } + + utilPutDword(buffer, crc); + fwrite(buffer, 1, 4, file); // CRC? + + fclose(file); + delete [] temp; + return true; +} + +bool CPUImportEepromFile(const char *fileName) +{ + FILE *file = fopen(fileName, "rb"); + + if(!file) + return false; + + // check file size to know what we should read + fseek(file, 0, SEEK_END); + + long size = ftell(file); + fseek(file, 0, SEEK_SET); + if(size == 512 || size == 0x2000) { + if(fread(eepromData, 1, size, file) != (size_t)size) { + fclose(file); + return false; + } + for(int i = 0; i < size;) { + u8 tmp = eepromData[i]; + eepromData[i] = eepromData[7-i]; + eepromData[7-i] = tmp; + i++; + tmp = eepromData[i]; + eepromData[i] = eepromData[7-i]; + eepromData[7-i] = tmp; + i++; + tmp = eepromData[i]; + eepromData[i] = eepromData[7-i]; + eepromData[7-i] = tmp; + i++; + tmp = eepromData[i]; + eepromData[i] = eepromData[7-i]; + eepromData[7-i] = tmp; + i++; + i += 4; + } + } else { + fclose(file); + return false; + } + fclose(file); + return true; +} + +bool CPUReadBatteryFile(const char *fileName) +{ + FILE *file = fopen(fileName, "rb"); + + if(!file) + return false; + + // check file size to know what we should read + fseek(file, 0, SEEK_END); + + long size = ftell(file); + fseek(file, 0, SEEK_SET); + systemSaveUpdateCounter = SYSTEM_SAVE_NOT_UPDATED; + + if(size == 512 || size == 0x2000) { + if(fread(eepromData, 1, size, file) != (size_t)size) { + fclose(file); + return false; + } + } else { + if(size == 0x20000) { + if(fread(flashSaveMemory, 1, 0x20000, file) != 0x20000) { + fclose(file); + return false; + } + flashSetSize(0x20000); + } else { + if(fread(flashSaveMemory, 1, 0x10000, file) != 0x10000) { + fclose(file); + return false; + } + flashSetSize(0x10000); + } + } + fclose(file); + return true; +} + +bool CPUWritePNGFile(const char *fileName) +{ + return utilWritePNGFile(fileName, 240, 160, pix); +} + +bool CPUWriteBMPFile(const char *fileName) +{ + return utilWriteBMPFile(fileName, 240, 160, pix); +} + +bool CPUIsZipFile(const char * file) +{ + if(strlen(file) > 4) { + const char * p = strrchr(file,'.'); + + if(p != NULL) { + if(_stricmp(p, ".zip") == 0) + return true; + } + } + + return false; +} + +bool CPUIsGBAImage(const char * file) +{ + cpuIsMultiBoot = false; + if(strlen(file) > 4) { + const char * p = strrchr(file,'.'); + + if(p != NULL) { + if(_stricmp(p, ".gba") == 0) + return true; + if(_stricmp(p, ".agb") == 0) + return true; + if(_stricmp(p, ".bin") == 0) + return true; + if(_stricmp(p, ".elf") == 0) + return true; + if(_stricmp(p, ".mb") == 0) { + cpuIsMultiBoot = true; + return true; + } + } + } + + return false; +} + +bool CPUIsGBABios(const char * file) +{ + if(strlen(file) > 4) { + const char * p = strrchr(file,'.'); + + if(p != NULL) { + if(_stricmp(p, ".gba") == 0) + return true; + if(_stricmp(p, ".agb") == 0) + return true; + if(_stricmp(p, ".bin") == 0) + return true; + if(_stricmp(p, ".bios") == 0) + return true; + if(_stricmp(p, ".rom") == 0) + return true; + } + } + + return false; +} + +bool CPUIsELF(const char *file) +{ + if(file == NULL) + return false; + + if(strlen(file) > 4) { + const char * p = strrchr(file,'.'); + + if(p != NULL) { + if(_stricmp(p, ".elf") == 0) + return true; + } + } + return false; +} + +void CPUCleanUp() +{ +#ifdef PROFILING + if(profilingTicksReload) { + profCleanup(); + } +#endif + + if(rom != NULL) { + free(rom); + rom = NULL; + } + + if(vram != NULL) { + free(vram); + vram = NULL; + } + + if(paletteRAM != NULL) { + free(paletteRAM); + paletteRAM = NULL; + } + + if(internalRAM != NULL) { + free(internalRAM); + internalRAM = NULL; + } + + if(workRAM != NULL) { + free(workRAM); + workRAM = NULL; + } + + if(bios != NULL) { + free(bios); + bios = NULL; + } + + if(pix != NULL) { + free(pix); + pix = NULL; + } + + if(oam != NULL) { + free(oam); + oam = NULL; + } + + if(ioMem != NULL) { + free(ioMem); + ioMem = NULL; + } + +#ifndef NO_DEBUGGER + elfCleanUp(); +#endif //NO_DEBUGGER + + systemSaveUpdateCounter = SYSTEM_SAVE_NOT_UPDATED; + + emulating = 0; +} + +int CPULoadRom(const char *szFile) +{ + romSize = 0x2000000; + if(rom != NULL) { + CPUCleanUp(); + } + + systemSaveUpdateCounter = SYSTEM_SAVE_NOT_UPDATED; + + rom = (u8 *)malloc(0x2000000); + if(rom == NULL) { + systemMessage(MSG_OUT_OF_MEMORY, N_("Failed to allocate memory for %s"), + "ROM"); + return 0; + } + workRAM = (u8 *)calloc(1, 0x40000); + if(workRAM == NULL) { + systemMessage(MSG_OUT_OF_MEMORY, N_("Failed to allocate memory for %s"), + "WRAM"); + return 0; + } + + u8 *whereToLoad = cpuIsMultiBoot ? workRAM : rom; + +#ifndef NO_DEBUGGER + if(CPUIsELF(szFile)) { + FILE *f = fopen(szFile, "rb"); + if(!f) { + systemMessage(MSG_ERROR_OPENING_IMAGE, N_("Error opening image %s"), + szFile); + free(rom); + rom = NULL; + free(workRAM); + workRAM = NULL; + return 0; + } + bool res = elfRead(szFile, romSize, f); + if(!res || romSize == 0) { + free(rom); + rom = NULL; + free(workRAM); + workRAM = NULL; + elfCleanUp(); + return 0; + } + } else +#endif //NO_DEBUGGER + if(szFile!=NULL) + { + if(!utilLoad(szFile, + utilIsGBAImage, + whereToLoad, + romSize)) { + free(rom); + rom = NULL; + free(workRAM); + workRAM = NULL; + return 0; + } + } + + u16 *temp = (u16 *)(rom+((romSize+1)&~1)); + int i; + for(i = (romSize+1)&~1; i < 0x2000000; i+=2) { + WRITE16LE(temp, (i >> 1) & 0xFFFF); + temp++; + } + + bios = (u8 *)calloc(1,0x4000); + if(bios == NULL) { + systemMessage(MSG_OUT_OF_MEMORY, N_("Failed to allocate memory for %s"), + "BIOS"); + CPUCleanUp(); + return 0; + } + internalRAM = (u8 *)calloc(1,0x8000); + if(internalRAM == NULL) { + systemMessage(MSG_OUT_OF_MEMORY, N_("Failed to allocate memory for %s"), + "IRAM"); + CPUCleanUp(); + return 0; + } + paletteRAM = (u8 *)calloc(1,0x400); + if(paletteRAM == NULL) { + systemMessage(MSG_OUT_OF_MEMORY, N_("Failed to allocate memory for %s"), + "PRAM"); + CPUCleanUp(); + return 0; + } + vram = (u8 *)calloc(1, 0x20000); + if(vram == NULL) { + systemMessage(MSG_OUT_OF_MEMORY, N_("Failed to allocate memory for %s"), + "VRAM"); + CPUCleanUp(); + return 0; + } + oam = (u8 *)calloc(1, 0x400); + if(oam == NULL) { + systemMessage(MSG_OUT_OF_MEMORY, N_("Failed to allocate memory for %s"), + "OAM"); + CPUCleanUp(); + return 0; + } + pix = (u8 *)calloc(1, 4 * 241 * 162); + if(pix == NULL) { + systemMessage(MSG_OUT_OF_MEMORY, N_("Failed to allocate memory for %s"), + "PIX"); + CPUCleanUp(); + return 0; + } + ioMem = (u8 *)calloc(1, 0x400); + if(ioMem == NULL) { + systemMessage(MSG_OUT_OF_MEMORY, N_("Failed to allocate memory for %s"), + "IO"); + CPUCleanUp(); + return 0; + } + + flashInit(); + eepromInit(); + + CPUUpdateRenderBuffers(true); + + return romSize; +} + +void doMirroring (bool b) +{ + u32 mirroredRomSize = (((romSize)>>20) & 0x3F)<<20; + u32 mirroredRomAddress = romSize; + if ((mirroredRomSize <=0x800000) && (b)) + { + mirroredRomAddress = mirroredRomSize; + if (mirroredRomSize==0) + mirroredRomSize=0x100000; + while (mirroredRomAddress<0x01000000) + { + memcpy ((u16 *)(rom+mirroredRomAddress), (u16 *)(rom), mirroredRomSize); + mirroredRomAddress+=mirroredRomSize; + } + } +} + +void CPUUpdateRender() +{ + switch(DISPCNT & 7) { + case 0: + if((!fxOn && !windowOn && !(layerEnable & 0x8000)) || + cpuDisableSfx) + renderLine = mode0RenderLine; + else if(fxOn && !windowOn && !(layerEnable & 0x8000)) + renderLine = mode0RenderLineNoWindow; + else + renderLine = mode0RenderLineAll; + break; + case 1: + if((!fxOn && !windowOn && !(layerEnable & 0x8000)) || + cpuDisableSfx) + renderLine = mode1RenderLine; + else if(fxOn && !windowOn && !(layerEnable & 0x8000)) + renderLine = mode1RenderLineNoWindow; + else + renderLine = mode1RenderLineAll; + break; + case 2: + if((!fxOn && !windowOn && !(layerEnable & 0x8000)) || + cpuDisableSfx) + renderLine = mode2RenderLine; + else if(fxOn && !windowOn && !(layerEnable & 0x8000)) + renderLine = mode2RenderLineNoWindow; + else + renderLine = mode2RenderLineAll; + break; + case 3: + if((!fxOn && !windowOn && !(layerEnable & 0x8000)) || + cpuDisableSfx) + renderLine = mode3RenderLine; + else if(fxOn && !windowOn && !(layerEnable & 0x8000)) + renderLine = mode3RenderLineNoWindow; + else + renderLine = mode3RenderLineAll; + break; + case 4: + if((!fxOn && !windowOn && !(layerEnable & 0x8000)) || + cpuDisableSfx) + renderLine = mode4RenderLine; + else if(fxOn && !windowOn && !(layerEnable & 0x8000)) + renderLine = mode4RenderLineNoWindow; + else + renderLine = mode4RenderLineAll; + break; + case 5: + if((!fxOn && !windowOn && !(layerEnable & 0x8000)) || + cpuDisableSfx) + renderLine = mode5RenderLine; + else if(fxOn && !windowOn && !(layerEnable & 0x8000)) + renderLine = mode5RenderLineNoWindow; + else + renderLine = mode5RenderLineAll; + default: + break; + } +} + +void CPUUpdateCPSR() +{ + u32 CPSR = reg[16].I & 0x40; + if(N_FLAG) + CPSR |= 0x80000000; + if(Z_FLAG) + CPSR |= 0x40000000; + if(C_FLAG) + CPSR |= 0x20000000; + if(V_FLAG) + CPSR |= 0x10000000; + if(!armState) + CPSR |= 0x00000020; + if(!armIrqEnable) + CPSR |= 0x80; + CPSR |= (armMode & 0x1F); + reg[16].I = CPSR; +} + +void CPUUpdateFlags(bool breakLoop) +{ + u32 CPSR = reg[16].I; + + N_FLAG = (CPSR & 0x80000000) ? true: false; + Z_FLAG = (CPSR & 0x40000000) ? true: false; + C_FLAG = (CPSR & 0x20000000) ? true: false; + V_FLAG = (CPSR & 0x10000000) ? true: false; + armState = (CPSR & 0x20) ? false : true; + armIrqEnable = (CPSR & 0x80) ? false : true; + if(breakLoop) { + if (armIrqEnable && (IF & IE) && (IME & 1)) + cpuNextEvent = cpuTotalTicks; + } +} + +void CPUUpdateFlags() +{ + CPUUpdateFlags(true); +} + +#ifdef WORDS_BIGENDIAN +static void CPUSwap(volatile u32 *a, volatile u32 *b) +{ + volatile u32 c = *b; + *b = *a; + *a = c; +} +#else +static void CPUSwap(u32 *a, u32 *b) +{ + u32 c = *b; + *b = *a; + *a = c; +} +#endif + +void CPUSwitchMode(int mode, bool saveState, bool breakLoop) +{ + // if(armMode == mode) + // return; + + CPUUpdateCPSR(); + + switch(armMode) { + case 0x10: + case 0x1F: + reg[R13_USR].I = reg[13].I; + reg[R14_USR].I = reg[14].I; + reg[17].I = reg[16].I; + break; + case 0x11: + CPUSwap(®[R8_FIQ].I, ®[8].I); + CPUSwap(®[R9_FIQ].I, ®[9].I); + CPUSwap(®[R10_FIQ].I, ®[10].I); + CPUSwap(®[R11_FIQ].I, ®[11].I); + CPUSwap(®[R12_FIQ].I, ®[12].I); + reg[R13_FIQ].I = reg[13].I; + reg[R14_FIQ].I = reg[14].I; + reg[SPSR_FIQ].I = reg[17].I; + break; + case 0x12: + reg[R13_IRQ].I = reg[13].I; + reg[R14_IRQ].I = reg[14].I; + reg[SPSR_IRQ].I = reg[17].I; + break; + case 0x13: + reg[R13_SVC].I = reg[13].I; + reg[R14_SVC].I = reg[14].I; + reg[SPSR_SVC].I = reg[17].I; + break; + case 0x17: + reg[R13_ABT].I = reg[13].I; + reg[R14_ABT].I = reg[14].I; + reg[SPSR_ABT].I = reg[17].I; + break; + case 0x1b: + reg[R13_UND].I = reg[13].I; + reg[R14_UND].I = reg[14].I; + reg[SPSR_UND].I = reg[17].I; + break; + } + + u32 CPSR = reg[16].I; + u32 SPSR = reg[17].I; + + switch(mode) { + case 0x10: + case 0x1F: + reg[13].I = reg[R13_USR].I; + reg[14].I = reg[R14_USR].I; + reg[16].I = SPSR; + break; + case 0x11: + CPUSwap(®[8].I, ®[R8_FIQ].I); + CPUSwap(®[9].I, ®[R9_FIQ].I); + CPUSwap(®[10].I, ®[R10_FIQ].I); + CPUSwap(®[11].I, ®[R11_FIQ].I); + CPUSwap(®[12].I, ®[R12_FIQ].I); + reg[13].I = reg[R13_FIQ].I; + reg[14].I = reg[R14_FIQ].I; + if(saveState) + reg[17].I = CPSR; + else + reg[17].I = reg[SPSR_FIQ].I; + break; + case 0x12: + reg[13].I = reg[R13_IRQ].I; + reg[14].I = reg[R14_IRQ].I; + reg[16].I = SPSR; + if(saveState) + reg[17].I = CPSR; + else + reg[17].I = reg[SPSR_IRQ].I; + break; + case 0x13: + reg[13].I = reg[R13_SVC].I; + reg[14].I = reg[R14_SVC].I; + reg[16].I = SPSR; + if(saveState) + reg[17].I = CPSR; + else + reg[17].I = reg[SPSR_SVC].I; + break; + case 0x17: + reg[13].I = reg[R13_ABT].I; + reg[14].I = reg[R14_ABT].I; + reg[16].I = SPSR; + if(saveState) + reg[17].I = CPSR; + else + reg[17].I = reg[SPSR_ABT].I; + break; + case 0x1b: + reg[13].I = reg[R13_UND].I; + reg[14].I = reg[R14_UND].I; + reg[16].I = SPSR; + if(saveState) + reg[17].I = CPSR; + else + reg[17].I = reg[SPSR_UND].I; + break; + default: + systemMessage(MSG_UNSUPPORTED_ARM_MODE, N_("Unsupported ARM mode %02x"), mode); + break; + } + armMode = mode; + CPUUpdateFlags(breakLoop); + CPUUpdateCPSR(); +} + +void CPUSwitchMode(int mode, bool saveState) +{ + CPUSwitchMode(mode, saveState, true); +} + +void CPUUndefinedException() +{ + u32 PC = reg[15].I; + bool savedArmState = armState; + CPUSwitchMode(0x1b, true, false); + reg[14].I = PC - (savedArmState ? 4 : 2); + reg[15].I = 0x04; + armState = true; + armIrqEnable = false; + armNextPC = 0x04; + ARM_PREFETCH; + reg[15].I += 4; +} + +void CPUSoftwareInterrupt() +{ + u32 PC = reg[15].I; + bool savedArmState = armState; + CPUSwitchMode(0x13, true, false); + reg[14].I = PC - (savedArmState ? 4 : 2); + reg[15].I = 0x08; + armState = true; + armIrqEnable = false; + armNextPC = 0x08; + ARM_PREFETCH; + reg[15].I += 4; +} + +void CPUSoftwareInterrupt(int comment) +{ + static bool disableMessage = false; + if(armState) comment >>= 16; +#ifdef BKPT_SUPPORT + if(comment == 0xff) { + dbgOutput(NULL, reg[0].I); + return; + } +#endif +#ifdef PROFILING + if(comment == 0xfe) { + profStartup(reg[0].I, reg[1].I); + return; + } + if(comment == 0xfd) { + profControl(reg[0].I); + return; + } + if(comment == 0xfc) { + profCleanup(); + return; + } + if(comment == 0xfb) { + profCount(); + return; + } +#endif + if(comment == 0xfa) { + agbPrintFlush(); + return; + } +#ifdef SDL + if(comment == 0xf9) { + emulating = 0; + cpuNextEvent = cpuTotalTicks; + cpuBreakLoop = true; + return; + } +#endif + if(useBios) { +#ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_SWI) { + log("SWI: %08x at %08x (0x%08x,0x%08x,0x%08x,VCOUNT = %2d)\n", comment, + armState ? armNextPC - 4: armNextPC -2, + reg[0].I, + reg[1].I, + reg[2].I, + VCOUNT); + } +#endif + CPUSoftwareInterrupt(); + return; + } + // This would be correct, but it causes problems if uncommented + // else { + // biosProtected = 0xe3a02004; + // } + + switch(comment) { + case 0x00: + BIOS_SoftReset(); + ARM_PREFETCH; + break; + case 0x01: + BIOS_RegisterRamReset(); + break; + case 0x02: +#ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_SWI) { + log("Halt: (VCOUNT = %2d)\n", + VCOUNT); + } +#endif + holdState = true; + holdType = -1; + cpuNextEvent = cpuTotalTicks; + break; + case 0x03: +#ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_SWI) { + log("Stop: (VCOUNT = %2d)\n", + VCOUNT); + } +#endif + holdState = true; + holdType = -1; + stopState = true; + cpuNextEvent = cpuTotalTicks; + break; + case 0x04: +#ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_SWI) { + log("IntrWait: 0x%08x,0x%08x (VCOUNT = %2d)\n", + reg[0].I, + reg[1].I, + VCOUNT); + } +#endif + CPUSoftwareInterrupt(); + break; + case 0x05: +#ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_SWI) { + log("VBlankIntrWait: (VCOUNT = %2d)\n", + VCOUNT); + } +#endif + CPUSoftwareInterrupt(); + break; + case 0x06: + CPUSoftwareInterrupt(); + break; + case 0x07: + CPUSoftwareInterrupt(); + break; + case 0x08: + BIOS_Sqrt(); + break; + case 0x09: + BIOS_ArcTan(); + break; + case 0x0A: + BIOS_ArcTan2(); + break; + case 0x0B: + { + int len = (reg[2].I & 0x1FFFFF) >>1; + if (!(((reg[0].I & 0xe000000) == 0) || + ((reg[0].I + len) & 0xe000000) == 0)) + { + if ((reg[2].I >> 24) & 1) + { + if ((reg[2].I >> 26) & 1) + SWITicks = (7 + memoryWait32[(reg[1].I>>24) & 0xF]) * (len>>1); + else + SWITicks = (8 + memoryWait[(reg[1].I>>24) & 0xF]) * (len); + } + else + { + if ((reg[2].I >> 26) & 1) + SWITicks = (10 + memoryWait32[(reg[0].I>>24) & 0xF] + + memoryWait32[(reg[1].I>>24) & 0xF]) * (len>>1); + else + SWITicks = (11 + memoryWait[(reg[0].I>>24) & 0xF] + + memoryWait[(reg[1].I>>24) & 0xF]) * len; + } + } + } + BIOS_CpuSet(); + break; + case 0x0C: + { + int len = (reg[2].I & 0x1FFFFF) >>5; + if (!(((reg[0].I & 0xe000000) == 0) || + ((reg[0].I + len) & 0xe000000) == 0)) + { + if ((reg[2].I >> 24) & 1) + SWITicks = (6 + memoryWait32[(reg[1].I>>24) & 0xF] + + 7 * (memoryWaitSeq32[(reg[1].I>>24) & 0xF] + 1)) * len; + else + SWITicks = (9 + memoryWait32[(reg[0].I>>24) & 0xF] + + memoryWait32[(reg[1].I>>24) & 0xF] + + 7 * (memoryWaitSeq32[(reg[0].I>>24) & 0xF] + + memoryWaitSeq32[(reg[1].I>>24) & 0xF] + 2)) * len; + } + } + BIOS_CpuFastSet(); + break; + case 0x0D: + BIOS_GetBiosChecksum(); + break; + case 0x0E: + BIOS_BgAffineSet(); + break; + case 0x0F: + BIOS_ObjAffineSet(); + break; + case 0x10: + { + int len = CPUReadHalfWord(reg[2].I); + if (!(((reg[0].I & 0xe000000) == 0) || + ((reg[0].I + len) & 0xe000000) == 0)) + SWITicks = (32 + memoryWait[(reg[0].I>>24) & 0xF]) * len; + } + BIOS_BitUnPack(); + break; + case 0x11: + { + u32 len = CPUReadMemory(reg[0].I) >> 8; + if(!(((reg[0].I & 0xe000000) == 0) || + ((reg[0].I + (len & 0x1fffff)) & 0xe000000) == 0)) + SWITicks = (9 + memoryWait[(reg[1].I>>24) & 0xF]) * len; + } + BIOS_LZ77UnCompWram(); + break; + case 0x12: + { + u32 len = CPUReadMemory(reg[0].I) >> 8; + if(!(((reg[0].I & 0xe000000) == 0) || + ((reg[0].I + (len & 0x1fffff)) & 0xe000000) == 0)) + SWITicks = (19 + memoryWait[(reg[1].I>>24) & 0xF]) * len; + } + BIOS_LZ77UnCompVram(); + break; + case 0x13: + { + u32 len = CPUReadMemory(reg[0].I) >> 8; + if(!(((reg[0].I & 0xe000000) == 0) || + ((reg[0].I + (len & 0x1fffff)) & 0xe000000) == 0)) + SWITicks = (29 + (memoryWait[(reg[0].I>>24) & 0xF]<<1)) * len; + } + BIOS_HuffUnComp(); + break; + case 0x14: + { + u32 len = CPUReadMemory(reg[0].I) >> 8; + if(!(((reg[0].I & 0xe000000) == 0) || + ((reg[0].I + (len & 0x1fffff)) & 0xe000000) == 0)) + SWITicks = (11 + memoryWait[(reg[0].I>>24) & 0xF] + + memoryWait[(reg[1].I>>24) & 0xF]) * len; + } + BIOS_RLUnCompWram(); + break; + case 0x15: + { + u32 len = CPUReadMemory(reg[0].I) >> 9; + if(!(((reg[0].I & 0xe000000) == 0) || + ((reg[0].I + (len & 0x1fffff)) & 0xe000000) == 0)) + SWITicks = (34 + (memoryWait[(reg[0].I>>24) & 0xF] << 1) + + memoryWait[(reg[1].I>>24) & 0xF]) * len; + } + BIOS_RLUnCompVram(); + break; + case 0x16: + { + u32 len = CPUReadMemory(reg[0].I) >> 8; + if(!(((reg[0].I & 0xe000000) == 0) || + ((reg[0].I + (len & 0x1fffff)) & 0xe000000) == 0)) + SWITicks = (13 + memoryWait[(reg[0].I>>24) & 0xF] + + memoryWait[(reg[1].I>>24) & 0xF]) * len; + } + BIOS_Diff8bitUnFilterWram(); + break; + case 0x17: + { + u32 len = CPUReadMemory(reg[0].I) >> 9; + if(!(((reg[0].I & 0xe000000) == 0) || + ((reg[0].I + (len & 0x1fffff)) & 0xe000000) == 0)) + SWITicks = (39 + (memoryWait[(reg[0].I>>24) & 0xF]<<1) + + memoryWait[(reg[1].I>>24) & 0xF]) * len; + } + BIOS_Diff8bitUnFilterVram(); + break; + case 0x18: + { + u32 len = CPUReadMemory(reg[0].I) >> 9; + if(!(((reg[0].I & 0xe000000) == 0) || + ((reg[0].I + (len & 0x1fffff)) & 0xe000000) == 0)) + SWITicks = (13 + memoryWait[(reg[0].I>>24) & 0xF] + + memoryWait[(reg[1].I>>24) & 0xF]) * len; + } + BIOS_Diff16bitUnFilter(); + break; + case 0x19: +#ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_SWI) { + log("SoundBiasSet: 0x%08x (VCOUNT = %2d)\n", + reg[0].I, + VCOUNT); + } +#endif + if(reg[0].I) + soundPause(); + else + soundResume(); + break; + case 0x1F: + BIOS_MidiKey2Freq(); + break; + case 0x2A: + BIOS_SndDriverJmpTableCopy(); + // let it go, because we don't really emulate this function + default: +#ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_SWI) { + log("SWI: %08x at %08x (0x%08x,0x%08x,0x%08x,VCOUNT = %2d)\n", comment, + armState ? armNextPC - 4: armNextPC -2, + reg[0].I, + reg[1].I, + reg[2].I, + VCOUNT); + } +#endif + + if(!disableMessage) { + systemMessage(MSG_UNSUPPORTED_BIOS_FUNCTION, + N_("Unsupported BIOS function %02x called from %08x. A BIOS file is needed in order to get correct behaviour."), + comment, + armMode ? armNextPC - 4: armNextPC - 2); + disableMessage = true; + } + break; + } +} + +void CPUCompareVCOUNT() +{ + if(VCOUNT == (DISPSTAT >> 8)) { + DISPSTAT |= 4; + UPDATE_REG(0x04, DISPSTAT); + + if(DISPSTAT & 0x20) { + IF |= 4; + UPDATE_REG(0x202, IF); + } + } else { + DISPSTAT &= 0xFFFB; + UPDATE_REG(0x4, DISPSTAT); + } + if (layerEnableDelay>0) + { + layerEnableDelay--; + if (layerEnableDelay==1) + layerEnable = layerSettings & DISPCNT; + } + +} + +void doDMA(u32 &s, u32 &d, u32 si, u32 di, u32 c, int transfer32) +{ + int sm = s >> 24; + int dm = d >> 24; + int sw = 0; + int dw = 0; + int sc = c; + + cpuDmaCount = c; + // This is done to get the correct waitstates. + if (sm>15) + sm=15; + if (dm>15) + dm=15; + + //if ((sm>=0x05) && (sm<=0x07) || (dm>=0x05) && (dm <=0x07)) + // blank = (((DISPSTAT | ((DISPSTAT>>1)&1))==1) ? true : false); + + if(transfer32) { + s &= 0xFFFFFFFC; + if(s < 0x02000000 && (reg[15].I >> 24)) { + while(c != 0) { + CPUWriteMemory(d, 0); + d += di; + c--; + } + } else { + while(c != 0) { + cpuDmaLast = CPUReadMemory(s); + CPUWriteMemory(d, cpuDmaLast); + d += di; + s += si; + c--; + } + } + } else { + s &= 0xFFFFFFFE; + si = (int)si >> 1; + di = (int)di >> 1; + if(s < 0x02000000 && (reg[15].I >> 24)) { + while(c != 0) { + CPUWriteHalfWord(d, 0); + d += di; + c--; + } + } else { + while(c != 0) { + cpuDmaLast = CPUReadHalfWord(s); + CPUWriteHalfWord(d, cpuDmaLast); + cpuDmaLast |= (cpuDmaLast<<16); + d += di; + s += si; + c--; + } + } + } + + cpuDmaCount = 0; + + int totalTicks = 0; + + if(transfer32) { + sw =1+memoryWaitSeq32[sm & 15]; + dw =1+memoryWaitSeq32[dm & 15]; + totalTicks = (sw+dw)*(sc-1) + 6 + memoryWait32[sm & 15] + + memoryWaitSeq32[dm & 15]; + } + else + { + sw = 1+memoryWaitSeq[sm & 15]; + dw = 1+memoryWaitSeq[dm & 15]; + totalTicks = (sw+dw)*(sc-1) + 6 + memoryWait[sm & 15] + + memoryWaitSeq[dm & 15]; + } + + cpuDmaTicksToUpdate += totalTicks; + +} + +void CPUCheckDMA(int reason, int dmamask) +{ + // DMA 0 + if((DM0CNT_H & 0x8000) && (dmamask & 1)) { + if(((DM0CNT_H >> 12) & 3) == reason) { + u32 sourceIncrement = 4; + u32 destIncrement = 4; + switch((DM0CNT_H >> 7) & 3) { + case 0: + break; + case 1: + sourceIncrement = (u32)-4; + break; + case 2: + sourceIncrement = 0; + break; + } + switch((DM0CNT_H >> 5) & 3) { + case 0: + break; + case 1: + destIncrement = (u32)-4; + break; + case 2: + destIncrement = 0; + break; + } +#ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_DMA0) { + int count = (DM0CNT_L ? DM0CNT_L : 0x4000) << 1; + if(DM0CNT_H & 0x0400) + count <<= 1; + log("DMA0: s=%08x d=%08x c=%04x count=%08x\n", dma0Source, dma0Dest, + DM0CNT_H, + count); + } +#endif + doDMA(dma0Source, dma0Dest, sourceIncrement, destIncrement, + DM0CNT_L ? DM0CNT_L : 0x4000, + DM0CNT_H & 0x0400); + cpuDmaHack = true; + + if(DM0CNT_H & 0x4000) { + IF |= 0x0100; + UPDATE_REG(0x202, IF); + cpuNextEvent = cpuTotalTicks; + } + + if(((DM0CNT_H >> 5) & 3) == 3) { + dma0Dest = DM0DAD_L | (DM0DAD_H << 16); + } + + if(!(DM0CNT_H & 0x0200) || (reason == 0)) { + DM0CNT_H &= 0x7FFF; + UPDATE_REG(0xBA, DM0CNT_H); + } + } + } + + // DMA 1 + if((DM1CNT_H & 0x8000) && (dmamask & 2)) { + if(((DM1CNT_H >> 12) & 3) == reason) { + u32 sourceIncrement = 4; + u32 destIncrement = 4; + switch((DM1CNT_H >> 7) & 3) { + case 0: + break; + case 1: + sourceIncrement = (u32)-4; + break; + case 2: + sourceIncrement = 0; + break; + } + switch((DM1CNT_H >> 5) & 3) { + case 0: + break; + case 1: + destIncrement = (u32)-4; + break; + case 2: + destIncrement = 0; + break; + } + if(reason == 3) { +#ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_DMA1) { + log("DMA1: s=%08x d=%08x c=%04x count=%08x\n", dma1Source, dma1Dest, + DM1CNT_H, + 16); + } +#endif + doDMA(dma1Source, dma1Dest, sourceIncrement, 0, 4, + 0x0400); + } else { +#ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_DMA1) { + int count = (DM1CNT_L ? DM1CNT_L : 0x4000) << 1; + if(DM1CNT_H & 0x0400) + count <<= 1; + log("DMA1: s=%08x d=%08x c=%04x count=%08x\n", dma1Source, dma1Dest, + DM1CNT_H, + count); + } +#endif + doDMA(dma1Source, dma1Dest, sourceIncrement, destIncrement, + DM1CNT_L ? DM1CNT_L : 0x4000, + DM1CNT_H & 0x0400); + } + cpuDmaHack = true; + + if(DM1CNT_H & 0x4000) { + IF |= 0x0200; + UPDATE_REG(0x202, IF); + cpuNextEvent = cpuTotalTicks; + } + + if(((DM1CNT_H >> 5) & 3) == 3) { + dma1Dest = DM1DAD_L | (DM1DAD_H << 16); + } + + if(!(DM1CNT_H & 0x0200) || (reason == 0)) { + DM1CNT_H &= 0x7FFF; + UPDATE_REG(0xC6, DM1CNT_H); + } + } + } + + // DMA 2 + if((DM2CNT_H & 0x8000) && (dmamask & 4)) { + if(((DM2CNT_H >> 12) & 3) == reason) { + u32 sourceIncrement = 4; + u32 destIncrement = 4; + switch((DM2CNT_H >> 7) & 3) { + case 0: + break; + case 1: + sourceIncrement = (u32)-4; + break; + case 2: + sourceIncrement = 0; + break; + } + switch((DM2CNT_H >> 5) & 3) { + case 0: + break; + case 1: + destIncrement = (u32)-4; + break; + case 2: + destIncrement = 0; + break; + } + if(reason == 3) { +#ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_DMA2) { + int count = (4) << 2; + log("DMA2: s=%08x d=%08x c=%04x count=%08x\n", dma2Source, dma2Dest, + DM2CNT_H, + count); + } +#endif + doDMA(dma2Source, dma2Dest, sourceIncrement, 0, 4, + 0x0400); + } else { +#ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_DMA2) { + int count = (DM2CNT_L ? DM2CNT_L : 0x4000) << 1; + if(DM2CNT_H & 0x0400) + count <<= 1; + log("DMA2: s=%08x d=%08x c=%04x count=%08x\n", dma2Source, dma2Dest, + DM2CNT_H, + count); + } +#endif + doDMA(dma2Source, dma2Dest, sourceIncrement, destIncrement, + DM2CNT_L ? DM2CNT_L : 0x4000, + DM2CNT_H & 0x0400); + } + cpuDmaHack = true; + + if(DM2CNT_H & 0x4000) { + IF |= 0x0400; + UPDATE_REG(0x202, IF); + cpuNextEvent = cpuTotalTicks; + } + + if(((DM2CNT_H >> 5) & 3) == 3) { + dma2Dest = DM2DAD_L | (DM2DAD_H << 16); + } + + if(!(DM2CNT_H & 0x0200) || (reason == 0)) { + DM2CNT_H &= 0x7FFF; + UPDATE_REG(0xD2, DM2CNT_H); + } + } + } + + // DMA 3 + if((DM3CNT_H & 0x8000) && (dmamask & 8)) { + if(((DM3CNT_H >> 12) & 3) == reason) { + u32 sourceIncrement = 4; + u32 destIncrement = 4; + switch((DM3CNT_H >> 7) & 3) { + case 0: + break; + case 1: + sourceIncrement = (u32)-4; + break; + case 2: + sourceIncrement = 0; + break; + } + switch((DM3CNT_H >> 5) & 3) { + case 0: + break; + case 1: + destIncrement = (u32)-4; + break; + case 2: + destIncrement = 0; + break; + } +#ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_DMA3) { + int count = (DM3CNT_L ? DM3CNT_L : 0x10000) << 1; + if(DM3CNT_H & 0x0400) + count <<= 1; + log("DMA3: s=%08x d=%08x c=%04x count=%08x\n", dma3Source, dma3Dest, + DM3CNT_H, + count); + } +#endif + doDMA(dma3Source, dma3Dest, sourceIncrement, destIncrement, + DM3CNT_L ? DM3CNT_L : 0x10000, + DM3CNT_H & 0x0400); + if(DM3CNT_H & 0x4000) { + IF |= 0x0800; + UPDATE_REG(0x202, IF); + cpuNextEvent = cpuTotalTicks; + } + + if(((DM3CNT_H >> 5) & 3) == 3) { + dma3Dest = DM3DAD_L | (DM3DAD_H << 16); + } + + if(!(DM3CNT_H & 0x0200) || (reason == 0)) { + DM3CNT_H &= 0x7FFF; + UPDATE_REG(0xDE, DM3CNT_H); + } + } + } +} + +void CPUUpdateRegister(u32 address, u16 value) +{ + switch(address) + { + case 0x00: + { // we need to place the following code in { } because we declare & initialize variables in a case statement + if((value & 7) > 5) { + // display modes above 0-5 are prohibited + DISPCNT = (value & 7); + } + bool change = (0 != ((DISPCNT ^ value) & 0x80)); + bool changeBG = (0 != ((DISPCNT ^ value) & 0x0F00)); + u16 changeBGon = ((~DISPCNT) & value) & 0x0F00; // these layers are being activated + + DISPCNT = (value & 0xFFF7); // bit 3 can only be accessed by the BIOS to enable GBC mode + UPDATE_REG(0x00, DISPCNT); + + if(changeBGon) { + layerEnableDelay = 4; + layerEnable = layerSettings & value & (~changeBGon); + } else { + layerEnable = layerSettings & value; + // CPUUpdateTicks(); + } + + windowOn = (layerEnable & 0x6000) ? true : false; + if(change && !((value & 0x80))) { + if(!(DISPSTAT & 1)) { + lcdTicks = 1008; + // VCOUNT = 0; + // UPDATE_REG(0x06, VCOUNT); + DISPSTAT &= 0xFFFC; + UPDATE_REG(0x04, DISPSTAT); + CPUCompareVCOUNT(); + } + // (*renderLine)(); + } + CPUUpdateRender(); + // we only care about changes in BG0-BG3 + if(changeBG) { + CPUUpdateRenderBuffers(false); + } + break; + } + case 0x04: + DISPSTAT = (value & 0xFF38) | (DISPSTAT & 7); + UPDATE_REG(0x04, DISPSTAT); + break; + case 0x06: + // not writable + break; + case 0x08: + BG0CNT = (value & 0xDFCF); + UPDATE_REG(0x08, BG0CNT); + break; + case 0x0A: + BG1CNT = (value & 0xDFCF); + UPDATE_REG(0x0A, BG1CNT); + break; + case 0x0C: + BG2CNT = (value & 0xFFCF); + UPDATE_REG(0x0C, BG2CNT); + break; + case 0x0E: + BG3CNT = (value & 0xFFCF); + UPDATE_REG(0x0E, BG3CNT); + break; + case 0x10: + BG0HOFS = value & 511; + UPDATE_REG(0x10, BG0HOFS); + break; + case 0x12: + BG0VOFS = value & 511; + UPDATE_REG(0x12, BG0VOFS); + break; + case 0x14: + BG1HOFS = value & 511; + UPDATE_REG(0x14, BG1HOFS); + break; + case 0x16: + BG1VOFS = value & 511; + UPDATE_REG(0x16, BG1VOFS); + break; + case 0x18: + BG2HOFS = value & 511; + UPDATE_REG(0x18, BG2HOFS); + break; + case 0x1A: + BG2VOFS = value & 511; + UPDATE_REG(0x1A, BG2VOFS); + break; + case 0x1C: + BG3HOFS = value & 511; + UPDATE_REG(0x1C, BG3HOFS); + break; + case 0x1E: + BG3VOFS = value & 511; + UPDATE_REG(0x1E, BG3VOFS); + break; + case 0x20: + BG2PA = value; + UPDATE_REG(0x20, BG2PA); + break; + case 0x22: + BG2PB = value; + UPDATE_REG(0x22, BG2PB); + break; + case 0x24: + BG2PC = value; + UPDATE_REG(0x24, BG2PC); + break; + case 0x26: + BG2PD = value; + UPDATE_REG(0x26, BG2PD); + break; + case 0x28: + BG2X_L = value; + UPDATE_REG(0x28, BG2X_L); + gfxBG2Changed |= 1; + break; + case 0x2A: + BG2X_H = (value & 0xFFF); + UPDATE_REG(0x2A, BG2X_H); + gfxBG2Changed |= 1; + break; + case 0x2C: + BG2Y_L = value; + UPDATE_REG(0x2C, BG2Y_L); + gfxBG2Changed |= 2; + break; + case 0x2E: + BG2Y_H = value & 0xFFF; + UPDATE_REG(0x2E, BG2Y_H); + gfxBG2Changed |= 2; + break; + case 0x30: + BG3PA = value; + UPDATE_REG(0x30, BG3PA); + break; + case 0x32: + BG3PB = value; + UPDATE_REG(0x32, BG3PB); + break; + case 0x34: + BG3PC = value; + UPDATE_REG(0x34, BG3PC); + break; + case 0x36: + BG3PD = value; + UPDATE_REG(0x36, BG3PD); + break; + case 0x38: + BG3X_L = value; + UPDATE_REG(0x38, BG3X_L); + gfxBG3Changed |= 1; + break; + case 0x3A: + BG3X_H = value & 0xFFF; + UPDATE_REG(0x3A, BG3X_H); + gfxBG3Changed |= 1; + break; + case 0x3C: + BG3Y_L = value; + UPDATE_REG(0x3C, BG3Y_L); + gfxBG3Changed |= 2; + break; + case 0x3E: + BG3Y_H = value & 0xFFF; + UPDATE_REG(0x3E, BG3Y_H); + gfxBG3Changed |= 2; + break; + case 0x40: + WIN0H = value; + UPDATE_REG(0x40, WIN0H); + CPUUpdateWindow0(); + break; + case 0x42: + WIN1H = value; + UPDATE_REG(0x42, WIN1H); + CPUUpdateWindow1(); + break; + case 0x44: + WIN0V = value; + UPDATE_REG(0x44, WIN0V); + break; + case 0x46: + WIN1V = value; + UPDATE_REG(0x46, WIN1V); + break; + case 0x48: + WININ = value & 0x3F3F; + UPDATE_REG(0x48, WININ); + break; + case 0x4A: + WINOUT = value & 0x3F3F; + UPDATE_REG(0x4A, WINOUT); + break; + case 0x4C: + MOSAIC = value; + UPDATE_REG(0x4C, MOSAIC); + break; + case 0x50: + BLDMOD = value & 0x3FFF; + UPDATE_REG(0x50, BLDMOD); + fxOn = ((BLDMOD>>6)&3) != 0; + CPUUpdateRender(); + break; + case 0x52: + COLEV = value & 0x1F1F; + UPDATE_REG(0x52, COLEV); + break; + case 0x54: + COLY = value & 0x1F; + UPDATE_REG(0x54, COLY); + break; + case 0x60: + case 0x62: + case 0x64: + case 0x68: + case 0x6c: + case 0x70: + case 0x72: + case 0x74: + case 0x78: + case 0x7c: + case 0x80: + case 0x84: + soundEvent(address&0xFF, (u8)(value & 0xFF)); + soundEvent((address&0xFF)+1, (u8)(value>>8)); + break; + case 0x82: + case 0x88: + case 0xa0: + case 0xa2: + case 0xa4: + case 0xa6: + case 0x90: + case 0x92: + case 0x94: + case 0x96: + case 0x98: + case 0x9a: + case 0x9c: + case 0x9e: + soundEvent(address&0xFF, value); + break; + case 0xB0: + DM0SAD_L = value; + UPDATE_REG(0xB0, DM0SAD_L); + break; + case 0xB2: + DM0SAD_H = value & 0x07FF; + UPDATE_REG(0xB2, DM0SAD_H); + break; + case 0xB4: + DM0DAD_L = value; + UPDATE_REG(0xB4, DM0DAD_L); + break; + case 0xB6: + DM0DAD_H = value & 0x07FF; + UPDATE_REG(0xB6, DM0DAD_H); + break; + case 0xB8: + DM0CNT_L = value & 0x3FFF; + UPDATE_REG(0xB8, 0); + break; + case 0xBA: + { + bool start = ((DM0CNT_H ^ value) & 0x8000) ? true : false; + value &= 0xF7E0; + + DM0CNT_H = value; + UPDATE_REG(0xBA, DM0CNT_H); + + if(start && (value & 0x8000)) { + dma0Source = DM0SAD_L | (DM0SAD_H << 16); + dma0Dest = DM0DAD_L | (DM0DAD_H << 16); + CPUCheckDMA(0, 1); + } + } + break; + case 0xBC: + DM1SAD_L = value; + UPDATE_REG(0xBC, DM1SAD_L); + break; + case 0xBE: + DM1SAD_H = value & 0x0FFF; + UPDATE_REG(0xBE, DM1SAD_H); + break; + case 0xC0: + DM1DAD_L = value; + UPDATE_REG(0xC0, DM1DAD_L); + break; + case 0xC2: + DM1DAD_H = value & 0x07FF; + UPDATE_REG(0xC2, DM1DAD_H); + break; + case 0xC4: + DM1CNT_L = value & 0x3FFF; + UPDATE_REG(0xC4, 0); + break; + case 0xC6: + { + bool start = ((DM1CNT_H ^ value) & 0x8000) ? true : false; + value &= 0xF7E0; + + DM1CNT_H = value; + UPDATE_REG(0xC6, DM1CNT_H); + + if(start && (value & 0x8000)) { + dma1Source = DM1SAD_L | (DM1SAD_H << 16); + dma1Dest = DM1DAD_L | (DM1DAD_H << 16); + CPUCheckDMA(0, 2); + } + } + break; + case 0xC8: + DM2SAD_L = value; + UPDATE_REG(0xC8, DM2SAD_L); + break; + case 0xCA: + DM2SAD_H = value & 0x0FFF; + UPDATE_REG(0xCA, DM2SAD_H); + break; + case 0xCC: + DM2DAD_L = value; + UPDATE_REG(0xCC, DM2DAD_L); + break; + case 0xCE: + DM2DAD_H = value & 0x07FF; + UPDATE_REG(0xCE, DM2DAD_H); + break; + case 0xD0: + DM2CNT_L = value & 0x3FFF; + UPDATE_REG(0xD0, 0); + break; + case 0xD2: + { + bool start = ((DM2CNT_H ^ value) & 0x8000) ? true : false; + + value &= 0xF7E0; + + DM2CNT_H = value; + UPDATE_REG(0xD2, DM2CNT_H); + + if(start && (value & 0x8000)) { + dma2Source = DM2SAD_L | (DM2SAD_H << 16); + dma2Dest = DM2DAD_L | (DM2DAD_H << 16); + + CPUCheckDMA(0, 4); + } + } + break; + case 0xD4: + DM3SAD_L = value; + UPDATE_REG(0xD4, DM3SAD_L); + break; + case 0xD6: + DM3SAD_H = value & 0x0FFF; + UPDATE_REG(0xD6, DM3SAD_H); + break; + case 0xD8: + DM3DAD_L = value; + UPDATE_REG(0xD8, DM3DAD_L); + break; + case 0xDA: + DM3DAD_H = value & 0x0FFF; + UPDATE_REG(0xDA, DM3DAD_H); + break; + case 0xDC: + DM3CNT_L = value; + UPDATE_REG(0xDC, 0); + break; + case 0xDE: + { + bool start = ((DM3CNT_H ^ value) & 0x8000) ? true : false; + + value &= 0xFFE0; + + DM3CNT_H = value; + UPDATE_REG(0xDE, DM3CNT_H); + + if(start && (value & 0x8000)) { + dma3Source = DM3SAD_L | (DM3SAD_H << 16); + dma3Dest = DM3DAD_L | (DM3DAD_H << 16); + CPUCheckDMA(0,8); + } + } + break; + case 0x100: + timer0Reload = value; + interp_rate(); + break; + case 0x102: + timer0Value = value; + timerOnOffDelay|=1; + cpuNextEvent = cpuTotalTicks; + break; + case 0x104: + timer1Reload = value; + interp_rate(); + break; + case 0x106: + timer1Value = value; + timerOnOffDelay|=2; + cpuNextEvent = cpuTotalTicks; + break; + case 0x108: + timer2Reload = value; + break; + case 0x10A: + timer2Value = value; + timerOnOffDelay|=4; + cpuNextEvent = cpuTotalTicks; + break; + case 0x10C: + timer3Reload = value; + break; + case 0x10E: + timer3Value = value; + timerOnOffDelay|=8; + cpuNextEvent = cpuTotalTicks; + break; + + + case COMM_SIOCNT: + StartLink(value); + /* + // old code path for no linking... + { + if (value & 0x80) { + value &= 0xff7f; + if ((value & 1) && (value & 0x4000)) { + UPDATE_REG(COMM_SIODATA8, 0xFF); + IF |= 0x80; + UPDATE_REG(0x202, IF); + value &= 0x7f7f; + } + } + UPDATE_REG(COMM_SIOCNT, value); + } + */ + break; + + case COMM_SIODATA8: + if (gba_link_enabled) + LinkSSend(value); + UPDATE_REG(COMM_RCNT, value); + break; + + case 0x130: + P1 |= (value & 0x3FF); + UPDATE_REG(0x130, P1); + break; + + case 0x132: + UPDATE_REG(0x132, value & 0xC3FF); + break; + + case COMM_RCNT: + StartGPLink(value); + break; + + case COMM_JOYCNT: + { + u16 cur = READ16LE(&ioMem[COMM_JOYCNT]); + + if (value & JOYCNT_RESET) cur &= ~JOYCNT_RESET; + if (value & JOYCNT_RECV_COMPLETE) cur &= ~JOYCNT_RECV_COMPLETE; + if (value & JOYCNT_SEND_COMPLETE) cur &= ~JOYCNT_SEND_COMPLETE; + if (value & JOYCNT_INT_ENABLE) cur |= JOYCNT_INT_ENABLE; + + UPDATE_REG(COMM_JOYCNT, cur); + } + break; + + case COMM_JOY_RECV_L: + UPDATE_REG(COMM_JOY_RECV_L, value); + break; + case COMM_JOY_RECV_H: + UPDATE_REG(COMM_JOY_RECV_H, value); + break; + + case COMM_JOY_TRANS_L: + UPDATE_REG(COMM_JOY_TRANS_L, value); + UPDATE_REG(COMM_JOYSTAT, READ16LE(&ioMem[COMM_JOYSTAT]) | JOYSTAT_SEND); + break; + case COMM_JOY_TRANS_H: + UPDATE_REG(COMM_JOY_TRANS_H, value); + break; + + case COMM_JOYSTAT: + UPDATE_REG(COMM_JOYSTAT, (READ16LE(&ioMem[COMM_JOYSTAT]) & 0xf) | (value & 0xf0)); + break; + + case 0x200: + IE = value & 0x3FFF; + UPDATE_REG(0x200, IE); + if ((IME & 1) && (IF & IE) && armIrqEnable) + cpuNextEvent = cpuTotalTicks; + break; + case 0x202: + IF ^= (value & IF); + UPDATE_REG(0x202, IF); + break; + case 0x204: + { + memoryWait[0x0e] = memoryWaitSeq[0x0e] = gamepakRamWaitState[value & 3]; + + if(!speedHack) { + memoryWait[0x08] = memoryWait[0x09] = gamepakWaitState[(value >> 2) & 3]; + memoryWaitSeq[0x08] = memoryWaitSeq[0x09] = + gamepakWaitState0[(value >> 4) & 1]; + + memoryWait[0x0a] = memoryWait[0x0b] = gamepakWaitState[(value >> 5) & 3]; + memoryWaitSeq[0x0a] = memoryWaitSeq[0x0b] = + gamepakWaitState1[(value >> 7) & 1]; + + memoryWait[0x0c] = memoryWait[0x0d] = gamepakWaitState[(value >> 8) & 3]; + memoryWaitSeq[0x0c] = memoryWaitSeq[0x0d] = + gamepakWaitState2[(value >> 10) & 1]; + } else { + memoryWait[0x08] = memoryWait[0x09] = 3; + memoryWaitSeq[0x08] = memoryWaitSeq[0x09] = 1; + + memoryWait[0x0a] = memoryWait[0x0b] = 3; + memoryWaitSeq[0x0a] = memoryWaitSeq[0x0b] = 1; + + memoryWait[0x0c] = memoryWait[0x0d] = 3; + memoryWaitSeq[0x0c] = memoryWaitSeq[0x0d] = 1; + } + + for(int i = 8; i < 15; i++) { + memoryWait32[i] = memoryWait[i] + memoryWaitSeq[i] + 1; + memoryWaitSeq32[i] = memoryWaitSeq[i]*2 + 1; + } + + if((value & 0x4000) == 0x4000) { + busPrefetchEnable = true; + busPrefetch = false; + busPrefetchCount = 0; + } else { + busPrefetchEnable = false; + busPrefetch = false; + busPrefetchCount = 0; + } + UPDATE_REG(0x204, value & 0x7FFF); + + } + break; + case 0x208: + IME = value & 1; + UPDATE_REG(0x208, IME); + if ((IME & 1) && (IF & IE) && armIrqEnable) + cpuNextEvent = cpuTotalTicks; + break; + case 0x300: + if(value != 0) + value &= 0xFFFE; + UPDATE_REG(0x300, value); + break; + default: + UPDATE_REG(address&0x3FE, value); + break; + } +} + +void applyTimer () +{ + if (timerOnOffDelay & 1) + { + timer0ClockReload = TIMER_TICKS[timer0Value & 3]; + if(!timer0On && (timer0Value & 0x80)) { + // reload the counter + TM0D = timer0Reload; + timer0Ticks = (0x10000 - TM0D) << timer0ClockReload; + UPDATE_REG(0x100, TM0D); + } + timer0On = timer0Value & 0x80 ? true : false; + TM0CNT = timer0Value & 0xC7; + interp_rate(); + UPDATE_REG(0x102, TM0CNT); + // CPUUpdateTicks(); + } + if (timerOnOffDelay & 2) + { + timer1ClockReload = TIMER_TICKS[timer1Value & 3]; + if(!timer1On && (timer1Value & 0x80)) { + // reload the counter + TM1D = timer1Reload; + timer1Ticks = (0x10000 - TM1D) << timer1ClockReload; + UPDATE_REG(0x104, TM1D); + } + timer1On = timer1Value & 0x80 ? true : false; + TM1CNT = timer1Value & 0xC7; + interp_rate(); + UPDATE_REG(0x106, TM1CNT); + } + if (timerOnOffDelay & 4) + { + timer2ClockReload = TIMER_TICKS[timer2Value & 3]; + if(!timer2On && (timer2Value & 0x80)) { + // reload the counter + TM2D = timer2Reload; + timer2Ticks = (0x10000 - TM2D) << timer2ClockReload; + UPDATE_REG(0x108, TM2D); + } + timer2On = timer2Value & 0x80 ? true : false; + TM2CNT = timer2Value & 0xC7; + UPDATE_REG(0x10A, TM2CNT); + } + if (timerOnOffDelay & 8) + { + timer3ClockReload = TIMER_TICKS[timer3Value & 3]; + if(!timer3On && (timer3Value & 0x80)) { + // reload the counter + TM3D = timer3Reload; + timer3Ticks = (0x10000 - TM3D) << timer3ClockReload; + UPDATE_REG(0x10C, TM3D); + } + timer3On = timer3Value & 0x80 ? true : false; + TM3CNT = timer3Value & 0xC7; + UPDATE_REG(0x10E, TM3CNT); + } + cpuNextEvent = CPUUpdateTicks(); + timerOnOffDelay = 0; +} + +u8 cpuBitsSet[256]; +u8 cpuLowestBitSet[256]; + +void CPUInit(const char *biosFileName, bool useBiosFile) +{ +#ifdef WORDS_BIGENDIAN + if(!cpuBiosSwapped) { + for(unsigned int i = 0; i < sizeof(myROM)/4; i++) { + WRITE32LE(&myROM[i], myROM[i]); + } + cpuBiosSwapped = true; + } +#endif + gbaSaveType = 0; + eepromInUse = 0; + saveType = 0; + useBios = false; + + if(useBiosFile) { + int size = 0x4000; + if(utilLoad(biosFileName, + CPUIsGBABios, + bios, + size)) { + if(size == 0x4000) + useBios = true; + else + systemMessage(MSG_INVALID_BIOS_FILE_SIZE, N_("Invalid BIOS file size")); + } + } + + if(!useBios) { + memcpy(bios, myROM, sizeof(myROM)); + } + + int i = 0; + + biosProtected[0] = 0x00; + biosProtected[1] = 0xf0; + biosProtected[2] = 0x29; + biosProtected[3] = 0xe1; + + for(i = 0; i < 256; i++) { + int count = 0; + int j; + for(j = 0; j < 8; j++) + if(i & (1 << j)) + count++; + cpuBitsSet[i] = count; + + for(j = 0; j < 8; j++) + if(i & (1 << j)) + break; + cpuLowestBitSet[i] = j; + } + + for(i = 0; i < 0x400; i++) + ioReadable[i] = true; + for(i = 0x10; i < 0x48; i++) + ioReadable[i] = false; + for(i = 0x4c; i < 0x50; i++) + ioReadable[i] = false; + for(i = 0x54; i < 0x60; i++) + ioReadable[i] = false; + for(i = 0x8c; i < 0x90; i++) + ioReadable[i] = false; + for(i = 0xa0; i < 0xb8; i++) + ioReadable[i] = false; + for(i = 0xbc; i < 0xc4; i++) + ioReadable[i] = false; + for(i = 0xc8; i < 0xd0; i++) + ioReadable[i] = false; + for(i = 0xd4; i < 0xdc; i++) + ioReadable[i] = false; + for(i = 0xe0; i < 0x100; i++) + ioReadable[i] = false; + for(i = 0x110; i < 0x120; i++) + ioReadable[i] = false; + for(i = 0x12c; i < 0x130; i++) + ioReadable[i] = false; + for(i = 0x138; i < 0x140; i++) + ioReadable[i] = false; + for(i = 0x144; i < 0x150; i++) + ioReadable[i] = false; + for(i = 0x15c; i < 0x200; i++) + ioReadable[i] = false; + for(i = 0x20c; i < 0x300; i++) + ioReadable[i] = false; + for(i = 0x304; i < 0x400; i++) + ioReadable[i] = false; + + if(romSize < 0x1fe2000) { + *((u16 *)&rom[0x1fe209c]) = 0xdffa; // SWI 0xFA + *((u16 *)&rom[0x1fe209e]) = 0x4770; // BX LR + } else { + agbPrintEnable(false); + } +} + +void CPUReset() +{ + if(gbaSaveType == 0) { + if(eepromInUse) + gbaSaveType = 3; + else + switch(saveType) { + case 1: + gbaSaveType = 1; + break; + case 2: + gbaSaveType = 2; + break; + } + } + rtcReset(); + // clean registers + memset(®[0], 0, sizeof(reg)); + // clean OAM + memset(oam, 0, 0x400); + // clean palette + memset(paletteRAM, 0, 0x400); + // clean picture + memset(pix, 0, 4*160*240); + // clean vram + memset(vram, 0, 0x20000); + // clean io memory + memset(ioMem, 0, 0x400); + + DISPCNT = 0x0080; + DISPSTAT = 0x0000; + VCOUNT = (useBios && !skipBios) ? 0 :0x007E; + BG0CNT = 0x0000; + BG1CNT = 0x0000; + BG2CNT = 0x0000; + BG3CNT = 0x0000; + BG0HOFS = 0x0000; + BG0VOFS = 0x0000; + BG1HOFS = 0x0000; + BG1VOFS = 0x0000; + BG2HOFS = 0x0000; + BG2VOFS = 0x0000; + BG3HOFS = 0x0000; + BG3VOFS = 0x0000; + BG2PA = 0x0100; + BG2PB = 0x0000; + BG2PC = 0x0000; + BG2PD = 0x0100; + BG2X_L = 0x0000; + BG2X_H = 0x0000; + BG2Y_L = 0x0000; + BG2Y_H = 0x0000; + BG3PA = 0x0100; + BG3PB = 0x0000; + BG3PC = 0x0000; + BG3PD = 0x0100; + BG3X_L = 0x0000; + BG3X_H = 0x0000; + BG3Y_L = 0x0000; + BG3Y_H = 0x0000; + WIN0H = 0x0000; + WIN1H = 0x0000; + WIN0V = 0x0000; + WIN1V = 0x0000; + WININ = 0x0000; + WINOUT = 0x0000; + MOSAIC = 0x0000; + BLDMOD = 0x0000; + COLEV = 0x0000; + COLY = 0x0000; + DM0SAD_L = 0x0000; + DM0SAD_H = 0x0000; + DM0DAD_L = 0x0000; + DM0DAD_H = 0x0000; + DM0CNT_L = 0x0000; + DM0CNT_H = 0x0000; + DM1SAD_L = 0x0000; + DM1SAD_H = 0x0000; + DM1DAD_L = 0x0000; + DM1DAD_H = 0x0000; + DM1CNT_L = 0x0000; + DM1CNT_H = 0x0000; + DM2SAD_L = 0x0000; + DM2SAD_H = 0x0000; + DM2DAD_L = 0x0000; + DM2DAD_H = 0x0000; + DM2CNT_L = 0x0000; + DM2CNT_H = 0x0000; + DM3SAD_L = 0x0000; + DM3SAD_H = 0x0000; + DM3DAD_L = 0x0000; + DM3DAD_H = 0x0000; + DM3CNT_L = 0x0000; + DM3CNT_H = 0x0000; + TM0D = 0x0000; + TM0CNT = 0x0000; + TM1D = 0x0000; + TM1CNT = 0x0000; + TM2D = 0x0000; + TM2CNT = 0x0000; + TM3D = 0x0000; + TM3CNT = 0x0000; + P1 = 0x03FF; + IE = 0x0000; + IF = 0x0000; + IME = 0x0000; + + armMode = 0x1F; + + if(cpuIsMultiBoot) { + reg[13].I = 0x03007F00; + reg[15].I = 0x02000000; + reg[16].I = 0x00000000; + reg[R13_IRQ].I = 0x03007FA0; + reg[R13_SVC].I = 0x03007FE0; + armIrqEnable = true; + } else { + if(useBios && !skipBios) { + reg[15].I = 0x00000000; + armMode = 0x13; + armIrqEnable = false; + } else { + reg[13].I = 0x03007F00; + reg[15].I = 0x08000000; + reg[16].I = 0x00000000; + reg[R13_IRQ].I = 0x03007FA0; + reg[R13_SVC].I = 0x03007FE0; + armIrqEnable = true; + } + } + armState = true; + C_FLAG = V_FLAG = N_FLAG = Z_FLAG = false; + UPDATE_REG(0x00, DISPCNT); + UPDATE_REG(0x06, VCOUNT); + UPDATE_REG(0x20, BG2PA); + UPDATE_REG(0x26, BG2PD); + UPDATE_REG(0x30, BG3PA); + UPDATE_REG(0x36, BG3PD); + UPDATE_REG(0x130, P1); + UPDATE_REG(0x88, 0x200); + + // disable FIQ + reg[16].I |= 0x40; + + CPUUpdateCPSR(); + + armNextPC = reg[15].I; + reg[15].I += 4; + + // reset internal state + holdState = false; + holdType = 0; + + biosProtected[0] = 0x00; + biosProtected[1] = 0xf0; + biosProtected[2] = 0x29; + biosProtected[3] = 0xe1; + + lcdTicks = (useBios && !skipBios) ? 1008 : 208; + timer0On = false; + timer0Ticks = 0; + timer0Reload = 0; + timer0ClockReload = 0; + timer1On = false; + timer1Ticks = 0; + timer1Reload = 0; + timer1ClockReload = 0; + timer2On = false; + timer2Ticks = 0; + timer2Reload = 0; + timer2ClockReload = 0; + timer3On = false; + timer3Ticks = 0; + timer3Reload = 0; + timer3ClockReload = 0; + dma0Source = 0; + dma0Dest = 0; + dma1Source = 0; + dma1Dest = 0; + dma2Source = 0; + dma2Dest = 0; + dma3Source = 0; + dma3Dest = 0; + cpuSaveGameFunc = flashSaveDecide; + renderLine = mode0RenderLine; + fxOn = false; + windowOn = false; + frameCount = 0; + saveType = 0; + layerEnable = DISPCNT & layerSettings; + + CPUUpdateRenderBuffers(true); + + for(int i = 0; i < 256; i++) { + map[i].address = (u8 *)&dummyAddress; + map[i].mask = 0; + } + + map[0].address = bios; + map[0].mask = 0x3FFF; + map[2].address = workRAM; + map[2].mask = 0x3FFFF; + map[3].address = internalRAM; + map[3].mask = 0x7FFF; + map[4].address = ioMem; + map[4].mask = 0x3FF; + map[5].address = paletteRAM; + map[5].mask = 0x3FF; + map[6].address = vram; + map[6].mask = 0x1FFFF; + map[7].address = oam; + map[7].mask = 0x3FF; + map[8].address = rom; + map[8].mask = 0x1FFFFFF; + map[9].address = rom; + map[9].mask = 0x1FFFFFF; + map[10].address = rom; + map[10].mask = 0x1FFFFFF; + map[12].address = rom; + map[12].mask = 0x1FFFFFF; + map[14].address = flashSaveMemory; + map[14].mask = 0xFFFF; + + eepromReset(); + flashReset(); + + soundReset(); + + CPUUpdateWindow0(); + CPUUpdateWindow1(); + + // make sure registers are correctly initialized if not using BIOS + if(!useBios) { + if(cpuIsMultiBoot) + BIOS_RegisterRamReset(0xfe); + else + BIOS_RegisterRamReset(0xff); + } else { + if(cpuIsMultiBoot) + BIOS_RegisterRamReset(0xfe); + } + + switch(cpuSaveType) { + case 0: // automatic + cpuSramEnabled = true; + cpuFlashEnabled = true; + cpuEEPROMEnabled = true; + cpuEEPROMSensorEnabled = false; + saveType = gbaSaveType = 0; + break; + case 1: // EEPROM + cpuSramEnabled = false; + cpuFlashEnabled = false; + cpuEEPROMEnabled = true; + cpuEEPROMSensorEnabled = false; + saveType = gbaSaveType = 3; + // EEPROM usage is automatically detected + break; + case 2: // SRAM + cpuSramEnabled = true; + cpuFlashEnabled = false; + cpuEEPROMEnabled = false; + cpuEEPROMSensorEnabled = false; + cpuSaveGameFunc = sramDelayedWrite; // to insure we detect the write + saveType = gbaSaveType = 1; + break; + case 3: // FLASH + cpuSramEnabled = false; + cpuFlashEnabled = true; + cpuEEPROMEnabled = false; + cpuEEPROMSensorEnabled = false; + cpuSaveGameFunc = flashDelayedWrite; // to insure we detect the write + saveType = gbaSaveType = 2; + break; + case 4: // EEPROM+Sensor + cpuSramEnabled = false; + cpuFlashEnabled = false; + cpuEEPROMEnabled = true; + cpuEEPROMSensorEnabled = true; + // EEPROM usage is automatically detected + saveType = gbaSaveType = 3; + break; + case 5: // NONE + cpuSramEnabled = false; + cpuFlashEnabled = false; + cpuEEPROMEnabled = false; + cpuEEPROMSensorEnabled = false; + // no save at all + saveType = gbaSaveType = 5; + break; + } + + ARM_PREFETCH; + + systemSaveUpdateCounter = SYSTEM_SAVE_NOT_UPDATED; + + cpuDmaHack = false; + + lastTime = systemGetClock(); + + SWITicks = 0; +} + +void CPUInterrupt() +{ + u32 PC = reg[15].I; + bool savedState = armState; + CPUSwitchMode(0x12, true, false); + reg[14].I = PC; + if(!savedState) + reg[14].I += 2; + reg[15].I = 0x18; + armState = true; + armIrqEnable = false; + + armNextPC = reg[15].I; + reg[15].I += 4; + ARM_PREFETCH; + + // if(!holdState) + biosProtected[0] = 0x02; + biosProtected[1] = 0xc0; + biosProtected[2] = 0x5e; + biosProtected[3] = 0xe5; +} + +#ifdef SDL +void log(const char *defaultMsg, ...) +{ + char buffer[2048]; + va_list valist; + + va_start(valist, defaultMsg); + vsprintf(buffer, defaultMsg, valist); + + if(out == NULL) { + out = fopen("trace.log","w"); + } + + fputs(buffer, out); + + va_end(valist); +} +#else +extern void winlog(const char *, ...); +#endif + +void CPULoop(int ticks) +{ + int clockTicks; + int timerOverflow = 0; + // variable used by the CPU core + cpuTotalTicks = 0; + + // shuffle2: what's the purpose? + if(gba_link_enabled) + cpuNextEvent = 1; + + cpuBreakLoop = false; + cpuNextEvent = CPUUpdateTicks(); + if(cpuNextEvent > ticks) + cpuNextEvent = ticks; + + + for(;;) { +#ifndef FINAL_VERSION + if(systemDebug) { + if(systemDebug >= 10 && !holdState) { + CPUUpdateCPSR(); +#ifdef BKPT_SUPPORT + if (debugger_last) + { + sprintf(buffer, "R00=%08x R01=%08x R02=%08x R03=%08x R04=%08x R05=%08x R06=%08x R07=%08x R08=%08x R09=%08x R10=%08x R11=%08x R12=%08x R13=%08x R14=%08x R15=%08x R16=%08x R17=%08x\n", + oldreg[0], oldreg[1], oldreg[2], oldreg[3], oldreg[4], oldreg[5], + oldreg[6], oldreg[7], oldreg[8], oldreg[9], oldreg[10], oldreg[11], + oldreg[12], oldreg[13], oldreg[14], oldreg[15], oldreg[16], + oldreg[17]); + } +#endif + sprintf(buffer, "R00=%08x R01=%08x R02=%08x R03=%08x R04=%08x R05=%08x R06=%08x R07=%08x R08=%08x R09=%08x R10=%08x R11=%08x R12=%08x R13=%08x R14=%08x R15=%08x R16=%08x R17=%08x\n", + reg[0].I, reg[1].I, reg[2].I, reg[3].I, reg[4].I, reg[5].I, + reg[6].I, reg[7].I, reg[8].I, reg[9].I, reg[10].I, reg[11].I, + reg[12].I, reg[13].I, reg[14].I, reg[15].I, reg[16].I, + reg[17].I); +#ifdef SDL + log(buffer); +#else + winlog(buffer); +#endif + } else if(!holdState) { + sprintf(buffer, "PC=%08x\n", armNextPC); +#ifdef SDL + log(buffer); +#else + winlog(buffer); +#endif + } + } +#endif /* FINAL_VERSION */ + + if(!holdState && !SWITicks) { + if(armState) { + if (!armExecute()) + return; + } else { + if (!thumbExecute()) + return; + } + clockTicks = 0; + } else + clockTicks = CPUUpdateTicks(); + + cpuTotalTicks += clockTicks; + + + if(cpuTotalTicks >= cpuNextEvent) { + int remainingTicks = cpuTotalTicks - cpuNextEvent; + + if (SWITicks) + { + SWITicks-=clockTicks; + if (SWITicks<0) + SWITicks = 0; + } + + clockTicks = cpuNextEvent; + cpuTotalTicks = 0; + cpuDmaHack = false; + + updateLoop: + + if (IRQTicks) + { + IRQTicks -= clockTicks; + if (IRQTicks<0) + IRQTicks = 0; + } + + lcdTicks -= clockTicks; + + + if(lcdTicks <= 0) { + if(DISPSTAT & 1) { // V-BLANK + // if in V-Blank mode, keep computing... + if(DISPSTAT & 2) { + lcdTicks += 1008; + VCOUNT++; + UPDATE_REG(0x06, VCOUNT); + DISPSTAT &= 0xFFFD; + UPDATE_REG(0x04, DISPSTAT); + CPUCompareVCOUNT(); + } else { + lcdTicks += 224; + DISPSTAT |= 2; + UPDATE_REG(0x04, DISPSTAT); + if(DISPSTAT & 16) { + IF |= 2; + UPDATE_REG(0x202, IF); + } + } + + if(VCOUNT >= 228) { //Reaching last line + DISPSTAT &= 0xFFFC; + UPDATE_REG(0x04, DISPSTAT); + VCOUNT = 0; + UPDATE_REG(0x06, VCOUNT); + CPUCompareVCOUNT(); + } + } else { + int framesToSkip = systemFrameSkip; + if(speedup) + framesToSkip = 9; // try 6 FPS during speedup + + if(DISPSTAT & 2) { + // if in H-Blank, leave it and move to drawing mode + VCOUNT++; + UPDATE_REG(0x06, VCOUNT); + + lcdTicks += 1008; + DISPSTAT &= 0xFFFD; + if(VCOUNT == 160) { + count++; + systemFrame(); + + if((count % 10) == 0) { + system10Frames(60); + } + if(count == 60) { + u32 time = systemGetClock(); + if(time != lastTime) { + u32 t = 100000/(time - lastTime); + systemShowSpeed(t); + } else + systemShowSpeed(0); + lastTime = time; + count = 0; + } + u32 joy = 0; + // update joystick information + if(systemReadJoypads()) + // read default joystick + joy = systemReadJoypad(-1); + P1 = 0x03FF ^ (joy & 0x3FF); + if(cpuEEPROMSensorEnabled) + systemUpdateMotionSensor(); + UPDATE_REG(0x130, P1); + u16 P1CNT = READ16LE(((u16 *)&ioMem[0x132])); + // this seems wrong, but there are cases where the game + // can enter the stop state without requesting an IRQ from + // the joypad. + if((P1CNT & 0x4000) || stopState) { + u16 p1 = (0x3FF ^ P1) & 0x3FF; + if(P1CNT & 0x8000) { + if(p1 == (P1CNT & 0x3FF)) { + IF |= 0x1000; + UPDATE_REG(0x202, IF); + } + } else { + if(p1 & P1CNT) { + IF |= 0x1000; + UPDATE_REG(0x202, IF); + } + } + } + + u32 ext = (joy >> 10); + // If no (m) code is enabled, apply the cheats at each LCDline + if((cheatsEnabled) && (mastercode==0)) + remainingTicks += cheatsCheckKeys(P1^0x3FF, ext); + speedup = (ext & 1) ? true : false; + capture = (ext & 2) ? true : false; + + if(capture && !capturePrevious) { + captureNumber++; + systemScreenCapture(captureNumber); + } + capturePrevious = capture; + + DISPSTAT |= 1; + DISPSTAT &= 0xFFFD; + UPDATE_REG(0x04, DISPSTAT); + if(DISPSTAT & 0x0008) { + IF |= 1; + UPDATE_REG(0x202, IF); + } + CPUCheckDMA(1, 0x0f); + if(frameCount >= framesToSkip) { + systemDrawScreen(); + frameCount = 0; + } else + frameCount++; + if(systemPauseOnFrame()) + ticks = 0; + } + + UPDATE_REG(0x04, DISPSTAT); + CPUCompareVCOUNT(); + + } else { + if(frameCount >= framesToSkip) + { + (*renderLine)(); + switch(systemColorDepth) { + case 16: + { + u16 *dest = (u16 *)pix + 242 * (VCOUNT+1); + for(int x = 0; x < 240;) { + *dest++ = systemColorMap16[lineMix[x++]&0xFFFF]; + *dest++ = systemColorMap16[lineMix[x++]&0xFFFF]; + *dest++ = systemColorMap16[lineMix[x++]&0xFFFF]; + *dest++ = systemColorMap16[lineMix[x++]&0xFFFF]; + + *dest++ = systemColorMap16[lineMix[x++]&0xFFFF]; + *dest++ = systemColorMap16[lineMix[x++]&0xFFFF]; + *dest++ = systemColorMap16[lineMix[x++]&0xFFFF]; + *dest++ = systemColorMap16[lineMix[x++]&0xFFFF]; + + *dest++ = systemColorMap16[lineMix[x++]&0xFFFF]; + *dest++ = systemColorMap16[lineMix[x++]&0xFFFF]; + *dest++ = systemColorMap16[lineMix[x++]&0xFFFF]; + *dest++ = systemColorMap16[lineMix[x++]&0xFFFF]; + + *dest++ = systemColorMap16[lineMix[x++]&0xFFFF]; + *dest++ = systemColorMap16[lineMix[x++]&0xFFFF]; + *dest++ = systemColorMap16[lineMix[x++]&0xFFFF]; + *dest++ = systemColorMap16[lineMix[x++]&0xFFFF]; + } + // for filters that read past the screen + *dest++ = 0; + } + break; + case 24: + { + u8 *dest = (u8 *)pix + 240 * VCOUNT * 3; + for(int x = 0; x < 240;) { + *((u32 *)dest) = systemColorMap32[lineMix[x++] & 0xFFFF]; + dest += 3; + *((u32 *)dest) = systemColorMap32[lineMix[x++] & 0xFFFF]; + dest += 3; + *((u32 *)dest) = systemColorMap32[lineMix[x++] & 0xFFFF]; + dest += 3; + *((u32 *)dest) = systemColorMap32[lineMix[x++] & 0xFFFF]; + dest += 3; + + *((u32 *)dest) = systemColorMap32[lineMix[x++] & 0xFFFF]; + dest += 3; + *((u32 *)dest) = systemColorMap32[lineMix[x++] & 0xFFFF]; + dest += 3; + *((u32 *)dest) = systemColorMap32[lineMix[x++] & 0xFFFF]; + dest += 3; + *((u32 *)dest) = systemColorMap32[lineMix[x++] & 0xFFFF]; + dest += 3; + + *((u32 *)dest) = systemColorMap32[lineMix[x++] & 0xFFFF]; + dest += 3; + *((u32 *)dest) = systemColorMap32[lineMix[x++] & 0xFFFF]; + dest += 3; + *((u32 *)dest) = systemColorMap32[lineMix[x++] & 0xFFFF]; + dest += 3; + *((u32 *)dest) = systemColorMap32[lineMix[x++] & 0xFFFF]; + dest += 3; + + *((u32 *)dest) = systemColorMap32[lineMix[x++] & 0xFFFF]; + dest += 3; + *((u32 *)dest) = systemColorMap32[lineMix[x++] & 0xFFFF]; + dest += 3; + *((u32 *)dest) = systemColorMap32[lineMix[x++] & 0xFFFF]; + dest += 3; + *((u32 *)dest) = systemColorMap32[lineMix[x++] & 0xFFFF]; + dest += 3; + } + } + break; + case 32: + { + u32 *dest = (u32 *)pix + 241 * (VCOUNT+1); + for(int x = 0; x < 240; ) { + *dest++ = systemColorMap32[lineMix[x++] & 0xFFFF]; + *dest++ = systemColorMap32[lineMix[x++] & 0xFFFF]; + *dest++ = systemColorMap32[lineMix[x++] & 0xFFFF]; + *dest++ = systemColorMap32[lineMix[x++] & 0xFFFF]; + + *dest++ = systemColorMap32[lineMix[x++] & 0xFFFF]; + *dest++ = systemColorMap32[lineMix[x++] & 0xFFFF]; + *dest++ = systemColorMap32[lineMix[x++] & 0xFFFF]; + *dest++ = systemColorMap32[lineMix[x++] & 0xFFFF]; + + *dest++ = systemColorMap32[lineMix[x++] & 0xFFFF]; + *dest++ = systemColorMap32[lineMix[x++] & 0xFFFF]; + *dest++ = systemColorMap32[lineMix[x++] & 0xFFFF]; + *dest++ = systemColorMap32[lineMix[x++] & 0xFFFF]; + + *dest++ = systemColorMap32[lineMix[x++] & 0xFFFF]; + *dest++ = systemColorMap32[lineMix[x++] & 0xFFFF]; + *dest++ = systemColorMap32[lineMix[x++] & 0xFFFF]; + *dest++ = systemColorMap32[lineMix[x++] & 0xFFFF]; + } + } + break; + } + } + // entering H-Blank + DISPSTAT |= 2; + UPDATE_REG(0x04, DISPSTAT); + lcdTicks += 224; + CPUCheckDMA(2, 0x0f); + if(DISPSTAT & 16) { + IF |= 2; + UPDATE_REG(0x202, IF); + } + } + } + } + + // we shouldn't be doing sound in stop state, but we loose synchronization + // if sound is disabled, so in stop state, soundTick will just produce + // mute sound + soundTicks -= clockTicks; + if(soundTicks <= 0) { + psoundTickfn(); + soundTicks += SOUND_CLOCK_TICKS; + } + + if(!stopState) { + if(timer0On) { + timer0Ticks -= clockTicks; + if(timer0Ticks <= 0) { + timer0Ticks += (0x10000 - timer0Reload) << timer0ClockReload; + timerOverflow |= 1; + soundTimerOverflow(0); + if(TM0CNT & 0x40) { + IF |= 0x08; + UPDATE_REG(0x202, IF); + } + } + TM0D = 0xFFFF - (timer0Ticks >> timer0ClockReload); + UPDATE_REG(0x100, TM0D); + } + + if(timer1On) { + if(TM1CNT & 4) { + if(timerOverflow & 1) { + TM1D++; + if(TM1D == 0) { + TM1D += timer1Reload; + timerOverflow |= 2; + soundTimerOverflow(1); + if(TM1CNT & 0x40) { + IF |= 0x10; + UPDATE_REG(0x202, IF); + } + } + UPDATE_REG(0x104, TM1D); + } + } else { + timer1Ticks -= clockTicks; + if(timer1Ticks <= 0) { + timer1Ticks += (0x10000 - timer1Reload) << timer1ClockReload; + timerOverflow |= 2; + soundTimerOverflow(1); + if(TM1CNT & 0x40) { + IF |= 0x10; + UPDATE_REG(0x202, IF); + } + } + TM1D = 0xFFFF - (timer1Ticks >> timer1ClockReload); + UPDATE_REG(0x104, TM1D); + } + } + + if(timer2On) { + if(TM2CNT & 4) { + if(timerOverflow & 2) { + TM2D++; + if(TM2D == 0) { + TM2D += timer2Reload; + timerOverflow |= 4; + if(TM2CNT & 0x40) { + IF |= 0x20; + UPDATE_REG(0x202, IF); + } + } + UPDATE_REG(0x108, TM2D); + } + } else { + timer2Ticks -= clockTicks; + if(timer2Ticks <= 0) { + timer2Ticks += (0x10000 - timer2Reload) << timer2ClockReload; + timerOverflow |= 4; + if(TM2CNT & 0x40) { + IF |= 0x20; + UPDATE_REG(0x202, IF); + } + } + TM2D = 0xFFFF - (timer2Ticks >> timer2ClockReload); + UPDATE_REG(0x108, TM2D); + } + } + + if(timer3On) { + if(TM3CNT & 4) { + if(timerOverflow & 4) { + TM3D++; + if(TM3D == 0) { + TM3D += timer3Reload; + if(TM3CNT & 0x40) { + IF |= 0x40; + UPDATE_REG(0x202, IF); + } + } + UPDATE_REG(0x10C, TM3D); + } + } else { + timer3Ticks -= clockTicks; + if(timer3Ticks <= 0) { + timer3Ticks += (0x10000 - timer3Reload) << timer3ClockReload; + if(TM3CNT & 0x40) { + IF |= 0x40; + UPDATE_REG(0x202, IF); + } + } + TM3D = 0xFFFF - (timer3Ticks >> timer3ClockReload); + UPDATE_REG(0x10C, TM3D); + } + } + } + + timerOverflow = 0; + + + +#ifdef PROFILING + profilingTicks -= clockTicks; + if(profilingTicks <= 0) { + profilingTicks += profilingTicksReload; + if(profilSegment) { + profile_segment *seg = profilSegment; + do { + u16 *b = (u16 *)seg->sbuf; + int pc = ((reg[15].I - seg->s_lowpc) * seg->s_scale)/0x10000; + if(pc >= 0 && pc < seg->ssiz) { + b[pc]++; + break; + } + + seg = seg->next; + } while(seg); + } + } +#endif + + ticks -= clockTicks; + + if (gba_joybus_enabled) + JoyBusUpdate(clockTicks); + + if (gba_link_enabled) + LinkUpdate(clockTicks); + + cpuNextEvent = CPUUpdateTicks(); + + if(cpuDmaTicksToUpdate > 0) { + if(cpuDmaTicksToUpdate > cpuNextEvent) + clockTicks = cpuNextEvent; + else + clockTicks = cpuDmaTicksToUpdate; + cpuDmaTicksToUpdate -= clockTicks; + if(cpuDmaTicksToUpdate < 0) + cpuDmaTicksToUpdate = 0; + cpuDmaHack = true; + goto updateLoop; + } + + // shuffle2: what's the purpose? + if(gba_link_enabled) + cpuNextEvent = 1; + + if(IF && (IME & 1) && armIrqEnable) { + int res = IF & IE; + if(stopState) + res &= 0x3080; + if(res) { + if (intState) + { + if (!IRQTicks) + { + CPUInterrupt(); + intState = false; + holdState = false; + stopState = false; + holdType = 0; + } + } + else + { + if (!holdState) + { + intState = true; + IRQTicks=7; + if (cpuNextEvent> IRQTicks) + cpuNextEvent = IRQTicks; + } + else + { + CPUInterrupt(); + holdState = false; + stopState = false; + holdType = 0; + } + } + + // Stops the SWI Ticks emulation if an IRQ is executed + //(to avoid problems with nested IRQ/SWI) + if (SWITicks) + SWITicks = 0; + } + } + + if(remainingTicks > 0) { + if(remainingTicks > cpuNextEvent) + clockTicks = cpuNextEvent; + else + clockTicks = remainingTicks; + remainingTicks -= clockTicks; + if(remainingTicks < 0) + remainingTicks = 0; + goto updateLoop; + } + + if (timerOnOffDelay) + applyTimer(); + + if(cpuNextEvent > ticks) + cpuNextEvent = ticks; + + if(ticks <= 0 || cpuBreakLoop) + break; + + } + } +} + + + +struct EmulatedSystem GBASystem = { + // emuMain + CPULoop, + // emuReset + CPUReset, + // emuCleanUp + CPUCleanUp, + // emuReadBattery + CPUReadBatteryFile, + // emuWriteBattery + CPUWriteBatteryFile, + // emuReadState + CPUReadState, + // emuWriteState + CPUWriteState, + // emuReadMemState + CPUReadMemState, + // emuWriteMemState + CPUWriteMemState, + // emuWritePNG + CPUWritePNGFile, + // emuWriteBMP + CPUWriteBMPFile, + // emuUpdateCPSR + CPUUpdateCPSR, + // emuHasDebugger + true, + // emuCount +#ifdef FINAL_VERSION + 250000 +#else + 5000 +#endif +}; diff --git a/src/gba/GBASockClient.cpp b/src/gba/GBASockClient.cpp index a9a38ad6..833c44d3 100644 --- a/src/gba/GBASockClient.cpp +++ b/src/gba/GBASockClient.cpp @@ -1,38 +1,38 @@ -#include "GBASockClient.h" - -// Currently only for Joybus communications - -GBASockClient::GBASockClient(sf::IPAddress _server_addr) -{ - if (!_server_addr.IsValid()) - server_addr = sf::IPAddress::LocalHost; - else - server_addr = _server_addr; - - client.Connect(0xd6ba, server_addr); - //client.SetBlocking(false); -} - -GBASockClient::~GBASockClient() -{ - client.Close(); -} - -void GBASockClient::Send(std::vector data) -{ - char* plain_data = new char[data.size()]; - std::copy(data.begin(), data.end(), plain_data); - - client.Send(plain_data, data.size()); - - delete[] plain_data; -} - -// Returns cmd for convenience -char GBASockClient::ReceiveCmd(char* data_in) -{ - std::size_t num_received; - client.Receive(data_in, 5, num_received); - - return data_in[0]; -} +#include "GBASockClient.h" + +// Currently only for Joybus communications + +GBASockClient::GBASockClient(sf::IPAddress _server_addr) +{ + if (!_server_addr.IsValid()) + server_addr = sf::IPAddress::LocalHost; + else + server_addr = _server_addr; + + client.Connect(0xd6ba, server_addr); + //client.SetBlocking(false); +} + +GBASockClient::~GBASockClient() +{ + client.Close(); +} + +void GBASockClient::Send(std::vector data) +{ + char* plain_data = new char[data.size()]; + std::copy(data.begin(), data.end(), plain_data); + + client.Send(plain_data, data.size()); + + delete[] plain_data; +} + +// Returns cmd for convenience +char GBASockClient::ReceiveCmd(char* data_in) +{ + std::size_t num_received; + client.Receive(data_in, 5, num_received); + + return data_in[0]; +} diff --git a/src/gba/GBASockClient.h b/src/gba/GBASockClient.h index a4347d75..937959d1 100644 --- a/src/gba/GBASockClient.h +++ b/src/gba/GBASockClient.h @@ -1,18 +1,18 @@ -#pragma once - -#include -#include "../common/Types.h" - -class GBASockClient : public sf::SocketTCP -{ -public: - GBASockClient(sf::IPAddress server_addr); - ~GBASockClient(); - - void Send(std::vector data); - char ReceiveCmd(char* data_in); - -private: - sf::IPAddress server_addr; - sf::SocketTCP client; -}; +#pragma once + +#include +#include "../common/Types.h" + +class GBASockClient : public sf::SocketTCP +{ +public: + GBASockClient(sf::IPAddress server_addr); + ~GBASockClient(); + + void Send(std::vector data); + char ReceiveCmd(char* data_in); + +private: + sf::IPAddress server_addr; + sf::SocketTCP client; +}; diff --git a/src/gba/GBAinline.h b/src/gba/GBAinline.h index 9ee861a6..37a41401 100644 --- a/src/gba/GBAinline.h +++ b/src/gba/GBAinline.h @@ -1,724 +1,724 @@ -#ifndef GBAINLINE_H -#define GBAINLINE_H - -#include "../System.h" -#include "../common/Port.h" -#include "RTC.h" -#include "Sound.h" -#include "agbprint.h" -#include "GBAcpu.h" -#include "GBALink.h" - -extern const u32 objTilesAddress[3]; - -extern bool stopState; -extern bool holdState; -extern int holdType; -extern int cpuNextEvent; -extern bool cpuSramEnabled; -extern bool cpuFlashEnabled; -extern bool cpuEEPROMEnabled; -extern bool cpuEEPROMSensorEnabled; -extern bool cpuDmaHack; -extern u32 cpuDmaLast; -extern bool timer0On; -extern int timer0Ticks; -extern int timer0ClockReload; -extern bool timer1On; -extern int timer1Ticks; -extern int timer1ClockReload; -extern bool timer2On; -extern int timer2Ticks; -extern int timer2ClockReload; -extern bool timer3On; -extern int timer3Ticks; -extern int timer3ClockReload; -extern int cpuTotalTicks; - -#define CPUReadByteQuick(addr) \ - map[(addr)>>24].address[(addr) & map[(addr)>>24].mask] - -#define CPUReadHalfWordQuick(addr) \ - READ16LE(((u16*)&map[(addr)>>24].address[(addr) & map[(addr)>>24].mask])) - -#define CPUReadMemoryQuick(addr) \ - READ32LE(((u32*)&map[(addr)>>24].address[(addr) & map[(addr)>>24].mask])) - -static inline u32 CPUReadMemory(u32 address) -{ -#ifdef GBA_LOGGING - if(address & 3) { - if(systemVerbose & VERBOSE_UNALIGNED_MEMORY) { - log("Unaligned word read: %08x at %08x\n", address, armMode ? - armNextPC - 4 : armNextPC - 2); - } - } -#endif - - u32 value; - switch(address >> 24) { - case 0: - if(reg[15].I >> 24) { - if(address < 0x4000) { -#ifdef GBA_LOGGING - if(systemVerbose & VERBOSE_ILLEGAL_READ) { - log("Illegal word read: %08x at %08x\n", address, armMode ? - armNextPC - 4 : armNextPC - 2); - } -#endif - - value = READ32LE(((u32 *)&biosProtected)); - } - else goto unreadable; - } else - value = READ32LE(((u32 *)&bios[address & 0x3FFC])); - break; - case 2: - value = READ32LE(((u32 *)&workRAM[address & 0x3FFFC])); - break; - case 3: - value = READ32LE(((u32 *)&internalRAM[address & 0x7ffC])); - break; - case 4: - if((address < 0x4000400) && ioReadable[address & 0x3fc]) { - if(ioReadable[(address & 0x3fc) + 2]) { - value = READ32LE(((u32 *)&ioMem[address & 0x3fC])); - if ((address & 0x3fc) == COMM_JOY_RECV_L) - UPDATE_REG(COMM_JOYSTAT, READ16LE(&ioMem[COMM_JOYSTAT]) & ~JOYSTAT_RECV); - } else { - value = READ16LE(((u16 *)&ioMem[address & 0x3fc])); - } - } - else - goto unreadable; - break; - case 5: - value = READ32LE(((u32 *)&paletteRAM[address & 0x3fC])); - break; - case 6: - address = (address & 0x1fffc); - if (((DISPCNT & 7) >2) && ((address & 0x1C000) == 0x18000)) - { - value = 0; - break; - } - if ((address & 0x18000) == 0x18000) - address &= 0x17fff; - value = READ32LE(((u32 *)&vram[address])); - break; - case 7: - value = READ32LE(((u32 *)&oam[address & 0x3FC])); - break; - case 8: - case 9: - case 10: - case 11: - case 12: - value = READ32LE(((u32 *)&rom[address&0x1FFFFFC])); - break; - case 13: - if(cpuEEPROMEnabled) - // no need to swap this - return eepromRead(address); - goto unreadable; - case 14: - if(cpuFlashEnabled | cpuSramEnabled) - // no need to swap this - return flashRead(address); - // default - default: -unreadable: -#ifdef GBA_LOGGING - if(systemVerbose & VERBOSE_ILLEGAL_READ) { - log("Illegal word read: %08x at %08x\n", address, armMode ? - armNextPC - 4 : armNextPC - 2); - } -#endif - - if(cpuDmaHack) { - value = cpuDmaLast; - } else { - if(armState) { - value = CPUReadMemoryQuick(reg[15].I); - } else { - value = CPUReadHalfWordQuick(reg[15].I) | - CPUReadHalfWordQuick(reg[15].I) << 16; - } - } - } - - if(address & 3) { -#ifdef C_CORE - int shift = (address & 3) << 3; - value = (value >> shift) | (value << (32 - shift)); -#else -#ifdef __GNUC__ - asm("and $3, %%ecx;" - "shl $3 ,%%ecx;" - "ror %%cl, %0" - : "=r" (value) - : "r" (value), "c" (address)); -#else - __asm { - mov ecx, address; - and ecx, 3; - shl ecx, 3; - ror [dword ptr value], cl; - } -#endif -#endif - } - return value; -} - -extern u32 myROM[]; - -static inline u32 CPUReadHalfWord(u32 address) -{ -#ifdef GBA_LOGGING - if(address & 1) { - if(systemVerbose & VERBOSE_UNALIGNED_MEMORY) { - log("Unaligned halfword read: %08x at %08x\n", address, armMode ? - armNextPC - 4 : armNextPC - 2); - } - } -#endif - - u32 value; - - switch(address >> 24) { - case 0: - if (reg[15].I >> 24) { - if(address < 0x4000) { -#ifdef GBA_LOGGING - if(systemVerbose & VERBOSE_ILLEGAL_READ) { - log("Illegal halfword read: %08x at %08x\n", address, armMode ? - armNextPC - 4 : armNextPC - 2); - } -#endif - value = READ16LE(((u16 *)&biosProtected[address&2])); - } else goto unreadable; - } else - value = READ16LE(((u16 *)&bios[address & 0x3FFE])); - break; - case 2: - value = READ16LE(((u16 *)&workRAM[address & 0x3FFFE])); - break; - case 3: - value = READ16LE(((u16 *)&internalRAM[address & 0x7ffe])); - break; - case 4: - if((address < 0x4000400) && ioReadable[address & 0x3fe]) - { - value = READ16LE(((u16 *)&ioMem[address & 0x3fe])); - if (((address & 0x3fe)>0xFF) && ((address & 0x3fe)<0x10E)) - { - if (((address & 0x3fe) == 0x100) && timer0On) - value = 0xFFFF - ((timer0Ticks-cpuTotalTicks) >> timer0ClockReload); - else - if (((address & 0x3fe) == 0x104) && timer1On && !(TM1CNT & 4)) - value = 0xFFFF - ((timer1Ticks-cpuTotalTicks) >> timer1ClockReload); - else - if (((address & 0x3fe) == 0x108) && timer2On && !(TM2CNT & 4)) - value = 0xFFFF - ((timer2Ticks-cpuTotalTicks) >> timer2ClockReload); - else - if (((address & 0x3fe) == 0x10C) && timer3On && !(TM3CNT & 4)) - value = 0xFFFF - ((timer3Ticks-cpuTotalTicks) >> timer3ClockReload); - } - } - else goto unreadable; - break; - case 5: - value = READ16LE(((u16 *)&paletteRAM[address & 0x3fe])); - break; - case 6: - address = (address & 0x1fffe); - if (((DISPCNT & 7) >2) && ((address & 0x1C000) == 0x18000)) - { - value = 0; - break; - } - if ((address & 0x18000) == 0x18000) - address &= 0x17fff; - value = READ16LE(((u16 *)&vram[address])); - break; - case 7: - value = READ16LE(((u16 *)&oam[address & 0x3fe])); - break; - case 8: - case 9: - case 10: - case 11: - case 12: - if(address == 0x80000c4 || address == 0x80000c6 || address == 0x80000c8) - value = rtcRead(address); - else - value = READ16LE(((u16 *)&rom[address & 0x1FFFFFE])); - break; - case 13: - if(cpuEEPROMEnabled) - // no need to swap this - return eepromRead(address); - goto unreadable; - case 14: - if(cpuFlashEnabled | cpuSramEnabled) - // no need to swap this - return flashRead(address); - // default - default: -unreadable: -#ifdef GBA_LOGGING - if(systemVerbose & VERBOSE_ILLEGAL_READ) { - log("Illegal halfword read: %08x at %08x\n", address, armMode ? - armNextPC - 4 : armNextPC - 2); - } -#endif - if(cpuDmaHack) { - value = cpuDmaLast & 0xFFFF; - } else { - if(armState) { - value = CPUReadHalfWordQuick(reg[15].I + (address & 2)); - } else { - value = CPUReadHalfWordQuick(reg[15].I); - } - } - break; - } - - if(address & 1) { - value = (value >> 8) | (value << 24); - } - - return value; -} - -static inline u16 CPUReadHalfWordSigned(u32 address) -{ - u16 value = CPUReadHalfWord(address); - if((address & 1)) - value = (s8)value; - return value; -} - -static inline u8 CPUReadByte(u32 address) -{ - switch(address >> 24) { - case 0: - if (reg[15].I >> 24) { - if(address < 0x4000) { -#ifdef GBA_LOGGING - if(systemVerbose & VERBOSE_ILLEGAL_READ) { - log("Illegal byte read: %08x at %08x\n", address, armMode ? - armNextPC - 4 : armNextPC - 2); - } -#endif - return biosProtected[address & 3]; - } else goto unreadable; - } - return bios[address & 0x3FFF]; - case 2: - return workRAM[address & 0x3FFFF]; - case 3: - return internalRAM[address & 0x7fff]; - case 4: - if((address < 0x4000400) && ioReadable[address & 0x3ff]) - return ioMem[address & 0x3ff]; - else goto unreadable; - case 5: - return paletteRAM[address & 0x3ff]; - case 6: - address = (address & 0x1ffff); - if (((DISPCNT & 7) >2) && ((address & 0x1C000) == 0x18000)) - return 0; - if ((address & 0x18000) == 0x18000) - address &= 0x17fff; - return vram[address]; - case 7: - return oam[address & 0x3ff]; - case 8: - case 9: - case 10: - case 11: - case 12: - return rom[address & 0x1FFFFFF]; - case 13: - if(cpuEEPROMEnabled) - return eepromRead(address); - goto unreadable; - case 14: - if(cpuSramEnabled | cpuFlashEnabled) - return flashRead(address); - if(cpuEEPROMSensorEnabled) { - switch(address & 0x00008f00) { - case 0x8200: - return systemGetSensorX() & 255; - case 0x8300: - return (systemGetSensorX() >> 8)|0x80; - case 0x8400: - return systemGetSensorY() & 255; - case 0x8500: - return systemGetSensorY() >> 8; - } - } - // default - default: -unreadable: -#ifdef GBA_LOGGING - if(systemVerbose & VERBOSE_ILLEGAL_READ) { - log("Illegal byte read: %08x at %08x\n", address, armMode ? - armNextPC - 4 : armNextPC - 2); - } -#endif - if(cpuDmaHack) { - return cpuDmaLast & 0xFF; - } else { - if(armState) { - return CPUReadByteQuick(reg[15].I+(address & 3)); - } else { - return CPUReadByteQuick(reg[15].I+(address & 1)); - } - } - break; - } -} - -static inline void CPUWriteMemory(u32 address, u32 value) -{ - -#ifdef GBA_LOGGING - if(address & 3) { - if(systemVerbose & VERBOSE_UNALIGNED_MEMORY) { - log("Unaligned word write: %08x to %08x from %08x\n", - value, - address, - armMode ? armNextPC - 4 : armNextPC - 2); - } - } -#endif - - switch(address >> 24) { - case 0x02: -#ifdef BKPT_SUPPORT - if(*((u32 *)&freezeWorkRAM[address & 0x3FFFC])) - cheatsWriteMemory(address & 0x203FFFC, - value); - else -#endif - WRITE32LE(((u32 *)&workRAM[address & 0x3FFFC]), value); - break; - case 0x03: -#ifdef BKPT_SUPPORT - if(*((u32 *)&freezeInternalRAM[address & 0x7ffc])) - cheatsWriteMemory(address & 0x3007FFC, - value); - else -#endif - WRITE32LE(((u32 *)&internalRAM[address & 0x7ffC]), value); - break; - case 0x04: - if(address < 0x4000400) { - CPUUpdateRegister((address & 0x3FC), value & 0xFFFF); - CPUUpdateRegister((address & 0x3FC) + 2, (value >> 16)); - } else goto unwritable; - break; - case 0x05: -#ifdef BKPT_SUPPORT - if(*((u32 *)&freezePRAM[address & 0x3fc])) - cheatsWriteMemory(address & 0x70003FC, - value); - else -#endif - WRITE32LE(((u32 *)&paletteRAM[address & 0x3FC]), value); - break; - case 0x06: - address = (address & 0x1fffc); - if (((DISPCNT & 7) >2) && ((address & 0x1C000) == 0x18000)) - return; - if ((address & 0x18000) == 0x18000) - address &= 0x17fff; - -#ifdef BKPT_SUPPORT - if(*((u32 *)&freezeVRAM[address])) - cheatsWriteMemory(address + 0x06000000, value); - else -#endif - - WRITE32LE(((u32 *)&vram[address]), value); - break; - case 0x07: -#ifdef BKPT_SUPPORT - if(*((u32 *)&freezeOAM[address & 0x3fc])) - cheatsWriteMemory(address & 0x70003FC, - value); - else -#endif - WRITE32LE(((u32 *)&oam[address & 0x3fc]), value); - break; - case 0x0D: - if(cpuEEPROMEnabled) { - eepromWrite(address, value); - break; - } - goto unwritable; - case 0x0E: - if((!eepromInUse) | cpuSramEnabled | cpuFlashEnabled) { - (*cpuSaveGameFunc)(address, (u8)value); - break; - } - // default - default: -unwritable: -#ifdef GBA_LOGGING - if(systemVerbose & VERBOSE_ILLEGAL_WRITE) { - log("Illegal word write: %08x to %08x from %08x\n", - value, - address, - armMode ? armNextPC - 4 : armNextPC - 2); - } -#endif - break; - } -} - -static inline void CPUWriteHalfWord(u32 address, u16 value) -{ -#ifdef GBA_LOGGING - if(address & 1) { - if(systemVerbose & VERBOSE_UNALIGNED_MEMORY) { - log("Unaligned halfword write: %04x to %08x from %08x\n", - value, - address, - armMode ? armNextPC - 4 : armNextPC - 2); - } - } -#endif - - switch(address >> 24) { - case 2: -#ifdef BKPT_SUPPORT - if(*((u16 *)&freezeWorkRAM[address & 0x3FFFE])) - cheatsWriteHalfWord(address & 0x203FFFE, - value); - else -#endif - WRITE16LE(((u16 *)&workRAM[address & 0x3FFFE]),value); - break; - case 3: -#ifdef BKPT_SUPPORT - if(*((u16 *)&freezeInternalRAM[address & 0x7ffe])) - cheatsWriteHalfWord(address & 0x3007ffe, - value); - else -#endif - WRITE16LE(((u16 *)&internalRAM[address & 0x7ffe]), value); - break; - case 4: - if(address < 0x4000400) - CPUUpdateRegister(address & 0x3fe, value); - else goto unwritable; - break; - case 5: -#ifdef BKPT_SUPPORT - if(*((u16 *)&freezePRAM[address & 0x03fe])) - cheatsWriteHalfWord(address & 0x70003fe, - value); - else -#endif - WRITE16LE(((u16 *)&paletteRAM[address & 0x3fe]), value); - break; - case 6: - address = (address & 0x1fffe); - if (((DISPCNT & 7) >2) && ((address & 0x1C000) == 0x18000)) - return; - if ((address & 0x18000) == 0x18000) - address &= 0x17fff; -#ifdef BKPT_SUPPORT - if(*((u16 *)&freezeVRAM[address])) - cheatsWriteHalfWord(address + 0x06000000, - value); - else -#endif - WRITE16LE(((u16 *)&vram[address]), value); - break; - case 7: -#ifdef BKPT_SUPPORT - if(*((u16 *)&freezeOAM[address & 0x03fe])) - cheatsWriteHalfWord(address & 0x70003fe, - value); - else -#endif - WRITE16LE(((u16 *)&oam[address & 0x3fe]), value); - break; - case 8: - case 9: - if(address == 0x80000c4 || address == 0x80000c6 || address == 0x80000c8) { - if(!rtcWrite(address, value)) - goto unwritable; - } else if(!agbPrintWrite(address, value)) goto unwritable; - break; - case 13: - if(cpuEEPROMEnabled) { - eepromWrite(address, (u8)value); - break; - } - goto unwritable; - case 14: - if((!eepromInUse) | cpuSramEnabled | cpuFlashEnabled) { - (*cpuSaveGameFunc)(address, (u8)value); - break; - } - goto unwritable; - default: -unwritable: -#ifdef GBA_LOGGING - if(systemVerbose & VERBOSE_ILLEGAL_WRITE) { - log("Illegal halfword write: %04x to %08x from %08x\n", - value, - address, - armMode ? armNextPC - 4 : armNextPC - 2); - } -#endif - break; - } -} - -static inline void CPUWriteByte(u32 address, u8 b) -{ - switch(address >> 24) { - case 2: -#ifdef BKPT_SUPPORT - if(freezeWorkRAM[address & 0x3FFFF]) - cheatsWriteByte(address & 0x203FFFF, b); - else -#endif - workRAM[address & 0x3FFFF] = b; - break; - case 3: -#ifdef BKPT_SUPPORT - if(freezeInternalRAM[address & 0x7fff]) - cheatsWriteByte(address & 0x3007fff, b); - else -#endif - internalRAM[address & 0x7fff] = b; - break; - case 4: - if(address < 0x4000400) { - switch(address & 0x3FF) { - case 0x60: - case 0x61: - case 0x62: - case 0x63: - case 0x64: - case 0x65: - case 0x68: - case 0x69: - case 0x6c: - case 0x6d: - case 0x70: - case 0x71: - case 0x72: - case 0x73: - case 0x74: - case 0x75: - case 0x78: - case 0x79: - case 0x7c: - case 0x7d: - case 0x80: - case 0x81: - case 0x84: - case 0x85: - case 0x90: - case 0x91: - case 0x92: - case 0x93: - case 0x94: - case 0x95: - case 0x96: - case 0x97: - case 0x98: - case 0x99: - case 0x9a: - case 0x9b: - case 0x9c: - case 0x9d: - case 0x9e: - case 0x9f: - soundEvent(address&0xFF, b); - break; - case 0x301: // HALTCNT, undocumented - if(b == 0x80) - stopState = true; - holdState = 1; - holdType = -1; - cpuNextEvent = cpuTotalTicks; - break; - default: // every other register - u32 lowerBits = address & 0x3fe; - if(address & 1) { - CPUUpdateRegister(lowerBits, (READ16LE(&ioMem[lowerBits]) & 0x00FF) | (b << 8)); - } else { - CPUUpdateRegister(lowerBits, (READ16LE(&ioMem[lowerBits]) & 0xFF00) | b); - } - } - break; - } else goto unwritable; - break; - case 5: - // no need to switch - *((u16 *)&paletteRAM[address & 0x3FE]) = (b << 8) | b; - break; - case 6: - address = (address & 0x1fffe); - if (((DISPCNT & 7) >2) && ((address & 0x1C000) == 0x18000)) - return; - if ((address & 0x18000) == 0x18000) - address &= 0x17fff; - - // no need to switch - // byte writes to OBJ VRAM are ignored - if ((address) < objTilesAddress[((DISPCNT&7)+1)>>2]) - { -#ifdef BKPT_SUPPORT - if(freezeVRAM[address]) - cheatsWriteByte(address + 0x06000000, b); - else -#endif - *((u16 *)&vram[address]) = (b << 8) | b; - } - break; - case 7: - // no need to switch - // byte writes to OAM are ignored - // *((u16 *)&oam[address & 0x3FE]) = (b << 8) | b; - break; - case 13: - if(cpuEEPROMEnabled) { - eepromWrite(address, b); - break; - } - goto unwritable; - case 14: - if ((saveType != 5) && ((!eepromInUse) | cpuSramEnabled | cpuFlashEnabled)) { - - //if(!cpuEEPROMEnabled && (cpuSramEnabled | cpuFlashEnabled)) { - - (*cpuSaveGameFunc)(address, b); - break; - } - // default - default: -unwritable: -#ifdef GBA_LOGGING - if(systemVerbose & VERBOSE_ILLEGAL_WRITE) { - log("Illegal byte write: %02x to %08x from %08x\n", - b, - address, - armMode ? armNextPC - 4 : armNextPC -2 ); - } -#endif - break; - } -} - -#endif // GBAINLINE_H +#ifndef GBAINLINE_H +#define GBAINLINE_H + +#include "../System.h" +#include "../common/Port.h" +#include "RTC.h" +#include "Sound.h" +#include "agbprint.h" +#include "GBAcpu.h" +#include "GBALink.h" + +extern const u32 objTilesAddress[3]; + +extern bool stopState; +extern bool holdState; +extern int holdType; +extern int cpuNextEvent; +extern bool cpuSramEnabled; +extern bool cpuFlashEnabled; +extern bool cpuEEPROMEnabled; +extern bool cpuEEPROMSensorEnabled; +extern bool cpuDmaHack; +extern u32 cpuDmaLast; +extern bool timer0On; +extern int timer0Ticks; +extern int timer0ClockReload; +extern bool timer1On; +extern int timer1Ticks; +extern int timer1ClockReload; +extern bool timer2On; +extern int timer2Ticks; +extern int timer2ClockReload; +extern bool timer3On; +extern int timer3Ticks; +extern int timer3ClockReload; +extern int cpuTotalTicks; + +#define CPUReadByteQuick(addr) \ + map[(addr)>>24].address[(addr) & map[(addr)>>24].mask] + +#define CPUReadHalfWordQuick(addr) \ + READ16LE(((u16*)&map[(addr)>>24].address[(addr) & map[(addr)>>24].mask])) + +#define CPUReadMemoryQuick(addr) \ + READ32LE(((u32*)&map[(addr)>>24].address[(addr) & map[(addr)>>24].mask])) + +static inline u32 CPUReadMemory(u32 address) +{ +#ifdef GBA_LOGGING + if(address & 3) { + if(systemVerbose & VERBOSE_UNALIGNED_MEMORY) { + log("Unaligned word read: %08x at %08x\n", address, armMode ? + armNextPC - 4 : armNextPC - 2); + } + } +#endif + + u32 value; + switch(address >> 24) { + case 0: + if(reg[15].I >> 24) { + if(address < 0x4000) { +#ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_ILLEGAL_READ) { + log("Illegal word read: %08x at %08x\n", address, armMode ? + armNextPC - 4 : armNextPC - 2); + } +#endif + + value = READ32LE(((u32 *)&biosProtected)); + } + else goto unreadable; + } else + value = READ32LE(((u32 *)&bios[address & 0x3FFC])); + break; + case 2: + value = READ32LE(((u32 *)&workRAM[address & 0x3FFFC])); + break; + case 3: + value = READ32LE(((u32 *)&internalRAM[address & 0x7ffC])); + break; + case 4: + if((address < 0x4000400) && ioReadable[address & 0x3fc]) { + if(ioReadable[(address & 0x3fc) + 2]) { + value = READ32LE(((u32 *)&ioMem[address & 0x3fC])); + if ((address & 0x3fc) == COMM_JOY_RECV_L) + UPDATE_REG(COMM_JOYSTAT, READ16LE(&ioMem[COMM_JOYSTAT]) & ~JOYSTAT_RECV); + } else { + value = READ16LE(((u16 *)&ioMem[address & 0x3fc])); + } + } + else + goto unreadable; + break; + case 5: + value = READ32LE(((u32 *)&paletteRAM[address & 0x3fC])); + break; + case 6: + address = (address & 0x1fffc); + if (((DISPCNT & 7) >2) && ((address & 0x1C000) == 0x18000)) + { + value = 0; + break; + } + if ((address & 0x18000) == 0x18000) + address &= 0x17fff; + value = READ32LE(((u32 *)&vram[address])); + break; + case 7: + value = READ32LE(((u32 *)&oam[address & 0x3FC])); + break; + case 8: + case 9: + case 10: + case 11: + case 12: + value = READ32LE(((u32 *)&rom[address&0x1FFFFFC])); + break; + case 13: + if(cpuEEPROMEnabled) + // no need to swap this + return eepromRead(address); + goto unreadable; + case 14: + if(cpuFlashEnabled | cpuSramEnabled) + // no need to swap this + return flashRead(address); + // default + default: +unreadable: +#ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_ILLEGAL_READ) { + log("Illegal word read: %08x at %08x\n", address, armMode ? + armNextPC - 4 : armNextPC - 2); + } +#endif + + if(cpuDmaHack) { + value = cpuDmaLast; + } else { + if(armState) { + value = CPUReadMemoryQuick(reg[15].I); + } else { + value = CPUReadHalfWordQuick(reg[15].I) | + CPUReadHalfWordQuick(reg[15].I) << 16; + } + } + } + + if(address & 3) { +#ifdef C_CORE + int shift = (address & 3) << 3; + value = (value >> shift) | (value << (32 - shift)); +#else +#ifdef __GNUC__ + asm("and $3, %%ecx;" + "shl $3 ,%%ecx;" + "ror %%cl, %0" + : "=r" (value) + : "r" (value), "c" (address)); +#else + __asm { + mov ecx, address; + and ecx, 3; + shl ecx, 3; + ror [dword ptr value], cl; + } +#endif +#endif + } + return value; +} + +extern u32 myROM[]; + +static inline u32 CPUReadHalfWord(u32 address) +{ +#ifdef GBA_LOGGING + if(address & 1) { + if(systemVerbose & VERBOSE_UNALIGNED_MEMORY) { + log("Unaligned halfword read: %08x at %08x\n", address, armMode ? + armNextPC - 4 : armNextPC - 2); + } + } +#endif + + u32 value; + + switch(address >> 24) { + case 0: + if (reg[15].I >> 24) { + if(address < 0x4000) { +#ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_ILLEGAL_READ) { + log("Illegal halfword read: %08x at %08x\n", address, armMode ? + armNextPC - 4 : armNextPC - 2); + } +#endif + value = READ16LE(((u16 *)&biosProtected[address&2])); + } else goto unreadable; + } else + value = READ16LE(((u16 *)&bios[address & 0x3FFE])); + break; + case 2: + value = READ16LE(((u16 *)&workRAM[address & 0x3FFFE])); + break; + case 3: + value = READ16LE(((u16 *)&internalRAM[address & 0x7ffe])); + break; + case 4: + if((address < 0x4000400) && ioReadable[address & 0x3fe]) + { + value = READ16LE(((u16 *)&ioMem[address & 0x3fe])); + if (((address & 0x3fe)>0xFF) && ((address & 0x3fe)<0x10E)) + { + if (((address & 0x3fe) == 0x100) && timer0On) + value = 0xFFFF - ((timer0Ticks-cpuTotalTicks) >> timer0ClockReload); + else + if (((address & 0x3fe) == 0x104) && timer1On && !(TM1CNT & 4)) + value = 0xFFFF - ((timer1Ticks-cpuTotalTicks) >> timer1ClockReload); + else + if (((address & 0x3fe) == 0x108) && timer2On && !(TM2CNT & 4)) + value = 0xFFFF - ((timer2Ticks-cpuTotalTicks) >> timer2ClockReload); + else + if (((address & 0x3fe) == 0x10C) && timer3On && !(TM3CNT & 4)) + value = 0xFFFF - ((timer3Ticks-cpuTotalTicks) >> timer3ClockReload); + } + } + else goto unreadable; + break; + case 5: + value = READ16LE(((u16 *)&paletteRAM[address & 0x3fe])); + break; + case 6: + address = (address & 0x1fffe); + if (((DISPCNT & 7) >2) && ((address & 0x1C000) == 0x18000)) + { + value = 0; + break; + } + if ((address & 0x18000) == 0x18000) + address &= 0x17fff; + value = READ16LE(((u16 *)&vram[address])); + break; + case 7: + value = READ16LE(((u16 *)&oam[address & 0x3fe])); + break; + case 8: + case 9: + case 10: + case 11: + case 12: + if(address == 0x80000c4 || address == 0x80000c6 || address == 0x80000c8) + value = rtcRead(address); + else + value = READ16LE(((u16 *)&rom[address & 0x1FFFFFE])); + break; + case 13: + if(cpuEEPROMEnabled) + // no need to swap this + return eepromRead(address); + goto unreadable; + case 14: + if(cpuFlashEnabled | cpuSramEnabled) + // no need to swap this + return flashRead(address); + // default + default: +unreadable: +#ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_ILLEGAL_READ) { + log("Illegal halfword read: %08x at %08x\n", address, armMode ? + armNextPC - 4 : armNextPC - 2); + } +#endif + if(cpuDmaHack) { + value = cpuDmaLast & 0xFFFF; + } else { + if(armState) { + value = CPUReadHalfWordQuick(reg[15].I + (address & 2)); + } else { + value = CPUReadHalfWordQuick(reg[15].I); + } + } + break; + } + + if(address & 1) { + value = (value >> 8) | (value << 24); + } + + return value; +} + +static inline u16 CPUReadHalfWordSigned(u32 address) +{ + u16 value = CPUReadHalfWord(address); + if((address & 1)) + value = (s8)value; + return value; +} + +static inline u8 CPUReadByte(u32 address) +{ + switch(address >> 24) { + case 0: + if (reg[15].I >> 24) { + if(address < 0x4000) { +#ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_ILLEGAL_READ) { + log("Illegal byte read: %08x at %08x\n", address, armMode ? + armNextPC - 4 : armNextPC - 2); + } +#endif + return biosProtected[address & 3]; + } else goto unreadable; + } + return bios[address & 0x3FFF]; + case 2: + return workRAM[address & 0x3FFFF]; + case 3: + return internalRAM[address & 0x7fff]; + case 4: + if((address < 0x4000400) && ioReadable[address & 0x3ff]) + return ioMem[address & 0x3ff]; + else goto unreadable; + case 5: + return paletteRAM[address & 0x3ff]; + case 6: + address = (address & 0x1ffff); + if (((DISPCNT & 7) >2) && ((address & 0x1C000) == 0x18000)) + return 0; + if ((address & 0x18000) == 0x18000) + address &= 0x17fff; + return vram[address]; + case 7: + return oam[address & 0x3ff]; + case 8: + case 9: + case 10: + case 11: + case 12: + return rom[address & 0x1FFFFFF]; + case 13: + if(cpuEEPROMEnabled) + return eepromRead(address); + goto unreadable; + case 14: + if(cpuSramEnabled | cpuFlashEnabled) + return flashRead(address); + if(cpuEEPROMSensorEnabled) { + switch(address & 0x00008f00) { + case 0x8200: + return systemGetSensorX() & 255; + case 0x8300: + return (systemGetSensorX() >> 8)|0x80; + case 0x8400: + return systemGetSensorY() & 255; + case 0x8500: + return systemGetSensorY() >> 8; + } + } + // default + default: +unreadable: +#ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_ILLEGAL_READ) { + log("Illegal byte read: %08x at %08x\n", address, armMode ? + armNextPC - 4 : armNextPC - 2); + } +#endif + if(cpuDmaHack) { + return cpuDmaLast & 0xFF; + } else { + if(armState) { + return CPUReadByteQuick(reg[15].I+(address & 3)); + } else { + return CPUReadByteQuick(reg[15].I+(address & 1)); + } + } + break; + } +} + +static inline void CPUWriteMemory(u32 address, u32 value) +{ + +#ifdef GBA_LOGGING + if(address & 3) { + if(systemVerbose & VERBOSE_UNALIGNED_MEMORY) { + log("Unaligned word write: %08x to %08x from %08x\n", + value, + address, + armMode ? armNextPC - 4 : armNextPC - 2); + } + } +#endif + + switch(address >> 24) { + case 0x02: +#ifdef BKPT_SUPPORT + if(*((u32 *)&freezeWorkRAM[address & 0x3FFFC])) + cheatsWriteMemory(address & 0x203FFFC, + value); + else +#endif + WRITE32LE(((u32 *)&workRAM[address & 0x3FFFC]), value); + break; + case 0x03: +#ifdef BKPT_SUPPORT + if(*((u32 *)&freezeInternalRAM[address & 0x7ffc])) + cheatsWriteMemory(address & 0x3007FFC, + value); + else +#endif + WRITE32LE(((u32 *)&internalRAM[address & 0x7ffC]), value); + break; + case 0x04: + if(address < 0x4000400) { + CPUUpdateRegister((address & 0x3FC), value & 0xFFFF); + CPUUpdateRegister((address & 0x3FC) + 2, (value >> 16)); + } else goto unwritable; + break; + case 0x05: +#ifdef BKPT_SUPPORT + if(*((u32 *)&freezePRAM[address & 0x3fc])) + cheatsWriteMemory(address & 0x70003FC, + value); + else +#endif + WRITE32LE(((u32 *)&paletteRAM[address & 0x3FC]), value); + break; + case 0x06: + address = (address & 0x1fffc); + if (((DISPCNT & 7) >2) && ((address & 0x1C000) == 0x18000)) + return; + if ((address & 0x18000) == 0x18000) + address &= 0x17fff; + +#ifdef BKPT_SUPPORT + if(*((u32 *)&freezeVRAM[address])) + cheatsWriteMemory(address + 0x06000000, value); + else +#endif + + WRITE32LE(((u32 *)&vram[address]), value); + break; + case 0x07: +#ifdef BKPT_SUPPORT + if(*((u32 *)&freezeOAM[address & 0x3fc])) + cheatsWriteMemory(address & 0x70003FC, + value); + else +#endif + WRITE32LE(((u32 *)&oam[address & 0x3fc]), value); + break; + case 0x0D: + if(cpuEEPROMEnabled) { + eepromWrite(address, value); + break; + } + goto unwritable; + case 0x0E: + if((!eepromInUse) | cpuSramEnabled | cpuFlashEnabled) { + (*cpuSaveGameFunc)(address, (u8)value); + break; + } + // default + default: +unwritable: +#ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_ILLEGAL_WRITE) { + log("Illegal word write: %08x to %08x from %08x\n", + value, + address, + armMode ? armNextPC - 4 : armNextPC - 2); + } +#endif + break; + } +} + +static inline void CPUWriteHalfWord(u32 address, u16 value) +{ +#ifdef GBA_LOGGING + if(address & 1) { + if(systemVerbose & VERBOSE_UNALIGNED_MEMORY) { + log("Unaligned halfword write: %04x to %08x from %08x\n", + value, + address, + armMode ? armNextPC - 4 : armNextPC - 2); + } + } +#endif + + switch(address >> 24) { + case 2: +#ifdef BKPT_SUPPORT + if(*((u16 *)&freezeWorkRAM[address & 0x3FFFE])) + cheatsWriteHalfWord(address & 0x203FFFE, + value); + else +#endif + WRITE16LE(((u16 *)&workRAM[address & 0x3FFFE]),value); + break; + case 3: +#ifdef BKPT_SUPPORT + if(*((u16 *)&freezeInternalRAM[address & 0x7ffe])) + cheatsWriteHalfWord(address & 0x3007ffe, + value); + else +#endif + WRITE16LE(((u16 *)&internalRAM[address & 0x7ffe]), value); + break; + case 4: + if(address < 0x4000400) + CPUUpdateRegister(address & 0x3fe, value); + else goto unwritable; + break; + case 5: +#ifdef BKPT_SUPPORT + if(*((u16 *)&freezePRAM[address & 0x03fe])) + cheatsWriteHalfWord(address & 0x70003fe, + value); + else +#endif + WRITE16LE(((u16 *)&paletteRAM[address & 0x3fe]), value); + break; + case 6: + address = (address & 0x1fffe); + if (((DISPCNT & 7) >2) && ((address & 0x1C000) == 0x18000)) + return; + if ((address & 0x18000) == 0x18000) + address &= 0x17fff; +#ifdef BKPT_SUPPORT + if(*((u16 *)&freezeVRAM[address])) + cheatsWriteHalfWord(address + 0x06000000, + value); + else +#endif + WRITE16LE(((u16 *)&vram[address]), value); + break; + case 7: +#ifdef BKPT_SUPPORT + if(*((u16 *)&freezeOAM[address & 0x03fe])) + cheatsWriteHalfWord(address & 0x70003fe, + value); + else +#endif + WRITE16LE(((u16 *)&oam[address & 0x3fe]), value); + break; + case 8: + case 9: + if(address == 0x80000c4 || address == 0x80000c6 || address == 0x80000c8) { + if(!rtcWrite(address, value)) + goto unwritable; + } else if(!agbPrintWrite(address, value)) goto unwritable; + break; + case 13: + if(cpuEEPROMEnabled) { + eepromWrite(address, (u8)value); + break; + } + goto unwritable; + case 14: + if((!eepromInUse) | cpuSramEnabled | cpuFlashEnabled) { + (*cpuSaveGameFunc)(address, (u8)value); + break; + } + goto unwritable; + default: +unwritable: +#ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_ILLEGAL_WRITE) { + log("Illegal halfword write: %04x to %08x from %08x\n", + value, + address, + armMode ? armNextPC - 4 : armNextPC - 2); + } +#endif + break; + } +} + +static inline void CPUWriteByte(u32 address, u8 b) +{ + switch(address >> 24) { + case 2: +#ifdef BKPT_SUPPORT + if(freezeWorkRAM[address & 0x3FFFF]) + cheatsWriteByte(address & 0x203FFFF, b); + else +#endif + workRAM[address & 0x3FFFF] = b; + break; + case 3: +#ifdef BKPT_SUPPORT + if(freezeInternalRAM[address & 0x7fff]) + cheatsWriteByte(address & 0x3007fff, b); + else +#endif + internalRAM[address & 0x7fff] = b; + break; + case 4: + if(address < 0x4000400) { + switch(address & 0x3FF) { + case 0x60: + case 0x61: + case 0x62: + case 0x63: + case 0x64: + case 0x65: + case 0x68: + case 0x69: + case 0x6c: + case 0x6d: + case 0x70: + case 0x71: + case 0x72: + case 0x73: + case 0x74: + case 0x75: + case 0x78: + case 0x79: + case 0x7c: + case 0x7d: + case 0x80: + case 0x81: + case 0x84: + case 0x85: + case 0x90: + case 0x91: + case 0x92: + case 0x93: + case 0x94: + case 0x95: + case 0x96: + case 0x97: + case 0x98: + case 0x99: + case 0x9a: + case 0x9b: + case 0x9c: + case 0x9d: + case 0x9e: + case 0x9f: + soundEvent(address&0xFF, b); + break; + case 0x301: // HALTCNT, undocumented + if(b == 0x80) + stopState = true; + holdState = 1; + holdType = -1; + cpuNextEvent = cpuTotalTicks; + break; + default: // every other register + u32 lowerBits = address & 0x3fe; + if(address & 1) { + CPUUpdateRegister(lowerBits, (READ16LE(&ioMem[lowerBits]) & 0x00FF) | (b << 8)); + } else { + CPUUpdateRegister(lowerBits, (READ16LE(&ioMem[lowerBits]) & 0xFF00) | b); + } + } + break; + } else goto unwritable; + break; + case 5: + // no need to switch + *((u16 *)&paletteRAM[address & 0x3FE]) = (b << 8) | b; + break; + case 6: + address = (address & 0x1fffe); + if (((DISPCNT & 7) >2) && ((address & 0x1C000) == 0x18000)) + return; + if ((address & 0x18000) == 0x18000) + address &= 0x17fff; + + // no need to switch + // byte writes to OBJ VRAM are ignored + if ((address) < objTilesAddress[((DISPCNT&7)+1)>>2]) + { +#ifdef BKPT_SUPPORT + if(freezeVRAM[address]) + cheatsWriteByte(address + 0x06000000, b); + else +#endif + *((u16 *)&vram[address]) = (b << 8) | b; + } + break; + case 7: + // no need to switch + // byte writes to OAM are ignored + // *((u16 *)&oam[address & 0x3FE]) = (b << 8) | b; + break; + case 13: + if(cpuEEPROMEnabled) { + eepromWrite(address, b); + break; + } + goto unwritable; + case 14: + if ((saveType != 5) && ((!eepromInUse) | cpuSramEnabled | cpuFlashEnabled)) { + + //if(!cpuEEPROMEnabled && (cpuSramEnabled | cpuFlashEnabled)) { + + (*cpuSaveGameFunc)(address, b); + break; + } + // default + default: +unwritable: +#ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_ILLEGAL_WRITE) { + log("Illegal byte write: %02x to %08x from %08x\n", + b, + address, + armMode ? armNextPC - 4 : armNextPC -2 ); + } +#endif + break; + } +} + +#endif // GBAINLINE_H diff --git a/src/gba/RTC.cpp b/src/gba/RTC.cpp index 2d597a33..21d0a2de 100644 --- a/src/gba/RTC.cpp +++ b/src/gba/RTC.cpp @@ -1,208 +1,208 @@ -#include "../System.h" -#include "GBA.h" -#include "Globals.h" -#include "../common/Port.h" -#include "../Util.h" -#include "../NLS.h" - -#include -#include - -enum RTCSTATE { IDLE, COMMAND, DATA, READDATA }; - -typedef struct { - u8 byte0; - u8 byte1; - u8 byte2; - u8 command; - int dataLen; - int bits; - RTCSTATE state; - u8 data[12]; - // reserved variables for future - u8 reserved[12]; - bool reserved2; - u32 reserved3; -} RTCCLOCKDATA; - -static RTCCLOCKDATA rtcClockData; -static bool rtcEnabled = false; - -void rtcEnable(bool e) -{ - rtcEnabled = e; -} - -bool rtcIsEnabled() -{ - return rtcEnabled; -} - -u16 rtcRead(u32 address) -{ - if(rtcEnabled) { - switch(address){ - case 0x80000c8: - return rtcClockData.byte2; - break; - case 0x80000c6: - return rtcClockData.byte1; - break; - case 0x80000c4: - return rtcClockData.byte0; - break; - } - } - - return READ16LE((&rom[address & 0x1FFFFFE])); -} - -static u8 toBCD(u8 value) -{ - value = value % 100; - int l = value % 10; - int h = value / 10; - return h * 16 + l; -} - -bool rtcWrite(u32 address, u16 value) -{ - if(!rtcEnabled) - return false; - - if(address == 0x80000c8) { - rtcClockData.byte2 = (u8)value; // enable ? - } else if(address == 0x80000c6) { - rtcClockData.byte1 = (u8)value; // read/write - } else if(address == 0x80000c4) { - if(rtcClockData.byte2 & 1) { - if(rtcClockData.state == IDLE && rtcClockData.byte0 == 1 && value == 5) { - rtcClockData.state = COMMAND; - rtcClockData.bits = 0; - rtcClockData.command = 0; - } else if(!(rtcClockData.byte0 & 1) && (value & 1)) { // bit transfer - rtcClockData.byte0 = (u8)value; - switch(rtcClockData.state) { - case COMMAND: - rtcClockData.command |= ((value & 2) >> 1) << (7-rtcClockData.bits); - rtcClockData.bits++; - if(rtcClockData.bits == 8) { - rtcClockData.bits = 0; - switch(rtcClockData.command) { - case 0x60: - // not sure what this command does but it doesn't take parameters - // maybe it is a reset or stop - rtcClockData.state = IDLE; - rtcClockData.bits = 0; - break; - case 0x62: - // this sets the control state but not sure what those values are - rtcClockData.state = READDATA; - rtcClockData.dataLen = 1; - break; - case 0x63: - rtcClockData.dataLen = 1; - rtcClockData.data[0] = 0x40; - rtcClockData.state = DATA; - break; - case 0x64: - break; - case 0x65: - { - struct tm *newtime; - time_t long_time; - - time( &long_time ); /* Get time as long integer. */ - newtime = localtime( &long_time ); /* Convert to local time. */ - - rtcClockData.dataLen = 7; - rtcClockData.data[0] = toBCD(newtime->tm_year); - rtcClockData.data[1] = toBCD(newtime->tm_mon+1); - rtcClockData.data[2] = toBCD(newtime->tm_mday); - rtcClockData.data[3] = toBCD(newtime->tm_wday); - rtcClockData.data[4] = toBCD(newtime->tm_hour); - rtcClockData.data[5] = toBCD(newtime->tm_min); - rtcClockData.data[6] = toBCD(newtime->tm_sec); - rtcClockData.state = DATA; - } - break; - case 0x67: - { - struct tm *newtime; - time_t long_time; - - time( &long_time ); /* Get time as long integer. */ - newtime = localtime( &long_time ); /* Convert to local time. */ - - rtcClockData.dataLen = 3; - rtcClockData.data[0] = toBCD(newtime->tm_hour); - rtcClockData.data[1] = toBCD(newtime->tm_min); - rtcClockData.data[2] = toBCD(newtime->tm_sec); - rtcClockData.state = DATA; - } - break; - default: - systemMessage(0, N_("Unknown RTC command %02x"), rtcClockData.command); - rtcClockData.state = IDLE; - break; - } - } - break; - case DATA: - if(rtcClockData.byte1 & 2) { - } else { - rtcClockData.byte0 = (rtcClockData.byte0 & ~2) | - ((rtcClockData.data[rtcClockData.bits >> 3] >> - (rtcClockData.bits & 7)) & 1)*2; - rtcClockData.bits++; - if(rtcClockData.bits == 8*rtcClockData.dataLen) { - rtcClockData.bits = 0; - rtcClockData.state = IDLE; - } - } - break; - case READDATA: - if(!(rtcClockData.byte1 & 2)) { - } else { - rtcClockData.data[rtcClockData.bits >> 3] = - (rtcClockData.data[rtcClockData.bits >> 3] >> 1) | - ((value << 6) & 128); - rtcClockData.bits++; - if(rtcClockData.bits == 8*rtcClockData.dataLen) { - rtcClockData.bits = 0; - rtcClockData.state = IDLE; - } - } - break; - default: - break; - } - } else - rtcClockData.byte0 = (u8)value; - } - } - return true; -} - -void rtcReset() -{ - memset(&rtcClockData, 0, sizeof(rtcClockData)); - - rtcClockData.byte0 = 0; - rtcClockData.byte1 = 0; - rtcClockData.byte2 = 0; - rtcClockData.command = 0; - rtcClockData.dataLen = 0; - rtcClockData.bits = 0; - rtcClockData.state = IDLE; -} - -void rtcSaveGame(gzFile gzFile) -{ - utilGzWrite(gzFile, &rtcClockData, sizeof(rtcClockData)); -} - -void rtcReadGame(gzFile gzFile) -{ - utilGzRead(gzFile, &rtcClockData, sizeof(rtcClockData)); -} +#include "../System.h" +#include "GBA.h" +#include "Globals.h" +#include "../common/Port.h" +#include "../Util.h" +#include "../NLS.h" + +#include +#include + +enum RTCSTATE { IDLE, COMMAND, DATA, READDATA }; + +typedef struct { + u8 byte0; + u8 byte1; + u8 byte2; + u8 command; + int dataLen; + int bits; + RTCSTATE state; + u8 data[12]; + // reserved variables for future + u8 reserved[12]; + bool reserved2; + u32 reserved3; +} RTCCLOCKDATA; + +static RTCCLOCKDATA rtcClockData; +static bool rtcEnabled = false; + +void rtcEnable(bool e) +{ + rtcEnabled = e; +} + +bool rtcIsEnabled() +{ + return rtcEnabled; +} + +u16 rtcRead(u32 address) +{ + if(rtcEnabled) { + switch(address){ + case 0x80000c8: + return rtcClockData.byte2; + break; + case 0x80000c6: + return rtcClockData.byte1; + break; + case 0x80000c4: + return rtcClockData.byte0; + break; + } + } + + return READ16LE((&rom[address & 0x1FFFFFE])); +} + +static u8 toBCD(u8 value) +{ + value = value % 100; + int l = value % 10; + int h = value / 10; + return h * 16 + l; +} + +bool rtcWrite(u32 address, u16 value) +{ + if(!rtcEnabled) + return false; + + if(address == 0x80000c8) { + rtcClockData.byte2 = (u8)value; // enable ? + } else if(address == 0x80000c6) { + rtcClockData.byte1 = (u8)value; // read/write + } else if(address == 0x80000c4) { + if(rtcClockData.byte2 & 1) { + if(rtcClockData.state == IDLE && rtcClockData.byte0 == 1 && value == 5) { + rtcClockData.state = COMMAND; + rtcClockData.bits = 0; + rtcClockData.command = 0; + } else if(!(rtcClockData.byte0 & 1) && (value & 1)) { // bit transfer + rtcClockData.byte0 = (u8)value; + switch(rtcClockData.state) { + case COMMAND: + rtcClockData.command |= ((value & 2) >> 1) << (7-rtcClockData.bits); + rtcClockData.bits++; + if(rtcClockData.bits == 8) { + rtcClockData.bits = 0; + switch(rtcClockData.command) { + case 0x60: + // not sure what this command does but it doesn't take parameters + // maybe it is a reset or stop + rtcClockData.state = IDLE; + rtcClockData.bits = 0; + break; + case 0x62: + // this sets the control state but not sure what those values are + rtcClockData.state = READDATA; + rtcClockData.dataLen = 1; + break; + case 0x63: + rtcClockData.dataLen = 1; + rtcClockData.data[0] = 0x40; + rtcClockData.state = DATA; + break; + case 0x64: + break; + case 0x65: + { + struct tm *newtime; + time_t long_time; + + time( &long_time ); /* Get time as long integer. */ + newtime = localtime( &long_time ); /* Convert to local time. */ + + rtcClockData.dataLen = 7; + rtcClockData.data[0] = toBCD(newtime->tm_year); + rtcClockData.data[1] = toBCD(newtime->tm_mon+1); + rtcClockData.data[2] = toBCD(newtime->tm_mday); + rtcClockData.data[3] = toBCD(newtime->tm_wday); + rtcClockData.data[4] = toBCD(newtime->tm_hour); + rtcClockData.data[5] = toBCD(newtime->tm_min); + rtcClockData.data[6] = toBCD(newtime->tm_sec); + rtcClockData.state = DATA; + } + break; + case 0x67: + { + struct tm *newtime; + time_t long_time; + + time( &long_time ); /* Get time as long integer. */ + newtime = localtime( &long_time ); /* Convert to local time. */ + + rtcClockData.dataLen = 3; + rtcClockData.data[0] = toBCD(newtime->tm_hour); + rtcClockData.data[1] = toBCD(newtime->tm_min); + rtcClockData.data[2] = toBCD(newtime->tm_sec); + rtcClockData.state = DATA; + } + break; + default: + systemMessage(0, N_("Unknown RTC command %02x"), rtcClockData.command); + rtcClockData.state = IDLE; + break; + } + } + break; + case DATA: + if(rtcClockData.byte1 & 2) { + } else { + rtcClockData.byte0 = (rtcClockData.byte0 & ~2) | + ((rtcClockData.data[rtcClockData.bits >> 3] >> + (rtcClockData.bits & 7)) & 1)*2; + rtcClockData.bits++; + if(rtcClockData.bits == 8*rtcClockData.dataLen) { + rtcClockData.bits = 0; + rtcClockData.state = IDLE; + } + } + break; + case READDATA: + if(!(rtcClockData.byte1 & 2)) { + } else { + rtcClockData.data[rtcClockData.bits >> 3] = + (rtcClockData.data[rtcClockData.bits >> 3] >> 1) | + ((value << 6) & 128); + rtcClockData.bits++; + if(rtcClockData.bits == 8*rtcClockData.dataLen) { + rtcClockData.bits = 0; + rtcClockData.state = IDLE; + } + } + break; + default: + break; + } + } else + rtcClockData.byte0 = (u8)value; + } + } + return true; +} + +void rtcReset() +{ + memset(&rtcClockData, 0, sizeof(rtcClockData)); + + rtcClockData.byte0 = 0; + rtcClockData.byte1 = 0; + rtcClockData.byte2 = 0; + rtcClockData.command = 0; + rtcClockData.dataLen = 0; + rtcClockData.bits = 0; + rtcClockData.state = IDLE; +} + +void rtcSaveGame(gzFile gzFile) +{ + utilGzWrite(gzFile, &rtcClockData, sizeof(rtcClockData)); +} + +void rtcReadGame(gzFile gzFile) +{ + utilGzRead(gzFile, &rtcClockData, sizeof(rtcClockData)); +} diff --git a/src/gba/RTC.h b/src/gba/RTC.h index ea43b83e..8bedd68e 100644 --- a/src/gba/RTC.h +++ b/src/gba/RTC.h @@ -1,13 +1,13 @@ -#ifndef RTC_H -#define RTC_H - -u16 rtcRead(u32 address); -bool rtcWrite(u32 address, u16 value); -void rtcEnable(bool); -bool rtcIsEnabled(); -void rtcReset(); - -void rtcReadGame(gzFile gzFile); -void rtcSaveGame(gzFile gzFile); - -#endif // RTC_H +#ifndef RTC_H +#define RTC_H + +u16 rtcRead(u32 address); +bool rtcWrite(u32 address, u16 value); +void rtcEnable(bool); +bool rtcIsEnabled(); +void rtcReset(); + +void rtcReadGame(gzFile gzFile); +void rtcSaveGame(gzFile gzFile); + +#endif // RTC_H diff --git a/src/gba/gbafilter.cpp b/src/gba/gbafilter.cpp index b74ad4cd..523ec82e 100644 --- a/src/gba/gbafilter.cpp +++ b/src/gba/gbafilter.cpp @@ -1,227 +1,227 @@ -#include "gbafilter.h" - -#include - -extern int systemColorDepth; -extern int systemRedShift; -extern int systemGreenShift; -extern int systemBlueShift; - -extern u16 systemColorMap16[0x10000]; -extern u32 systemColorMap32[0x10000]; - -static const unsigned char curve[32] = { 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0e, 0x10, 0x12, - 0x14, 0x16, 0x18, 0x1c, 0x20, 0x28, 0x30, 0x38, - 0x40, 0x48, 0x50, 0x58, 0x60, 0x68, 0x70, 0x80, - 0x88, 0x90, 0xa0, 0xb0, 0xc0, 0xd0, 0xe0, 0xf0}; - -// output R G B -static const unsigned char influence[3 * 3] = { 16, 4, 4, // red - 8, 16, 8, // green - 0, 8, 16};// blue - -inline void swap(short & a, short & b) -{ - short temp = a; - a = b; - b = temp; -} - -void gbafilter_pal(u16 * buf, int count) -{ - short temp[3 * 3], s; - unsigned pix; - u8 red, green, blue; - - while (count--) - { - pix = *buf; - - s = curve[(pix >> systemGreenShift) & 0x1f]; - temp[3] = s * influence[3]; - temp[4] = s * influence[4]; - temp[5] = s * influence[5]; - - s = curve[(pix >> systemRedShift) & 0x1f]; - temp[0] = s * influence[0]; - temp[1] = s * influence[1]; - temp[2] = s * influence[2]; - - s = curve[(pix >> systemBlueShift) & 0x1f]; - temp[6] = s * influence[6]; - temp[7] = s * influence[7]; - temp[8] = s * influence[8]; - - if (temp[0] < temp[3]) swap(temp[0], temp[3]); - if (temp[0] < temp[6]) swap(temp[0], temp[6]); - if (temp[3] < temp[6]) swap(temp[3], temp[6]); - temp[3] <<= 1; - temp[0] <<= 2; - temp[0] += temp[3] + temp[6]; - - red = ((int(temp[0]) * 160) >> 17) + 4; - if (red > 31) red = 31; - - if (temp[2] < temp[5]) swap(temp[2], temp[5]); - if (temp[2] < temp[8]) swap(temp[2], temp[8]); - if (temp[5] < temp[8]) swap(temp[5], temp[8]); - temp[5] <<= 1; - temp[2] <<= 2; - temp[2] += temp[5] + temp[8]; - - blue = ((int(temp[2]) * 160) >> 17) + 4; - if (blue > 31) blue = 31; - - if (temp[1] < temp[4]) swap(temp[1], temp[4]); - if (temp[1] < temp[7]) swap(temp[1], temp[7]); - if (temp[4] < temp[7]) swap(temp[4], temp[7]); - temp[4] <<= 1; - temp[1] <<= 2; - temp[1] += temp[4] + temp[7]; - - green = ((int(temp[1]) * 160) >> 17) + 4; - if (green > 31) green = 31; - - pix = red << systemRedShift; - pix += green << systemGreenShift; - pix += blue << systemBlueShift; - - *buf++ = pix; - } -} - -void gbafilter_pal32(u32 * buf, int count) -{ - short temp[3 * 3], s; - unsigned pix; - u8 red, green, blue; - - while (count--) - { - pix = *buf; - - s = curve[(pix >> systemGreenShift) & 0x1f]; - temp[3] = s * influence[3]; - temp[4] = s * influence[4]; - temp[5] = s * influence[5]; - - s = curve[(pix >> systemRedShift) & 0x1f]; - temp[0] = s * influence[0]; - temp[1] = s * influence[1]; - temp[2] = s * influence[2]; - - s = curve[(pix >> systemBlueShift) & 0x1f]; - temp[6] = s * influence[6]; - temp[7] = s * influence[7]; - temp[8] = s * influence[8]; - - if (temp[0] < temp[3]) swap(temp[0], temp[3]); - if (temp[0] < temp[6]) swap(temp[0], temp[6]); - if (temp[3] < temp[6]) swap(temp[3], temp[6]); - temp[3] <<= 1; - temp[0] <<= 2; - temp[0] += temp[3] + temp[6]; - - //red = ((int(temp[0]) * 160) >> 17) + 4; - red = ((int(temp[0]) * 160) >> 14) + 32; - - if (temp[2] < temp[5]) swap(temp[2], temp[5]); - if (temp[2] < temp[8]) swap(temp[2], temp[8]); - if (temp[5] < temp[8]) swap(temp[5], temp[8]); - temp[5] <<= 1; - temp[2] <<= 2; - temp[2] += temp[5] + temp[8]; - - //blue = ((int(temp[2]) * 160) >> 17) + 4; - blue = ((int(temp[2]) * 160) >> 14) + 32; - - if (temp[1] < temp[4]) swap(temp[1], temp[4]); - if (temp[1] < temp[7]) swap(temp[1], temp[7]); - if (temp[4] < temp[7]) swap(temp[4], temp[7]); - temp[4] <<= 1; - temp[1] <<= 2; - temp[1] += temp[4] + temp[7]; - - //green = ((int(temp[1]) * 160) >> 17) + 4; - green = ((int(temp[1]) * 160) >> 14) + 32; - - //pix = red << redshift; - //pix += green << greenshift; - //pix += blue << blueshift; - - pix = red << (systemRedShift - 3); - pix += green << (systemGreenShift - 3); - pix += blue << (systemBlueShift - 3); - - *buf++ = pix; - } -} - -// for palette mode to work with the three spoony filters in 32bpp depth - -void gbafilter_pad(u8 * buf, int count) -{ - union - { - struct - { - u8 r; - u8 g; - u8 b; - u8 a; - } part; - unsigned whole; - } - mask; - - mask.whole = 0x1f << systemRedShift; - mask.whole += 0x1f << systemGreenShift; - mask.whole += 0x1f << systemBlueShift; - - switch (systemColorDepth) - { - case 24: - while (count--) - { - *buf++ &= mask.part.r; - *buf++ &= mask.part.g; - *buf++ &= mask.part.b; - } - break; - case 32: - while (count--) - { - *((u32*)buf) &= mask.whole; - buf += 4; - } - } -} - -/* -void UpdateSystemColorMaps(int lcd) -{ - switch(systemColorDepth) { - case 16: - { - for(int i = 0; i < 0x10000; i++) { - systemColorMap16[i] = ((i & 0x1f) << systemRedShift) | - (((i & 0x3e0) >> 5) << systemGreenShift) | - (((i & 0x7c00) >> 10) << systemBlueShift); - } - if (lcd == 1) gbafilter_pal(systemColorMap16, 0x10000); - } - break; - case 24: - case 32: - { - for(int i = 0; i < 0x10000; i++) { - systemColorMap32[i] = ((i & 0x1f) << systemRedShift) | - (((i & 0x3e0) >> 5) << systemGreenShift) | - (((i & 0x7c00) >> 10) << systemBlueShift); - } - if (lcd == 1) gbafilter_pal32(systemColorMap32, 0x10000); - } - break; - } -} -*/ +#include "gbafilter.h" + +#include + +extern int systemColorDepth; +extern int systemRedShift; +extern int systemGreenShift; +extern int systemBlueShift; + +extern u16 systemColorMap16[0x10000]; +extern u32 systemColorMap32[0x10000]; + +static const unsigned char curve[32] = { 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0e, 0x10, 0x12, + 0x14, 0x16, 0x18, 0x1c, 0x20, 0x28, 0x30, 0x38, + 0x40, 0x48, 0x50, 0x58, 0x60, 0x68, 0x70, 0x80, + 0x88, 0x90, 0xa0, 0xb0, 0xc0, 0xd0, 0xe0, 0xf0}; + +// output R G B +static const unsigned char influence[3 * 3] = { 16, 4, 4, // red + 8, 16, 8, // green + 0, 8, 16};// blue + +inline void swap(short & a, short & b) +{ + short temp = a; + a = b; + b = temp; +} + +void gbafilter_pal(u16 * buf, int count) +{ + short temp[3 * 3], s; + unsigned pix; + u8 red, green, blue; + + while (count--) + { + pix = *buf; + + s = curve[(pix >> systemGreenShift) & 0x1f]; + temp[3] = s * influence[3]; + temp[4] = s * influence[4]; + temp[5] = s * influence[5]; + + s = curve[(pix >> systemRedShift) & 0x1f]; + temp[0] = s * influence[0]; + temp[1] = s * influence[1]; + temp[2] = s * influence[2]; + + s = curve[(pix >> systemBlueShift) & 0x1f]; + temp[6] = s * influence[6]; + temp[7] = s * influence[7]; + temp[8] = s * influence[8]; + + if (temp[0] < temp[3]) swap(temp[0], temp[3]); + if (temp[0] < temp[6]) swap(temp[0], temp[6]); + if (temp[3] < temp[6]) swap(temp[3], temp[6]); + temp[3] <<= 1; + temp[0] <<= 2; + temp[0] += temp[3] + temp[6]; + + red = ((int(temp[0]) * 160) >> 17) + 4; + if (red > 31) red = 31; + + if (temp[2] < temp[5]) swap(temp[2], temp[5]); + if (temp[2] < temp[8]) swap(temp[2], temp[8]); + if (temp[5] < temp[8]) swap(temp[5], temp[8]); + temp[5] <<= 1; + temp[2] <<= 2; + temp[2] += temp[5] + temp[8]; + + blue = ((int(temp[2]) * 160) >> 17) + 4; + if (blue > 31) blue = 31; + + if (temp[1] < temp[4]) swap(temp[1], temp[4]); + if (temp[1] < temp[7]) swap(temp[1], temp[7]); + if (temp[4] < temp[7]) swap(temp[4], temp[7]); + temp[4] <<= 1; + temp[1] <<= 2; + temp[1] += temp[4] + temp[7]; + + green = ((int(temp[1]) * 160) >> 17) + 4; + if (green > 31) green = 31; + + pix = red << systemRedShift; + pix += green << systemGreenShift; + pix += blue << systemBlueShift; + + *buf++ = pix; + } +} + +void gbafilter_pal32(u32 * buf, int count) +{ + short temp[3 * 3], s; + unsigned pix; + u8 red, green, blue; + + while (count--) + { + pix = *buf; + + s = curve[(pix >> systemGreenShift) & 0x1f]; + temp[3] = s * influence[3]; + temp[4] = s * influence[4]; + temp[5] = s * influence[5]; + + s = curve[(pix >> systemRedShift) & 0x1f]; + temp[0] = s * influence[0]; + temp[1] = s * influence[1]; + temp[2] = s * influence[2]; + + s = curve[(pix >> systemBlueShift) & 0x1f]; + temp[6] = s * influence[6]; + temp[7] = s * influence[7]; + temp[8] = s * influence[8]; + + if (temp[0] < temp[3]) swap(temp[0], temp[3]); + if (temp[0] < temp[6]) swap(temp[0], temp[6]); + if (temp[3] < temp[6]) swap(temp[3], temp[6]); + temp[3] <<= 1; + temp[0] <<= 2; + temp[0] += temp[3] + temp[6]; + + //red = ((int(temp[0]) * 160) >> 17) + 4; + red = ((int(temp[0]) * 160) >> 14) + 32; + + if (temp[2] < temp[5]) swap(temp[2], temp[5]); + if (temp[2] < temp[8]) swap(temp[2], temp[8]); + if (temp[5] < temp[8]) swap(temp[5], temp[8]); + temp[5] <<= 1; + temp[2] <<= 2; + temp[2] += temp[5] + temp[8]; + + //blue = ((int(temp[2]) * 160) >> 17) + 4; + blue = ((int(temp[2]) * 160) >> 14) + 32; + + if (temp[1] < temp[4]) swap(temp[1], temp[4]); + if (temp[1] < temp[7]) swap(temp[1], temp[7]); + if (temp[4] < temp[7]) swap(temp[4], temp[7]); + temp[4] <<= 1; + temp[1] <<= 2; + temp[1] += temp[4] + temp[7]; + + //green = ((int(temp[1]) * 160) >> 17) + 4; + green = ((int(temp[1]) * 160) >> 14) + 32; + + //pix = red << redshift; + //pix += green << greenshift; + //pix += blue << blueshift; + + pix = red << (systemRedShift - 3); + pix += green << (systemGreenShift - 3); + pix += blue << (systemBlueShift - 3); + + *buf++ = pix; + } +} + +// for palette mode to work with the three spoony filters in 32bpp depth + +void gbafilter_pad(u8 * buf, int count) +{ + union + { + struct + { + u8 r; + u8 g; + u8 b; + u8 a; + } part; + unsigned whole; + } + mask; + + mask.whole = 0x1f << systemRedShift; + mask.whole += 0x1f << systemGreenShift; + mask.whole += 0x1f << systemBlueShift; + + switch (systemColorDepth) + { + case 24: + while (count--) + { + *buf++ &= mask.part.r; + *buf++ &= mask.part.g; + *buf++ &= mask.part.b; + } + break; + case 32: + while (count--) + { + *((u32*)buf) &= mask.whole; + buf += 4; + } + } +} + +/* +void UpdateSystemColorMaps(int lcd) +{ + switch(systemColorDepth) { + case 16: + { + for(int i = 0; i < 0x10000; i++) { + systemColorMap16[i] = ((i & 0x1f) << systemRedShift) | + (((i & 0x3e0) >> 5) << systemGreenShift) | + (((i & 0x7c00) >> 10) << systemBlueShift); + } + if (lcd == 1) gbafilter_pal(systemColorMap16, 0x10000); + } + break; + case 24: + case 32: + { + for(int i = 0; i < 0x10000; i++) { + systemColorMap32[i] = ((i & 0x1f) << systemRedShift) | + (((i & 0x3e0) >> 5) << systemGreenShift) | + (((i & 0x7c00) >> 10) << systemBlueShift); + } + if (lcd == 1) gbafilter_pal32(systemColorMap32, 0x10000); + } + break; + } +} +*/ diff --git a/src/gba/gbafilter.h b/src/gba/gbafilter.h index 17dfa3cb..18ff53d3 100644 --- a/src/gba/gbafilter.h +++ b/src/gba/gbafilter.h @@ -1,5 +1,5 @@ -#include "../System.h" - -void gbafilter_pal(u16 * buf, int count); -void gbafilter_pal32(u32 * buf, int count); -void gbafilter_pad(u8 * buf, int count); +#include "../System.h" + +void gbafilter_pal(u16 * buf, int count); +void gbafilter_pal32(u32 * buf, int count); +void gbafilter_pad(u8 * buf, int count); diff --git a/src/sdl/inputSDL.cpp b/src/sdl/inputSDL.cpp index 29a35d3b..c463b8be 100644 --- a/src/sdl/inputSDL.cpp +++ b/src/sdl/inputSDL.cpp @@ -1,621 +1,621 @@ -// VBA-M, A Nintendo Handheld Console Emulator -// Copyright (C) 2008 VBA-M development team -// -// 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, 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. - -#include "inputSDL.h" - -#define SDLBUTTONS_NUM 14 - -static void sdlUpdateKey(uint32_t key, bool down); -static void sdlUpdateJoyButton(int which, int button, bool pressed); -static void sdlUpdateJoyHat(int which, int hat, int value); -static void sdlUpdateJoyAxis(int which, int axis, int value); -static bool sdlCheckJoyKey(int key); - -static bool sdlButtons[4][SDLBUTTONS_NUM] = { - { false, false, false, false, false, false, - false, false, false, false, false, false, - false, false - }, - { false, false, false, false, false, false, - false, false, false, false, false, false, - false, false - }, - { false, false, false, false, false, false, - false, false, false, false, false, false, - false, false - }, - { false, false, false, false, false, false, - false, false, false, false, false, false, - false, false - } -}; - -static bool sdlMotionButtons[4] = { false, false, false, false }; - -static int sdlNumDevices = 0; -static SDL_Joystick **sdlDevices = NULL; - -static EPad sdlDefaultJoypad = PAD_MAIN; - -static int autoFire = 0; -static bool autoFireToggle = false; -static int autoFireCountdown = 0; -int autoFireMaxCount = 1; - -static uint32_t joypad[5][SDLBUTTONS_NUM] = { - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - { SDLK_LEFT, SDLK_RIGHT, - SDLK_UP, SDLK_DOWN, - SDLK_z, SDLK_x, - SDLK_RETURN,SDLK_BACKSPACE, - SDLK_a, SDLK_s, - SDLK_SPACE, SDLK_F12, - SDLK_q, SDLK_w, - } -}; - -static uint32_t motion[4] = { - SDLK_KP4, SDLK_KP6, SDLK_KP8, SDLK_KP2 -}; - -static uint32_t defaultMotion[4] = { - SDLK_KP4, SDLK_KP6, SDLK_KP8, SDLK_KP2 -}; - -static int sensorX = 2047; -static int sensorY = 2047; - -static uint32_t sdlGetHatCode(const SDL_Event &event) -{ - if (!event.jhat.value) return 0; - - return ( - ((event.jhat.which + 1) << 16) | - 32 | - (event.jhat.hat << 2) | - ( - event.jhat.value & SDL_HAT_UP ? 0 : - event.jhat.value & SDL_HAT_DOWN ? 1 : - event.jhat.value & SDL_HAT_RIGHT ? 2 : - event.jhat.value & SDL_HAT_LEFT ? 3 : 0 - ) - ); -} - -static uint32_t sdlGetButtonCode(const SDL_Event &event) -{ - return ( - ((event.jbutton.which + 1) << 16) | - (event.jbutton.button + 0x80) - ); -} - -static uint32_t sdlGetAxisCode(const SDL_Event &event) -{ - if (event.jaxis.value >= -16384 && event.jaxis.value <= 16384) return 0; - - return ( - ((event.jaxis.which + 1) << 16) | - (event.jaxis.axis << 1) | - ( - event.jaxis.value > 16384 ? 1 : - event.jaxis.value < -16384 ? 0 : 0 - ) - ); -} - -uint32_t inputGetEventCode(const SDL_Event &event) -{ - switch(event.type) - { - case SDL_KEYDOWN: - case SDL_KEYUP: - return event.key.keysym.sym; - break; - case SDL_JOYHATMOTION: - return sdlGetHatCode(event); - break; - case SDL_JOYBUTTONDOWN: - case SDL_JOYBUTTONUP: - return sdlGetButtonCode(event); - break; - case SDL_JOYAXISMOTION: - return sdlGetAxisCode(event); - break; - default: - return 0; - break; - } -} - -uint32_t inputGetKeymap(EPad pad, EKey key) -{ - return joypad[pad][key]; -} - -void inputSetKeymap(EPad pad, EKey key, uint32_t code) -{ - joypad[pad][key] = code; -} - -void inputSetMotionKeymap(EKey key, uint32_t code) -{ - motion[key] = code; -} - -bool inputGetAutoFire(EKey key) -{ - int mask = 0; - - switch (key) - { - case KEY_BUTTON_A: - mask = 1 << 0; - break; - case KEY_BUTTON_B: - mask = 1 << 1; - break; - case KEY_BUTTON_R: - mask = 1 << 8; - break; - case KEY_BUTTON_L: - mask = 1 << 9; - break; - default: - break; - } - - return !(autoFire & mask); -} - -bool inputToggleAutoFire(EKey key) -{ - int mask = 0; - - switch (key) - { - case KEY_BUTTON_A: - mask = 1 << 0; - break; - case KEY_BUTTON_B: - mask = 1 << 1; - break; - case KEY_BUTTON_R: - mask = 1 << 8; - break; - case KEY_BUTTON_L: - mask = 1 << 9; - break; - default: - break; - } - - if(autoFire & mask) - { - autoFire &= ~mask; - return false; - } - else - { - autoFire |= mask; - return true; - } -} - -static void sdlUpdateKey(uint32_t key, bool down) -{ - int i; - for(int j = 0; j < 4; j++) { - for(i = 0 ; i < SDLBUTTONS_NUM; i++) { - if((joypad[j][i] & 0xffff0000) == 0) { - if(key == joypad[j][i]) - sdlButtons[j][i] = down; - } - } - } - for(i = 0 ; i < 4; i++) { - if((motion[i] & 0xffff0000) == 0) { - if(key == motion[i]) - sdlMotionButtons[i] = down; - } - } -} - -static void sdlUpdateJoyButton(int which, - int button, - bool pressed) -{ - int i; - for(int j = 0; j < 4; j++) { - for(i = 0; i < SDLBUTTONS_NUM; i++) { - int dev = (joypad[j][i] >> 16); - int b = joypad[j][i] & 0xffff; - if(dev) { - dev--; - - if((dev == which) && (b >= 128) && (b == (button+128))) { - sdlButtons[j][i] = pressed; - } - } - } - } - for(i = 0; i < 4; i++) { - int dev = (motion[i] >> 16); - int b = motion[i] & 0xffff; - if(dev) { - dev--; - - if((dev == which) && (b >= 128) && (b == (button+128))) { - sdlMotionButtons[i] = pressed; - } - } - } -} - -static void sdlUpdateJoyHat(int which, - int hat, - int value) -{ - int i; - for(int j = 0; j < 4; j++) { - for(i = 0; i < SDLBUTTONS_NUM; i++) { - int dev = (joypad[j][i] >> 16); - int a = joypad[j][i] & 0xffff; - if(dev) { - dev--; - - if((dev == which) && (a>=32) && (a < 48) && (((a&15)>>2) == hat)) { - int dir = a & 3; - int v = 0; - switch(dir) { - case 0: - v = value & SDL_HAT_UP; - break; - case 1: - v = value & SDL_HAT_DOWN; - break; - case 2: - v = value & SDL_HAT_RIGHT; - break; - case 3: - v = value & SDL_HAT_LEFT; - break; - } - sdlButtons[j][i] = (v ? true : false); - } - } - } - } - for(i = 0; i < 4; i++) { - int dev = (motion[i] >> 16); - int a = motion[i] & 0xffff; - if(dev) { - dev--; - - if((dev == which) && (a>=32) && (a < 48) && (((a&15)>>2) == hat)) { - int dir = a & 3; - int v = 0; - switch(dir) { - case 0: - v = value & SDL_HAT_UP; - break; - case 1: - v = value & SDL_HAT_DOWN; - break; - case 2: - v = value & SDL_HAT_RIGHT; - break; - case 3: - v = value & SDL_HAT_LEFT; - break; - } - sdlMotionButtons[i] = (v ? true : false); - } - } - } -} - -static void sdlUpdateJoyAxis(int which, - int axis, - int value) -{ - int i; - for(int j = 0; j < 4; j++) { - for(i = 0; i < SDLBUTTONS_NUM; i++) { - int dev = (joypad[j][i] >> 16); - int a = joypad[j][i] & 0xffff; - if(dev) { - dev--; - - if((dev == which) && (a < 32) && ((a>>1) == axis)) { - sdlButtons[j][i] = (a & 1) ? (value > 16384) : (value < -16384); - } - } - } - } - for(i = 0; i < 4; i++) { - int dev = (motion[i] >> 16); - int a = motion[i] & 0xffff; - if(dev) { - dev--; - - if((dev == which) && (a < 32) && ((a>>1) == axis)) { - sdlMotionButtons[i] = (a & 1) ? (value > 16384) : (value < -16384); - } - } - } -} - -static bool sdlCheckJoyKey(int key) -{ - int dev = (key >> 16) - 1; - int what = key & 0xffff; - - if(what >= 128) { - // joystick button - int button = what - 128; - - if(button >= SDL_JoystickNumButtons(sdlDevices[dev])) - return false; - } else if (what < 0x20) { - // joystick axis - what >>= 1; - if(what >= SDL_JoystickNumAxes(sdlDevices[dev])) - return false; - } else if (what < 0x30) { - // joystick hat - what = (what & 15); - what >>= 2; - if(what >= SDL_JoystickNumHats(sdlDevices[dev])) - return false; - } - - // no problem found - return true; -} - -void inputInitJoysticks() -{ - // The main joypad has to be entirely defined - for(int i = 0; i < SDLBUTTONS_NUM; i++) { - if (!joypad[PAD_MAIN][i]) - joypad[PAD_MAIN][i] = joypad[PAD_DEFAULT][i]; - } - - sdlNumDevices = SDL_NumJoysticks(); - - if(sdlNumDevices) - sdlDevices = (SDL_Joystick **)calloc(1,sdlNumDevices * - sizeof(SDL_Joystick **)); - bool usesJoy = false; - - for(int j = 0; j < 4; j++) { - for(int i = 0; i < SDLBUTTONS_NUM; i++) { - int dev = joypad[j][i] >> 16; - if(dev) { - dev--; - bool ok = false; - - if(sdlDevices) { - if(dev < sdlNumDevices) { - if(sdlDevices[dev] == NULL) { - sdlDevices[dev] = SDL_JoystickOpen(dev); - } - - ok = sdlCheckJoyKey(joypad[j][i]); - } else - ok = false; - } - - if(!ok) - joypad[j][i] = joypad[PAD_DEFAULT][i]; - else - usesJoy = true; - } - } - } - - for(int i = 0; i < 4; i++) { - int dev = motion[i] >> 16; - if(dev) { - dev--; - bool ok = false; - - if(sdlDevices) { - if(dev < sdlNumDevices) { - if(sdlDevices[dev] == NULL) { - sdlDevices[dev] = SDL_JoystickOpen(dev); - } - - ok = sdlCheckJoyKey(motion[i]); - } else - ok = false; - } - - if(!ok) - motion[i] = defaultMotion[i]; - else - usesJoy = true; - } - } - - if(usesJoy) - SDL_JoystickEventState(SDL_ENABLE); -} - -void inputProcessSDLEvent(const SDL_Event &event) -{ -// fprintf(stdout, "%x\n", inputGetEventCode(event)); - - switch(event.type) - { - case SDL_KEYDOWN: - sdlUpdateKey(event.key.keysym.sym, true); - break; - case SDL_KEYUP: - sdlUpdateKey(event.key.keysym.sym, false); - break; - case SDL_JOYHATMOTION: - sdlUpdateJoyHat(event.jhat.which, - event.jhat.hat, - event.jhat.value); - break; - case SDL_JOYBUTTONDOWN: - case SDL_JOYBUTTONUP: - sdlUpdateJoyButton(event.jbutton.which, - event.jbutton.button, - event.jbutton.state == SDL_PRESSED); - break; - case SDL_JOYAXISMOTION: - sdlUpdateJoyAxis(event.jaxis.which, - event.jaxis.axis, - event.jaxis.value); - break; - } -} - -uint32_t inputReadJoypad(int which) -{ - int realAutoFire = autoFire; - - if(which < 0 || which > 3) - which = sdlDefaultJoypad; - - uint32_t res = 0; - - if(sdlButtons[which][KEY_BUTTON_A]) - res |= 1; - if(sdlButtons[which][KEY_BUTTON_B]) - res |= 2; - if(sdlButtons[which][KEY_BUTTON_SELECT]) - res |= 4; - if(sdlButtons[which][KEY_BUTTON_START]) - res |= 8; - if(sdlButtons[which][KEY_RIGHT]) - res |= 16; - if(sdlButtons[which][KEY_LEFT]) - res |= 32; - if(sdlButtons[which][KEY_UP]) - res |= 64; - if(sdlButtons[which][KEY_DOWN]) - res |= 128; - if(sdlButtons[which][KEY_BUTTON_R]) - res |= 256; - if(sdlButtons[which][KEY_BUTTON_L]) - res |= 512; - if(sdlButtons[which][KEY_BUTTON_AUTO_A]) - realAutoFire ^= 1; - if(sdlButtons[which][KEY_BUTTON_AUTO_B]) - realAutoFire ^= 2; - - // disallow L+R or U+D of being pressed at the same time - if((res & 48) == 48) - res &= ~16; - if((res & 192) == 192) - res &= ~128; - - if(sdlButtons[which][KEY_BUTTON_SPEED]) - res |= 1024; - if(sdlButtons[which][KEY_BUTTON_CAPTURE]) - res |= 2048; - - if(realAutoFire) { - res &= (~realAutoFire); - if(autoFireToggle) - res |= realAutoFire; - autoFireCountdown--; // this needs decrementing even when autoFireToggle is toggled, - // so that autoFireMaxCount==1 (the default) will alternate at the maximum possible - // frequency (every time this code is reached). Which is what it did before - // introducing autoFireCountdown. - if (autoFireCountdown <= 0) { - autoFireToggle = !autoFireToggle; - autoFireCountdown = autoFireMaxCount; - } - } - - return res; -} - -void inputUpdateMotionSensor() -{ - if(sdlMotionButtons[KEY_LEFT]) { - sensorX += 3; - if(sensorX > 2197) - sensorX = 2197; - if(sensorX < 2047) - sensorX = 2057; - } else if(sdlMotionButtons[KEY_RIGHT]) { - sensorX -= 3; - if(sensorX < 1897) - sensorX = 1897; - if(sensorX > 2047) - sensorX = 2037; - } else if(sensorX > 2047) { - sensorX -= 2; - if(sensorX < 2047) - sensorX = 2047; - } else { - sensorX += 2; - if(sensorX > 2047) - sensorX = 2047; - } - - if(sdlMotionButtons[KEY_UP]) { - sensorY += 3; - if(sensorY > 2197) - sensorY = 2197; - if(sensorY < 2047) - sensorY = 2057; - } else if(sdlMotionButtons[KEY_DOWN]) { - sensorY -= 3; - if(sensorY < 1897) - sensorY = 1897; - if(sensorY > 2047) - sensorY = 2037; - } else if(sensorY > 2047) { - sensorY -= 2; - if(sensorY < 2047) - sensorY = 2047; - } else { - sensorY += 2; - if(sensorY > 2047) - sensorY = 2047; - } -} - -int inputGetSensorX() -{ - return sensorX; -} - -int inputGetSensorY() -{ - return sensorY; -} - -void inputSetDefaultJoypad(EPad pad) -{ - sdlDefaultJoypad = pad; -} - -EPad inputGetDefaultJoypad() -{ - return sdlDefaultJoypad; -} +// VBA-M, A Nintendo Handheld Console Emulator +// Copyright (C) 2008 VBA-M development team +// +// 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, 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. + +#include "inputSDL.h" + +#define SDLBUTTONS_NUM 14 + +static void sdlUpdateKey(uint32_t key, bool down); +static void sdlUpdateJoyButton(int which, int button, bool pressed); +static void sdlUpdateJoyHat(int which, int hat, int value); +static void sdlUpdateJoyAxis(int which, int axis, int value); +static bool sdlCheckJoyKey(int key); + +static bool sdlButtons[4][SDLBUTTONS_NUM] = { + { false, false, false, false, false, false, + false, false, false, false, false, false, + false, false + }, + { false, false, false, false, false, false, + false, false, false, false, false, false, + false, false + }, + { false, false, false, false, false, false, + false, false, false, false, false, false, + false, false + }, + { false, false, false, false, false, false, + false, false, false, false, false, false, + false, false + } +}; + +static bool sdlMotionButtons[4] = { false, false, false, false }; + +static int sdlNumDevices = 0; +static SDL_Joystick **sdlDevices = NULL; + +static EPad sdlDefaultJoypad = PAD_MAIN; + +static int autoFire = 0; +static bool autoFireToggle = false; +static int autoFireCountdown = 0; +int autoFireMaxCount = 1; + +static uint32_t joypad[5][SDLBUTTONS_NUM] = { + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { SDLK_LEFT, SDLK_RIGHT, + SDLK_UP, SDLK_DOWN, + SDLK_z, SDLK_x, + SDLK_RETURN,SDLK_BACKSPACE, + SDLK_a, SDLK_s, + SDLK_SPACE, SDLK_F12, + SDLK_q, SDLK_w, + } +}; + +static uint32_t motion[4] = { + SDLK_KP4, SDLK_KP6, SDLK_KP8, SDLK_KP2 +}; + +static uint32_t defaultMotion[4] = { + SDLK_KP4, SDLK_KP6, SDLK_KP8, SDLK_KP2 +}; + +static int sensorX = 2047; +static int sensorY = 2047; + +static uint32_t sdlGetHatCode(const SDL_Event &event) +{ + if (!event.jhat.value) return 0; + + return ( + ((event.jhat.which + 1) << 16) | + 32 | + (event.jhat.hat << 2) | + ( + event.jhat.value & SDL_HAT_UP ? 0 : + event.jhat.value & SDL_HAT_DOWN ? 1 : + event.jhat.value & SDL_HAT_RIGHT ? 2 : + event.jhat.value & SDL_HAT_LEFT ? 3 : 0 + ) + ); +} + +static uint32_t sdlGetButtonCode(const SDL_Event &event) +{ + return ( + ((event.jbutton.which + 1) << 16) | + (event.jbutton.button + 0x80) + ); +} + +static uint32_t sdlGetAxisCode(const SDL_Event &event) +{ + if (event.jaxis.value >= -16384 && event.jaxis.value <= 16384) return 0; + + return ( + ((event.jaxis.which + 1) << 16) | + (event.jaxis.axis << 1) | + ( + event.jaxis.value > 16384 ? 1 : + event.jaxis.value < -16384 ? 0 : 0 + ) + ); +} + +uint32_t inputGetEventCode(const SDL_Event &event) +{ + switch(event.type) + { + case SDL_KEYDOWN: + case SDL_KEYUP: + return event.key.keysym.sym; + break; + case SDL_JOYHATMOTION: + return sdlGetHatCode(event); + break; + case SDL_JOYBUTTONDOWN: + case SDL_JOYBUTTONUP: + return sdlGetButtonCode(event); + break; + case SDL_JOYAXISMOTION: + return sdlGetAxisCode(event); + break; + default: + return 0; + break; + } +} + +uint32_t inputGetKeymap(EPad pad, EKey key) +{ + return joypad[pad][key]; +} + +void inputSetKeymap(EPad pad, EKey key, uint32_t code) +{ + joypad[pad][key] = code; +} + +void inputSetMotionKeymap(EKey key, uint32_t code) +{ + motion[key] = code; +} + +bool inputGetAutoFire(EKey key) +{ + int mask = 0; + + switch (key) + { + case KEY_BUTTON_A: + mask = 1 << 0; + break; + case KEY_BUTTON_B: + mask = 1 << 1; + break; + case KEY_BUTTON_R: + mask = 1 << 8; + break; + case KEY_BUTTON_L: + mask = 1 << 9; + break; + default: + break; + } + + return !(autoFire & mask); +} + +bool inputToggleAutoFire(EKey key) +{ + int mask = 0; + + switch (key) + { + case KEY_BUTTON_A: + mask = 1 << 0; + break; + case KEY_BUTTON_B: + mask = 1 << 1; + break; + case KEY_BUTTON_R: + mask = 1 << 8; + break; + case KEY_BUTTON_L: + mask = 1 << 9; + break; + default: + break; + } + + if(autoFire & mask) + { + autoFire &= ~mask; + return false; + } + else + { + autoFire |= mask; + return true; + } +} + +static void sdlUpdateKey(uint32_t key, bool down) +{ + int i; + for(int j = 0; j < 4; j++) { + for(i = 0 ; i < SDLBUTTONS_NUM; i++) { + if((joypad[j][i] & 0xffff0000) == 0) { + if(key == joypad[j][i]) + sdlButtons[j][i] = down; + } + } + } + for(i = 0 ; i < 4; i++) { + if((motion[i] & 0xffff0000) == 0) { + if(key == motion[i]) + sdlMotionButtons[i] = down; + } + } +} + +static void sdlUpdateJoyButton(int which, + int button, + bool pressed) +{ + int i; + for(int j = 0; j < 4; j++) { + for(i = 0; i < SDLBUTTONS_NUM; i++) { + int dev = (joypad[j][i] >> 16); + int b = joypad[j][i] & 0xffff; + if(dev) { + dev--; + + if((dev == which) && (b >= 128) && (b == (button+128))) { + sdlButtons[j][i] = pressed; + } + } + } + } + for(i = 0; i < 4; i++) { + int dev = (motion[i] >> 16); + int b = motion[i] & 0xffff; + if(dev) { + dev--; + + if((dev == which) && (b >= 128) && (b == (button+128))) { + sdlMotionButtons[i] = pressed; + } + } + } +} + +static void sdlUpdateJoyHat(int which, + int hat, + int value) +{ + int i; + for(int j = 0; j < 4; j++) { + for(i = 0; i < SDLBUTTONS_NUM; i++) { + int dev = (joypad[j][i] >> 16); + int a = joypad[j][i] & 0xffff; + if(dev) { + dev--; + + if((dev == which) && (a>=32) && (a < 48) && (((a&15)>>2) == hat)) { + int dir = a & 3; + int v = 0; + switch(dir) { + case 0: + v = value & SDL_HAT_UP; + break; + case 1: + v = value & SDL_HAT_DOWN; + break; + case 2: + v = value & SDL_HAT_RIGHT; + break; + case 3: + v = value & SDL_HAT_LEFT; + break; + } + sdlButtons[j][i] = (v ? true : false); + } + } + } + } + for(i = 0; i < 4; i++) { + int dev = (motion[i] >> 16); + int a = motion[i] & 0xffff; + if(dev) { + dev--; + + if((dev == which) && (a>=32) && (a < 48) && (((a&15)>>2) == hat)) { + int dir = a & 3; + int v = 0; + switch(dir) { + case 0: + v = value & SDL_HAT_UP; + break; + case 1: + v = value & SDL_HAT_DOWN; + break; + case 2: + v = value & SDL_HAT_RIGHT; + break; + case 3: + v = value & SDL_HAT_LEFT; + break; + } + sdlMotionButtons[i] = (v ? true : false); + } + } + } +} + +static void sdlUpdateJoyAxis(int which, + int axis, + int value) +{ + int i; + for(int j = 0; j < 4; j++) { + for(i = 0; i < SDLBUTTONS_NUM; i++) { + int dev = (joypad[j][i] >> 16); + int a = joypad[j][i] & 0xffff; + if(dev) { + dev--; + + if((dev == which) && (a < 32) && ((a>>1) == axis)) { + sdlButtons[j][i] = (a & 1) ? (value > 16384) : (value < -16384); + } + } + } + } + for(i = 0; i < 4; i++) { + int dev = (motion[i] >> 16); + int a = motion[i] & 0xffff; + if(dev) { + dev--; + + if((dev == which) && (a < 32) && ((a>>1) == axis)) { + sdlMotionButtons[i] = (a & 1) ? (value > 16384) : (value < -16384); + } + } + } +} + +static bool sdlCheckJoyKey(int key) +{ + int dev = (key >> 16) - 1; + int what = key & 0xffff; + + if(what >= 128) { + // joystick button + int button = what - 128; + + if(button >= SDL_JoystickNumButtons(sdlDevices[dev])) + return false; + } else if (what < 0x20) { + // joystick axis + what >>= 1; + if(what >= SDL_JoystickNumAxes(sdlDevices[dev])) + return false; + } else if (what < 0x30) { + // joystick hat + what = (what & 15); + what >>= 2; + if(what >= SDL_JoystickNumHats(sdlDevices[dev])) + return false; + } + + // no problem found + return true; +} + +void inputInitJoysticks() +{ + // The main joypad has to be entirely defined + for(int i = 0; i < SDLBUTTONS_NUM; i++) { + if (!joypad[PAD_MAIN][i]) + joypad[PAD_MAIN][i] = joypad[PAD_DEFAULT][i]; + } + + sdlNumDevices = SDL_NumJoysticks(); + + if(sdlNumDevices) + sdlDevices = (SDL_Joystick **)calloc(1,sdlNumDevices * + sizeof(SDL_Joystick **)); + bool usesJoy = false; + + for(int j = 0; j < 4; j++) { + for(int i = 0; i < SDLBUTTONS_NUM; i++) { + int dev = joypad[j][i] >> 16; + if(dev) { + dev--; + bool ok = false; + + if(sdlDevices) { + if(dev < sdlNumDevices) { + if(sdlDevices[dev] == NULL) { + sdlDevices[dev] = SDL_JoystickOpen(dev); + } + + ok = sdlCheckJoyKey(joypad[j][i]); + } else + ok = false; + } + + if(!ok) + joypad[j][i] = joypad[PAD_DEFAULT][i]; + else + usesJoy = true; + } + } + } + + for(int i = 0; i < 4; i++) { + int dev = motion[i] >> 16; + if(dev) { + dev--; + bool ok = false; + + if(sdlDevices) { + if(dev < sdlNumDevices) { + if(sdlDevices[dev] == NULL) { + sdlDevices[dev] = SDL_JoystickOpen(dev); + } + + ok = sdlCheckJoyKey(motion[i]); + } else + ok = false; + } + + if(!ok) + motion[i] = defaultMotion[i]; + else + usesJoy = true; + } + } + + if(usesJoy) + SDL_JoystickEventState(SDL_ENABLE); +} + +void inputProcessSDLEvent(const SDL_Event &event) +{ +// fprintf(stdout, "%x\n", inputGetEventCode(event)); + + switch(event.type) + { + case SDL_KEYDOWN: + sdlUpdateKey(event.key.keysym.sym, true); + break; + case SDL_KEYUP: + sdlUpdateKey(event.key.keysym.sym, false); + break; + case SDL_JOYHATMOTION: + sdlUpdateJoyHat(event.jhat.which, + event.jhat.hat, + event.jhat.value); + break; + case SDL_JOYBUTTONDOWN: + case SDL_JOYBUTTONUP: + sdlUpdateJoyButton(event.jbutton.which, + event.jbutton.button, + event.jbutton.state == SDL_PRESSED); + break; + case SDL_JOYAXISMOTION: + sdlUpdateJoyAxis(event.jaxis.which, + event.jaxis.axis, + event.jaxis.value); + break; + } +} + +uint32_t inputReadJoypad(int which) +{ + int realAutoFire = autoFire; + + if(which < 0 || which > 3) + which = sdlDefaultJoypad; + + uint32_t res = 0; + + if(sdlButtons[which][KEY_BUTTON_A]) + res |= 1; + if(sdlButtons[which][KEY_BUTTON_B]) + res |= 2; + if(sdlButtons[which][KEY_BUTTON_SELECT]) + res |= 4; + if(sdlButtons[which][KEY_BUTTON_START]) + res |= 8; + if(sdlButtons[which][KEY_RIGHT]) + res |= 16; + if(sdlButtons[which][KEY_LEFT]) + res |= 32; + if(sdlButtons[which][KEY_UP]) + res |= 64; + if(sdlButtons[which][KEY_DOWN]) + res |= 128; + if(sdlButtons[which][KEY_BUTTON_R]) + res |= 256; + if(sdlButtons[which][KEY_BUTTON_L]) + res |= 512; + if(sdlButtons[which][KEY_BUTTON_AUTO_A]) + realAutoFire ^= 1; + if(sdlButtons[which][KEY_BUTTON_AUTO_B]) + realAutoFire ^= 2; + + // disallow L+R or U+D of being pressed at the same time + if((res & 48) == 48) + res &= ~16; + if((res & 192) == 192) + res &= ~128; + + if(sdlButtons[which][KEY_BUTTON_SPEED]) + res |= 1024; + if(sdlButtons[which][KEY_BUTTON_CAPTURE]) + res |= 2048; + + if(realAutoFire) { + res &= (~realAutoFire); + if(autoFireToggle) + res |= realAutoFire; + autoFireCountdown--; // this needs decrementing even when autoFireToggle is toggled, + // so that autoFireMaxCount==1 (the default) will alternate at the maximum possible + // frequency (every time this code is reached). Which is what it did before + // introducing autoFireCountdown. + if (autoFireCountdown <= 0) { + autoFireToggle = !autoFireToggle; + autoFireCountdown = autoFireMaxCount; + } + } + + return res; +} + +void inputUpdateMotionSensor() +{ + if(sdlMotionButtons[KEY_LEFT]) { + sensorX += 3; + if(sensorX > 2197) + sensorX = 2197; + if(sensorX < 2047) + sensorX = 2057; + } else if(sdlMotionButtons[KEY_RIGHT]) { + sensorX -= 3; + if(sensorX < 1897) + sensorX = 1897; + if(sensorX > 2047) + sensorX = 2037; + } else if(sensorX > 2047) { + sensorX -= 2; + if(sensorX < 2047) + sensorX = 2047; + } else { + sensorX += 2; + if(sensorX > 2047) + sensorX = 2047; + } + + if(sdlMotionButtons[KEY_UP]) { + sensorY += 3; + if(sensorY > 2197) + sensorY = 2197; + if(sensorY < 2047) + sensorY = 2057; + } else if(sdlMotionButtons[KEY_DOWN]) { + sensorY -= 3; + if(sensorY < 1897) + sensorY = 1897; + if(sensorY > 2047) + sensorY = 2037; + } else if(sensorY > 2047) { + sensorY -= 2; + if(sensorY < 2047) + sensorY = 2047; + } else { + sensorY += 2; + if(sensorY > 2047) + sensorY = 2047; + } +} + +int inputGetSensorX() +{ + return sensorX; +} + +int inputGetSensorY() +{ + return sensorY; +} + +void inputSetDefaultJoypad(EPad pad) +{ + sdlDefaultJoypad = pad; +} + +EPad inputGetDefaultJoypad() +{ + return sdlDefaultJoypad; +} diff --git a/src/version.h b/src/version.h index 4039b1a8..1fd6b8a9 100644 --- a/src/version.h +++ b/src/version.h @@ -1,57 +1,57 @@ -// -*- C++ -*- -// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. -// Copyright (C) 1999-2003 Forgotten -// Copyright (C) 2004 Forgotten and the VBA development team - -// 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, 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. - -#define VBA_NAME "VisualBoyAdvance-M" - -#ifdef WIN32 -#include "svnrev.h" -#else -#ifdef SVN_REV -#define SVN_REV_STR SVN_REV -#else -#define SVN_REV_STR "" -#endif -#endif - -#define VBA_FEATURE_STRING "" - -#ifdef DEBUG -#define VBA_SUBVERSION_STRING " debug" -#elif defined(PUBLIC_RELEASE) -#define VBA_SUBVERSION_STRING "" -#else -#define VBA_SUBVERSION_STRING " (SVN" SVN_REV_STR ")" -#endif - -#if defined(_MSC_VER) -#define VBA_COMPILER "" -#define VBA_COMPILER_DETAIL " msvc " _Py_STRINGIZE(_MSC_VER) -#define _Py_STRINGIZE(X) _Py_STRINGIZE1((X)) -#define _Py_STRINGIZE1(X) _Py_STRINGIZE2 ## X -#define _Py_STRINGIZE2(X) #X -//re: http://72.14.203.104/search?q=cache:HG-okth5NGkJ:mail.python.org/pipermail/python-checkins/2002-November/030704.html+_msc_ver+compiler+version+string&hl=en&gl=us&ct=clnk&cd=5 -#else -// TODO: make for others compilers -#define VBA_COMPILER "" -#define VBA_COMPILER_DETAIL "" -#endif - -#define VBA_VERSION_STRING " " "1.8.0" VBA_SUBVERSION_STRING VBA_FEATURE_STRING VBA_COMPILER -#define VBA_NAME_AND_VERSION " " VBA_NAME VBA_VERSION_STRING -#define VBA_NAME_AND_SUBVERSION " " VBA_NAME VBA_SUBVERSION_STRING +// -*- C++ -*- +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 1999-2003 Forgotten +// Copyright (C) 2004 Forgotten and the VBA development team + +// 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, 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. + +#define VBA_NAME "VisualBoyAdvance-M" + +#ifdef WIN32 +#include "svnrev.h" +#else +#ifdef SVN_REV +#define SVN_REV_STR SVN_REV +#else +#define SVN_REV_STR "" +#endif +#endif + +#define VBA_FEATURE_STRING "" + +#ifdef DEBUG +#define VBA_SUBVERSION_STRING " debug" +#elif defined(PUBLIC_RELEASE) +#define VBA_SUBVERSION_STRING "" +#else +#define VBA_SUBVERSION_STRING " (SVN" SVN_REV_STR ")" +#endif + +#if defined(_MSC_VER) +#define VBA_COMPILER "" +#define VBA_COMPILER_DETAIL " msvc " _Py_STRINGIZE(_MSC_VER) +#define _Py_STRINGIZE(X) _Py_STRINGIZE1((X)) +#define _Py_STRINGIZE1(X) _Py_STRINGIZE2 ## X +#define _Py_STRINGIZE2(X) #X +//re: http://72.14.203.104/search?q=cache:HG-okth5NGkJ:mail.python.org/pipermail/python-checkins/2002-November/030704.html+_msc_ver+compiler+version+string&hl=en&gl=us&ct=clnk&cd=5 +#else +// TODO: make for others compilers +#define VBA_COMPILER "" +#define VBA_COMPILER_DETAIL "" +#endif + +#define VBA_VERSION_STRING " " "1.8.0" VBA_SUBVERSION_STRING VBA_FEATURE_STRING VBA_COMPILER +#define VBA_NAME_AND_VERSION " " VBA_NAME VBA_VERSION_STRING +#define VBA_NAME_AND_SUBVERSION " " VBA_NAME VBA_SUBVERSION_STRING diff --git a/src/win32/AudioCoreSettingsDlg.cpp b/src/win32/AudioCoreSettingsDlg.cpp index 5f719190..373c06ba 100644 --- a/src/win32/AudioCoreSettingsDlg.cpp +++ b/src/win32/AudioCoreSettingsDlg.cpp @@ -1,191 +1,191 @@ -#include "stdafx.h" -#include "VBA.h" - -#include "AudioCoreSettingsDlg.h" - -#define MIN_VOLUME 0.0f -#define MAX_VOLUME 4.0f - - -// AudioCoreSettingsDlg dialog - -IMPLEMENT_DYNAMIC(AudioCoreSettingsDlg, CDialog) - -AudioCoreSettingsDlg::AudioCoreSettingsDlg(CWnd* pParent /*=NULL*/) - : CDialog(AudioCoreSettingsDlg::IDD, pParent) - , m_enabled( false ) - , m_surround( false ) - , m_declicking( false ) - , m_sound_interpolation( false ) - , m_echo( 0.0f ) - , m_stereo( 0.0f ) - , m_volume( 0.0f ) - , m_sound_filtering( 0.0f ) - , m_sample_rate( 0 ) - , toolTip( NULL ) -{ -} - -AudioCoreSettingsDlg::~AudioCoreSettingsDlg() -{ - delete toolTip; -} - -void AudioCoreSettingsDlg::DoDataExchange(CDataExchange* pDX) -{ - CDialog::DoDataExchange(pDX); - - DDX_Control(pDX, IDC_ENHANCE_SOUND, enhance_sound); - DDX_Control(pDX, IDC_SURROUND, surround); - DDX_Control(pDX, IDC_ECHO, echo); - DDX_Control(pDX, IDC_STEREO, stereo); - DDX_Control(pDX, IDC_VOLUME, volume); - DDX_Control(pDX, IDC_DECLICKING, declicking); - DDX_Control(pDX, IDC_SOUND_INTERPOLATION, sound_interpolation); - DDX_Control(pDX, IDC_SOUND_FILTERING, sound_filtering); - DDX_Control(pDX, IDC_SAMPLE_RATE, sample_rate); - - if( pDX->m_bSaveAndValidate == TRUE ) { - m_enabled = BST_CHECKED == enhance_sound.GetCheck(); - m_surround = BST_CHECKED == surround.GetCheck(); - m_declicking = BST_CHECKED == declicking.GetCheck(); - m_sound_interpolation = BST_CHECKED == sound_interpolation.GetCheck(); - m_echo = (float)echo.GetPos() / 100.0f; - m_stereo = (float)stereo.GetPos() / 100.0f; - m_volume = (float)volume.GetPos() / 100.0f; - m_sound_filtering = (float)sound_filtering.GetPos() / 100.0f; - m_sample_rate = (unsigned int)sample_rate.GetItemData( sample_rate.GetCurSel() ); - } -} - -BOOL AudioCoreSettingsDlg::OnTtnNeedText(UINT id, NMHDR *pNMHDR, LRESULT *pResult) -{ - TOOLTIPTEXT *t3 = (TOOLTIPTEXT *)pNMHDR; // dirty Windows API - BOOL i_provided_tooltip_with_text = TRUE; - - if( !( t3->uFlags & TTF_IDISHWND ) ) { - return FALSE; - } - // even dirtier Windows API: - // t3->hdr.idFrom is actually a HWND, holy cow, why? - // The other case does not even occur. - int controlID = ::GetDlgCtrlID( (HWND)t3->hdr.idFrom ); - CString res; - TCHAR buf[0x400]; // Use own string buffer because szText has an 80 char limit. - // We can't use a dynamic buffer size because Windows does some shady things with - // t3->lpszText at the end of this function, so we have no chance to free the buffer - // before the end of this function. - - switch( controlID ) { - case IDC_VOLUME: - _stprintf_s( t3->szText, _countof( t3->szText ), _T( "%i%%" ), volume.GetPos() ); - break; - case IDC_ECHO: - _stprintf_s( t3->szText, _countof( t3->szText ), _T( "%i%%" ), echo.GetPos() ); - break; - case IDC_STEREO: - _stprintf_s( t3->szText, _countof( t3->szText ), _T( "%i%%" ), stereo.GetPos() ); - break; - case IDC_SOUND_FILTERING: - _stprintf_s( t3->szText, _countof( t3->szText ), _T( "%i%%" ), sound_filtering.GetPos() ); - break; - case IDC_DEFAULT_VOLUME: - res.LoadString( IDS_TOOLTIP_DEFAULT_VOLUME ); - _tcscpy_s( buf, _countof( buf ), res.GetString() ); - t3->lpszText = buf; - break; - case IDC_ENHANCE_SOUND: - res.LoadString( IDS_TOOLTIP_ENHANCE_SOUND ); - _tcscpy_s( buf, _countof( buf ), res.GetString() ); - t3->lpszText = buf; - break; - case IDC_SURROUND: - res.LoadString( IDS_TOOLTIP_SURROUND ); - _tcscpy_s( buf, _countof( buf ), res.GetString() ); - t3->lpszText = buf; - break; - case IDC_DECLICKING: - res.LoadString( IDS_TOOLTIP_DECLICKING ); - _tcscpy_s( buf, _countof( buf ), res.GetString() ); - t3->lpszText = buf; - break; - default: - i_provided_tooltip_with_text = FALSE; - break; - } - - return i_provided_tooltip_with_text; -} - - -BEGIN_MESSAGE_MAP(AudioCoreSettingsDlg, CDialog) - ON_NOTIFY_EX(TTN_NEEDTEXT, 0, &AudioCoreSettingsDlg::OnTtnNeedText) - ON_BN_CLICKED(IDC_DEFAULT_VOLUME, &AudioCoreSettingsDlg::OnBnClickedDefaultVolume) -END_MESSAGE_MAP() - - -// AudioCoreSettingsDlg message handlers - -BOOL AudioCoreSettingsDlg::OnInitDialog() -{ - CDialog::OnInitDialog(); - - // Set up tooltip control - toolTip = new CToolTipCtrl; - toolTip->Create( this ); - toolTip->AddTool( GetDlgItem( IDC_DEFAULT_VOLUME ) ); - toolTip->AddTool( GetDlgItem( IDC_ENHANCE_SOUND ) ); - toolTip->AddTool( GetDlgItem( IDC_SURROUND ) ); - toolTip->AddTool( GetDlgItem( IDC_DECLICKING ) ); - toolTip->Activate( TRUE ); - - enhance_sound.SetCheck( m_enabled ? BST_CHECKED : BST_UNCHECKED ); - - surround.SetCheck( m_surround ? BST_CHECKED : BST_UNCHECKED ); - - declicking.SetCheck( m_declicking ? BST_CHECKED : BST_UNCHECKED ); - - sound_interpolation.SetCheck( m_sound_interpolation ? BST_CHECKED : BST_UNCHECKED ); - - echo.SetRange( 0, 100 ); - echo.SetPos( (int)( m_echo * 100.0f ) ); - - stereo.SetRange( 0, 100 ); - stereo.SetPos( (int)( m_stereo * 100.0f ) ); - - sound_filtering.SetRange( 0, 100 ); - sound_filtering.SetPos( (int)( m_sound_filtering * 100.0f ) ); - - volume.SetRange( (int)( MIN_VOLUME * 100.0f ), (int)( MAX_VOLUME * 100.0f ) ); - volume.SetPos( (int)( m_volume * 100.0f ) ); - - unsigned int rate = 44100; - CString temp; - for( int i = 0 ; i <= 2 ; i++ ) { - temp.Format( _T("%u Hz"), rate ); - int id = sample_rate.AddString( temp.GetString() ); - sample_rate.SetItemData( id, rate ); - if( rate == m_sample_rate ) { - sample_rate.SetCurSel( id ); - } - rate /= 2; - } - - return TRUE; // return TRUE unless you set the focus to a control - // EXCEPTION: OCX Property Pages should return FALSE -} - -BOOL AudioCoreSettingsDlg::PreTranslateMessage(MSG* pMsg) -{ - // Required for enabling ToolTips in a modal dialog box. - if( NULL != toolTip ) { - toolTip->RelayEvent( pMsg ); - } - - return CDialog::PreTranslateMessage(pMsg); -} - -void AudioCoreSettingsDlg::OnBnClickedDefaultVolume() -{ - volume.SetPos( 100 ); -} +#include "stdafx.h" +#include "VBA.h" + +#include "AudioCoreSettingsDlg.h" + +#define MIN_VOLUME 0.0f +#define MAX_VOLUME 4.0f + + +// AudioCoreSettingsDlg dialog + +IMPLEMENT_DYNAMIC(AudioCoreSettingsDlg, CDialog) + +AudioCoreSettingsDlg::AudioCoreSettingsDlg(CWnd* pParent /*=NULL*/) + : CDialog(AudioCoreSettingsDlg::IDD, pParent) + , m_enabled( false ) + , m_surround( false ) + , m_declicking( false ) + , m_sound_interpolation( false ) + , m_echo( 0.0f ) + , m_stereo( 0.0f ) + , m_volume( 0.0f ) + , m_sound_filtering( 0.0f ) + , m_sample_rate( 0 ) + , toolTip( NULL ) +{ +} + +AudioCoreSettingsDlg::~AudioCoreSettingsDlg() +{ + delete toolTip; +} + +void AudioCoreSettingsDlg::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + + DDX_Control(pDX, IDC_ENHANCE_SOUND, enhance_sound); + DDX_Control(pDX, IDC_SURROUND, surround); + DDX_Control(pDX, IDC_ECHO, echo); + DDX_Control(pDX, IDC_STEREO, stereo); + DDX_Control(pDX, IDC_VOLUME, volume); + DDX_Control(pDX, IDC_DECLICKING, declicking); + DDX_Control(pDX, IDC_SOUND_INTERPOLATION, sound_interpolation); + DDX_Control(pDX, IDC_SOUND_FILTERING, sound_filtering); + DDX_Control(pDX, IDC_SAMPLE_RATE, sample_rate); + + if( pDX->m_bSaveAndValidate == TRUE ) { + m_enabled = BST_CHECKED == enhance_sound.GetCheck(); + m_surround = BST_CHECKED == surround.GetCheck(); + m_declicking = BST_CHECKED == declicking.GetCheck(); + m_sound_interpolation = BST_CHECKED == sound_interpolation.GetCheck(); + m_echo = (float)echo.GetPos() / 100.0f; + m_stereo = (float)stereo.GetPos() / 100.0f; + m_volume = (float)volume.GetPos() / 100.0f; + m_sound_filtering = (float)sound_filtering.GetPos() / 100.0f; + m_sample_rate = (unsigned int)sample_rate.GetItemData( sample_rate.GetCurSel() ); + } +} + +BOOL AudioCoreSettingsDlg::OnTtnNeedText(UINT id, NMHDR *pNMHDR, LRESULT *pResult) +{ + TOOLTIPTEXT *t3 = (TOOLTIPTEXT *)pNMHDR; // dirty Windows API + BOOL i_provided_tooltip_with_text = TRUE; + + if( !( t3->uFlags & TTF_IDISHWND ) ) { + return FALSE; + } + // even dirtier Windows API: + // t3->hdr.idFrom is actually a HWND, holy cow, why? + // The other case does not even occur. + int controlID = ::GetDlgCtrlID( (HWND)t3->hdr.idFrom ); + CString res; + TCHAR buf[0x400]; // Use own string buffer because szText has an 80 char limit. + // We can't use a dynamic buffer size because Windows does some shady things with + // t3->lpszText at the end of this function, so we have no chance to free the buffer + // before the end of this function. + + switch( controlID ) { + case IDC_VOLUME: + _stprintf_s( t3->szText, _countof( t3->szText ), _T( "%i%%" ), volume.GetPos() ); + break; + case IDC_ECHO: + _stprintf_s( t3->szText, _countof( t3->szText ), _T( "%i%%" ), echo.GetPos() ); + break; + case IDC_STEREO: + _stprintf_s( t3->szText, _countof( t3->szText ), _T( "%i%%" ), stereo.GetPos() ); + break; + case IDC_SOUND_FILTERING: + _stprintf_s( t3->szText, _countof( t3->szText ), _T( "%i%%" ), sound_filtering.GetPos() ); + break; + case IDC_DEFAULT_VOLUME: + res.LoadString( IDS_TOOLTIP_DEFAULT_VOLUME ); + _tcscpy_s( buf, _countof( buf ), res.GetString() ); + t3->lpszText = buf; + break; + case IDC_ENHANCE_SOUND: + res.LoadString( IDS_TOOLTIP_ENHANCE_SOUND ); + _tcscpy_s( buf, _countof( buf ), res.GetString() ); + t3->lpszText = buf; + break; + case IDC_SURROUND: + res.LoadString( IDS_TOOLTIP_SURROUND ); + _tcscpy_s( buf, _countof( buf ), res.GetString() ); + t3->lpszText = buf; + break; + case IDC_DECLICKING: + res.LoadString( IDS_TOOLTIP_DECLICKING ); + _tcscpy_s( buf, _countof( buf ), res.GetString() ); + t3->lpszText = buf; + break; + default: + i_provided_tooltip_with_text = FALSE; + break; + } + + return i_provided_tooltip_with_text; +} + + +BEGIN_MESSAGE_MAP(AudioCoreSettingsDlg, CDialog) + ON_NOTIFY_EX(TTN_NEEDTEXT, 0, &AudioCoreSettingsDlg::OnTtnNeedText) + ON_BN_CLICKED(IDC_DEFAULT_VOLUME, &AudioCoreSettingsDlg::OnBnClickedDefaultVolume) +END_MESSAGE_MAP() + + +// AudioCoreSettingsDlg message handlers + +BOOL AudioCoreSettingsDlg::OnInitDialog() +{ + CDialog::OnInitDialog(); + + // Set up tooltip control + toolTip = new CToolTipCtrl; + toolTip->Create( this ); + toolTip->AddTool( GetDlgItem( IDC_DEFAULT_VOLUME ) ); + toolTip->AddTool( GetDlgItem( IDC_ENHANCE_SOUND ) ); + toolTip->AddTool( GetDlgItem( IDC_SURROUND ) ); + toolTip->AddTool( GetDlgItem( IDC_DECLICKING ) ); + toolTip->Activate( TRUE ); + + enhance_sound.SetCheck( m_enabled ? BST_CHECKED : BST_UNCHECKED ); + + surround.SetCheck( m_surround ? BST_CHECKED : BST_UNCHECKED ); + + declicking.SetCheck( m_declicking ? BST_CHECKED : BST_UNCHECKED ); + + sound_interpolation.SetCheck( m_sound_interpolation ? BST_CHECKED : BST_UNCHECKED ); + + echo.SetRange( 0, 100 ); + echo.SetPos( (int)( m_echo * 100.0f ) ); + + stereo.SetRange( 0, 100 ); + stereo.SetPos( (int)( m_stereo * 100.0f ) ); + + sound_filtering.SetRange( 0, 100 ); + sound_filtering.SetPos( (int)( m_sound_filtering * 100.0f ) ); + + volume.SetRange( (int)( MIN_VOLUME * 100.0f ), (int)( MAX_VOLUME * 100.0f ) ); + volume.SetPos( (int)( m_volume * 100.0f ) ); + + unsigned int rate = 44100; + CString temp; + for( int i = 0 ; i <= 2 ; i++ ) { + temp.Format( _T("%u Hz"), rate ); + int id = sample_rate.AddString( temp.GetString() ); + sample_rate.SetItemData( id, rate ); + if( rate == m_sample_rate ) { + sample_rate.SetCurSel( id ); + } + rate /= 2; + } + + return TRUE; // return TRUE unless you set the focus to a control + // EXCEPTION: OCX Property Pages should return FALSE +} + +BOOL AudioCoreSettingsDlg::PreTranslateMessage(MSG* pMsg) +{ + // Required for enabling ToolTips in a modal dialog box. + if( NULL != toolTip ) { + toolTip->RelayEvent( pMsg ); + } + + return CDialog::PreTranslateMessage(pMsg); +} + +void AudioCoreSettingsDlg::OnBnClickedDefaultVolume() +{ + volume.SetPos( 100 ); +} diff --git a/src/win32/AudioCoreSettingsDlg.h b/src/win32/AudioCoreSettingsDlg.h index 992ae759..4ce69d25 100644 --- a/src/win32/AudioCoreSettingsDlg.h +++ b/src/win32/AudioCoreSettingsDlg.h @@ -1,47 +1,47 @@ -#pragma once - -// AudioCoreSettingsDlg dialog - -class AudioCoreSettingsDlg : public CDialog -{ - DECLARE_DYNAMIC(AudioCoreSettingsDlg) - -public: - bool m_enabled; - bool m_surround; - bool m_declicking; - bool m_sound_interpolation; - float m_echo; - float m_stereo; - float m_volume; - float m_sound_filtering; - unsigned int m_sample_rate; - - AudioCoreSettingsDlg(CWnd* pParent = NULL); // standard constructor - virtual ~AudioCoreSettingsDlg(); - - virtual BOOL OnInitDialog(); - virtual BOOL PreTranslateMessage(MSG* pMsg); - afx_msg void OnBnClickedDefaultVolume(); - -// Dialog Data - enum { IDD = IDD_AUDIO_CORE_SETTINGS }; - -protected: - virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support - afx_msg BOOL OnTtnNeedText(UINT id, NMHDR *pNMHDR, LRESULT *pResult); // Retrieve text for ToolTip - - DECLARE_MESSAGE_MAP() - -private: - CButton enhance_sound; - CButton surround; - CButton declicking; - CButton sound_interpolation; - CSliderCtrl echo; - CSliderCtrl stereo; - CSliderCtrl volume; - CSliderCtrl sound_filtering; - CToolTipCtrl *toolTip; - CComboBox sample_rate; -}; +#pragma once + +// AudioCoreSettingsDlg dialog + +class AudioCoreSettingsDlg : public CDialog +{ + DECLARE_DYNAMIC(AudioCoreSettingsDlg) + +public: + bool m_enabled; + bool m_surround; + bool m_declicking; + bool m_sound_interpolation; + float m_echo; + float m_stereo; + float m_volume; + float m_sound_filtering; + unsigned int m_sample_rate; + + AudioCoreSettingsDlg(CWnd* pParent = NULL); // standard constructor + virtual ~AudioCoreSettingsDlg(); + + virtual BOOL OnInitDialog(); + virtual BOOL PreTranslateMessage(MSG* pMsg); + afx_msg void OnBnClickedDefaultVolume(); + +// Dialog Data + enum { IDD = IDD_AUDIO_CORE_SETTINGS }; + +protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + afx_msg BOOL OnTtnNeedText(UINT id, NMHDR *pNMHDR, LRESULT *pResult); // Retrieve text for ToolTip + + DECLARE_MESSAGE_MAP() + +private: + CButton enhance_sound; + CButton surround; + CButton declicking; + CButton sound_interpolation; + CSliderCtrl echo; + CSliderCtrl stereo; + CSliderCtrl volume; + CSliderCtrl sound_filtering; + CToolTipCtrl *toolTip; + CComboBox sample_rate; +}; diff --git a/src/win32/JoybusOptions.cpp b/src/win32/JoybusOptions.cpp index a27fba78..16f4f1bb 100644 --- a/src/win32/JoybusOptions.cpp +++ b/src/win32/JoybusOptions.cpp @@ -1,76 +1,76 @@ -#include "stdafx.h" -#include "vba.h" -#include "JoybusOptions.h" -#include "../gba/GBALink.h" - -// JoybusOptions dialog - -IMPLEMENT_DYNAMIC(JoybusOptions, CDialog) - -JoybusOptions::JoybusOptions(CWnd* pParent /*=NULL*/) - : CDialog(JoybusOptions::IDD, pParent) -{ -} - -JoybusOptions::~JoybusOptions() -{ -} - -void JoybusOptions::DoDataExchange(CDataExchange* pDX) -{ - CDialog::DoDataExchange(pDX); - DDX_Control(pDX, IDC_JOYBUS_ENABLE, enable_check); - DDX_Control(pDX, IDC_JOYBUS_HOSTNAME, hostname); -} - -BEGIN_MESSAGE_MAP(JoybusOptions, CDialog) - ON_BN_CLICKED(IDC_JOYBUS_ENABLE, &JoybusOptions::OnBnClickedJoybusEnable) - ON_BN_CLICKED(IDOK, &JoybusOptions::OnBnClickedOk) -END_MESSAGE_MAP() - -BOOL JoybusOptions::OnInitDialog() -{ - CDialog::OnInitDialog(); - - enable_check.SetCheck(gba_joybus_enabled ? BST_CHECKED : BST_UNCHECKED); - - hostname.EnableWindow(enable_check.GetCheck() == BST_CHECKED); - - hostname.SetWindowText(joybusHostAddr.ToString().c_str()); - - return TRUE; -} - -void JoybusOptions::OnBnClickedJoybusEnable() -{ - hostname.EnableWindow(enable_check.GetCheck() == BST_CHECKED); -} - -void JoybusOptions::OnBnClickedOk() -{ - if ( (hostname.GetWindowTextLength() == 0) - && (enable_check.GetCheck() == BST_CHECKED) ) - { - hostname.SetWindowText("Enter IP or Hostname"); - return; - } - - gba_joybus_enabled = enable_check.GetCheck() == BST_CHECKED; - - CString address; - hostname.GetWindowText(address); - - sf::IPAddress new_server; - new_server = std::string(address); - - if (!new_server.IsValid()) - { - hostname.SetWindowText("Enter IP or Hostname"); - return; - } - - joybusHostAddr = new_server; - JoyBusConnect(); - - OnOK(); -} +#include "stdafx.h" +#include "vba.h" +#include "JoybusOptions.h" +#include "../gba/GBALink.h" + +// JoybusOptions dialog + +IMPLEMENT_DYNAMIC(JoybusOptions, CDialog) + +JoybusOptions::JoybusOptions(CWnd* pParent /*=NULL*/) + : CDialog(JoybusOptions::IDD, pParent) +{ +} + +JoybusOptions::~JoybusOptions() +{ +} + +void JoybusOptions::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + DDX_Control(pDX, IDC_JOYBUS_ENABLE, enable_check); + DDX_Control(pDX, IDC_JOYBUS_HOSTNAME, hostname); +} + +BEGIN_MESSAGE_MAP(JoybusOptions, CDialog) + ON_BN_CLICKED(IDC_JOYBUS_ENABLE, &JoybusOptions::OnBnClickedJoybusEnable) + ON_BN_CLICKED(IDOK, &JoybusOptions::OnBnClickedOk) +END_MESSAGE_MAP() + +BOOL JoybusOptions::OnInitDialog() +{ + CDialog::OnInitDialog(); + + enable_check.SetCheck(gba_joybus_enabled ? BST_CHECKED : BST_UNCHECKED); + + hostname.EnableWindow(enable_check.GetCheck() == BST_CHECKED); + + hostname.SetWindowText(joybusHostAddr.ToString().c_str()); + + return TRUE; +} + +void JoybusOptions::OnBnClickedJoybusEnable() +{ + hostname.EnableWindow(enable_check.GetCheck() == BST_CHECKED); +} + +void JoybusOptions::OnBnClickedOk() +{ + if ( (hostname.GetWindowTextLength() == 0) + && (enable_check.GetCheck() == BST_CHECKED) ) + { + hostname.SetWindowText("Enter IP or Hostname"); + return; + } + + gba_joybus_enabled = enable_check.GetCheck() == BST_CHECKED; + + CString address; + hostname.GetWindowText(address); + + sf::IPAddress new_server; + new_server = std::string(address); + + if (!new_server.IsValid()) + { + hostname.SetWindowText("Enter IP or Hostname"); + return; + } + + joybusHostAddr = new_server; + JoyBusConnect(); + + OnOK(); +} diff --git a/src/win32/JoybusOptions.h b/src/win32/JoybusOptions.h index ea11f5eb..bfbda832 100644 --- a/src/win32/JoybusOptions.h +++ b/src/win32/JoybusOptions.h @@ -1,27 +1,27 @@ -#pragma once -#include "afxwin.h" - -// JoybusOptions dialog - -class JoybusOptions : public CDialog -{ - DECLARE_DYNAMIC(JoybusOptions) - -public: - JoybusOptions(CWnd* pParent = NULL); // standard constructor - virtual ~JoybusOptions(); - -// Dialog Data - enum { IDD = IDD_JOYBUS_DIALOG }; - -protected: - virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support - - DECLARE_MESSAGE_MAP() -public: - virtual BOOL OnInitDialog(); - afx_msg void OnBnClickedJoybusEnable(); - CButton enable_check; - CEdit hostname; - afx_msg void OnBnClickedOk(); -}; +#pragma once +#include "afxwin.h" + +// JoybusOptions dialog + +class JoybusOptions : public CDialog +{ + DECLARE_DYNAMIC(JoybusOptions) + +public: + JoybusOptions(CWnd* pParent = NULL); // standard constructor + virtual ~JoybusOptions(); + +// Dialog Data + enum { IDD = IDD_JOYBUS_DIALOG }; + +protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + + DECLARE_MESSAGE_MAP() +public: + virtual BOOL OnInitDialog(); + afx_msg void OnBnClickedJoybusEnable(); + CButton enable_check; + CEdit hostname; + afx_msg void OnBnClickedOk(); +}; diff --git a/src/win32/XAudio2.cpp b/src/win32/XAudio2.cpp index 73de71c5..3a048848 100644 --- a/src/win32/XAudio2.cpp +++ b/src/win32/XAudio2.cpp @@ -1,384 +1,384 @@ -#ifndef NO_XAUDIO2 - -// MFC -#include "stdafx.h" - -// Application -#include "VBA.h" - -// Interface -#include "../common/SoundDriver.h" - -// XAudio2 -#include - -// Internals -#include "../System.h" // for systemMessage() -#include "../gba/Globals.h" - - -// Synchronization Event -class XAudio2_BufferNotify : public IXAudio2VoiceCallback -{ -public: - HANDLE hBufferEndEvent; - - XAudio2_BufferNotify() { - hBufferEndEvent = NULL; - hBufferEndEvent = CreateEvent( NULL, FALSE, FALSE, NULL ); - ASSERT( hBufferEndEvent != NULL ); - } - - ~XAudio2_BufferNotify() { - CloseHandle( hBufferEndEvent ); - hBufferEndEvent = NULL; - } - - STDMETHOD_( void, OnBufferEnd ) ( void *pBufferContext ) { - ASSERT( hBufferEndEvent != NULL ); - SetEvent( hBufferEndEvent ); - } - - - // dummies: - STDMETHOD_( void, OnVoiceProcessingPassStart ) ( UINT32 BytesRequired ) {} - STDMETHOD_( void, OnVoiceProcessingPassEnd ) () {} - STDMETHOD_( void, OnStreamEnd ) () {} - STDMETHOD_( void, OnBufferStart ) ( void *pBufferContext ) {} - STDMETHOD_( void, OnLoopEnd ) ( void *pBufferContext ) {} - STDMETHOD_( void, OnVoiceError ) ( void *pBufferContext, HRESULT Error ) {}; -}; - - -// Class Declaration -class XAudio2_Output - : public SoundDriver -{ -public: - XAudio2_Output(); - ~XAudio2_Output(); - - // Initialization - bool init(long sampleRate); - - // Sound Data Feed - void write(u16 * finalWave, int length); - - // Play Control - void pause(); - void resume(); - void reset(); - - // Configuration Changes - void setThrottle( unsigned short throttle ); - -private: - bool failed; - bool initialized; - bool playing; - UINT32 freq; - UINT32 bufferCount; - BYTE *buffers; - int currentBuffer; - int soundBufferLen; - - IXAudio2 *xaud; - IXAudio2MasteringVoice *mVoice; // listener - IXAudio2SourceVoice *sVoice; // sound source - XAUDIO2_BUFFER buf; - XAUDIO2_VOICE_STATE vState; - XAudio2_BufferNotify notify; // buffer end notification -}; - - -// Class Implementation -XAudio2_Output::XAudio2_Output() -{ - failed = false; - initialized = false; - playing = false; - freq = 0; - bufferCount = theApp.xa2BufferCount; - buffers = NULL; - currentBuffer = 0; - - xaud = NULL; - mVoice = NULL; - sVoice = NULL; - ZeroMemory( &buf, sizeof( buf ) ); - ZeroMemory( &vState, sizeof( vState ) ); -} - - -XAudio2_Output::~XAudio2_Output() -{ - initialized = false; - - if( sVoice ) { - if( playing ) { - HRESULT hr = sVoice->Stop( 0 ); - ASSERT( hr == S_OK ); - } - sVoice->DestroyVoice(); - } - - if( buffers ) { - free( buffers ); - buffers = NULL; - } - - if( mVoice ) { - mVoice->DestroyVoice(); - } - - if( xaud ) { - xaud->Release(); - xaud = NULL; - } -} - - -bool XAudio2_Output::init(long sampleRate) -{ - if( failed || initialized ) return false; - - HRESULT hr; - - // Initialize XAudio2 - UINT32 flags = 0; -#ifdef _DEBUG - flags = XAUDIO2_DEBUG_ENGINE; -#endif - - hr = XAudio2Create( &xaud, flags ); - if( hr != S_OK ) { - systemMessage( IDS_XAUDIO2_FAILURE, NULL ); - failed = true; - return false; - } - - - freq = sampleRate; - - // calculate the number of samples per frame first - // then multiply it with the size of a sample frame (16 bit * stereo) - soundBufferLen = ( freq / 60 ) * 4; - - // create own buffers to store sound data because it must not be - // manipulated while the voice plays from it - buffers = (BYTE *)malloc( ( bufferCount + 1 ) * soundBufferLen ); - // + 1 because we need one temporary buffer when all others are in use - - WAVEFORMATEX wfx; - ZeroMemory( &wfx, sizeof( wfx ) ); - wfx.wFormatTag = WAVE_FORMAT_PCM; - wfx.nChannels = 2; - wfx.nSamplesPerSec = freq; - wfx.wBitsPerSample = 16; - wfx.nBlockAlign = wfx.nChannels * ( wfx.wBitsPerSample / 8 ); - wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign; - - - // create sound receiver - hr = xaud->CreateMasteringVoice( - &mVoice, - XAUDIO2_DEFAULT_CHANNELS, - XAUDIO2_DEFAULT_SAMPLERATE, - 0, - theApp.xa2Device, - NULL ); - if( hr != S_OK ) { - systemMessage( IDS_XAUDIO2_CANNOT_CREATE_MASTERINGVOICE, NULL ); - failed = true; - return false; - } - - - // create sound emitter - hr = xaud->CreateSourceVoice( &sVoice, &wfx, 0, 4.0f, ¬ify ); - if( hr != S_OK ) { - systemMessage( IDS_XAUDIO2_CANNOT_CREATE_SOURCEVOICE, NULL ); - failed = true; - return false; - } - - - if( theApp.xa2Upmixing ) { - // set up stereo upmixing - XAUDIO2_DEVICE_DETAILS dd; - ZeroMemory( &dd, sizeof( dd ) ); - hr = xaud->GetDeviceDetails( 0, &dd ); - ASSERT( hr == S_OK ); - float *matrix = NULL; - matrix = (float*)malloc( sizeof( float ) * 2 * dd.OutputFormat.Format.nChannels ); - if( matrix == NULL ) return false; - bool matrixAvailable = true; - switch( dd.OutputFormat.Format.nChannels ) { - case 4: // 4.0 - //Speaker \ Left Source Right Source - /*Front L*/ matrix[0] = 1.0000f; matrix[1] = 0.0000f; - /*Front R*/ matrix[2] = 0.0000f; matrix[3] = 1.0000f; - /*Back L*/ matrix[4] = 1.0000f; matrix[5] = 0.0000f; - /*Back R*/ matrix[6] = 0.0000f; matrix[7] = 1.0000f; - break; - case 5: // 5.0 - //Speaker \ Left Source Right Source - /*Front L*/ matrix[0] = 1.0000f; matrix[1] = 0.0000f; - /*Front R*/ matrix[2] = 0.0000f; matrix[3] = 1.0000f; - /*Front C*/ matrix[4] = 0.7071f; matrix[5] = 0.7071f; - /*Side L*/ matrix[6] = 1.0000f; matrix[7] = 0.0000f; - /*Side R*/ matrix[8] = 0.0000f; matrix[9] = 1.0000f; - break; - case 6: // 5.1 - //Speaker \ Left Source Right Source - /*Front L*/ matrix[0] = 1.0000f; matrix[1] = 0.0000f; - /*Front R*/ matrix[2] = 0.0000f; matrix[3] = 1.0000f; - /*Front C*/ matrix[4] = 0.7071f; matrix[5] = 0.7071f; - /*LFE */ matrix[6] = 0.0000f; matrix[7] = 0.0000f; - /*Side L*/ matrix[8] = 1.0000f; matrix[9] = 0.0000f; - /*Side R*/ matrix[10] = 0.0000f; matrix[11] = 1.0000f; - break; - case 7: // 6.1 - //Speaker \ Left Source Right Source - /*Front L*/ matrix[0] = 1.0000f; matrix[1] = 0.0000f; - /*Front R*/ matrix[2] = 0.0000f; matrix[3] = 1.0000f; - /*Front C*/ matrix[4] = 0.7071f; matrix[5] = 0.7071f; - /*LFE */ matrix[6] = 0.0000f; matrix[7] = 0.0000f; - /*Side L*/ matrix[8] = 1.0000f; matrix[9] = 0.0000f; - /*Side R*/ matrix[10] = 0.0000f; matrix[11] = 1.0000f; - /*Back C*/ matrix[12] = 0.7071f; matrix[13] = 0.7071f; - break; - case 8: // 7.1 - //Speaker \ Left Source Right Source - /*Front L*/ matrix[0] = 1.0000f; matrix[1] = 0.0000f; - /*Front R*/ matrix[2] = 0.0000f; matrix[3] = 1.0000f; - /*Front C*/ matrix[4] = 0.7071f; matrix[5] = 0.7071f; - /*LFE */ matrix[6] = 0.0000f; matrix[7] = 0.0000f; - /*Back L*/ matrix[8] = 1.0000f; matrix[9] = 0.0000f; - /*Back R*/ matrix[10] = 0.0000f; matrix[11] = 1.0000f; - /*Side L*/ matrix[12] = 1.0000f; matrix[13] = 0.0000f; - /*Side R*/ matrix[14] = 0.0000f; matrix[15] = 1.0000f; - break; - default: - matrixAvailable = false; - break; - } - if( matrixAvailable ) { - hr = sVoice->SetOutputMatrix( NULL, 2, dd.OutputFormat.Format.nChannels, matrix ); - ASSERT( hr == S_OK ); - } - free( matrix ); - matrix = NULL; - } - - - hr = sVoice->Start( 0 ); - ASSERT( hr == S_OK ); - playing = true; - - - initialized = true; - return true; -} - - -void XAudio2_Output::write(u16 * finalWave, int length) -{ - if( !initialized || failed ) return; - - while( true ) { - sVoice->GetState( &vState ); - - ASSERT( vState.BuffersQueued <= bufferCount ); - - if( vState.BuffersQueued < bufferCount ) { - if( vState.BuffersQueued == 0 ) { - // buffers ran dry - if( systemVerbose & VERBOSE_SOUNDOUTPUT ) { - static unsigned int i = 0; - log( "XAudio2: Buffers were not refilled fast enough (i=%i)\n", i++ ); - } - } - // there is at least one free buffer - break; - } else { - // the maximum number of buffers is currently queued - if( synchronize && !speedup && !theApp.throttle ) { - // wait for one buffer to finish playing - WaitForSingleObject( notify.hBufferEndEvent, INFINITE ); - } else { - // drop current audio frame - return; - } - } - } - - // copy & protect the audio data in own memory area while playing it - CopyMemory( &buffers[ currentBuffer * soundBufferLen ], finalWave, soundBufferLen ); - - buf.AudioBytes = soundBufferLen; - buf.pAudioData = &buffers[ currentBuffer * soundBufferLen ]; - - currentBuffer++; - currentBuffer %= ( bufferCount + 1 ); // + 1 because we need one temporary buffer - - HRESULT hr = sVoice->SubmitSourceBuffer( &buf ); // send buffer to queue - ASSERT( hr == S_OK ); -} - - -void XAudio2_Output::pause() -{ - if( !initialized || failed ) return; - - if( playing ) { - HRESULT hr = sVoice->Stop( 0 ); - ASSERT( hr == S_OK ); - playing = false; - } -} - - -void XAudio2_Output::resume() -{ - if( !initialized || failed ) return; - - if( !playing ) { - HRESULT hr = sVoice->Start( 0 ); - ASSERT( hr == S_OK ); - playing = true; - } -} - - -void XAudio2_Output::reset() -{ - if( !initialized || failed ) return; - - if( playing ) { - HRESULT hr = sVoice->Stop( 0 ); - ASSERT( hr == S_OK ); - } - - sVoice->FlushSourceBuffers(); - sVoice->Start( 0 ); - playing = true; -} - - -void XAudio2_Output::setThrottle( unsigned short throttle ) -{ - if( !initialized || failed ) return; - - if( throttle == 0 ) throttle = 100; - HRESULT hr = sVoice->SetFrequencyRatio( (float)throttle / 100.0f ); - ASSERT( hr == S_OK ); -} - -SoundDriver *newXAudio2_Output() -{ - return new XAudio2_Output(); -} - - -#endif // #ifndef NO_XAUDIO2 +#ifndef NO_XAUDIO2 + +// MFC +#include "stdafx.h" + +// Application +#include "VBA.h" + +// Interface +#include "../common/SoundDriver.h" + +// XAudio2 +#include + +// Internals +#include "../System.h" // for systemMessage() +#include "../gba/Globals.h" + + +// Synchronization Event +class XAudio2_BufferNotify : public IXAudio2VoiceCallback +{ +public: + HANDLE hBufferEndEvent; + + XAudio2_BufferNotify() { + hBufferEndEvent = NULL; + hBufferEndEvent = CreateEvent( NULL, FALSE, FALSE, NULL ); + ASSERT( hBufferEndEvent != NULL ); + } + + ~XAudio2_BufferNotify() { + CloseHandle( hBufferEndEvent ); + hBufferEndEvent = NULL; + } + + STDMETHOD_( void, OnBufferEnd ) ( void *pBufferContext ) { + ASSERT( hBufferEndEvent != NULL ); + SetEvent( hBufferEndEvent ); + } + + + // dummies: + STDMETHOD_( void, OnVoiceProcessingPassStart ) ( UINT32 BytesRequired ) {} + STDMETHOD_( void, OnVoiceProcessingPassEnd ) () {} + STDMETHOD_( void, OnStreamEnd ) () {} + STDMETHOD_( void, OnBufferStart ) ( void *pBufferContext ) {} + STDMETHOD_( void, OnLoopEnd ) ( void *pBufferContext ) {} + STDMETHOD_( void, OnVoiceError ) ( void *pBufferContext, HRESULT Error ) {}; +}; + + +// Class Declaration +class XAudio2_Output + : public SoundDriver +{ +public: + XAudio2_Output(); + ~XAudio2_Output(); + + // Initialization + bool init(long sampleRate); + + // Sound Data Feed + void write(u16 * finalWave, int length); + + // Play Control + void pause(); + void resume(); + void reset(); + + // Configuration Changes + void setThrottle( unsigned short throttle ); + +private: + bool failed; + bool initialized; + bool playing; + UINT32 freq; + UINT32 bufferCount; + BYTE *buffers; + int currentBuffer; + int soundBufferLen; + + IXAudio2 *xaud; + IXAudio2MasteringVoice *mVoice; // listener + IXAudio2SourceVoice *sVoice; // sound source + XAUDIO2_BUFFER buf; + XAUDIO2_VOICE_STATE vState; + XAudio2_BufferNotify notify; // buffer end notification +}; + + +// Class Implementation +XAudio2_Output::XAudio2_Output() +{ + failed = false; + initialized = false; + playing = false; + freq = 0; + bufferCount = theApp.xa2BufferCount; + buffers = NULL; + currentBuffer = 0; + + xaud = NULL; + mVoice = NULL; + sVoice = NULL; + ZeroMemory( &buf, sizeof( buf ) ); + ZeroMemory( &vState, sizeof( vState ) ); +} + + +XAudio2_Output::~XAudio2_Output() +{ + initialized = false; + + if( sVoice ) { + if( playing ) { + HRESULT hr = sVoice->Stop( 0 ); + ASSERT( hr == S_OK ); + } + sVoice->DestroyVoice(); + } + + if( buffers ) { + free( buffers ); + buffers = NULL; + } + + if( mVoice ) { + mVoice->DestroyVoice(); + } + + if( xaud ) { + xaud->Release(); + xaud = NULL; + } +} + + +bool XAudio2_Output::init(long sampleRate) +{ + if( failed || initialized ) return false; + + HRESULT hr; + + // Initialize XAudio2 + UINT32 flags = 0; +#ifdef _DEBUG + flags = XAUDIO2_DEBUG_ENGINE; +#endif + + hr = XAudio2Create( &xaud, flags ); + if( hr != S_OK ) { + systemMessage( IDS_XAUDIO2_FAILURE, NULL ); + failed = true; + return false; + } + + + freq = sampleRate; + + // calculate the number of samples per frame first + // then multiply it with the size of a sample frame (16 bit * stereo) + soundBufferLen = ( freq / 60 ) * 4; + + // create own buffers to store sound data because it must not be + // manipulated while the voice plays from it + buffers = (BYTE *)malloc( ( bufferCount + 1 ) * soundBufferLen ); + // + 1 because we need one temporary buffer when all others are in use + + WAVEFORMATEX wfx; + ZeroMemory( &wfx, sizeof( wfx ) ); + wfx.wFormatTag = WAVE_FORMAT_PCM; + wfx.nChannels = 2; + wfx.nSamplesPerSec = freq; + wfx.wBitsPerSample = 16; + wfx.nBlockAlign = wfx.nChannels * ( wfx.wBitsPerSample / 8 ); + wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign; + + + // create sound receiver + hr = xaud->CreateMasteringVoice( + &mVoice, + XAUDIO2_DEFAULT_CHANNELS, + XAUDIO2_DEFAULT_SAMPLERATE, + 0, + theApp.xa2Device, + NULL ); + if( hr != S_OK ) { + systemMessage( IDS_XAUDIO2_CANNOT_CREATE_MASTERINGVOICE, NULL ); + failed = true; + return false; + } + + + // create sound emitter + hr = xaud->CreateSourceVoice( &sVoice, &wfx, 0, 4.0f, ¬ify ); + if( hr != S_OK ) { + systemMessage( IDS_XAUDIO2_CANNOT_CREATE_SOURCEVOICE, NULL ); + failed = true; + return false; + } + + + if( theApp.xa2Upmixing ) { + // set up stereo upmixing + XAUDIO2_DEVICE_DETAILS dd; + ZeroMemory( &dd, sizeof( dd ) ); + hr = xaud->GetDeviceDetails( 0, &dd ); + ASSERT( hr == S_OK ); + float *matrix = NULL; + matrix = (float*)malloc( sizeof( float ) * 2 * dd.OutputFormat.Format.nChannels ); + if( matrix == NULL ) return false; + bool matrixAvailable = true; + switch( dd.OutputFormat.Format.nChannels ) { + case 4: // 4.0 + //Speaker \ Left Source Right Source + /*Front L*/ matrix[0] = 1.0000f; matrix[1] = 0.0000f; + /*Front R*/ matrix[2] = 0.0000f; matrix[3] = 1.0000f; + /*Back L*/ matrix[4] = 1.0000f; matrix[5] = 0.0000f; + /*Back R*/ matrix[6] = 0.0000f; matrix[7] = 1.0000f; + break; + case 5: // 5.0 + //Speaker \ Left Source Right Source + /*Front L*/ matrix[0] = 1.0000f; matrix[1] = 0.0000f; + /*Front R*/ matrix[2] = 0.0000f; matrix[3] = 1.0000f; + /*Front C*/ matrix[4] = 0.7071f; matrix[5] = 0.7071f; + /*Side L*/ matrix[6] = 1.0000f; matrix[7] = 0.0000f; + /*Side R*/ matrix[8] = 0.0000f; matrix[9] = 1.0000f; + break; + case 6: // 5.1 + //Speaker \ Left Source Right Source + /*Front L*/ matrix[0] = 1.0000f; matrix[1] = 0.0000f; + /*Front R*/ matrix[2] = 0.0000f; matrix[3] = 1.0000f; + /*Front C*/ matrix[4] = 0.7071f; matrix[5] = 0.7071f; + /*LFE */ matrix[6] = 0.0000f; matrix[7] = 0.0000f; + /*Side L*/ matrix[8] = 1.0000f; matrix[9] = 0.0000f; + /*Side R*/ matrix[10] = 0.0000f; matrix[11] = 1.0000f; + break; + case 7: // 6.1 + //Speaker \ Left Source Right Source + /*Front L*/ matrix[0] = 1.0000f; matrix[1] = 0.0000f; + /*Front R*/ matrix[2] = 0.0000f; matrix[3] = 1.0000f; + /*Front C*/ matrix[4] = 0.7071f; matrix[5] = 0.7071f; + /*LFE */ matrix[6] = 0.0000f; matrix[7] = 0.0000f; + /*Side L*/ matrix[8] = 1.0000f; matrix[9] = 0.0000f; + /*Side R*/ matrix[10] = 0.0000f; matrix[11] = 1.0000f; + /*Back C*/ matrix[12] = 0.7071f; matrix[13] = 0.7071f; + break; + case 8: // 7.1 + //Speaker \ Left Source Right Source + /*Front L*/ matrix[0] = 1.0000f; matrix[1] = 0.0000f; + /*Front R*/ matrix[2] = 0.0000f; matrix[3] = 1.0000f; + /*Front C*/ matrix[4] = 0.7071f; matrix[5] = 0.7071f; + /*LFE */ matrix[6] = 0.0000f; matrix[7] = 0.0000f; + /*Back L*/ matrix[8] = 1.0000f; matrix[9] = 0.0000f; + /*Back R*/ matrix[10] = 0.0000f; matrix[11] = 1.0000f; + /*Side L*/ matrix[12] = 1.0000f; matrix[13] = 0.0000f; + /*Side R*/ matrix[14] = 0.0000f; matrix[15] = 1.0000f; + break; + default: + matrixAvailable = false; + break; + } + if( matrixAvailable ) { + hr = sVoice->SetOutputMatrix( NULL, 2, dd.OutputFormat.Format.nChannels, matrix ); + ASSERT( hr == S_OK ); + } + free( matrix ); + matrix = NULL; + } + + + hr = sVoice->Start( 0 ); + ASSERT( hr == S_OK ); + playing = true; + + + initialized = true; + return true; +} + + +void XAudio2_Output::write(u16 * finalWave, int length) +{ + if( !initialized || failed ) return; + + while( true ) { + sVoice->GetState( &vState ); + + ASSERT( vState.BuffersQueued <= bufferCount ); + + if( vState.BuffersQueued < bufferCount ) { + if( vState.BuffersQueued == 0 ) { + // buffers ran dry + if( systemVerbose & VERBOSE_SOUNDOUTPUT ) { + static unsigned int i = 0; + log( "XAudio2: Buffers were not refilled fast enough (i=%i)\n", i++ ); + } + } + // there is at least one free buffer + break; + } else { + // the maximum number of buffers is currently queued + if( synchronize && !speedup && !theApp.throttle ) { + // wait for one buffer to finish playing + WaitForSingleObject( notify.hBufferEndEvent, INFINITE ); + } else { + // drop current audio frame + return; + } + } + } + + // copy & protect the audio data in own memory area while playing it + CopyMemory( &buffers[ currentBuffer * soundBufferLen ], finalWave, soundBufferLen ); + + buf.AudioBytes = soundBufferLen; + buf.pAudioData = &buffers[ currentBuffer * soundBufferLen ]; + + currentBuffer++; + currentBuffer %= ( bufferCount + 1 ); // + 1 because we need one temporary buffer + + HRESULT hr = sVoice->SubmitSourceBuffer( &buf ); // send buffer to queue + ASSERT( hr == S_OK ); +} + + +void XAudio2_Output::pause() +{ + if( !initialized || failed ) return; + + if( playing ) { + HRESULT hr = sVoice->Stop( 0 ); + ASSERT( hr == S_OK ); + playing = false; + } +} + + +void XAudio2_Output::resume() +{ + if( !initialized || failed ) return; + + if( !playing ) { + HRESULT hr = sVoice->Start( 0 ); + ASSERT( hr == S_OK ); + playing = true; + } +} + + +void XAudio2_Output::reset() +{ + if( !initialized || failed ) return; + + if( playing ) { + HRESULT hr = sVoice->Stop( 0 ); + ASSERT( hr == S_OK ); + } + + sVoice->FlushSourceBuffers(); + sVoice->Start( 0 ); + playing = true; +} + + +void XAudio2_Output::setThrottle( unsigned short throttle ) +{ + if( !initialized || failed ) return; + + if( throttle == 0 ) throttle = 100; + HRESULT hr = sVoice->SetFrequencyRatio( (float)throttle / 100.0f ); + ASSERT( hr == S_OK ); +} + +SoundDriver *newXAudio2_Output() +{ + return new XAudio2_Output(); +} + + +#endif // #ifndef NO_XAUDIO2 diff --git a/src/win32/XAudio2_Config.cpp b/src/win32/XAudio2_Config.cpp index 1a6cd5bb..4a8b45d2 100644 --- a/src/win32/XAudio2_Config.cpp +++ b/src/win32/XAudio2_Config.cpp @@ -1,151 +1,151 @@ -#include "stdafx.h" -#ifndef NO_XAUDIO2 -#include "VBA.h" - -#include "XAudio2_Config.h" - -#include - - -// XAudio2_Config dialog - -IMPLEMENT_DYNAMIC(XAudio2_Config, CDialog) - -XAudio2_Config::XAudio2_Config(CWnd* pParent /*=NULL*/) - : CDialog(XAudio2_Config::IDD, pParent) - , m_selected_device_index(0) - , m_enable_upmixing(false) - , m_buffer_count(0) -{ -} - -XAudio2_Config::~XAudio2_Config() -{ -} - -void XAudio2_Config::DoDataExchange(CDataExchange* pDX) -{ - CDialog::DoDataExchange(pDX); - DDX_Control(pDX, IDC_COMBO_DEV, m_combo_dev); - DDX_Control(pDX, IDC_SLIDER_BUFFER, m_slider_buffer); - DDX_Control(pDX, IDC_INFO_BUFFER, m_info_buffer); - DDX_Control(pDX, IDC_CHECK_UPMIX, m_check_upmix); - - if( pDX->m_bSaveAndValidate == TRUE ) { - if( CB_ERR != m_combo_dev.GetCurSel() ) { - if( CB_ERR != m_combo_dev.GetItemData( m_combo_dev.GetCurSel() ) ) { - m_selected_device_index = m_combo_dev.GetItemData( m_combo_dev.GetCurSel() ); - } - } - - m_enable_upmixing = ( m_check_upmix.GetCheck() == BST_CHECKED ); - - m_buffer_count = (UINT32)m_slider_buffer.GetPos(); - } else { - m_check_upmix.SetCheck( m_enable_upmixing ? BST_CHECKED : BST_UNCHECKED ); - } -} - - -BEGIN_MESSAGE_MAP(XAudio2_Config, CDialog) - ON_WM_HSCROLL() -END_MESSAGE_MAP() - - -// XAudio2_Config message handlers - -BOOL XAudio2_Config::OnInitDialog() -{ - CDialog::OnInitDialog(); - - m_combo_dev.ResetContent(); - - m_slider_buffer.SetRange( 2, 10, FALSE ); - m_slider_buffer.SetTicFreq( 1 ); - m_slider_buffer.SetPos( (int)m_buffer_count ); - - CString info; - int pos = m_slider_buffer.GetPos(); - info.Format( _T("%i frames = %.2f ms"), pos, (float)pos / 60.0f * 1000.0f ); - m_info_buffer.SetWindowText( info ); - - HRESULT hr; - IXAudio2 *xa = NULL; - UINT32 flags = 0; -#ifdef _DEBUG - flags = XAUDIO2_DEBUG_ENGINE; -#endif - - hr = XAudio2Create( &xa, flags ); - if( hr != S_OK ) { - systemMessage( IDS_XAUDIO2_FAILURE, NULL ); - } else { - UINT32 dev_count = 0; - hr = xa->GetDeviceCount( &dev_count ); - if( hr != S_OK ) { - systemMessage( IDS_XAUDIO2_CANNOT_ENUMERATE_DEVICES, NULL ); - } else { - XAUDIO2_DEVICE_DETAILS dd; - for( UINT32 i = 0; i < dev_count; i++ ) { - hr = xa->GetDeviceDetails( i, &dd ); - if( hr != S_OK ) { - continue; - } else { -#ifdef _UNICODE - int id = m_combo_dev.AddString( dd.DisplayName ); -#else - CHAR temp[256]; - ZeroMemory( temp, sizeof( temp ) ); - WideCharToMultiByte( - CP_ACP, - WC_NO_BEST_FIT_CHARS, - dd.DisplayName, - -1, - temp, - sizeof( temp ) - 1, - NULL, - NULL ); - - int id = m_combo_dev.AddString( temp ); -#endif - if( id < 0 ) { - systemMessage( IDS_XAUDIO2_CANNOT_ENUMERATE_DEVICES, NULL ); - break; - } else { - m_combo_dev.SetItemData( id, i ); - } - } - } - - // select the currently configured device { - int count = m_combo_dev.GetCount(); - if( count > 0 ) { - for( int i = 0; i < count; i++ ) { - if( m_combo_dev.GetItemData( i ) == m_selected_device_index ) { - m_combo_dev.SetCurSel( i ); - break; - } - } - } - // } - - } - xa->Release(); - xa = NULL; - } - - return TRUE; // return TRUE unless you set the focus to a control - // EXCEPTION: OCX Property Pages should return FALSE -} - -void XAudio2_Config::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar) -{ - CString info; - int pos = m_slider_buffer.GetPos(); - info.Format( _T("%i frames = %.2f ms"), pos, (float)pos / 60.0f * 1000.0f ); - m_info_buffer.SetWindowText( info ); - - CDialog::OnHScroll(nSBCode, nPos, pScrollBar); -} - -#endif +#include "stdafx.h" +#ifndef NO_XAUDIO2 +#include "VBA.h" + +#include "XAudio2_Config.h" + +#include + + +// XAudio2_Config dialog + +IMPLEMENT_DYNAMIC(XAudio2_Config, CDialog) + +XAudio2_Config::XAudio2_Config(CWnd* pParent /*=NULL*/) + : CDialog(XAudio2_Config::IDD, pParent) + , m_selected_device_index(0) + , m_enable_upmixing(false) + , m_buffer_count(0) +{ +} + +XAudio2_Config::~XAudio2_Config() +{ +} + +void XAudio2_Config::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + DDX_Control(pDX, IDC_COMBO_DEV, m_combo_dev); + DDX_Control(pDX, IDC_SLIDER_BUFFER, m_slider_buffer); + DDX_Control(pDX, IDC_INFO_BUFFER, m_info_buffer); + DDX_Control(pDX, IDC_CHECK_UPMIX, m_check_upmix); + + if( pDX->m_bSaveAndValidate == TRUE ) { + if( CB_ERR != m_combo_dev.GetCurSel() ) { + if( CB_ERR != m_combo_dev.GetItemData( m_combo_dev.GetCurSel() ) ) { + m_selected_device_index = m_combo_dev.GetItemData( m_combo_dev.GetCurSel() ); + } + } + + m_enable_upmixing = ( m_check_upmix.GetCheck() == BST_CHECKED ); + + m_buffer_count = (UINT32)m_slider_buffer.GetPos(); + } else { + m_check_upmix.SetCheck( m_enable_upmixing ? BST_CHECKED : BST_UNCHECKED ); + } +} + + +BEGIN_MESSAGE_MAP(XAudio2_Config, CDialog) + ON_WM_HSCROLL() +END_MESSAGE_MAP() + + +// XAudio2_Config message handlers + +BOOL XAudio2_Config::OnInitDialog() +{ + CDialog::OnInitDialog(); + + m_combo_dev.ResetContent(); + + m_slider_buffer.SetRange( 2, 10, FALSE ); + m_slider_buffer.SetTicFreq( 1 ); + m_slider_buffer.SetPos( (int)m_buffer_count ); + + CString info; + int pos = m_slider_buffer.GetPos(); + info.Format( _T("%i frames = %.2f ms"), pos, (float)pos / 60.0f * 1000.0f ); + m_info_buffer.SetWindowText( info ); + + HRESULT hr; + IXAudio2 *xa = NULL; + UINT32 flags = 0; +#ifdef _DEBUG + flags = XAUDIO2_DEBUG_ENGINE; +#endif + + hr = XAudio2Create( &xa, flags ); + if( hr != S_OK ) { + systemMessage( IDS_XAUDIO2_FAILURE, NULL ); + } else { + UINT32 dev_count = 0; + hr = xa->GetDeviceCount( &dev_count ); + if( hr != S_OK ) { + systemMessage( IDS_XAUDIO2_CANNOT_ENUMERATE_DEVICES, NULL ); + } else { + XAUDIO2_DEVICE_DETAILS dd; + for( UINT32 i = 0; i < dev_count; i++ ) { + hr = xa->GetDeviceDetails( i, &dd ); + if( hr != S_OK ) { + continue; + } else { +#ifdef _UNICODE + int id = m_combo_dev.AddString( dd.DisplayName ); +#else + CHAR temp[256]; + ZeroMemory( temp, sizeof( temp ) ); + WideCharToMultiByte( + CP_ACP, + WC_NO_BEST_FIT_CHARS, + dd.DisplayName, + -1, + temp, + sizeof( temp ) - 1, + NULL, + NULL ); + + int id = m_combo_dev.AddString( temp ); +#endif + if( id < 0 ) { + systemMessage( IDS_XAUDIO2_CANNOT_ENUMERATE_DEVICES, NULL ); + break; + } else { + m_combo_dev.SetItemData( id, i ); + } + } + } + + // select the currently configured device { + int count = m_combo_dev.GetCount(); + if( count > 0 ) { + for( int i = 0; i < count; i++ ) { + if( m_combo_dev.GetItemData( i ) == m_selected_device_index ) { + m_combo_dev.SetCurSel( i ); + break; + } + } + } + // } + + } + xa->Release(); + xa = NULL; + } + + return TRUE; // return TRUE unless you set the focus to a control + // EXCEPTION: OCX Property Pages should return FALSE +} + +void XAudio2_Config::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar) +{ + CString info; + int pos = m_slider_buffer.GetPos(); + info.Format( _T("%i frames = %.2f ms"), pos, (float)pos / 60.0f * 1000.0f ); + m_info_buffer.SetWindowText( info ); + + CDialog::OnHScroll(nSBCode, nPos, pScrollBar); +} + +#endif diff --git a/src/win32/XAudio2_Config.h b/src/win32/XAudio2_Config.h index ee0bcf5c..4614baf5 100644 --- a/src/win32/XAudio2_Config.h +++ b/src/win32/XAudio2_Config.h @@ -1,37 +1,37 @@ -#ifndef NO_XAUDIO2 - -#pragma once - - -class XAudio2_Config : public CDialog -{ - DECLARE_DYNAMIC(XAudio2_Config) - DECLARE_MESSAGE_MAP() - -public: - UINT32 m_selected_device_index; - UINT32 m_buffer_count; - bool m_enable_upmixing; - -private: - CComboBox m_combo_dev; - CSliderCtrl m_slider_buffer; - CStatic m_info_buffer; - CButton m_check_upmix; - - -public: - XAudio2_Config(CWnd* pParent = NULL); // standard constructor - virtual ~XAudio2_Config(); - -// Dialog Data - enum { IDD = IDD_XAUDIO2_CONFIG }; - - virtual BOOL OnInitDialog(); - afx_msg void OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar); - -protected: - virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support -}; - -#endif +#ifndef NO_XAUDIO2 + +#pragma once + + +class XAudio2_Config : public CDialog +{ + DECLARE_DYNAMIC(XAudio2_Config) + DECLARE_MESSAGE_MAP() + +public: + UINT32 m_selected_device_index; + UINT32 m_buffer_count; + bool m_enable_upmixing; + +private: + CComboBox m_combo_dev; + CSliderCtrl m_slider_buffer; + CStatic m_info_buffer; + CButton m_check_upmix; + + +public: + XAudio2_Config(CWnd* pParent = NULL); // standard constructor + virtual ~XAudio2_Config(); + +// Dialog Data + enum { IDD = IDD_XAUDIO2_CONFIG }; + + virtual BOOL OnInitDialog(); + afx_msg void OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar); + +protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support +}; + +#endif diff --git a/src/win32/targetver.h b/src/win32/targetver.h index 5e89b3f6..4f07adf6 100644 --- a/src/win32/targetver.h +++ b/src/win32/targetver.h @@ -1,24 +1,24 @@ -#pragma once - -// The following macros define the minimum required platform. The minimum required platform -// is the earliest version of Windows, Internet Explorer etc. that has the necessary features to run -// your application. The macros work by enabling all features available on platform versions up to and -// including the version specified. - -// Modify the following defines if you have to target a platform prior to the ones specified below. -// Refer to MSDN for the latest info on corresponding values for different platforms. -#ifndef WINVER // Specifies that the minimum required platform is Windows 2000. -#define WINVER 0x0500 // Change this to the appropriate value to target other versions of Windows. -#endif - -#ifndef _WIN32_WINNT // Specifies that the minimum required platform is Windows 2000. -#define _WIN32_WINNT 0x0500 // Change this to the appropriate value to target other versions of Windows. -#endif - -#ifndef _WIN32_WINDOWS // Specifies that the minimum required platform is Windows 98. -#define _WIN32_WINDOWS 0x0410 // Change this to the appropriate value to target Windows Me or later. -#endif - -#ifndef _WIN32_IE // Specifies that the minimum required platform is Internet Explorer 5.0. -#define _WIN32_IE 0x0500 // Change this to the appropriate value to target other versions of IE. -#endif // Actually, we don't care +#pragma once + +// The following macros define the minimum required platform. The minimum required platform +// is the earliest version of Windows, Internet Explorer etc. that has the necessary features to run +// your application. The macros work by enabling all features available on platform versions up to and +// including the version specified. + +// Modify the following defines if you have to target a platform prior to the ones specified below. +// Refer to MSDN for the latest info on corresponding values for different platforms. +#ifndef WINVER // Specifies that the minimum required platform is Windows 2000. +#define WINVER 0x0500 // Change this to the appropriate value to target other versions of Windows. +#endif + +#ifndef _WIN32_WINNT // Specifies that the minimum required platform is Windows 2000. +#define _WIN32_WINNT 0x0500 // Change this to the appropriate value to target other versions of Windows. +#endif + +#ifndef _WIN32_WINDOWS // Specifies that the minimum required platform is Windows 98. +#define _WIN32_WINDOWS 0x0410 // Change this to the appropriate value to target Windows Me or later. +#endif + +#ifndef _WIN32_IE // Specifies that the minimum required platform is Internet Explorer 5.0. +#define _WIN32_IE 0x0500 // Change this to the appropriate value to target other versions of IE. +#endif // Actually, we don't care